From da8f2e6cd616e9430f8bb8d56bb9d25dffe1b98a Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Tue, 20 Aug 2024 11:49:41 -0400 Subject: [PATCH 1/7] 3.0.0: Sync changes to develop (#884) --- .eslintrc.js | 19 +- .gitattributes | 2 + .gitignore | 7 +- .prettierrc.json | 3 +- .vscode/launch.json | 27 + Makefile | 6 +- README.md | 4 + dist/esm/package.json | 1 + examples/accounts.ts | 38 +- examples/app.ts | 142 +- examples/asa.ts | 76 +- examples/atc.ts | 13 +- examples/atomics.ts | 17 +- examples/block_fetcher/index.ts | 2 +- examples/codec.ts | 24 +- examples/debug.ts | 5 +- examples/indexer.ts | 26 +- examples/kmd.ts | 2 +- examples/lsig.ts | 34 +- examples/overview.ts | 17 +- examples/participation.ts | 45 +- examples/smoke_test.sh | 2 +- examples/utils.ts | 24 +- package-lock.json | 8877 +++-------------- package.json | 66 +- src/abi/abi_type.ts | 46 +- src/abi/contract.ts | 4 +- src/abi/index.ts | 12 +- src/abi/interface.ts | 2 +- src/abi/method.ts | 20 +- src/abi/transaction.ts | 4 +- src/account.ts | 10 +- src/bid.ts | 96 - src/boxStorage.ts | 48 +- src/client/baseHTTPClient.ts | 18 +- src/client/client.ts | 222 +- src/client/index.ts | 6 + src/client/kmd.ts | 151 +- src/client/urlTokenBaseHTTPClient.ts | 39 +- .../v2/algod/accountApplicationInformation.ts | 25 +- .../v2/algod/accountAssetInformation.ts | 25 +- src/client/v2/algod/accountInformation.ts | 27 +- src/client/v2/algod/algod.ts | 188 +- src/client/v2/algod/block.ts | 18 +- src/client/v2/algod/compile.ts | 46 +- src/client/v2/algod/disassemble.ts | 46 +- src/client/v2/algod/dryrun.ts | 42 +- src/client/v2/algod/genesis.ts | 10 +- .../v2/algod/getApplicationBoxByName.ts | 25 +- src/client/v2/algod/getApplicationBoxes.ts | 25 +- src/client/v2/algod/getApplicationByID.ts | 22 +- src/client/v2/algod/getAssetByID.ts | 22 +- src/client/v2/algod/getBlockHash.ts | 22 +- .../v2/algod/getBlockOffsetTimestamp.ts | 15 +- src/client/v2/algod/getBlockTxids.ts | 18 +- src/client/v2/algod/getLedgerStateDelta.ts | 24 +- .../getLedgerStateDeltaForTransactionGroup.ts | 24 +- src/client/v2/algod/getSyncRound.ts | 15 +- ...ansactionGroupLedgerStateDeltasForRound.ts | 30 +- src/client/v2/algod/getTransactionProof.ts | 20 +- src/client/v2/algod/healthCheck.ts | 14 +- src/client/v2/algod/index.ts | 3 + src/client/v2/algod/lightBlockHeaderProof.ts | 23 +- src/client/v2/algod/models/types.ts | 6962 ++++++++----- .../v2/algod/pendingTransactionInformation.ts | 22 +- src/client/v2/algod/pendingTransactions.ts | 16 +- .../v2/algod/pendingTransactionsByAddress.ts | 23 +- src/client/v2/algod/ready.ts | 8 +- src/client/v2/algod/sendRawTransaction.ts | 37 +- .../v2/algod/setBlockOffsetTimestamp.ts | 32 +- src/client/v2/algod/setSyncRound.ts | 32 +- src/client/v2/algod/simulateTransaction.ts | 45 +- src/client/v2/algod/stateproof.ts | 23 +- src/client/v2/algod/status.ts | 12 +- src/client/v2/algod/statusAfterBlock.ts | 22 +- src/client/v2/algod/suggestedParams.ts | 50 +- src/client/v2/algod/supply.ts | 12 +- src/client/v2/algod/unsetSyncRound.ts | 21 +- src/client/v2/algod/versions.ts | 12 +- src/client/v2/basemodel.ts | 81 - src/client/v2/indexer/index.ts | 3 + src/client/v2/indexer/indexer.ts | 110 +- .../v2/indexer/lookupAccountAppLocalStates.ts | 27 +- src/client/v2/indexer/lookupAccountAssets.ts | 27 +- src/client/v2/indexer/lookupAccountByID.ts | 27 +- .../lookupAccountCreatedApplications.ts | 27 +- .../v2/indexer/lookupAccountCreatedAssets.ts | 27 +- .../v2/indexer/lookupAccountTransactions.ts | 45 +- .../lookupApplicationBoxByIDandName.ts | 25 +- .../v2/indexer/lookupApplicationLogs.ts | 22 +- src/client/v2/indexer/lookupApplications.ts | 22 +- src/client/v2/indexer/lookupAssetBalances.ts | 22 +- src/client/v2/indexer/lookupAssetByID.ts | 22 +- .../v2/indexer/lookupAssetTransactions.ts | 43 +- src/client/v2/indexer/lookupBlock.ts | 22 +- .../v2/indexer/lookupTransactionByID.ts | 22 +- src/client/v2/indexer/makeHealthCheck.ts | 12 +- src/client/v2/indexer/models/types.ts | 6442 ++++++++---- src/client/v2/indexer/searchAccounts.ts | 17 +- .../v2/indexer/searchForApplicationBoxes.ts | 25 +- .../v2/indexer/searchForApplications.ts | 17 +- src/client/v2/indexer/searchForAssets.ts | 17 +- .../v2/indexer/searchForTransactions.ts | 33 +- src/client/v2/jsonrequest.ts | 96 +- src/client/v2/serviceClient.ts | 40 +- src/client/v2/untypedmodel.ts | 25 + src/composer.ts | 143 +- src/dryrun.ts | 435 +- src/encoding/address.ts | 280 +- src/encoding/bigint.ts | 6 +- src/encoding/binarydata.ts | 80 + src/encoding/encoding.ts | 571 +- src/encoding/schema/address.ts | 53 + src/encoding/schema/array.ts | 66 + src/encoding/schema/binarystring.ts | 73 + src/encoding/schema/blockhash.ts | 84 + src/encoding/schema/boolean.ts | 54 + src/encoding/schema/bytearray.ts | 127 + src/encoding/schema/index.ts | 26 + src/encoding/schema/map.ts | 713 ++ src/encoding/schema/optional.ts | 72 + src/encoding/schema/string.ts | 55 + src/encoding/schema/uint64.ts | 46 + src/encoding/schema/untyped.ts | 47 + src/encoding/uint64.ts | 3 +- src/group.ts | 102 +- src/index.ts | 4 +- src/logic/sourcemap.ts | 123 +- src/logicsig.ts | 356 +- src/main.ts | 185 +- src/makeTxn.ts | 1998 ++-- src/mnemonic/mnemonic.ts | 28 +- src/multisig.ts | 492 +- src/multisigSigning.ts | 359 + src/nacl/naclWrappers.ts | 2 +- src/signedTransaction.ts | 163 + src/signer.ts | 15 +- src/signing.ts | 95 + src/stateproof.ts | 595 ++ src/transaction.ts | 2228 ++--- src/types/account.ts | 4 +- src/types/address.ts | 7 - src/types/block.ts | 1278 +++ src/types/blockHeader.ts | 81 - src/types/index.ts | 3 - src/types/intDecoding.ts | 2 +- src/types/multisig.ts | 22 - src/types/statedelta.ts | 1717 ++++ src/types/transactions/application.ts | 111 - src/types/transactions/asset.ts | 108 - src/types/transactions/base.ts | 352 +- src/types/transactions/builder.ts | 69 - src/types/transactions/encoded.ts | 452 +- src/types/transactions/index.ts | 74 +- src/types/transactions/keyreg.ts | 23 - src/types/transactions/payment.ts | 14 - src/types/transactions/stateproof.ts | 17 - src/types/utils.ts | 6 +- src/utils/utils.ts | 121 +- src/wait.ts | 15 +- ....Mnemonics_test.js => 1.Mnemonics_test.ts} | 39 +- tests/10.ABI.ts | 93 +- tests/2.Encoding.js | 541 - tests/2.Encoding.ts | 3218 ++++++ tests/{3.Address.js => 3.Address.ts} | 71 +- tests/4.Utils.ts | 525 +- tests/5.Transaction.js | 1648 --- tests/5.Transaction.ts | 2144 ++++ tests/6.Multisig.ts | 481 +- tests/{7.AlgoSDK.js => 7.AlgoSDK.ts} | 1095 +- tests/8.LogicSig.ts | 393 +- tests/9.Client.ts | 75 +- tests/cucumber/browser/test.js | 17 +- tests/cucumber/browser/webpack.config.js | 4 + tests/cucumber/integration.tags | 1 - tests/cucumber/steps/index.js | 1 - tests/cucumber/steps/steps.js | 2335 +++-- tests/cucumber/unit.tags | 7 +- tests/mocha.js | 53 +- .../groupdelta-betanet_23963123_2.msgp | Bin 0 -> 8475 bytes tests/resources/stateproof.msgp | Bin 0 -> 172573 bytes tsconfig.json | 3 +- tsdoc.json | 23 + v2_TO_v3_MIGRATION_GUIDE.md | 347 + webpack.config.js | 1 + 185 files changed, 30162 insertions(+), 22621 deletions(-) create mode 100644 .gitattributes create mode 100644 .vscode/launch.json create mode 100644 dist/esm/package.json delete mode 100644 src/bid.ts create mode 100644 src/client/index.ts create mode 100644 src/client/v2/algod/index.ts delete mode 100644 src/client/v2/basemodel.ts create mode 100644 src/client/v2/indexer/index.ts create mode 100644 src/client/v2/untypedmodel.ts create mode 100644 src/encoding/binarydata.ts create mode 100644 src/encoding/schema/address.ts create mode 100644 src/encoding/schema/array.ts create mode 100644 src/encoding/schema/binarystring.ts create mode 100644 src/encoding/schema/blockhash.ts create mode 100644 src/encoding/schema/boolean.ts create mode 100644 src/encoding/schema/bytearray.ts create mode 100644 src/encoding/schema/index.ts create mode 100644 src/encoding/schema/map.ts create mode 100644 src/encoding/schema/optional.ts create mode 100644 src/encoding/schema/string.ts create mode 100644 src/encoding/schema/uint64.ts create mode 100644 src/encoding/schema/untyped.ts create mode 100644 src/multisigSigning.ts create mode 100644 src/signedTransaction.ts create mode 100644 src/signing.ts create mode 100644 src/stateproof.ts delete mode 100644 src/types/address.ts create mode 100644 src/types/block.ts delete mode 100644 src/types/blockHeader.ts delete mode 100644 src/types/index.ts delete mode 100644 src/types/multisig.ts create mode 100644 src/types/statedelta.ts delete mode 100644 src/types/transactions/application.ts delete mode 100644 src/types/transactions/asset.ts delete mode 100644 src/types/transactions/builder.ts delete mode 100644 src/types/transactions/keyreg.ts delete mode 100644 src/types/transactions/payment.ts delete mode 100644 src/types/transactions/stateproof.ts rename tests/{1.Mnemonics_test.js => 1.Mnemonics_test.ts} (71%) delete mode 100644 tests/2.Encoding.js create mode 100644 tests/2.Encoding.ts rename tests/{3.Address.js => 3.Address.ts} (69%) delete mode 100644 tests/5.Transaction.js create mode 100644 tests/5.Transaction.ts rename tests/{7.AlgoSDK.js => 7.AlgoSDK.ts} (58%) create mode 100644 tests/resources/groupdelta-betanet_23963123_2.msgp create mode 100644 tests/resources/stateproof.msgp create mode 100644 tsdoc.json create mode 100644 v2_TO_v3_MIGRATION_GUIDE.md diff --git a/.eslintrc.js b/.eslintrc.js index 99ec03842..6c76ac01f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,16 +16,6 @@ module.exports = { }, 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 }], @@ -68,4 +58,13 @@ module.exports = { 'tests/cucumber/browser/build/', 'tests/browser/bundle.*', ], + settings: { + 'import/resolver': { + typescript: { + extensionAlias: { + '.js': ['.ts', '.d.ts', '.js'], + }, + }, + }, + }, }; diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..bcb84a383 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +src/client/v2/algod/models/types.ts linguist-generated=true +src/client/v2/indexer/models/types.ts linguist-generated=true diff --git a/.gitignore b/.gitignore index 6bf3410c1..c6ca46d5c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +dist/* +!dist/esm +dist/esm/* +!dist/esm/package.json + .DS_Store .idea/ @@ -6,6 +11,7 @@ .vscode/* !.vscode/settings.json !.vscode/extensions.json +!.vscode/launch.json # npm node_modules/ @@ -23,7 +29,6 @@ tests/cucumber/browser/build tests/browser/bundle.* # Builds -dist/ docs/ built/ diff --git a/.prettierrc.json b/.prettierrc.json index 544138be4..c1a6f6671 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,3 +1,4 @@ { - "singleQuote": true + "singleQuote": true, + "trailingComma": "es5" } diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..c020d53f4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug unit tests", + "type": "node", + "request": "launch", + "program": "${workspaceRoot}/tests/mocha.js", + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/tsx", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "env": { + "NODE_ENV": "testing", + "MOCHA_TIMEOUT": "0" + }, + "skipFiles": [ + // Node.js internal core modules + "/**", + // Ignore all dependencies (optional) + "${workspaceFolder}/node_modules/**" + ] + } + ] +} diff --git a/Makefile b/Makefile index 057f89340..d4751e66a 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,10 @@ UNIT_TAGS := "$(subst :, or ,$(shell awk '{print $2}' tests/cucumber/unit.tags INTEGRATIONS_TAGS := "$(subst :, or ,$(shell awk '{print $2}' tests/cucumber/integration.tags | paste -s -d: -))" unit: - node_modules/.bin/cucumber-js --tags $(UNIT_TAGS) tests/cucumber/features --require-module ts-node/register --require tests/cucumber/steps/index.js - + node_modules/.bin/cucumber-js --tags $(UNIT_TAGS) tests/cucumber/features --require-module tsx/cjs --require tests/cucumber/steps/index.js + integration: - node_modules/.bin/cucumber-js --tags $(INTEGRATIONS_TAGS) tests/cucumber/features --require-module ts-node/register --require tests/cucumber/steps/index.js + node_modules/.bin/cucumber-js --tags $(INTEGRATIONS_TAGS) tests/cucumber/features --require-module tsx/cjs --require tests/cucumber/steps/index.js # The following assumes that all cucumber steps are defined in `./tests/cucumber/steps/steps.js` and begin past line 135 of that file. # Please note any deviations of the above before presuming correctness. diff --git a/README.md b/README.md index 81e2d8f48..a95f68533 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ AlgoSDK is the official JavaScript library for communicating with the Algorand network. It's designed for modern browsers and Node.js. +## New Major Version 3 + +Existing codebases using v2 of this library will be incompatible with v3. The v3 release introduces breaking changes to the API, and a migration guide is available [here](v2_TO_v3_MIGRATION_GUIDE.md). + ## Installation ### [Node.js](https://nodejs.org/en/download/) diff --git a/dist/esm/package.json b/dist/esm/package.json new file mode 100644 index 000000000..6990891ff --- /dev/null +++ b/dist/esm/package.json @@ -0,0 +1 @@ +{"type": "module"} diff --git a/examples/accounts.ts b/examples/accounts.ts index 8491c0877..4f144ca7c 100644 --- a/examples/accounts.ts +++ b/examples/accounts.ts @@ -19,7 +19,7 @@ async function main() { 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); + console.log('Recovered mnemonic account: ', recoveredAccount.addr.toString()); // example: ACCOUNT_RECOVER_MNEMONIC const funder = accounts[0]; @@ -31,30 +31,30 @@ async function main() { signerAccounts.push(algosdk.generateAccount()); // multiSigParams is used when creating the address and when signing transactions - const multiSigParams = { + const multiSigParams: algosdk.MultisigMetadata = { version: 1, threshold: 2, addrs: signerAccounts.map((a) => a.addr), }; const multisigAddr = algosdk.multisigAddress(multiSigParams); - console.log('Created MultiSig Address: ', multisigAddr); + console.log('Created MultiSig Address: ', multisigAddr.toString()); // example: MULTISIG_CREATE const fundMsigTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: funder.addr, - to: multisigAddr, + sender: funder.addr, + receiver: multisigAddr, amount: 1_000_000, suggestedParams, }); await client.sendRawTransaction(fundMsigTxn.signTxn(funder.privateKey)).do(); - await algosdk.waitForConfirmation(client, fundMsigTxn.txID().toString(), 3); + await algosdk.waitForConfirmation(client, fundMsigTxn.txID(), 3); // example: MULTISIG_SIGN const msigTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: multisigAddr, - to: funder.addr, + sender: multisigAddr, + receiver: funder.addr, amount: 100, suggestedParams, }); @@ -74,13 +74,13 @@ async function main() { ).blob; await client.sendRawTransaction(msigWithSecondSig).do(); - await algosdk.waitForConfirmation(client, msigTxn.txID().toString(), 3); + await algosdk.waitForConfirmation(client, msigTxn.txID(), 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 address: ${generatedAccount.addr.toString()}`); console.log(`My passphrase: ${passphrase}`); // example: ACCOUNT_GENERATE @@ -88,32 +88,36 @@ async function main() { // 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, + sender: acct1.addr, + receiver: 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); + await algosdk.waitForConfirmation(client, rekeyTxn.txID(), 3); const acctInfo = await client.accountInformation(acct1.addr).do(); - console.log(`Account Info: ${acctInfo} Auth Addr: ${acctInfo['auth-addr']}`); + console.log( + `Account Info: ${algosdk.stringifyJSON(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, + sender: acct1.addr, + receiver: acct1.addr, amount: 0, suggestedParams, rekeyTo: acct1.addr, }); await client.sendRawTransaction(rekeyBack.signTxn(acct2.privateKey)).do(); - await algosdk.waitForConfirmation(client, rekeyBack.txID().toString(), 3); + await algosdk.waitForConfirmation(client, rekeyBack.txID(), 3); } main(); diff --git a/examples/app.ts b/examples/app.ts index aef7517ea..8d690d9a3 100644 --- a/examples/app.ts +++ b/examples/app.ts @@ -4,7 +4,6 @@ /* 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'; @@ -27,20 +26,16 @@ async function main() { // example: APP_SOURCE // example: APP_COMPILE - const approvalCompileResp = await algodClient - .compile(Buffer.from(approvalProgram)) - .do(); + const approvalCompileResp = await algodClient.compile(approvalProgram).do(); - const compiledApprovalProgram = new Uint8Array( - Buffer.from(approvalCompileResp.result, 'base64') + const compiledApprovalProgram: Uint8Array = algosdk.base64ToBytes( + approvalCompileResp.result ); - const clearCompileResp = await algodClient - .compile(Buffer.from(clearProgram)) - .do(); + const clearCompileResp = await algodClient.compile(clearProgram).do(); - const compiledClearProgram = new Uint8Array( - Buffer.from(clearCompileResp.result, 'base64') + const compiledClearProgram: Uint8Array = algosdk.base64ToBytes( + clearCompileResp.result ); // example: APP_COMPILE @@ -54,9 +49,9 @@ async function main() { // example: APP_CREATE const appCreateTxn = algosdk.makeApplicationCreateTxnFromObject({ - from: creator.addr, - approvalProgram: compiledApprovalProgram, - clearProgram: compiledClearProgram, + sender: creator.addr, + approvalProgram: new Uint8Array(compiledApprovalProgram), + clearProgram: new Uint8Array(compiledClearProgram), numGlobalByteSlices, numGlobalInts, numLocalByteSlices, @@ -71,11 +66,11 @@ async function main() { .do(); const result = await algosdk.waitForConfirmation( algodClient, - appCreateTxn.txID().toString(), + appCreateTxn.txID(), 3 ); // Grab app id from confirmed transaction result - const appId = result['application-index']; + const appId = Number(result.applicationIndex); console.log(`Created app with index: ${appId}`); // example: APP_CREATE @@ -83,7 +78,7 @@ async function main() { // example: APP_OPTIN const appOptInTxn = algosdk.makeApplicationOptInTxnFromObject({ - from: caller.addr, + sender: caller.addr, appIndex: appId, suggestedParams, }); @@ -91,16 +86,12 @@ async function main() { await algodClient .sendRawTransaction(appOptInTxn.signTxn(caller.privateKey)) .do(); - await algosdk.waitForConfirmation( - algodClient, - appOptInTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(algodClient, appOptInTxn.txID(), 3); // example: APP_OPTIN // example: APP_NOOP const appNoOpTxn = algosdk.makeApplicationNoOpTxnFromObject({ - from: caller.addr, + sender: caller.addr, appIndex: appId, suggestedParams, }); @@ -108,17 +99,13 @@ async function main() { await algodClient .sendRawTransaction(appNoOpTxn.signTxn(caller.privateKey)) .do(); - await algosdk.waitForConfirmation( - algodClient, - appNoOpTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(algodClient, appNoOpTxn.txID(), 3); // example: APP_NOOP const anotherCaller = accounts[2]; const anotherAppOptInTxn = algosdk.makeApplicationOptInTxnFromObject({ - from: anotherCaller.addr, + sender: anotherCaller.addr, appIndex: appId, suggestedParams, }); @@ -126,63 +113,68 @@ async function main() { await algodClient .sendRawTransaction(anotherAppOptInTxn.signTxn(anotherCaller.privateKey)) .do(); - await algosdk.waitForConfirmation( - algodClient, - anotherAppOptInTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(algodClient, anotherAppOptInTxn.txID(), 3); // example: APP_CALL const now = new Date().toString(); const simpleAddTxn = algosdk.makeApplicationNoOpTxnFromObject({ - from: caller.addr, + sender: caller.addr, suggestedParams, appIndex: appId, - appArgs: [new Uint8Array(Buffer.from(now))], + appArgs: [algosdk.coerceToBytes(now)], }); await algodClient .sendRawTransaction(simpleAddTxn.signTxn(caller.privateKey)) .do(); - await algosdk.waitForConfirmation( - algodClient, - simpleAddTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(algodClient, simpleAddTxn.txID(), 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') + if (!appInfo.params.globalState || appInfo.params.globalState.length === 0) { + throw new Error('Global state not present'); + } + const { globalState } = appInfo.params; + console.log( + `Raw global state - ${globalState.map((kv) => algosdk.encodeJSON(kv))}` ); - console.log(`Decoded global state - ${globalKey}: ${globalValue}`); + const globalKey = globalState[0].key; + // show global value + const globalValue = globalState[0].value.bytes; + + console.log( + `Decoded global state - ${algosdk.bytesToBase64(globalKey)}: ${algosdk.bytesToBase64(globalValue)}` + ); const accountAppInfo = await algodClient .accountApplicationInformation(caller.addr, appId) .do(); + if ( + !accountAppInfo.appLocalState || + !accountAppInfo.appLocalState.keyValue || + accountAppInfo.appLocalState.keyValue.length === 0 + ) { + throw new Error('Local state values not present'); + } + const localState = accountAppInfo.appLocalState.keyValue; + console.log( + `Raw local state - ${localState.map((kv) => algosdk.encodeJSON(kv))}` + ); - 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(); + const localKey = localState[0].key; // get uint value directly - const localValue = localState.value.uint; + const localValue = localState[0].value.uint; - console.log(`Decoded local state - ${localKey}: ${localValue}`); + console.log( + `Decoded local state - ${algosdk.bytesToBase64(localKey)}: ${localValue}` + ); // example: APP_READ_STATE // example: APP_CLOSEOUT const appCloseOutTxn = algosdk.makeApplicationCloseOutTxnFromObject({ - from: caller.addr, + sender: caller.addr, appIndex: appId, suggestedParams, }); @@ -190,11 +182,7 @@ async function main() { await algodClient .sendRawTransaction(appCloseOutTxn.signTxn(caller.privateKey)) .do(); - await algosdk.waitForConfirmation( - algodClient, - appCloseOutTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(algodClient, appCloseOutTxn.txID(), 3); // example: APP_CLOSEOUT // example: APP_UPDATE @@ -205,27 +193,23 @@ async function main() { const compiledNewProgram = await compileProgram(algodClient, newProgram); const appUpdateTxn = algosdk.makeApplicationUpdateTxnFromObject({ - from: creator.addr, + sender: creator.addr, suggestedParams, appIndex: appId, // updates must define both approval and clear programs, even if unchanged - approvalProgram: compiledNewProgram, - clearProgram: compiledClearProgram, + approvalProgram: new Uint8Array(compiledNewProgram), + clearProgram: new Uint8Array(compiledClearProgram), }); await algodClient .sendRawTransaction(appUpdateTxn.signTxn(creator.privateKey)) .do(); - await algosdk.waitForConfirmation( - algodClient, - appUpdateTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(algodClient, appUpdateTxn.txID(), 3); // example: APP_UPDATE // example: APP_CLEAR const appClearTxn = algosdk.makeApplicationClearStateTxnFromObject({ - from: anotherCaller.addr, + sender: anotherCaller.addr, suggestedParams, appIndex: appId, }); @@ -233,16 +217,12 @@ async function main() { await algodClient .sendRawTransaction(appClearTxn.signTxn(anotherCaller.privateKey)) .do(); - await algosdk.waitForConfirmation( - algodClient, - appClearTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(algodClient, appClearTxn.txID(), 3); // example: APP_CLEAR // example: APP_DELETE const appDeleteTxn = algosdk.makeApplicationDeleteTxnFromObject({ - from: creator.addr, + sender: creator.addr, suggestedParams, appIndex: appId, }); @@ -250,11 +230,7 @@ async function main() { await algodClient .sendRawTransaction(appDeleteTxn.signTxn(creator.privateKey)) .do(); - await algosdk.waitForConfirmation( - algodClient, - appDeleteTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(algodClient, appDeleteTxn.txID(), 3); // example: APP_DELETE } diff --git a/examples/asa.ts b/examples/asa.ts index db30e2946..adc337530 100644 --- a/examples/asa.ts +++ b/examples/asa.ts @@ -19,7 +19,7 @@ async function main() { // example: ASSET_CREATE const suggestedParams = await algodClient.getTransactionParams().do(); const txn = algosdk.makeAssetCreateTxnWithSuggestedParamsFromObject({ - from: creator.addr, + sender: creator.addr, suggestedParams, defaultFrozen: false, unitName: 'rug', @@ -35,24 +35,20 @@ async function main() { const signedTxn = txn.signTxn(creator.privateKey); await algodClient.sendRawTransaction(signedTxn).do(); - const result = await algosdk.waitForConfirmation( - algodClient, - txn.txID().toString(), - 3 - ); + const result = await algosdk.waitForConfirmation(algodClient, txn.txID(), 3); - const assetIndex = result['asset-index']; + const assetIndex = Number(result.assetIndex); 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}`); + console.log(`Asset Params: ${algosdk.stringifyJSON(assetInfo.params)}`); // example: ASSET_INFO // ensure indexer is caught up - await indexerWaitForRound(indexerClient, result['confirmed-round'], 30); + await indexerWaitForRound(indexerClient, result.confirmedRound!, 30); // example: INDEXER_LOOKUP_ASSET const indexerAssetInfo = await indexerClient.lookupAssetByID(assetIndex).do(); @@ -63,7 +59,7 @@ async function main() { const manager = accounts[1]; const configTxn = algosdk.makeAssetConfigTxnWithSuggestedParamsFromObject({ - from: creator.addr, + sender: creator.addr, manager: manager.addr, freeze: manager.addr, clawback: manager.addr, @@ -78,10 +74,10 @@ async function main() { await algodClient.sendRawTransaction(signedConfigTxn).do(); const configResult = await algosdk.waitForConfirmation( algodClient, - txn.txID().toString(), + txn.txID(), 3 ); - console.log(`Result confirmed in round: ${configResult['confirmed-round']}`); + console.log(`Result confirmed in round: ${configResult.confirmedRound}`); // example: ASSET_CONFIG const receiver = accounts[2]; @@ -89,8 +85,8 @@ async function main() { // opt-in is simply a 0 amount transfer of the asset to oneself const optInTxn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ - from: receiver.addr, - to: receiver.addr, + sender: receiver.addr, + receiver: receiver.addr, suggestedParams, assetIndex, amount: 0, @@ -98,13 +94,13 @@ async function main() { const signedOptInTxn = optInTxn.signTxn(receiver.privateKey); await algodClient.sendRawTransaction(signedOptInTxn).do(); - await algosdk.waitForConfirmation(algodClient, optInTxn.txID().toString(), 3); + await algosdk.waitForConfirmation(algodClient, optInTxn.txID(), 3); // example: ASSET_OPTIN // example: ASSET_XFER const xferTxn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ - from: creator.addr, - to: receiver.addr, + sender: creator.addr, + receiver: receiver.addr, suggestedParams, assetIndex, amount: 1, @@ -112,36 +108,32 @@ async function main() { const signedXferTxn = xferTxn.signTxn(creator.privateKey); await algodClient.sendRawTransaction(signedXferTxn).do(); - await algosdk.waitForConfirmation(algodClient, xferTxn.txID().toString(), 3); + await algosdk.waitForConfirmation(algodClient, xferTxn.txID(), 3); // example: ASSET_XFER // example: ASSET_FREEZE const freezeTxn = algosdk.makeAssetFreezeTxnWithSuggestedParamsFromObject({ - from: manager.addr, + sender: manager.addr, suggestedParams, assetIndex, - // freezeState: false would unfreeze the account's asset holding - freezeState: true, + // frozen: false would unfreeze the account's asset holding + frozen: 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 - ); + await algosdk.waitForConfirmation(algodClient, freezeTxn.txID(), 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, + sender: manager.addr, + receiver: creator.addr, + // assetSender is the account that is being clawed back from + assetSender: receiver.addr, suggestedParams, assetIndex, amount: 1, @@ -150,11 +142,7 @@ async function main() { const signedClawbackTxn = clawbackTxn.signTxn(manager.privateKey); await algodClient.sendRawTransaction(signedClawbackTxn).do(); - await algosdk.waitForConfirmation( - algodClient, - clawbackTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(algodClient, clawbackTxn.txID(), 3); // example: ASSET_CLAWBACK // example: ASSET_OPT_OUT @@ -163,8 +151,8 @@ async function main() { // 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, + sender: receiver.addr, + receiver: creator.addr, closeRemainderTo: creator.addr, suggestedParams, assetIndex, @@ -173,27 +161,19 @@ async function main() { const signedOptOutTxn = optOutTxn.signTxn(receiver.privateKey); await algodClient.sendRawTransaction(signedOptOutTxn).do(); - await algosdk.waitForConfirmation( - algodClient, - optOutTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(algodClient, optOutTxn.txID(), 3); // example: ASSET_OPT_OUT // example: ASSET_DELETE const deleteTxn = algosdk.makeAssetDestroyTxnWithSuggestedParamsFromObject({ - from: manager.addr, + sender: manager.addr, suggestedParams, assetIndex, }); const signedDeleteTxn = deleteTxn.signTxn(manager.privateKey); await algodClient.sendRawTransaction(signedDeleteTxn).do(); - await algosdk.waitForConfirmation( - algodClient, - deleteTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(algodClient, deleteTxn.txID(), 3); // example: ASSET_DELETE } diff --git a/examples/atc.ts b/examples/atc.ts index eccac7107..4a7e07fd5 100644 --- a/examples/atc.ts +++ b/examples/atc.ts @@ -4,7 +4,6 @@ /* 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'; @@ -28,7 +27,7 @@ async function main() { const compiledClearProgram = await compileProgram(client, clearProgram); const createTxn = algosdk.makeApplicationCreateTxnFromObject({ - from: sender.addr, + sender: sender.addr, suggestedParams, onComplete: algosdk.OnApplicationComplete.NoOpOC, approvalProgram: compiledApprovalProgram, @@ -43,10 +42,10 @@ async function main() { await client.sendRawTransaction(createTxn.signTxn(sender.privateKey)).do(); const response = await algosdk.waitForConfirmation( client, - createTxn.txID().toString(), + createTxn.txID(), 3 ); - const appIndex = response['application-index']; + const appIndex = Number(response.applicationIndex); // example: ATC_CREATE const atc = new algosdk.AtomicTransactionComposer(); @@ -62,9 +61,9 @@ async function main() { // example: ATC_ADD_TRANSACTION // construct a transaction const paymentTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: sender.addr, + sender: sender.addr, suggestedParams, - to: sender.addr, + receiver: sender.addr, amount: 1000, }); @@ -99,7 +98,7 @@ async function main() { // example: ATC_BOX_REF const boxATC = new algosdk.AtomicTransactionComposer(); - const boxKey = new Uint8Array(Buffer.from('key')); + const boxKey = algosdk.coerceToBytes('key'); boxATC.addMethodCall({ appID: appIndex, method: boxAccessorMethod, diff --git a/examples/atomics.ts b/examples/atomics.ts index ed4b919ab..0c0b1e40a 100644 --- a/examples/atomics.ts +++ b/examples/atomics.ts @@ -16,15 +16,15 @@ async function main() { const suggestedParams = await client.getTransactionParams().do(); const alicesTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: acct1.addr, - to: acct2.addr, + sender: acct1.addr, + receiver: acct2.addr, amount: 1e6, suggestedParams, }); const bobsTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: acct2.addr, - to: acct1.addr, + sender: acct2.addr, + receiver: acct1.addr, amount: 1e6, suggestedParams, }); @@ -47,21 +47,22 @@ async function main() { // example: ATOMIC_GROUP_SEND await client.sendRawTransaction(signedTxns).do(); - await algosdk.waitForConfirmation(client, alicesTxn.txID().toString(), 3); + await algosdk.waitForConfirmation(client, alicesTxn.txID(), 3); // example: ATOMIC_GROUP_SEND // example: CONST_MIN_FEE - const minFee = algosdk.ALGORAND_MIN_TX_FEE; + // This SDK does not expose a constant for the minimum fee // example: CONST_MIN_FEE // example: TRANSACTION_FEE_OVERRIDE const sp = await client.getTransactionParams().do(); - sp.fee = 2 * minFee; + sp.fee = BigInt(2) * sp.minFee; sp.flatFee = true; // example: TRANSACTION_FEE_OVERRIDE // example: SP_MIN_FEE - // Not supported because getTransactionParams erases the information + const params = await client.getTransactionParams().do(); + console.log(params.minFee); // example: SP_MIN_FEE } diff --git a/examples/block_fetcher/index.ts b/examples/block_fetcher/index.ts index 695c732fc..786d28a99 100644 --- a/examples/block_fetcher/index.ts +++ b/examples/block_fetcher/index.ts @@ -29,7 +29,7 @@ function removeNulls(obj) { while (true) { // Get latest round number - let lastRound = status['last-round']; + let lastRound = Number(status.lastRound); console.log(`Round: ${lastRound}`); // Fetch block diff --git a/examples/codec.ts b/examples/codec.ts index c74b9b640..3ba74c7ed 100644 --- a/examples/codec.ts +++ b/examples/codec.ts @@ -2,9 +2,8 @@ /* 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'; +import { getLocalAccounts, getLocalAlgodClient } from './utils'; async function main() { const client = getLocalAlgodClient(); @@ -15,14 +14,13 @@ async function main() { // example: CODEC_ADDRESS const address = '4H5UNRBJ2Q6JENAXQ6HNTGKLKINP4J4VTQBEPK5F3I6RDICMZBPGNH6KD4'; - const pk = algosdk.decodeAddress(address); - const addr = algosdk.encodeAddress(pk.publicKey); + const addr = algosdk.Address.fromString(address); console.log(address, addr); // example: CODEC_ADDRESS // example: CODEC_BASE64 const b64Encoded = 'SGksIEknbSBkZWNvZGVkIGZyb20gYmFzZTY0'; - const b64Decoded = Buffer.from(b64Encoded, 'base64').toString(); + const b64Decoded = algosdk.base64ToBytes(b64Encoded); console.log(b64Encoded, b64Decoded); // example: CODEC_BASE64 @@ -36,26 +34,26 @@ async function main() { // example: CODEC_TRANSACTION_UNSIGNED const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: sender.addr, - to: receiver.addr, + sender: sender.addr, + receiver: receiver.addr, amount: 1e6, suggestedParams, }); const txnBytes = algosdk.encodeUnsignedTransaction(txn); - const txnB64 = Buffer.from(txnBytes).toString('base64'); + const txnB64 = algosdk.bytesToBase64(txnBytes); // ... const restoredTxn = algosdk.decodeUnsignedTransaction( - Buffer.from(txnB64, 'base64') + algosdk.base64ToBytes(txnB64) ); 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 signedB64Txn = algosdk.bytesToBase64(signedTxn); const restoredSignedTxn = algosdk.decodeSignedTransaction( - Buffer.from(signedB64Txn, 'base64') + algosdk.base64ToBytes(signedB64Txn) ); console.log(restoredSignedTxn); // example: CODEC_TRANSACTION_SIGNED @@ -65,7 +63,7 @@ async function main() { const stringTupleData = ['hello', 'world']; const encodedTuple = stringTupleCodec.encode(stringTupleData); - console.log(Buffer.from(encodedTuple).toString('hex')); + console.log(algosdk.bytesToHex(encodedTuple)); const decodedTuple = stringTupleCodec.decode(encodedTuple); console.log(decodedTuple); // ['hello', 'world'] @@ -74,7 +72,7 @@ async function main() { const uintArrayData = [1, 2, 3, 4, 5]; const encodedArray = uintArrayCodec.encode(uintArrayData); - console.log(Buffer.from(encodedArray).toString('hex')); + console.log(algosdk.bytesToHex(encodedArray)); const decodeArray = uintArrayCodec.decode(encodedArray); console.log(decodeArray); // [1, 2, 3, 4, 5] diff --git a/examples/debug.ts b/examples/debug.ts index ed5515bf2..b7c580592 100644 --- a/examples/debug.ts +++ b/examples/debug.ts @@ -43,14 +43,13 @@ async function main() { txns: signedTxns, }); - console.log('Dryrun:', dryrunRequest.get_obj_for_encoding()); + console.log('Dryrun:', dryrunRequest); // 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); + console.log('Txn:', txn); }); // example: DEBUG_DRYRUN_SUBMIT } diff --git a/examples/indexer.ts b/examples/indexer.ts index 27ccb9e71..9610773de 100644 --- a/examples/indexer.ts +++ b/examples/indexer.ts @@ -2,7 +2,6 @@ /* eslint-disable import/no-unresolved */ /* eslint-disable no-promise-executor-return */ /* eslint-disable no-console */ -import { Buffer } from 'buffer'; import { getLocalIndexerClient, getLocalAccounts, @@ -40,7 +39,7 @@ async function main() { // example: INDEXER_SEARCH_MIN_AMOUNT // example: INDEXER_PAGINATE_RESULTS - let nextToken = ''; + let nextToken: string | undefined = ''; // nextToken will be undefined if we reached the last page while (nextToken !== undefined) { @@ -52,7 +51,7 @@ async function main() { .nextToken(nextToken) .do(); - nextToken = response['next-token']; + nextToken = response.nextToken; const txns = response.transactions; if (txns.length > 0) console.log(`Transaction IDs: ${response.transactions.map((t) => t.id)}`); @@ -66,33 +65,28 @@ async function main() { const sender = accounts[0]; const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: sender.addr, - to: sender.addr, + sender: sender.addr, + receiver: sender.addr, amount: 1e6, - note: new Uint8Array(Buffer.from('Hello World!')), + note: algosdk.coerceToBytes('Hello World!'), suggestedParams, }); await client.sendRawTransaction(txn.signTxn(sender.privateKey)).do(); - const result = await algosdk.waitForConfirmation( - client, - txn.txID().toString(), - 3 - ); + const result = await algosdk.waitForConfirmation(client, txn.txID(), 3); // ensure indexer is caught up - await indexerWaitForRound(indexerClient, result['confirmed-round'], 30); + await indexerWaitForRound(indexerClient, result.confirmedRound!, 30); // example: INDEXER_PREFIX_SEARCH const txnsWithNotePrefix = await indexerClient .searchForTransactions() - .notePrefix(Buffer.from('Hello')) + .notePrefix(algosdk.coerceToBytes('Hello')) .do(); console.log( - `Transactions with note prefix "Hello" ${JSON.stringify( + `Transactions with note prefix "Hello" ${algosdk.encodeJSON( txnsWithNotePrefix, - undefined, - 2 + { space: 2 } )}` ); // example: INDEXER_PREFIX_SEARCH diff --git a/examples/kmd.ts b/examples/kmd.ts index 42d525f3e..a70b5d5c8 100644 --- a/examples/kmd.ts +++ b/examples/kmd.ts @@ -56,7 +56,7 @@ async function main() { // example: KMD_IMPORT_ACCOUNT const newAccount = algosdk.generateAccount(); - console.log('Account: ', newAccount.addr); + console.log('Account: ', newAccount.addr.toString()); const importedAccount = await kmdClient.importKey( wallethandle, newAccount.sk diff --git a/examples/lsig.ts b/examples/lsig.ts index 33140ef92..ecbb609ae 100644 --- a/examples/lsig.ts +++ b/examples/lsig.ts @@ -2,9 +2,8 @@ /* 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'; +import { getLocalAccounts, getLocalAlgodClient } from './utils'; async function main() { const client = getLocalAlgodClient(); @@ -14,7 +13,7 @@ async function main() { // example: LSIG_COMPILE const smartSigSource = '#pragma version 8\nint 1\nreturn'; // approve everything - const result = await client.compile(Buffer.from(smartSigSource)).do(); + const result = await client.compile(smartSigSource).do(); // Hash is equivalent to the contract address console.log('Hash: ', result.hash); @@ -23,22 +22,17 @@ async function main() { // example: LSIG_COMPILE // example: LSIG_INIT - let smartSig = new algosdk.LogicSig( - new Uint8Array(Buffer.from(b64program, 'base64')) - ); + let smartSig = new algosdk.LogicSig(algosdk.base64ToBytes(b64program)); // 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 - ); + const args = [algosdk.coerceToBytes('This is an argument!')]; + smartSig = new algosdk.LogicSig(algosdk.base64ToBytes(b64program), args); // example: LSIG_PASS_ARGS const fundSmartSigTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: funder.addr, - to: smartSig.address(), + sender: funder.addr, + receiver: smartSig.address(), amount: 1e6, suggestedParams, }); @@ -46,16 +40,12 @@ async function main() { await client .sendRawTransaction(fundSmartSigTxn.signTxn(funder.privateKey)) .do(); - await algosdk.waitForConfirmation( - client, - fundSmartSigTxn.txID().toString(), - 3 - ); + await algosdk.waitForConfirmation(client, fundSmartSigTxn.txID(), 3); // example: LSIG_SIGN_FULL const smartSigTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: smartSig.address(), - to: funder.addr, + sender: smartSig.address(), + receiver: funder.addr, amount: 0.1e6, suggestedParams, }); @@ -76,8 +66,8 @@ async function main() { smartSig.sign(userAccount.privateKey); const delegatedTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: userAccount.addr, - to: funder.addr, + sender: userAccount.addr, + receiver: funder.addr, amount: 0.1e6, suggestedParams, }); diff --git a/examples/overview.ts b/examples/overview.ts index 0bec40744..d042ecb51 100644 --- a/examples/overview.ts +++ b/examples/overview.ts @@ -1,4 +1,3 @@ -import { Buffer } from 'buffer'; import algosdk from '../src'; import { getLocalAccounts, getLocalAlgodClient } from './utils'; @@ -23,11 +22,11 @@ async function main() { // example: TRANSACTION_PAYMENT_CREATE const suggestedParams = await algodClient.getTransactionParams().do(); const ptxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: acct.addr, + sender: acct.addr, suggestedParams, - to: acct2.addr, + receiver: acct2.addr, amount: 10000, - note: new Uint8Array(Buffer.from('hello world')), + note: algosdk.coerceToBytes('hello world'), }); // example: TRANSACTION_PAYMENT_CREATE @@ -36,11 +35,13 @@ async function main() { // example: TRANSACTION_PAYMENT_SIGN // example: TRANSACTION_PAYMENT_SUBMIT - const { txId } = await algodClient.sendRawTransaction(signedTxn).do(); - const result = await algosdk.waitForConfirmation(algodClient, txId, 4); + 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()}`); + console.log(`Transaction Information: ${algosdk.stringifyJSON(result.txn)}`); + console.log( + `Decoded Note: ${new TextDecoder('utf-8').decode(result.txn.txn.note)}` + ); // example: TRANSACTION_PAYMENT_SUBMIT // example: ALGOD_FETCH_ACCOUNT_INFO diff --git a/examples/participation.ts b/examples/participation.ts index f5fd2ae61..68d885d6f 100644 --- a/examples/participation.ts +++ b/examples/participation.ts @@ -11,47 +11,50 @@ async function main() { // Parent addr const addr = 'MWAPNXBDFFD2V5KWXAHWKBO7FO4JN36VR4CIBDKDDE7WAUAGZIXM3QPJW4'; // VRF public key - const selectionKey = 'LrpLhvzr+QpN/bivh6IPpOaKGbGzTTB5lJtVfixmmgk='; + const selectionKey = algosdk.base64ToBytes( + 'LrpLhvzr+QpN/bivh6IPpOaKGbGzTTB5lJtVfixmmgk=' + ); // Voting pub key - const voteKey = 'G/lqTV6MKspW6J8wH2d8ZliZ5XZVZsruqSBJMwLwlmo='; + const voteKey = algosdk.base64ToBytes( + 'G/lqTV6MKspW6J8wH2d8ZliZ5XZVZsruqSBJMwLwlmo=' + ); // State proof key - const stateProofKey = - 'RpUpNWfZMjZ1zOOjv3MF2tjO714jsBt0GKnNsw0ihJ4HSZwci+d9zvUi3i67LwFUJgjQ5Dz4zZgHgGduElnmSA=='; + const stateProofKey = algosdk.base64ToBytes( + 'RpUpNWfZMjZ1zOOjv3MF2tjO714jsBt0GKnNsw0ihJ4HSZwci+d9zvUi3i67LwFUJgjQ5Dz4zZgHgGduElnmSA==' + ); - // sets up keys for 100000 rounds - const numRounds = 1e5; + // sets up keys for 100,000 rounds + const numRounds = 100_000; // dilution default is sqrt num rounds - const keyDilution = numRounds ** 0.5; + const keyDilution = Math.floor(Math.sqrt(numRounds)); // create transaction - const onlineKeyreg = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject( - { - from: addr, + const onlineKeyreg = + algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ + sender: addr, voteKey, selectionKey, stateProofKey, - voteFirst: params.firstRound, - voteLast: params.firstRound + numRounds, + voteFirst: params.firstValid, + voteLast: params.firstValid + BigInt(numRounds), voteKeyDilution: keyDilution, suggestedParams: params, - } - ); + }); - console.log(onlineKeyreg.get_obj_for_encoding()); + console.log(onlineKeyreg); // 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, + const offlineKeyReg = + algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ + sender: addr, suggestedParams, - } - ); - console.log(offlineKeyReg.get_obj_for_encoding()); + }); + console.log(offlineKeyReg); // example: TRANSACTION_KEYREG_OFFLINE_CREATE } diff --git a/examples/smoke_test.sh b/examples/smoke_test.sh index 405b7d9dc..0a2c643a8 100755 --- a/examples/smoke_test.sh +++ b/examples/smoke_test.sh @@ -11,7 +11,7 @@ for file in *; do # 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" + ../node_modules/.bin/tsx "$file" # Check if the test failed if [ $? -ne 0 ]; then echo "Test failed, stopping script" diff --git a/examples/utils.ts b/examples/utils.ts index 809d46b8a..f51d8c559 100644 --- a/examples/utils.ts +++ b/examples/utils.ts @@ -1,16 +1,13 @@ 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') - ); + const compileResponse = await client.compile(programSource).do(); + const compiledBytes = algosdk.base64ToBytes(compileResponse.result); return compiledBytes; } @@ -46,6 +43,7 @@ export function getLocalAlgodClient() { } function sleep(ms: number) { + // eslint-disable-next-line no-promise-executor-return return new Promise((resolve) => setTimeout(resolve, ms)); } @@ -54,7 +52,7 @@ export async function indexerWaitForRound( round: number | bigint, maxAttempts: number ) { - let indexerRound = 0; + let indexerRound = BigInt(0); let attempts = 0; for (;;) { @@ -81,7 +79,7 @@ export async function indexerWaitForRound( } export interface SandboxAccount { - addr: string; + addr: algosdk.Address; privateKey: Uint8Array; signer: algosdk.TransactionSigner; } @@ -105,7 +103,7 @@ export async function getLocalAccounts(): Promise { const addresses = await kmdClient.listKeys(handle); // eslint-disable-next-line camelcase - const acctPromises: Promise<{ private_key: Buffer }>[] = []; + const acctPromises: Promise<{ private_key: Uint8Array }>[] = []; // eslint-disable-next-line no-restricted-syntax for (const addr of addresses.addresses) { @@ -117,8 +115,8 @@ export async function getLocalAccounts(): Promise { 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 addr = new algosdk.Address(k.private_key.slice(32)); + const acct: algosdk.Account = { sk: k.private_key, addr }; const signer = algosdk.makeBasicAccountTransactionSigner(acct); return { @@ -146,7 +144,7 @@ export async function deployCalculatorApp( const clearBin = await compileProgram(algodClient, clearProgram); const suggestedParams = await algodClient.getTransactionParams().do(); const appCreateTxn = algosdk.makeApplicationCreateTxnFromObject({ - from: creator.addr, + sender: creator.addr, approvalProgram: approvalBin, clearProgram: clearBin, numGlobalByteSlices: 0, @@ -163,9 +161,9 @@ export async function deployCalculatorApp( const result = await algosdk.waitForConfirmation( algodClient, - appCreateTxn.txID().toString(), + appCreateTxn.txID(), 3 ); - const appId = result['application-index']; + const appId = Number(result.applicationIndex); return appId; } diff --git a/package-lock.json b/package-lock.json index 970294906..a91f2a894 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "algosdk", "version": "2.9.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -9,8 +9,7 @@ "version": "2.9.0", "license": "MIT", "dependencies": { - "algo-msgpack-with-bigint": "^2.1.1", - "buffer": "^6.0.3", + "algorand-msgpack": "^1.1.0", "hi-base32": "^0.5.1", "js-sha256": "^0.9.0", "js-sha3": "^0.8.0", @@ -22,34 +21,36 @@ "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", + "@types/node": "^20.11.5", + "@typescript-eslint/eslint-plugin": "^6.18.1", + "@typescript-eslint/parser": "^6.18.1", "assert": "^2.0.0", - "chromedriver": "^122.0.3", + "chromedriver": "^126.0.3", "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": "8.22.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^9.1.0", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-import": "^2.25.2", "eslint-plugin-tsdoc": "^0.2.11", - "express": "^4.17.1", - "geckodriver": "^3.0.1", + "express": "^4.19.2", + "geckodriver": "^4.3.0", "husky": "^4.3.8", "lint-staged": "^10.5.4", "mocha": "^9.0.0", "mock-http-server": "^1.4.3", - "prettier": "2.2.1", + "prettier": "^3.2.1", "selenium-webdriver": "^4.10.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", + "tsx": "^4.7.0", + "typedoc": "^0.25.7", + "typedoc-plugin-missing-exports": "^2.1.0", + "typedoc-plugin-rename-defaults": "^0.7.0", + "typescript": "^5.3.3", + "webpack": "^5.89.0", "webpack-cli": "^5.0.1" }, "engines": { @@ -170,47 +171,143 @@ "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==", + "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/@esbuild/darwin-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz", + "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "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==", + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", "dev": true, "engines": { - "node": ">=10.0.0" + "node": "^12.0.0 || ^14.0.0 || >=16.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==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", "dev": true, "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/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/@humanwhocodes/config-array": { + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz", + "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -323,9 +420,9 @@ } }, "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==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -335,30 +432,6 @@ "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.4", "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.4.tgz", @@ -371,42 +444,6 @@ "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "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", @@ -433,12 +470,6 @@ "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", @@ -446,9 +477,9 @@ "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==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/json5": { @@ -457,15 +488,6 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "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", @@ -473,10 +495,13 @@ "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 + "version": "20.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", + "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -484,14 +509,11 @@ "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/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true }, "node_modules/@types/yauzl": { "version": "2.10.0", @@ -504,30 +526,33 @@ } }, "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==", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz", + "integrity": "sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==", "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" + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/type-utils": "6.18.1", + "@typescript-eslint/utils": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.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" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -535,50 +560,44 @@ } } }, - "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==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "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" + "ms": "2.1.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.0" }, - "peerDependencies": { - "eslint": "*" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "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==", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.1.tgz", + "integrity": "sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==", "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" + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -586,129 +605,311 @@ } } }, - "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==", + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.26.1", - "@typescript-eslint/visitor-keys": "4.26.1" + "ms": "2.1.2" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": ">=6.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "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==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz", + "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==", "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1" + }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^16.0.0 || >=18.0.0" }, "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==", + "node_modules/@typescript-eslint/type-utils": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.1.tgz", + "integrity": "sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==", "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" + "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/utils": "6.18.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, "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==", + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.26.1", - "eslint-visitor-keys": "^2.0.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz", + "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==", + "dev": true, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^16.0.0 || >=18.0.0" }, "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==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz", + "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==", "dev": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.5", - "@webassemblyjs/helper-wasm-bytecode": "1.11.5" + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "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==", + "node_modules/@typescript-eslint/typescript-estree/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": { - "@webassemblyjs/floating-point-hex-parser": "1.11.5", - "@webassemblyjs/helper-api-error": "1.11.5", - "@xtuc/long": "4.2.2" + "balanced-match": "^1.0.0" } }, - "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==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz", + "integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz", + "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "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/@wdio/logger": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.28.0.tgz", + "integrity": "sha512-/s6zNCqwy1hoc+K4SJypis0Ud0dlJ+urOelJFO1x0G0rwDRWyFiUP6ijTaCcFxAm29jYEcEPWijl2xkVIHwOyA==", + "dev": true, + "dependencies": { + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/logger/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@wdio/logger/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/logger/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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": { @@ -888,9 +1089,9 @@ } }, "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "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" @@ -899,43 +1100,51 @@ "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==", + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "acorn": "^8" } }, - "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==", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "engines": { - "node": ">=0.4.0" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.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==", + "node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, "engines": { - "node": ">=6.0" + "node": ">= 14" } }, - "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==", + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "debug": "4" + "ms": "2.1.2" }, "engines": { - "node": ">= 6.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/aggregate-error": { @@ -985,12 +1194,12 @@ "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==", + "node_modules/algorand-msgpack": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/algorand-msgpack/-/algorand-msgpack-1.1.0.tgz", + "integrity": "sha512-08k7pBQnkaUB5p+jL7f1TRaUIlTSDE0cesFu1mD7llLao+1cAhtvvZmGE3OnisTd0xOn118QMw74SRqddqaYvw==", "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/ansi-colors": { @@ -1038,6 +1247,12 @@ "node": ">=8" } }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1072,21 +1287,6 @@ "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-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", @@ -1258,35 +1458,22 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/b4a": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", + "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", + "dev": true + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "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/basic-ftp": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", - "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "dev": true, "engines": { "node": ">=10.0.0" @@ -1298,6 +1485,15 @@ "integrity": "sha1-Qpzuu/pffpNueNc/vcfacWKyDiA=", "dev": true }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/bignumber.js": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", @@ -1306,6 +1502,19 @@ "node": "*" } }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1322,13 +1531,13 @@ "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==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -1336,7 +1545,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -1432,29 +1641,6 @@ "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", @@ -1470,40 +1656,31 @@ "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==", + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", "dev": true, "engines": { - "node": ">= 0.8" + "node": ">=0.10" } }, - "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==", + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", "dev": true, "engines": { - "node": ">=10.6.0" + "node": ">=0.2.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==", + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "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": ">= 0.8" } }, "node_modules/call-bind": { @@ -1550,6 +1727,18 @@ "url": "https://opencollective.com/browserslist" } }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", @@ -1605,28 +1794,19 @@ "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==", + "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": "122.0.3", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-122.0.3.tgz", - "integrity": "sha512-f7TcCYM6tPxQAl4NQ4KckZ55j62RUfUswbl2iEScs+gI1cqRhzacjMR/FiFx3LUa4S/EZIBgnCx9L+JDhIzVpw==", + "version": "126.0.3", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-126.0.3.tgz", + "integrity": "sha512-4o+ZK8926/8lqIlnnvcljCHV88Z8IguEMB5PInOiS9/Lb6cyeZSj2Uvz+ky1Jgyw2Bn7qCLJFfbUslaWnvUUbg==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -1763,18 +1943,6 @@ "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", @@ -1929,18 +2097,18 @@ ] }, "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==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "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==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true, "engines": { "node": ">= 0.6" @@ -1982,12 +2150,6 @@ "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-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -2084,12 +2246,12 @@ } }, "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "dev": true, "engines": { - "node": ">= 14" + "node": ">= 12" } }, "node_modules/date-fns": { @@ -2134,33 +2296,6 @@ "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", @@ -2173,15 +2308,6 @@ "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.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -2273,6 +2399,15 @@ "node": ">=6.0.0" } }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, "node_modules/duration": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", @@ -2320,9 +2455,9 @@ } }, "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==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -2506,6 +2641,44 @@ "ext": "^1.1.2" } }, + "node_modules/esbuild": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz", + "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.11", + "@esbuild/android-arm": "0.19.11", + "@esbuild/android-arm64": "0.19.11", + "@esbuild/android-x64": "0.19.11", + "@esbuild/darwin-arm64": "0.19.11", + "@esbuild/darwin-x64": "0.19.11", + "@esbuild/freebsd-arm64": "0.19.11", + "@esbuild/freebsd-x64": "0.19.11", + "@esbuild/linux-arm": "0.19.11", + "@esbuild/linux-arm64": "0.19.11", + "@esbuild/linux-ia32": "0.19.11", + "@esbuild/linux-loong64": "0.19.11", + "@esbuild/linux-mips64el": "0.19.11", + "@esbuild/linux-ppc64": "0.19.11", + "@esbuild/linux-riscv64": "0.19.11", + "@esbuild/linux-s390x": "0.19.11", + "@esbuild/linux-x64": "0.19.11", + "@esbuild/netbsd-x64": "0.19.11", + "@esbuild/openbsd-x64": "0.19.11", + "@esbuild/sunos-x64": "0.19.11", + "@esbuild/win32-arm64": "0.19.11", + "@esbuild/win32-ia32": "0.19.11", + "@esbuild/win32-x64": "0.19.11" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2571,48 +2744,48 @@ } }, "node_modules/eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz", + "integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==", "dev": true, "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.2", + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "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", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.3", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "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", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -2620,34 +2793,44 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.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==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", "dev": true, "dependencies": { "confusing-browser-globals": "^1.0.10", "object.assign": "^4.1.2", - "object.entries": "^1.1.2" + "object.entries": "^1.1.5", + "semver": "^6.3.0" }, "engines": { - "node": ">= 6" + "node": "^10.12.0 || >=12.0.0" }, "peerDependencies": { - "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", - "eslint-plugin-import": "^2.22.1" + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-config-airbnb-base/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" } }, "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==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -2676,6 +2859,48 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz", + "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "enhanced-resolve": "^5.12.0", + "eslint-module-utils": "^2.7.4", + "fast-glob": "^3.3.1", + "get-tsconfig": "^4.5.0", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, + "node_modules/eslint-import-resolver-typescript/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/eslint-module-utils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", @@ -2802,7 +3027,7 @@ "eslint": ">=5" } }, - "node_modules/eslint-visitor-keys": { + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", @@ -2811,6 +3036,24 @@ "node": ">=10" } }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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/eslint/node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2825,6 +3068,23 @@ "node": ">= 8" } }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "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", @@ -2837,28 +3097,123 @@ "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==", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "url": "https://opencollective.com/eslint" } }, - "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==", + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { - "node": ">=4" + "node": ">=4.0" + } + }, + "node_modules/eslint/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/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/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/eslint/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/eslint/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/eslint/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/eslint/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/eslint/node_modules/path-key": { @@ -2907,26 +3262,20 @@ } }, "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==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", + "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", "dev": true, "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.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": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esprima": { @@ -2943,9 +3292,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -2955,9 +3304,9 @@ } }, "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==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -3112,17 +3461,17 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -3286,21 +3635,26 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "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==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -3325,9 +3679,9 @@ } }, "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==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -3342,6 +3696,29 @@ "pend": "~1.2.0" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -3497,6 +3874,18 @@ "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -3529,18 +3918,6 @@ "node": ">=14.14" } }, - "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", @@ -3548,9 +3925,9 @@ "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==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -3561,6 +3938,33 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3601,23 +4005,62 @@ } }, "node_modules/geckodriver": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-3.2.0.tgz", - "integrity": "sha512-p+qR2RKlI/TQoCEYrSuTaYCLqsJNni96WmEukTyXmOmLn+3FLdgPAEwMZ0sG2Cwi9hozUzGAWyT6zLuhF6cpiQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.3.0.tgz", + "integrity": "sha512-QfpvxFsMORwKpvnLslkHCr3NTCczHAvkte6+pQGsiUZXKBe6mO4TTb727b+9KMVSK6XZqhR6ZwImKdP+F5vS6A==", "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" + "@wdio/logger": "^8.24.12", + "decamelize": "^6.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "node-fetch": "^3.3.2", + "tar-fs": "^3.0.4", + "unzipper": "^0.10.14", + "which": "^4.0.0" }, "bin": { - "geckodriver": "bin/geckodriver" + "geckodriver": "bin/geckodriver.js" }, "engines": { - "node": ">=12.0.0" + "node": "^16.13 || >=18 || >=20" + } + }, + "node_modules/geckodriver/node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/geckodriver/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/geckodriver/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/get-caller-file": { @@ -3681,6 +4124,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/get-uri": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", @@ -3696,6 +4151,15 @@ "node": ">= 14" } }, + "node_modules/get-uri/node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/get-uri/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3762,9 +4226,9 @@ "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==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3792,16 +4256,16 @@ } }, "node_modules/globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "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", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -3811,15 +4275,6 @@ "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/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -3832,37 +4287,24 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -3967,12 +4409,6 @@ "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==" }, - "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", @@ -4029,18 +4465,6 @@ "node": ">= 14" } }, - "node_modules/http-proxy-agent/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/http-proxy-agent/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4058,30 +4482,17 @@ } } }, - "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==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "dev": true, "dependencies": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/human-signals": { @@ -4217,29 +4628,10 @@ "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==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -4438,12 +4830,6 @@ "node": ">= 12" } }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true - }, "node_modules/ip-regex": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", @@ -4904,19 +5290,6 @@ "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/jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", @@ -4931,12 +5304,6 @@ "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-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", @@ -4968,9 +5335,9 @@ } }, "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==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, "node_modules/jsonfile": { @@ -4997,15 +5364,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", @@ -5099,6 +5457,12 @@ "node": ">=0.6.19" } }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true + }, "node_modules/listr2": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.10.0.tgz", @@ -5135,24 +5499,12 @@ "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", @@ -5232,21 +5584,31 @@ "node": ">=8" } }, + "node_modules/loglevel": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", + "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/loglevel-plugin-prefix": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", + "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", + "dev": true + }, "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", @@ -5265,16 +5627,10 @@ "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==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -5377,15 +5733,6 @@ "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", @@ -5407,43 +5754,24 @@ "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==", + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "minimist": "^1.2.6" }, - "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/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, "node_modules/mocha": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", @@ -5794,31 +6122,56 @@ "lower-case": "^1.1.1" } }, - "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-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==", + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "dev": true, - "engines": { - "node": ">=10" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "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-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/npm-run-path": { @@ -5904,14 +6257,14 @@ } }, "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==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" @@ -5996,15 +6349,6 @@ "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-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -6039,18 +6383,6 @@ "node": ">= 14" } }, - "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/pac-proxy-agent/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -6068,19 +6400,6 @@ } } }, - "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/pac-resolver": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", @@ -6227,15 +6546,18 @@ } }, "node_modules/prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.1.tgz", + "integrity": "sha512-qSUWshj1IobVbKc226Gw2pync27t0Kf0EdufZa9j7uBSJay1CC+B3K5lAAZoqgX3ASiKuWsk6OmzKRetXNObWg==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/process-nextick-args": { @@ -6285,18 +6607,6 @@ "node": ">= 14" } }, - "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/proxy-agent/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -6314,19 +6624,6 @@ } } }, - "node_modules/proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/proxy-agent/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -6396,17 +6693,11 @@ } ] }, - "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/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true }, "node_modules/random-bytes": { "version": "1.0.0", @@ -6436,9 +6727,9 @@ } }, "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==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "dependencies": { "bytes": "3.1.2", @@ -6513,9 +6804,9 @@ } }, "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, "engines": { "node": ">=8" @@ -6542,15 +6833,6 @@ "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.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", @@ -6568,12 +6850,6 @@ "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", @@ -6604,16 +6880,13 @@ "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==", + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, - "dependencies": { - "lowercase-keys": "^2.0.0" - }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, "node_modules/restore-cursor": { @@ -6716,9 +6989,9 @@ "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==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.8", @@ -6754,9 +7027,9 @@ } }, "node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6940,14 +7213,15 @@ } }, "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==", + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", "dev": true, "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" } }, "node_modules/side-channel": { @@ -7031,18 +7305,6 @@ "node": ">= 14" } }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/socks-proxy-agent/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -7137,9 +7399,9 @@ "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=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true }, "node_modules/stack-chain": { @@ -7193,6 +7455,16 @@ "node": ">= 0.6" } }, + "node_modules/streamx": { + "version": "2.15.6", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz", + "integrity": "sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==", + "dev": true, + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -7314,12 +7586,12 @@ } }, "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==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -7382,76 +7654,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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", @@ -7461,21 +7663,26 @@ "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==", + "node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", "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" + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "node_modules/tar-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", + "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, "node_modules/tcp-port-used": { @@ -7559,18 +7766,6 @@ "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", @@ -7647,6 +7842,15 @@ "node": ">=0.6" } }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -7656,16 +7860,29 @@ "tree-kill": "cli.js" } }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "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==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.0.tgz", + "integrity": "sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg==", "dev": true, "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", - "semver": "^7.3.4" + "semver": "^7.3.4", + "source-map": "^0.7.4" }, "engines": { "node": ">=12.0.0" @@ -7675,68 +7892,13 @@ "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==", + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">= 8" } }, "node_modules/tsconfig-paths": { @@ -7757,19 +7919,23 @@ "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==", + "node_modules/tsx": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.0.tgz", + "integrity": "sha512-I+t79RYPlEYlHn9a+KzwrvEwhJg35h/1zHsLC2JXvhC2mdynMv6Zxzvhv5EMV6VF5qJlLlkSnMVvdZV3PSIGcg==", "dev": true, "dependencies": { - "tslib": "^1.8.1" + "esbuild": "~0.19.10", + "get-tsconfig": "^4.7.2" }, - "engines": { - "node": ">= 6" + "bin": { + "tsx": "dist/cli.mjs" }, - "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" + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" } }, "node_modules/tweetnacl": { @@ -7835,42 +8001,57 @@ } }, "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==", + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.7.tgz", + "integrity": "sha512-m6A6JjQRg39p2ZVRIN3NKXgrN8vzlHhOS+r9ymUYtcUP/TIQPvWSq7YgE5ZjASfv5Vd5BW5xrir6Gm2XNNcOow==", "dev": true, "dependencies": { "lunr": "^2.3.9", - "marked": "^4.0.16", - "minimatch": "^5.1.0", - "shiki": "^0.10.1" + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.7" }, "bin": { "typedoc": "bin/typedoc" }, "engines": { - "node": ">= 14.14" + "node": ">= 16" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.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==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-2.1.0.tgz", + "integrity": "sha512-+1DhqZCEu7Vu5APnrqpPwl31D+hXpt1fV0Le9ycCRL1eLVdatdl6KVt4SEVwPxnEpKwgOn2dNX6I9+0F1aO2aA==", "dev": true, "peerDependencies": { - "typedoc": "0.22.x || 0.23.x" + "typedoc": "0.24.x || 0.25.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==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-rename-defaults/-/typedoc-plugin-rename-defaults-0.7.0.tgz", + "integrity": "sha512-NudSQ1o/XLHNF9c4y7LzIZxfE9ltz09yCDklBPJpP5VMRvuBpYGIbQ0ZgmPz+EIV8vPx9Z/OyKwzp4HT2vDtfg==", "dev": true, + "dependencies": { + "camelcase": "^8.0.0" + }, "peerDependencies": { - "typedoc": "0.22.x || 0.23.x" + "typedoc": "0.22.x || 0.23.x || 0.24.x || 0.25.x" + } + }, + "node_modules/typedoc-plugin-rename-defaults/node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -7883,28 +8064,31 @@ } }, "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==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/typescript": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", - "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/uid-safe": { @@ -7940,6 +8124,12 @@ "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", "dev": true }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -7958,6 +8148,30 @@ "node": ">= 0.8" } }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true + }, "node_modules/upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", @@ -8014,12 +8228,6 @@ "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/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -8049,15 +8257,15 @@ "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==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", "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==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, "node_modules/watchpack": { @@ -8073,10 +8281,19 @@ "node": ">=10.13.0" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", + "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/webpack": { - "version": "5.81.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.81.0.tgz", - "integrity": "sha512-AAjaJ9S4hYCVODKLQTgG5p5e11hiMawBwV2v8MYLE0C/6UAGLuAF4n1qa9GOwdxnicaP+5k6M5HrLmD4+gIB8Q==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -8085,10 +8302,10 @@ "@webassemblyjs/wasm-edit": "^1.11.5", "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.13.0", + "enhanced-resolve": "^5.15.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -8098,7 +8315,7 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.2", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", @@ -8261,27 +8478,6 @@ "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/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -8496,15 +8692,6 @@ "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", @@ -8517,6363 +8704,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, - "@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.4", - "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.4.tgz", - "integrity": "sha512-kIhULpw9TrGYnHp/8VfdcneIcxKnLixmADtukQRtJUmsVlMg0niMkwV0xZmi8hqa57xqilIHjWFA0GKvEjVU5g==", - "dev": true - }, - "@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "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": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "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/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.10.0", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", - "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-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - } - }, - "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.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - } - }, - "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.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "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" - } - }, - "ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dev": true, - "requires": { - "tslib": "^2.0.1" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - } - } - }, - "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": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", - "dev": true, - "requires": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "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==" - }, - "basic-ftp": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", - "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", - "dev": true - }, - "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": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "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": "122.0.3", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-122.0.3.tgz", - "integrity": "sha512-f7TcCYM6tPxQAl4NQ4KckZ55j62RUfUswbl2iEScs+gI1cqRhzacjMR/FiFx3LUa4S/EZIBgnCx9L+JDhIzVpw==", - "dev": true, - "requires": { - "@testim/chrome-version": "^1.1.4", - "axios": "^1.6.7", - "compare-versions": "^6.1.0", - "extract-zip": "^2.0.1", - "proxy-agent": "^6.4.0", - "proxy-from-env": "^1.1.0", - "tcp-port-used": "^1.0.2" - }, - "dependencies": { - "compare-versions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz", - "integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==", - "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.5.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", - "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "date-fns": "^2.16.1", - "lodash": "^4.17.21", - "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-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" - } - }, - "data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "dev": true - }, - "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.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "dev": true, - "requires": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "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.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - } - }, - "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-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - } - }, - "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "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 - }, - "escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "source-map": "~0.6.1" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "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, - "optional": 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.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - }, - "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-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "requires": { - "debug": "^3.2.7" - }, - "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.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" - }, - "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" - } - }, - "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" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "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": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "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-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.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", - "dev": true - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "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" - } - }, - "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-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "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 - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "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 - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "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.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - } - }, - "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" - } - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "get-uri": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", - "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", - "dev": true, - "requires": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4", - "fs-extra": "^11.2.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - } - } - }, - "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" - } - }, - "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3" - } - }, - "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 - } - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "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.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "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-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "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==" - }, - "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 - } - } - }, - "http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "requires": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "dependencies": { - "agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dev": true, - "requires": { - "debug": "^4.3.4" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - } - } - }, - "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 - }, - "internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "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-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "dev": true, - "requires": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "dependencies": { - "sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "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-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - } - }, - "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.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "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.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "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.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "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.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "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.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "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.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "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-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "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.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "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.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "is-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 - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is2": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.9.tgz", - "integrity": "sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g==", - "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" - } - }, - "jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true - }, - "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-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 - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "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" - } - }, - "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 - }, - "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 - }, - "netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "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-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-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.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "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.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "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.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "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.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, - "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-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" - } - }, - "pac-proxy-agent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", - "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", - "dev": true, - "requires": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", - "pac-resolver": "^7.0.0", - "socks-proxy-agent": "^8.0.2" - }, - "dependencies": { - "agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dev": true, - "requires": { - "debug": "^4.3.4" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", - "dev": true, - "requires": { - "agent-base": "^7.0.2", - "debug": "4" - } - } - } - }, - "pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "dev": true, - "requires": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - } - }, - "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-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": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "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 - }, - "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-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", - "dev": true, - "requires": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" - }, - "dependencies": { - "agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dev": true, - "requires": { - "debug": "^4.3.4" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", - "dev": true, - "requires": { - "agent-base": "^7.0.2", - "debug": "4" - } - }, - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true - } - } - }, - "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" - } - }, - "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 - }, - "regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - } - }, - "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.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dev": true, - "requires": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "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 - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "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.10.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.10.0.tgz", - "integrity": "sha512-hSQPw6jgc+ej/UEcdQPG/iBwwMeCEgZr9HByY/J8ToyXztEqXzU9aLsIyrlj1BywBcStO4JQK/zMUWWrV8+riA==", - "dev": true, - "requires": { - "jszip": "^3.10.1", - "tmp": "^0.2.1", - "ws": ">=8.13.0" - } - }, - "semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "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" - } - }, - "smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true - }, - "socks": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", - "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", - "dev": true, - "requires": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - } - }, - "socks-proxy-agent": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", - "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", - "dev": true, - "requires": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "socks": "^2.7.1" - }, - "dependencies": { - "agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dev": true, - "requires": { - "debug": "^4.3.4" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - } - } - }, - "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 - }, - "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.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "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": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "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" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "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 - }, - "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.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "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" - } - }, - "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "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.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "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 - }, - "universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "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 - }, - "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" - } - }, - "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 - }, - "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.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - } - }, - "wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "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": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "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": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "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 index 8daac89fe..7c6ba92cf 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,33 @@ ".": "dist/browser/algosdk.min.js", "crypto": false }, + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js", + "types": "./dist/types/index.d.ts" + }, + "./client": { + "import": "./dist/esm/client/index.js", + "require": "./dist/cjs/client/index.js", + "types": "./dist/types/client/index.d.ts" + }, + "./client/algod": { + "import": "./dist/esm/client/v2/algod/index.js", + "require": "./dist/cjs/client/v2/algod/index.js", + "types": "./dist/types/client/v2/algod/index.d.ts" + }, + "./client/indexer": { + "import": "./dist/esm/client/v2/indexer/index.js", + "require": "./dist/cjs/client/v2/indexer/index.js", + "types": "./dist/types/client/v2/indexer/index.d.ts" + }, + "./client/kmd": { + "import": "./dist/esm/client/kmd.js", + "require": "./dist/cjs/client/kmd.js", + "types": "./dist/types/client/kmd.d.ts" + } + }, "types": "dist/types/index.d.ts", "files": [ "dist/", @@ -21,8 +48,7 @@ "url": "git://github.com/algorand/js-algorand-sdk.git" }, "dependencies": { - "algo-msgpack-with-bigint": "^2.1.1", - "buffer": "^6.0.3", + "algorand-msgpack": "^1.1.0", "hi-base32": "^0.5.1", "js-sha256": "^0.9.0", "js-sha3": "^0.8.0", @@ -34,38 +60,40 @@ "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", + "@types/node": "^20.11.5", + "@typescript-eslint/eslint-plugin": "^6.18.1", + "@typescript-eslint/parser": "^6.18.1", "assert": "^2.0.0", - "chromedriver": "^122.0.3", + "chromedriver": "^126.0.3", "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": "8.22.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^9.1.0", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-import": "^2.25.2", "eslint-plugin-tsdoc": "^0.2.11", - "express": "^4.17.1", - "geckodriver": "^3.0.1", + "express": "^4.19.2", + "geckodriver": "^4.3.0", "husky": "^4.3.8", "lint-staged": "^10.5.4", "mocha": "^9.0.0", "mock-http-server": "^1.4.3", - "prettier": "2.2.1", + "prettier": "^3.2.1", "selenium-webdriver": "^4.10.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", + "tsx": "^4.7.0", + "typedoc": "^0.25.7", + "typedoc-plugin-missing-exports": "^2.1.0", + "typedoc-plugin-rename-defaults": "^0.7.0", + "typescript": "^5.3.3", + "webpack": "^5.89.0", "webpack-cli": "^5.0.1" }, "scripts": { - "test": "node -r ts-node/register tests/mocha.js", + "test": "tsx 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\"", diff --git a/src/abi/abi_type.ts b/src/abi/abi_type.ts index cbcbd9b9d..f2d8b5b61 100644 --- a/src/abi/abi_type.ts +++ b/src/abi/abi_type.ts @@ -13,10 +13,9 @@ // | string // | (T1, ..., Tn) */ -import { Buffer } from 'buffer'; -import { encodeAddress, decodeAddress } from '../encoding/address'; -import { bigIntToBytes, bytesToBigInt } from '../encoding/bigint'; -import { concatArrays } from '../utils/utils'; +import { encodeAddress, decodeAddress } from '../encoding/address.js'; +import { bigIntToBytes, bytesToBigInt } from '../encoding/bigint.js'; +import { concatArrays } from '../utils/utils.js'; export const MAX_LEN = 2 ** 16 - 1; export const ADDR_BYTE_SIZE = 32; @@ -62,7 +61,7 @@ export abstract class ABIType { if (str.endsWith(']')) { const stringMatches = str.match(staticArrayRegexp); // Match the string itself, array element type, then array length - if (stringMatches.length !== 3) { + if (!stringMatches || stringMatches.length !== 3) { throw new Error(`malformed static array string: ${str}`); } // Parse static array using regex @@ -77,8 +76,8 @@ export abstract class ABIType { } if (str.startsWith('uint')) { // Checks if the parsed number contains only digits, no whitespaces - const digitsOnly = (string) => - [...string].every((c) => '0123456789'.includes(c)); + const digitsOnly = (s: string) => + [...s].every((c) => '0123456789'.includes(c)); const typeSizeStr = str.slice(4, str.length); if (!digitsOnly(typeSizeStr)) { throw new Error(`malformed uint string: ${typeSizeStr}`); @@ -94,7 +93,7 @@ export abstract class ABIType { } if (str.startsWith('ufixed')) { const stringMatches = str.match(ufixedRegexp); - if (stringMatches.length !== 3) { + if (!stringMatches || stringMatches.length !== 3) { throw new Error(`malformed ufixed type: ${str}`); } const ufixedSize = parseInt(stringMatches[1], 10); @@ -380,7 +379,12 @@ export class ABIStringType extends ABIType { if (typeof value !== 'string' && !(value instanceof Uint8Array)) { throw new Error(`Cannot encode value as string: ${value}`); } - const encodedBytes = Buffer.from(value); + let encodedBytes: Uint8Array; + if (typeof value === 'string') { + encodedBytes = new TextEncoder().encode(value); + } else { + encodedBytes = value; + } const encodedLength = bigIntToBytes( encodedBytes.length, LENGTH_ENCODE_BYTE_SIZE @@ -399,8 +403,12 @@ export class ABIStringType extends ABIType { `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 view = new DataView( + byteString.buffer, + byteString.byteOffset, + LENGTH_ENCODE_BYTE_SIZE + ); + const byteLength = view.getUint16(0); const byteValue = byteString.slice( LENGTH_ENCODE_BYTE_SIZE, byteString.length @@ -410,7 +418,7 @@ export class ABIStringType extends ABIType { `string length bytes do not match the actual length of string. Expected ${byteLength}, got ${byteValue.length}` ); } - return Buffer.from(byteValue).toString('utf-8'); + return new TextDecoder('utf-8').decode(byteValue); } } @@ -517,8 +525,8 @@ export class ABIArrayDynamicType extends ABIType { } decode(byteString: Uint8Array): ABIValue[] { - const buf = Buffer.from(byteString); - const byteLength = buf.readUIntBE(0, LENGTH_ENCODE_BYTE_SIZE); + const view = new DataView(byteString.buffer, 0, LENGTH_ENCODE_BYTE_SIZE); + const byteLength = view.getUint16(0); const convertedTuple = this.toABITupleType(byteLength); return convertedTuple.decode( byteString.slice(LENGTH_ENCODE_BYTE_SIZE, byteString.length) @@ -657,10 +665,10 @@ export class ABITupleType extends ABIType { decode(byteString: Uint8Array): ABIValue[] { const tupleTypes = this.childTypes; const dynamicSegments: Segment[] = []; - const valuePartition: Uint8Array[] = []; + const valuePartition: Array = []; let i = 0; let iterIndex = 0; - const buf = Buffer.from(byteString); + const view = new DataView(byteString.buffer); while (i < tupleTypes.length) { const tupleType = tupleTypes[i]; @@ -671,7 +679,9 @@ export class ABITupleType extends ABIType { ) { throw new Error('dynamic type in tuple is too short to be decoded'); } - const dynamicIndex = buf.readUIntBE(iterIndex, LENGTH_ENCODE_BYTE_SIZE); + // Since LENGTH_ENCODE_BYTE_SIZE is 2 and indices are at most 2 bytes, + // we can use getUint16 using the iterIndex offset. + const dynamicIndex = view.getUint16(iterIndex); if (dynamicSegments.length > 0) { dynamicSegments[dynamicSegments.length - 1].right = dynamicIndex; // Check that right side of segment is greater than the left side @@ -761,7 +771,7 @@ export class ABITupleType extends ABIType { // Decode each tuple element const returnValues: ABIValue[] = []; for (let j = 0; j < tupleTypes.length; j++) { - const valueTi = tupleTypes[j].decode(valuePartition[j]); + const valueTi = tupleTypes[j].decode(valuePartition[j]!); returnValues.push(valueTi); } return returnValues; diff --git a/src/abi/contract.ts b/src/abi/contract.ts index 2362914bc..a3613ff08 100644 --- a/src/abi/contract.ts +++ b/src/abi/contract.ts @@ -1,5 +1,5 @@ -import { ABIMethod, ABIMethodParams, getMethodByName } from './method'; -import { ARC28Event } from './event'; +import { ABIMethod, ABIMethodParams, getMethodByName } from './method.js'; +import { ARC28Event } from './event.js'; export interface ABIContractNetworkInfo { appID: number; diff --git a/src/abi/index.ts b/src/abi/index.ts index 01c18dace..ba8ad251b 100644 --- a/src/abi/index.ts +++ b/src/abi/index.ts @@ -1,6 +1,6 @@ -export * from './abi_type'; -export * from './contract'; -export * from './interface'; -export * from './method'; -export * from './transaction'; -export * from './reference'; +export * from './abi_type.js'; +export * from './contract.js'; +export * from './interface.js'; +export * from './method.js'; +export * from './transaction.js'; +export * from './reference.js'; diff --git a/src/abi/interface.ts b/src/abi/interface.ts index b676c5d80..6dc91d2a0 100644 --- a/src/abi/interface.ts +++ b/src/abi/interface.ts @@ -1,4 +1,4 @@ -import { ABIMethod, ABIMethodParams, getMethodByName } from './method'; +import { ABIMethod, ABIMethodParams, getMethodByName } from './method.js'; export interface ABIInterfaceParams { name: string; diff --git a/src/abi/method.ts b/src/abi/method.ts index 709895a58..353bedeac 100644 --- a/src/abi/method.ts +++ b/src/abi/method.ts @@ -1,12 +1,14 @@ -import { genericHash } from '../nacl/naclWrappers'; -import { ABIType, ABITupleType } from './abi_type'; -import { ABITransactionType, abiTypeIsTransaction } from './transaction'; -import { ABIReferenceType, abiTypeIsReference } from './reference'; -import { ARC28Event } from './event'; - -function parseMethodSignature( - signature: string -): { name: string; args: string[]; returns: string } { +import { genericHash } from '../nacl/naclWrappers.js'; +import { ABIType, ABITupleType } from './abi_type.js'; +import { ABITransactionType, abiTypeIsTransaction } from './transaction.js'; +import { ABIReferenceType, abiTypeIsReference } from './reference.js'; +import { ARC28Event } from './event.js'; + +function parseMethodSignature(signature: string): { + name: string; + args: string[]; + returns: string; +} { const argsStart = signature.indexOf('('); if (argsStart === -1) { throw new Error(`Invalid method signature: ${signature}`); diff --git a/src/abi/transaction.ts b/src/abi/transaction.ts index 880250ff0..920e555f3 100644 --- a/src/abi/transaction.ts +++ b/src/abi/transaction.ts @@ -1,4 +1,4 @@ -import { Transaction } from '../transaction'; +import { Transaction } from '../transaction.js'; export enum ABITransactionType { /** @@ -57,5 +57,5 @@ export function abiCheckTransactionType( return true; } - return txn.type && txn.type.toString() === type.toString(); + return txn.type ? txn.type.toString() === type.toString() : false; } diff --git a/src/account.ts b/src/account.ts index b6756c443..89c7b33e4 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,12 +1,12 @@ -import * as nacl from './nacl/naclWrappers'; -import * as address from './encoding/address'; -import Account from './types/account'; +import * as nacl from './nacl/naclWrappers.js'; +import { Address } from './encoding/address.js'; +import Account from './types/account.js'; /** * 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 }; + const addr = new Address(keys.publicKey); + return { addr, sk: keys.secretKey }; } diff --git a/src/bid.ts b/src/bid.ts deleted file mode 100644 index 7874d647c..000000000 --- a/src/bid.ts +++ /dev/null @@ -1,96 +0,0 @@ -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 index 05e994800..335512782 100644 --- a/src/boxStorage.ts +++ b/src/boxStorage.ts @@ -1,20 +1,17 @@ -import { EncodedBoxReference } from './types'; -import { BoxReference } from './types/transactions/base'; +import { BoxReference } from './types/transactions/base.js'; -function translateBoxReference( +function boxReferenceToEncodingData( reference: BoxReference, - foreignApps: number[], - appIndex: number -): EncodedBoxReference { - const referenceId = reference.appIndex; + foreignApps: bigint[], + appIndex: bigint +): Map { + const referenceId = BigInt(reference.appIndex); const referenceName = reference.name; - const isOwnReference = referenceId === 0 || referenceId === appIndex; - let index = 0; + const isOwnReference = referenceId === BigInt(0) || referenceId === appIndex; + + // Foreign apps start from index 1; index 0 is its own app ID. + const index = foreignApps.indexOf(referenceId) + 1; - 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. @@ -23,20 +20,25 @@ function translateBoxReference( // its own foreign apps array. throw new Error(`Box ref with appId ${referenceId} not in foreign-apps`); } - return { i: index, n: referenceName }; + + return new Map([ + ['i', index], + ['n', referenceName], + ]); } /** - * translateBoxReferences translates an array of BoxReferences with app IDs - * into an array of EncodedBoxReferences with foreign indices. + * boxReferencesToEncodingData translates an array of BoxReferences into an array of encoding data + * maps. */ -export function translateBoxReferences( - references: BoxReference[] | undefined, - foreignApps: number[], - appIndex: number -): EncodedBoxReference[] { - if (references == null) return []; +export function boxReferencesToEncodingData( + references: ReadonlyArray, + foreignApps: ReadonlyArray, + appIndex: number | bigint +): Array> { + const appIndexBigInt = BigInt(appIndex); + const foreignAppsBigInt = foreignApps.map(BigInt); return references.map((bx) => - translateBoxReference(bx, foreignApps, appIndex) + boxReferenceToEncodingData(bx, foreignAppsBigInt, appIndexBigInt) ); } diff --git a/src/client/baseHTTPClient.ts b/src/client/baseHTTPClient.ts index 39b1af632..0513f47c7 100644 --- a/src/client/baseHTTPClient.ts +++ b/src/client/baseHTTPClient.ts @@ -26,8 +26,11 @@ export interface BaseHTTPClientError { * 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 + * The parameter `customOptions` is an object that can be used to configure + * individual requests with specific specific to the BaseHTTPClient implementation. + * + * Note that DELETE requests have an optional query parameter. + * This is to allow future extension where DELETE may have queries * Currently however HTTPClient does not make use of it * * Compared to HTTPClient, BaseHTTPClient does not deal with serialization/deserialization @@ -41,18 +44,21 @@ export interface BaseHTTPClient { get( relativePath: string, query?: Query, - requestHeaders?: Record + requestHeaders?: Record, + customOptions?: Record ): Promise; post( relativePath: string, data: Uint8Array, query?: Query, - requestHeaders?: Record + requestHeaders?: Record, + customOptions?: Record ): Promise; delete( relativePath: string, - data: Uint8Array, + data?: Uint8Array, query?: Query, - requestHeaders?: Record + requestHeaders?: Record, + customOptions?: Record ): Promise; } diff --git a/src/client/client.ts b/src/client/client.ts index 6a2036632..bbe13dee9 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,23 +1,73 @@ -import { Buffer } from 'buffer'; -import * as utils from '../utils/utils'; +import * as utils from '../utils/utils.js'; import { BaseHTTPClient, BaseHTTPClientResponse, Query, -} from './baseHTTPClient'; -import { TokenHeader, URLTokenBaseHTTPClient } from './urlTokenBaseHTTPClient'; +} from './baseHTTPClient.js'; +import { + TokenHeader, + URLTokenBaseHTTPClient, +} from './urlTokenBaseHTTPClient.js'; 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 +export class HTTPClientResponse { + /** + * The raw response bytes + */ + body: Uint8Array; + /** + * If the expected response type is JSON, this is the response bytes converted to a string. + */ text?: string; + format: 'application/msgpack' | 'application/json'; headers: Record; status: number; ok: boolean; + + constructor(options: { + body: Uint8Array; + text?: string; + format: 'application/msgpack' | 'application/json'; + headers: Record; + status: number; + ok: boolean; + }) { + this.body = options.body; + this.text = options.text; + this.format = options.format; + this.headers = options.headers; + this.status = options.status; + this.ok = options.ok; + } + + /** + * Returns the response body as a string, ready to be parsed as JSON. + */ + getJSONText(): string { + if (this.text === undefined) { + throw new Error( + `Response body does not contain JSON data. Format is ${this.format}` + ); + } + return this.text; + } + + /** + * Parses the response body as JSON with the given options. + */ + parseBodyAsJSON(jsonOptions: utils.ParseJSONOptions) { + if (this.text === undefined) { + throw new Error( + `Response body does not contain JSON data. Format is ${this.format}` + ); + } + // eslint-disable-next-line no-use-before-define + return HTTPClient.parseJSON(this.text, this.status, jsonOptions); + } } /** @@ -38,9 +88,12 @@ function removeFalsyOrEmpty(obj: Record) { * 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 { +function tolowerCaseKeys(o: Record): Record { /* eslint-disable no-param-reassign,no-return-assign,no-sequences */ - return Object.keys(o).reduce((c, k) => ((c[k.toLowerCase()] = o[k]), c), {}); + return Object.keys(o).reduce( + (c, k) => ((c[k.toLowerCase()] = o[k]), c), + {} as Record + ); /* eslint-enable no-param-reassign,no-return-assign,no-sequences */ } @@ -70,7 +123,7 @@ function getAcceptFormat( * It takes care of setting the proper "Accept" header and of * decoding the JSON outputs. */ -export default class HTTPClient { +export class HTTPClient { private bc: BaseHTTPClient; /** @@ -107,8 +160,7 @@ export default class HTTPClient { } /** - * Parse JSON using either the built-in JSON.parse or utils.parseJSON - * depending on whether jsonOptions are provided or not + * Parse JSON using utils.parseJSON * * @param text - JSON data * @param status - Status of the response (used in case parseJSON fails) @@ -118,15 +170,15 @@ export default class HTTPClient { public static parseJSON( text: string, status: number, - jsonOptions: utils.JSONOptions = {} + jsonOptions: utils.ParseJSONOptions ) { try { - if (Object.keys(jsonOptions).length === 0) { - return text && JSON.parse(text); + if (!text) { + return null; } - return text && utils.parseJSON(text, jsonOptions); + return utils.parseJSON(text, jsonOptions); } catch (err_) { - const err: ErrorWithAdditionalInfo = err_; + const err = err_ as ErrorWithAdditionalInfo; // return the raw response if the response parsing fails err.rawResponse = text || null; // return the http status code if the response parsing fails @@ -151,10 +203,10 @@ export default class HTTPClient { return new Uint8Array(0); // empty Uint8Array } if (requestHeaders['content-type'] === 'application/json') { - return new Uint8Array(Buffer.from(JSON.stringify(data))); + return new TextEncoder().encode(utils.stringifyJSON(data)); } if (typeof data === 'string') { - return new Uint8Array(Buffer.from(data)); + return new TextEncoder().encode(data); } if (data instanceof Uint8Array) { return data; @@ -171,27 +223,21 @@ export default class HTTPClient { */ private static prepareResponse( res: BaseHTTPClientResponse, - format: 'application/msgpack' | 'application/json', - parseBody: boolean, - jsonOptions: utils.JSONOptions = {} + format: 'application/msgpack' | 'application/json' ): HTTPClientResponse { - let { body } = res; - let text; + const { body } = res; + let text: string | undefined; if (format !== 'application/msgpack') { - text = (body && Buffer.from(body).toString()) || ''; - } - - if (parseBody && format === 'application/json') { - body = HTTPClient.parseJSON(text, res.status, jsonOptions); + text = (body && new TextDecoder().decode(body)) || ''; } - return { + return new HTTPClientResponse({ ...res, - body, + format, text, ok: Math.trunc(res.status / 100) === 2, - }; + }); } /** @@ -200,13 +246,12 @@ export default class HTTPClient { * by adding the status and preparing the internal response * @private */ - private static prepareResponseError(err) { + private static prepareResponseError(err: any) { if (err.response) { // eslint-disable-next-line no-param-reassign err.response = HTTPClient.prepareResponse( err.response, - 'application/json', - true + 'application/json' ); // eslint-disable-next-line no-param-reassign err.status = err.response.status; @@ -216,33 +261,39 @@ export default class HTTPClient { /** * 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 + * + * @param options - The options to use for the request. + * @param options.relativePath - The path of the request. + * @param options.query - An object containing the query parameters of the request. + * @param options.requestHeaders - An object containing additional request headers to use. * or not. + * @param options.customOptions - An object containing additional options to pass to the + * underlying BaseHTTPClient instance. * @returns Response object. */ - async get( - relativePath: string, - query?: Query, - requestHeaders: Record = {}, - jsonOptions: utils.JSONOptions = {}, - parseBody: boolean = true - ): Promise { + async get({ + relativePath, + query, + requestHeaders, + customOptions, + }: { + relativePath: string; + query?: Query; + requestHeaders?: Record; + customOptions?: Record; + }): Promise { const format = getAcceptFormat(query); - const fullHeaders = { ...requestHeaders, accept: format }; + const fullHeaders = { ...(requestHeaders ?? {}), accept: format }; try { const res = await this.bc.get( relativePath, - removeFalsyOrEmpty(query), - fullHeaders + query ? removeFalsyOrEmpty(query) : undefined, + fullHeaders, + customOptions ); - return HTTPClient.prepareResponse(res, format, parseBody, jsonOptions); + return HTTPClient.prepareResponse(res, format); } catch (err) { throw HTTPClient.prepareResponseError(err); } @@ -252,17 +303,24 @@ export default class HTTPClient { * 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) + * @param options - The options to use for the request. */ - async post( - relativePath: string, - data: any, - requestHeaders: Record = {}, - query?: Query, - parseBody: boolean = true - ): Promise { + async post({ + relativePath, + data, + query, + requestHeaders, + customOptions, + }: { + relativePath: string; + data: any; + query?: Query; + requestHeaders?: Record; + customOptions?: Record; + }): Promise { const fullHeaders = { 'content-type': 'application/json', - ...tolowerCaseKeys(requestHeaders), + ...tolowerCaseKeys(requestHeaders ?? {}), }; try { @@ -270,10 +328,11 @@ export default class HTTPClient { relativePath, HTTPClient.serializeData(data, fullHeaders), query, - fullHeaders + fullHeaders, + customOptions ); - return HTTPClient.prepareResponse(res, 'application/json', parseBody); + return HTTPClient.prepareResponse(res, 'application/json'); } catch (err) { throw HTTPClient.prepareResponseError(err); } @@ -283,25 +342,38 @@ export default class HTTPClient { * 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) + * @param options - The options to use for the request. */ - async delete( - relativePath: string, - data: any, - requestHeaders: Record = {}, - parseBody: boolean = true - ) { + async delete({ + relativePath, + data, + requestHeaders, + customOptions, + }: { + relativePath: string; + data: any; + requestHeaders?: Record; + customOptions?: Record; + }) { const fullHeaders = { 'content-type': 'application/json', - ...tolowerCaseKeys(requestHeaders), + ...tolowerCaseKeys(requestHeaders ?? {}), }; - const res = await this.bc.delete( - relativePath, - HTTPClient.serializeData(data, fullHeaders), - undefined, - fullHeaders - ); + try { + const res = await this.bc.delete( + relativePath, + typeof data !== 'undefined' + ? HTTPClient.serializeData(data, fullHeaders) + : undefined, + undefined, + fullHeaders, + customOptions + ); - return HTTPClient.prepareResponse(res, 'application/json', parseBody); + return HTTPClient.prepareResponse(res, 'application/json'); + } catch (err) { + throw HTTPClient.prepareResponseError(err); + } } } diff --git a/src/client/index.ts b/src/client/index.ts new file mode 100644 index 000000000..e7ddc4507 --- /dev/null +++ b/src/client/index.ts @@ -0,0 +1,6 @@ +// Generics +export { default as JSONRequest } from './v2/jsonrequest.js'; +export { default as ServiceClient } from './v2/serviceClient.js'; +export * from './baseHTTPClient.js'; +export * from './urlTokenBaseHTTPClient.js'; +export * from './client.js'; diff --git a/src/client/kmd.ts b/src/client/kmd.ts index 4c832b259..2c4f85a47 100644 --- a/src/client/kmd.ts +++ b/src/client/kmd.ts @@ -1,9 +1,14 @@ -import { Buffer } from 'buffer'; -import ServiceClient from './v2/serviceClient'; -import * as txn from '../transaction'; -import { CustomTokenHeader, KMDTokenHeader } from './urlTokenBaseHTTPClient'; +import { + base64ToBytes, + bytesToBase64, + coerceToBytes, +} from '../encoding/binarydata.js'; +import IntDecoding from '../types/intDecoding.js'; +import { Transaction } from '../transaction.js'; +import { CustomTokenHeader, KMDTokenHeader } from './urlTokenBaseHTTPClient.js'; +import ServiceClient from './v2/serviceClient.js'; -export default class Kmd extends ServiceClient { +export class KmdClient extends ServiceClient { constructor( token: string | KMDTokenHeader | CustomTokenHeader, baseServer = 'http://127.0.0.1', @@ -13,12 +18,43 @@ export default class Kmd extends ServiceClient { super('X-KMD-API-Token', token, baseServer, port, headers); } + private async get(relativePath: string): Promise { + const res = await this.c.get({ + relativePath, + }); + return res.parseBodyAsJSON({ + // Using SAFE for all KMD endpoints because no integers in responses should ever be too big + intDecoding: IntDecoding.SAFE, + }); + } + + private async delete(relativePath: string, data: any): Promise { + const res = await this.c.delete({ + relativePath, + data, + }); + return res.parseBodyAsJSON({ + // Using SAFE for all KMD endpoints because no integers in responses should ever be too big + intDecoding: IntDecoding.SAFE, + }); + } + + private async post(relativePath: string, data: any): Promise { + const res = await this.c.post({ + relativePath, + data, + }); + return res.parseBodyAsJSON({ + // Using SAFE for all KMD endpoints because no integers in responses should ever be too big + intDecoding: IntDecoding.SAFE, + }); + } + /** * 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; + return this.get('/versions'); } /** @@ -26,8 +62,7 @@ export default class Kmd extends ServiceClient { * 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; + return this.get('/v1/wallets'); } /** @@ -50,10 +85,9 @@ export default class Kmd extends ServiceClient { wallet_name: walletName, wallet_driver_name: walletDriverName, wallet_password: walletPassword, - master_derivation_key: Buffer.from(walletMDK).toString('base64'), + master_derivation_key: bytesToBase64(walletMDK), }; - const res = await this.c.post('/v1/wallet', req); - return res.body; + return this.post('/v1/wallet', req); } /** @@ -72,8 +106,7 @@ export default class Kmd extends ServiceClient { wallet_id: walletID, wallet_password: walletPassword, }; - const res = await this.c.post('/v1/wallet/init', req); - return res.body; + return this.post('/v1/wallet/init', req); } /** @@ -85,8 +118,7 @@ export default class Kmd extends ServiceClient { const req = { wallet_handle_token: walletHandle, }; - const res = await this.c.post('/v1/wallet/release', req); - return res.body; + return this.post('/v1/wallet/release', req); } /** @@ -100,8 +132,7 @@ export default class Kmd extends ServiceClient { const req = { wallet_handle_token: walletHandle, }; - const res = await this.c.post('/v1/wallet/renew', req); - return res.body; + return this.post('/v1/wallet/renew', req); } /** @@ -121,8 +152,7 @@ export default class Kmd extends ServiceClient { wallet_password: walletPassword, wallet_name: newWalletName, }; - const res = await this.c.post('/v1/wallet/rename', req); - return res.body; + return this.post('/v1/wallet/rename', req); } /** @@ -134,8 +164,7 @@ export default class Kmd extends ServiceClient { const req = { wallet_handle_token: walletHandle, }; - const res = await this.c.post('/v1/wallet/info', req); - return res.body; + return this.post('/v1/wallet/info', req); } /** @@ -155,12 +184,9 @@ export default class Kmd extends ServiceClient { wallet_handle_token: walletHandle, wallet_password: walletPassword, }; - const res = await this.c.post('/v1/master-key/export', req); + const res = await this.post('/v1/master-key/export', req); return { - master_derivation_key: Buffer.from( - res.body.master_derivation_key, - 'base64' - ), + master_derivation_key: base64ToBytes(res.master_derivation_key), }; } @@ -174,10 +200,9 @@ export default class Kmd extends ServiceClient { async importKey(walletHandle: string, secretKey: Uint8Array) { const req = { wallet_handle_token: walletHandle, - private_key: Buffer.from(secretKey).toString('base64'), + private_key: bytesToBase64(secretKey), }; - const res = await this.c.post('/v1/key/import', req); - return res.body; + return this.post('/v1/key/import', req); } /** @@ -194,8 +219,8 @@ export default class Kmd extends ServiceClient { 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') }; + const res = await this.post('/v1/key/export', req); + return { private_key: base64ToBytes(res.private_key) }; } /** @@ -209,8 +234,7 @@ export default class Kmd extends ServiceClient { wallet_handle_token: walletHandle, display_mnemonic: false, }; - const res = await this.c.post('/v1/key', req); - return res.body; + return this.post('/v1/key', req); } /** @@ -230,8 +254,7 @@ export default class Kmd extends ServiceClient { address: addr, wallet_password: walletPassword, }; - const res = await this.c.delete('/v1/key', req); - return res.body; + return this.delete('/v1/key', req); } /** @@ -243,8 +266,7 @@ export default class Kmd extends ServiceClient { const req = { wallet_handle_token: walletHandle, }; - const res = await this.c.post('/v1/key/list', req); - return res.body; + return this.post('/v1/key/list', req); } /** @@ -259,21 +281,15 @@ export default class Kmd extends ServiceClient { async signTransaction( walletHandle: string, walletPassword: string, - transaction: txn.TransactionLike + transaction: Transaction ) { - const tx = txn.instantiateTxnIfNeeded(transaction); - const req = { wallet_handle_token: walletHandle, wallet_password: walletPassword, - transaction: Buffer.from(tx.toByte()).toString('base64'), + transaction: bytesToBase64(transaction.toByte()), }; - 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; + const res = await this.post('/v1/transaction/sign', req); + return base64ToBytes(res.signed_transaction); } /** @@ -289,23 +305,19 @@ export default class Kmd extends ServiceClient { async signTransactionWithSpecificPublicKey( walletHandle: string, walletPassword: string, - transaction: txn.TransactionLike, + transaction: Transaction, publicKey: Uint8Array | string ) { - const tx = txn.instantiateTxnIfNeeded(transaction); + const pk = coerceToBytes(publicKey); const req = { wallet_handle_token: walletHandle, wallet_password: walletPassword, - transaction: Buffer.from(tx.toByte()).toString('base64'), - public_key: Buffer.from(publicKey).toString('base64'), + transaction: bytesToBase64(transaction.toByte()), + public_key: bytesToBase64(pk), }; - 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; + const res = await this.post('/v1/transaction/sign', req); + return base64ToBytes(res.signed_transaction); } /** @@ -320,8 +332,7 @@ export default class Kmd extends ServiceClient { const req = { wallet_handle_token: walletHandle, }; - const res = await this.c.post('/v1/multisig/list', req); - return res.body; + return this.post('/v1/multisig/list', req); } /** @@ -346,8 +357,7 @@ export default class Kmd extends ServiceClient { threshold, pks, }; - const res = await this.c.post('/v1/multisig/import', req); - return res.body; + return this.post('/v1/multisig/import', req); } /** @@ -365,8 +375,7 @@ export default class Kmd extends ServiceClient { wallet_handle_token: walletHandle, address: addr, }; - const res = await this.c.post('/v1/multisig/export', req); - return res.body; + return this.post('/v1/multisig/export', req); } /** @@ -384,20 +393,19 @@ export default class Kmd extends ServiceClient { async signMultisigTransaction( walletHandle: string, pw: string, - transaction: txn.TransactionLike, + transaction: Transaction, pk: Uint8Array | string, partial: string ) { - const tx = txn.instantiateTxnIfNeeded(transaction); + const pubkey = coerceToBytes(pk); const req = { wallet_handle_token: walletHandle, - transaction: Buffer.from(tx.toByte()).toString('base64'), - public_key: Buffer.from(pk).toString('base64'), + transaction: bytesToBase64(transaction.toByte()), + public_key: bytesToBase64(pubkey), partial_multisig: partial, wallet_password: pw, }; - const res = await this.c.post('/v1/multisig/sign', req); - return res.body; + return this.post('/v1/multisig/sign', req); } /** @@ -418,7 +426,6 @@ export default class Kmd extends ServiceClient { address: addr, wallet_password: walletPassword, }; - const res = await this.c.delete('/v1/multisig', req); - return res.body; + return this.delete('/v1/multisig', req); } } diff --git a/src/client/urlTokenBaseHTTPClient.ts b/src/client/urlTokenBaseHTTPClient.ts index 3961c913e..b172c2d45 100644 --- a/src/client/urlTokenBaseHTTPClient.ts +++ b/src/client/urlTokenBaseHTTPClient.ts @@ -1,10 +1,9 @@ -import { Buffer } from 'buffer'; import { BaseHTTPClient, BaseHTTPClientResponse, BaseHTTPClientError, Query, -} from './baseHTTPClient'; +} from './baseHTTPClient.js'; export interface AlgodTokenHeader { 'X-Algo-API-Token': string; @@ -23,7 +22,10 @@ export interface CustomTokenHeader { } class URLTokenBaseHTTPError extends Error implements BaseHTTPClientError { - constructor(message: string, public response: BaseHTTPClientResponse) { + constructor( + message: string, + public response: BaseHTTPClientResponse + ) { super(message); this.name = 'URLTokenBaseHTTPError'; this.response = response; @@ -40,6 +42,9 @@ export type TokenHeader = * Implementation of BaseHTTPClient that uses a URL and a token * and make the REST queries using fetch. * This is the default implementation of BaseHTTPClient. + * + * Additional fetch options can be configured by using the `customOptions` parameter on + * get/post/delete requests. */ export class URLTokenBaseHTTPClient implements BaseHTTPClient { private readonly baseURL: URL; @@ -111,13 +116,13 @@ export class URLTokenBaseHTTPClient implements BaseHTTPClient { return; } - let body: Uint8Array | null = null; - let bodyErrorMessage: string | null = null; + let body: Uint8Array | undefined; + let bodyErrorMessage: string | undefined; try { body = new Uint8Array(await res.arrayBuffer()); const decoded: Record = JSON.parse( - Buffer.from(body).toString() + new TextDecoder().decode(body) ); if (decoded.message) { bodyErrorMessage = decoded.message; @@ -132,7 +137,7 @@ export class URLTokenBaseHTTPClient implements BaseHTTPClient { } throw new URLTokenBaseHTTPError(message, { - body, + body: body ?? new Uint8Array(), status: res.status, headers: URLTokenBaseHTTPClient.formatFetchResponseHeaders(res.headers), }); @@ -152,17 +157,19 @@ export class URLTokenBaseHTTPClient implements BaseHTTPClient { async get( relativePath: string, query?: Query, - requestHeaders: Record = {} + requestHeaders?: Record, + customOptions?: Record ): Promise { // Expand headers for use in fetch const headers = { ...this.tokenHeader, ...this.defaultHeaders, - ...requestHeaders, + ...(requestHeaders ?? {}), }; const res = await fetch(this.getURL(relativePath, query), { headers, + ...(customOptions ?? {}), }); return URLTokenBaseHTTPClient.formatFetchResponse(res); @@ -172,19 +179,21 @@ export class URLTokenBaseHTTPClient implements BaseHTTPClient { relativePath: string, data: Uint8Array, query?: Query, - requestHeaders: Record = {} + requestHeaders?: Record, + customOptions?: Record ): Promise { // Expand headers for use in fetch const headers = { ...this.tokenHeader, ...this.defaultHeaders, - ...requestHeaders, + ...(requestHeaders ?? {}), }; const res = await fetch(this.getURL(relativePath, query), { method: 'POST', body: data, headers, + ...(customOptions ?? {}), }); return URLTokenBaseHTTPClient.formatFetchResponse(res); @@ -192,21 +201,23 @@ export class URLTokenBaseHTTPClient implements BaseHTTPClient { async delete( relativePath: string, - data: Uint8Array, + data?: Uint8Array, query?: Query, - requestHeaders: Record = {} + requestHeaders?: Record, + customOptions?: Record ): Promise { // Expand headers for use in fetch const headers = { ...this.tokenHeader, ...this.defaultHeaders, - ...requestHeaders, + ...(requestHeaders ?? {}), }; const res = await fetch(this.getURL(relativePath, query), { method: 'DELETE', body: data, headers, + ...(customOptions ?? {}), }); return URLTokenBaseHTTPClient.formatFetchResponse(res); diff --git a/src/client/v2/algod/accountApplicationInformation.ts b/src/client/v2/algod/accountApplicationInformation.ts index 26ec531c5..6b26c3df3 100644 --- a/src/client/v2/algod/accountApplicationInformation.ts +++ b/src/client/v2/algod/accountApplicationInformation.ts @@ -1,20 +1,27 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { AccountApplicationResponse } from './models/types.js'; +import { Address } from '../../../encoding/address.js'; + +export default class AccountApplicationInformation extends JSONRequest { + private account: string; -export default class AccountApplicationInformation extends JSONRequest { constructor( c: HTTPClient, - intDecoding: IntDecoding, - private account: string, + account: string | Address, private applicationID: number ) { - super(c, intDecoding); - this.account = account; - this.applicationID = applicationID; + super(c); + this.account = account.toString(); } path() { return `/v2/accounts/${this.account}/applications/${this.applicationID}`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AccountApplicationResponse { + return decodeJSON(response.getJSONText(), AccountApplicationResponse); + } } diff --git a/src/client/v2/algod/accountAssetInformation.ts b/src/client/v2/algod/accountAssetInformation.ts index f05e2f8da..eef272257 100644 --- a/src/client/v2/algod/accountAssetInformation.ts +++ b/src/client/v2/algod/accountAssetInformation.ts @@ -1,20 +1,27 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { AccountAssetResponse } from './models/types.js'; +import { Address } from '../../../encoding/address.js'; + +export default class AccountAssetInformation extends JSONRequest { + private account: string; -export default class AccountAssetInformation extends JSONRequest { constructor( c: HTTPClient, - intDecoding: IntDecoding, - private account: string, + account: string | Address, private assetID: number ) { - super(c, intDecoding); - this.account = account; - this.assetID = assetID; + super(c); + this.account = account.toString(); } path() { return `/v2/accounts/${this.account}/assets/${this.assetID}`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AccountAssetResponse { + return decodeJSON(response.getJSONText(), AccountAssetResponse); + } } diff --git a/src/client/v2/algod/accountInformation.ts b/src/client/v2/algod/accountInformation.ts index bbff87182..431029761 100644 --- a/src/client/v2/algod/accountInformation.ts +++ b/src/client/v2/algod/accountInformation.ts @@ -1,15 +1,15 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Account } from './models/types.js'; +import { Address } from '../../../encoding/address.js'; -export default class AccountInformation extends JSONRequest { - constructor( - c: HTTPClient, - intDecoding: IntDecoding, - private account: string - ) { - super(c, intDecoding); - this.account = account; +export default class AccountInformation extends JSONRequest { + private account: string; + + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); } path() { @@ -34,4 +34,9 @@ export default class AccountInformation extends JSONRequest { this.query.exclude = exclude; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): Account { + return decodeJSON(response.getJSONText(), Account); + } } diff --git a/src/client/v2/algod/algod.ts b/src/client/v2/algod/algod.ts index 33ecfdc10..2a373ba74 100644 --- a/src/client/v2/algod/algod.ts +++ b/src/client/v2/algod/algod.ts @@ -1,49 +1,50 @@ -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 GetBlockTxids from './getBlockTxids'; -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 ServiceClient from '../serviceClient.js'; +import * as modelsv2 from './models/types.js'; +import AccountInformation from './accountInformation.js'; +import AccountAssetInformation from './accountAssetInformation.js'; +import AccountApplicationInformation from './accountApplicationInformation.js'; +import Block from './block.js'; +import Compile from './compile.js'; +import Dryrun from './dryrun.js'; +import Genesis from './genesis.js'; +import GetAssetByID from './getAssetByID.js'; +import GetApplicationByID from './getApplicationByID.js'; +import GetBlockHash from './getBlockHash.js'; +import GetBlockTxids from './getBlockTxids.js'; +import GetApplicationBoxByName from './getApplicationBoxByName.js'; +import GetApplicationBoxes from './getApplicationBoxes.js'; +import HealthCheck from './healthCheck.js'; +import PendingTransactionInformation from './pendingTransactionInformation.js'; +import PendingTransactions from './pendingTransactions.js'; +import PendingTransactionsByAddress from './pendingTransactionsByAddress.js'; +import GetTransactionProof from './getTransactionProof.js'; +import SendRawTransaction from './sendRawTransaction.js'; +import Status from './status.js'; +import StatusAfterBlock from './statusAfterBlock.js'; +import SuggestedParams from './suggestedParams.js'; +import Supply from './supply.js'; +import Versions from './versions.js'; +import { BaseHTTPClient } from '../../baseHTTPClient.js'; 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'; +} from '../../urlTokenBaseHTTPClient.js'; +import LightBlockHeaderProof from './lightBlockHeaderProof.js'; +import StateProof from './stateproof.js'; +import SetSyncRound from './setSyncRound.js'; +import GetSyncRound from './getSyncRound.js'; +import SetBlockOffsetTimestamp from './setBlockOffsetTimestamp.js'; +import GetBlockOffsetTimestamp from './getBlockOffsetTimestamp.js'; +import Disassemble from './disassemble.js'; +import SimulateRawTransactions from './simulateTransaction.js'; +import { SignedTransaction } from '../../../signedTransaction.js'; +import * as encoding from '../../../encoding/encoding.js'; +import Ready from './ready.js'; +import UnsetSyncRound from './unsetSyncRound.js'; +import GetLedgerStateDeltaForTransactionGroup from './getLedgerStateDeltaForTransactionGroup.js'; +import GetLedgerStateDelta from './getLedgerStateDelta.js'; +import GetTransactionGroupLedgerStateDeltasForRound from './getTransactionGroupLedgerStateDeltasForRound.js'; +import { Address } from '../../../encoding/address.js'; /** * 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. @@ -55,7 +56,7 @@ import GetTransactionGroupLedgerStateDeltasForRound from './getTransactionGroupL * * [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 { +export class AlgodClient extends ServiceClient { /** * Create an AlgodClient from * * either a token, baseServer, port, and optional headers @@ -153,8 +154,8 @@ export default class AlgodClient extends ServiceClient { * @param account - The address of the account to look up. * @category GET */ - accountInformation(account: string) { - return new AccountInformation(this.c, this.intDecoding, account); + accountInformation(account: string | Address) { + return new AccountInformation(this.c, account); } /** @@ -172,13 +173,8 @@ export default class AlgodClient extends ServiceClient { * @param index - The asset ID to look up. * @category GET */ - accountAssetInformation(account: string, index: number) { - return new AccountAssetInformation( - this.c, - this.intDecoding, - account, - index - ); + accountAssetInformation(account: string | Address, index: number) { + return new AccountAssetInformation(this.c, account, index); } /** @@ -196,13 +192,8 @@ export default class AlgodClient extends ServiceClient { * @param index - The application ID to look up. * @category GET */ - accountApplicationInformation(account: string, index: number) { - return new AccountApplicationInformation( - this.c, - this.intDecoding, - account, - index - ); + accountApplicationInformation(account: string | Address, index: number) { + return new AccountApplicationInformation(this.c, account, index); } /** @@ -236,7 +227,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ getBlockHash(roundNumber: number) { - return new GetBlockHash(this.c, this.intDecoding, roundNumber); + return new GetBlockHash(this.c, roundNumber); } /** @@ -253,7 +244,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ getBlockTxids(roundNumber: number) { - return new GetBlockTxids(this.c, this.intDecoding, roundNumber); + return new GetBlockTxids(this.c, roundNumber); } /** @@ -332,7 +323,7 @@ export default class AlgodClient extends ServiceClient { * @param address - The address of the sender. * @category GET */ - pendingTransactionByAddress(address: string) { + pendingTransactionByAddress(address: string | Address) { return new PendingTransactionsByAddress(this.c, address); } @@ -348,7 +339,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ status() { - return new Status(this.c, this.intDecoding); + return new Status(this.c); } /** @@ -365,7 +356,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ statusAfterBlock(round: number) { - return new StatusAfterBlock(this.c, this.intDecoding, round); + return new StatusAfterBlock(this.c, round); } /** @@ -376,8 +367,8 @@ export default class AlgodClient extends ServiceClient { * const suggestedParams = await algodClient.getTransactionParams().do(); * const amountInMicroAlgos = algosdk.algosToMicroalgos(2); // 2 Algos * const unsignedTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - * from: senderAddress, - * to: receiverAddress, + * sender: senderAddress, + * receiver: receiverAddress, * amount: amountInMicroAlgos, * suggestedParams: suggestedParams, * }); @@ -406,7 +397,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ supply() { - return new Supply(this.c, this.intDecoding); + return new Supply(this.c); } /** @@ -476,8 +467,8 @@ export default class AlgodClient extends ServiceClient { * @param index - The asset ID to look up. * @category GET */ - getAssetByID(index: number) { - return new GetAssetByID(this.c, this.intDecoding, index); + getAssetByID(index: number | bigint) { + return new GetAssetByID(this.c, index); } /** @@ -494,8 +485,8 @@ export default class AlgodClient extends ServiceClient { * @param index - The application ID to look up. * @category GET */ - getApplicationByID(index: number) { - return new GetApplicationByID(this.c, this.intDecoding, index); + getApplicationByID(index: number | bigint) { + return new GetApplicationByID(this.c, index); } /** @@ -514,12 +505,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ getApplicationBoxByName(index: number, boxName: Uint8Array) { - return new GetApplicationBoxByName( - this.c, - this.intDecoding, - index, - boxName - ); + return new GetApplicationBoxByName(this.c, index, boxName); } /** @@ -537,7 +523,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ getApplicationBoxes(index: number) { - return new GetApplicationBoxes(this.c, this.intDecoding, index); + return new GetApplicationBoxes(this.c, index); } /** @@ -552,7 +538,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ genesis() { - return new Genesis(this.c, this.intDecoding); + return new Genesis(this.c); } /** @@ -571,7 +557,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ getTransactionProof(round: number, txID: string) { - return new GetTransactionProof(this.c, this.intDecoding, round, txID); + return new GetTransactionProof(this.c, round, txID); } /** @@ -587,7 +573,7 @@ export default class AlgodClient extends ServiceClient { * @param round */ getLightBlockHeaderProof(round: number) { - return new LightBlockHeaderProof(this.c, this.intDecoding, round); + return new LightBlockHeaderProof(this.c, round); } /** @@ -603,7 +589,7 @@ export default class AlgodClient extends ServiceClient { * @param round */ getStateProof(round: number) { - return new StateProof(this.c, this.intDecoding, round); + return new StateProof(this.c, round); } /** @@ -628,13 +614,13 @@ export default class AlgodClient extends ServiceClient { * @category POST */ simulateRawTransactions(stxOrStxs: Uint8Array | Uint8Array[]) { - const txnObjects: EncodedSignedTransaction[] = []; + const txnObjects: SignedTransaction[] = []; if (Array.isArray(stxOrStxs)) { for (const stxn of stxOrStxs) { - txnObjects.push(encoding.decode(stxn) as EncodedSignedTransaction); + txnObjects.push(encoding.decodeMsgpack(stxn, SignedTransaction)); } } else { - txnObjects.push(encoding.decode(stxOrStxs) as EncodedSignedTransaction); + txnObjects.push(encoding.decodeMsgpack(stxOrStxs, SignedTransaction)); } const request = new modelsv2.SimulateRequest({ txnGroups: [ @@ -693,7 +679,7 @@ export default class AlgodClient extends ServiceClient { * @category POST */ setBlockOffsetTimestamp(offset: number) { - return new SetBlockOffsetTimestamp(this.c, this.intDecoding, offset); + return new SetBlockOffsetTimestamp(this.c, offset); } /** @@ -708,7 +694,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ getBlockOffsetTimestamp() { - return new GetBlockOffsetTimestamp(this.c, this.intDecoding); + return new GetBlockOffsetTimestamp(this.c); } /** @@ -725,7 +711,7 @@ export default class AlgodClient extends ServiceClient { * @category POST */ setSyncRound(round: number) { - return new SetSyncRound(this.c, this.intDecoding, round); + return new SetSyncRound(this.c, round); } /** @@ -740,7 +726,7 @@ export default class AlgodClient extends ServiceClient { * @category DELETE */ unsetSyncRound() { - return new UnsetSyncRound(this.c, this.intDecoding); + return new UnsetSyncRound(this.c); } /** @@ -755,7 +741,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ getSyncRound() { - return new GetSyncRound(this.c, this.intDecoding); + return new GetSyncRound(this.c); } /** @@ -770,7 +756,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ ready() { - return new Ready(this.c, this.intDecoding); + return new Ready(this.c); } /** @@ -787,11 +773,7 @@ export default class AlgodClient extends ServiceClient { * @category GET */ getLedgerStateDeltaForTransactionGroup(id: string) { - return new GetLedgerStateDeltaForTransactionGroup( - this.c, - this.intDecoding, - id - ); + return new GetLedgerStateDeltaForTransactionGroup(this.c, id); } /** @@ -807,8 +789,8 @@ export default class AlgodClient extends ServiceClient { * @param round the round number to be searched for * @category GET */ - getLedgerStateDelta(round: bigint) { - return new GetLedgerStateDelta(this.c, this.intDecoding, round); + getLedgerStateDelta(round: number) { + return new GetLedgerStateDelta(this.c, round); } /** @@ -824,11 +806,7 @@ export default class AlgodClient extends ServiceClient { * @param round the round number to be searched for * @category GET */ - getTransactionGroupLedgerStateDeltasForRound(round: bigint) { - return new GetTransactionGroupLedgerStateDeltasForRound( - this.c, - this.intDecoding, - round - ); + getTransactionGroupLedgerStateDeltasForRound(round: number) { + return new GetTransactionGroupLedgerStateDeltasForRound(this.c, round); } } diff --git a/src/client/v2/algod/block.ts b/src/client/v2/algod/block.ts index 24dc2e271..52ac5bfbb 100644 --- a/src/client/v2/algod/block.ts +++ b/src/client/v2/algod/block.ts @@ -1,17 +1,16 @@ -import * as encoding from '../../../encoding/encoding'; -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeMsgpack } from '../../../encoding/encoding.js'; +import { BlockResponse } from './models/types.js'; /** * block gets the block info for the given round. this call may block */ -export default class Block extends JSONRequest { +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' }; } @@ -21,10 +20,7 @@ export default class Block extends JSONRequest { } // eslint-disable-next-line class-methods-use-this - prepare(body: Uint8Array) { - if (body && body.byteLength > 0) { - return encoding.decode(body) as Record; - } - return undefined; + prepare(response: HTTPClientResponse): BlockResponse { + return decodeMsgpack(response.body, BlockResponse); } } diff --git a/src/client/v2/algod/compile.ts b/src/client/v2/algod/compile.ts index db8769c0e..927434a56 100644 --- a/src/client/v2/algod/compile.ts +++ b/src/client/v2/algod/compile.ts @@ -1,12 +1,14 @@ -import { Buffer } from 'buffer'; -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; +import { coerceToBytes } from '../../../encoding/binarydata.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { CompileResponse } from './models/types.js'; +import JSONRequest from '../jsonrequest.js'; /** * Sets the default header (if not previously set) * @param headers - A headers object */ -export function setHeaders(headers = {}) { +export function setHeaders(headers: Record = {}) { let hdrs = headers; if (Object.keys(hdrs).every((key) => key.toLowerCase() !== 'content-type')) { hdrs = { ...headers }; @@ -18,10 +20,12 @@ export function setHeaders(headers = {}) { /** * Executes compile */ -export default class Compile extends JSONRequest { - constructor(c: HTTPClient, private source: string | Uint8Array) { +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 @@ -34,18 +38,22 @@ export default class Compile extends JSONRequest { return this; } - /** - * Executes compile - * @param headers - A headers object - */ - async do(headers = {}) { + protected executeRequest( + headers?: Record, + customOptions?: Record + ): Promise { const txHeaders = setHeaders(headers); - const res = await this.c.post( - this.path(), - Buffer.from(this.source), - txHeaders, - this.query - ); - return res.body; + return this.c.post({ + relativePath: this.path(), + data: coerceToBytes(this.source), + query: this.query, + requestHeaders: txHeaders, + customOptions, + }); + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): CompileResponse { + return decodeJSON(response.getJSONText(), CompileResponse); } } diff --git a/src/client/v2/algod/disassemble.ts b/src/client/v2/algod/disassemble.ts index 552e8c622..7ecfb8118 100644 --- a/src/client/v2/algod/disassemble.ts +++ b/src/client/v2/algod/disassemble.ts @@ -1,12 +1,14 @@ -import { Buffer } from 'buffer'; -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; +import { coerceToBytes } from '../../../encoding/binarydata.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { DisassembleResponse } from './models/types.js'; +import JSONRequest from '../jsonrequest.js'; /** * Sets the default header (if not previously set) * @param headers - A headers object */ -export function setHeaders(headers = {}) { +export function setHeaders(headers: Record = {}) { let hdrs = headers; if (Object.keys(hdrs).every((key) => key.toLowerCase() !== 'content-type')) { hdrs = { ...headers }; @@ -18,10 +20,12 @@ export function setHeaders(headers = {}) { /** * Executes disassemble */ -export default class Disassemble extends JSONRequest { - constructor(c: HTTPClient, private source: string | Uint8Array) { +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 @@ -29,18 +33,22 @@ export default class Disassemble extends JSONRequest { return `/v2/teal/disassemble`; } - /** - * Executes disassemble - * @param headers - A headers object - */ - async do(headers = {}) { + protected executeRequest( + headers?: Record, + customOptions?: Record + ): Promise { const txHeaders = setHeaders(headers); - const res = await this.c.post( - this.path(), - Buffer.from(this.source), - txHeaders, - this.query - ); - return res.body; + return this.c.post({ + relativePath: this.path(), + data: coerceToBytes(this.source), + query: this.query, + requestHeaders: txHeaders, + customOptions, + }); + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): DisassembleResponse { + return decodeJSON(response.getJSONText(), DisassembleResponse); } } diff --git a/src/client/v2/algod/dryrun.ts b/src/client/v2/algod/dryrun.ts index 98b2fb463..9c2381a52 100644 --- a/src/client/v2/algod/dryrun.ts +++ b/src/client/v2/algod/dryrun.ts @@ -1,16 +1,16 @@ -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'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON, encodeMsgpack } from '../../../encoding/encoding.js'; +import JSONRequest from '../jsonrequest.js'; +import { setHeaders } from './compile.js'; +import { DryrunResponse } from './models/types.js'; +import * as modelsv2 from './models/types.js'; -export default class Dryrun extends JSONRequest { +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)); + this.blob = encodeMsgpack(dr); } // eslint-disable-next-line class-methods-use-this @@ -18,17 +18,21 @@ export default class Dryrun extends JSONRequest { return '/v2/teal/dryrun'; } - /** - * Executes dryrun - * @param headers - A headers object - */ - async do(headers = {}) { + protected executeRequest( + headers?: Record, + customOptions?: Record + ): Promise { const txHeaders = setHeaders(headers); - const res = await this.c.post( - this.path(), - Buffer.from(this.blob), - txHeaders - ); - return res.body; + return this.c.post({ + relativePath: this.path(), + data: this.blob, + requestHeaders: txHeaders, + customOptions, + }); + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): DryrunResponse { + return decodeJSON(response.getJSONText(), DryrunResponse); } } diff --git a/src/client/v2/algod/genesis.ts b/src/client/v2/algod/genesis.ts index 2f88cad3c..497b4c797 100644 --- a/src/client/v2/algod/genesis.ts +++ b/src/client/v2/algod/genesis.ts @@ -1,8 +1,14 @@ -import JSONRequest from '../jsonrequest'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; -export default class Genesis extends JSONRequest { +export default class Genesis extends JSONRequest { // eslint-disable-next-line class-methods-use-this path() { return '/genesis'; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): string { + return response.getJSONText(); + } } diff --git a/src/client/v2/algod/getApplicationBoxByName.ts b/src/client/v2/algod/getApplicationBoxByName.ts index b58aaf845..415f88709 100644 --- a/src/client/v2/algod/getApplicationBoxByName.ts +++ b/src/client/v2/algod/getApplicationBoxByName.ts @@ -1,8 +1,8 @@ -import { Buffer } from 'buffer'; -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; -import { Box } from './models/types'; +import { bytesToBase64 } from '../../../encoding/binarydata.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import JSONRequest from '../jsonrequest.js'; +import { Box } from './models/types.js'; /** * Given an application ID and the box name (key), return the value stored in the box. @@ -19,20 +19,15 @@ import { Box } from './models/types'; * @param index - The application ID to look up. * @category GET */ -export default class GetApplicationBoxByName extends JSONRequest< - Box, - Record -> { +export default class GetApplicationBoxByName extends JSONRequest { constructor( c: HTTPClient, - intDecoding: IntDecoding, private index: number, name: Uint8Array ) { - super(c, intDecoding); - this.index = index; + super(c); // Encode name in base64 format and append the encoding prefix. - const encodedName = Buffer.from(name).toString('base64'); + const encodedName = bytesToBase64(name); this.query.name = encodeURI(`b64:${encodedName}`); } @@ -44,7 +39,7 @@ export default class GetApplicationBoxByName extends JSONRequest< } // eslint-disable-next-line class-methods-use-this - prepare(body: Record): Box { - return Box.from_obj_for_encoding(body); + prepare(response: HTTPClientResponse): Box { + return decodeJSON(response.getJSONText(), Box); } } diff --git a/src/client/v2/algod/getApplicationBoxes.ts b/src/client/v2/algod/getApplicationBoxes.ts index 68f6f0447..96b9cbd96 100644 --- a/src/client/v2/algod/getApplicationBoxes.ts +++ b/src/client/v2/algod/getApplicationBoxes.ts @@ -1,7 +1,7 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; -import { BoxesResponse } from './models/types'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { BoxesResponse } from './models/types.js'; /** * Given an application ID, return all the box names associated with the app. @@ -17,13 +17,12 @@ import { BoxesResponse } from './models/types'; * @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; +export default class GetApplicationBoxes extends JSONRequest { + constructor( + c: HTTPClient, + private index: number + ) { + super(c); this.query.max = 0; } @@ -55,7 +54,7 @@ export default class GetApplicationBoxes extends JSONRequest< } // eslint-disable-next-line class-methods-use-this - prepare(body: Record): BoxesResponse { - return BoxesResponse.from_obj_for_encoding(body); + prepare(response: HTTPClientResponse): BoxesResponse { + return decodeJSON(response.getJSONText(), BoxesResponse); } } diff --git a/src/client/v2/algod/getApplicationByID.ts b/src/client/v2/algod/getApplicationByID.ts index 91e1c12e1..60be5ea7c 100644 --- a/src/client/v2/algod/getApplicationByID.ts +++ b/src/client/v2/algod/getApplicationByID.ts @@ -1,14 +1,22 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Application } from './models/types.js'; -export default class GetApplicationByID extends JSONRequest { - constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { - super(c, intDecoding); - this.index = index; +export default class GetApplicationByID extends JSONRequest { + constructor( + c: HTTPClient, + private index: number | bigint + ) { + super(c); } path() { return `/v2/applications/${this.index}`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): Application { + return decodeJSON(response.getJSONText(), Application); + } } diff --git a/src/client/v2/algod/getAssetByID.ts b/src/client/v2/algod/getAssetByID.ts index 3abb5696d..9ad064eaa 100644 --- a/src/client/v2/algod/getAssetByID.ts +++ b/src/client/v2/algod/getAssetByID.ts @@ -1,14 +1,22 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Asset } from './models/types.js'; -export default class GetAssetByID extends JSONRequest { - constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { - super(c, intDecoding); - this.index = index; +export default class GetAssetByID extends JSONRequest { + constructor( + c: HTTPClient, + private index: number | bigint + ) { + super(c); } path() { return `/v2/assets/${this.index}`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): Asset { + return decodeJSON(response.getJSONText(), Asset); + } } diff --git a/src/client/v2/algod/getBlockHash.ts b/src/client/v2/algod/getBlockHash.ts index 40d01b499..57991a430 100644 --- a/src/client/v2/algod/getBlockHash.ts +++ b/src/client/v2/algod/getBlockHash.ts @@ -1,18 +1,22 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { BlockHashResponse } from './models/types.js'; -export default class GetBlockHash extends JSONRequest { - round: number; +export default class GetBlockHash extends JSONRequest { + round: number | bigint; - constructor(c: HTTPClient, intDecoding: IntDecoding, roundNumber: number) { - super(c, intDecoding); - if (!Number.isInteger(roundNumber)) - throw Error('roundNumber should be an integer'); + constructor(c: HTTPClient, roundNumber: number) { + super(c); this.round = roundNumber; } path() { return `/v2/blocks/${this.round}/hash`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): BlockHashResponse { + return decodeJSON(response.getJSONText(), BlockHashResponse); + } } diff --git a/src/client/v2/algod/getBlockOffsetTimestamp.ts b/src/client/v2/algod/getBlockOffsetTimestamp.ts index a2c04fe53..df7d6400d 100644 --- a/src/client/v2/algod/getBlockOffsetTimestamp.ts +++ b/src/client/v2/algod/getBlockOffsetTimestamp.ts @@ -1,17 +1,16 @@ -import JSONRequest from '../jsonrequest'; -import { GetBlockTimeStampOffsetResponse } from './models/types'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { GetBlockTimeStampOffsetResponse } from './models/types.js'; -export default class GetBlockOffsetTimestamp extends JSONRequest< - GetBlockTimeStampOffsetResponse, - Record -> { +export default class GetBlockOffsetTimestamp extends JSONRequest { // 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); + prepare(response: HTTPClientResponse): GetBlockTimeStampOffsetResponse { + return decodeJSON(response.getJSONText(), GetBlockTimeStampOffsetResponse); } } diff --git a/src/client/v2/algod/getBlockTxids.ts b/src/client/v2/algod/getBlockTxids.ts index fe52ae8d6..479721c71 100644 --- a/src/client/v2/algod/getBlockTxids.ts +++ b/src/client/v2/algod/getBlockTxids.ts @@ -1,12 +1,13 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { BlockTxidsResponse } from './models/types.js'; -export default class GetBlockTxids extends JSONRequest { +export default class GetBlockTxids extends JSONRequest { round: number; - constructor(c: HTTPClient, intDecoding: IntDecoding, roundNumber: number) { - super(c, intDecoding); + constructor(c: HTTPClient, roundNumber: number) { + super(c); if (!Number.isInteger(roundNumber)) throw Error('roundNumber should be an integer'); this.round = roundNumber; @@ -15,4 +16,9 @@ export default class GetBlockTxids extends JSONRequest { path() { return `/v2/blocks/${this.round}/txids`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): BlockTxidsResponse { + return decodeJSON(response.getJSONText(), BlockTxidsResponse); + } } diff --git a/src/client/v2/algod/getLedgerStateDelta.ts b/src/client/v2/algod/getLedgerStateDelta.ts index ac69daeab..131154c6b 100644 --- a/src/client/v2/algod/getLedgerStateDelta.ts +++ b/src/client/v2/algod/getLedgerStateDelta.ts @@ -1,16 +1,24 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeMsgpack } from '../../../encoding/encoding.js'; +import { LedgerStateDelta } from '../../../types/statedelta.js'; -export default class GetLedgerStateDelta extends JSONRequest { - constructor(c: HTTPClient, intDecoding: IntDecoding, private round: bigint) { - super(c, intDecoding); - this.round = round; - this.query = { format: 'json' }; +export default class GetLedgerStateDelta extends JSONRequest { + constructor( + c: HTTPClient, + private round: number + ) { + super(c); + this.query = { format: 'msgpack' }; } // eslint-disable-next-line class-methods-use-this path() { return `/v2/deltas/${this.round}`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): LedgerStateDelta { + return decodeMsgpack(response.body, LedgerStateDelta); + } } diff --git a/src/client/v2/algod/getLedgerStateDeltaForTransactionGroup.ts b/src/client/v2/algod/getLedgerStateDeltaForTransactionGroup.ts index 4186232dc..c566ea777 100644 --- a/src/client/v2/algod/getLedgerStateDeltaForTransactionGroup.ts +++ b/src/client/v2/algod/getLedgerStateDeltaForTransactionGroup.ts @@ -1,16 +1,24 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeMsgpack } from '../../../encoding/encoding.js'; +import { LedgerStateDelta } from '../../../types/statedelta.js'; -export default class GetLedgerStateDeltaForTransactionGroup extends JSONRequest { - constructor(c: HTTPClient, intDecoding: IntDecoding, private id: string) { - super(c, intDecoding); - this.id = id; - this.query = { format: 'json' }; +export default class GetLedgerStateDeltaForTransactionGroup extends JSONRequest { + constructor( + c: HTTPClient, + private id: string + ) { + super(c); + this.query = { format: 'msgpack' }; } // eslint-disable-next-line class-methods-use-this path() { return `/v2/deltas/txn/group/${this.id}`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): LedgerStateDelta { + return decodeMsgpack(response.body, LedgerStateDelta); + } } diff --git a/src/client/v2/algod/getSyncRound.ts b/src/client/v2/algod/getSyncRound.ts index 4a761a150..20648f574 100644 --- a/src/client/v2/algod/getSyncRound.ts +++ b/src/client/v2/algod/getSyncRound.ts @@ -1,17 +1,16 @@ -import JSONRequest from '../jsonrequest'; -import { GetSyncRoundResponse } from './models/types'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { GetSyncRoundResponse } from './models/types.js'; -export default class GetSyncRound extends JSONRequest< - GetSyncRoundResponse, - Record -> { +export default class GetSyncRound extends JSONRequest { // 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); + prepare(response: HTTPClientResponse): GetSyncRoundResponse { + return decodeJSON(response.getJSONText(), GetSyncRoundResponse); } } diff --git a/src/client/v2/algod/getTransactionGroupLedgerStateDeltasForRound.ts b/src/client/v2/algod/getTransactionGroupLedgerStateDeltasForRound.ts index 1c1776034..3e17b6ef0 100644 --- a/src/client/v2/algod/getTransactionGroupLedgerStateDeltasForRound.ts +++ b/src/client/v2/algod/getTransactionGroupLedgerStateDeltasForRound.ts @@ -1,16 +1,15 @@ -import JSONRequest from '../jsonrequest'; -import { TransactionGroupLedgerStateDeltasForRoundResponse } from './models/types'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { TransactionGroupLedgerStateDeltasForRoundResponse } from './models/types.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeMsgpack } from '../../../encoding/encoding.js'; -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' }; +export default class GetTransactionGroupLedgerStateDeltasForRound extends JSONRequest { + constructor( + c: HTTPClient, + private round: number + ) { + super(c); + this.query = { format: 'msgpack' }; } // eslint-disable-next-line class-methods-use-this @@ -20,10 +19,11 @@ export default class GetTransactionGroupLedgerStateDeltasForRound extends JSONRe // eslint-disable-next-line class-methods-use-this prepare( - body: Record + response: HTTPClientResponse ): TransactionGroupLedgerStateDeltasForRoundResponse { - return TransactionGroupLedgerStateDeltasForRoundResponse.from_obj_for_encoding( - body + return decodeMsgpack( + response.body, + TransactionGroupLedgerStateDeltasForRoundResponse ); } } diff --git a/src/client/v2/algod/getTransactionProof.ts b/src/client/v2/algod/getTransactionProof.ts index 398039a43..ac3d5c524 100644 --- a/src/client/v2/algod/getTransactionProof.ts +++ b/src/client/v2/algod/getTransactionProof.ts @@ -1,18 +1,15 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { TransactionProofResponse } from './models/types.js'; -export default class GetTransactionProof extends JSONRequest { +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; + super(c); } path() { @@ -40,4 +37,9 @@ export default class GetTransactionProof extends JSONRequest { this.query.hashtype = hashType; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): TransactionProofResponse { + return decodeJSON(response.getJSONText(), TransactionProofResponse); + } } diff --git a/src/client/v2/algod/healthCheck.ts b/src/client/v2/algod/healthCheck.ts index e6077f06f..9f0254778 100644 --- a/src/client/v2/algod/healthCheck.ts +++ b/src/client/v2/algod/healthCheck.ts @@ -1,19 +1,15 @@ -import JSONRequest from '../jsonrequest'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; /** * healthCheck returns an empty object iff the node is running */ -export default class HealthCheck extends JSONRequest { +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 {}; - } + // eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars + prepare(_response: HTTPClientResponse): void {} } diff --git a/src/client/v2/algod/index.ts b/src/client/v2/algod/index.ts new file mode 100644 index 000000000..4f841407b --- /dev/null +++ b/src/client/v2/algod/index.ts @@ -0,0 +1,3 @@ +// ALGOD +export { AlgodClient } from './algod.js'; +export * from './models/types.js'; diff --git a/src/client/v2/algod/lightBlockHeaderProof.ts b/src/client/v2/algod/lightBlockHeaderProof.ts index ac3794c36..37e6720f3 100644 --- a/src/client/v2/algod/lightBlockHeaderProof.ts +++ b/src/client/v2/algod/lightBlockHeaderProof.ts @@ -1,15 +1,22 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { LightBlockHeaderProof as LBHP } from './models/types.js'; -export default class LightBlockHeaderProof extends JSONRequest { - constructor(c: HTTPClient, intDecoding: IntDecoding, private round: number) { - super(c, intDecoding); - - this.round = round; +export default class LightBlockHeaderProof extends JSONRequest { + constructor( + c: HTTPClient, + private round: number + ) { + super(c); } path() { return `/v2/blocks/${this.round}/lightheader/proof`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): LBHP { + return decodeJSON(response.getJSONText(), LBHP); + } } diff --git a/src/client/v2/algod/models/types.ts b/src/client/v2/algod/models/types.ts index f77a3fa48..7fc46ea60 100644 --- a/src/client/v2/algod/models/types.ts +++ b/src/client/v2/algod/models/types.ts @@ -3,17 +3,166 @@ */ /* 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'; +import { ensureBigInt, ensureSafeInteger } from '../../../../utils/utils.js'; +import { Encodable, Schema } from '../../../../encoding/encoding.js'; +import { + NamedMapSchema, + ArraySchema, + Uint64Schema, + StringSchema, + BooleanSchema, + ByteArraySchema, + OptionalSchema, +} from '../../../../encoding/schema/index.js'; +import { base64ToBytes } from '../../../../encoding/binarydata.js'; +import { Block } from '../../../../types/block.js'; +import { LedgerStateDelta } from '../../../../types/statedelta.js'; +import { SignedTransaction } from '../../../../signedTransaction.js'; +import { Address } from '../../../../encoding/address.js'; +import { UntypedValue } from '../../untypedmodel.js'; /** * Account information at a given round. * Definition: * data/basics/userBalance.go : AccountData */ -export class Account extends BaseModel { +export class Account implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'amount-without-pending-rewards', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'min-balance', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'pending-rewards', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'rewards', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'status', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'total-apps-opted-in', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-assets-opted-in', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-created-apps', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-created-assets', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'apps-local-state', + valueSchema: new OptionalSchema( + new ArraySchema(ApplicationLocalState.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'apps-total-extra-pages', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'apps-total-schema', + valueSchema: new OptionalSchema( + ApplicationStateSchema.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'assets', + valueSchema: new OptionalSchema( + new ArraySchema(AssetHolding.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'auth-addr', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'created-apps', + valueSchema: new OptionalSchema( + new ArraySchema(Application.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'created-assets', + valueSchema: new OptionalSchema( + new ArraySchema(Asset.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'incentive-eligible', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'last-heartbeat', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'last-proposed', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'participation', + valueSchema: new OptionalSchema(AccountParticipation.encodingSchema), + omitEmpty: true, + }, + { + key: 'reward-base', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'sig-type', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'total-box-bytes', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'total-boxes', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * the account public key */ @@ -22,34 +171,34 @@ export class Account extends BaseModel { /** * (algo) total number of MicroAlgos in the account */ - public amount: number | bigint; + public amount: bigint; /** * specifies the amount of MicroAlgos in the account, without the pending rewards. */ - public amountWithoutPendingRewards: number | bigint; + public amountWithoutPendingRewards: bigint; /** * MicroAlgo balance required by the account. * The requirement grows based on asset and application usage. */ - public minBalance: number | bigint; + public minBalance: bigint; /** * amount of MicroAlgos of pending rewards in this account. */ - public pendingRewards: number | bigint; + public pendingRewards: bigint; /** * (ern) total rewards of MicroAlgos the account has received, including pending * rewards. */ - public rewards: number | bigint; + public rewards: bigint; /** * The round for which this information is relevant. */ - public round: number | bigint; + public round: bigint; /** * (onl) delegation status of the account's MicroAlgos @@ -65,23 +214,23 @@ export class Account extends BaseModel { * 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; + public totalAppsOptedIn: number; /** * 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; + public totalAssetsOptedIn: number; /** * The count of all apps (AppParams objects) created by this account. */ - public totalCreatedApps: number | bigint; + public totalCreatedApps: number; /** * The count of all assets (AssetParams objects) created by this account. */ - public totalCreatedAssets: number | bigint; + public totalCreatedAssets: number; /** * (appl) applications local data stored in this account. @@ -92,7 +241,7 @@ export class Account extends BaseModel { /** * (teap) the sum of all extra application program pages for this account. */ - public appsTotalExtraPages?: number | bigint; + public appsTotalExtraPages?: number; /** * (tsch) stores the sum of all of the local schemas and global schemas in this @@ -112,7 +261,7 @@ export class Account extends BaseModel { * address of the current account is used. This field can be updated in any * transaction by setting the RekeyTo field. */ - public authAddr?: string; + public authAddr?: Address; /** * (appp) parameters of applications created by this account including app global @@ -137,12 +286,12 @@ export class Account extends BaseModel { * The round in which this account last went online, or explicitly renewed their * online status. */ - public lastHeartbeat?: number | bigint; + public lastHeartbeat?: number; /** * The round in which this account last proposed the block. */ - public lastProposed?: number | bigint; + public lastProposed?: number; /** * AccountParticipation describes the parameters used by this account in consensus @@ -154,7 +303,7 @@ export class Account extends BaseModel { * (ebase) used as part of the rewards computation. Only applicable to accounts * which are participating. */ - public rewardBase?: number | bigint; + public rewardBase?: bigint; /** * Indicates what type of signature is used by this account, must be one of: @@ -168,12 +317,12 @@ export class Account extends BaseModel { * (tbxb) The total number of bytes used by this account's app's box keys and * values. */ - public totalBoxBytes?: number | bigint; + public totalBoxBytes?: number; /** * (tbx) The number of existing boxes created by this account's app. */ - public totalBoxes?: number | bigint; + public totalBoxes?: number; /** * Creates a new `Account` object. @@ -276,7 +425,7 @@ export class Account extends BaseModel { appsTotalExtraPages?: number | bigint; appsTotalSchema?: ApplicationStateSchema; assets?: AssetHolding[]; - authAddr?: string; + authAddr?: Address | string; createdApps?: Application[]; createdAssets?: Asset[]; incentiveEligible?: boolean; @@ -288,159 +437,187 @@ export class Account extends BaseModel { 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.amount = ensureBigInt(amount); + this.amountWithoutPendingRewards = ensureBigInt( + amountWithoutPendingRewards + ); + this.minBalance = ensureBigInt(minBalance); + this.pendingRewards = ensureBigInt(pendingRewards); + this.rewards = ensureBigInt(rewards); + this.round = ensureBigInt(round); this.status = status; - this.totalAppsOptedIn = totalAppsOptedIn; - this.totalAssetsOptedIn = totalAssetsOptedIn; - this.totalCreatedApps = totalCreatedApps; - this.totalCreatedAssets = totalCreatedAssets; + this.totalAppsOptedIn = ensureSafeInteger(totalAppsOptedIn); + this.totalAssetsOptedIn = ensureSafeInteger(totalAssetsOptedIn); + this.totalCreatedApps = ensureSafeInteger(totalCreatedApps); + this.totalCreatedAssets = ensureSafeInteger(totalCreatedAssets); this.appsLocalState = appsLocalState; - this.appsTotalExtraPages = appsTotalExtraPages; + this.appsTotalExtraPages = + typeof appsTotalExtraPages === 'undefined' + ? undefined + : ensureSafeInteger(appsTotalExtraPages); this.appsTotalSchema = appsTotalSchema; this.assets = assets; - this.authAddr = authAddr; + this.authAddr = + typeof authAddr === 'string' ? Address.fromString(authAddr) : authAddr; this.createdApps = createdApps; this.createdAssets = createdAssets; this.incentiveEligible = incentiveEligible; - this.lastHeartbeat = lastHeartbeat; - this.lastProposed = lastProposed; + this.lastHeartbeat = + typeof lastHeartbeat === 'undefined' + ? undefined + : ensureSafeInteger(lastHeartbeat); + this.lastProposed = + typeof lastProposed === 'undefined' + ? undefined + : ensureSafeInteger(lastProposed); this.participation = participation; - this.rewardBase = rewardBase; + this.rewardBase = + typeof rewardBase === 'undefined' ? undefined : ensureBigInt(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', - incentiveEligible: 'incentive-eligible', - lastHeartbeat: 'last-heartbeat', - lastProposed: 'last-proposed', - 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}` - ); + this.totalBoxBytes = + typeof totalBoxBytes === 'undefined' + ? undefined + : ensureSafeInteger(totalBoxBytes); + this.totalBoxes = + typeof totalBoxes === 'undefined' + ? undefined + : ensureSafeInteger(totalBoxes); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Account.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['address', this.address], + ['amount', this.amount], + ['amount-without-pending-rewards', this.amountWithoutPendingRewards], + ['min-balance', this.minBalance], + ['pending-rewards', this.pendingRewards], + ['rewards', this.rewards], + ['round', this.round], + ['status', this.status], + ['total-apps-opted-in', this.totalAppsOptedIn], + ['total-assets-opted-in', this.totalAssetsOptedIn], + ['total-created-apps', this.totalCreatedApps], + ['total-created-assets', this.totalCreatedAssets], + [ + 'apps-local-state', + typeof this.appsLocalState !== 'undefined' + ? this.appsLocalState.map((v) => v.toEncodingData()) + : undefined, + ], + ['apps-total-extra-pages', this.appsTotalExtraPages], + [ + 'apps-total-schema', + typeof this.appsTotalSchema !== 'undefined' + ? this.appsTotalSchema.toEncodingData() + : undefined, + ], + [ + 'assets', + typeof this.assets !== 'undefined' + ? this.assets.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'auth-addr', + typeof this.authAddr !== 'undefined' + ? this.authAddr.toString() + : undefined, + ], + [ + 'created-apps', + typeof this.createdApps !== 'undefined' + ? this.createdApps.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'created-assets', + typeof this.createdAssets !== 'undefined' + ? this.createdAssets.map((v) => v.toEncodingData()) + : undefined, + ], + ['incentive-eligible', this.incentiveEligible], + ['last-heartbeat', this.lastHeartbeat], + ['last-proposed', this.lastProposed], + [ + 'participation', + typeof this.participation !== 'undefined' + ? this.participation.toEncodingData() + : undefined, + ], + ['reward-base', this.rewardBase], + ['sig-type', this.sigType], + ['total-box-bytes', this.totalBoxBytes], + ['total-boxes', this.totalBoxes], + ]); + } + + static fromEncodingData(data: unknown): Account { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Account: ${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'], + address: data.get('address'), + amount: data.get('amount'), + amountWithoutPendingRewards: data.get('amount-without-pending-rewards'), + minBalance: data.get('min-balance'), + pendingRewards: data.get('pending-rewards'), + rewards: data.get('rewards'), + round: data.get('round'), + status: data.get('status'), + totalAppsOptedIn: data.get('total-apps-opted-in'), + totalAssetsOptedIn: data.get('total-assets-opted-in'), + totalCreatedApps: data.get('total-created-apps'), + totalCreatedAssets: data.get('total-created-assets'), appsLocalState: - typeof data['apps-local-state'] !== 'undefined' - ? data['apps-local-state'].map( - ApplicationLocalState.from_obj_for_encoding - ) + typeof data.get('apps-local-state') !== 'undefined' + ? data + .get('apps-local-state') + .map((v: unknown) => ApplicationLocalState.fromEncodingData(v)) : undefined, - appsTotalExtraPages: data['apps-total-extra-pages'], + appsTotalExtraPages: data.get('apps-total-extra-pages'), appsTotalSchema: - typeof data['apps-total-schema'] !== 'undefined' - ? ApplicationStateSchema.from_obj_for_encoding( - data['apps-total-schema'] + typeof data.get('apps-total-schema') !== 'undefined' + ? ApplicationStateSchema.fromEncodingData( + data.get('apps-total-schema') ) : undefined, assets: - typeof data['assets'] !== 'undefined' - ? data['assets'].map(AssetHolding.from_obj_for_encoding) + typeof data.get('assets') !== 'undefined' + ? data + .get('assets') + .map((v: unknown) => AssetHolding.fromEncodingData(v)) : undefined, - authAddr: data['auth-addr'], + authAddr: data.get('auth-addr'), createdApps: - typeof data['created-apps'] !== 'undefined' - ? data['created-apps'].map(Application.from_obj_for_encoding) + typeof data.get('created-apps') !== 'undefined' + ? data + .get('created-apps') + .map((v: unknown) => Application.fromEncodingData(v)) : undefined, createdAssets: - typeof data['created-assets'] !== 'undefined' - ? data['created-assets'].map(Asset.from_obj_for_encoding) + typeof data.get('created-assets') !== 'undefined' + ? data + .get('created-assets') + .map((v: unknown) => Asset.fromEncodingData(v)) : undefined, - incentiveEligible: data['incentive-eligible'], - lastHeartbeat: data['last-heartbeat'], - lastProposed: data['last-proposed'], + incentiveEligible: data.get('incentive-eligible'), + lastHeartbeat: data.get('last-heartbeat'), + lastProposed: data.get('last-proposed'), participation: - typeof data['participation'] !== 'undefined' - ? AccountParticipation.from_obj_for_encoding(data['participation']) + typeof data.get('participation') !== 'undefined' + ? AccountParticipation.fromEncodingData(data.get('participation')) : undefined, - rewardBase: data['reward-base'], - sigType: data['sig-type'], - totalBoxBytes: data['total-box-bytes'], - totalBoxes: data['total-boxes'], + rewardBase: data.get('reward-base'), + sigType: data.get('sig-type'), + totalBoxBytes: data.get('total-box-bytes'), + totalBoxes: data.get('total-boxes'), }); - /* eslint-enable dot-notation */ } } @@ -450,11 +627,33 @@ export class Account extends BaseModel { * application ID. Global state will only be returned if the provided address is * the application's creator. */ -export class AccountApplicationResponse extends BaseModel { +export class AccountApplicationResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'app-local-state', + valueSchema: new OptionalSchema(ApplicationLocalState.encodingSchema), + omitEmpty: true, + }, + { + key: 'created-app', + valueSchema: new OptionalSchema(ApplicationParams.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * The round for which this information is relevant. */ - public round: number | bigint; + public round: bigint; /** * (appl) the application local data stored in this account. @@ -487,37 +686,49 @@ export class AccountApplicationResponse extends BaseModel { appLocalState?: ApplicationLocalState; createdApp?: ApplicationParams; }) { - super(); - this.round = round; + this.round = ensureBigInt(round); this.appLocalState = appLocalState; this.createdApp = createdApp; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountApplicationResponse.encodingSchema; + } - this.attribute_map = { - round: 'round', - appLocalState: 'app-local-state', - createdApp: 'created-app', - }; + toEncodingData(): Map { + return new Map([ + ['round', this.round], + [ + 'app-local-state', + typeof this.appLocalState !== 'undefined' + ? this.appLocalState.toEncodingData() + : undefined, + ], + [ + 'created-app', + typeof this.createdApp !== 'undefined' + ? this.createdApp.toEncodingData() + : undefined, + ], + ]); } - // 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}`); + static fromEncodingData(data: unknown): AccountApplicationResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountApplicationResponse: ${data}`); + } return new AccountApplicationResponse({ - round: data['round'], + round: data.get('round'), appLocalState: - typeof data['app-local-state'] !== 'undefined' - ? ApplicationLocalState.from_obj_for_encoding(data['app-local-state']) + typeof data.get('app-local-state') !== 'undefined' + ? ApplicationLocalState.fromEncodingData(data.get('app-local-state')) : undefined, createdApp: - typeof data['created-app'] !== 'undefined' - ? ApplicationParams.from_obj_for_encoding(data['created-app']) + typeof data.get('created-app') !== 'undefined' + ? ApplicationParams.fromEncodingData(data.get('created-app')) : undefined, }); - /* eslint-enable dot-notation */ } } @@ -525,7 +736,28 @@ export class AccountApplicationResponse extends BaseModel { * AccountAssetHolding describes the account's asset holding and asset parameters * (if either exist) for a specific asset ID. */ -export class AccountAssetHolding extends BaseModel { +export class AccountAssetHolding implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'asset-holding', + valueSchema: AssetHolding.encodingSchema, + omitEmpty: true, + }, + { + key: 'asset-params', + valueSchema: new OptionalSchema(AssetParams.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (asset) Details about the asset held by this account. * The raw account uses `AssetHolding` for this type. @@ -552,31 +784,40 @@ export class AccountAssetHolding extends BaseModel { assetHolding: AssetHolding; assetParams?: AssetParams; }) { - super(); this.assetHolding = assetHolding; this.assetParams = assetParams; + } - this.attribute_map = { - assetHolding: 'asset-holding', - assetParams: 'asset-params', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountAssetHolding.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): AccountAssetHolding { - /* eslint-disable dot-notation */ - if (typeof data['asset-holding'] === 'undefined') - throw new Error( - `Response is missing required field 'asset-holding': ${data}` - ); + toEncodingData(): Map { + return new Map([ + ['asset-holding', this.assetHolding.toEncodingData()], + [ + 'asset-params', + typeof this.assetParams !== 'undefined' + ? this.assetParams.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): AccountAssetHolding { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountAssetHolding: ${data}`); + } return new AccountAssetHolding({ - assetHolding: AssetHolding.from_obj_for_encoding(data['asset-holding']), + assetHolding: AssetHolding.fromEncodingData( + data.get('asset-holding') ?? new Map() + ), assetParams: - typeof data['asset-params'] !== 'undefined' - ? AssetParams.from_obj_for_encoding(data['asset-params']) + typeof data.get('asset-params') !== 'undefined' + ? AssetParams.fromEncodingData(data.get('asset-params')) : undefined, }); - /* eslint-enable dot-notation */ } } @@ -585,11 +826,33 @@ export class AccountAssetHolding extends BaseModel { * (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 { +export class AccountAssetResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'asset-holding', + valueSchema: new OptionalSchema(AssetHolding.encodingSchema), + omitEmpty: true, + }, + { + key: 'created-asset', + valueSchema: new OptionalSchema(AssetParams.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * The round for which this information is relevant. */ - public round: number | bigint; + public round: bigint; /** * (asset) Details about the asset held by this account. @@ -620,48 +883,84 @@ export class AccountAssetResponse extends BaseModel { assetHolding?: AssetHolding; createdAsset?: AssetParams; }) { - super(); - this.round = round; + this.round = ensureBigInt(round); this.assetHolding = assetHolding; this.createdAsset = createdAsset; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountAssetResponse.encodingSchema; + } - this.attribute_map = { - round: 'round', - assetHolding: 'asset-holding', - createdAsset: 'created-asset', - }; + toEncodingData(): Map { + return new Map([ + ['round', this.round], + [ + 'asset-holding', + typeof this.assetHolding !== 'undefined' + ? this.assetHolding.toEncodingData() + : undefined, + ], + [ + 'created-asset', + typeof this.createdAsset !== 'undefined' + ? this.createdAsset.toEncodingData() + : undefined, + ], + ]); } - // 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}`); + static fromEncodingData(data: unknown): AccountAssetResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountAssetResponse: ${data}`); + } return new AccountAssetResponse({ - round: data['round'], + round: data.get('round'), assetHolding: - typeof data['asset-holding'] !== 'undefined' - ? AssetHolding.from_obj_for_encoding(data['asset-holding']) + typeof data.get('asset-holding') !== 'undefined' + ? AssetHolding.fromEncodingData(data.get('asset-holding')) : undefined, createdAsset: - typeof data['created-asset'] !== 'undefined' - ? AssetParams.from_obj_for_encoding(data['created-asset']) + typeof data.get('created-asset') !== 'undefined' + ? AssetParams.fromEncodingData(data.get('created-asset')) : undefined, }); - /* eslint-enable dot-notation */ } } /** * AccountAssetsInformationResponse contains a list of assets held by an account. */ -export class AccountAssetsInformationResponse extends BaseModel { +export class AccountAssetsInformationResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'asset-holdings', + valueSchema: new OptionalSchema( + new ArraySchema(AccountAssetHolding.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * The round for which this information is relevant. */ - public round: number | bigint; + public round: number; public assetHoldings?: AccountAssetHolding[]; @@ -687,36 +986,45 @@ export class AccountAssetsInformationResponse extends BaseModel { assetHoldings?: AccountAssetHolding[]; nextToken?: string; }) { - super(); - this.round = round; + this.round = ensureSafeInteger(round); this.assetHoldings = assetHoldings; this.nextToken = nextToken; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountAssetsInformationResponse.encodingSchema; + } - this.attribute_map = { - round: 'round', - assetHoldings: 'asset-holdings', - nextToken: 'next-token', - }; + toEncodingData(): Map { + return new Map([ + ['round', this.round], + [ + 'asset-holdings', + typeof this.assetHoldings !== 'undefined' + ? this.assetHoldings.map((v) => v.toEncodingData()) + : undefined, + ], + ['next-token', this.nextToken], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): AccountAssetsInformationResponse { - /* eslint-disable dot-notation */ - if (typeof data['round'] === 'undefined') - throw new Error(`Response is missing required field 'round': ${data}`); + static fromEncodingData(data: unknown): AccountAssetsInformationResponse { + if (!(data instanceof Map)) { + throw new Error( + `Invalid decoded AccountAssetsInformationResponse: ${data}` + ); + } return new AccountAssetsInformationResponse({ - round: data['round'], + round: data.get('round'), assetHoldings: - typeof data['asset-holdings'] !== 'undefined' - ? data['asset-holdings'].map( - AccountAssetHolding.from_obj_for_encoding - ) + typeof data.get('asset-holdings') !== 'undefined' + ? data + .get('asset-holdings') + .map((v: unknown) => AccountAssetHolding.fromEncodingData(v)) : undefined, - nextToken: data['next-token'], + nextToken: data.get('next-token'), }); - /* eslint-enable dot-notation */ } } @@ -724,7 +1032,48 @@ export class AccountAssetsInformationResponse extends BaseModel { * AccountParticipation describes the parameters used by this account in consensus * protocol. */ -export class AccountParticipation extends BaseModel { +export class AccountParticipation implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'selection-participation-key', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'vote-first-valid', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'vote-key-dilution', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'vote-last-valid', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'vote-participation-key', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'state-proof-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (sel) Selection public key (if any) currently registered for this round. */ @@ -733,17 +1082,17 @@ export class AccountParticipation extends BaseModel { /** * (voteFst) First round for which this participation is valid. */ - public voteFirstValid: number | bigint; + public voteFirstValid: bigint; /** * (voteKD) Number of subkeys in each batch of participation keys. */ - public voteKeyDilution: number | bigint; + public voteKeyDilution: bigint; /** * (voteLst) Last round for which this participation is valid. */ - public voteLastValid: number | bigint; + public voteLastValid: bigint; /** * (vote) root participation public key (if any) currently registered for this @@ -781,74 +1130,75 @@ export class AccountParticipation extends BaseModel { voteParticipationKey: string | Uint8Array; stateProofKey?: string | Uint8Array; }) { - super(); this.selectionParticipationKey = typeof selectionParticipationKey === 'string' - ? new Uint8Array(Buffer.from(selectionParticipationKey, 'base64')) + ? base64ToBytes(selectionParticipationKey) : selectionParticipationKey; - this.voteFirstValid = voteFirstValid; - this.voteKeyDilution = voteKeyDilution; - this.voteLastValid = voteLastValid; + this.voteFirstValid = ensureBigInt(voteFirstValid); + this.voteKeyDilution = ensureBigInt(voteKeyDilution); + this.voteLastValid = ensureBigInt(voteLastValid); this.voteParticipationKey = typeof voteParticipationKey === 'string' - ? new Uint8Array(Buffer.from(voteParticipationKey, 'base64')) + ? base64ToBytes(voteParticipationKey) : voteParticipationKey; this.stateProofKey = typeof stateProofKey === 'string' - ? new Uint8Array(Buffer.from(stateProofKey, 'base64')) + ? base64ToBytes(stateProofKey) : 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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountParticipation.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['selection-participation-key', this.selectionParticipationKey], + ['vote-first-valid', this.voteFirstValid], + ['vote-key-dilution', this.voteKeyDilution], + ['vote-last-valid', this.voteLastValid], + ['vote-participation-key', this.voteParticipationKey], + ['state-proof-key', this.stateProofKey], + ]); + } + + static fromEncodingData(data: unknown): AccountParticipation { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountParticipation: ${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'], + selectionParticipationKey: data.get('selection-participation-key'), + voteFirstValid: data.get('vote-first-valid'), + voteKeyDilution: data.get('vote-key-dilution'), + voteLastValid: data.get('vote-last-valid'), + voteParticipationKey: data.get('vote-participation-key'), + stateProofKey: data.get('state-proof-key'), }); - /* eslint-enable dot-notation */ } } /** * Application state delta. */ -export class AccountStateDelta extends BaseModel { +export class AccountStateDelta implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'delta', + valueSchema: new ArraySchema(EvalDeltaKeyValue.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public address: string; /** @@ -868,30 +1218,32 @@ export class AccountStateDelta extends BaseModel { address: string; delta: EvalDeltaKeyValue[]; }) { - super(); this.address = address; this.delta = delta; + } - this.attribute_map = { - address: 'address', - delta: 'delta', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountStateDelta.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['address', this.address], + ['delta', this.delta.map((v) => v.toEncodingData())], + ]); + } + + static fromEncodingData(data: unknown): AccountStateDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountStateDelta: ${data}`); + } return new AccountStateDelta({ - address: data['address'], - delta: data['delta'].map(EvalDeltaKeyValue.from_obj_for_encoding), + address: data.get('address'), + delta: (data.get('delta') ?? []).map((v: unknown) => + EvalDeltaKeyValue.fromEncodingData(v) + ), }); - /* eslint-enable dot-notation */ } } @@ -899,11 +1251,33 @@ export class AccountStateDelta extends BaseModel { * The logged messages from an app call along with the app ID and outer transaction * ID. Logs appear in the same order that they were emitted. */ -export class AppCallLogs extends BaseModel { +export class AppCallLogs implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'application-index', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'logs', + valueSchema: new ArraySchema(new ByteArraySchema()), + omitEmpty: true, + }, + { key: 'txId', valueSchema: new StringSchema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * The application from which the logs were generated */ - public applicationIndex: number | bigint; + public applicationIndex: number; /** * An array of logs @@ -930,48 +1304,61 @@ export class AppCallLogs extends BaseModel { logs: Uint8Array[]; txid: string; }) { - super(); - this.applicationIndex = applicationIndex; + this.applicationIndex = ensureSafeInteger(applicationIndex); this.logs = logs; this.txid = txid; + } - this.attribute_map = { - applicationIndex: 'application-index', - logs: 'logs', - txid: 'txId', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AppCallLogs.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): AppCallLogs { - /* eslint-disable dot-notation */ - if (typeof data['application-index'] === 'undefined') - throw new Error( - `Response is missing required field 'application-index': ${data}` - ); - 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}`); + toEncodingData(): Map { + return new Map([ + ['application-index', this.applicationIndex], + ['logs', this.logs], + ['txId', this.txid], + ]); + } + + static fromEncodingData(data: unknown): AppCallLogs { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AppCallLogs: ${data}`); + } return new AppCallLogs({ - applicationIndex: data['application-index'], - logs: data['logs'], - txid: data['txId'], + applicationIndex: data.get('application-index'), + logs: data.get('logs'), + txid: data.get('txId'), }); - /* eslint-enable dot-notation */ } } /** * Application index and its parameters */ -export class Application extends BaseModel { +export class Application implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'params', + valueSchema: ApplicationParams.encodingSchema, + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (appidx) application index. */ - public id: number | bigint; + public id: bigint; /** * (appparams) application parameters. @@ -990,28 +1377,32 @@ export class Application extends BaseModel { id: number | bigint; params: ApplicationParams; }) { - super(); - this.id = id; + this.id = ensureBigInt(id); this.params = params; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Application.encodingSchema; + } - this.attribute_map = { - id: 'id', - params: 'params', - }; + toEncodingData(): Map { + return new Map([ + ['id', this.id], + ['params', this.params.toEncodingData()], + ]); } - // 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}`); + static fromEncodingData(data: unknown): Application { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Application: ${data}`); + } return new Application({ - id: data['id'], - params: ApplicationParams.from_obj_for_encoding(data['params']), + id: data.get('id'), + params: ApplicationParams.fromEncodingData( + data.get('params') ?? new Map() + ), }); - /* eslint-enable dot-notation */ } } @@ -1019,11 +1410,40 @@ export class Application extends BaseModel { * An application's initial global/local/box states that were accessed during * simulation. */ -export class ApplicationInitialStates extends BaseModel { +export class ApplicationInitialStates implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'app-boxes', + valueSchema: new OptionalSchema(ApplicationKVStorage.encodingSchema), + omitEmpty: true, + }, + { + key: 'app-globals', + valueSchema: new OptionalSchema(ApplicationKVStorage.encodingSchema), + omitEmpty: true, + }, + { + key: 'app-locals', + valueSchema: new OptionalSchema( + new ArraySchema(ApplicationKVStorage.encodingSchema) + ), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Application index. */ - public id: number | bigint; + public id: bigint; /** * An application's global/local/box state. @@ -1058,50 +1478,90 @@ export class ApplicationInitialStates extends BaseModel { appGlobals?: ApplicationKVStorage; appLocals?: ApplicationKVStorage[]; }) { - super(); - this.id = id; + this.id = ensureBigInt(id); this.appBoxes = appBoxes; this.appGlobals = appGlobals; this.appLocals = appLocals; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationInitialStates.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['id', this.id], + [ + 'app-boxes', + typeof this.appBoxes !== 'undefined' + ? this.appBoxes.toEncodingData() + : undefined, + ], + [ + 'app-globals', + typeof this.appGlobals !== 'undefined' + ? this.appGlobals.toEncodingData() + : undefined, + ], + [ + 'app-locals', + typeof this.appLocals !== 'undefined' + ? this.appLocals.map((v) => v.toEncodingData()) + : undefined, + ], + ]); + } - this.attribute_map = { - id: 'id', - appBoxes: 'app-boxes', - appGlobals: 'app-globals', - appLocals: 'app-locals', - }; - } - - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): ApplicationInitialStates { - /* eslint-disable dot-notation */ - if (typeof data['id'] === 'undefined') - throw new Error(`Response is missing required field 'id': ${data}`); + static fromEncodingData(data: unknown): ApplicationInitialStates { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationInitialStates: ${data}`); + } return new ApplicationInitialStates({ - id: data['id'], + id: data.get('id'), appBoxes: - typeof data['app-boxes'] !== 'undefined' - ? ApplicationKVStorage.from_obj_for_encoding(data['app-boxes']) + typeof data.get('app-boxes') !== 'undefined' + ? ApplicationKVStorage.fromEncodingData(data.get('app-boxes')) : undefined, appGlobals: - typeof data['app-globals'] !== 'undefined' - ? ApplicationKVStorage.from_obj_for_encoding(data['app-globals']) + typeof data.get('app-globals') !== 'undefined' + ? ApplicationKVStorage.fromEncodingData(data.get('app-globals')) : undefined, appLocals: - typeof data['app-locals'] !== 'undefined' - ? data['app-locals'].map(ApplicationKVStorage.from_obj_for_encoding) + typeof data.get('app-locals') !== 'undefined' + ? data + .get('app-locals') + .map((v: unknown) => ApplicationKVStorage.fromEncodingData(v)) : undefined, }); - /* eslint-enable dot-notation */ } } /** * An application's global/local/box state. */ -export class ApplicationKVStorage extends BaseModel { +export class ApplicationKVStorage implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'kvs', + valueSchema: new ArraySchema(AvmKeyValue.encodingSchema), + omitEmpty: true, + }, + { + key: 'account', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Key-Value pairs representing application states. */ @@ -1110,96 +1570,154 @@ export class ApplicationKVStorage extends BaseModel { /** * The address of the account associated with the local state. */ - public account?: string; + public account?: Address; /** * Creates a new `ApplicationKVStorage` object. * @param kvs - Key-Value pairs representing application states. * @param account - The address of the account associated with the local state. */ - constructor({ kvs, account }: { kvs: AvmKeyValue[]; account?: string }) { - super(); + constructor({ + kvs, + account, + }: { + kvs: AvmKeyValue[]; + account?: Address | string; + }) { this.kvs = kvs; - this.account = account; + this.account = + typeof account === 'string' ? Address.fromString(account) : account; + } - this.attribute_map = { - kvs: 'kvs', - account: 'account', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationKVStorage.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): ApplicationKVStorage { - /* eslint-disable dot-notation */ - if (!Array.isArray(data['kvs'])) - throw new Error( - `Response is missing required array field 'kvs': ${data}` - ); + toEncodingData(): Map { + return new Map([ + ['kvs', this.kvs.map((v) => v.toEncodingData())], + [ + 'account', + typeof this.account !== 'undefined' + ? this.account.toString() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): ApplicationKVStorage { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationKVStorage: ${data}`); + } return new ApplicationKVStorage({ - kvs: data['kvs'].map(AvmKeyValue.from_obj_for_encoding), - account: data['account'], + kvs: (data.get('kvs') ?? []).map((v: unknown) => + AvmKeyValue.fromEncodingData(v) + ), + account: data.get('account'), }); - /* eslint-enable dot-notation */ } } /** * References an account's local state for an application. */ -export class ApplicationLocalReference extends BaseModel { +export class ApplicationLocalReference implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'account', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'app', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * Address of the account with the local state. */ - public account: string; + public account: Address; /** * Application ID of the local state application. */ - public app: number | bigint; + public app: bigint; /** * Creates a new `ApplicationLocalReference` object. * @param account - Address of the account with the local state. * @param app - Application ID of the local state application. */ - constructor({ account, app }: { account: string; app: number | bigint }) { - super(); - this.account = account; - this.app = app; - - this.attribute_map = { - account: 'account', - app: 'app', - }; - } - - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): ApplicationLocalReference { - /* eslint-disable dot-notation */ - if (typeof data['account'] === 'undefined') - throw new Error(`Response is missing required field 'account': ${data}`); - if (typeof data['app'] === 'undefined') - throw new Error(`Response is missing required field 'app': ${data}`); + constructor({ + account, + app, + }: { + account: Address | string; + app: number | bigint; + }) { + this.account = + typeof account === 'string' ? Address.fromString(account) : account; + this.app = ensureBigInt(app); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationLocalReference.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['account', this.account.toString()], + ['app', this.app], + ]); + } + + static fromEncodingData(data: unknown): ApplicationLocalReference { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationLocalReference: ${data}`); + } return new ApplicationLocalReference({ - account: data['account'], - app: data['app'], + account: data.get('account'), + app: data.get('app'), }); - /* eslint-enable dot-notation */ } } /** * Stores local state associated with an application. */ -export class ApplicationLocalState extends BaseModel { +export class ApplicationLocalState implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'schema', + valueSchema: ApplicationStateSchema.encodingSchema, + omitEmpty: true, + }, + { + key: 'key-value', + valueSchema: new OptionalSchema( + new ArraySchema(TealKeyValue.encodingSchema) + ), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * The application which this local state is for. */ - public id: number | bigint; + public id: bigint; /** * (hsch) schema. @@ -1226,43 +1744,100 @@ export class ApplicationLocalState extends BaseModel { schema: ApplicationStateSchema; keyValue?: TealKeyValue[]; }) { - super(); - this.id = id; + this.id = ensureBigInt(id); this.schema = schema; this.keyValue = keyValue; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationLocalState.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['id', this.id], + ['schema', this.schema.toEncodingData()], + [ + 'key-value', + typeof this.keyValue !== 'undefined' + ? this.keyValue.map((v) => v.toEncodingData()) + : undefined, + ], + ]); + } - 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}`); + static fromEncodingData(data: unknown): ApplicationLocalState { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationLocalState: ${data}`); + } return new ApplicationLocalState({ - id: data['id'], - schema: ApplicationStateSchema.from_obj_for_encoding(data['schema']), + id: data.get('id'), + schema: ApplicationStateSchema.fromEncodingData( + data.get('schema') ?? new Map() + ), keyValue: - typeof data['key-value'] !== 'undefined' - ? data['key-value'].map(TealKeyValue.from_obj_for_encoding) + typeof data.get('key-value') !== 'undefined' + ? data + .get('key-value') + .map((v: unknown) => TealKeyValue.fromEncodingData(v)) : undefined, }); - /* eslint-enable dot-notation */ } } /** * Stores the global information associated with an application. */ -export class ApplicationParams extends BaseModel { +export class ApplicationParams implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'approval-program', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'clear-state-program', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { key: 'creator', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'extra-program-pages', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'global-state', + valueSchema: new OptionalSchema( + new ArraySchema(TealKeyValue.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'global-state-schema', + valueSchema: new OptionalSchema( + ApplicationStateSchema.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'local-state-schema', + valueSchema: new OptionalSchema( + ApplicationStateSchema.encodingSchema + ), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (approv) approval program. */ @@ -1277,12 +1852,12 @@ export class ApplicationParams extends BaseModel { * 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; + public creator: Address; /** * (epp) the amount of extra program pages available to this app. */ - public extraProgramPages?: number | bigint; + public extraProgramPages?: number; /** * (gs) global state @@ -1321,81 +1896,126 @@ export class ApplicationParams extends BaseModel { }: { approvalProgram: string | Uint8Array; clearStateProgram: string | Uint8Array; - creator: string; + creator: Address | string; extraProgramPages?: number | bigint; globalState?: TealKeyValue[]; globalStateSchema?: ApplicationStateSchema; localStateSchema?: ApplicationStateSchema; }) { - super(); this.approvalProgram = typeof approvalProgram === 'string' - ? new Uint8Array(Buffer.from(approvalProgram, 'base64')) + ? base64ToBytes(approvalProgram) : approvalProgram; this.clearStateProgram = typeof clearStateProgram === 'string' - ? new Uint8Array(Buffer.from(clearStateProgram, 'base64')) + ? base64ToBytes(clearStateProgram) : clearStateProgram; - this.creator = creator; - this.extraProgramPages = extraProgramPages; + this.creator = + typeof creator === 'string' ? Address.fromString(creator) : creator; + this.extraProgramPages = + typeof extraProgramPages === 'undefined' + ? undefined + : ensureSafeInteger(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}`); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationParams.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['approval-program', this.approvalProgram], + ['clear-state-program', this.clearStateProgram], + ['creator', this.creator.toString()], + ['extra-program-pages', this.extraProgramPages], + [ + 'global-state', + typeof this.globalState !== 'undefined' + ? this.globalState.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'global-state-schema', + typeof this.globalStateSchema !== 'undefined' + ? this.globalStateSchema.toEncodingData() + : undefined, + ], + [ + 'local-state-schema', + typeof this.localStateSchema !== 'undefined' + ? this.localStateSchema.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): ApplicationParams { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationParams: ${data}`); + } return new ApplicationParams({ - approvalProgram: data['approval-program'], - clearStateProgram: data['clear-state-program'], - creator: data['creator'], - extraProgramPages: data['extra-program-pages'], + approvalProgram: data.get('approval-program'), + clearStateProgram: data.get('clear-state-program'), + creator: data.get('creator'), + extraProgramPages: data.get('extra-program-pages'), globalState: - typeof data['global-state'] !== 'undefined' - ? data['global-state'].map(TealKeyValue.from_obj_for_encoding) + typeof data.get('global-state') !== 'undefined' + ? data + .get('global-state') + .map((v: unknown) => TealKeyValue.fromEncodingData(v)) : undefined, globalStateSchema: - typeof data['global-state-schema'] !== 'undefined' - ? ApplicationStateSchema.from_obj_for_encoding( - data['global-state-schema'] + typeof data.get('global-state-schema') !== 'undefined' + ? ApplicationStateSchema.fromEncodingData( + data.get('global-state-schema') ) : undefined, localStateSchema: - typeof data['local-state-schema'] !== 'undefined' - ? ApplicationStateSchema.from_obj_for_encoding( - data['local-state-schema'] + typeof data.get('local-state-schema') !== 'undefined' + ? ApplicationStateSchema.fromEncodingData( + data.get('local-state-schema') ) : undefined, }); - /* eslint-enable dot-notation */ } } /** * An operation against an application's global/local/box state. */ -export class ApplicationStateOperation extends BaseModel { +export class ApplicationStateOperation implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'app-state-type', + valueSchema: new StringSchema(), + omitEmpty: true, + }, + { key: 'key', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'operation', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'account', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'new-value', + valueSchema: new OptionalSchema(AvmValue.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Type of application state. Value `g` is **global state**, `l` is **local * state**, `b` is **boxes**. @@ -1416,7 +2036,7 @@ export class ApplicationStateOperation extends BaseModel { * For local state changes, the address of the account associated with the local * state. */ - public account?: string; + public account?: Address; /** * Represents an AVM value. @@ -1443,120 +2063,154 @@ export class ApplicationStateOperation extends BaseModel { appStateType: string; key: string | Uint8Array; operation: string; - account?: string; + account?: Address | string; newValue?: AvmValue; }) { - super(); this.appStateType = appStateType; - this.key = - typeof key === 'string' - ? new Uint8Array(Buffer.from(key, 'base64')) - : key; + this.key = typeof key === 'string' ? base64ToBytes(key) : key; this.operation = operation; - this.account = account; + this.account = + typeof account === 'string' ? Address.fromString(account) : account; this.newValue = newValue; + } - this.attribute_map = { - appStateType: 'app-state-type', - key: 'key', - operation: 'operation', - account: 'account', - newValue: 'new-value', - }; - } - - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): ApplicationStateOperation { - /* eslint-disable dot-notation */ - if (typeof data['app-state-type'] === 'undefined') - throw new Error( - `Response is missing required field 'app-state-type': ${data}` - ); - if (typeof data['key'] === 'undefined') - throw new Error(`Response is missing required field 'key': ${data}`); - if (typeof data['operation'] === 'undefined') - throw new Error( - `Response is missing required field 'operation': ${data}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationStateOperation.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['app-state-type', this.appStateType], + ['key', this.key], + ['operation', this.operation], + [ + 'account', + typeof this.account !== 'undefined' + ? this.account.toString() + : undefined, + ], + [ + 'new-value', + typeof this.newValue !== 'undefined' + ? this.newValue.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): ApplicationStateOperation { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationStateOperation: ${data}`); + } return new ApplicationStateOperation({ - appStateType: data['app-state-type'], - key: data['key'], - operation: data['operation'], - account: data['account'], + appStateType: data.get('app-state-type'), + key: data.get('key'), + operation: data.get('operation'), + account: data.get('account'), newValue: - typeof data['new-value'] !== 'undefined' - ? AvmValue.from_obj_for_encoding(data['new-value']) + typeof data.get('new-value') !== 'undefined' + ? AvmValue.fromEncodingData(data.get('new-value')) : undefined, }); - /* eslint-enable dot-notation */ } } /** * Specifies maximums on the number of each type that may be stored. */ -export class ApplicationStateSchema extends BaseModel { +export class ApplicationStateSchema implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'num-byte-slice', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'num-uint', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** - * (nui) num of uints. + * (nbs) num of byte slices. */ - public numUint: number | bigint; + public numByteSlice: number; /** - * (nbs) num of byte slices. + * (nui) num of uints. */ - public numByteSlice: number | bigint; + public numUint: number; /** * Creates a new `ApplicationStateSchema` object. - * @param numUint - (nui) num of uints. * @param numByteSlice - (nbs) num of byte slices. + * @param numUint - (nui) num of uints. */ constructor({ - numUint, numByteSlice, + numUint, }: { - numUint: number | bigint; numByteSlice: number | bigint; + numUint: 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}` - ); + this.numByteSlice = ensureSafeInteger(numByteSlice); + this.numUint = ensureSafeInteger(numUint); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationStateSchema.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['num-byte-slice', this.numByteSlice], + ['num-uint', this.numUint], + ]); + } + + static fromEncodingData(data: unknown): ApplicationStateSchema { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationStateSchema: ${data}`); + } return new ApplicationStateSchema({ - numUint: data['num-uint'], - numByteSlice: data['num-byte-slice'], + numByteSlice: data.get('num-byte-slice'), + numUint: data.get('num-uint'), }); - /* eslint-enable dot-notation */ } } /** * Specifies both the unique identifier and the parameters for an asset */ -export class Asset extends BaseModel { +export class Asset implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'index', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'params', + valueSchema: AssetParams.encodingSchema, + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * unique asset identifier */ - public index: number | bigint; + public index: bigint; /** * AssetParams specifies the parameters for an asset. @@ -1581,28 +2235,30 @@ export class Asset extends BaseModel { index: number | bigint; params: AssetParams; }) { - super(); - this.index = index; + this.index = ensureBigInt(index); this.params = params; + } - this.attribute_map = { - index: 'index', - params: 'params', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Asset.encodingSchema; } - // 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}`); + toEncodingData(): Map { + return new Map([ + ['index', this.index], + ['params', this.params.toEncodingData()], + ]); + } + + static fromEncodingData(data: unknown): Asset { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Asset: ${data}`); + } return new Asset({ - index: data['index'], - params: AssetParams.from_obj_for_encoding(data['params']), + index: data.get('index'), + params: AssetParams.fromEncodingData(data.get('params') ?? new Map()), }); - /* eslint-enable dot-notation */ } } @@ -1611,16 +2267,30 @@ export class Asset extends BaseModel { * Definition: * data/basics/userBalance.go : AssetHolding */ -export class AssetHolding extends BaseModel { +export class AssetHolding implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'asset-id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'is-frozen', valueSchema: new BooleanSchema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * (a) number of units held. */ - public amount: number | bigint; + public amount: bigint; /** * Asset ID of the holding. */ - public assetId: number | bigint; + public assetId: bigint; /** * (f) whether or not the holding is frozen. @@ -1642,82 +2312,100 @@ export class AssetHolding extends BaseModel { assetId: number | bigint; isFrozen: boolean; }) { - super(); - this.amount = amount; - this.assetId = assetId; + this.amount = ensureBigInt(amount); + this.assetId = ensureBigInt(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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetHolding.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['amount', this.amount], + ['asset-id', this.assetId], + ['is-frozen', this.isFrozen], + ]); + } + + static fromEncodingData(data: unknown): AssetHolding { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetHolding: ${data}`); + } return new AssetHolding({ - amount: data['amount'], - assetId: data['asset-id'], - isFrozen: data['is-frozen'], + amount: data.get('amount'), + assetId: data.get('asset-id'), + isFrozen: data.get('is-frozen'), }); - /* eslint-enable dot-notation */ } } /** * References an asset held by an account. */ -export class AssetHoldingReference extends BaseModel { +export class AssetHoldingReference implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'account', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'asset', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * Address of the account holding the asset. */ - public account: string; + public account: Address; /** * Asset ID of the holding. */ - public asset: number | bigint; + public asset: bigint; /** * Creates a new `AssetHoldingReference` object. * @param account - Address of the account holding the asset. * @param asset - Asset ID of the holding. */ - constructor({ account, asset }: { account: string; asset: number | bigint }) { - super(); - this.account = account; - this.asset = asset; - - this.attribute_map = { - account: 'account', - asset: 'asset', - }; - } - - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): AssetHoldingReference { - /* eslint-disable dot-notation */ - if (typeof data['account'] === 'undefined') - throw new Error(`Response is missing required field 'account': ${data}`); - if (typeof data['asset'] === 'undefined') - throw new Error(`Response is missing required field 'asset': ${data}`); + constructor({ + account, + asset, + }: { + account: Address | string; + asset: number | bigint; + }) { + this.account = + typeof account === 'string' ? Address.fromString(account) : account; + this.asset = ensureBigInt(asset); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetHoldingReference.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['account', this.account.toString()], + ['asset', this.asset], + ]); + } + + static fromEncodingData(data: unknown): AssetHoldingReference { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetHoldingReference: ${data}`); + } return new AssetHoldingReference({ - account: data['account'], - asset: data['asset'], + account: data.get('account'), + asset: data.get('asset'), }); - /* eslint-enable dot-notation */ } } @@ -1727,7 +2415,81 @@ export class AssetHoldingReference extends BaseModel { * Definition: * data/transactions/asset.go : AssetParams */ -export class AssetParams extends BaseModel { +export class AssetParams implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'creator', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'decimals', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'total', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'clawback', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'default-frozen', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'freeze', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'manager', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'metadata-hash', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'name', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'name-b64', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'reserve', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'unit-name', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'unit-name-b64', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'url', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'url-b64', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * 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 @@ -1741,12 +2503,12 @@ export class AssetParams extends BaseModel { * 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; + public decimals: number; /** * (t) The total number of units of this asset. */ - public total: number | bigint; + public total: bigint; /** * (c) Address of account used to clawback holdings of this asset. If empty, @@ -1876,88 +2638,96 @@ export class AssetParams extends BaseModel { url?: string; urlB64?: string | Uint8Array; }) { - super(); this.creator = creator; - this.decimals = decimals; - this.total = total; + this.decimals = ensureSafeInteger(decimals); + this.total = ensureBigInt(total); this.clawback = clawback; this.defaultFrozen = defaultFrozen; this.freeze = freeze; this.manager = manager; this.metadataHash = typeof metadataHash === 'string' - ? new Uint8Array(Buffer.from(metadataHash, 'base64')) + ? base64ToBytes(metadataHash) : metadataHash; this.name = name; this.nameB64 = - typeof nameB64 === 'string' - ? new Uint8Array(Buffer.from(nameB64, 'base64')) - : nameB64; + typeof nameB64 === 'string' ? base64ToBytes(nameB64) : nameB64; this.reserve = reserve; this.unitName = unitName; this.unitNameB64 = typeof unitNameB64 === 'string' - ? new Uint8Array(Buffer.from(unitNameB64, 'base64')) + ? base64ToBytes(unitNameB64) : 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}`); + this.urlB64 = typeof urlB64 === 'string' ? base64ToBytes(urlB64) : urlB64; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetParams.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['creator', this.creator], + ['decimals', this.decimals], + ['total', this.total], + ['clawback', this.clawback], + ['default-frozen', this.defaultFrozen], + ['freeze', this.freeze], + ['manager', this.manager], + ['metadata-hash', this.metadataHash], + ['name', this.name], + ['name-b64', this.nameB64], + ['reserve', this.reserve], + ['unit-name', this.unitName], + ['unit-name-b64', this.unitNameB64], + ['url', this.url], + ['url-b64', this.urlB64], + ]); + } + + static fromEncodingData(data: unknown): AssetParams { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetParams: ${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'], + creator: data.get('creator'), + decimals: data.get('decimals'), + total: data.get('total'), + clawback: data.get('clawback'), + defaultFrozen: data.get('default-frozen'), + freeze: data.get('freeze'), + manager: data.get('manager'), + metadataHash: data.get('metadata-hash'), + name: data.get('name'), + nameB64: data.get('name-b64'), + reserve: data.get('reserve'), + unitName: data.get('unit-name'), + unitNameB64: data.get('unit-name-b64'), + url: data.get('url'), + urlB64: data.get('url-b64'), }); - /* eslint-enable dot-notation */ } } /** * Represents an AVM key-value pair in an application store. */ -export class AvmKeyValue extends BaseModel { +export class AvmKeyValue implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'key', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'value', valueSchema: AvmValue.encodingSchema, omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + public key: Uint8Array; /** @@ -1971,42 +2741,63 @@ export class AvmKeyValue extends BaseModel { * @param value - Represents an AVM value. */ constructor({ key, value }: { key: string | Uint8Array; value: AvmValue }) { - super(); - this.key = - typeof key === 'string' - ? new Uint8Array(Buffer.from(key, 'base64')) - : key; + this.key = typeof key === 'string' ? base64ToBytes(key) : key; this.value = value; + } - this.attribute_map = { - key: 'key', - value: 'value', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AvmKeyValue.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): AvmKeyValue { - /* 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}`); + toEncodingData(): Map { + return new Map([ + ['key', this.key], + ['value', this.value.toEncodingData()], + ]); + } + + static fromEncodingData(data: unknown): AvmKeyValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AvmKeyValue: ${data}`); + } return new AvmKeyValue({ - key: data['key'], - value: AvmValue.from_obj_for_encoding(data['value']), + key: data.get('key'), + value: AvmValue.fromEncodingData(data.get('value') ?? new Map()), }); - /* eslint-enable dot-notation */ } } /** * Represents an AVM value. */ -export class AvmValue extends BaseModel { +export class AvmValue implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'type', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'bytes', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'uint', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * value type. Value `1` refers to **bytes**, value `2` refers to **uint64** */ - public type: number | bigint; + public type: number; /** * bytes value. @@ -2016,7 +2807,7 @@ export class AvmValue extends BaseModel { /** * uint value. */ - public uint?: number | bigint; + public uint?: bigint; /** * Creates a new `AvmValue` object. @@ -2033,40 +2824,55 @@ export class AvmValue extends BaseModel { bytes?: string | Uint8Array; uint?: number | bigint; }) { - super(); - this.type = type; - this.bytes = - typeof bytes === 'string' - ? new Uint8Array(Buffer.from(bytes, 'base64')) - : 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): AvmValue { - /* eslint-disable dot-notation */ - if (typeof data['type'] === 'undefined') - throw new Error(`Response is missing required field 'type': ${data}`); + this.type = ensureSafeInteger(type); + this.bytes = typeof bytes === 'string' ? base64ToBytes(bytes) : bytes; + this.uint = typeof uint === 'undefined' ? undefined : ensureBigInt(uint); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AvmValue.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['type', this.type], + ['bytes', this.bytes], + ['uint', this.uint], + ]); + } + + static fromEncodingData(data: unknown): AvmValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AvmValue: ${data}`); + } return new AvmValue({ - type: data['type'], - bytes: data['bytes'], - uint: data['uint'], + type: data.get('type'), + bytes: data.get('bytes'), + uint: data.get('uint'), }); - /* eslint-enable dot-notation */ } } /** * Hash of a block header. */ -export class BlockHashResponse extends BaseModel { - /** +export class BlockHashResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'blockHash', + valueSchema: new StringSchema(), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + + /** * Block header hash. */ public blockhash: string; @@ -2076,25 +2882,25 @@ export class BlockHashResponse extends BaseModel { * @param blockhash - Block header hash. */ constructor({ blockhash }: { blockhash: string }) { - super(); this.blockhash = blockhash; + } - this.attribute_map = { - blockhash: 'blockHash', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BlockHashResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([['blockHash', this.blockhash]]); + } + + static fromEncodingData(data: unknown): BlockHashResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockHashResponse: ${data}`); + } return new BlockHashResponse({ - blockhash: data['blockHash'], + blockhash: data.get('blockHash'), }); - /* eslint-enable dot-notation */ } } @@ -2107,7 +2913,21 @@ export class BlockHashResponse extends BaseModel { * that their corresponding app call appeared in the block (pre-order traversal of * inner app calls) */ -export class BlockLogsResponse extends BaseModel { +export class BlockLogsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'logs', + valueSchema: new ArraySchema(AppCallLogs.encodingSchema), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + public logs: AppCallLogs[]; /** @@ -2115,42 +2935,63 @@ export class BlockLogsResponse extends BaseModel { * @param logs - */ constructor({ logs }: { logs: AppCallLogs[] }) { - super(); this.logs = logs; + } - this.attribute_map = { - logs: 'logs', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BlockLogsResponse.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): BlockLogsResponse { - /* eslint-disable dot-notation */ - if (!Array.isArray(data['logs'])) - throw new Error( - `Response is missing required array field 'logs': ${data}` - ); + toEncodingData(): Map { + return new Map([ + ['logs', this.logs.map((v) => v.toEncodingData())], + ]); + } + + static fromEncodingData(data: unknown): BlockLogsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockLogsResponse: ${data}`); + } return new BlockLogsResponse({ - logs: data['logs'].map(AppCallLogs.from_obj_for_encoding), + logs: (data.get('logs') ?? []).map((v: unknown) => + AppCallLogs.fromEncodingData(v) + ), }); - /* eslint-enable dot-notation */ } } /** * Encoded block object. */ -export class BlockResponse extends BaseModel { +export class BlockResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'block', valueSchema: Block.encodingSchema, omitEmpty: true }, + { + key: 'cert', + valueSchema: new OptionalSchema(UntypedValue.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Block header data. */ - public block: BlockHeader; + public block: Block; /** * Optional certificate object. This is only included when the format is set to * message pack. */ - public cert?: Record; + public cert?: UntypedValue; /** * Creates a new `BlockResponse` object. @@ -2158,40 +2999,60 @@ export class BlockResponse extends BaseModel { * @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(); + constructor({ block, cert }: { block: Block; cert?: UntypedValue }) { this.block = block; this.cert = cert; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BlockResponse.encodingSchema; + } - this.attribute_map = { - block: 'block', - cert: 'cert', - }; + toEncodingData(): Map { + return new Map([ + ['block', this.block.toEncodingData()], + [ + 'cert', + typeof this.cert !== 'undefined' + ? this.cert.toEncodingData() + : undefined, + ], + ]); } - // 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}`); + static fromEncodingData(data: unknown): BlockResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockResponse: ${data}`); + } return new BlockResponse({ - block: data['block'], - cert: data['cert'], + block: Block.fromEncodingData(data.get('block') ?? new Map()), + cert: + typeof data.get('cert') !== 'undefined' + ? UntypedValue.fromEncodingData(data.get('cert')) + : undefined, }); - /* eslint-enable dot-notation */ } } /** * Top level transaction IDs in a block. */ -export class BlockTxidsResponse extends BaseModel { +export class BlockTxidsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'blockTxids', + valueSchema: new ArraySchema(new StringSchema()), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + /** * Block transaction IDs. */ @@ -2202,32 +3063,46 @@ export class BlockTxidsResponse extends BaseModel { * @param blocktxids - Block transaction IDs. */ constructor({ blocktxids }: { blocktxids: string[] }) { - super(); this.blocktxids = blocktxids; + } - this.attribute_map = { - blocktxids: 'blockTxids', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BlockTxidsResponse.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): BlockTxidsResponse { - /* eslint-disable dot-notation */ - if (!Array.isArray(data['blockTxids'])) - throw new Error( - `Response is missing required array field 'blockTxids': ${data}` - ); + toEncodingData(): Map { + return new Map([['blockTxids', this.blocktxids]]); + } + + static fromEncodingData(data: unknown): BlockTxidsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockTxidsResponse: ${data}`); + } return new BlockTxidsResponse({ - blocktxids: data['blockTxids'], + blocktxids: data.get('blockTxids'), }); - /* eslint-enable dot-notation */ } } /** * Box name and its content. */ -export class Box extends BaseModel { +export class Box implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'name', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'value', valueSchema: new ByteArraySchema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * (name) box name, base64 encoded */ @@ -2236,7 +3111,7 @@ export class Box extends BaseModel { /** * The round for which this information is relevant */ - public round: number | bigint; + public round: bigint; /** * (value) box value, base64 encoded. @@ -2258,46 +3133,54 @@ export class Box extends BaseModel { round: number | bigint; value: string | Uint8Array; }) { - super(); - this.name = - typeof name === 'string' - ? new Uint8Array(Buffer.from(name, 'base64')) - : name; - this.round = round; - this.value = - typeof value === 'string' - ? new Uint8Array(Buffer.from(value, 'base64')) - : value; - - this.attribute_map = { - name: 'name', - round: 'round', - 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['round'] === 'undefined') - throw new Error(`Response is missing required field 'round': ${data}`); - if (typeof data['value'] === 'undefined') - throw new Error(`Response is missing required field 'value': ${data}`); + this.name = typeof name === 'string' ? base64ToBytes(name) : name; + this.round = ensureBigInt(round); + this.value = typeof value === 'string' ? base64ToBytes(value) : value; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Box.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['name', this.name], + ['round', this.round], + ['value', this.value], + ]); + } + + static fromEncodingData(data: unknown): Box { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Box: ${data}`); + } return new Box({ - name: data['name'], - round: data['round'], - value: data['value'], + name: data.get('name'), + round: data.get('round'), + value: data.get('value'), }); - /* eslint-enable dot-notation */ } } /** * Box descriptor describes a Box. */ -export class BoxDescriptor extends BaseModel { +export class BoxDescriptor implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'name', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + /** * Base64 encoded box name */ @@ -2308,37 +3191,49 @@ export class BoxDescriptor extends BaseModel { * @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}`); + this.name = typeof name === 'string' ? base64ToBytes(name) : name; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BoxDescriptor.encodingSchema; + } + + toEncodingData(): Map { + return new Map([['name', this.name]]); + } + + static fromEncodingData(data: unknown): BoxDescriptor { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BoxDescriptor: ${data}`); + } return new BoxDescriptor({ - name: data['name'], + name: data.get('name'), }); - /* eslint-enable dot-notation */ } } /** * References a box of an application. */ -export class BoxReference extends BaseModel { +export class BoxReference implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'app', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'name', valueSchema: new ByteArraySchema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * Application ID which this box belongs to */ - public app: number | bigint; + public app: bigint; /** * Base64 encoded box name @@ -2357,38 +3252,51 @@ export class BoxReference extends BaseModel { app: number | bigint; name: string | Uint8Array; }) { - super(); - this.app = app; - this.name = - typeof name === 'string' - ? new Uint8Array(Buffer.from(name, 'base64')) - : name; - - this.attribute_map = { - app: 'app', - name: 'name', - }; - } - - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): BoxReference { - /* eslint-disable dot-notation */ - if (typeof data['app'] === 'undefined') - throw new Error(`Response is missing required field 'app': ${data}`); - if (typeof data['name'] === 'undefined') - throw new Error(`Response is missing required field 'name': ${data}`); + this.app = ensureBigInt(app); + this.name = typeof name === 'string' ? base64ToBytes(name) : name; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BoxReference.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['app', this.app], + ['name', this.name], + ]); + } + + static fromEncodingData(data: unknown): BoxReference { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BoxReference: ${data}`); + } return new BoxReference({ - app: data['app'], - name: data['name'], + app: data.get('app'), + name: data.get('name'), }); - /* eslint-enable dot-notation */ } } /** * Box names of an application */ -export class BoxesResponse extends BaseModel { +export class BoxesResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'boxes', + valueSchema: new ArraySchema(BoxDescriptor.encodingSchema), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + public boxes: BoxDescriptor[]; /** @@ -2396,40 +3304,69 @@ export class BoxesResponse extends BaseModel { * @param boxes - */ constructor({ boxes }: { boxes: BoxDescriptor[] }) { - super(); this.boxes = boxes; + } - this.attribute_map = { - boxes: 'boxes', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BoxesResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['boxes', this.boxes.map((v) => v.toEncodingData())], + ]); + } + + static fromEncodingData(data: unknown): BoxesResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BoxesResponse: ${data}`); + } return new BoxesResponse({ - boxes: data['boxes'].map(BoxDescriptor.from_obj_for_encoding), + boxes: (data.get('boxes') ?? []).map((v: unknown) => + BoxDescriptor.fromEncodingData(v) + ), }); - /* eslint-enable dot-notation */ } } -export class BuildVersion extends BaseModel { +export class BuildVersion implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'branch', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'build_number', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'channel', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'commit_hash', + valueSchema: new StringSchema(), + omitEmpty: true, + }, + { key: 'major', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'minor', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + public branch: string; - public buildNumber: number | bigint; + public buildNumber: number; public channel: string; public commitHash: string; - public major: number | bigint; + public major: number; - public minor: number | bigint; + public minor: number; /** * Creates a new `BuildVersion` object. @@ -2455,59 +3392,67 @@ export class BuildVersion extends BaseModel { major: number | bigint; minor: number | bigint; }) { - super(); this.branch = branch; - this.buildNumber = buildNumber; + this.buildNumber = ensureSafeInteger(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}`); + this.major = ensureSafeInteger(major); + this.minor = ensureSafeInteger(minor); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BuildVersion.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['branch', this.branch], + ['build_number', this.buildNumber], + ['channel', this.channel], + ['commit_hash', this.commitHash], + ['major', this.major], + ['minor', this.minor], + ]); + } + + static fromEncodingData(data: unknown): BuildVersion { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BuildVersion: ${data}`); + } return new BuildVersion({ - branch: data['branch'], - buildNumber: data['build_number'], - channel: data['channel'], - commitHash: data['commit_hash'], - major: data['major'], - minor: data['minor'], + branch: data.get('branch'), + buildNumber: data.get('build_number'), + channel: data.get('channel'), + commitHash: data.get('commit_hash'), + major: data.get('major'), + minor: data.get('minor'), }); - /* eslint-enable dot-notation */ } } /** * Teal compile Result */ -export class CompileResponse extends BaseModel { +export class CompileResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'hash', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'result', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'sourcemap', + valueSchema: new OptionalSchema(UntypedValue.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * base32 SHA512_256 of program bytes (Address style) */ @@ -2521,7 +3466,7 @@ export class CompileResponse extends BaseModel { /** * JSON of the source map */ - public sourcemap?: Record; + public sourcemap?: UntypedValue; /** * Creates a new `CompileResponse` object. @@ -2536,40 +3481,64 @@ export class CompileResponse extends BaseModel { }: { hash: string; result: string; - sourcemap?: Record; + sourcemap?: UntypedValue; }) { - super(); this.hash = hash; this.result = result; this.sourcemap = sourcemap; + } - this.attribute_map = { - hash: 'hash', - result: 'result', - sourcemap: 'sourcemap', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return CompileResponse.encodingSchema; } - // 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}`); + toEncodingData(): Map { + return new Map([ + ['hash', this.hash], + ['result', this.result], + [ + 'sourcemap', + typeof this.sourcemap !== 'undefined' + ? this.sourcemap.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): CompileResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded CompileResponse: ${data}`); + } return new CompileResponse({ - hash: data['hash'], - result: data['result'], - sourcemap: data['sourcemap'], + hash: data.get('hash'), + result: data.get('result'), + sourcemap: + typeof data.get('sourcemap') !== 'undefined' + ? UntypedValue.fromEncodingData(data.get('sourcemap')) + : undefined, }); - /* eslint-enable dot-notation */ } } /** * Teal disassembly Result */ -export class DisassembleResponse extends BaseModel { +export class DisassembleResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'result', + valueSchema: new StringSchema(), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + /** * disassembled Teal code */ @@ -2580,23 +3549,25 @@ export class DisassembleResponse extends BaseModel { * @param result - disassembled Teal code */ constructor({ result }: { result: string }) { - super(); this.result = result; + } - this.attribute_map = { - result: 'result', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return DisassembleResponse.encodingSchema; } - // 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}`); + toEncodingData(): Map { + return new Map([['result', this.result]]); + } + + static fromEncodingData(data: unknown): DisassembleResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded DisassembleResponse: ${data}`); + } return new DisassembleResponse({ - result: data['result'], + result: data.get('result'), }); - /* eslint-enable dot-notation */ } } @@ -2604,7 +3575,49 @@ export class DisassembleResponse extends BaseModel { * 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 { +export class DryrunRequest implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'accounts', + valueSchema: new ArraySchema(Account.encodingSchema), + omitEmpty: true, + }, + { + key: 'apps', + valueSchema: new ArraySchema(Application.encodingSchema), + omitEmpty: true, + }, + { + key: 'latest-timestamp', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'protocol-version', + valueSchema: new StringSchema(), + omitEmpty: true, + }, + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'sources', + valueSchema: new ArraySchema(DryrunSource.encodingSchema), + omitEmpty: true, + }, + { + key: 'txns', + valueSchema: new ArraySchema(SignedTransaction.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public accounts: Account[]; public apps: Application[]; @@ -2613,7 +3626,7 @@ export class DryrunRequest extends BaseModel { * LatestTimestamp is available to some TEAL scripts. Defaults to the latest * confirmed timestamp this algod is attached to. */ - public latestTimestamp: number | bigint; + public latestTimestamp: number; /** * ProtocolVersion specifies a specific version string to operate under, otherwise @@ -2625,11 +3638,11 @@ export class DryrunRequest extends BaseModel { * 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 round: bigint; public sources: DryrunSource[]; - public txns: EncodedSignedTransaction[]; + public txns: SignedTransaction[]; /** * Creates a new `DryrunRequest` object. @@ -2659,74 +3672,84 @@ export class DryrunRequest extends BaseModel { protocolVersion: string; round: number | bigint; sources: DryrunSource[]; - txns: EncodedSignedTransaction[]; + txns: SignedTransaction[]; }) { - super(); this.accounts = accounts; this.apps = apps; - this.latestTimestamp = latestTimestamp; + this.latestTimestamp = ensureSafeInteger(latestTimestamp); this.protocolVersion = protocolVersion; - this.round = round; + this.round = ensureBigInt(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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return DryrunRequest.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['accounts', this.accounts.map((v) => v.toEncodingData())], + ['apps', this.apps.map((v) => v.toEncodingData())], + ['latest-timestamp', this.latestTimestamp], + ['protocol-version', this.protocolVersion], + ['round', this.round], + ['sources', this.sources.map((v) => v.toEncodingData())], + ['txns', this.txns.map((v) => v.toEncodingData())], + ]); + } + + static fromEncodingData(data: unknown): DryrunRequest { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded DryrunRequest: ${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'], + accounts: (data.get('accounts') ?? []).map((v: unknown) => + Account.fromEncodingData(v) + ), + apps: (data.get('apps') ?? []).map((v: unknown) => + Application.fromEncodingData(v) + ), + latestTimestamp: data.get('latest-timestamp'), + protocolVersion: data.get('protocol-version'), + round: data.get('round'), + sources: (data.get('sources') ?? []).map((v: unknown) => + DryrunSource.fromEncodingData(v) + ), + txns: (data.get('txns') ?? []).map((v: unknown) => + SignedTransaction.fromEncodingData(v) + ), }); - /* eslint-enable dot-notation */ } } /** * DryrunResponse contains per-txn debug information from a dryrun. */ -export class DryrunResponse extends BaseModel { +export class DryrunResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'error', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'protocol-version', + valueSchema: new StringSchema(), + omitEmpty: true, + }, + { + key: 'txns', + valueSchema: new ArraySchema(DryrunTxnResult.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public error: string; /** @@ -2751,37 +3774,35 @@ export class DryrunResponse extends BaseModel { 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 class-methods-use-this + getEncodingSchema(): Schema { + return DryrunResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['error', this.error], + ['protocol-version', this.protocolVersion], + ['txns', this.txns.map((v) => v.toEncodingData())], + ]); + } + + static fromEncodingData(data: unknown): DryrunResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded DryrunResponse: ${data}`); + } return new DryrunResponse({ - error: data['error'], - protocolVersion: data['protocol-version'], - txns: data['txns'].map(DryrunTxnResult.from_obj_for_encoding), + error: data.get('error'), + protocolVersion: data.get('protocol-version'), + txns: (data.get('txns') ?? []).map((v: unknown) => + DryrunTxnResult.fromEncodingData(v) + ), }); - /* eslint-enable dot-notation */ } } @@ -2789,7 +3810,24 @@ export class DryrunResponse extends BaseModel { * DryrunSource is TEAL source text that gets uploaded, compiled, and inserted into * transactions or application state. */ -export class DryrunSource extends BaseModel { +export class DryrunSource implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'app-index', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'field-name', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'source', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'txn-index', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + public appIndex: bigint; + /** * 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 @@ -2799,84 +3837,104 @@ export class DryrunSource extends BaseModel { public source: string; - public txnIndex: number | bigint; - - public appIndex: number | bigint; + public txnIndex: number; /** * Creates a new `DryrunSource` object. + * @param appIndex - * @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({ + appIndex, fieldName, source, txnIndex, - appIndex, }: { + appIndex: number | bigint; fieldName: string; source: string; txnIndex: number | bigint; - appIndex: number | bigint; }) { - super(); + this.appIndex = ensureBigInt(appIndex); this.fieldName = fieldName; this.source = source; - this.txnIndex = txnIndex; - this.appIndex = appIndex; + this.txnIndex = ensureSafeInteger(txnIndex); + } - this.attribute_map = { - fieldName: 'field-name', - source: 'source', - txnIndex: 'txn-index', - appIndex: 'app-index', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return DryrunSource.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['app-index', this.appIndex], + ['field-name', this.fieldName], + ['source', this.source], + ['txn-index', this.txnIndex], + ]); + } + + static fromEncodingData(data: unknown): DryrunSource { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded DryrunSource: ${data}`); + } return new DryrunSource({ - fieldName: data['field-name'], - source: data['source'], - txnIndex: data['txn-index'], - appIndex: data['app-index'], + appIndex: data.get('app-index'), + fieldName: data.get('field-name'), + source: data.get('source'), + txnIndex: data.get('txn-index'), }); - /* eslint-enable dot-notation */ } } /** * Stores the TEAL eval step data */ -export class DryrunState extends BaseModel { +export class DryrunState implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'line', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'pc', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'stack', + valueSchema: new ArraySchema(TealValue.encodingSchema), + omitEmpty: true, + }, + { + key: 'error', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'scratch', + valueSchema: new OptionalSchema( + new ArraySchema(TealValue.encodingSchema) + ), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Line number */ - public line: number | bigint; + public line: number; /** * Program counter */ - public pc: number | bigint; + public pc: number; public stack: TealValue[]; @@ -2908,44 +3966,51 @@ export class DryrunState extends BaseModel { error?: string; scratch?: TealValue[]; }) { - super(); - this.line = line; - this.pc = pc; + this.line = ensureSafeInteger(line); + this.pc = ensureSafeInteger(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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return DryrunState.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['line', this.line], + ['pc', this.pc], + ['stack', this.stack.map((v) => v.toEncodingData())], + ['error', this.error], + [ + 'scratch', + typeof this.scratch !== 'undefined' + ? this.scratch.map((v) => v.toEncodingData()) + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): DryrunState { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded DryrunState: ${data}`); + } return new DryrunState({ - line: data['line'], - pc: data['pc'], - stack: data['stack'].map(TealValue.from_obj_for_encoding), - error: data['error'], + line: data.get('line'), + pc: data.get('pc'), + stack: (data.get('stack') ?? []).map((v: unknown) => + TealValue.fromEncodingData(v) + ), + error: data.get('error'), scratch: - typeof data['scratch'] !== 'undefined' - ? data['scratch'].map(TealValue.from_obj_for_encoding) + typeof data.get('scratch') !== 'undefined' + ? data + .get('scratch') + .map((v: unknown) => TealValue.fromEncodingData(v)) : undefined, }); - /* eslint-enable dot-notation */ } } @@ -2953,7 +4018,83 @@ export class DryrunState extends BaseModel { * DryrunTxnResult contains any LogicSig or ApplicationCall program debug * information and state updates from a dryrun. */ -export class DryrunTxnResult extends BaseModel { +export class DryrunTxnResult implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'disassembly', + valueSchema: new ArraySchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'app-call-messages', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + }, + { + key: 'app-call-trace', + valueSchema: new OptionalSchema( + new ArraySchema(DryrunState.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'budget-added', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'budget-consumed', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'global-delta', + valueSchema: new OptionalSchema( + new ArraySchema(EvalDeltaKeyValue.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'local-deltas', + valueSchema: new OptionalSchema( + new ArraySchema(AccountStateDelta.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'logic-sig-disassembly', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + }, + { + key: 'logic-sig-messages', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + }, + { + key: 'logic-sig-trace', + valueSchema: new OptionalSchema( + new ArraySchema(DryrunState.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'logs', + valueSchema: new OptionalSchema( + new ArraySchema(new ByteArraySchema()) + ), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Disassembled program line by line. */ @@ -2966,12 +4107,12 @@ export class DryrunTxnResult extends BaseModel { /** * Budget added during execution of app call transaction. */ - public budgetAdded?: number | bigint; + public budgetAdded?: number; /** * Budget consumed during execution of app call transaction. */ - public budgetConsumed?: number | bigint; + public budgetConsumed?: number; /** * Application state delta. @@ -3030,121 +4171,202 @@ export class DryrunTxnResult extends BaseModel { logicSigTrace?: DryrunState[]; logs?: Uint8Array[]; }) { - super(); this.disassembly = disassembly; this.appCallMessages = appCallMessages; this.appCallTrace = appCallTrace; - this.budgetAdded = budgetAdded; - this.budgetConsumed = budgetConsumed; + this.budgetAdded = + typeof budgetAdded === 'undefined' + ? undefined + : ensureSafeInteger(budgetAdded); + this.budgetConsumed = + typeof budgetConsumed === 'undefined' + ? undefined + : ensureSafeInteger(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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return DryrunTxnResult.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['disassembly', this.disassembly], + ['app-call-messages', this.appCallMessages], + [ + 'app-call-trace', + typeof this.appCallTrace !== 'undefined' + ? this.appCallTrace.map((v) => v.toEncodingData()) + : undefined, + ], + ['budget-added', this.budgetAdded], + ['budget-consumed', this.budgetConsumed], + [ + 'global-delta', + typeof this.globalDelta !== 'undefined' + ? this.globalDelta.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'local-deltas', + typeof this.localDeltas !== 'undefined' + ? this.localDeltas.map((v) => v.toEncodingData()) + : undefined, + ], + ['logic-sig-disassembly', this.logicSigDisassembly], + ['logic-sig-messages', this.logicSigMessages], + [ + 'logic-sig-trace', + typeof this.logicSigTrace !== 'undefined' + ? this.logicSigTrace.map((v) => v.toEncodingData()) + : undefined, + ], + ['logs', this.logs], + ]); + } + + static fromEncodingData(data: unknown): DryrunTxnResult { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded DryrunTxnResult: ${data}`); + } return new DryrunTxnResult({ - disassembly: data['disassembly'], - appCallMessages: data['app-call-messages'], + disassembly: data.get('disassembly'), + appCallMessages: data.get('app-call-messages'), appCallTrace: - typeof data['app-call-trace'] !== 'undefined' - ? data['app-call-trace'].map(DryrunState.from_obj_for_encoding) + typeof data.get('app-call-trace') !== 'undefined' + ? data + .get('app-call-trace') + .map((v: unknown) => DryrunState.fromEncodingData(v)) : undefined, - budgetAdded: data['budget-added'], - budgetConsumed: data['budget-consumed'], + budgetAdded: data.get('budget-added'), + budgetConsumed: data.get('budget-consumed'), globalDelta: - typeof data['global-delta'] !== 'undefined' - ? data['global-delta'].map(EvalDeltaKeyValue.from_obj_for_encoding) + typeof data.get('global-delta') !== 'undefined' + ? data + .get('global-delta') + .map((v: unknown) => EvalDeltaKeyValue.fromEncodingData(v)) : undefined, localDeltas: - typeof data['local-deltas'] !== 'undefined' - ? data['local-deltas'].map(AccountStateDelta.from_obj_for_encoding) + typeof data.get('local-deltas') !== 'undefined' + ? data + .get('local-deltas') + .map((v: unknown) => AccountStateDelta.fromEncodingData(v)) : undefined, - logicSigDisassembly: data['logic-sig-disassembly'], - logicSigMessages: data['logic-sig-messages'], + logicSigDisassembly: data.get('logic-sig-disassembly'), + logicSigMessages: data.get('logic-sig-messages'), logicSigTrace: - typeof data['logic-sig-trace'] !== 'undefined' - ? data['logic-sig-trace'].map(DryrunState.from_obj_for_encoding) + typeof data.get('logic-sig-trace') !== 'undefined' + ? data + .get('logic-sig-trace') + .map((v: unknown) => DryrunState.fromEncodingData(v)) : undefined, - logs: data['logs'], + logs: data.get('logs'), }); - /* eslint-enable dot-notation */ } } /** * An error response with optional data field. */ -export class ErrorResponse extends BaseModel { +export class ErrorResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'message', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'data', + valueSchema: new OptionalSchema(UntypedValue.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public message: string; - public data?: Record; + public data?: UntypedValue; /** * Creates a new `ErrorResponse` object. * @param message - * @param data - */ - constructor({ - message, - data, - }: { - message: string; - data?: Record; - }) { - super(); + constructor({ message, data }: { message: string; data?: UntypedValue }) { this.message = message; this.data = data; + } - this.attribute_map = { - message: 'message', - data: 'data', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ErrorResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['message', this.message], + [ + 'data', + typeof this.data !== 'undefined' + ? this.data.toEncodingData() + : undefined, + ], + ]); } - // 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}`); + static fromEncodingData(data: unknown): ErrorResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ErrorResponse: ${data}`); + } return new ErrorResponse({ - message: data['message'], - data: data['data'], + message: data.get('message'), + data: + typeof data.get('data') !== 'undefined' + ? UntypedValue.fromEncodingData(data.get('data')) + : undefined, }); - /* eslint-enable dot-notation */ } } /** * Represents a TEAL value delta. */ -export class EvalDelta extends BaseModel { +export class EvalDelta implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'action', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'bytes', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'uint', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (at) delta action. */ - public action: number | bigint; + public action: number; /** * (bs) bytes value. @@ -3154,7 +4376,7 @@ export class EvalDelta extends BaseModel { /** * (ui) uint value. */ - public uint?: number | bigint; + public uint?: bigint; /** * Creates a new `EvalDelta` object. @@ -3171,36 +4393,53 @@ export class EvalDelta extends BaseModel { bytes?: string; uint?: number | bigint; }) { - super(); - this.action = action; + this.action = ensureSafeInteger(action); this.bytes = bytes; - this.uint = uint; + this.uint = typeof uint === 'undefined' ? undefined : ensureBigInt(uint); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return EvalDelta.encodingSchema; + } - this.attribute_map = { - action: 'action', - bytes: 'bytes', - uint: 'uint', - }; + toEncodingData(): Map { + return new Map([ + ['action', this.action], + ['bytes', this.bytes], + ['uint', this.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}`); + static fromEncodingData(data: unknown): EvalDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded EvalDelta: ${data}`); + } return new EvalDelta({ - action: data['action'], - bytes: data['bytes'], - uint: data['uint'], + action: data.get('action'), + bytes: data.get('bytes'), + uint: data.get('uint'), }); - /* eslint-enable dot-notation */ } } /** * Key-value pairs for StateDelta. */ -export class EvalDeltaKeyValue extends BaseModel { +export class EvalDeltaKeyValue implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'key', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'value', valueSchema: EvalDelta.encodingSchema, omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + public key: string; /** @@ -3214,100 +4453,132 @@ export class EvalDeltaKeyValue extends BaseModel { * @param value - Represents a TEAL value delta. */ constructor({ key, value }: { key: string; value: EvalDelta }) { - super(); this.key = key; this.value = value; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return EvalDeltaKeyValue.encodingSchema; + } - this.attribute_map = { - key: 'key', - value: 'value', - }; + toEncodingData(): Map { + return new Map([ + ['key', this.key], + ['value', this.value.toEncodingData()], + ]); } - // 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}`); + static fromEncodingData(data: unknown): EvalDeltaKeyValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded EvalDeltaKeyValue: ${data}`); + } return new EvalDeltaKeyValue({ - key: data['key'], - value: EvalDelta.from_obj_for_encoding(data['value']), + key: data.get('key'), + value: EvalDelta.fromEncodingData(data.get('value') ?? new Map()), }); - /* eslint-enable dot-notation */ } } /** * Response containing the timestamp offset in seconds */ -export class GetBlockTimeStampOffsetResponse extends BaseModel { +export class GetBlockTimeStampOffsetResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'offset', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + /** * Timestamp offset in seconds. */ - public offset: number | bigint; + public offset: number; /** * Creates a new `GetBlockTimeStampOffsetResponse` object. * @param offset - Timestamp offset in seconds. */ constructor({ offset }: { offset: number | bigint }) { - super(); - this.offset = offset; + this.offset = ensureSafeInteger(offset); + } - this.attribute_map = { - offset: 'offset', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return GetBlockTimeStampOffsetResponse.encodingSchema; } - // 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}`); + toEncodingData(): Map { + return new Map([['offset', this.offset]]); + } + + static fromEncodingData(data: unknown): GetBlockTimeStampOffsetResponse { + if (!(data instanceof Map)) { + throw new Error( + `Invalid decoded GetBlockTimeStampOffsetResponse: ${data}` + ); + } return new GetBlockTimeStampOffsetResponse({ - offset: data['offset'], + offset: data.get('offset'), }); - /* eslint-enable dot-notation */ } } /** * Response containing the ledger's minimum sync round */ -export class GetSyncRoundResponse extends BaseModel { +export class GetSyncRoundResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + /** * The minimum sync round for the ledger. */ - public round: number | bigint; + public round: 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.round = ensureBigInt(round); + } - this.attribute_map = { - round: 'round', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return GetSyncRoundResponse.encodingSchema; } - // 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}`); + toEncodingData(): Map { + return new Map([['round', this.round]]); + } + + static fromEncodingData(data: unknown): GetSyncRoundResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded GetSyncRoundResponse: ${data}`); + } return new GetSyncRoundResponse({ - round: data['round'], + round: data.get('round'), }); - /* eslint-enable dot-notation */ } } @@ -3315,7 +4586,28 @@ export class GetSyncRoundResponse extends BaseModel { * A single Delta containing the key, the previous value and the current value for * a single round. */ -export class KvDelta extends BaseModel { +export class KvDelta implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'value', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * The key, base64 encoded. */ @@ -3338,41 +4630,62 @@ export class KvDelta extends BaseModel { 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 */ + this.key = typeof key === 'string' ? base64ToBytes(key) : key; + this.value = typeof value === 'string' ? base64ToBytes(value) : value; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return KvDelta.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['key', this.key], + ['value', this.value], + ]); + } + + static fromEncodingData(data: unknown): KvDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded KvDelta: ${data}`); + } return new KvDelta({ - key: data['key'], - value: data['value'], + key: data.get('key'), + value: data.get('value'), }); - /* eslint-enable dot-notation */ } } /** * Contains a ledger delta for a single transaction group */ -export class LedgerStateDeltaForTransactionGroup extends BaseModel { +export class LedgerStateDeltaForTransactionGroup implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'Delta', + valueSchema: LedgerStateDelta.encodingSchema, + omitEmpty: true, + }, + { + key: 'Ids', + valueSchema: new ArraySchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Ledger StateDelta object */ - public delta: Record; + public delta: LedgerStateDelta; public ids: string[]; @@ -3381,44 +4694,58 @@ export class LedgerStateDeltaForTransactionGroup extends BaseModel { * @param delta - Ledger StateDelta object * @param ids - */ - constructor({ delta, ids }: { delta: Record; ids: string[] }) { - super(); + constructor({ delta, ids }: { delta: LedgerStateDelta; ids: string[] }) { this.delta = delta; this.ids = ids; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return LedgerStateDeltaForTransactionGroup.encodingSchema; + } - this.attribute_map = { - delta: 'Delta', - ids: 'Ids', - }; + toEncodingData(): Map { + return new Map([ + ['Delta', this.delta.toEncodingData()], + ['Ids', this.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'])) + static fromEncodingData(data: unknown): LedgerStateDeltaForTransactionGroup { + if (!(data instanceof Map)) { throw new Error( - `Response is missing required array field 'Ids': ${data}` + `Invalid decoded LedgerStateDeltaForTransactionGroup: ${data}` ); + } return new LedgerStateDeltaForTransactionGroup({ - delta: data['Delta'], - ids: data['Ids'], + delta: LedgerStateDelta.fromEncodingData(data.get('Delta') ?? new Map()), + ids: data.get('Ids'), }); - /* eslint-enable dot-notation */ } } /** * Proof of membership and position of a light block header. */ -export class LightBlockHeaderProof extends BaseModel { +export class LightBlockHeaderProof implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'index', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'proof', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'treedepth', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * The index of the light block header in the vector commitment tree */ - public index: number | bigint; + public index: number; /** * The encoded proof. @@ -3429,7 +4756,7 @@ export class LightBlockHeaderProof extends BaseModel { * 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; + public treedepth: number; /** * Creates a new `LightBlockHeaderProof` object. @@ -3447,56 +4774,186 @@ export class LightBlockHeaderProof extends BaseModel { 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}` - ); + this.index = ensureSafeInteger(index); + this.proof = typeof proof === 'string' ? base64ToBytes(proof) : proof; + this.treedepth = ensureSafeInteger(treedepth); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return LightBlockHeaderProof.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['index', this.index], + ['proof', this.proof], + ['treedepth', this.treedepth], + ]); + } + + static fromEncodingData(data: unknown): LightBlockHeaderProof { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded LightBlockHeaderProof: ${data}`); + } return new LightBlockHeaderProof({ - index: data['index'], - proof: data['proof'], - treedepth: data['treedepth'], + index: data.get('index'), + proof: data.get('proof'), + treedepth: data.get('treedepth'), }); - /* eslint-enable dot-notation */ } } /** * */ -export class NodeStatusResponse extends BaseModel { +export class NodeStatusResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'catchup-time', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'last-round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'last-version', + valueSchema: new StringSchema(), + omitEmpty: true, + }, + { + key: 'next-version', + valueSchema: new StringSchema(), + omitEmpty: true, + }, + { + key: 'next-version-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-version-supported', + valueSchema: new BooleanSchema(), + omitEmpty: true, + }, + { + key: 'stopped-at-unsupported-round', + valueSchema: new BooleanSchema(), + omitEmpty: true, + }, + { + key: 'time-since-last-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'catchpoint', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'catchpoint-acquired-blocks', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'catchpoint-processed-accounts', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'catchpoint-processed-kvs', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'catchpoint-total-accounts', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'catchpoint-total-blocks', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'catchpoint-total-kvs', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'catchpoint-verified-accounts', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'catchpoint-verified-kvs', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'last-catchpoint', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'upgrade-delay', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'upgrade-next-protocol-vote-before', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'upgrade-no-votes', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'upgrade-node-vote', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'upgrade-vote-rounds', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'upgrade-votes', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'upgrade-votes-required', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'upgrade-yes-votes', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * CatchupTime in nanoseconds */ - public catchupTime: number | bigint; + public catchupTime: bigint; /** * LastRound indicates the last round seen */ - public lastRound: number | bigint; + public lastRound: bigint; /** * LastVersion indicates the last consensus version supported @@ -3511,7 +4968,7 @@ export class NodeStatusResponse extends BaseModel { /** * NextVersionRound is the round at which the next consensus version will apply */ - public nextVersionRound: number | bigint; + public nextVersionRound: bigint; /** * NextVersionSupported indicates whether the next consensus version is supported @@ -3528,7 +4985,7 @@ export class NodeStatusResponse extends BaseModel { /** * TimeSinceLastRound in nanoseconds */ - public timeSinceLastRound: number | bigint; + public timeSinceLastRound: bigint; /** * The current catchpoint that is being caught up to @@ -3539,47 +4996,47 @@ export class NodeStatusResponse extends BaseModel { * The number of blocks that have already been obtained by the node as part of the * catchup */ - public catchpointAcquiredBlocks?: number | bigint; + public catchpointAcquiredBlocks?: number; /** * The number of accounts from the current catchpoint that have been processed so * far as part of the catchup */ - public catchpointProcessedAccounts?: number | bigint; + public catchpointProcessedAccounts?: number; /** * 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; + public catchpointProcessedKvs?: number; /** * The total number of accounts included in the current catchpoint */ - public catchpointTotalAccounts?: number | bigint; + public catchpointTotalAccounts?: number; /** * The total number of blocks that are required to complete the current catchpoint * catchup */ - public catchpointTotalBlocks?: number | bigint; + public catchpointTotalBlocks?: number; /** * The total number of key-values (KVs) included in the current catchpoint */ - public catchpointTotalKvs?: number | bigint; + public catchpointTotalKvs?: number; /** * The number of accounts from the current catchpoint that have been verified so * far as part of the catchup */ - public catchpointVerifiedAccounts?: number | bigint; + public catchpointVerifiedAccounts?: number; /** * 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; + public catchpointVerifiedKvs?: number; /** * The last catchpoint seen by the node @@ -3589,17 +5046,17 @@ export class NodeStatusResponse extends BaseModel { /** * Upgrade delay */ - public upgradeDelay?: number | bigint; + public upgradeDelay?: bigint; /** * Next protocol round */ - public upgradeNextProtocolVoteBefore?: number | bigint; + public upgradeNextProtocolVoteBefore?: bigint; /** * No votes cast for consensus upgrade */ - public upgradeNoVotes?: number | bigint; + public upgradeNoVotes?: number; /** * This node's upgrade vote @@ -3609,22 +5066,22 @@ export class NodeStatusResponse extends BaseModel { /** * Total voting rounds for current upgrade */ - public upgradeVoteRounds?: number | bigint; + public upgradeVoteRounds?: number; /** * Total votes cast for consensus upgrade */ - public upgradeVotes?: number | bigint; + public upgradeVotes?: number; /** * Yes votes required for consensus upgrade */ - public upgradeVotesRequired?: number | bigint; + public upgradeVotesRequired?: number; /** * Yes votes cast for consensus upgrade */ - public upgradeYesVotes?: number | bigint; + public upgradeYesVotes?: number; /** * Creates a new `NodeStatusResponse` object. @@ -3718,128 +5175,149 @@ export class NodeStatusResponse extends BaseModel { upgradeVotesRequired?: number | bigint; upgradeYesVotes?: number | bigint; }) { - super(); - this.catchupTime = catchupTime; - this.lastRound = lastRound; + this.catchupTime = ensureBigInt(catchupTime); + this.lastRound = ensureBigInt(lastRound); this.lastVersion = lastVersion; this.nextVersion = nextVersion; - this.nextVersionRound = nextVersionRound; + this.nextVersionRound = ensureBigInt(nextVersionRound); this.nextVersionSupported = nextVersionSupported; this.stoppedAtUnsupportedRound = stoppedAtUnsupportedRound; - this.timeSinceLastRound = timeSinceLastRound; + this.timeSinceLastRound = ensureBigInt(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.catchpointAcquiredBlocks = + typeof catchpointAcquiredBlocks === 'undefined' + ? undefined + : ensureSafeInteger(catchpointAcquiredBlocks); + this.catchpointProcessedAccounts = + typeof catchpointProcessedAccounts === 'undefined' + ? undefined + : ensureSafeInteger(catchpointProcessedAccounts); + this.catchpointProcessedKvs = + typeof catchpointProcessedKvs === 'undefined' + ? undefined + : ensureSafeInteger(catchpointProcessedKvs); + this.catchpointTotalAccounts = + typeof catchpointTotalAccounts === 'undefined' + ? undefined + : ensureSafeInteger(catchpointTotalAccounts); + this.catchpointTotalBlocks = + typeof catchpointTotalBlocks === 'undefined' + ? undefined + : ensureSafeInteger(catchpointTotalBlocks); + this.catchpointTotalKvs = + typeof catchpointTotalKvs === 'undefined' + ? undefined + : ensureSafeInteger(catchpointTotalKvs); + this.catchpointVerifiedAccounts = + typeof catchpointVerifiedAccounts === 'undefined' + ? undefined + : ensureSafeInteger(catchpointVerifiedAccounts); + this.catchpointVerifiedKvs = + typeof catchpointVerifiedKvs === 'undefined' + ? undefined + : ensureSafeInteger(catchpointVerifiedKvs); this.lastCatchpoint = lastCatchpoint; - this.upgradeDelay = upgradeDelay; - this.upgradeNextProtocolVoteBefore = upgradeNextProtocolVoteBefore; - this.upgradeNoVotes = upgradeNoVotes; + this.upgradeDelay = + typeof upgradeDelay === 'undefined' + ? undefined + : ensureBigInt(upgradeDelay); + this.upgradeNextProtocolVoteBefore = + typeof upgradeNextProtocolVoteBefore === 'undefined' + ? undefined + : ensureBigInt(upgradeNextProtocolVoteBefore); + this.upgradeNoVotes = + typeof upgradeNoVotes === 'undefined' + ? undefined + : ensureSafeInteger(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}` - ); + this.upgradeVoteRounds = + typeof upgradeVoteRounds === 'undefined' + ? undefined + : ensureSafeInteger(upgradeVoteRounds); + this.upgradeVotes = + typeof upgradeVotes === 'undefined' + ? undefined + : ensureSafeInteger(upgradeVotes); + this.upgradeVotesRequired = + typeof upgradeVotesRequired === 'undefined' + ? undefined + : ensureSafeInteger(upgradeVotesRequired); + this.upgradeYesVotes = + typeof upgradeYesVotes === 'undefined' + ? undefined + : ensureSafeInteger(upgradeYesVotes); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return NodeStatusResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['catchup-time', this.catchupTime], + ['last-round', this.lastRound], + ['last-version', this.lastVersion], + ['next-version', this.nextVersion], + ['next-version-round', this.nextVersionRound], + ['next-version-supported', this.nextVersionSupported], + ['stopped-at-unsupported-round', this.stoppedAtUnsupportedRound], + ['time-since-last-round', this.timeSinceLastRound], + ['catchpoint', this.catchpoint], + ['catchpoint-acquired-blocks', this.catchpointAcquiredBlocks], + ['catchpoint-processed-accounts', this.catchpointProcessedAccounts], + ['catchpoint-processed-kvs', this.catchpointProcessedKvs], + ['catchpoint-total-accounts', this.catchpointTotalAccounts], + ['catchpoint-total-blocks', this.catchpointTotalBlocks], + ['catchpoint-total-kvs', this.catchpointTotalKvs], + ['catchpoint-verified-accounts', this.catchpointVerifiedAccounts], + ['catchpoint-verified-kvs', this.catchpointVerifiedKvs], + ['last-catchpoint', this.lastCatchpoint], + ['upgrade-delay', this.upgradeDelay], + ['upgrade-next-protocol-vote-before', this.upgradeNextProtocolVoteBefore], + ['upgrade-no-votes', this.upgradeNoVotes], + ['upgrade-node-vote', this.upgradeNodeVote], + ['upgrade-vote-rounds', this.upgradeVoteRounds], + ['upgrade-votes', this.upgradeVotes], + ['upgrade-votes-required', this.upgradeVotesRequired], + ['upgrade-yes-votes', this.upgradeYesVotes], + ]); + } + + static fromEncodingData(data: unknown): NodeStatusResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded NodeStatusResponse: ${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'], + catchupTime: data.get('catchup-time'), + lastRound: data.get('last-round'), + lastVersion: data.get('last-version'), + nextVersion: data.get('next-version'), + nextVersionRound: data.get('next-version-round'), + nextVersionSupported: data.get('next-version-supported'), + stoppedAtUnsupportedRound: data.get('stopped-at-unsupported-round'), + timeSinceLastRound: data.get('time-since-last-round'), + catchpoint: data.get('catchpoint'), + catchpointAcquiredBlocks: data.get('catchpoint-acquired-blocks'), + catchpointProcessedAccounts: data.get('catchpoint-processed-accounts'), + catchpointProcessedKvs: data.get('catchpoint-processed-kvs'), + catchpointTotalAccounts: data.get('catchpoint-total-accounts'), + catchpointTotalBlocks: data.get('catchpoint-total-blocks'), + catchpointTotalKvs: data.get('catchpoint-total-kvs'), + catchpointVerifiedAccounts: data.get('catchpoint-verified-accounts'), + catchpointVerifiedKvs: data.get('catchpoint-verified-kvs'), + lastCatchpoint: data.get('last-catchpoint'), + upgradeDelay: data.get('upgrade-delay'), + upgradeNextProtocolVoteBefore: data.get( + 'upgrade-next-protocol-vote-before' + ), + upgradeNoVotes: data.get('upgrade-no-votes'), + upgradeNodeVote: data.get('upgrade-node-vote'), + upgradeVoteRounds: data.get('upgrade-vote-rounds'), + upgradeVotes: data.get('upgrade-votes'), + upgradeVotesRequired: data.get('upgrade-votes-required'), + upgradeYesVotes: data.get('upgrade-yes-votes'), }); - /* eslint-enable dot-notation */ } } @@ -3847,7 +5325,92 @@ export class NodeStatusResponse extends BaseModel { * 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 { +export class PendingTransactionResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'pool-error', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'txn', + valueSchema: SignedTransaction.encodingSchema, + omitEmpty: true, + }, + { + key: 'application-index', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'asset-closing-amount', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'asset-index', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'close-rewards', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'closing-amount', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'confirmed-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'global-state-delta', + valueSchema: new OptionalSchema( + new ArraySchema(EvalDeltaKeyValue.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'inner-txns', + valueSchema: new OptionalSchema( + new ArraySchema(PendingTransactionResponse.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'local-state-delta', + valueSchema: new OptionalSchema( + new ArraySchema(AccountStateDelta.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'logs', + valueSchema: new OptionalSchema( + new ArraySchema(new ByteArraySchema()) + ), + omitEmpty: true, + }, + { + key: 'receiver-rewards', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'sender-rewards', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Indicates that the transaction was kicked out of this node's transaction pool * (and specifies why that happened). An empty string indicates the transaction @@ -3858,38 +5421,38 @@ export class PendingTransactionResponse extends BaseModel { /** * The raw signed transaction. */ - public txn: EncodedSignedTransaction; + public txn: SignedTransaction; /** * The application index if the transaction was found and it created an * application. */ - public applicationIndex?: number | bigint; + public applicationIndex?: bigint; /** * The number of the asset's unit that were transferred to the close-to address. */ - public assetClosingAmount?: number | bigint; + public assetClosingAmount?: bigint; /** * The asset index if the transaction was found and it created an asset. */ - public assetIndex?: number | bigint; + public assetIndex?: bigint; /** * Rewards in microalgos applied to the close remainder to account. */ - public closeRewards?: number | bigint; + public closeRewards?: bigint; /** * Closing amount for the transaction. */ - public closingAmount?: number | bigint; + public closingAmount?: bigint; /** * The round where this transaction was confirmed, if present. */ - public confirmedRound?: number | bigint; + public confirmedRound?: bigint; /** * Global state key/value changes for the application being executed by this @@ -3916,12 +5479,12 @@ export class PendingTransactionResponse extends BaseModel { /** * Rewards in microalgos applied to the receiver account. */ - public receiverRewards?: number | bigint; + public receiverRewards?: bigint; /** * Rewards in microalgos applied to the sender account. */ - public senderRewards?: number | bigint; + public senderRewards?: bigint; /** * Creates a new `PendingTransactionResponse` object. @@ -3962,7 +5525,7 @@ export class PendingTransactionResponse extends BaseModel { senderRewards, }: { poolError: string; - txn: EncodedSignedTransaction; + txn: SignedTransaction; applicationIndex?: number | bigint; assetClosingAmount?: number | bigint; assetIndex?: number | bigint; @@ -3976,83 +5539,120 @@ export class PendingTransactionResponse extends BaseModel { 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.applicationIndex = + typeof applicationIndex === 'undefined' + ? undefined + : ensureBigInt(applicationIndex); + this.assetClosingAmount = + typeof assetClosingAmount === 'undefined' + ? undefined + : ensureBigInt(assetClosingAmount); + this.assetIndex = + typeof assetIndex === 'undefined' ? undefined : ensureBigInt(assetIndex); + this.closeRewards = + typeof closeRewards === 'undefined' + ? undefined + : ensureBigInt(closeRewards); + this.closingAmount = + typeof closingAmount === 'undefined' + ? undefined + : ensureBigInt(closingAmount); + this.confirmedRound = + typeof confirmedRound === 'undefined' + ? undefined + : ensureBigInt(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}`); + this.receiverRewards = + typeof receiverRewards === 'undefined' + ? undefined + : ensureBigInt(receiverRewards); + this.senderRewards = + typeof senderRewards === 'undefined' + ? undefined + : ensureBigInt(senderRewards); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return PendingTransactionResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['pool-error', this.poolError], + ['txn', this.txn.toEncodingData()], + ['application-index', this.applicationIndex], + ['asset-closing-amount', this.assetClosingAmount], + ['asset-index', this.assetIndex], + ['close-rewards', this.closeRewards], + ['closing-amount', this.closingAmount], + ['confirmed-round', this.confirmedRound], + [ + 'global-state-delta', + typeof this.globalStateDelta !== 'undefined' + ? this.globalStateDelta.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'inner-txns', + typeof this.innerTxns !== 'undefined' + ? this.innerTxns.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'local-state-delta', + typeof this.localStateDelta !== 'undefined' + ? this.localStateDelta.map((v) => v.toEncodingData()) + : undefined, + ], + ['logs', this.logs], + ['receiver-rewards', this.receiverRewards], + ['sender-rewards', this.senderRewards], + ]); + } + + static fromEncodingData(data: unknown): PendingTransactionResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded PendingTransactionResponse: ${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'], + poolError: data.get('pool-error'), + txn: SignedTransaction.fromEncodingData(data.get('txn') ?? new Map()), + applicationIndex: data.get('application-index'), + assetClosingAmount: data.get('asset-closing-amount'), + assetIndex: data.get('asset-index'), + closeRewards: data.get('close-rewards'), + closingAmount: data.get('closing-amount'), + confirmedRound: data.get('confirmed-round'), globalStateDelta: - typeof data['global-state-delta'] !== 'undefined' - ? data['global-state-delta'].map( - EvalDeltaKeyValue.from_obj_for_encoding - ) + typeof data.get('global-state-delta') !== 'undefined' + ? data + .get('global-state-delta') + .map((v: unknown) => EvalDeltaKeyValue.fromEncodingData(v)) : undefined, innerTxns: - typeof data['inner-txns'] !== 'undefined' - ? data['inner-txns'].map( - PendingTransactionResponse.from_obj_for_encoding - ) + typeof data.get('inner-txns') !== 'undefined' + ? data + .get('inner-txns') + .map((v: unknown) => + PendingTransactionResponse.fromEncodingData(v) + ) : undefined, localStateDelta: - typeof data['local-state-delta'] !== 'undefined' - ? data['local-state-delta'].map( - AccountStateDelta.from_obj_for_encoding - ) + typeof data.get('local-state-delta') !== 'undefined' + ? data + .get('local-state-delta') + .map((v: unknown) => AccountStateDelta.fromEncodingData(v)) : undefined, - logs: data['logs'], - receiverRewards: data['receiver-rewards'], - senderRewards: data['sender-rewards'], + logs: data.get('logs'), + receiverRewards: data.get('receiver-rewards'), + senderRewards: data.get('sender-rewards'), }); - /* eslint-enable dot-notation */ } } @@ -4061,16 +5661,37 @@ export class PendingTransactionResponse extends BaseModel { * 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 { +export class PendingTransactionsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'top-transactions', + valueSchema: new ArraySchema(SignedTransaction.encodingSchema), + omitEmpty: true, + }, + { + key: 'total-transactions', + valueSchema: new Uint64Schema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * An array of signed transaction objects. */ - public topTransactions: EncodedSignedTransaction[]; + public topTransactions: SignedTransaction[]; /** * Total number of transactions in the pool. */ - public totalTransactions: number | bigint; + public totalTransactions: number; /** * Creates a new `PendingTransactionsResponse` object. @@ -4081,44 +5702,56 @@ export class PendingTransactionsResponse extends BaseModel { topTransactions, totalTransactions, }: { - topTransactions: EncodedSignedTransaction[]; + topTransactions: SignedTransaction[]; totalTransactions: number | bigint; }) { - super(); this.topTransactions = topTransactions; - this.totalTransactions = totalTransactions; + this.totalTransactions = ensureSafeInteger(totalTransactions); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return PendingTransactionsResponse.encodingSchema; + } - this.attribute_map = { - topTransactions: 'top-transactions', - totalTransactions: 'total-transactions', - }; + toEncodingData(): Map { + return new Map([ + ['top-transactions', this.topTransactions.map((v) => v.toEncodingData())], + ['total-transactions', this.totalTransactions], + ]); } - // 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}` - ); + static fromEncodingData(data: unknown): PendingTransactionsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded PendingTransactionsResponse: ${data}`); + } return new PendingTransactionsResponse({ - topTransactions: data['top-transactions'], - totalTransactions: data['total-transactions'], + topTransactions: (data.get('top-transactions') ?? []).map((v: unknown) => + SignedTransaction.fromEncodingData(v) + ), + totalTransactions: data.get('total-transactions'), }); - /* eslint-enable dot-notation */ } } /** * Transaction ID of the submission. */ -export class PostTransactionsResponse extends BaseModel { +export class PostTransactionsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'txId', + valueSchema: new StringSchema(), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + /** * encoding of the transaction hash. */ @@ -4129,32 +5762,49 @@ export class PostTransactionsResponse extends BaseModel { * @param txid - encoding of the transaction hash. */ constructor({ txid }: { txid: string }) { - super(); this.txid = txid; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return PostTransactionsResponse.encodingSchema; + } - this.attribute_map = { - txid: 'txId', - }; + toEncodingData(): Map { + return new Map([['txId', this.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}`); + static fromEncodingData(data: unknown): PostTransactionsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded PostTransactionsResponse: ${data}`); + } return new PostTransactionsResponse({ - txid: data['txId'], + txid: data.get('txId'), }); - /* eslint-enable dot-notation */ } } /** * A write operation into a scratch slot. */ -export class ScratchChange extends BaseModel { +export class ScratchChange implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'new-value', + valueSchema: AvmValue.encodingSchema, + omitEmpty: true, + }, + { key: 'slot', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * Represents an AVM value. */ @@ -4163,7 +5813,7 @@ export class ScratchChange extends BaseModel { /** * The scratch slot written. */ - public slot: number | bigint; + public slot: number; /** * Creates a new `ScratchChange` object. @@ -4177,37 +5827,53 @@ export class ScratchChange extends BaseModel { newValue: AvmValue; slot: number | bigint; }) { - super(); this.newValue = newValue; - this.slot = slot; + this.slot = ensureSafeInteger(slot); + } - this.attribute_map = { - newValue: 'new-value', - slot: 'slot', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ScratchChange.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): ScratchChange { - /* eslint-disable dot-notation */ - if (typeof data['new-value'] === 'undefined') - throw new Error( - `Response is missing required field 'new-value': ${data}` - ); - if (typeof data['slot'] === 'undefined') - throw new Error(`Response is missing required field 'slot': ${data}`); + toEncodingData(): Map { + return new Map([ + ['new-value', this.newValue.toEncodingData()], + ['slot', this.slot], + ]); + } + + static fromEncodingData(data: unknown): ScratchChange { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ScratchChange: ${data}`); + } return new ScratchChange({ - newValue: AvmValue.from_obj_for_encoding(data['new-value']), - slot: data['slot'], + newValue: AvmValue.fromEncodingData(data.get('new-value') ?? new Map()), + slot: data.get('slot'), }); - /* eslint-enable dot-notation */ } } /** * Initial states of resources that were accessed during simulation. */ -export class SimulateInitialStates extends BaseModel { +export class SimulateInitialStates implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'app-initial-states', + valueSchema: new OptionalSchema( + new ArraySchema(ApplicationInitialStates.encodingSchema) + ), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + /** * The initial states of accessed application before simulation. The order of this * array is arbitrary. @@ -4224,35 +5890,97 @@ export class SimulateInitialStates extends BaseModel { }: { appInitialStates?: ApplicationInitialStates[]; }) { - super(); this.appInitialStates = appInitialStates; + } - this.attribute_map = { - appInitialStates: 'app-initial-states', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SimulateInitialStates.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): SimulateInitialStates { - /* eslint-disable dot-notation */ + toEncodingData(): Map { + return new Map([ + [ + 'app-initial-states', + typeof this.appInitialStates !== 'undefined' + ? this.appInitialStates.map((v) => v.toEncodingData()) + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): SimulateInitialStates { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SimulateInitialStates: ${data}`); + } return new SimulateInitialStates({ appInitialStates: - typeof data['app-initial-states'] !== 'undefined' - ? data['app-initial-states'].map( - ApplicationInitialStates.from_obj_for_encoding - ) + typeof data.get('app-initial-states') !== 'undefined' + ? data + .get('app-initial-states') + .map((v: unknown) => ApplicationInitialStates.fromEncodingData(v)) : undefined, }); - /* eslint-enable dot-notation */ } } /** * Request type for simulation endpoint. */ -export class SimulateRequest extends BaseModel { +export class SimulateRequest implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'txn-groups', + valueSchema: new ArraySchema( + SimulateRequestTransactionGroup.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'allow-empty-signatures', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'allow-more-logging', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'allow-unnamed-resources', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'exec-trace-config', + valueSchema: new OptionalSchema(SimulateTraceConfig.encodingSchema), + omitEmpty: true, + }, + { + key: 'extra-opcode-budget', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'fix-signers', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * The transaction groups to simulate. */ @@ -4282,7 +6010,7 @@ export class SimulateRequest extends BaseModel { /** * Applies extra opcode budget during simulation for each transaction group. */ - public extraOpcodeBudget?: number | bigint; + public extraOpcodeBudget?: number; /** * If true, signers for transactions that are missing signatures will be fixed @@ -4296,7 +6024,7 @@ export class SimulateRequest extends BaseModel { * rounds will be available (controlled by the node config value MaxAcctLookback). * If not specified, defaults to the latest available round. */ - public round?: number | bigint; + public round?: bigint; /** * Creates a new `SimulateRequest` object. @@ -4333,101 +6061,166 @@ export class SimulateRequest extends BaseModel { fixSigners?: boolean; round?: number | bigint; }) { - super(); this.txnGroups = txnGroups; this.allowEmptySignatures = allowEmptySignatures; this.allowMoreLogging = allowMoreLogging; this.allowUnnamedResources = allowUnnamedResources; this.execTraceConfig = execTraceConfig; - this.extraOpcodeBudget = extraOpcodeBudget; + this.extraOpcodeBudget = + typeof extraOpcodeBudget === 'undefined' + ? undefined + : ensureSafeInteger(extraOpcodeBudget); this.fixSigners = fixSigners; - this.round = round; - - this.attribute_map = { - txnGroups: 'txn-groups', - allowEmptySignatures: 'allow-empty-signatures', - allowMoreLogging: 'allow-more-logging', - allowUnnamedResources: 'allow-unnamed-resources', - execTraceConfig: 'exec-trace-config', - extraOpcodeBudget: 'extra-opcode-budget', - fixSigners: 'fix-signers', - round: 'round', - }; - } - - // 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}` - ); + this.round = typeof round === 'undefined' ? undefined : ensureBigInt(round); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SimulateRequest.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['txn-groups', this.txnGroups.map((v) => v.toEncodingData())], + ['allow-empty-signatures', this.allowEmptySignatures], + ['allow-more-logging', this.allowMoreLogging], + ['allow-unnamed-resources', this.allowUnnamedResources], + [ + 'exec-trace-config', + typeof this.execTraceConfig !== 'undefined' + ? this.execTraceConfig.toEncodingData() + : undefined, + ], + ['extra-opcode-budget', this.extraOpcodeBudget], + ['fix-signers', this.fixSigners], + ['round', this.round], + ]); + } + + static fromEncodingData(data: unknown): SimulateRequest { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SimulateRequest: ${data}`); + } return new SimulateRequest({ - txnGroups: data['txn-groups'].map( - SimulateRequestTransactionGroup.from_obj_for_encoding + txnGroups: (data.get('txn-groups') ?? []).map((v: unknown) => + SimulateRequestTransactionGroup.fromEncodingData(v) ), - allowEmptySignatures: data['allow-empty-signatures'], - allowMoreLogging: data['allow-more-logging'], - allowUnnamedResources: data['allow-unnamed-resources'], + allowEmptySignatures: data.get('allow-empty-signatures'), + allowMoreLogging: data.get('allow-more-logging'), + allowUnnamedResources: data.get('allow-unnamed-resources'), execTraceConfig: - typeof data['exec-trace-config'] !== 'undefined' - ? SimulateTraceConfig.from_obj_for_encoding(data['exec-trace-config']) + typeof data.get('exec-trace-config') !== 'undefined' + ? SimulateTraceConfig.fromEncodingData(data.get('exec-trace-config')) : undefined, - extraOpcodeBudget: data['extra-opcode-budget'], - fixSigners: data['fix-signers'], - round: data['round'], + extraOpcodeBudget: data.get('extra-opcode-budget'), + fixSigners: data.get('fix-signers'), + round: data.get('round'), }); - /* eslint-enable dot-notation */ } } /** * A transaction group to simulate. */ -export class SimulateRequestTransactionGroup extends BaseModel { +export class SimulateRequestTransactionGroup implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'txns', + valueSchema: new ArraySchema(SignedTransaction.encodingSchema), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + /** * An atomic transaction group. */ - public txns: EncodedSignedTransaction[]; + public txns: SignedTransaction[]; /** * Creates a new `SimulateRequestTransactionGroup` object. * @param txns - An atomic transaction group. */ - constructor({ txns }: { txns: EncodedSignedTransaction[] }) { - super(); + constructor({ txns }: { txns: SignedTransaction[] }) { this.txns = txns; + } - this.attribute_map = { - txns: 'txns', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SimulateRequestTransactionGroup.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): SimulateRequestTransactionGroup { - /* eslint-disable dot-notation */ - if (!Array.isArray(data['txns'])) + toEncodingData(): Map { + return new Map([ + ['txns', this.txns.map((v) => v.toEncodingData())], + ]); + } + + static fromEncodingData(data: unknown): SimulateRequestTransactionGroup { + if (!(data instanceof Map)) { throw new Error( - `Response is missing required array field 'txns': ${data}` + `Invalid decoded SimulateRequestTransactionGroup: ${data}` ); + } return new SimulateRequestTransactionGroup({ - txns: data['txns'], + txns: (data.get('txns') ?? []).map((v: unknown) => + SignedTransaction.fromEncodingData(v) + ), }); - /* eslint-enable dot-notation */ } } /** * Result of a transaction group simulation. */ -export class SimulateResponse extends BaseModel { +export class SimulateResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'last-round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'txn-groups', + valueSchema: new ArraySchema( + SimulateTransactionGroupResult.encodingSchema + ), + omitEmpty: true, + }, + { key: 'version', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'eval-overrides', + valueSchema: new OptionalSchema( + SimulationEvalOverrides.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'exec-trace-config', + valueSchema: new OptionalSchema(SimulateTraceConfig.encodingSchema), + omitEmpty: true, + }, + { + key: 'initial-states', + valueSchema: new OptionalSchema(SimulateInitialStates.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * The round immediately preceding this simulation. State changes through this * round were used to run this simulation. */ - public lastRound: number | bigint; + public lastRound: bigint; /** * A result object for each transaction group that was simulated. @@ -4437,7 +6230,7 @@ export class SimulateResponse extends BaseModel { /** * The version of this response object. */ - public version: number | bigint; + public version: number; /** * The set of parameters and limits override during simulation. If this set of @@ -4483,66 +6276,106 @@ export class SimulateResponse extends BaseModel { execTraceConfig?: SimulateTraceConfig; initialStates?: SimulateInitialStates; }) { - super(); - this.lastRound = lastRound; + this.lastRound = ensureBigInt(lastRound); this.txnGroups = txnGroups; - this.version = version; + this.version = ensureSafeInteger(version); this.evalOverrides = evalOverrides; this.execTraceConfig = execTraceConfig; this.initialStates = initialStates; + } - this.attribute_map = { - lastRound: 'last-round', - txnGroups: 'txn-groups', - version: 'version', - evalOverrides: 'eval-overrides', - execTraceConfig: 'exec-trace-config', - initialStates: 'initial-states', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SimulateResponse.encodingSchema; } - // 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}`); + toEncodingData(): Map { + return new Map([ + ['last-round', this.lastRound], + ['txn-groups', this.txnGroups.map((v) => v.toEncodingData())], + ['version', this.version], + [ + 'eval-overrides', + typeof this.evalOverrides !== 'undefined' + ? this.evalOverrides.toEncodingData() + : undefined, + ], + [ + 'exec-trace-config', + typeof this.execTraceConfig !== 'undefined' + ? this.execTraceConfig.toEncodingData() + : undefined, + ], + [ + 'initial-states', + typeof this.initialStates !== 'undefined' + ? this.initialStates.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): SimulateResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SimulateResponse: ${data}`); + } return new SimulateResponse({ - lastRound: data['last-round'], - txnGroups: data['txn-groups'].map( - SimulateTransactionGroupResult.from_obj_for_encoding + lastRound: data.get('last-round'), + txnGroups: (data.get('txn-groups') ?? []).map((v: unknown) => + SimulateTransactionGroupResult.fromEncodingData(v) ), - version: data['version'], + version: data.get('version'), evalOverrides: - typeof data['eval-overrides'] !== 'undefined' - ? SimulationEvalOverrides.from_obj_for_encoding( - data['eval-overrides'] - ) + typeof data.get('eval-overrides') !== 'undefined' + ? SimulationEvalOverrides.fromEncodingData(data.get('eval-overrides')) : undefined, execTraceConfig: - typeof data['exec-trace-config'] !== 'undefined' - ? SimulateTraceConfig.from_obj_for_encoding(data['exec-trace-config']) + typeof data.get('exec-trace-config') !== 'undefined' + ? SimulateTraceConfig.fromEncodingData(data.get('exec-trace-config')) : undefined, initialStates: - typeof data['initial-states'] !== 'undefined' - ? SimulateInitialStates.from_obj_for_encoding(data['initial-states']) + typeof data.get('initial-states') !== 'undefined' + ? SimulateInitialStates.fromEncodingData(data.get('initial-states')) : undefined, }); - /* eslint-enable dot-notation */ } } /** * An object that configures simulation execution trace. */ -export class SimulateTraceConfig extends BaseModel { +export class SimulateTraceConfig implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'enable', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'scratch-change', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'stack-change', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'state-change', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * A boolean option for opting in execution trace features simulation endpoint. */ @@ -4587,37 +6420,88 @@ export class SimulateTraceConfig extends BaseModel { stackChange?: boolean; stateChange?: boolean; }) { - super(); this.enable = enable; this.scratchChange = scratchChange; this.stackChange = stackChange; this.stateChange = stateChange; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SimulateTraceConfig.encodingSchema; + } - this.attribute_map = { - enable: 'enable', - scratchChange: 'scratch-change', - stackChange: 'stack-change', - stateChange: 'state-change', - }; + toEncodingData(): Map { + return new Map([ + ['enable', this.enable], + ['scratch-change', this.scratchChange], + ['stack-change', this.stackChange], + ['state-change', this.stateChange], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): SimulateTraceConfig { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): SimulateTraceConfig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SimulateTraceConfig: ${data}`); + } return new SimulateTraceConfig({ - enable: data['enable'], - scratchChange: data['scratch-change'], - stackChange: data['stack-change'], - stateChange: data['state-change'], + enable: data.get('enable'), + scratchChange: data.get('scratch-change'), + stackChange: data.get('stack-change'), + stateChange: data.get('state-change'), }); - /* eslint-enable dot-notation */ } } /** * Simulation result for an atomic transaction group */ -export class SimulateTransactionGroupResult extends BaseModel { +export class SimulateTransactionGroupResult implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'txn-results', + valueSchema: new ArraySchema( + SimulateTransactionResult.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'app-budget-added', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'app-budget-consumed', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'failed-at', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + omitEmpty: true, + }, + { + key: 'failure-message', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'unnamed-resources-accessed', + valueSchema: new OptionalSchema( + SimulateUnnamedResourcesAccessed.encodingSchema + ), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Simulation result for individual transactions */ @@ -4626,12 +6510,12 @@ export class SimulateTransactionGroupResult extends BaseModel { /** * Total budget added during execution of app calls in the transaction group. */ - public appBudgetAdded?: number | bigint; + public appBudgetAdded?: number; /** * Total budget consumed during execution of app calls in the transaction group. */ - public appBudgetConsumed?: number | bigint; + public appBudgetConsumed?: number; /** * If present, indicates which transaction in this group caused the failure. This @@ -4639,7 +6523,7 @@ export class SimulateTransactionGroupResult extends BaseModel { * the first element indicates the top-level transaction, and successive elements * indicate deeper inner transactions. */ - public failedAt?: (number | bigint)[]; + public failedAt?: number[]; /** * If present, indicates that the transaction group failed and specifies why that @@ -4696,56 +6580,117 @@ export class SimulateTransactionGroupResult extends BaseModel { failureMessage?: string; unnamedResourcesAccessed?: SimulateUnnamedResourcesAccessed; }) { - super(); this.txnResults = txnResults; - this.appBudgetAdded = appBudgetAdded; - this.appBudgetConsumed = appBudgetConsumed; - this.failedAt = failedAt; + this.appBudgetAdded = + typeof appBudgetAdded === 'undefined' + ? undefined + : ensureSafeInteger(appBudgetAdded); + this.appBudgetConsumed = + typeof appBudgetConsumed === 'undefined' + ? undefined + : ensureSafeInteger(appBudgetConsumed); + this.failedAt = + typeof failedAt === 'undefined' + ? undefined + : failedAt.map(ensureSafeInteger); this.failureMessage = failureMessage; this.unnamedResourcesAccessed = unnamedResourcesAccessed; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SimulateTransactionGroupResult.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['txn-results', this.txnResults.map((v) => v.toEncodingData())], + ['app-budget-added', this.appBudgetAdded], + ['app-budget-consumed', this.appBudgetConsumed], + ['failed-at', this.failedAt], + ['failure-message', this.failureMessage], + [ + 'unnamed-resources-accessed', + typeof this.unnamedResourcesAccessed !== 'undefined' + ? this.unnamedResourcesAccessed.toEncodingData() + : undefined, + ], + ]); + } - this.attribute_map = { - txnResults: 'txn-results', - appBudgetAdded: 'app-budget-added', - appBudgetConsumed: 'app-budget-consumed', - failedAt: 'failed-at', - failureMessage: 'failure-message', - unnamedResourcesAccessed: 'unnamed-resources-accessed', - }; - } - - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): SimulateTransactionGroupResult { - /* eslint-disable dot-notation */ - if (!Array.isArray(data['txn-results'])) + static fromEncodingData(data: unknown): SimulateTransactionGroupResult { + if (!(data instanceof Map)) { throw new Error( - `Response is missing required array field 'txn-results': ${data}` + `Invalid decoded SimulateTransactionGroupResult: ${data}` ); + } return new SimulateTransactionGroupResult({ - txnResults: data['txn-results'].map( - SimulateTransactionResult.from_obj_for_encoding + txnResults: (data.get('txn-results') ?? []).map((v: unknown) => + SimulateTransactionResult.fromEncodingData(v) ), - appBudgetAdded: data['app-budget-added'], - appBudgetConsumed: data['app-budget-consumed'], - failedAt: data['failed-at'], - failureMessage: data['failure-message'], + appBudgetAdded: data.get('app-budget-added'), + appBudgetConsumed: data.get('app-budget-consumed'), + failedAt: data.get('failed-at'), + failureMessage: data.get('failure-message'), unnamedResourcesAccessed: - typeof data['unnamed-resources-accessed'] !== 'undefined' - ? SimulateUnnamedResourcesAccessed.from_obj_for_encoding( - data['unnamed-resources-accessed'] + typeof data.get('unnamed-resources-accessed') !== 'undefined' + ? SimulateUnnamedResourcesAccessed.fromEncodingData( + data.get('unnamed-resources-accessed') ) : undefined, }); - /* eslint-enable dot-notation */ } } /** * Simulation result for an individual transaction */ -export class SimulateTransactionResult extends BaseModel { +export class SimulateTransactionResult implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'txn-result', + valueSchema: PendingTransactionResponse.encodingSchema, + omitEmpty: true, + }, + { + key: 'app-budget-consumed', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'exec-trace', + valueSchema: new OptionalSchema( + SimulationTransactionExecTrace.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'fixed-signer', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'logic-sig-budget-consumed', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'unnamed-resources-accessed', + valueSchema: new OptionalSchema( + SimulateUnnamedResourcesAccessed.encodingSchema + ), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Details about a pending transaction. If the transaction was recently confirmed, * includes confirmation details like the round and reward details. @@ -4756,7 +6701,7 @@ export class SimulateTransactionResult extends BaseModel { * 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; + public appBudgetConsumed?: number; /** * The execution trace of calling an app or a logic sig, containing the inner app @@ -4768,12 +6713,12 @@ export class SimulateTransactionResult extends BaseModel { * The account that needed to sign this transaction when no signature was provided * and the provided signer was incorrect. */ - public fixedSigner?: string; + public fixedSigner?: Address; /** * Budget used during execution of a logic sig transaction. */ - public logicSigBudgetConsumed?: number | bigint; + public logicSigBudgetConsumed?: number; /** * These are resources that were accessed by this group that would normally have @@ -4820,58 +6765,82 @@ export class SimulateTransactionResult extends BaseModel { txnResult: PendingTransactionResponse; appBudgetConsumed?: number | bigint; execTrace?: SimulationTransactionExecTrace; - fixedSigner?: string; + fixedSigner?: Address | string; logicSigBudgetConsumed?: number | bigint; unnamedResourcesAccessed?: SimulateUnnamedResourcesAccessed; }) { - super(); this.txnResult = txnResult; - this.appBudgetConsumed = appBudgetConsumed; + this.appBudgetConsumed = + typeof appBudgetConsumed === 'undefined' + ? undefined + : ensureSafeInteger(appBudgetConsumed); this.execTrace = execTrace; - this.fixedSigner = fixedSigner; - this.logicSigBudgetConsumed = logicSigBudgetConsumed; + this.fixedSigner = + typeof fixedSigner === 'string' + ? Address.fromString(fixedSigner) + : fixedSigner; + this.logicSigBudgetConsumed = + typeof logicSigBudgetConsumed === 'undefined' + ? undefined + : ensureSafeInteger(logicSigBudgetConsumed); this.unnamedResourcesAccessed = unnamedResourcesAccessed; + } - this.attribute_map = { - txnResult: 'txn-result', - appBudgetConsumed: 'app-budget-consumed', - execTrace: 'exec-trace', - fixedSigner: 'fixed-signer', - logicSigBudgetConsumed: 'logic-sig-budget-consumed', - unnamedResourcesAccessed: 'unnamed-resources-accessed', - }; - } - - // 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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SimulateTransactionResult.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['txn-result', this.txnResult.toEncodingData()], + ['app-budget-consumed', this.appBudgetConsumed], + [ + 'exec-trace', + typeof this.execTrace !== 'undefined' + ? this.execTrace.toEncodingData() + : undefined, + ], + [ + 'fixed-signer', + typeof this.fixedSigner !== 'undefined' + ? this.fixedSigner.toString() + : undefined, + ], + ['logic-sig-budget-consumed', this.logicSigBudgetConsumed], + [ + 'unnamed-resources-accessed', + typeof this.unnamedResourcesAccessed !== 'undefined' + ? this.unnamedResourcesAccessed.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): SimulateTransactionResult { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SimulateTransactionResult: ${data}`); + } return new SimulateTransactionResult({ - txnResult: PendingTransactionResponse.from_obj_for_encoding( - data['txn-result'] + txnResult: PendingTransactionResponse.fromEncodingData( + data.get('txn-result') ?? new Map() ), - appBudgetConsumed: data['app-budget-consumed'], + appBudgetConsumed: data.get('app-budget-consumed'), execTrace: - typeof data['exec-trace'] !== 'undefined' - ? SimulationTransactionExecTrace.from_obj_for_encoding( - data['exec-trace'] + typeof data.get('exec-trace') !== 'undefined' + ? SimulationTransactionExecTrace.fromEncodingData( + data.get('exec-trace') ) : undefined, - fixedSigner: data['fixed-signer'], - logicSigBudgetConsumed: data['logic-sig-budget-consumed'], + fixedSigner: data.get('fixed-signer'), + logicSigBudgetConsumed: data.get('logic-sig-budget-consumed'), unnamedResourcesAccessed: - typeof data['unnamed-resources-accessed'] !== 'undefined' - ? SimulateUnnamedResourcesAccessed.from_obj_for_encoding( - data['unnamed-resources-accessed'] + typeof data.get('unnamed-resources-accessed') !== 'undefined' + ? SimulateUnnamedResourcesAccessed.fromEncodingData( + data.get('unnamed-resources-accessed') ) : undefined, }); - /* eslint-enable dot-notation */ } } @@ -4886,11 +6855,63 @@ export class SimulateTransactionResult extends BaseModel { * transaction of the group; otherwise, resources must be placed in the same * transaction which accessed them. */ -export class SimulateUnnamedResourcesAccessed extends BaseModel { +export class SimulateUnnamedResourcesAccessed implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'accounts', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + }, + { + key: 'app-locals', + valueSchema: new OptionalSchema( + new ArraySchema(ApplicationLocalReference.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'apps', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + omitEmpty: true, + }, + { + key: 'asset-holdings', + valueSchema: new OptionalSchema( + new ArraySchema(AssetHoldingReference.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'assets', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + omitEmpty: true, + }, + { + key: 'boxes', + valueSchema: new OptionalSchema( + new ArraySchema(BoxReference.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'extra-box-refs', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * The unnamed accounts that were referenced. The order of this array is arbitrary. */ - public accounts?: string[]; + public accounts?: Address[]; /** * The unnamed application local states that were referenced. The order of this @@ -4902,7 +6923,7 @@ export class SimulateUnnamedResourcesAccessed extends BaseModel { * The unnamed applications that were referenced. The order of this array is * arbitrary. */ - public apps?: (number | bigint)[]; + public apps?: bigint[]; /** * The unnamed asset holdings that were referenced. The order of this array is @@ -4913,7 +6934,7 @@ export class SimulateUnnamedResourcesAccessed extends BaseModel { /** * The unnamed assets that were referenced. The order of this array is arbitrary. */ - public assets?: (number | bigint)[]; + public assets?: bigint[]; /** * The unnamed boxes that were referenced. The order of this array is arbitrary. @@ -4925,7 +6946,7 @@ export class SimulateUnnamedResourcesAccessed extends BaseModel { * addition to the references defined in the input transaction group and any * referenced to unnamed boxes. */ - public extraBoxRefs?: number | bigint; + public extraBoxRefs?: number; /** * Creates a new `SimulateUnnamedResourcesAccessed` object. @@ -4951,7 +6972,7 @@ export class SimulateUnnamedResourcesAccessed extends BaseModel { boxes, extraBoxRefs, }: { - accounts?: string[]; + accounts?: (Address | string)[]; appLocals?: ApplicationLocalReference[]; apps?: (number | bigint)[]; assetHoldings?: AssetHoldingReference[]; @@ -4959,54 +6980,94 @@ export class SimulateUnnamedResourcesAccessed extends BaseModel { boxes?: BoxReference[]; extraBoxRefs?: number | bigint; }) { - super(); - this.accounts = accounts; + this.accounts = + typeof accounts !== 'undefined' + ? accounts.map((addr) => + typeof addr === 'string' ? Address.fromString(addr) : addr + ) + : undefined; this.appLocals = appLocals; - this.apps = apps; + this.apps = + typeof apps === 'undefined' ? undefined : apps.map(ensureBigInt); this.assetHoldings = assetHoldings; - this.assets = assets; + this.assets = + typeof assets === 'undefined' ? undefined : assets.map(ensureBigInt); this.boxes = boxes; - this.extraBoxRefs = extraBoxRefs; - - this.attribute_map = { - accounts: 'accounts', - appLocals: 'app-locals', - apps: 'apps', - assetHoldings: 'asset-holdings', - assets: 'assets', - boxes: 'boxes', - extraBoxRefs: 'extra-box-refs', - }; - } - - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): SimulateUnnamedResourcesAccessed { - /* eslint-disable dot-notation */ + this.extraBoxRefs = + typeof extraBoxRefs === 'undefined' + ? undefined + : ensureSafeInteger(extraBoxRefs); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SimulateUnnamedResourcesAccessed.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'accounts', + typeof this.accounts !== 'undefined' + ? this.accounts.map((v) => v.toString()) + : undefined, + ], + [ + 'app-locals', + typeof this.appLocals !== 'undefined' + ? this.appLocals.map((v) => v.toEncodingData()) + : undefined, + ], + ['apps', this.apps], + [ + 'asset-holdings', + typeof this.assetHoldings !== 'undefined' + ? this.assetHoldings.map((v) => v.toEncodingData()) + : undefined, + ], + ['assets', this.assets], + [ + 'boxes', + typeof this.boxes !== 'undefined' + ? this.boxes.map((v) => v.toEncodingData()) + : undefined, + ], + ['extra-box-refs', this.extraBoxRefs], + ]); + } + + static fromEncodingData(data: unknown): SimulateUnnamedResourcesAccessed { + if (!(data instanceof Map)) { + throw new Error( + `Invalid decoded SimulateUnnamedResourcesAccessed: ${data}` + ); + } return new SimulateUnnamedResourcesAccessed({ - accounts: data['accounts'], + accounts: data.get('accounts'), appLocals: - typeof data['app-locals'] !== 'undefined' - ? data['app-locals'].map( - ApplicationLocalReference.from_obj_for_encoding - ) + typeof data.get('app-locals') !== 'undefined' + ? data + .get('app-locals') + .map((v: unknown) => + ApplicationLocalReference.fromEncodingData(v) + ) : undefined, - apps: data['apps'], + apps: data.get('apps'), assetHoldings: - typeof data['asset-holdings'] !== 'undefined' - ? data['asset-holdings'].map( - AssetHoldingReference.from_obj_for_encoding - ) + typeof data.get('asset-holdings') !== 'undefined' + ? data + .get('asset-holdings') + .map((v: unknown) => AssetHoldingReference.fromEncodingData(v)) : undefined, - assets: data['assets'], + assets: data.get('assets'), boxes: - typeof data['boxes'] !== 'undefined' - ? data['boxes'].map(BoxReference.from_obj_for_encoding) + typeof data.get('boxes') !== 'undefined' + ? data + .get('boxes') + .map((v: unknown) => BoxReference.fromEncodingData(v)) : undefined, - extraBoxRefs: data['extra-box-refs'], + extraBoxRefs: data.get('extra-box-refs'), }); - /* eslint-enable dot-notation */ } } @@ -5015,7 +7076,48 @@ export class SimulateUnnamedResourcesAccessed extends BaseModel { * parameters is present, then evaluation parameters may differ from standard * evaluation in certain ways. */ -export class SimulationEvalOverrides extends BaseModel { +export class SimulationEvalOverrides implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'allow-empty-signatures', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'allow-unnamed-resources', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'extra-opcode-budget', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'fix-signers', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'max-log-calls', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'max-log-size', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * If true, transactions without signatures are allowed and simulated as if they * were properly signed. @@ -5030,7 +7132,7 @@ export class SimulationEvalOverrides extends BaseModel { /** * The extra opcode budget added to each transaction group during simulation */ - public extraOpcodeBudget?: number | bigint; + public extraOpcodeBudget?: number; /** * If true, signers for transactions that are missing signatures will be fixed @@ -5041,12 +7143,12 @@ export class SimulationEvalOverrides extends BaseModel { /** * The maximum log calls one can make during simulation */ - public maxLogCalls?: number | bigint; + public maxLogCalls?: number; /** * The maximum byte number to log during simulation */ - public maxLogSize?: number | bigint; + public maxLogSize?: number; /** * Creates a new `SimulationEvalOverrides` object. @@ -5074,49 +7176,105 @@ export class SimulationEvalOverrides extends BaseModel { maxLogCalls?: number | bigint; maxLogSize?: number | bigint; }) { - super(); this.allowEmptySignatures = allowEmptySignatures; this.allowUnnamedResources = allowUnnamedResources; - this.extraOpcodeBudget = extraOpcodeBudget; + this.extraOpcodeBudget = + typeof extraOpcodeBudget === 'undefined' + ? undefined + : ensureSafeInteger(extraOpcodeBudget); this.fixSigners = fixSigners; - this.maxLogCalls = maxLogCalls; - this.maxLogSize = maxLogSize; - - this.attribute_map = { - allowEmptySignatures: 'allow-empty-signatures', - allowUnnamedResources: 'allow-unnamed-resources', - extraOpcodeBudget: 'extra-opcode-budget', - fixSigners: 'fix-signers', - 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 */ + this.maxLogCalls = + typeof maxLogCalls === 'undefined' + ? undefined + : ensureSafeInteger(maxLogCalls); + this.maxLogSize = + typeof maxLogSize === 'undefined' + ? undefined + : ensureSafeInteger(maxLogSize); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SimulationEvalOverrides.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['allow-empty-signatures', this.allowEmptySignatures], + ['allow-unnamed-resources', this.allowUnnamedResources], + ['extra-opcode-budget', this.extraOpcodeBudget], + ['fix-signers', this.fixSigners], + ['max-log-calls', this.maxLogCalls], + ['max-log-size', this.maxLogSize], + ]); + } + + static fromEncodingData(data: unknown): SimulationEvalOverrides { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SimulationEvalOverrides: ${data}`); + } return new SimulationEvalOverrides({ - allowEmptySignatures: data['allow-empty-signatures'], - allowUnnamedResources: data['allow-unnamed-resources'], - extraOpcodeBudget: data['extra-opcode-budget'], - fixSigners: data['fix-signers'], - maxLogCalls: data['max-log-calls'], - maxLogSize: data['max-log-size'], + allowEmptySignatures: data.get('allow-empty-signatures'), + allowUnnamedResources: data.get('allow-unnamed-resources'), + extraOpcodeBudget: data.get('extra-opcode-budget'), + fixSigners: data.get('fix-signers'), + maxLogCalls: data.get('max-log-calls'), + maxLogSize: data.get('max-log-size'), }); - /* eslint-enable dot-notation */ } } /** * The set of trace information and effect from evaluating a single opcode. */ -export class SimulationOpcodeTraceUnit extends BaseModel { +export class SimulationOpcodeTraceUnit implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'pc', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'scratch-changes', + valueSchema: new OptionalSchema( + new ArraySchema(ScratchChange.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'spawned-inners', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + omitEmpty: true, + }, + { + key: 'stack-additions', + valueSchema: new OptionalSchema( + new ArraySchema(AvmValue.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'stack-pop-count', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'state-changes', + valueSchema: new OptionalSchema( + new ArraySchema(ApplicationStateOperation.encodingSchema) + ), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * The program counter of the current opcode being evaluated. */ - public pc: number | bigint; + public pc: number; /** * The writes into scratch slots. @@ -5126,7 +7284,7 @@ export class SimulationOpcodeTraceUnit extends BaseModel { /** * The indexes of the traces for inner transactions spawned by this opcode, if any. */ - public spawnedInners?: (number | bigint)[]; + public spawnedInners?: number[]; /** * The values added by this opcode to the stack. @@ -5136,7 +7294,7 @@ export class SimulationOpcodeTraceUnit extends BaseModel { /** * The number of deleted stack values by this opcode. */ - public stackPopCount?: number | bigint; + public stackPopCount?: number; /** * The operations against the current application's states. @@ -5167,51 +7325,80 @@ export class SimulationOpcodeTraceUnit extends BaseModel { stackPopCount?: number | bigint; stateChanges?: ApplicationStateOperation[]; }) { - super(); - this.pc = pc; + this.pc = ensureSafeInteger(pc); this.scratchChanges = scratchChanges; - this.spawnedInners = spawnedInners; + this.spawnedInners = + typeof spawnedInners === 'undefined' + ? undefined + : spawnedInners.map(ensureSafeInteger); this.stackAdditions = stackAdditions; - this.stackPopCount = stackPopCount; + this.stackPopCount = + typeof stackPopCount === 'undefined' + ? undefined + : ensureSafeInteger(stackPopCount); this.stateChanges = stateChanges; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SimulationOpcodeTraceUnit.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['pc', this.pc], + [ + 'scratch-changes', + typeof this.scratchChanges !== 'undefined' + ? this.scratchChanges.map((v) => v.toEncodingData()) + : undefined, + ], + ['spawned-inners', this.spawnedInners], + [ + 'stack-additions', + typeof this.stackAdditions !== 'undefined' + ? this.stackAdditions.map((v) => v.toEncodingData()) + : undefined, + ], + ['stack-pop-count', this.stackPopCount], + [ + 'state-changes', + typeof this.stateChanges !== 'undefined' + ? this.stateChanges.map((v) => v.toEncodingData()) + : undefined, + ], + ]); + } - this.attribute_map = { - pc: 'pc', - scratchChanges: 'scratch-changes', - spawnedInners: 'spawned-inners', - stackAdditions: 'stack-additions', - stackPopCount: 'stack-pop-count', - stateChanges: 'state-changes', - }; - } - - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): SimulationOpcodeTraceUnit { - /* eslint-disable dot-notation */ - if (typeof data['pc'] === 'undefined') - throw new Error(`Response is missing required field 'pc': ${data}`); + static fromEncodingData(data: unknown): SimulationOpcodeTraceUnit { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SimulationOpcodeTraceUnit: ${data}`); + } return new SimulationOpcodeTraceUnit({ - pc: data['pc'], + pc: data.get('pc'), scratchChanges: - typeof data['scratch-changes'] !== 'undefined' - ? data['scratch-changes'].map(ScratchChange.from_obj_for_encoding) + typeof data.get('scratch-changes') !== 'undefined' + ? data + .get('scratch-changes') + .map((v: unknown) => ScratchChange.fromEncodingData(v)) : undefined, - spawnedInners: data['spawned-inners'], + spawnedInners: data.get('spawned-inners'), stackAdditions: - typeof data['stack-additions'] !== 'undefined' - ? data['stack-additions'].map(AvmValue.from_obj_for_encoding) + typeof data.get('stack-additions') !== 'undefined' + ? data + .get('stack-additions') + .map((v: unknown) => AvmValue.fromEncodingData(v)) : undefined, - stackPopCount: data['stack-pop-count'], + stackPopCount: data.get('stack-pop-count'), stateChanges: - typeof data['state-changes'] !== 'undefined' - ? data['state-changes'].map( - ApplicationStateOperation.from_obj_for_encoding - ) + typeof data.get('state-changes') !== 'undefined' + ? data + .get('state-changes') + .map((v: unknown) => + ApplicationStateOperation.fromEncodingData(v) + ) : undefined, }); - /* eslint-enable dot-notation */ } } @@ -5219,7 +7406,71 @@ export class SimulationOpcodeTraceUnit extends BaseModel { * The execution trace of calling an app or a logic sig, containing the inner app * call trace in a recursive way. */ -export class SimulationTransactionExecTrace extends BaseModel { +export class SimulationTransactionExecTrace implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'approval-program-hash', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'approval-program-trace', + valueSchema: new OptionalSchema( + new ArraySchema(SimulationOpcodeTraceUnit.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'clear-state-program-hash', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'clear-state-program-trace', + valueSchema: new OptionalSchema( + new ArraySchema(SimulationOpcodeTraceUnit.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'clear-state-rollback', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'clear-state-rollback-error', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'inner-trace', + valueSchema: new OptionalSchema( + new ArraySchema(SimulationTransactionExecTrace.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'logic-sig-hash', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'logic-sig-trace', + valueSchema: new OptionalSchema( + new ArraySchema(SimulationOpcodeTraceUnit.encodingSchema) + ), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * SHA512_256 hash digest of the approval program executed in transaction. */ @@ -5306,15 +7557,14 @@ export class SimulationTransactionExecTrace extends BaseModel { logicSigHash?: string | Uint8Array; logicSigTrace?: SimulationOpcodeTraceUnit[]; }) { - super(); this.approvalProgramHash = typeof approvalProgramHash === 'string' - ? new Uint8Array(Buffer.from(approvalProgramHash, 'base64')) + ? base64ToBytes(approvalProgramHash) : approvalProgramHash; this.approvalProgramTrace = approvalProgramTrace; this.clearStateProgramHash = typeof clearStateProgramHash === 'string' - ? new Uint8Array(Buffer.from(clearStateProgramHash, 'base64')) + ? base64ToBytes(clearStateProgramHash) : clearStateProgramHash; this.clearStateProgramTrace = clearStateProgramTrace; this.clearStateRollback = clearStateRollback; @@ -5322,67 +7572,123 @@ export class SimulationTransactionExecTrace extends BaseModel { this.innerTrace = innerTrace; this.logicSigHash = typeof logicSigHash === 'string' - ? new Uint8Array(Buffer.from(logicSigHash, 'base64')) + ? base64ToBytes(logicSigHash) : logicSigHash; this.logicSigTrace = logicSigTrace; + } - this.attribute_map = { - approvalProgramHash: 'approval-program-hash', - approvalProgramTrace: 'approval-program-trace', - clearStateProgramHash: 'clear-state-program-hash', - clearStateProgramTrace: 'clear-state-program-trace', - clearStateRollback: 'clear-state-rollback', - clearStateRollbackError: 'clear-state-rollback-error', - innerTrace: 'inner-trace', - logicSigHash: 'logic-sig-hash', - logicSigTrace: 'logic-sig-trace', - }; - } - - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): SimulationTransactionExecTrace { - /* eslint-disable dot-notation */ + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SimulationTransactionExecTrace.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['approval-program-hash', this.approvalProgramHash], + [ + 'approval-program-trace', + typeof this.approvalProgramTrace !== 'undefined' + ? this.approvalProgramTrace.map((v) => v.toEncodingData()) + : undefined, + ], + ['clear-state-program-hash', this.clearStateProgramHash], + [ + 'clear-state-program-trace', + typeof this.clearStateProgramTrace !== 'undefined' + ? this.clearStateProgramTrace.map((v) => v.toEncodingData()) + : undefined, + ], + ['clear-state-rollback', this.clearStateRollback], + ['clear-state-rollback-error', this.clearStateRollbackError], + [ + 'inner-trace', + typeof this.innerTrace !== 'undefined' + ? this.innerTrace.map((v) => v.toEncodingData()) + : undefined, + ], + ['logic-sig-hash', this.logicSigHash], + [ + 'logic-sig-trace', + typeof this.logicSigTrace !== 'undefined' + ? this.logicSigTrace.map((v) => v.toEncodingData()) + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): SimulationTransactionExecTrace { + if (!(data instanceof Map)) { + throw new Error( + `Invalid decoded SimulationTransactionExecTrace: ${data}` + ); + } return new SimulationTransactionExecTrace({ - approvalProgramHash: data['approval-program-hash'], + approvalProgramHash: data.get('approval-program-hash'), approvalProgramTrace: - typeof data['approval-program-trace'] !== 'undefined' - ? data['approval-program-trace'].map( - SimulationOpcodeTraceUnit.from_obj_for_encoding - ) + typeof data.get('approval-program-trace') !== 'undefined' + ? data + .get('approval-program-trace') + .map((v: unknown) => + SimulationOpcodeTraceUnit.fromEncodingData(v) + ) : undefined, - clearStateProgramHash: data['clear-state-program-hash'], + clearStateProgramHash: data.get('clear-state-program-hash'), clearStateProgramTrace: - typeof data['clear-state-program-trace'] !== 'undefined' - ? data['clear-state-program-trace'].map( - SimulationOpcodeTraceUnit.from_obj_for_encoding - ) + typeof data.get('clear-state-program-trace') !== 'undefined' + ? data + .get('clear-state-program-trace') + .map((v: unknown) => + SimulationOpcodeTraceUnit.fromEncodingData(v) + ) : undefined, - clearStateRollback: data['clear-state-rollback'], - clearStateRollbackError: data['clear-state-rollback-error'], + clearStateRollback: data.get('clear-state-rollback'), + clearStateRollbackError: data.get('clear-state-rollback-error'), innerTrace: - typeof data['inner-trace'] !== 'undefined' - ? data['inner-trace'].map( - SimulationTransactionExecTrace.from_obj_for_encoding - ) + typeof data.get('inner-trace') !== 'undefined' + ? data + .get('inner-trace') + .map((v: unknown) => + SimulationTransactionExecTrace.fromEncodingData(v) + ) : undefined, - logicSigHash: data['logic-sig-hash'], + logicSigHash: data.get('logic-sig-hash'), logicSigTrace: - typeof data['logic-sig-trace'] !== 'undefined' - ? data['logic-sig-trace'].map( - SimulationOpcodeTraceUnit.from_obj_for_encoding - ) + typeof data.get('logic-sig-trace') !== 'undefined' + ? data + .get('logic-sig-trace') + .map((v: unknown) => + SimulationOpcodeTraceUnit.fromEncodingData(v) + ) : undefined, }); - /* eslint-enable dot-notation */ } } /** * Represents a state proof and its corresponding message */ -export class StateProof extends BaseModel { +export class StateProof implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'Message', + valueSchema: StateProofMessage.encodingSchema, + omitEmpty: true, + }, + { + key: 'StateProof', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Represents the message that the state proofs are attesting to. */ @@ -5405,40 +7711,76 @@ export class StateProof extends BaseModel { 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}` - ); + typeof stateproof === 'string' ? base64ToBytes(stateproof) : stateproof; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProof.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['Message', this.message.toEncodingData()], + ['StateProof', this.stateproof], + ]); + } + + static fromEncodingData(data: unknown): StateProof { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProof: ${data}`); + } return new StateProof({ - message: StateProofMessage.from_obj_for_encoding(data['Message']), - stateproof: data['StateProof'], + message: StateProofMessage.fromEncodingData( + data.get('Message') ?? new Map() + ), + stateproof: data.get('StateProof'), }); - /* eslint-enable dot-notation */ } } /** * Represents the message that the state proofs are attesting to. */ -export class StateProofMessage extends BaseModel { +export class StateProofMessage implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'BlockHeadersCommitment', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'FirstAttestedRound', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'LastAttestedRound', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'LnProvenWeight', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'VotersCommitment', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * The vector commitment root on all light block headers within a state proof * interval. @@ -5448,18 +7790,18 @@ export class StateProofMessage extends BaseModel { /** * The first round the message attests to. */ - public firstattestedround: number | bigint; + public firstattestedround: bigint; /** * The last round the message attests to. */ - public lastattestedround: number | bigint; + public lastattestedround: 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; + public lnprovenweight: bigint; /** * The vector commitment root of the top N accounts to sign the next StateProof. @@ -5489,80 +7831,88 @@ export class StateProofMessage extends BaseModel { lnprovenweight: number | bigint; voterscommitment: string | Uint8Array; }) { - super(); this.blockheaderscommitment = typeof blockheaderscommitment === 'string' - ? new Uint8Array(Buffer.from(blockheaderscommitment, 'base64')) + ? base64ToBytes(blockheaderscommitment) : blockheaderscommitment; - this.firstattestedround = firstattestedround; - this.lastattestedround = lastattestedround; - this.lnprovenweight = lnprovenweight; + this.firstattestedround = ensureBigInt(firstattestedround); + this.lastattestedround = ensureBigInt(lastattestedround); + this.lnprovenweight = ensureBigInt(lnprovenweight); this.voterscommitment = typeof voterscommitment === 'string' - ? new Uint8Array(Buffer.from(voterscommitment, 'base64')) + ? base64ToBytes(voterscommitment) : voterscommitment; + } - this.attribute_map = { - blockheaderscommitment: 'BlockHeadersCommitment', - firstattestedround: 'FirstAttestedRound', - lastattestedround: 'LastAttestedRound', - lnprovenweight: 'LnProvenWeight', - voterscommitment: 'VotersCommitment', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofMessage.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['BlockHeadersCommitment', this.blockheaderscommitment], + ['FirstAttestedRound', this.firstattestedround], + ['LastAttestedRound', this.lastattestedround], + ['LnProvenWeight', this.lnprovenweight], + ['VotersCommitment', this.voterscommitment], + ]); + } + + static fromEncodingData(data: unknown): StateProofMessage { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofMessage: ${data}`); + } return new StateProofMessage({ - blockheaderscommitment: data['BlockHeadersCommitment'], - firstattestedround: data['FirstAttestedRound'], - lastattestedround: data['LastAttestedRound'], - lnprovenweight: data['LnProvenWeight'], - voterscommitment: data['VotersCommitment'], + blockheaderscommitment: data.get('BlockHeadersCommitment'), + firstattestedround: data.get('FirstAttestedRound'), + lastattestedround: data.get('LastAttestedRound'), + lnprovenweight: data.get('LnProvenWeight'), + voterscommitment: data.get('VotersCommitment'), }); - /* eslint-enable dot-notation */ } } /** * Supply represents the current supply of MicroAlgos in the system. */ -export class SupplyResponse extends BaseModel { +export class SupplyResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'current_round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'online-money', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'total-money', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * Round */ - public currentRound: number | bigint; + public currentRound: bigint; /** * OnlineMoney */ - public onlineMoney: number | bigint; + public onlineMoney: bigint; /** * TotalMoney */ - public totalMoney: number | bigint; + public totalMoney: bigint; /** * Creates a new `SupplyResponse` object. @@ -5579,47 +7929,54 @@ export class SupplyResponse extends BaseModel { 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}` - ); + this.currentRound = ensureBigInt(currentRound); + this.onlineMoney = ensureBigInt(onlineMoney); + this.totalMoney = ensureBigInt(totalMoney); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return SupplyResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['current_round', this.currentRound], + ['online-money', this.onlineMoney], + ['total-money', this.totalMoney], + ]); + } + + static fromEncodingData(data: unknown): SupplyResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SupplyResponse: ${data}`); + } return new SupplyResponse({ - currentRound: data['current_round'], - onlineMoney: data['online-money'], - totalMoney: data['total-money'], + currentRound: data.get('current_round'), + onlineMoney: data.get('online-money'), + totalMoney: data.get('total-money'), }); - /* eslint-enable dot-notation */ } } /** * Represents a key-value pair in an application store. */ -export class TealKeyValue extends BaseModel { - public key: string; +export class TealKeyValue implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'key', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'value', valueSchema: TealValue.encodingSchema, omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + public key: Uint8Array; /** * Represents a TEAL value. @@ -5631,93 +7988,109 @@ export class TealKeyValue extends BaseModel { * @param key - * @param value - Represents a TEAL value. */ - constructor({ key, value }: { key: string; value: TealValue }) { - super(); - this.key = key; + constructor({ key, value }: { key: string | Uint8Array; value: TealValue }) { + this.key = typeof key === 'string' ? base64ToBytes(key) : key; this.value = value; + } - this.attribute_map = { - key: 'key', - value: 'value', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TealKeyValue.encodingSchema; } - // 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}`); + toEncodingData(): Map { + return new Map([ + ['key', this.key], + ['value', this.value.toEncodingData()], + ]); + } + + static fromEncodingData(data: unknown): TealKeyValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TealKeyValue: ${data}`); + } return new TealKeyValue({ - key: data['key'], - value: TealValue.from_obj_for_encoding(data['value']), + key: data.get('key'), + value: TealValue.fromEncodingData(data.get('value') ?? new Map()), }); - /* eslint-enable dot-notation */ } } /** * Represents a TEAL value. */ -export class TealValue extends BaseModel { +export class TealValue implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'bytes', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'type', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'uint', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** - * (tt) value type. Value `1` refers to **bytes**, value `2` refers to **uint** + * (tb) bytes value. */ - public type: number | bigint; + public bytes: Uint8Array; /** - * (tb) bytes value. + * (tt) value type. Value `1` refers to **bytes**, value `2` refers to **uint** */ - public bytes: string; + public type: number; /** * (ui) uint value. */ - public uint: number | bigint; + public uint: 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 type - (tt) value type. Value `1` refers to **bytes**, value `2` refers to **uint** * @param uint - (ui) uint value. */ constructor({ - type, bytes, + type, uint, }: { + bytes: string | Uint8Array; 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}`); + this.bytes = typeof bytes === 'string' ? base64ToBytes(bytes) : bytes; + this.type = ensureSafeInteger(type); + this.uint = ensureBigInt(uint); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TealValue.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['bytes', this.bytes], + ['type', this.type], + ['uint', this.uint], + ]); + } + + static fromEncodingData(data: unknown): TealValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TealValue: ${data}`); + } return new TealValue({ - type: data['type'], - bytes: data['bytes'], - uint: data['uint'], + bytes: data.get('bytes'), + type: data.get('type'), + uint: data.get('uint'), }); - /* eslint-enable dot-notation */ } } @@ -5725,7 +8098,25 @@ export class TealValue extends BaseModel { * Response containing all ledger state deltas for transaction groups, with their * associated Ids, in a single round. */ -export class TransactionGroupLedgerStateDeltasForRoundResponse extends BaseModel { +export class TransactionGroupLedgerStateDeltasForRoundResponse + implements Encodable +{ + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'Deltas', + valueSchema: new ArraySchema( + LedgerStateDeltaForTransactionGroup.encodingSchema + ), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + public deltas: LedgerStateDeltaForTransactionGroup[]; /** @@ -5733,29 +8124,33 @@ export class TransactionGroupLedgerStateDeltasForRoundResponse extends BaseModel * @param deltas - */ constructor({ deltas }: { deltas: LedgerStateDeltaForTransactionGroup[] }) { - super(); this.deltas = deltas; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionGroupLedgerStateDeltasForRoundResponse.encodingSchema; + } - this.attribute_map = { - deltas: 'Deltas', - }; + toEncodingData(): Map { + return new Map([ + ['Deltas', this.deltas.map((v) => v.toEncodingData())], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record + static fromEncodingData( + data: unknown ): TransactionGroupLedgerStateDeltasForRoundResponse { - /* eslint-disable dot-notation */ - if (!Array.isArray(data['Deltas'])) + if (!(data instanceof Map)) { throw new Error( - `Response is missing required array field 'Deltas': ${data}` + `Invalid decoded TransactionGroupLedgerStateDeltasForRoundResponse: ${data}` ); + } return new TransactionGroupLedgerStateDeltasForRoundResponse({ - deltas: data['Deltas'].map( - LedgerStateDeltaForTransactionGroup.from_obj_for_encoding + deltas: (data.get('Deltas') ?? []).map((v: unknown) => + LedgerStateDeltaForTransactionGroup.fromEncodingData(v) ), }); - /* eslint-enable dot-notation */ } } @@ -5763,7 +8158,32 @@ export class TransactionGroupLedgerStateDeltasForRoundResponse extends BaseModel * TransactionParams contains the parameters that help a client construct a new * transaction. */ -export class TransactionParametersResponse extends BaseModel { +export class TransactionParametersResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'consensus-version', + valueSchema: new StringSchema(), + omitEmpty: true, + }, + { key: 'fee', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'genesis-hash', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { key: 'genesis-id', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'last-round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'min-fee', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * ConsensusVersion indicates the consensus protocol version * as of LastRound. @@ -5776,7 +8196,7 @@ export class TransactionParametersResponse extends BaseModel { * 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; + public fee: bigint; /** * GenesisHash is the hash of the genesis block. @@ -5791,13 +8211,13 @@ export class TransactionParametersResponse extends BaseModel { /** * LastRound indicates the last round seen */ - public lastRound: number | bigint; + public lastRound: bigint; /** * The minimum transaction fee (not per byte) required for the * txn to validate for the current network protocol. */ - public minFee: number | bigint; + public minFee: bigint; /** * Creates a new `TransactionParametersResponse` object. @@ -5828,72 +8248,80 @@ export class TransactionParametersResponse extends BaseModel { lastRound: number | bigint; minFee: number | bigint; }) { - super(); this.consensusVersion = consensusVersion; - this.fee = fee; + this.fee = ensureBigInt(fee); this.genesisHash = typeof genesisHash === 'string' - ? new Uint8Array(Buffer.from(genesisHash, 'base64')) + ? base64ToBytes(genesisHash) : 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}`); + this.lastRound = ensureBigInt(lastRound); + this.minFee = ensureBigInt(minFee); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionParametersResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['consensus-version', this.consensusVersion], + ['fee', this.fee], + ['genesis-hash', this.genesisHash], + ['genesis-id', this.genesisId], + ['last-round', this.lastRound], + ['min-fee', this.minFee], + ]); + } + + static fromEncodingData(data: unknown): TransactionParametersResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionParametersResponse: ${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'], + consensusVersion: data.get('consensus-version'), + fee: data.get('fee'), + genesisHash: data.get('genesis-hash'), + genesisId: data.get('genesis-id'), + lastRound: data.get('last-round'), + minFee: data.get('min-fee'), }); - /* eslint-enable dot-notation */ } } /** * Proof of transaction in a block. */ -export class TransactionProofResponse extends BaseModel { +export class TransactionProofResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'idx', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'proof', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { + key: 'stibhash', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { key: 'treedepth', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'hashtype', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Index of the transaction in the block's payset. */ - public idx: number | bigint; + public idx: number; /** * Proof of transaction membership. @@ -5909,7 +8337,7 @@ export class TransactionProofResponse extends BaseModel { * 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; + public treedepth: number; /** * The type of hash function used to create the proof, must be one of: @@ -5942,58 +8370,74 @@ export class TransactionProofResponse extends BaseModel { treedepth: number | bigint; hashtype?: string; }) { - super(); - this.idx = idx; - this.proof = - typeof proof === 'string' - ? new Uint8Array(Buffer.from(proof, 'base64')) - : proof; + this.idx = ensureSafeInteger(idx); + this.proof = typeof proof === 'string' ? base64ToBytes(proof) : proof; this.stibhash = - typeof stibhash === 'string' - ? new Uint8Array(Buffer.from(stibhash, 'base64')) - : stibhash; - this.treedepth = treedepth; + typeof stibhash === 'string' ? base64ToBytes(stibhash) : stibhash; + this.treedepth = ensureSafeInteger(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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionProofResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['idx', this.idx], + ['proof', this.proof], + ['stibhash', this.stibhash], + ['treedepth', this.treedepth], + ['hashtype', this.hashtype], + ]); + } + + static fromEncodingData(data: unknown): TransactionProofResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionProofResponse: ${data}`); + } return new TransactionProofResponse({ - idx: data['idx'], - proof: data['proof'], - stibhash: data['stibhash'], - treedepth: data['treedepth'], - hashtype: data['hashtype'], + idx: data.get('idx'), + proof: data.get('proof'), + stibhash: data.get('stibhash'), + treedepth: data.get('treedepth'), + hashtype: data.get('hashtype'), }); - /* eslint-enable dot-notation */ } } /** * algod version information. */ -export class Version extends BaseModel { +export class Version implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'build', + valueSchema: BuildVersion.encodingSchema, + omitEmpty: true, + }, + { + key: 'genesis_hash_b64', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { key: 'genesis_id', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'versions', + valueSchema: new ArraySchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public build: BuildVersion; public genesisHashB64: Uint8Array; @@ -6020,46 +8464,38 @@ export class Version extends BaseModel { genesisId: string; versions: string[]; }) { - super(); this.build = build; this.genesisHashB64 = typeof genesisHashB64 === 'string' - ? new Uint8Array(Buffer.from(genesisHashB64, 'base64')) + ? base64ToBytes(genesisHashB64) : 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 class-methods-use-this + getEncodingSchema(): Schema { + return Version.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['build', this.build.toEncodingData()], + ['genesis_hash_b64', this.genesisHashB64], + ['genesis_id', this.genesisId], + ['versions', this.versions], + ]); + } + + static fromEncodingData(data: unknown): Version { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Version: ${data}`); + } return new Version({ - build: BuildVersion.from_obj_for_encoding(data['build']), - genesisHashB64: data['genesis_hash_b64'], - genesisId: data['genesis_id'], - versions: data['versions'], + build: BuildVersion.fromEncodingData(data.get('build') ?? new Map()), + genesisHashB64: data.get('genesis_hash_b64'), + genesisId: data.get('genesis_id'), + versions: data.get('versions'), }); - /* eslint-enable dot-notation */ } } diff --git a/src/client/v2/algod/pendingTransactionInformation.ts b/src/client/v2/algod/pendingTransactionInformation.ts index 75d2527ee..8fcb3a231 100644 --- a/src/client/v2/algod/pendingTransactionInformation.ts +++ b/src/client/v2/algod/pendingTransactionInformation.ts @@ -1,23 +1,23 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import * as encoding from '../../../encoding/encoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeMsgpack } from '../../../encoding/encoding.js'; +import { PendingTransactionResponse } from './models/types.js'; /** * returns the transaction information for a specific txid of a pending transaction */ -export default class PendingTransactionInformation extends JSONRequest { - constructor(c: HTTPClient, private txid: string) { +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; + prepare(response: HTTPClientResponse): PendingTransactionResponse { + return decodeMsgpack(response.body, PendingTransactionResponse); } path() { diff --git a/src/client/v2/algod/pendingTransactions.ts b/src/client/v2/algod/pendingTransactions.ts index bf8754812..347674a67 100644 --- a/src/client/v2/algod/pendingTransactions.ts +++ b/src/client/v2/algod/pendingTransactions.ts @@ -1,11 +1,12 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import * as encoding from '../../../encoding/encoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeMsgpack } from '../../../encoding/encoding.js'; +import { PendingTransactionsResponse } from './models/types.js'; /** * pendingTransactionsInformation returns transactions that are pending in the pool */ -export default class PendingTransactions extends JSONRequest { +export default class PendingTransactions extends JSONRequest { constructor(c: HTTPClient) { super(c); this.query.format = 'msgpack'; @@ -16,11 +17,8 @@ export default class PendingTransactions extends JSONRequest { return '/v2/transactions/pending'; } - prepare(body: Uint8Array) { - if (body && body.byteLength > 0) { - return encoding.decode(body) as Record; - } - return undefined; + prepare(response: HTTPClientResponse): PendingTransactionsResponse { + return decodeMsgpack(response.body, PendingTransactionsResponse); } /* eslint-enable class-methods-use-this */ diff --git a/src/client/v2/algod/pendingTransactionsByAddress.ts b/src/client/v2/algod/pendingTransactionsByAddress.ts index b9710ccb7..7dbdd01eb 100644 --- a/src/client/v2/algod/pendingTransactionsByAddress.ts +++ b/src/client/v2/algod/pendingTransactionsByAddress.ts @@ -1,23 +1,24 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import * as encoding from '../../../encoding/encoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeMsgpack } from '../../../encoding/encoding.js'; +import { PendingTransactionsResponse } from './models/types.js'; +import { Address } from '../../../encoding/address.js'; /** * 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) { +export default class PendingTransactionsByAddress extends JSONRequest { + private address: string; + + constructor(c: HTTPClient, address: string | Address) { super(c); - this.address = address; + this.address = address.toString(); 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; + prepare(response: HTTPClientResponse): PendingTransactionsResponse { + return decodeMsgpack(response.body, PendingTransactionsResponse); } path() { diff --git a/src/client/v2/algod/ready.ts b/src/client/v2/algod/ready.ts index 32c032952..466f6c863 100644 --- a/src/client/v2/algod/ready.ts +++ b/src/client/v2/algod/ready.ts @@ -1,8 +1,12 @@ -import JSONRequest from '../jsonrequest'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; -export default class Ready extends JSONRequest { +export default class Ready extends JSONRequest { // eslint-disable-next-line class-methods-use-this path() { return `/ready`; } + + // eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars + prepare(_response: HTTPClientResponse): void {} } diff --git a/src/client/v2/algod/sendRawTransaction.ts b/src/client/v2/algod/sendRawTransaction.ts index 8977c1c5f..bdc99775f 100644 --- a/src/client/v2/algod/sendRawTransaction.ts +++ b/src/client/v2/algod/sendRawTransaction.ts @@ -1,14 +1,17 @@ -import { Buffer } from 'buffer'; -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import { concatArrays } from '../../../utils/utils'; +import { concatArrays } from '../../../utils/utils.js'; +import { PostTransactionsResponse } from './models/types.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import JSONRequest from '../jsonrequest.js'; /** * Sets the default header (if not previously set) for sending a raw * transaction. * @param headers - A headers object */ -export function setSendTransactionHeaders(headers = {}) { +export function setSendTransactionHeaders( + headers: Record = {} +) { let hdrs = headers; if (Object.keys(hdrs).every((key) => key.toLowerCase() !== 'content-type')) { hdrs = { ...headers }; @@ -24,7 +27,7 @@ function isByteArray(array: any): array is Uint8Array { /** * broadcasts the passed signed txns to the network */ -export default class SendRawTransaction extends JSONRequest { +export default class SendRawTransaction extends JSONRequest { private txnBytesToPost: Uint8Array; constructor(c: HTTPClient, stxOrStxs: Uint8Array | Uint8Array[]) { @@ -48,13 +51,21 @@ export default class SendRawTransaction extends JSONRequest { return '/v2/transactions'; } - async do(headers = {}) { + protected executeRequest( + headers?: Record, + customOptions?: Record + ): Promise { const txHeaders = setSendTransactionHeaders(headers); - const res = await this.c.post( - this.path(), - Buffer.from(this.txnBytesToPost), - txHeaders - ); - return res.body; + return this.c.post({ + relativePath: this.path(), + data: this.txnBytesToPost, + requestHeaders: txHeaders, + customOptions, + }); + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): PostTransactionsResponse { + return decodeJSON(response.getJSONText(), PostTransactionsResponse); } } diff --git a/src/client/v2/algod/setBlockOffsetTimestamp.ts b/src/client/v2/algod/setBlockOffsetTimestamp.ts index 840228132..0acd09330 100644 --- a/src/client/v2/algod/setBlockOffsetTimestamp.ts +++ b/src/client/v2/algod/setBlockOffsetTimestamp.ts @@ -1,20 +1,30 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; -export default class SetBlockOffsetTimestamp extends JSONRequest { - constructor(c: HTTPClient, intDecoding: IntDecoding, private offset: number) { - super(c, intDecoding); - - this.offset = offset; +export default class SetBlockOffsetTimestamp extends JSONRequest { + constructor( + c: HTTPClient, + private offset: number + ) { + super(c); } path() { return `/v2/devmode/blocks/offset/${this.offset}`; } - async do(headers = {}) { - const res = await this.c.post(this.path(), headers); - return res.body; + protected executeRequest( + headers?: Record, + customOptions?: Record + ): Promise { + return this.c.post({ + relativePath: this.path(), + data: null, + requestHeaders: headers, + customOptions, + }); } + + // eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars + prepare(_response: HTTPClientResponse): void {} } diff --git a/src/client/v2/algod/setSyncRound.ts b/src/client/v2/algod/setSyncRound.ts index fdc32c75c..b150681d9 100644 --- a/src/client/v2/algod/setSyncRound.ts +++ b/src/client/v2/algod/setSyncRound.ts @@ -1,20 +1,30 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; -export default class SetSyncRound extends JSONRequest { - constructor(c: HTTPClient, intDecoding: IntDecoding, private round: number) { - super(c, intDecoding); - - this.round = round; +export default class SetSyncRound extends JSONRequest { + constructor( + c: HTTPClient, + private round: number + ) { + super(c); } path() { return `/v2/ledger/sync/${this.round}`; } - async do(headers = {}) { - const res = await this.c.post(this.path(), headers); - return res.body; + protected executeRequest( + headers?: Record, + customOptions?: Record + ): Promise { + return this.c.post({ + relativePath: this.path(), + data: null, + requestHeaders: headers, + customOptions, + }); } + + // eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars + prepare(_response: HTTPClientResponse): void {} } diff --git a/src/client/v2/algod/simulateTransaction.ts b/src/client/v2/algod/simulateTransaction.ts index c6dad54f5..e829b9942 100644 --- a/src/client/v2/algod/simulateTransaction.ts +++ b/src/client/v2/algod/simulateTransaction.ts @@ -1,15 +1,16 @@ -import { Buffer } from 'buffer'; -import * as encoding from '../../../encoding/encoding'; -import HTTPClient from '../../client'; -import JSONRequest from '../jsonrequest'; -import { SimulateRequest, SimulateResponse } from './models/types'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { encodeMsgpack, decodeMsgpack } from '../../../encoding/encoding.js'; +import JSONRequest from '../jsonrequest.js'; +import { SimulateRequest, SimulateResponse } from './models/types.js'; /** * Sets the default header (if not previously set) for simulating a raw * transaction. * @param headers - A headers object */ -export function setSimulateTransactionsHeaders(headers = {}) { +export function setSimulateTransactionsHeaders( + headers: Record = {} +) { let hdrs = headers; if (Object.keys(hdrs).every((key) => key.toLowerCase() !== 'content-type')) { hdrs = { ...headers }; @@ -21,16 +22,13 @@ export function setSimulateTransactionsHeaders(headers = {}) { /** * Simulates signed txns. */ -export default class SimulateRawTransactions extends JSONRequest< - SimulateResponse, - Uint8Array -> { +export default class SimulateRawTransactions extends JSONRequest { private requestBytes: Uint8Array; constructor(c: HTTPClient, request: SimulateRequest) { super(c); this.query.format = 'msgpack'; - this.requestBytes = encoding.rawEncode(request.get_obj_for_encoding(true)); + this.requestBytes = encodeMsgpack(request); } // eslint-disable-next-line class-methods-use-this @@ -38,21 +36,22 @@ export default class SimulateRawTransactions extends JSONRequest< return '/v2/transactions/simulate'; } - async do(headers = {}) { + protected executeRequest( + headers?: Record, + customOptions?: Record + ): Promise { 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); + return this.c.post({ + relativePath: this.path(), + data: this.requestBytes, + query: this.query, + requestHeaders: txHeaders, + customOptions, + }); } // eslint-disable-next-line class-methods-use-this - prepare(body: Uint8Array): SimulateResponse { - const decoded = encoding.decode(body); - return SimulateResponse.from_obj_for_encoding(decoded); + prepare(response: HTTPClientResponse): SimulateResponse { + return decodeMsgpack(response.body, SimulateResponse); } } diff --git a/src/client/v2/algod/stateproof.ts b/src/client/v2/algod/stateproof.ts index 98bab12a7..c61f692e9 100644 --- a/src/client/v2/algod/stateproof.ts +++ b/src/client/v2/algod/stateproof.ts @@ -1,15 +1,22 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { StateProof as SP } from './models/types.js'; -export default class StateProof extends JSONRequest { - constructor(c: HTTPClient, intDecoding: IntDecoding, private round: number) { - super(c, intDecoding); - - this.round = round; +export default class StateProof extends JSONRequest { + constructor( + c: HTTPClient, + private round: number + ) { + super(c); } path() { return `/v2/stateproofs/${this.round}`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): SP { + return decodeJSON(response.getJSONText(), SP); + } } diff --git a/src/client/v2/algod/status.ts b/src/client/v2/algod/status.ts index c8dcd4524..a3d48f4a2 100644 --- a/src/client/v2/algod/status.ts +++ b/src/client/v2/algod/status.ts @@ -1,8 +1,16 @@ -import JSONRequest from '../jsonrequest'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { NodeStatusResponse } from './models/types.js'; -export default class Status extends JSONRequest { +export default class Status extends JSONRequest { // eslint-disable-next-line class-methods-use-this path() { return '/v2/status'; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): NodeStatusResponse { + return decodeJSON(response.getJSONText(), NodeStatusResponse); + } } diff --git a/src/client/v2/algod/statusAfterBlock.ts b/src/client/v2/algod/statusAfterBlock.ts index 5d340c6fd..43cba1069 100644 --- a/src/client/v2/algod/statusAfterBlock.ts +++ b/src/client/v2/algod/statusAfterBlock.ts @@ -1,15 +1,23 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { NodeStatusResponse } from './models/types.js'; -export default class StatusAfterBlock extends JSONRequest { - constructor(c: HTTPClient, intDecoding: IntDecoding, private round: number) { - super(c, intDecoding); +export default class StatusAfterBlock extends JSONRequest { + constructor( + c: HTTPClient, + private round: number + ) { + super(c); if (!Number.isInteger(round)) throw Error('round should be an integer'); - this.round = round; } path() { return `/v2/status/wait-for-block-after/${this.round}`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): NodeStatusResponse { + return decodeJSON(response.getJSONText(), NodeStatusResponse); + } } diff --git a/src/client/v2/algod/suggestedParams.ts b/src/client/v2/algod/suggestedParams.ts index 86421a27f..df5c393ef 100644 --- a/src/client/v2/algod/suggestedParams.ts +++ b/src/client/v2/algod/suggestedParams.ts @@ -1,24 +1,54 @@ -import JSONRequest from '../jsonrequest'; -import { SuggestedParamsWithMinFee } from '../../../types/transactions/base'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { TransactionParametersResponse } from './models/types.js'; +import { SuggestedParams } from '../../../types/transactions/base.js'; + +/** + * SuggestedParamsFromAlgod contains the suggested parameters for a new transaction, as returned by + * the algod REST API. + * + * This exists because the SuggestedParams interface is purposefully general (e.g. fee can be a + * number or a bigint), and compared to that the algod API returns a narrower type. + */ +export interface SuggestedParamsFromAlgod extends SuggestedParams { + flatFee: boolean; + fee: bigint; + minFee: bigint; + firstValid: bigint; + lastValid: bigint; + genesisID: string; + genesisHash: Uint8Array; + + /** + * ConsensusVersion indicates the consensus protocol version as of the last round. + */ + consensusVersion: string; +} /** * Returns the common needed parameters for a new transaction, in a format the transaction builder expects */ -export default class SuggestedParamsRequest extends JSONRequest { +export default class SuggestedParamsRequest extends JSONRequest { /* eslint-disable class-methods-use-this */ path() { return '/v2/transactions/params'; } - prepare(body: Record): SuggestedParamsWithMinFee { + prepare(response: HTTPClientResponse): SuggestedParamsFromAlgod { + const params = decodeJSON( + response.getJSONText(), + TransactionParametersResponse + ); 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'], + fee: params.fee, + firstValid: params.lastRound, + lastValid: params.lastRound + BigInt(1000), + genesisID: params.genesisId, + genesisHash: params.genesisHash, + minFee: params.minFee, + consensusVersion: params.consensusVersion, }; } /* eslint-enable class-methods-use-this */ diff --git a/src/client/v2/algod/supply.ts b/src/client/v2/algod/supply.ts index 0e30f56e2..523eb3cb9 100644 --- a/src/client/v2/algod/supply.ts +++ b/src/client/v2/algod/supply.ts @@ -1,8 +1,16 @@ -import JSONRequest from '../jsonrequest'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { SupplyResponse } from './models/types.js'; -export default class Supply extends JSONRequest { +export default class Supply extends JSONRequest { // eslint-disable-next-line class-methods-use-this path() { return '/v2/ledger/supply'; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): SupplyResponse { + return decodeJSON(response.getJSONText(), SupplyResponse); + } } diff --git a/src/client/v2/algod/unsetSyncRound.ts b/src/client/v2/algod/unsetSyncRound.ts index 746dbe57e..d03478299 100644 --- a/src/client/v2/algod/unsetSyncRound.ts +++ b/src/client/v2/algod/unsetSyncRound.ts @@ -1,13 +1,24 @@ -import JSONRequest from '../jsonrequest'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; -export default class UnsetSyncRound extends 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; + protected executeRequest( + headers?: Record, + customOptions?: Record + ): Promise { + return this.c.delete({ + relativePath: this.path(), + data: undefined, + requestHeaders: headers, + customOptions, + }); } + + // eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars + prepare(_response: HTTPClientResponse): void {} } diff --git a/src/client/v2/algod/versions.ts b/src/client/v2/algod/versions.ts index 31bcabb16..82753d144 100644 --- a/src/client/v2/algod/versions.ts +++ b/src/client/v2/algod/versions.ts @@ -1,11 +1,19 @@ -import JSONRequest from '../jsonrequest'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Version } from './models/types.js'; /** * retrieves the VersionResponse from the running node */ -export default class Versions extends JSONRequest { +export default class Versions extends JSONRequest { // eslint-disable-next-line class-methods-use-this path() { return '/versions'; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): Version { + return decodeJSON(response.getJSONText(), Version); + } } diff --git a/src/client/v2/basemodel.ts b/src/client/v2/basemodel.ts deleted file mode 100644 index 39fa52e57..000000000 --- a/src/client/v2/basemodel.ts +++ /dev/null @@ -1,81 +0,0 @@ -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/index.ts b/src/client/v2/indexer/index.ts new file mode 100644 index 000000000..e2bc6ce23 --- /dev/null +++ b/src/client/v2/indexer/index.ts @@ -0,0 +1,3 @@ +// Indexer +export { IndexerClient } from './indexer.js'; +export * from './models/types.js'; diff --git a/src/client/v2/indexer/indexer.ts b/src/client/v2/indexer/indexer.ts index b767343d6..f88235fd1 100644 --- a/src/client/v2/indexer/indexer.ts +++ b/src/client/v2/indexer/indexer.ts @@ -1,29 +1,30 @@ -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 ServiceClient from '../serviceClient.js'; +import MakeHealthCheck from './makeHealthCheck.js'; +import LookupAssetBalances from './lookupAssetBalances.js'; +import LookupAssetTransactions from './lookupAssetTransactions.js'; +import LookupAccountTransactions from './lookupAccountTransactions.js'; +import LookupBlock from './lookupBlock.js'; +import LookupTransactionByID from './lookupTransactionByID.js'; +import LookupAccountByID from './lookupAccountByID.js'; +import LookupAccountAssets from './lookupAccountAssets.js'; +import LookupAccountCreatedAssets from './lookupAccountCreatedAssets.js'; +import LookupAccountAppLocalStates from './lookupAccountAppLocalStates.js'; +import LookupAccountCreatedApplications from './lookupAccountCreatedApplications.js'; +import LookupAssetByID from './lookupAssetByID.js'; +import LookupApplications from './lookupApplications.js'; +import LookupApplicationLogs from './lookupApplicationLogs.js'; +import LookupApplicationBoxByIDandName from './lookupApplicationBoxByIDandName.js'; +import SearchAccounts from './searchAccounts.js'; +import SearchForTransactions from './searchForTransactions.js'; +import SearchForAssets from './searchForAssets.js'; +import SearchForApplications from './searchForApplications.js'; +import SearchForApplicationBoxes from './searchForApplicationBoxes.js'; +import { BaseHTTPClient } from '../../baseHTTPClient.js'; import { CustomTokenHeader, IndexerTokenHeader, -} from '../../urlTokenBaseHTTPClient'; +} from '../../urlTokenBaseHTTPClient.js'; +import { Address } from '../../../encoding/address.js'; /** * The Indexer provides a REST API interface of API calls to support searching the Algorand Blockchain. @@ -39,7 +40,7 @@ import { * * [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 { +export class IndexerClient extends ServiceClient { /** * Create an IndexerClient from * * either a token, baseServer, port, and optional headers @@ -87,7 +88,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ makeHealthCheck() { - return new MakeHealthCheck(this.c, this.intDecoding); + return new MakeHealthCheck(this.c); } /** @@ -104,7 +105,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ lookupAssetBalances(index: number) { - return new LookupAssetBalances(this.c, this.intDecoding, index); + return new LookupAssetBalances(this.c, index); } /** @@ -121,7 +122,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ lookupAssetTransactions(index: number) { - return new LookupAssetTransactions(this.c, this.intDecoding, index); + return new LookupAssetTransactions(this.c, index); } /** @@ -137,8 +138,8 @@ export default class IndexerClient extends ServiceClient { * @param account - The address of the account. * @category GET */ - lookupAccountTransactions(account: string) { - return new LookupAccountTransactions(this.c, this.intDecoding, account); + lookupAccountTransactions(account: string | Address) { + return new LookupAccountTransactions(this.c, account); } /** @@ -155,7 +156,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ lookupBlock(round: number) { - return new LookupBlock(this.c, this.intDecoding, round); + return new LookupBlock(this.c, round); } /** @@ -172,7 +173,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ lookupTransactionByID(txID: string) { - return new LookupTransactionByID(this.c, this.intDecoding, txID); + return new LookupTransactionByID(this.c, txID); } /** @@ -188,8 +189,8 @@ export default class IndexerClient extends ServiceClient { * @param account - The address of the account to look up. * @category GET */ - lookupAccountByID(account: string) { - return new LookupAccountByID(this.c, this.intDecoding, account); + lookupAccountByID(account: string | Address) { + return new LookupAccountByID(this.c, account); } /** @@ -205,8 +206,8 @@ export default class IndexerClient extends ServiceClient { * @param account - The address of the account to look up. * @category GET */ - lookupAccountAssets(account: string) { - return new LookupAccountAssets(this.c, this.intDecoding, account); + lookupAccountAssets(account: string | Address) { + return new LookupAccountAssets(this.c, account); } /** @@ -222,8 +223,8 @@ export default class IndexerClient extends ServiceClient { * @param account - The address of the account to look up. * @category GET */ - lookupAccountCreatedAssets(account: string) { - return new LookupAccountCreatedAssets(this.c, this.intDecoding, account); + lookupAccountCreatedAssets(account: string | Address) { + return new LookupAccountCreatedAssets(this.c, account); } /** @@ -239,8 +240,8 @@ export default class IndexerClient extends ServiceClient { * @param account - The address of the account to look up. * @category GET */ - lookupAccountAppLocalStates(account: string) { - return new LookupAccountAppLocalStates(this.c, this.intDecoding, account); + lookupAccountAppLocalStates(account: string | Address) { + return new LookupAccountAppLocalStates(this.c, account); } /** @@ -256,12 +257,8 @@ export default class IndexerClient extends ServiceClient { * @param account - The address of the account to look up. * @category GET */ - lookupAccountCreatedApplications(account: string) { - return new LookupAccountCreatedApplications( - this.c, - this.intDecoding, - account - ); + lookupAccountCreatedApplications(account: string | Address) { + return new LookupAccountCreatedApplications(this.c, account); } /** @@ -278,7 +275,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ lookupAssetByID(index: number) { - return new LookupAssetByID(this.c, this.intDecoding, index); + return new LookupAssetByID(this.c, index); } /** @@ -295,7 +292,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ lookupApplications(index: number) { - return new LookupApplications(this.c, this.intDecoding, index); + return new LookupApplications(this.c, index); } /** @@ -312,7 +309,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ lookupApplicationLogs(appID: number) { - return new LookupApplicationLogs(this.c, this.intDecoding, appID); + return new LookupApplicationLogs(this.c, appID); } /** @@ -327,7 +324,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ searchAccounts() { - return new SearchAccounts(this.c, this.intDecoding); + return new SearchAccounts(this.c); } /** @@ -342,7 +339,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ searchForTransactions() { - return new SearchForTransactions(this.c, this.intDecoding); + return new SearchForTransactions(this.c); } /** @@ -357,7 +354,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ searchForAssets() { - return new SearchForAssets(this.c, this.intDecoding); + return new SearchForAssets(this.c); } /** @@ -372,7 +369,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ searchForApplications() { - return new SearchForApplications(this.c, this.intDecoding); + return new SearchForApplications(this.c); } /** @@ -402,7 +399,7 @@ export default class IndexerClient extends ServiceClient { * @category GET */ searchForApplicationBoxes(appID: number) { - return new SearchForApplicationBoxes(this.c, this.intDecoding, appID); + return new SearchForApplicationBoxes(this.c, appID); } /** @@ -422,11 +419,6 @@ export default class IndexerClient extends ServiceClient { * @category GET */ lookupApplicationBoxByIDandName(appID: number, boxName: Uint8Array) { - return new LookupApplicationBoxByIDandName( - this.c, - this.intDecoding, - appID, - boxName - ); + return new LookupApplicationBoxByIDandName(this.c, appID, boxName); } } diff --git a/src/client/v2/indexer/lookupAccountAppLocalStates.ts b/src/client/v2/indexer/lookupAccountAppLocalStates.ts index 4a2c2da76..20ecf3c61 100644 --- a/src/client/v2/indexer/lookupAccountAppLocalStates.ts +++ b/src/client/v2/indexer/lookupAccountAppLocalStates.ts @@ -1,8 +1,12 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { ApplicationLocalStatesResponse } from './models/types.js'; + +export default class LookupAccountAppLocalStates extends JSONRequest { + private account: string | Address; -export default class LookupAccountAppLocalStates extends JSONRequest { /** * Returns application local state about the given account. * @@ -16,13 +20,9 @@ export default class LookupAccountAppLocalStates extends JSONRequest { * @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; + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); } /** @@ -137,4 +137,9 @@ export default class LookupAccountAppLocalStates extends JSONRequest { this.query['application-id'] = index; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): ApplicationLocalStatesResponse { + return decodeJSON(response.getJSONText(), ApplicationLocalStatesResponse); + } } diff --git a/src/client/v2/indexer/lookupAccountAssets.ts b/src/client/v2/indexer/lookupAccountAssets.ts index 2d6194f4f..40087921e 100644 --- a/src/client/v2/indexer/lookupAccountAssets.ts +++ b/src/client/v2/indexer/lookupAccountAssets.ts @@ -1,8 +1,12 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { AssetHoldingsResponse } from './models/types.js'; + +export default class LookupAccountAssets extends JSONRequest { + private account: string; -export default class LookupAccountAssets extends JSONRequest { /** * Returns asset about the given account. * @@ -16,13 +20,9 @@ export default class LookupAccountAssets extends JSONRequest { * @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; + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); } /** @@ -138,4 +138,9 @@ export default class LookupAccountAssets extends JSONRequest { this.query['asset-id'] = index; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AssetHoldingsResponse { + return decodeJSON(response.getJSONText(), AssetHoldingsResponse); + } } diff --git a/src/client/v2/indexer/lookupAccountByID.ts b/src/client/v2/indexer/lookupAccountByID.ts index 8c024ec31..cb9cc0dfa 100644 --- a/src/client/v2/indexer/lookupAccountByID.ts +++ b/src/client/v2/indexer/lookupAccountByID.ts @@ -1,8 +1,12 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { AccountResponse } from './models/types.js'; + +export default class LookupAccountByID extends JSONRequest { + private account: string; -export default class LookupAccountByID extends JSONRequest { /** * Returns information about the given account. * @@ -16,13 +20,9 @@ export default class LookupAccountByID extends JSONRequest { * @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; + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); } /** @@ -106,4 +106,9 @@ export default class LookupAccountByID extends JSONRequest { this.query.exclude = exclude; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AccountResponse { + return decodeJSON(response.getJSONText(), AccountResponse); + } } diff --git a/src/client/v2/indexer/lookupAccountCreatedApplications.ts b/src/client/v2/indexer/lookupAccountCreatedApplications.ts index 187ab3363..f27ef9a16 100644 --- a/src/client/v2/indexer/lookupAccountCreatedApplications.ts +++ b/src/client/v2/indexer/lookupAccountCreatedApplications.ts @@ -1,8 +1,12 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { ApplicationsResponse } from './models/types.js'; + +export default class LookupAccountCreatedApplications extends JSONRequest { + private account: string; -export default class LookupAccountCreatedApplications extends JSONRequest { /** * Returns application information created by the given account. * @@ -16,13 +20,9 @@ export default class LookupAccountCreatedApplications extends JSONRequest { * @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; + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); } /** @@ -138,4 +138,9 @@ export default class LookupAccountCreatedApplications extends JSONRequest { this.query['application-id'] = index; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): ApplicationsResponse { + return decodeJSON(response.getJSONText(), ApplicationsResponse); + } } diff --git a/src/client/v2/indexer/lookupAccountCreatedAssets.ts b/src/client/v2/indexer/lookupAccountCreatedAssets.ts index f64a4e1e8..2b138cc36 100644 --- a/src/client/v2/indexer/lookupAccountCreatedAssets.ts +++ b/src/client/v2/indexer/lookupAccountCreatedAssets.ts @@ -1,8 +1,12 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { AssetsResponse } from './models/types.js'; + +export default class LookupAccountCreatedAssets extends JSONRequest { + private account: string; -export default class LookupAccountCreatedAssets extends JSONRequest { /** * Returns asset information created by the given account. * @@ -16,13 +20,9 @@ export default class LookupAccountCreatedAssets extends JSONRequest { * @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; + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); } /** @@ -139,4 +139,9 @@ export default class LookupAccountCreatedAssets extends JSONRequest { this.query['asset-id'] = index; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AssetsResponse { + return decodeJSON(response.getJSONText(), AssetsResponse); + } } diff --git a/src/client/v2/indexer/lookupAccountTransactions.ts b/src/client/v2/indexer/lookupAccountTransactions.ts index 8f76495b6..61816c4f3 100644 --- a/src/client/v2/indexer/lookupAccountTransactions.ts +++ b/src/client/v2/indexer/lookupAccountTransactions.ts @@ -1,7 +1,9 @@ -import { Buffer } from 'buffer'; -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import { bytesToBase64 } from '../../../encoding/binarydata.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import JSONRequest from '../jsonrequest.js'; +import { Address } from '../../../encoding/address.js'; +import { TransactionsResponse } from './models/types.js'; /** * Accept base64 string or Uint8Array and output base64 string @@ -12,10 +14,12 @@ export function base64StringFunnel(data: Uint8Array | string) { if (typeof data === 'string') { return data; } - return Buffer.from(data).toString('base64'); + return bytesToBase64(data); } -export default class LookupAccountTransactions extends JSONRequest { +export default class LookupAccountTransactions extends JSONRequest { + private account: string; + /** * Returns transactions relating to the given account. * @@ -28,13 +32,9 @@ export default class LookupAccountTransactions extends JSONRequest { * [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; + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); } /** @@ -247,11 +247,12 @@ export default class LookupAccountTransactions extends JSONRequest { * .do(); * ``` * - * @param before - rfc3339 string + * @param before - rfc3339 string or Date object * @category query */ - beforeTime(before: string) { - this.query['before-time'] = before; + beforeTime(before: string | Date) { + this.query['before-time'] = + before instanceof Date ? before.toISOString() : before; return this; } @@ -268,11 +269,12 @@ export default class LookupAccountTransactions extends JSONRequest { * .do(); * ``` * - * @param after - rfc3339 string + * @param after - rfc3339 string or Date object * @category query */ - afterTime(after: string) { - this.query['after-time'] = after; + afterTime(after: string | Date) { + this.query['after-time'] = + after instanceof Date ? after.toISOString() : after; return this; } @@ -390,4 +392,9 @@ export default class LookupAccountTransactions extends JSONRequest { this.query['rekey-to'] = rekeyTo; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): TransactionsResponse { + return decodeJSON(response.getJSONText(), TransactionsResponse); + } } diff --git a/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts b/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts index 94bc80175..2190f367a 100644 --- a/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts +++ b/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts @@ -1,13 +1,10 @@ -import { Buffer } from 'buffer'; -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; -import { Box } from './models/types'; +import { bytesToBase64 } from '../../../encoding/binarydata.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import JSONRequest from '../jsonrequest.js'; +import { Box } from './models/types.js'; -export default class LookupApplicationBoxByIDandName extends JSONRequest< - Box, - Record -> { +export default class LookupApplicationBoxByIDandName extends JSONRequest { /** * Returns information about indexed application boxes. * @@ -26,14 +23,12 @@ export default class LookupApplicationBoxByIDandName extends JSONRequest< */ constructor( c: HTTPClient, - intDecoding: IntDecoding, private index: number, boxName: Uint8Array ) { - super(c, intDecoding); - this.index = index; + super(c); // Encode query in base64 format and append the encoding prefix. - const encodedName = Buffer.from(boxName).toString('base64'); + const encodedName = bytesToBase64(boxName); this.query.name = encodeURI(`b64:${encodedName}`); } @@ -45,7 +40,7 @@ export default class LookupApplicationBoxByIDandName extends JSONRequest< } // eslint-disable-next-line class-methods-use-this - prepare(body: Record): Box { - return Box.from_obj_for_encoding(body); + prepare(response: HTTPClientResponse): Box { + return decodeJSON(response.getJSONText(), Box); } } diff --git a/src/client/v2/indexer/lookupApplicationLogs.ts b/src/client/v2/indexer/lookupApplicationLogs.ts index 2b945ba7a..3eb8c2545 100644 --- a/src/client/v2/indexer/lookupApplicationLogs.ts +++ b/src/client/v2/indexer/lookupApplicationLogs.ts @@ -1,8 +1,9 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { ApplicationLogsResponse } from './models/types.js'; -export default class LookupApplicationLogs extends JSONRequest { +export default class LookupApplicationLogs extends JSONRequest { /** * Returns log messages generated by the passed in application. * @@ -16,9 +17,11 @@ export default class LookupApplicationLogs extends JSONRequest { * @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; + constructor( + c: HTTPClient, + private appID: number + ) { + super(c); } /** @@ -153,4 +156,9 @@ export default class LookupApplicationLogs extends JSONRequest { this.query.txid = txid; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): ApplicationLogsResponse { + return decodeJSON(response.getJSONText(), ApplicationLogsResponse); + } } diff --git a/src/client/v2/indexer/lookupApplications.ts b/src/client/v2/indexer/lookupApplications.ts index 8fb44291f..1148f4d99 100644 --- a/src/client/v2/indexer/lookupApplications.ts +++ b/src/client/v2/indexer/lookupApplications.ts @@ -1,8 +1,9 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { ApplicationResponse } from './models/types.js'; -export default class LookupApplications extends JSONRequest { +export default class LookupApplications extends JSONRequest { /** * Returns information about the passed application. * @@ -16,9 +17,11 @@ export default class LookupApplications extends JSONRequest { * @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; + constructor( + c: HTTPClient, + private index: number + ) { + super(c); } /** @@ -56,4 +59,9 @@ export default class LookupApplications extends JSONRequest { this.query['include-all'] = value; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): ApplicationResponse { + return decodeJSON(response.getJSONText(), ApplicationResponse); + } } diff --git a/src/client/v2/indexer/lookupAssetBalances.ts b/src/client/v2/indexer/lookupAssetBalances.ts index 463f05117..1c1eea06b 100644 --- a/src/client/v2/indexer/lookupAssetBalances.ts +++ b/src/client/v2/indexer/lookupAssetBalances.ts @@ -1,8 +1,9 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { AssetBalancesResponse } from './models/types.js'; -export default class LookupAssetBalances extends JSONRequest { +export default class LookupAssetBalances extends JSONRequest { /** * Returns the list of accounts which hold the given asset and their balance. * @@ -15,9 +16,11 @@ export default class LookupAssetBalances extends JSONRequest { * [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; + constructor( + c: HTTPClient, + private index: number + ) { + super(c); } /** @@ -144,4 +147,9 @@ export default class LookupAssetBalances extends JSONRequest { this.query['include-all'] = value; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AssetBalancesResponse { + return decodeJSON(response.getJSONText(), AssetBalancesResponse); + } } diff --git a/src/client/v2/indexer/lookupAssetByID.ts b/src/client/v2/indexer/lookupAssetByID.ts index 76331fb2c..7cd85ad44 100644 --- a/src/client/v2/indexer/lookupAssetByID.ts +++ b/src/client/v2/indexer/lookupAssetByID.ts @@ -1,8 +1,9 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { AssetResponse } from './models/types.js'; -export default class LookupAssetByID extends JSONRequest { +export default class LookupAssetByID extends JSONRequest { /** * Returns asset information of the queried asset. * @@ -15,9 +16,11 @@ export default class LookupAssetByID extends JSONRequest { * [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; + constructor( + c: HTTPClient, + private index: number + ) { + super(c); } /** @@ -55,4 +58,9 @@ export default class LookupAssetByID extends JSONRequest { this.query['include-all'] = value; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AssetResponse { + return decodeJSON(response.getJSONText(), AssetResponse); + } } diff --git a/src/client/v2/indexer/lookupAssetTransactions.ts b/src/client/v2/indexer/lookupAssetTransactions.ts index 8ffb68615..6b73a7511 100644 --- a/src/client/v2/indexer/lookupAssetTransactions.ts +++ b/src/client/v2/indexer/lookupAssetTransactions.ts @@ -1,9 +1,11 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; -import { base64StringFunnel } from './lookupAccountTransactions'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { base64StringFunnel } from './lookupAccountTransactions.js'; +import { Address } from '../../../encoding/address.js'; +import { TransactionsResponse } from './models/types.js'; -export default class LookupAssetTransactions extends JSONRequest { +export default class LookupAssetTransactions extends JSONRequest { /** * Returns transactions relating to the given asset. * @@ -16,9 +18,11 @@ export default class LookupAssetTransactions extends JSONRequest { * [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; + constructor( + c: HTTPClient, + private index: number + ) { + super(c); } /** @@ -210,11 +214,12 @@ export default class LookupAssetTransactions extends JSONRequest { * .do(); * ``` * - * @param before - rfc3339 string + * @param before - rfc3339 string or Date object * @category query */ - beforeTime(before: string) { - this.query['before-time'] = before; + beforeTime(before: string | Date) { + this.query['before-time'] = + before instanceof Date ? before.toISOString() : before; return this; } @@ -231,11 +236,12 @@ export default class LookupAssetTransactions extends JSONRequest { * .do(); * ``` * - * @param after - rfc3339 string + * @param after - rfc3339 string or Date object * @category query */ - afterTime(after: string) { - this.query['after-time'] = after; + afterTime(after: string | Date) { + this.query['after-time'] = + after instanceof Date ? after.toISOString() : after; return this; } @@ -321,8 +327,8 @@ export default class LookupAssetTransactions extends JSONRequest { * @param address * @category query */ - address(address: string) { - this.query.address = address; + address(address: string | Address) { + this.query.address = address.toString(); return this; } @@ -393,4 +399,9 @@ export default class LookupAssetTransactions extends JSONRequest { this.query['rekey-to'] = rekeyTo; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): TransactionsResponse { + return decodeJSON(response.getJSONText(), TransactionsResponse); + } } diff --git a/src/client/v2/indexer/lookupBlock.ts b/src/client/v2/indexer/lookupBlock.ts index 3b5030712..8771590df 100644 --- a/src/client/v2/indexer/lookupBlock.ts +++ b/src/client/v2/indexer/lookupBlock.ts @@ -1,8 +1,9 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Block } from './models/types.js'; -export default class LookupBlock extends JSONRequest { +export default class LookupBlock extends JSONRequest { /** * Returns the block for the passed round. * @@ -16,9 +17,11 @@ export default class LookupBlock extends JSONRequest { * @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; + constructor( + c: HTTPClient, + private round: number + ) { + super(c); } /** @@ -36,4 +39,9 @@ export default class LookupBlock extends JSONRequest { this.query['header-only'] = headerOnly; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): Block { + return decodeJSON(response.getJSONText(), Block); + } } diff --git a/src/client/v2/indexer/lookupTransactionByID.ts b/src/client/v2/indexer/lookupTransactionByID.ts index d834db3ff..a01fa6f85 100644 --- a/src/client/v2/indexer/lookupTransactionByID.ts +++ b/src/client/v2/indexer/lookupTransactionByID.ts @@ -1,8 +1,9 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { TransactionResponse } from './models/types.js'; -export default class LookupTransactionByID extends JSONRequest { +export default class LookupTransactionByID extends JSONRequest { /** * Returns information about the given transaction. * @@ -16,9 +17,11 @@ export default class LookupTransactionByID extends JSONRequest { * @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; + constructor( + c: HTTPClient, + private txID: string + ) { + super(c); } /** @@ -27,4 +30,9 @@ export default class LookupTransactionByID extends JSONRequest { path() { return `/v2/transactions/${this.txID}`; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): TransactionResponse { + return decodeJSON(response.getJSONText(), TransactionResponse); + } } diff --git a/src/client/v2/indexer/makeHealthCheck.ts b/src/client/v2/indexer/makeHealthCheck.ts index 61059b045..415ca738e 100644 --- a/src/client/v2/indexer/makeHealthCheck.ts +++ b/src/client/v2/indexer/makeHealthCheck.ts @@ -1,4 +1,7 @@ -import JSONRequest from '../jsonrequest'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { HealthCheck } from './models/types.js'; /** * Returns the health object for the service. @@ -12,7 +15,7 @@ import JSONRequest from '../jsonrequest'; * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-health) * @category GET */ -export default class MakeHealthCheck extends JSONRequest { +export default class MakeHealthCheck extends JSONRequest { /** * @returns `/health` */ @@ -20,4 +23,9 @@ export default class MakeHealthCheck extends JSONRequest { path() { return '/health'; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): HealthCheck { + return decodeJSON(response.getJSONText(), HealthCheck); + } } diff --git a/src/client/v2/indexer/models/types.ts b/src/client/v2/indexer/models/types.ts index 7d51e9985..5b8929221 100644 --- a/src/client/v2/indexer/models/types.ts +++ b/src/client/v2/indexer/models/types.ts @@ -3,15 +3,178 @@ */ /* eslint-disable no-use-before-define */ -import { Buffer } from 'buffer'; -import BaseModel from '../../basemodel'; +import { ensureBigInt, ensureSafeInteger } from '../../../../utils/utils.js'; +import { Encodable, Schema } from '../../../../encoding/encoding.js'; +import { + NamedMapSchema, + ArraySchema, + Uint64Schema, + StringSchema, + BooleanSchema, + ByteArraySchema, + OptionalSchema, +} from '../../../../encoding/schema/index.js'; +import { base64ToBytes } from '../../../../encoding/binarydata.js'; +import { Address } from '../../../../encoding/address.js'; +import { UntypedValue } from '../../untypedmodel.js'; /** * Account information at a given round. * Definition: * data/basics/userBalance.go : AccountData */ -export class Account extends BaseModel { +export class Account implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'amount-without-pending-rewards', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'min-balance', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'pending-rewards', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'rewards', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'status', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'total-apps-opted-in', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-assets-opted-in', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-box-bytes', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-boxes', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-created-apps', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-created-assets', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'apps-local-state', + valueSchema: new OptionalSchema( + new ArraySchema(ApplicationLocalState.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'apps-total-extra-pages', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'apps-total-schema', + valueSchema: new OptionalSchema( + ApplicationStateSchema.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'assets', + valueSchema: new OptionalSchema( + new ArraySchema(AssetHolding.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'auth-addr', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'closed-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'created-apps', + valueSchema: new OptionalSchema( + new ArraySchema(Application.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'created-assets', + valueSchema: new OptionalSchema( + new ArraySchema(Asset.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'created-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'incentive-eligible', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'last-heartbeat', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'last-proposed', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'participation', + valueSchema: new OptionalSchema(AccountParticipation.encodingSchema), + omitEmpty: true, + }, + { + key: 'reward-base', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'sig-type', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * the account public key */ @@ -20,33 +183,33 @@ export class Account extends BaseModel { /** * total number of MicroAlgos in the account */ - public amount: number | bigint; + public amount: bigint; /** * specifies the amount of MicroAlgos in the account, without the pending rewards. */ - public amountWithoutPendingRewards: number | bigint; + public amountWithoutPendingRewards: bigint; /** * MicroAlgo balance required by the account. * The requirement grows based on asset and application usage. */ - public minBalance: number | bigint; + public minBalance: number; /** * amount of MicroAlgos of pending rewards in this account. */ - public pendingRewards: number | bigint; + public pendingRewards: bigint; /** * total rewards of MicroAlgos the account has received, including pending rewards. */ - public rewards: number | bigint; + public rewards: bigint; /** * The round for which this information is relevant. */ - public round: number | bigint; + public round: bigint; /** * voting status of the account's MicroAlgos @@ -62,35 +225,35 @@ export class Account extends BaseModel { * 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; + public totalAppsOptedIn: number; /** * 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; + public totalAssetsOptedIn: number; /** * 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; + public totalBoxBytes: number; /** * For app-accounts only. The total number of boxes which belong to the associated * application. */ - public totalBoxes: number | bigint; + public totalBoxes: number; /** * The count of all apps (AppParams objects) created by this account. */ - public totalCreatedApps: number | bigint; + public totalCreatedApps: number; /** * The count of all assets (AssetParams objects) created by this account. */ - public totalCreatedAssets: number | bigint; + public totalCreatedAssets: number; /** * application local data stored in this account. @@ -101,7 +264,7 @@ export class Account extends BaseModel { /** * the sum of all extra application program pages for this account. */ - public appsTotalExtraPages?: number | bigint; + public appsTotalExtraPages?: number; /** * the sum of all of the local schemas and global schemas in this account. @@ -120,12 +283,12 @@ export class Account extends BaseModel { * the current account is used. This field can be updated in any transaction by * setting the RekeyTo field. */ - public authAddr?: string; + public authAddr?: Address; /** * Round during which this account was most recently closed. */ - public closedAtRound?: number | bigint; + public closedAtRound?: bigint; /** * parameters of applications created by this account including app global data. @@ -142,7 +305,7 @@ export class Account extends BaseModel { /** * Round during which this account first appeared in a transaction. */ - public createdAtRound?: number | bigint; + public createdAtRound?: bigint; /** * Whether or not this account is currently closed. @@ -159,12 +322,12 @@ export class Account extends BaseModel { * The round in which this account last went online, or explicitly renewed their * online status. */ - public lastHeartbeat?: number | bigint; + public lastHeartbeat?: number; /** * The round in which this account last proposed the block. */ - public lastProposed?: number | bigint; + public lastProposed?: number; /** * AccountParticipation describes the parameters used by this account in consensus @@ -176,7 +339,7 @@ export class Account extends BaseModel { * used as part of the rewards computation. Only applicable to accounts which are * participating. */ - public rewardBase?: number | bigint; + public rewardBase?: bigint; /** * the type of signature used by this account, must be one of: @@ -295,7 +458,7 @@ export class Account extends BaseModel { appsTotalExtraPages?: number | bigint; appsTotalSchema?: ApplicationStateSchema; assets?: AssetHolding[]; - authAddr?: string; + authAddr?: Address | string; closedAtRound?: number | bigint; createdApps?: Application[]; createdAssets?: Asset[]; @@ -308,176 +471,196 @@ export class Account extends BaseModel { rewardBase?: number | bigint; sigType?: string; }) { - super(); this.address = address; - this.amount = amount; - this.amountWithoutPendingRewards = amountWithoutPendingRewards; - this.minBalance = minBalance; - this.pendingRewards = pendingRewards; - this.rewards = rewards; - this.round = round; + this.amount = ensureBigInt(amount); + this.amountWithoutPendingRewards = ensureBigInt( + amountWithoutPendingRewards + ); + this.minBalance = ensureSafeInteger(minBalance); + this.pendingRewards = ensureBigInt(pendingRewards); + this.rewards = ensureBigInt(rewards); + this.round = ensureBigInt(round); this.status = status; - this.totalAppsOptedIn = totalAppsOptedIn; - this.totalAssetsOptedIn = totalAssetsOptedIn; - this.totalBoxBytes = totalBoxBytes; - this.totalBoxes = totalBoxes; - this.totalCreatedApps = totalCreatedApps; - this.totalCreatedAssets = totalCreatedAssets; + this.totalAppsOptedIn = ensureSafeInteger(totalAppsOptedIn); + this.totalAssetsOptedIn = ensureSafeInteger(totalAssetsOptedIn); + this.totalBoxBytes = ensureSafeInteger(totalBoxBytes); + this.totalBoxes = ensureSafeInteger(totalBoxes); + this.totalCreatedApps = ensureSafeInteger(totalCreatedApps); + this.totalCreatedAssets = ensureSafeInteger(totalCreatedAssets); this.appsLocalState = appsLocalState; - this.appsTotalExtraPages = appsTotalExtraPages; + this.appsTotalExtraPages = + typeof appsTotalExtraPages === 'undefined' + ? undefined + : ensureSafeInteger(appsTotalExtraPages); this.appsTotalSchema = appsTotalSchema; this.assets = assets; - this.authAddr = authAddr; - this.closedAtRound = closedAtRound; + this.authAddr = + typeof authAddr === 'string' ? Address.fromString(authAddr) : authAddr; + this.closedAtRound = + typeof closedAtRound === 'undefined' + ? undefined + : ensureBigInt(closedAtRound); this.createdApps = createdApps; this.createdAssets = createdAssets; - this.createdAtRound = createdAtRound; + this.createdAtRound = + typeof createdAtRound === 'undefined' + ? undefined + : ensureBigInt(createdAtRound); this.deleted = deleted; this.incentiveEligible = incentiveEligible; - this.lastHeartbeat = lastHeartbeat; - this.lastProposed = lastProposed; + this.lastHeartbeat = + typeof lastHeartbeat === 'undefined' + ? undefined + : ensureSafeInteger(lastHeartbeat); + this.lastProposed = + typeof lastProposed === 'undefined' + ? undefined + : ensureSafeInteger(lastProposed); this.participation = participation; - this.rewardBase = rewardBase; + this.rewardBase = + typeof rewardBase === 'undefined' ? undefined : ensureBigInt(rewardBase); this.sigType = sigType; + } - 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', - 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', - incentiveEligible: 'incentive-eligible', - lastHeartbeat: 'last-heartbeat', - lastProposed: 'last-proposed', - 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['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-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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Account.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['address', this.address], + ['amount', this.amount], + ['amount-without-pending-rewards', this.amountWithoutPendingRewards], + ['min-balance', this.minBalance], + ['pending-rewards', this.pendingRewards], + ['rewards', this.rewards], + ['round', this.round], + ['status', this.status], + ['total-apps-opted-in', this.totalAppsOptedIn], + ['total-assets-opted-in', this.totalAssetsOptedIn], + ['total-box-bytes', this.totalBoxBytes], + ['total-boxes', this.totalBoxes], + ['total-created-apps', this.totalCreatedApps], + ['total-created-assets', this.totalCreatedAssets], + [ + 'apps-local-state', + typeof this.appsLocalState !== 'undefined' + ? this.appsLocalState.map((v) => v.toEncodingData()) + : undefined, + ], + ['apps-total-extra-pages', this.appsTotalExtraPages], + [ + 'apps-total-schema', + typeof this.appsTotalSchema !== 'undefined' + ? this.appsTotalSchema.toEncodingData() + : undefined, + ], + [ + 'assets', + typeof this.assets !== 'undefined' + ? this.assets.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'auth-addr', + typeof this.authAddr !== 'undefined' + ? this.authAddr.toString() + : undefined, + ], + ['closed-at-round', this.closedAtRound], + [ + 'created-apps', + typeof this.createdApps !== 'undefined' + ? this.createdApps.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'created-assets', + typeof this.createdAssets !== 'undefined' + ? this.createdAssets.map((v) => v.toEncodingData()) + : undefined, + ], + ['created-at-round', this.createdAtRound], + ['deleted', this.deleted], + ['incentive-eligible', this.incentiveEligible], + ['last-heartbeat', this.lastHeartbeat], + ['last-proposed', this.lastProposed], + [ + 'participation', + typeof this.participation !== 'undefined' + ? this.participation.toEncodingData() + : undefined, + ], + ['reward-base', this.rewardBase], + ['sig-type', this.sigType], + ]); + } + + static fromEncodingData(data: unknown): Account { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Account: ${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'], - totalBoxBytes: data['total-box-bytes'], - totalBoxes: data['total-boxes'], - totalCreatedApps: data['total-created-apps'], - totalCreatedAssets: data['total-created-assets'], + address: data.get('address'), + amount: data.get('amount'), + amountWithoutPendingRewards: data.get('amount-without-pending-rewards'), + minBalance: data.get('min-balance'), + pendingRewards: data.get('pending-rewards'), + rewards: data.get('rewards'), + round: data.get('round'), + status: data.get('status'), + totalAppsOptedIn: data.get('total-apps-opted-in'), + totalAssetsOptedIn: data.get('total-assets-opted-in'), + totalBoxBytes: data.get('total-box-bytes'), + totalBoxes: data.get('total-boxes'), + totalCreatedApps: data.get('total-created-apps'), + totalCreatedAssets: data.get('total-created-assets'), appsLocalState: - typeof data['apps-local-state'] !== 'undefined' - ? data['apps-local-state'].map( - ApplicationLocalState.from_obj_for_encoding - ) + typeof data.get('apps-local-state') !== 'undefined' + ? data + .get('apps-local-state') + .map((v: unknown) => ApplicationLocalState.fromEncodingData(v)) : undefined, - appsTotalExtraPages: data['apps-total-extra-pages'], + appsTotalExtraPages: data.get('apps-total-extra-pages'), appsTotalSchema: - typeof data['apps-total-schema'] !== 'undefined' - ? ApplicationStateSchema.from_obj_for_encoding( - data['apps-total-schema'] + typeof data.get('apps-total-schema') !== 'undefined' + ? ApplicationStateSchema.fromEncodingData( + data.get('apps-total-schema') ) : undefined, assets: - typeof data['assets'] !== 'undefined' - ? data['assets'].map(AssetHolding.from_obj_for_encoding) + typeof data.get('assets') !== 'undefined' + ? data + .get('assets') + .map((v: unknown) => AssetHolding.fromEncodingData(v)) : undefined, - authAddr: data['auth-addr'], - closedAtRound: data['closed-at-round'], + authAddr: data.get('auth-addr'), + closedAtRound: data.get('closed-at-round'), createdApps: - typeof data['created-apps'] !== 'undefined' - ? data['created-apps'].map(Application.from_obj_for_encoding) + typeof data.get('created-apps') !== 'undefined' + ? data + .get('created-apps') + .map((v: unknown) => Application.fromEncodingData(v)) : undefined, createdAssets: - typeof data['created-assets'] !== 'undefined' - ? data['created-assets'].map(Asset.from_obj_for_encoding) + typeof data.get('created-assets') !== 'undefined' + ? data + .get('created-assets') + .map((v: unknown) => Asset.fromEncodingData(v)) : undefined, - createdAtRound: data['created-at-round'], - deleted: data['deleted'], - incentiveEligible: data['incentive-eligible'], - lastHeartbeat: data['last-heartbeat'], - lastProposed: data['last-proposed'], + createdAtRound: data.get('created-at-round'), + deleted: data.get('deleted'), + incentiveEligible: data.get('incentive-eligible'), + lastHeartbeat: data.get('last-heartbeat'), + lastProposed: data.get('last-proposed'), participation: - typeof data['participation'] !== 'undefined' - ? AccountParticipation.from_obj_for_encoding(data['participation']) + typeof data.get('participation') !== 'undefined' + ? AccountParticipation.fromEncodingData(data.get('participation')) : undefined, - rewardBase: data['reward-base'], - sigType: data['sig-type'], + rewardBase: data.get('reward-base'), + sigType: data.get('sig-type'), }); - /* eslint-enable dot-notation */ } } @@ -485,7 +668,48 @@ export class Account extends BaseModel { * AccountParticipation describes the parameters used by this account in consensus * protocol. */ -export class AccountParticipation extends BaseModel { +export class AccountParticipation implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'selection-participation-key', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'vote-first-valid', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'vote-key-dilution', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'vote-last-valid', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'vote-participation-key', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'state-proof-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Selection public key (if any) currently registered for this round. */ @@ -494,17 +718,17 @@ export class AccountParticipation extends BaseModel { /** * First round for which this participation is valid. */ - public voteFirstValid: number | bigint; + public voteFirstValid: bigint; /** * Number of subkeys in each batch of participation keys. */ - public voteKeyDilution: number | bigint; + public voteKeyDilution: bigint; /** * Last round for which this participation is valid. */ - public voteLastValid: number | bigint; + public voteLastValid: bigint; /** * root participation public key (if any) currently registered for this round. @@ -540,74 +764,79 @@ export class AccountParticipation extends BaseModel { voteParticipationKey: string | Uint8Array; stateProofKey?: string | Uint8Array; }) { - super(); this.selectionParticipationKey = typeof selectionParticipationKey === 'string' - ? new Uint8Array(Buffer.from(selectionParticipationKey, 'base64')) + ? base64ToBytes(selectionParticipationKey) : selectionParticipationKey; - this.voteFirstValid = voteFirstValid; - this.voteKeyDilution = voteKeyDilution; - this.voteLastValid = voteLastValid; + this.voteFirstValid = ensureBigInt(voteFirstValid); + this.voteKeyDilution = ensureBigInt(voteKeyDilution); + this.voteLastValid = ensureBigInt(voteLastValid); this.voteParticipationKey = typeof voteParticipationKey === 'string' - ? new Uint8Array(Buffer.from(voteParticipationKey, 'base64')) + ? base64ToBytes(voteParticipationKey) : voteParticipationKey; this.stateProofKey = typeof stateProofKey === 'string' - ? new Uint8Array(Buffer.from(stateProofKey, 'base64')) + ? base64ToBytes(stateProofKey) : 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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountParticipation.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['selection-participation-key', this.selectionParticipationKey], + ['vote-first-valid', this.voteFirstValid], + ['vote-key-dilution', this.voteKeyDilution], + ['vote-last-valid', this.voteLastValid], + ['vote-participation-key', this.voteParticipationKey], + ['state-proof-key', this.stateProofKey], + ]); + } + + static fromEncodingData(data: unknown): AccountParticipation { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountParticipation: ${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'], + selectionParticipationKey: data.get('selection-participation-key'), + voteFirstValid: data.get('vote-first-valid'), + voteKeyDilution: data.get('vote-key-dilution'), + voteLastValid: data.get('vote-last-valid'), + voteParticipationKey: data.get('vote-participation-key'), + stateProofKey: data.get('state-proof-key'), }); - /* eslint-enable dot-notation */ } } /** * */ -export class AccountResponse extends BaseModel { +export class AccountResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'account', + valueSchema: Account.encodingSchema, + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Account information at a given round. * Definition: @@ -618,7 +847,7 @@ export class AccountResponse extends BaseModel { /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; /** * Creates a new `AccountResponse` object. @@ -634,37 +863,54 @@ export class AccountResponse extends BaseModel { account: Account; currentRound: number | bigint; }) { - super(); this.account = account; - this.currentRound = currentRound; + this.currentRound = ensureBigInt(currentRound); + } - this.attribute_map = { - account: 'account', - currentRound: 'current-round', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['account', this.account.toEncodingData()], + ['current-round', this.currentRound], + ]); + } + + static fromEncodingData(data: unknown): AccountResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountResponse: ${data}`); + } return new AccountResponse({ - account: Account.from_obj_for_encoding(data['account']), - currentRound: data['current-round'], + account: Account.fromEncodingData(data.get('account') ?? new Map()), + currentRound: data.get('current-round'), }); - /* eslint-enable dot-notation */ } } /** * Application state delta. */ -export class AccountStateDelta extends BaseModel { +export class AccountStateDelta implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'delta', + valueSchema: new ArraySchema(EvalDeltaKeyValue.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public address: string; /** @@ -684,43 +930,71 @@ export class AccountStateDelta extends BaseModel { address: string; delta: EvalDeltaKeyValue[]; }) { - super(); this.address = address; this.delta = delta; + } - this.attribute_map = { - address: 'address', - delta: 'delta', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountStateDelta.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['address', this.address], + ['delta', this.delta.map((v) => v.toEncodingData())], + ]); + } + + static fromEncodingData(data: unknown): AccountStateDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountStateDelta: ${data}`); + } return new AccountStateDelta({ - address: data['address'], - delta: data['delta'].map(EvalDeltaKeyValue.from_obj_for_encoding), + address: data.get('address'), + delta: (data.get('delta') ?? []).map((v: unknown) => + EvalDeltaKeyValue.fromEncodingData(v) + ), }); - /* eslint-enable dot-notation */ } } /** * */ -export class AccountsResponse extends BaseModel { +export class AccountsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'accounts', + valueSchema: new ArraySchema(Account.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public accounts: Account[]; /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; /** * Used for pagination, when making another request provide this token with the @@ -744,46 +1018,78 @@ export class AccountsResponse extends BaseModel { currentRound: number | bigint; nextToken?: string; }) { - super(); this.accounts = accounts; - this.currentRound = currentRound; + this.currentRound = ensureBigInt(currentRound); this.nextToken = nextToken; + } - this.attribute_map = { - accounts: 'accounts', - currentRound: 'current-round', - nextToken: 'next-token', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountsResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['accounts', this.accounts.map((v) => v.toEncodingData())], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): AccountsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountsResponse: ${data}`); + } return new AccountsResponse({ - accounts: data['accounts'].map(Account.from_obj_for_encoding), - currentRound: data['current-round'], - nextToken: data['next-token'], + accounts: (data.get('accounts') ?? []).map((v: unknown) => + Account.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), }); - /* eslint-enable dot-notation */ } } /** * Application index and its parameters */ -export class Application extends BaseModel { +export class Application implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'params', + valueSchema: ApplicationParams.encodingSchema, + omitEmpty: true, + }, + { + key: 'created-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'deleted-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * application index. */ - public id: number | bigint; + public id: bigint; /** * application parameters. @@ -793,7 +1099,7 @@ export class Application extends BaseModel { /** * Round when this application was created. */ - public createdAtRound?: number | bigint; + public createdAtRound?: bigint; /** * Whether or not this application is currently deleted. @@ -803,7 +1109,7 @@ export class Application extends BaseModel { /** * Round when this application was deleted. */ - public deletedAtRound?: number | bigint; + public deletedAtRound?: bigint; /** * Creates a new `Application` object. @@ -826,48 +1132,97 @@ export class Application extends BaseModel { deleted?: boolean; deletedAtRound?: number | bigint; }) { - super(); - this.id = id; + this.id = ensureBigInt(id); this.params = params; - this.createdAtRound = createdAtRound; + this.createdAtRound = + typeof createdAtRound === 'undefined' + ? undefined + : ensureBigInt(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}`); + this.deletedAtRound = + typeof deletedAtRound === 'undefined' + ? undefined + : ensureBigInt(deletedAtRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Application.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['id', this.id], + ['params', this.params.toEncodingData()], + ['created-at-round', this.createdAtRound], + ['deleted', this.deleted], + ['deleted-at-round', this.deletedAtRound], + ]); + } + + static fromEncodingData(data: unknown): Application { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Application: ${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'], + id: data.get('id'), + params: ApplicationParams.fromEncodingData( + data.get('params') ?? new Map() + ), + createdAtRound: data.get('created-at-round'), + deleted: data.get('deleted'), + deletedAtRound: data.get('deleted-at-round'), }); - /* eslint-enable dot-notation */ } } /** * Stores local state associated with an application. */ -export class ApplicationLocalState extends BaseModel { +export class ApplicationLocalState implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'schema', + valueSchema: ApplicationStateSchema.encodingSchema, + omitEmpty: true, + }, + { + key: 'closed-out-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'key-value', + valueSchema: new OptionalSchema( + new ArraySchema(TealKeyValue.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'opted-in-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * The application which this local state is for. */ - public id: number | bigint; + public id: bigint; /** * schema. @@ -877,7 +1232,7 @@ export class ApplicationLocalState extends BaseModel { /** * Round when account closed out of the application. */ - public closedOutAtRound?: number | bigint; + public closedOutAtRound?: bigint; /** * Whether or not the application local state is currently deleted from its @@ -893,7 +1248,7 @@ export class ApplicationLocalState extends BaseModel { /** * Round when the account opted into the application. */ - public optedInAtRound?: number | bigint; + public optedInAtRound?: bigint; /** * Creates a new `ApplicationLocalState` object. @@ -920,58 +1275,99 @@ export class ApplicationLocalState extends BaseModel { keyValue?: TealKeyValue[]; optedInAtRound?: number | bigint; }) { - super(); - this.id = id; + this.id = ensureBigInt(id); this.schema = schema; - this.closedOutAtRound = closedOutAtRound; + this.closedOutAtRound = + typeof closedOutAtRound === 'undefined' + ? undefined + : ensureBigInt(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}`); + this.optedInAtRound = + typeof optedInAtRound === 'undefined' + ? undefined + : ensureBigInt(optedInAtRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationLocalState.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['id', this.id], + ['schema', this.schema.toEncodingData()], + ['closed-out-at-round', this.closedOutAtRound], + ['deleted', this.deleted], + [ + 'key-value', + typeof this.keyValue !== 'undefined' + ? this.keyValue.map((v) => v.toEncodingData()) + : undefined, + ], + ['opted-in-at-round', this.optedInAtRound], + ]); + } + + static fromEncodingData(data: unknown): ApplicationLocalState { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationLocalState: ${data}`); + } return new ApplicationLocalState({ - id: data['id'], - schema: ApplicationStateSchema.from_obj_for_encoding(data['schema']), - closedOutAtRound: data['closed-out-at-round'], - deleted: data['deleted'], + id: data.get('id'), + schema: ApplicationStateSchema.fromEncodingData( + data.get('schema') ?? new Map() + ), + closedOutAtRound: data.get('closed-out-at-round'), + deleted: data.get('deleted'), keyValue: - typeof data['key-value'] !== 'undefined' - ? data['key-value'].map(TealKeyValue.from_obj_for_encoding) + typeof data.get('key-value') !== 'undefined' + ? data + .get('key-value') + .map((v: unknown) => TealKeyValue.fromEncodingData(v)) : undefined, - optedInAtRound: data['opted-in-at-round'], + optedInAtRound: data.get('opted-in-at-round'), }); - /* eslint-enable dot-notation */ } } /** * */ -export class ApplicationLocalStatesResponse extends BaseModel { +export class ApplicationLocalStatesResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'apps-local-states', + valueSchema: new ArraySchema(ApplicationLocalState.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public appsLocalStates: ApplicationLocalState[]; /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; /** * Used for pagination, when making another request provide this token with the @@ -995,46 +1391,64 @@ export class ApplicationLocalStatesResponse extends BaseModel { currentRound: number | bigint; nextToken?: string; }) { - super(); this.appsLocalStates = appsLocalStates; - this.currentRound = currentRound; + this.currentRound = ensureBigInt(currentRound); this.nextToken = nextToken; + } - this.attribute_map = { - appsLocalStates: 'apps-local-states', - currentRound: 'current-round', - nextToken: 'next-token', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationLocalStatesResponse.encodingSchema; } - // 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') + toEncodingData(): Map { + return new Map([ + [ + 'apps-local-states', + this.appsLocalStates.map((v) => v.toEncodingData()), + ], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): ApplicationLocalStatesResponse { + if (!(data instanceof Map)) { throw new Error( - `Response is missing required field 'current-round': ${data}` + `Invalid decoded ApplicationLocalStatesResponse: ${data}` ); + } return new ApplicationLocalStatesResponse({ - appsLocalStates: data['apps-local-states'].map( - ApplicationLocalState.from_obj_for_encoding + appsLocalStates: (data.get('apps-local-states') ?? []).map((v: unknown) => + ApplicationLocalState.fromEncodingData(v) ), - currentRound: data['current-round'], - nextToken: data['next-token'], + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), }); - /* eslint-enable dot-notation */ } } /** * Stores the global information associated with an application. */ -export class ApplicationLogData extends BaseModel { +export class ApplicationLogData implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'logs', + valueSchema: new ArraySchema(new ByteArraySchema()), + omitEmpty: true, + }, + { key: 'txid', valueSchema: new StringSchema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * Logs for the application being executed by the transaction. */ @@ -1051,46 +1465,79 @@ export class ApplicationLogData extends BaseModel { * @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 class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationLogData.encodingSchema; } - // 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}`); + toEncodingData(): Map { + return new Map([ + ['logs', this.logs], + ['txid', this.txid], + ]); + } + + static fromEncodingData(data: unknown): ApplicationLogData { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationLogData: ${data}`); + } return new ApplicationLogData({ - logs: data['logs'], - txid: data['txid'], + logs: data.get('logs'), + txid: data.get('txid'), }); - /* eslint-enable dot-notation */ } } /** * */ -export class ApplicationLogsResponse extends BaseModel { +export class ApplicationLogsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'application-id', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'log-data', + valueSchema: new OptionalSchema( + new ArraySchema(ApplicationLogData.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (appidx) application index. */ - public applicationId: number | bigint; + public applicationId: bigint; /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; public logData?: ApplicationLogData[]; @@ -1119,50 +1566,105 @@ export class ApplicationLogsResponse extends BaseModel { logData?: ApplicationLogData[]; nextToken?: string; }) { - super(); - this.applicationId = applicationId; - this.currentRound = currentRound; + this.applicationId = ensureBigInt(applicationId); + this.currentRound = ensureBigInt(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 class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationLogsResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['application-id', this.applicationId], + ['current-round', this.currentRound], + [ + 'log-data', + typeof this.logData !== 'undefined' + ? this.logData.map((v) => v.toEncodingData()) + : undefined, + ], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): ApplicationLogsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationLogsResponse: ${data}`); + } return new ApplicationLogsResponse({ - applicationId: data['application-id'], - currentRound: data['current-round'], + applicationId: data.get('application-id'), + currentRound: data.get('current-round'), logData: - typeof data['log-data'] !== 'undefined' - ? data['log-data'].map(ApplicationLogData.from_obj_for_encoding) + typeof data.get('log-data') !== 'undefined' + ? data + .get('log-data') + .map((v: unknown) => ApplicationLogData.fromEncodingData(v)) : undefined, - nextToken: data['next-token'], + nextToken: data.get('next-token'), }); - /* eslint-enable dot-notation */ } } /** * Stores the global information associated with an application. */ -export class ApplicationParams extends BaseModel { +export class ApplicationParams implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'approval-program', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'clear-state-program', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'creator', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'extra-program-pages', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'global-state', + valueSchema: new OptionalSchema( + new ArraySchema(TealKeyValue.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'global-state-schema', + valueSchema: new OptionalSchema( + ApplicationStateSchema.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'local-state-schema', + valueSchema: new OptionalSchema( + ApplicationStateSchema.encodingSchema + ), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * approval program. */ @@ -1177,12 +1679,12 @@ export class ApplicationParams extends BaseModel { * 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; + public creator?: Address; /** * the number of extra program pages available to this app. */ - public extraProgramPages?: number | bigint; + public extraProgramPages?: number; /** * global state @@ -1221,83 +1723,128 @@ export class ApplicationParams extends BaseModel { }: { approvalProgram: string | Uint8Array; clearStateProgram: string | Uint8Array; - creator?: string; + creator?: Address | string; extraProgramPages?: number | bigint; globalState?: TealKeyValue[]; globalStateSchema?: ApplicationStateSchema; localStateSchema?: ApplicationStateSchema; }) { - super(); this.approvalProgram = typeof approvalProgram === 'string' - ? new Uint8Array(Buffer.from(approvalProgram, 'base64')) + ? base64ToBytes(approvalProgram) : approvalProgram; this.clearStateProgram = typeof clearStateProgram === 'string' - ? new Uint8Array(Buffer.from(clearStateProgram, 'base64')) + ? base64ToBytes(clearStateProgram) : clearStateProgram; - this.creator = creator; - this.extraProgramPages = extraProgramPages; + this.creator = + typeof creator === 'string' ? Address.fromString(creator) : creator; + this.extraProgramPages = + typeof extraProgramPages === 'undefined' + ? undefined + : ensureSafeInteger(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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationParams.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['approval-program', this.approvalProgram], + ['clear-state-program', this.clearStateProgram], + [ + 'creator', + typeof this.creator !== 'undefined' + ? this.creator.toString() + : undefined, + ], + ['extra-program-pages', this.extraProgramPages], + [ + 'global-state', + typeof this.globalState !== 'undefined' + ? this.globalState.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'global-state-schema', + typeof this.globalStateSchema !== 'undefined' + ? this.globalStateSchema.toEncodingData() + : undefined, + ], + [ + 'local-state-schema', + typeof this.localStateSchema !== 'undefined' + ? this.localStateSchema.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): ApplicationParams { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationParams: ${data}`); + } return new ApplicationParams({ - approvalProgram: data['approval-program'], - clearStateProgram: data['clear-state-program'], - creator: data['creator'], - extraProgramPages: data['extra-program-pages'], + approvalProgram: data.get('approval-program'), + clearStateProgram: data.get('clear-state-program'), + creator: data.get('creator'), + extraProgramPages: data.get('extra-program-pages'), globalState: - typeof data['global-state'] !== 'undefined' - ? data['global-state'].map(TealKeyValue.from_obj_for_encoding) + typeof data.get('global-state') !== 'undefined' + ? data + .get('global-state') + .map((v: unknown) => TealKeyValue.fromEncodingData(v)) : undefined, globalStateSchema: - typeof data['global-state-schema'] !== 'undefined' - ? ApplicationStateSchema.from_obj_for_encoding( - data['global-state-schema'] + typeof data.get('global-state-schema') !== 'undefined' + ? ApplicationStateSchema.fromEncodingData( + data.get('global-state-schema') ) : undefined, localStateSchema: - typeof data['local-state-schema'] !== 'undefined' - ? ApplicationStateSchema.from_obj_for_encoding( - data['local-state-schema'] + typeof data.get('local-state-schema') !== 'undefined' + ? ApplicationStateSchema.fromEncodingData( + data.get('local-state-schema') ) : undefined, }); - /* eslint-enable dot-notation */ } } /** * */ -export class ApplicationResponse extends BaseModel { +export class ApplicationResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'application', + valueSchema: new OptionalSchema(Application.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; /** * Application index and its parameters @@ -1316,47 +1863,71 @@ export class ApplicationResponse extends BaseModel { currentRound: number | bigint; application?: Application; }) { - super(); - this.currentRound = currentRound; + this.currentRound = ensureBigInt(currentRound); this.application = application; + } - this.attribute_map = { - currentRound: 'current-round', - application: 'application', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['current-round', this.currentRound], + [ + 'application', + typeof this.application !== 'undefined' + ? this.application.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): ApplicationResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationResponse: ${data}`); + } return new ApplicationResponse({ - currentRound: data['current-round'], + currentRound: data.get('current-round'), application: - typeof data['application'] !== 'undefined' - ? Application.from_obj_for_encoding(data['application']) + typeof data.get('application') !== 'undefined' + ? Application.fromEncodingData(data.get('application')) : undefined, }); - /* eslint-enable dot-notation */ } } /** * Specifies maximums on the number of each type that may be stored. */ -export class ApplicationStateSchema extends BaseModel { +export class ApplicationStateSchema implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'num-byte-slice', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'num-uint', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * number of byte slices. */ - public numByteSlice: number | bigint; + public numByteSlice: number; /** * number of uints. */ - public numUint: number | bigint; + public numUint: number; /** * Creates a new `ApplicationStateSchema` object. @@ -1370,45 +1941,69 @@ export class ApplicationStateSchema extends BaseModel { 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}`); + this.numByteSlice = ensureSafeInteger(numByteSlice); + this.numUint = ensureSafeInteger(numUint); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationStateSchema.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['num-byte-slice', this.numByteSlice], + ['num-uint', this.numUint], + ]); + } + + static fromEncodingData(data: unknown): ApplicationStateSchema { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationStateSchema: ${data}`); + } return new ApplicationStateSchema({ - numByteSlice: data['num-byte-slice'], - numUint: data['num-uint'], + numByteSlice: data.get('num-byte-slice'), + numUint: data.get('num-uint'), }); - /* eslint-enable dot-notation */ } } /** * */ -export class ApplicationsResponse extends BaseModel { +export class ApplicationsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'applications', + valueSchema: new ArraySchema(Application.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public applications: Application[]; /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; /** * Used for pagination, when making another request provide this token with the @@ -1432,48 +2027,78 @@ export class ApplicationsResponse extends BaseModel { currentRound: number | bigint; nextToken?: string; }) { - super(); this.applications = applications; - this.currentRound = currentRound; + this.currentRound = ensureBigInt(currentRound); this.nextToken = nextToken; + } - this.attribute_map = { - applications: 'applications', - currentRound: 'current-round', - nextToken: 'next-token', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationsResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['applications', this.applications.map((v) => v.toEncodingData())], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): ApplicationsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationsResponse: ${data}`); + } return new ApplicationsResponse({ - applications: data['applications'].map(Application.from_obj_for_encoding), - currentRound: data['current-round'], - nextToken: data['next-token'], + applications: (data.get('applications') ?? []).map((v: unknown) => + Application.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), }); - /* eslint-enable dot-notation */ } } /** * Specifies both the unique identifier and the parameters for an asset */ -export class Asset extends BaseModel { +export class Asset implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'index', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'params', + valueSchema: AssetParams.encodingSchema, + omitEmpty: true, + }, + { + key: 'created-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'destroyed-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * unique asset identifier */ - public index: number | bigint; + public index: bigint; /** * AssetParams specifies the parameters for an asset. @@ -1486,7 +2111,7 @@ export class Asset extends BaseModel { /** * Round during which this asset was created. */ - public createdAtRound?: number | bigint; + public createdAtRound?: bigint; /** * Whether or not this asset is currently deleted. @@ -1496,7 +2121,7 @@ export class Asset extends BaseModel { /** * Round during which this asset was destroyed. */ - public destroyedAtRound?: number | bigint; + public destroyedAtRound?: bigint; /** * Creates a new `Asset` object. @@ -1522,50 +2147,84 @@ export class Asset extends BaseModel { deleted?: boolean; destroyedAtRound?: number | bigint; }) { - super(); - this.index = index; + this.index = ensureBigInt(index); this.params = params; - this.createdAtRound = createdAtRound; + this.createdAtRound = + typeof createdAtRound === 'undefined' + ? undefined + : ensureBigInt(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}`); + this.destroyedAtRound = + typeof destroyedAtRound === 'undefined' + ? undefined + : ensureBigInt(destroyedAtRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Asset.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['index', this.index], + ['params', this.params.toEncodingData()], + ['created-at-round', this.createdAtRound], + ['deleted', this.deleted], + ['destroyed-at-round', this.destroyedAtRound], + ]); + } + + static fromEncodingData(data: unknown): Asset { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Asset: ${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'], + index: data.get('index'), + params: AssetParams.fromEncodingData(data.get('params') ?? new Map()), + createdAtRound: data.get('created-at-round'), + deleted: data.get('deleted'), + destroyedAtRound: data.get('destroyed-at-round'), }); - /* eslint-enable dot-notation */ } } /** * */ -export class AssetBalancesResponse extends BaseModel { +export class AssetBalancesResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'balances', + valueSchema: new ArraySchema(MiniAssetHolding.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public balances: MiniAssetHolding[]; /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; /** * Used for pagination, when making another request provide this token with the @@ -1589,37 +2248,35 @@ export class AssetBalancesResponse extends BaseModel { currentRound: number | bigint; nextToken?: string; }) { - super(); this.balances = balances; - this.currentRound = currentRound; + this.currentRound = ensureBigInt(currentRound); this.nextToken = nextToken; + } - this.attribute_map = { - balances: 'balances', - currentRound: 'current-round', - nextToken: 'next-token', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetBalancesResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['balances', this.balances.map((v) => v.toEncodingData())], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): AssetBalancesResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetBalancesResponse: ${data}`); + } return new AssetBalancesResponse({ - balances: data['balances'].map(MiniAssetHolding.from_obj_for_encoding), - currentRound: data['current-round'], - nextToken: data['next-token'], + balances: (data.get('balances') ?? []).map((v: unknown) => + MiniAssetHolding.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), }); - /* eslint-enable dot-notation */ } } @@ -1628,16 +2285,45 @@ export class AssetBalancesResponse extends BaseModel { * Definition: * data/basics/userBalance.go : AssetHolding */ -export class AssetHolding extends BaseModel { +export class AssetHolding implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'asset-id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'is-frozen', valueSchema: new BooleanSchema(), omitEmpty: true }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'opted-in-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'opted-out-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * number of units held. */ - public amount: number | bigint; + public amount: bigint; /** * Asset ID of the holding. */ - public assetId: number | bigint; + public assetId: bigint; /** * whether or not the holding is frozen. @@ -1652,12 +2338,12 @@ export class AssetHolding extends BaseModel { /** * Round during which the account opted into this asset holding. */ - public optedInAtRound?: number | bigint; + public optedInAtRound?: bigint; /** * Round during which the account opted out of this asset holding. */ - public optedOutAtRound?: number | bigint; + public optedOutAtRound?: bigint; /** * Creates a new `AssetHolding` object. @@ -1683,57 +2369,87 @@ export class AssetHolding extends BaseModel { optedInAtRound?: number | bigint; optedOutAtRound?: number | bigint; }) { - super(); - this.amount = amount; - this.assetId = assetId; + this.amount = ensureBigInt(amount); + this.assetId = ensureBigInt(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}` - ); + this.optedInAtRound = + typeof optedInAtRound === 'undefined' + ? undefined + : ensureBigInt(optedInAtRound); + this.optedOutAtRound = + typeof optedOutAtRound === 'undefined' + ? undefined + : ensureBigInt(optedOutAtRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetHolding.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['amount', this.amount], + ['asset-id', this.assetId], + ['is-frozen', this.isFrozen], + ['deleted', this.deleted], + ['opted-in-at-round', this.optedInAtRound], + ['opted-out-at-round', this.optedOutAtRound], + ]); + } + + static fromEncodingData(data: unknown): AssetHolding { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetHolding: ${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'], + amount: data.get('amount'), + assetId: data.get('asset-id'), + isFrozen: data.get('is-frozen'), + deleted: data.get('deleted'), + optedInAtRound: data.get('opted-in-at-round'), + optedOutAtRound: data.get('opted-out-at-round'), }); - /* eslint-enable dot-notation */ } } /** * */ -export class AssetHoldingsResponse extends BaseModel { +export class AssetHoldingsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'assets', + valueSchema: new ArraySchema(AssetHolding.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public assets: AssetHolding[]; /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; /** * Used for pagination, when making another request provide this token with the @@ -1757,37 +2473,35 @@ export class AssetHoldingsResponse extends BaseModel { currentRound: number | bigint; nextToken?: string; }) { - super(); this.assets = assets; - this.currentRound = currentRound; + this.currentRound = ensureBigInt(currentRound); this.nextToken = nextToken; + } - this.attribute_map = { - assets: 'assets', - currentRound: 'current-round', - nextToken: 'next-token', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetHoldingsResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['assets', this.assets.map((v) => v.toEncodingData())], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): AssetHoldingsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetHoldingsResponse: ${data}`); + } return new AssetHoldingsResponse({ - assets: data['assets'].map(AssetHolding.from_obj_for_encoding), - currentRound: data['current-round'], - nextToken: data['next-token'], + assets: (data.get('assets') ?? []).map((v: unknown) => + AssetHolding.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), }); - /* eslint-enable dot-notation */ } } @@ -1797,7 +2511,81 @@ export class AssetHoldingsResponse extends BaseModel { * Definition: * data/transactions/asset.go : AssetParams */ -export class AssetParams extends BaseModel { +export class AssetParams implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'creator', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'decimals', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'total', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'clawback', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'default-frozen', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'freeze', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'manager', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'metadata-hash', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'name', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'name-b64', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'reserve', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'unit-name', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'unit-name-b64', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'url', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'url-b64', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * 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 @@ -1811,12 +2599,12 @@ export class AssetParams extends BaseModel { * 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; + public decimals: number; /** * The total number of units of this asset. */ - public total: number | bigint; + public total: bigint; /** * Address of account used to clawback holdings of this asset. If empty, clawback @@ -1946,88 +2734,100 @@ export class AssetParams extends BaseModel { url?: string; urlB64?: string | Uint8Array; }) { - super(); this.creator = creator; - this.decimals = decimals; - this.total = total; + this.decimals = ensureSafeInteger(decimals); + this.total = ensureBigInt(total); this.clawback = clawback; this.defaultFrozen = defaultFrozen; this.freeze = freeze; this.manager = manager; this.metadataHash = typeof metadataHash === 'string' - ? new Uint8Array(Buffer.from(metadataHash, 'base64')) + ? base64ToBytes(metadataHash) : metadataHash; this.name = name; this.nameB64 = - typeof nameB64 === 'string' - ? new Uint8Array(Buffer.from(nameB64, 'base64')) - : nameB64; + typeof nameB64 === 'string' ? base64ToBytes(nameB64) : nameB64; this.reserve = reserve; this.unitName = unitName; this.unitNameB64 = typeof unitNameB64 === 'string' - ? new Uint8Array(Buffer.from(unitNameB64, 'base64')) + ? base64ToBytes(unitNameB64) : 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}`); + this.urlB64 = typeof urlB64 === 'string' ? base64ToBytes(urlB64) : urlB64; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetParams.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['creator', this.creator], + ['decimals', this.decimals], + ['total', this.total], + ['clawback', this.clawback], + ['default-frozen', this.defaultFrozen], + ['freeze', this.freeze], + ['manager', this.manager], + ['metadata-hash', this.metadataHash], + ['name', this.name], + ['name-b64', this.nameB64], + ['reserve', this.reserve], + ['unit-name', this.unitName], + ['unit-name-b64', this.unitNameB64], + ['url', this.url], + ['url-b64', this.urlB64], + ]); + } + + static fromEncodingData(data: unknown): AssetParams { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetParams: ${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'], + creator: data.get('creator'), + decimals: data.get('decimals'), + total: data.get('total'), + clawback: data.get('clawback'), + defaultFrozen: data.get('default-frozen'), + freeze: data.get('freeze'), + manager: data.get('manager'), + metadataHash: data.get('metadata-hash'), + name: data.get('name'), + nameB64: data.get('name-b64'), + reserve: data.get('reserve'), + unitName: data.get('unit-name'), + unitNameB64: data.get('unit-name-b64'), + url: data.get('url'), + urlB64: data.get('url-b64'), }); - /* eslint-enable dot-notation */ } } /** * */ -export class AssetResponse extends BaseModel { +export class AssetResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'asset', valueSchema: Asset.encodingSchema, omitEmpty: true }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Specifies both the unique identifier and the parameters for an asset */ @@ -2036,7 +2836,7 @@ export class AssetResponse extends BaseModel { /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; /** * Creates a new `AssetResponse` object. @@ -2050,43 +2850,69 @@ export class AssetResponse extends BaseModel { asset: Asset; currentRound: number | bigint; }) { - super(); this.asset = asset; - this.currentRound = currentRound; + this.currentRound = ensureBigInt(currentRound); + } - this.attribute_map = { - asset: 'asset', - currentRound: 'current-round', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['asset', this.asset.toEncodingData()], + ['current-round', this.currentRound], + ]); + } + + static fromEncodingData(data: unknown): AssetResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetResponse: ${data}`); + } return new AssetResponse({ - asset: Asset.from_obj_for_encoding(data['asset']), - currentRound: data['current-round'], + asset: Asset.fromEncodingData(data.get('asset') ?? new Map()), + currentRound: data.get('current-round'), }); - /* eslint-enable dot-notation */ } } /** * */ -export class AssetsResponse extends BaseModel { +export class AssetsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'assets', + valueSchema: new ArraySchema(Asset.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public assets: Asset[]; /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; /** * Used for pagination, when making another request provide this token with the @@ -2110,35 +2936,35 @@ export class AssetsResponse extends BaseModel { currentRound: number | bigint; nextToken?: string; }) { - super(); this.assets = assets; - this.currentRound = currentRound; + this.currentRound = ensureBigInt(currentRound); this.nextToken = nextToken; + } - this.attribute_map = { - assets: 'assets', - currentRound: 'current-round', - nextToken: 'next-token', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetsResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['assets', this.assets.map((v) => v.toEncodingData())], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): AssetsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetsResponse: ${data}`); + } return new AssetsResponse({ - assets: data['assets'].map(Asset.from_obj_for_encoding), - currentRound: data['current-round'], - nextToken: data['next-token'], + assets: (data.get('assets') ?? []).map((v: unknown) => + Asset.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), }); - /* eslint-enable dot-notation */ } } @@ -2147,7 +2973,101 @@ export class AssetsResponse extends BaseModel { * Definition: * data/bookkeeping/block.go : Block */ -export class Block extends BaseModel { +export class Block implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'genesis-hash', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { key: 'genesis-id', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'previous-block-hash', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'seed', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'timestamp', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'transactions-root', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'transactions-root-sha256', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'bonus', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'fees-collected', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'participation-updates', + valueSchema: new OptionalSchema(ParticipationUpdates.encodingSchema), + omitEmpty: true, + }, + { + key: 'proposer', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'proposer-payout', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'rewards', + valueSchema: new OptionalSchema(BlockRewards.encodingSchema), + omitEmpty: true, + }, + { + key: 'state-proof-tracking', + valueSchema: new OptionalSchema( + new ArraySchema(StateProofTracking.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'transactions', + valueSchema: new OptionalSchema( + new ArraySchema(Transaction.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'txn-counter', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'upgrade-state', + valueSchema: new OptionalSchema(BlockUpgradeState.encodingSchema), + omitEmpty: true, + }, + { + key: 'upgrade-vote', + valueSchema: new OptionalSchema(BlockUpgradeVote.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (gh) hash to which this block belongs. */ @@ -2166,7 +3086,7 @@ export class Block extends BaseModel { /** * (rnd) Current round on which this block was appended to the chain. */ - public round: number | bigint; + public round: bigint; /** * (seed) Sortition seed. @@ -2176,7 +3096,7 @@ export class Block extends BaseModel { /** * (ts) Block creation timestamp in seconds since eposh */ - public timestamp: number | bigint; + public timestamp: number; /** * (txn) TransactionsRoot authenticates the set of transactions appearing in the @@ -2199,12 +3119,12 @@ export class Block extends BaseModel { /** * the potential bonus payout for this block. */ - public bonus?: number | bigint; + public bonus?: number; /** * the sum of all fees paid by transactions in this block. */ - public feesCollected?: number | bigint; + public feesCollected?: number; /** * Participation account data that needs to be checked/acted on by the network. @@ -2214,12 +3134,12 @@ export class Block extends BaseModel { /** * the proposer of this block. */ - public proposer?: string; + public proposer?: Address; /** * the actual amount transferred to the proposer from the fee sink. */ - public proposerPayout?: number | bigint; + public proposerPayout?: number; /** * Fields relating to rewards, @@ -2243,7 +3163,7 @@ export class Block extends BaseModel { * committed after this block. It is 0 when no transactions have ever been * committed (since TxnCounter started being supported). */ - public txnCounter?: number | bigint; + public txnCounter?: number; /** * Fields relating to a protocol upgrade. @@ -2321,7 +3241,7 @@ export class Block extends BaseModel { bonus?: number | bigint; feesCollected?: number | bigint; participationUpdates?: ParticipationUpdates; - proposer?: string; + proposer?: Address | string; proposerPayout?: number | bigint; rewards?: BlockRewards; stateProofTracking?: StateProofTracking[]; @@ -2330,147 +3250,207 @@ export class Block extends BaseModel { upgradeState?: BlockUpgradeState; upgradeVote?: BlockUpgradeVote; }) { - super(); this.genesisHash = typeof genesisHash === 'string' - ? new Uint8Array(Buffer.from(genesisHash, 'base64')) + ? base64ToBytes(genesisHash) : genesisHash; this.genesisId = genesisId; this.previousBlockHash = typeof previousBlockHash === 'string' - ? new Uint8Array(Buffer.from(previousBlockHash, 'base64')) + ? base64ToBytes(previousBlockHash) : previousBlockHash; - this.round = round; - this.seed = - typeof seed === 'string' - ? new Uint8Array(Buffer.from(seed, 'base64')) - : seed; - this.timestamp = timestamp; + this.round = ensureBigInt(round); + this.seed = typeof seed === 'string' ? base64ToBytes(seed) : seed; + this.timestamp = ensureSafeInteger(timestamp); this.transactionsRoot = typeof transactionsRoot === 'string' - ? new Uint8Array(Buffer.from(transactionsRoot, 'base64')) + ? base64ToBytes(transactionsRoot) : transactionsRoot; this.transactionsRootSha256 = typeof transactionsRootSha256 === 'string' - ? new Uint8Array(Buffer.from(transactionsRootSha256, 'base64')) + ? base64ToBytes(transactionsRootSha256) : transactionsRootSha256; - this.bonus = bonus; - this.feesCollected = feesCollected; + this.bonus = + typeof bonus === 'undefined' ? undefined : ensureSafeInteger(bonus); + this.feesCollected = + typeof feesCollected === 'undefined' + ? undefined + : ensureSafeInteger(feesCollected); this.participationUpdates = participationUpdates; - this.proposer = proposer; - this.proposerPayout = proposerPayout; + this.proposer = + typeof proposer === 'string' ? Address.fromString(proposer) : proposer; + this.proposerPayout = + typeof proposerPayout === 'undefined' + ? undefined + : ensureSafeInteger(proposerPayout); this.rewards = rewards; this.stateProofTracking = stateProofTracking; this.transactions = transactions; - this.txnCounter = txnCounter; + this.txnCounter = + typeof txnCounter === 'undefined' + ? undefined + : ensureSafeInteger(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', - bonus: 'bonus', - feesCollected: 'fees-collected', - participationUpdates: 'participation-updates', - proposer: 'proposer', - proposerPayout: 'proposer-payout', - 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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Block.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['genesis-hash', this.genesisHash], + ['genesis-id', this.genesisId], + ['previous-block-hash', this.previousBlockHash], + ['round', this.round], + ['seed', this.seed], + ['timestamp', this.timestamp], + ['transactions-root', this.transactionsRoot], + ['transactions-root-sha256', this.transactionsRootSha256], + ['bonus', this.bonus], + ['fees-collected', this.feesCollected], + [ + 'participation-updates', + typeof this.participationUpdates !== 'undefined' + ? this.participationUpdates.toEncodingData() + : undefined, + ], + [ + 'proposer', + typeof this.proposer !== 'undefined' + ? this.proposer.toString() + : undefined, + ], + ['proposer-payout', this.proposerPayout], + [ + 'rewards', + typeof this.rewards !== 'undefined' + ? this.rewards.toEncodingData() + : undefined, + ], + [ + 'state-proof-tracking', + typeof this.stateProofTracking !== 'undefined' + ? this.stateProofTracking.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'transactions', + typeof this.transactions !== 'undefined' + ? this.transactions.map((v) => v.toEncodingData()) + : undefined, + ], + ['txn-counter', this.txnCounter], + [ + 'upgrade-state', + typeof this.upgradeState !== 'undefined' + ? this.upgradeState.toEncodingData() + : undefined, + ], + [ + 'upgrade-vote', + typeof this.upgradeVote !== 'undefined' + ? this.upgradeVote.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): Block { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Block: ${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'], - bonus: data['bonus'], - feesCollected: data['fees-collected'], + genesisHash: data.get('genesis-hash'), + genesisId: data.get('genesis-id'), + previousBlockHash: data.get('previous-block-hash'), + round: data.get('round'), + seed: data.get('seed'), + timestamp: data.get('timestamp'), + transactionsRoot: data.get('transactions-root'), + transactionsRootSha256: data.get('transactions-root-sha256'), + bonus: data.get('bonus'), + feesCollected: data.get('fees-collected'), participationUpdates: - typeof data['participation-updates'] !== 'undefined' - ? ParticipationUpdates.from_obj_for_encoding( - data['participation-updates'] + typeof data.get('participation-updates') !== 'undefined' + ? ParticipationUpdates.fromEncodingData( + data.get('participation-updates') ) : undefined, - proposer: data['proposer'], - proposerPayout: data['proposer-payout'], + proposer: data.get('proposer'), + proposerPayout: data.get('proposer-payout'), rewards: - typeof data['rewards'] !== 'undefined' - ? BlockRewards.from_obj_for_encoding(data['rewards']) + typeof data.get('rewards') !== 'undefined' + ? BlockRewards.fromEncodingData(data.get('rewards')) : undefined, stateProofTracking: - typeof data['state-proof-tracking'] !== 'undefined' - ? data['state-proof-tracking'].map( - StateProofTracking.from_obj_for_encoding - ) + typeof data.get('state-proof-tracking') !== 'undefined' + ? data + .get('state-proof-tracking') + .map((v: unknown) => StateProofTracking.fromEncodingData(v)) : undefined, transactions: - typeof data['transactions'] !== 'undefined' - ? data['transactions'].map(Transaction.from_obj_for_encoding) + typeof data.get('transactions') !== 'undefined' + ? data + .get('transactions') + .map((v: unknown) => Transaction.fromEncodingData(v)) : undefined, - txnCounter: data['txn-counter'], + txnCounter: data.get('txn-counter'), upgradeState: - typeof data['upgrade-state'] !== 'undefined' - ? BlockUpgradeState.from_obj_for_encoding(data['upgrade-state']) + typeof data.get('upgrade-state') !== 'undefined' + ? BlockUpgradeState.fromEncodingData(data.get('upgrade-state')) : undefined, upgradeVote: - typeof data['upgrade-vote'] !== 'undefined' - ? BlockUpgradeVote.from_obj_for_encoding(data['upgrade-vote']) + typeof data.get('upgrade-vote') !== 'undefined' + ? BlockUpgradeVote.fromEncodingData(data.get('upgrade-vote')) : undefined, }); - /* eslint-enable dot-notation */ } } /** * Fields relating to rewards, */ -export class BlockRewards extends BaseModel { +export class BlockRewards implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'fee-sink', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'rewards-calculation-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'rewards-level', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'rewards-pool', + valueSchema: new StringSchema(), + omitEmpty: true, + }, + { + key: 'rewards-rate', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'rewards-residue', + valueSchema: new Uint64Schema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (fees) accepts transaction fees, it can only spend to the incentive pool. */ @@ -2480,13 +3460,13 @@ export class BlockRewards extends BaseModel { * (rwcalr) number of leftover MicroAlgos after the distribution of rewards-rate * MicroAlgos for every reward unit in the next round. */ - public rewardsCalculationRound: number | bigint; + public rewardsCalculationRound: bigint; /** * (earn) How many rewards, in MicroAlgos, have been distributed to each RewardUnit * of MicroAlgos since genesis. */ - public rewardsLevel: number | bigint; + public rewardsLevel: bigint; /** * (rwd) accepts periodic injections from the fee-sink and continually @@ -2498,13 +3478,13 @@ export class BlockRewards extends BaseModel { * (rate) Number of new MicroAlgos added to the participation stake from rewards at * the next round. */ - public rewardsRate: number | bigint; + public rewardsRate: 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; + public rewardsResidue: bigint; /** * Creates a new `BlockRewards` object. @@ -2535,65 +3515,85 @@ export class BlockRewards extends BaseModel { rewardsRate: number | bigint; rewardsResidue: number | bigint; }) { - super(); this.feeSink = feeSink; - this.rewardsCalculationRound = rewardsCalculationRound; - this.rewardsLevel = rewardsLevel; + this.rewardsCalculationRound = ensureBigInt(rewardsCalculationRound); + this.rewardsLevel = ensureBigInt(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}` - ); + this.rewardsRate = ensureBigInt(rewardsRate); + this.rewardsResidue = ensureBigInt(rewardsResidue); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BlockRewards.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['fee-sink', this.feeSink], + ['rewards-calculation-round', this.rewardsCalculationRound], + ['rewards-level', this.rewardsLevel], + ['rewards-pool', this.rewardsPool], + ['rewards-rate', this.rewardsRate], + ['rewards-residue', this.rewardsResidue], + ]); + } + + static fromEncodingData(data: unknown): BlockRewards { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockRewards: ${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'], + feeSink: data.get('fee-sink'), + rewardsCalculationRound: data.get('rewards-calculation-round'), + rewardsLevel: data.get('rewards-level'), + rewardsPool: data.get('rewards-pool'), + rewardsRate: data.get('rewards-rate'), + rewardsResidue: data.get('rewards-residue'), }); - /* eslint-enable dot-notation */ } } /** * Fields relating to a protocol upgrade. */ -export class BlockUpgradeState extends BaseModel { +export class BlockUpgradeState implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'current-protocol', + valueSchema: new StringSchema(), + omitEmpty: true, + }, + { + key: 'next-protocol', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'next-protocol-approvals', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'next-protocol-switch-on', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'next-protocol-vote-before', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (proto) The current protocol version. */ @@ -2607,18 +3607,18 @@ export class BlockUpgradeState extends BaseModel { /** * (nextyes) Number of blocks which approved the protocol upgrade. */ - public nextProtocolApprovals?: number | bigint; + public nextProtocolApprovals?: number; /** * (nextswitch) Round on which the protocol upgrade will take effect. */ - public nextProtocolSwitchOn?: number | bigint; + public nextProtocolSwitchOn?: bigint; /** * (nextbefore) Deadline round for this protocol upgrade (No votes will be consider * after this round). */ - public nextProtocolVoteBefore?: number | bigint; + public nextProtocolVoteBefore?: bigint; /** * Creates a new `BlockUpgradeState` object. @@ -2642,44 +3642,81 @@ export class BlockUpgradeState extends BaseModel { 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}` - ); + this.nextProtocolApprovals = + typeof nextProtocolApprovals === 'undefined' + ? undefined + : ensureSafeInteger(nextProtocolApprovals); + this.nextProtocolSwitchOn = + typeof nextProtocolSwitchOn === 'undefined' + ? undefined + : ensureBigInt(nextProtocolSwitchOn); + this.nextProtocolVoteBefore = + typeof nextProtocolVoteBefore === 'undefined' + ? undefined + : ensureBigInt(nextProtocolVoteBefore); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BlockUpgradeState.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['current-protocol', this.currentProtocol], + ['next-protocol', this.nextProtocol], + ['next-protocol-approvals', this.nextProtocolApprovals], + ['next-protocol-switch-on', this.nextProtocolSwitchOn], + ['next-protocol-vote-before', this.nextProtocolVoteBefore], + ]); + } + + static fromEncodingData(data: unknown): BlockUpgradeState { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockUpgradeState: ${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'], + currentProtocol: data.get('current-protocol'), + nextProtocol: data.get('next-protocol'), + nextProtocolApprovals: data.get('next-protocol-approvals'), + nextProtocolSwitchOn: data.get('next-protocol-switch-on'), + nextProtocolVoteBefore: data.get('next-protocol-vote-before'), }); - /* eslint-enable dot-notation */ } } /** * Fields relating to voting for a protocol upgrade. */ -export class BlockUpgradeVote extends BaseModel { +export class BlockUpgradeVote implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'upgrade-approve', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'upgrade-delay', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'upgrade-propose', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (upgradeyes) Indicates a yes vote for the current proposal. */ @@ -2688,7 +3725,7 @@ export class BlockUpgradeVote extends BaseModel { /** * (upgradedelay) Indicates the time between acceptance and execution. */ - public upgradeDelay?: number | bigint; + public upgradeDelay?: bigint; /** * (upgradeprop) Indicates a proposed upgrade. @@ -2710,34 +3747,57 @@ export class BlockUpgradeVote extends BaseModel { upgradeDelay?: number | bigint; upgradePropose?: string; }) { - super(); this.upgradeApprove = upgradeApprove; - this.upgradeDelay = upgradeDelay; + this.upgradeDelay = + typeof upgradeDelay === 'undefined' + ? undefined + : ensureBigInt(upgradeDelay); this.upgradePropose = upgradePropose; + } - this.attribute_map = { - upgradeApprove: 'upgrade-approve', - upgradeDelay: 'upgrade-delay', - upgradePropose: 'upgrade-propose', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BlockUpgradeVote.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): BlockUpgradeVote { - /* eslint-disable dot-notation */ + toEncodingData(): Map { + return new Map([ + ['upgrade-approve', this.upgradeApprove], + ['upgrade-delay', this.upgradeDelay], + ['upgrade-propose', this.upgradePropose], + ]); + } + + static fromEncodingData(data: unknown): BlockUpgradeVote { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockUpgradeVote: ${data}`); + } return new BlockUpgradeVote({ - upgradeApprove: data['upgrade-approve'], - upgradeDelay: data['upgrade-delay'], - upgradePropose: data['upgrade-propose'], + upgradeApprove: data.get('upgrade-approve'), + upgradeDelay: data.get('upgrade-delay'), + upgradePropose: data.get('upgrade-propose'), }); - /* eslint-enable dot-notation */ } } /** * Box name and its content. */ -export class Box extends BaseModel { +export class Box implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'name', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'value', valueSchema: new ByteArraySchema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * (name) box name, base64 encoded */ @@ -2746,7 +3806,7 @@ export class Box extends BaseModel { /** * The round for which this information is relevant */ - public round: number | bigint; + public round: bigint; /** * (value) box value, base64 encoded. @@ -2768,46 +3828,54 @@ export class Box extends BaseModel { round: number | bigint; value: string | Uint8Array; }) { - super(); - this.name = - typeof name === 'string' - ? new Uint8Array(Buffer.from(name, 'base64')) - : name; - this.round = round; - this.value = - typeof value === 'string' - ? new Uint8Array(Buffer.from(value, 'base64')) - : value; - - this.attribute_map = { - name: 'name', - round: 'round', - 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['round'] === 'undefined') - throw new Error(`Response is missing required field 'round': ${data}`); - if (typeof data['value'] === 'undefined') - throw new Error(`Response is missing required field 'value': ${data}`); + this.name = typeof name === 'string' ? base64ToBytes(name) : name; + this.round = ensureBigInt(round); + this.value = typeof value === 'string' ? base64ToBytes(value) : value; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Box.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['name', this.name], + ['round', this.round], + ['value', this.value], + ]); + } + + static fromEncodingData(data: unknown): Box { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Box: ${data}`); + } return new Box({ - name: data['name'], - round: data['round'], - value: data['value'], + name: data.get('name'), + round: data.get('round'), + value: data.get('value'), }); - /* eslint-enable dot-notation */ } } /** * Box descriptor describes an app box without a value. */ -export class BoxDescriptor extends BaseModel { +export class BoxDescriptor implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'name', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + /** * Base64 encoded box name */ @@ -2818,37 +3886,62 @@ export class BoxDescriptor extends BaseModel { * @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}`); + this.name = typeof name === 'string' ? base64ToBytes(name) : name; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BoxDescriptor.encodingSchema; + } + + toEncodingData(): Map { + return new Map([['name', this.name]]); + } + + static fromEncodingData(data: unknown): BoxDescriptor { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BoxDescriptor: ${data}`); + } return new BoxDescriptor({ - name: data['name'], + name: data.get('name'), }); - /* eslint-enable dot-notation */ } } /** * Box names of an application */ -export class BoxesResponse extends BaseModel { +export class BoxesResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'application-id', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'boxes', + valueSchema: new ArraySchema(BoxDescriptor.encodingSchema), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (appidx) application index. */ - public applicationId: number | bigint; + public applicationId: bigint; public boxes: BoxDescriptor[]; @@ -2874,89 +3967,134 @@ export class BoxesResponse extends BaseModel { boxes: BoxDescriptor[]; nextToken?: string; }) { - super(); - this.applicationId = applicationId; + this.applicationId = ensureBigInt(applicationId); this.boxes = boxes; this.nextToken = nextToken; + } - this.attribute_map = { - applicationId: 'application-id', - boxes: 'boxes', - nextToken: 'next-token', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BoxesResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['application-id', this.applicationId], + ['boxes', this.boxes.map((v) => v.toEncodingData())], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): BoxesResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BoxesResponse: ${data}`); + } return new BoxesResponse({ - applicationId: data['application-id'], - boxes: data['boxes'].map(BoxDescriptor.from_obj_for_encoding), - nextToken: data['next-token'], + applicationId: data.get('application-id'), + boxes: (data.get('boxes') ?? []).map((v: unknown) => + BoxDescriptor.fromEncodingData(v) + ), + nextToken: data.get('next-token'), }); - /* eslint-enable dot-notation */ } } /** * Response for errors */ -export class ErrorResponse extends BaseModel { +export class ErrorResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'message', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'data', + valueSchema: new OptionalSchema(UntypedValue.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public message: string; - public data?: Record; + public data?: UntypedValue; /** * Creates a new `ErrorResponse` object. * @param message - * @param data - */ - constructor({ - message, - data, - }: { - message: string; - data?: Record; - }) { - super(); + constructor({ message, data }: { message: string; data?: UntypedValue }) { this.message = message; this.data = data; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ErrorResponse.encodingSchema; + } - this.attribute_map = { - message: 'message', - data: 'data', - }; + toEncodingData(): Map { + return new Map([ + ['message', this.message], + [ + 'data', + typeof this.data !== 'undefined' + ? this.data.toEncodingData() + : undefined, + ], + ]); } - // 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}`); + static fromEncodingData(data: unknown): ErrorResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ErrorResponse: ${data}`); + } return new ErrorResponse({ - message: data['message'], - data: data['data'], + message: data.get('message'), + data: + typeof data.get('data') !== 'undefined' + ? UntypedValue.fromEncodingData(data.get('data')) + : undefined, }); - /* eslint-enable dot-notation */ } } /** * Represents a TEAL value delta. */ -export class EvalDelta extends BaseModel { +export class EvalDelta implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'action', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'bytes', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'uint', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (at) delta action. */ - public action: number | bigint; + public action: number; /** * (bs) bytes value. @@ -2966,7 +4104,7 @@ export class EvalDelta extends BaseModel { /** * (ui) uint value. */ - public uint?: number | bigint; + public uint?: bigint; /** * Creates a new `EvalDelta` object. @@ -2983,36 +4121,53 @@ export class EvalDelta extends BaseModel { bytes?: string; uint?: number | bigint; }) { - super(); - this.action = action; + this.action = ensureSafeInteger(action); this.bytes = bytes; - this.uint = uint; + this.uint = typeof uint === 'undefined' ? undefined : ensureBigInt(uint); + } - this.attribute_map = { - action: 'action', - bytes: 'bytes', - uint: 'uint', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return EvalDelta.encodingSchema; } - // 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}`); + toEncodingData(): Map { + return new Map([ + ['action', this.action], + ['bytes', this.bytes], + ['uint', this.uint], + ]); + } + + static fromEncodingData(data: unknown): EvalDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded EvalDelta: ${data}`); + } return new EvalDelta({ - action: data['action'], - bytes: data['bytes'], - uint: data['uint'], + action: data.get('action'), + bytes: data.get('bytes'), + uint: data.get('uint'), }); - /* eslint-enable dot-notation */ } } /** * Key-value pairs for StateDelta. */ -export class EvalDeltaKeyValue extends BaseModel { +export class EvalDeltaKeyValue implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'key', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'value', valueSchema: EvalDelta.encodingSchema, omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + public key: string; /** @@ -3026,78 +4181,133 @@ export class EvalDeltaKeyValue extends BaseModel { * @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 class-methods-use-this + getEncodingSchema(): Schema { + return EvalDeltaKeyValue.encodingSchema; } - // 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}`); + toEncodingData(): Map { + return new Map([ + ['key', this.key], + ['value', this.value.toEncodingData()], + ]); + } + + static fromEncodingData(data: unknown): EvalDeltaKeyValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded EvalDeltaKeyValue: ${data}`); + } return new EvalDeltaKeyValue({ - key: data['key'], - value: EvalDelta.from_obj_for_encoding(data['value']), + key: data.get('key'), + value: EvalDelta.fromEncodingData(data.get('value') ?? new Map()), }); - /* eslint-enable dot-notation */ } } -export class HashFactory extends BaseModel { +export class HashFactory implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'hash-type', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + /** * (t) */ - public hashType?: number | bigint; + public hashType?: number; /** * Creates a new `HashFactory` object. * @param hashType - (t) */ constructor({ hashType }: { hashType?: number | bigint }) { - super(); - this.hashType = hashType; + this.hashType = + typeof hashType === 'undefined' ? undefined : ensureSafeInteger(hashType); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return HashFactory.encodingSchema; + } - this.attribute_map = { - hashType: 'hash-type', - }; + toEncodingData(): Map { + return new Map([['hash-type', this.hashType]]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): HashFactory { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): HashFactory { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded HashFactory: ${data}`); + } return new HashFactory({ - hashType: data['hash-type'], + hashType: data.get('hash-type'), }); - /* eslint-enable dot-notation */ } } /** * A health check response. */ -export class HealthCheck extends BaseModel { +export class HealthCheck implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'db-available', + valueSchema: new BooleanSchema(), + omitEmpty: true, + }, + { + key: 'is-migrating', + valueSchema: new BooleanSchema(), + omitEmpty: true, + }, + { key: 'message', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'version', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'data', + valueSchema: new OptionalSchema(UntypedValue.encodingSchema), + omitEmpty: true, + }, + { + key: 'errors', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public dbAvailable: boolean; public isMigrating: boolean; public message: string; - public round: number | bigint; + public round: bigint; /** * Current version. */ public version: string; - public data?: Record; + public data?: UntypedValue; public errors?: string[]; @@ -3125,60 +4335,96 @@ export class HealthCheck extends BaseModel { message: string; round: number | bigint; version: string; - data?: Record; + data?: UntypedValue; errors?: string[]; }) { - super(); this.dbAvailable = dbAvailable; this.isMigrating = isMigrating; this.message = message; - this.round = round; + this.round = ensureBigInt(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}`); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return HealthCheck.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['db-available', this.dbAvailable], + ['is-migrating', this.isMigrating], + ['message', this.message], + ['round', this.round], + ['version', this.version], + [ + 'data', + typeof this.data !== 'undefined' + ? this.data.toEncodingData() + : undefined, + ], + ['errors', this.errors], + ]); + } + + static fromEncodingData(data: unknown): HealthCheck { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded HealthCheck: ${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'], + dbAvailable: data.get('db-available'), + isMigrating: data.get('is-migrating'), + message: data.get('message'), + round: data.get('round'), + version: data.get('version'), + data: + typeof data.get('data') !== 'undefined' + ? UntypedValue.fromEncodingData(data.get('data')) + : undefined, + errors: data.get('errors'), }); - /* eslint-enable dot-notation */ } } -export class IndexerStateProofMessage extends BaseModel { +export class IndexerStateProofMessage implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'block-headers-commitment', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'first-attested-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'latest-attested-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'ln-proven-weight', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'voters-commitment', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (b) */ @@ -3187,17 +4433,17 @@ export class IndexerStateProofMessage extends BaseModel { /** * (f) */ - public firstAttestedRound?: number | bigint; + public firstAttestedRound?: bigint; /** * (l) */ - public latestAttestedRound?: number | bigint; + public latestAttestedRound?: bigint; /** * (P) */ - public lnProvenWeight?: number | bigint; + public lnProvenWeight?: bigint; /** * (v) @@ -3225,45 +4471,86 @@ export class IndexerStateProofMessage extends BaseModel { lnProvenWeight?: number | bigint; votersCommitment?: string | Uint8Array; }) { - super(); this.blockHeadersCommitment = typeof blockHeadersCommitment === 'string' - ? new Uint8Array(Buffer.from(blockHeadersCommitment, 'base64')) + ? base64ToBytes(blockHeadersCommitment) : blockHeadersCommitment; - this.firstAttestedRound = firstAttestedRound; - this.latestAttestedRound = latestAttestedRound; - this.lnProvenWeight = lnProvenWeight; + this.firstAttestedRound = + typeof firstAttestedRound === 'undefined' + ? undefined + : ensureBigInt(firstAttestedRound); + this.latestAttestedRound = + typeof latestAttestedRound === 'undefined' + ? undefined + : ensureBigInt(latestAttestedRound); + this.lnProvenWeight = + typeof lnProvenWeight === 'undefined' + ? undefined + : ensureBigInt(lnProvenWeight); this.votersCommitment = typeof votersCommitment === 'string' - ? new Uint8Array(Buffer.from(votersCommitment, 'base64')) + ? base64ToBytes(votersCommitment) : votersCommitment; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return IndexerStateProofMessage.encodingSchema; + } - this.attribute_map = { - blockHeadersCommitment: 'block-headers-commitment', - firstAttestedRound: 'first-attested-round', - latestAttestedRound: 'latest-attested-round', - lnProvenWeight: 'ln-proven-weight', - votersCommitment: 'voters-commitment', - }; + toEncodingData(): Map { + return new Map([ + ['block-headers-commitment', this.blockHeadersCommitment], + ['first-attested-round', this.firstAttestedRound], + ['latest-attested-round', this.latestAttestedRound], + ['ln-proven-weight', this.lnProvenWeight], + ['voters-commitment', this.votersCommitment], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): IndexerStateProofMessage { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): IndexerStateProofMessage { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded IndexerStateProofMessage: ${data}`); + } 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'], + blockHeadersCommitment: data.get('block-headers-commitment'), + firstAttestedRound: data.get('first-attested-round'), + latestAttestedRound: data.get('latest-attested-round'), + lnProvenWeight: data.get('ln-proven-weight'), + votersCommitment: data.get('voters-commitment'), }); - /* eslint-enable dot-notation */ } } -export class MerkleArrayProof extends BaseModel { +export class MerkleArrayProof implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'hash-factory', + valueSchema: new OptionalSchema(HashFactory.encodingSchema), + omitEmpty: true, + }, + { + key: 'path', + valueSchema: new OptionalSchema( + new ArraySchema(new ByteArraySchema()) + ), + omitEmpty: true, + }, + { + key: 'tree-depth', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public hashFactory?: HashFactory; /** @@ -3274,7 +4561,7 @@ export class MerkleArrayProof extends BaseModel { /** * (td) */ - public treeDepth?: number | bigint; + public treeDepth?: number; /** * Creates a new `MerkleArrayProof` object. @@ -3291,40 +4578,83 @@ export class MerkleArrayProof extends BaseModel { path?: Uint8Array[]; treeDepth?: number | bigint; }) { - super(); this.hashFactory = hashFactory; this.path = path; - this.treeDepth = treeDepth; + this.treeDepth = + typeof treeDepth === 'undefined' + ? undefined + : ensureSafeInteger(treeDepth); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return MerkleArrayProof.encodingSchema; + } - this.attribute_map = { - hashFactory: 'hash-factory', - path: 'path', - treeDepth: 'tree-depth', - }; + toEncodingData(): Map { + return new Map([ + [ + 'hash-factory', + typeof this.hashFactory !== 'undefined' + ? this.hashFactory.toEncodingData() + : undefined, + ], + ['path', this.path], + ['tree-depth', this.treeDepth], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): MerkleArrayProof { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): MerkleArrayProof { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded MerkleArrayProof: ${data}`); + } return new MerkleArrayProof({ hashFactory: - typeof data['hash-factory'] !== 'undefined' - ? HashFactory.from_obj_for_encoding(data['hash-factory']) + typeof data.get('hash-factory') !== 'undefined' + ? HashFactory.fromEncodingData(data.get('hash-factory')) : undefined, - path: data['path'], - treeDepth: data['tree-depth'], + path: data.get('path'), + treeDepth: data.get('tree-depth'), }); - /* eslint-enable dot-notation */ } } /** * A simplified version of AssetHolding */ -export class MiniAssetHolding extends BaseModel { +export class MiniAssetHolding implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'is-frozen', valueSchema: new BooleanSchema(), omitEmpty: true }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'opted-in-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'opted-out-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public address: string; - public amount: number | bigint; + public amount: bigint; public isFrozen: boolean; @@ -3336,12 +4666,12 @@ export class MiniAssetHolding extends BaseModel { /** * Round during which the account opted into the asset. */ - public optedInAtRound?: number | bigint; + public optedInAtRound?: bigint; /** * Round during which the account opted out of the asset. */ - public optedOutAtRound?: number | bigint; + public optedOutAtRound?: bigint; /** * Creates a new `MiniAssetHolding` object. @@ -3367,51 +4697,76 @@ export class MiniAssetHolding extends BaseModel { optedInAtRound?: number | bigint; optedOutAtRound?: number | bigint; }) { - super(); this.address = address; - this.amount = amount; + this.amount = ensureBigInt(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}` - ); + this.optedInAtRound = + typeof optedInAtRound === 'undefined' + ? undefined + : ensureBigInt(optedInAtRound); + this.optedOutAtRound = + typeof optedOutAtRound === 'undefined' + ? undefined + : ensureBigInt(optedOutAtRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return MiniAssetHolding.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['address', this.address], + ['amount', this.amount], + ['is-frozen', this.isFrozen], + ['deleted', this.deleted], + ['opted-in-at-round', this.optedInAtRound], + ['opted-out-at-round', this.optedOutAtRound], + ]); + } + + static fromEncodingData(data: unknown): MiniAssetHolding { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded MiniAssetHolding: ${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'], + address: data.get('address'), + amount: data.get('amount'), + isFrozen: data.get('is-frozen'), + deleted: data.get('deleted'), + optedInAtRound: data.get('opted-in-at-round'), + optedOutAtRound: data.get('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 { +export class ParticipationUpdates implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'absent-participation-accounts', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + }, + { + key: 'expired-participation-accounts', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (partupabs) a list of online accounts that need to be suspended. */ @@ -3436,26 +4791,30 @@ export class ParticipationUpdates extends BaseModel { absentParticipationAccounts?: string[]; expiredParticipationAccounts?: string[]; }) { - super(); this.absentParticipationAccounts = absentParticipationAccounts; this.expiredParticipationAccounts = expiredParticipationAccounts; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ParticipationUpdates.encodingSchema; + } - this.attribute_map = { - absentParticipationAccounts: 'absent-participation-accounts', - expiredParticipationAccounts: 'expired-participation-accounts', - }; + toEncodingData(): Map { + return new Map([ + ['absent-participation-accounts', this.absentParticipationAccounts], + ['expired-participation-accounts', this.expiredParticipationAccounts], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): ParticipationUpdates { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): ParticipationUpdates { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ParticipationUpdates: ${data}`); + } return new ParticipationUpdates({ - absentParticipationAccounts: data['absent-participation-accounts'], - expiredParticipationAccounts: data['expired-participation-accounts'], + absentParticipationAccounts: data.get('absent-participation-accounts'), + expiredParticipationAccounts: data.get('expired-participation-accounts'), }); - /* eslint-enable dot-notation */ } } @@ -3464,7 +4823,55 @@ export class ParticipationUpdates extends BaseModel { * Definition: * crypto/stateproof/structs.go : StateProof */ -export class StateProofFields extends BaseModel { +export class StateProofFields implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'part-proofs', + valueSchema: new OptionalSchema(MerkleArrayProof.encodingSchema), + omitEmpty: true, + }, + { + key: 'positions-to-reveal', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + omitEmpty: true, + }, + { + key: 'reveals', + valueSchema: new OptionalSchema( + new ArraySchema(StateProofReveal.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'salt-version', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'sig-commit', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'sig-proofs', + valueSchema: new OptionalSchema(MerkleArrayProof.encodingSchema), + omitEmpty: true, + }, + { + key: 'signed-weight', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (P) */ @@ -3473,7 +4880,7 @@ export class StateProofFields extends BaseModel { /** * (pr) Sequence of reveal positions. */ - public positionsToReveal?: (number | bigint)[]; + public positionsToReveal?: bigint[]; /** * (r) Note that this is actually stored as a map[uint64] - Reveal in the actual @@ -3484,7 +4891,7 @@ export class StateProofFields extends BaseModel { /** * (v) Salt version of the merkle signature. */ - public saltVersion?: number | bigint; + public saltVersion?: number; /** * (c) @@ -3499,7 +4906,7 @@ export class StateProofFields extends BaseModel { /** * (w) */ - public signedWeight?: number | bigint; + public signedWeight?: bigint; /** * Creates a new `StateProofFields` object. @@ -3529,55 +4936,106 @@ export class StateProofFields extends BaseModel { sigProofs?: MerkleArrayProof; signedWeight?: number | bigint; }) { - super(); this.partProofs = partProofs; - this.positionsToReveal = positionsToReveal; + this.positionsToReveal = + typeof positionsToReveal === 'undefined' + ? undefined + : positionsToReveal.map(ensureBigInt); this.reveals = reveals; - this.saltVersion = saltVersion; + this.saltVersion = + typeof saltVersion === 'undefined' + ? undefined + : ensureSafeInteger(saltVersion); this.sigCommit = - typeof sigCommit === 'string' - ? new Uint8Array(Buffer.from(sigCommit, 'base64')) - : sigCommit; + typeof sigCommit === 'string' ? base64ToBytes(sigCommit) : 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 */ + this.signedWeight = + typeof signedWeight === 'undefined' + ? undefined + : ensureBigInt(signedWeight); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofFields.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'part-proofs', + typeof this.partProofs !== 'undefined' + ? this.partProofs.toEncodingData() + : undefined, + ], + ['positions-to-reveal', this.positionsToReveal], + [ + 'reveals', + typeof this.reveals !== 'undefined' + ? this.reveals.map((v) => v.toEncodingData()) + : undefined, + ], + ['salt-version', this.saltVersion], + ['sig-commit', this.sigCommit], + [ + 'sig-proofs', + typeof this.sigProofs !== 'undefined' + ? this.sigProofs.toEncodingData() + : undefined, + ], + ['signed-weight', this.signedWeight], + ]); + } + + static fromEncodingData(data: unknown): StateProofFields { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofFields: ${data}`); + } return new StateProofFields({ partProofs: - typeof data['part-proofs'] !== 'undefined' - ? MerkleArrayProof.from_obj_for_encoding(data['part-proofs']) + typeof data.get('part-proofs') !== 'undefined' + ? MerkleArrayProof.fromEncodingData(data.get('part-proofs')) : undefined, - positionsToReveal: data['positions-to-reveal'], + positionsToReveal: data.get('positions-to-reveal'), reveals: - typeof data['reveals'] !== 'undefined' - ? data['reveals'].map(StateProofReveal.from_obj_for_encoding) + typeof data.get('reveals') !== 'undefined' + ? data + .get('reveals') + .map((v: unknown) => StateProofReveal.fromEncodingData(v)) : undefined, - saltVersion: data['salt-version'], - sigCommit: data['sig-commit'], + saltVersion: data.get('salt-version'), + sigCommit: data.get('sig-commit'), sigProofs: - typeof data['sig-proofs'] !== 'undefined' - ? MerkleArrayProof.from_obj_for_encoding(data['sig-proofs']) + typeof data.get('sig-proofs') !== 'undefined' + ? MerkleArrayProof.fromEncodingData(data.get('sig-proofs')) : undefined, - signedWeight: data['signed-weight'], + signedWeight: data.get('signed-weight'), }); - /* eslint-enable dot-notation */ } } -export class StateProofParticipant extends BaseModel { +export class StateProofParticipant implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'verifier', + valueSchema: new OptionalSchema(StateProofVerifier.encodingSchema), + omitEmpty: true, + }, + { + key: 'weight', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (p) */ @@ -3586,7 +5044,7 @@ export class StateProofParticipant extends BaseModel { /** * (w) */ - public weight?: number | bigint; + public weight?: bigint; /** * Creates a new `StateProofParticipant` object. @@ -3600,33 +5058,69 @@ export class StateProofParticipant extends BaseModel { verifier?: StateProofVerifier; weight?: number | bigint; }) { - super(); this.verifier = verifier; - this.weight = weight; + this.weight = + typeof weight === 'undefined' ? undefined : ensureBigInt(weight); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofParticipant.encodingSchema; + } - this.attribute_map = { - verifier: 'verifier', - weight: 'weight', - }; + toEncodingData(): Map { + return new Map([ + [ + 'verifier', + typeof this.verifier !== 'undefined' + ? this.verifier.toEncodingData() + : undefined, + ], + ['weight', this.weight], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): StateProofParticipant { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): StateProofParticipant { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofParticipant: ${data}`); + } return new StateProofParticipant({ verifier: - typeof data['verifier'] !== 'undefined' - ? StateProofVerifier.from_obj_for_encoding(data['verifier']) + typeof data.get('verifier') !== 'undefined' + ? StateProofVerifier.fromEncodingData(data.get('verifier')) : undefined, - weight: data['weight'], + weight: data.get('weight'), }); - /* eslint-enable dot-notation */ } } -export class StateProofReveal extends BaseModel { +export class StateProofReveal implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'participant', + valueSchema: new OptionalSchema(StateProofParticipant.encodingSchema), + omitEmpty: true, + }, + { + key: 'position', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'sig-slot', + valueSchema: new OptionalSchema(StateProofSigSlot.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (p) */ @@ -3636,7 +5130,7 @@ export class StateProofReveal extends BaseModel { * The position in the signature and participants arrays corresponding to this * entry. */ - public position?: number | bigint; + public position?: bigint; /** * (s) @@ -3659,41 +5153,79 @@ export class StateProofReveal extends BaseModel { position?: number | bigint; sigSlot?: StateProofSigSlot; }) { - super(); this.participant = participant; - this.position = position; + this.position = + typeof position === 'undefined' ? undefined : ensureBigInt(position); this.sigSlot = sigSlot; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofReveal.encodingSchema; + } - this.attribute_map = { - participant: 'participant', - position: 'position', - sigSlot: 'sig-slot', - }; + toEncodingData(): Map { + return new Map([ + [ + 'participant', + typeof this.participant !== 'undefined' + ? this.participant.toEncodingData() + : undefined, + ], + ['position', this.position], + [ + 'sig-slot', + typeof this.sigSlot !== 'undefined' + ? this.sigSlot.toEncodingData() + : undefined, + ], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): StateProofReveal { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): StateProofReveal { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofReveal: ${data}`); + } return new StateProofReveal({ participant: - typeof data['participant'] !== 'undefined' - ? StateProofParticipant.from_obj_for_encoding(data['participant']) + typeof data.get('participant') !== 'undefined' + ? StateProofParticipant.fromEncodingData(data.get('participant')) : undefined, - position: data['position'], + position: data.get('position'), sigSlot: - typeof data['sig-slot'] !== 'undefined' - ? StateProofSigSlot.from_obj_for_encoding(data['sig-slot']) + typeof data.get('sig-slot') !== 'undefined' + ? StateProofSigSlot.fromEncodingData(data.get('sig-slot')) : undefined, }); - /* eslint-enable dot-notation */ } } -export class StateProofSigSlot extends BaseModel { +export class StateProofSigSlot implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'lower-sig-weight', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'signature', + valueSchema: new OptionalSchema(StateProofSignature.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (l) The total weight of signatures in the lower-numbered slots. */ - public lowerSigWeight?: number | bigint; + public lowerSigWeight?: bigint; public signature?: StateProofSignature; @@ -3709,34 +5241,79 @@ export class StateProofSigSlot extends BaseModel { lowerSigWeight?: number | bigint; signature?: StateProofSignature; }) { - super(); - this.lowerSigWeight = lowerSigWeight; + this.lowerSigWeight = + typeof lowerSigWeight === 'undefined' + ? undefined + : ensureBigInt(lowerSigWeight); this.signature = signature; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofSigSlot.encodingSchema; + } - this.attribute_map = { - lowerSigWeight: 'lower-sig-weight', - signature: 'signature', - }; + toEncodingData(): Map { + return new Map([ + ['lower-sig-weight', this.lowerSigWeight], + [ + 'signature', + typeof this.signature !== 'undefined' + ? this.signature.toEncodingData() + : undefined, + ], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): StateProofSigSlot { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): StateProofSigSlot { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofSigSlot: ${data}`); + } return new StateProofSigSlot({ - lowerSigWeight: data['lower-sig-weight'], + lowerSigWeight: data.get('lower-sig-weight'), signature: - typeof data['signature'] !== 'undefined' - ? StateProofSignature.from_obj_for_encoding(data['signature']) + typeof data.get('signature') !== 'undefined' + ? StateProofSignature.fromEncodingData(data.get('signature')) : undefined, }); - /* eslint-enable dot-notation */ } } -export class StateProofSignature extends BaseModel { +export class StateProofSignature implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'falcon-signature', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'merkle-array-index', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'proof', + valueSchema: new OptionalSchema(MerkleArrayProof.encodingSchema), + omitEmpty: true, + }, + { + key: 'verifying-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + public falconSignature?: Uint8Array; - public merkleArrayIndex?: number | bigint; + public merkleArrayIndex?: number; public proof?: MerkleArrayProof; @@ -3763,58 +5340,103 @@ export class StateProofSignature extends BaseModel { proof?: MerkleArrayProof; verifyingKey?: string | Uint8Array; }) { - super(); this.falconSignature = typeof falconSignature === 'string' - ? new Uint8Array(Buffer.from(falconSignature, 'base64')) + ? base64ToBytes(falconSignature) : falconSignature; - this.merkleArrayIndex = merkleArrayIndex; + this.merkleArrayIndex = + typeof merkleArrayIndex === 'undefined' + ? undefined + : ensureSafeInteger(merkleArrayIndex); this.proof = proof; this.verifyingKey = typeof verifyingKey === 'string' - ? new Uint8Array(Buffer.from(verifyingKey, 'base64')) + ? base64ToBytes(verifyingKey) : verifyingKey; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofSignature.encodingSchema; + } - this.attribute_map = { - falconSignature: 'falcon-signature', - merkleArrayIndex: 'merkle-array-index', - proof: 'proof', - verifyingKey: 'verifying-key', - }; + toEncodingData(): Map { + return new Map([ + ['falcon-signature', this.falconSignature], + ['merkle-array-index', this.merkleArrayIndex], + [ + 'proof', + typeof this.proof !== 'undefined' + ? this.proof.toEncodingData() + : undefined, + ], + ['verifying-key', this.verifyingKey], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): StateProofSignature { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): StateProofSignature { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofSignature: ${data}`); + } return new StateProofSignature({ - falconSignature: data['falcon-signature'], - merkleArrayIndex: data['merkle-array-index'], + falconSignature: data.get('falcon-signature'), + merkleArrayIndex: data.get('merkle-array-index'), proof: - typeof data['proof'] !== 'undefined' - ? MerkleArrayProof.from_obj_for_encoding(data['proof']) + typeof data.get('proof') !== 'undefined' + ? MerkleArrayProof.fromEncodingData(data.get('proof')) : undefined, - verifyingKey: data['verifying-key'], + verifyingKey: data.get('verifying-key'), }); - /* eslint-enable dot-notation */ } } -export class StateProofTracking extends BaseModel { +export class StateProofTracking implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'next-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'online-total-weight', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'type', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'voters-commitment', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (n) Next round for which we will accept a state proof transaction. */ - public nextRound?: number | bigint; + public nextRound?: bigint; /** * (t) The total number of microalgos held by the online accounts during the * StateProof round. */ - public onlineTotalWeight?: number | bigint; + public onlineTotalWeight?: bigint; /** * State Proof Type. Note the raw object uses map with this as key. */ - public type?: number | bigint; + public type?: number; /** * (v) Root of a vector commitment containing online accounts that will help sign @@ -3842,37 +5464,69 @@ export class StateProofTracking extends BaseModel { type?: number | bigint; votersCommitment?: string | Uint8Array; }) { - super(); - this.nextRound = nextRound; - this.onlineTotalWeight = onlineTotalWeight; - this.type = type; + this.nextRound = + typeof nextRound === 'undefined' ? undefined : ensureBigInt(nextRound); + this.onlineTotalWeight = + typeof onlineTotalWeight === 'undefined' + ? undefined + : ensureBigInt(onlineTotalWeight); + this.type = + typeof type === 'undefined' ? undefined : ensureSafeInteger(type); this.votersCommitment = typeof votersCommitment === 'string' - ? new Uint8Array(Buffer.from(votersCommitment, 'base64')) + ? base64ToBytes(votersCommitment) : votersCommitment; + } - this.attribute_map = { - nextRound: 'next-round', - onlineTotalWeight: 'online-total-weight', - type: 'type', - votersCommitment: 'voters-commitment', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofTracking.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): StateProofTracking { - /* eslint-disable dot-notation */ + toEncodingData(): Map { + return new Map([ + ['next-round', this.nextRound], + ['online-total-weight', this.onlineTotalWeight], + ['type', this.type], + ['voters-commitment', this.votersCommitment], + ]); + } + + static fromEncodingData(data: unknown): StateProofTracking { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofTracking: ${data}`); + } return new StateProofTracking({ - nextRound: data['next-round'], - onlineTotalWeight: data['online-total-weight'], - type: data['type'], - votersCommitment: data['voters-commitment'], + nextRound: data.get('next-round'), + onlineTotalWeight: data.get('online-total-weight'), + type: data.get('type'), + votersCommitment: data.get('voters-commitment'), }); - /* eslint-enable dot-notation */ } } -export class StateProofVerifier extends BaseModel { +export class StateProofVerifier implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'commitment', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'key-lifetime', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (cmt) Represents the root of the vector commitment tree. */ @@ -3881,7 +5535,7 @@ export class StateProofVerifier extends BaseModel { /** * (lf) Key lifetime. */ - public keyLifetime?: number | bigint; + public keyLifetime?: bigint; /** * Creates a new `StateProofVerifier` object. @@ -3895,27 +5549,34 @@ export class StateProofVerifier extends BaseModel { commitment?: string | Uint8Array; keyLifetime?: number | bigint; }) { - super(); this.commitment = - typeof commitment === 'string' - ? new Uint8Array(Buffer.from(commitment, 'base64')) - : commitment; - this.keyLifetime = keyLifetime; + typeof commitment === 'string' ? base64ToBytes(commitment) : commitment; + this.keyLifetime = + typeof keyLifetime === 'undefined' + ? undefined + : ensureBigInt(keyLifetime); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofVerifier.encodingSchema; + } - this.attribute_map = { - commitment: 'commitment', - keyLifetime: 'key-lifetime', - }; + toEncodingData(): Map { + return new Map([ + ['commitment', this.commitment], + ['key-lifetime', this.keyLifetime], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): StateProofVerifier { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): StateProofVerifier { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofVerifier: ${data}`); + } return new StateProofVerifier({ - commitment: data['commitment'], - keyLifetime: data['key-lifetime'], + commitment: data.get('commitment'), + keyLifetime: data.get('key-lifetime'), }); - /* eslint-enable dot-notation */ } } @@ -3925,16 +5586,33 @@ export class StateProofVerifier extends BaseModel { * application. The more space used, the larger minimum balance must be maintained * in the account holding the data. */ -export class StateSchema extends BaseModel { +export class StateSchema implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'num-byte-slice', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'num-uint', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * Maximum number of TEAL byte slices that may be stored in the key/value store. */ - public numByteSlice: number | bigint; + public numByteSlice: number; /** * Maximum number of TEAL uints that may be stored in the key/value store. */ - public numUint: number | bigint; + public numUint: number; /** * Creates a new `StateSchema` object. @@ -3948,38 +5626,51 @@ export class StateSchema extends BaseModel { numByteSlice: number | bigint; numUint: number | bigint; }) { - super(); - this.numByteSlice = numByteSlice; - this.numUint = numUint; + this.numByteSlice = ensureSafeInteger(numByteSlice); + this.numUint = ensureSafeInteger(numUint); + } - this.attribute_map = { - numByteSlice: 'num-byte-slice', - numUint: 'num-uint', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateSchema.encodingSchema; } - // 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}`); + toEncodingData(): Map { + return new Map([ + ['num-byte-slice', this.numByteSlice], + ['num-uint', this.numUint], + ]); + } + + static fromEncodingData(data: unknown): StateSchema { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateSchema: ${data}`); + } return new StateSchema({ - numByteSlice: data['num-byte-slice'], - numUint: data['num-uint'], + numByteSlice: data.get('num-byte-slice'), + numUint: data.get('num-uint'), }); - /* eslint-enable dot-notation */ } } /** * Represents a key-value pair in an application store. */ -export class TealKeyValue extends BaseModel { - public key: string; +export class TealKeyValue implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'key', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'value', valueSchema: TealValue.encodingSchema, omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + public key: Uint8Array; /** * Represents a TEAL value. @@ -3991,50 +5682,66 @@ export class TealKeyValue extends BaseModel { * @param key - * @param value - Represents a TEAL value. */ - constructor({ key, value }: { key: string; value: TealValue }) { - super(); - this.key = key; + constructor({ key, value }: { key: string | Uint8Array; value: TealValue }) { + this.key = typeof key === 'string' ? base64ToBytes(key) : key; this.value = value; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TealKeyValue.encodingSchema; + } - this.attribute_map = { - key: 'key', - value: 'value', - }; + toEncodingData(): Map { + return new Map([ + ['key', this.key], + ['value', this.value.toEncodingData()], + ]); } - // 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}`); + static fromEncodingData(data: unknown): TealKeyValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TealKeyValue: ${data}`); + } return new TealKeyValue({ - key: data['key'], - value: TealValue.from_obj_for_encoding(data['value']), + key: data.get('key'), + value: TealValue.fromEncodingData(data.get('value') ?? new Map()), }); - /* eslint-enable dot-notation */ } } /** * Represents a TEAL value. */ -export class TealValue extends BaseModel { +export class TealValue implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'bytes', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'type', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'uint', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + /** * bytes value. */ - public bytes: string; + public bytes: Uint8Array; /** * type of the value. Value `1` refers to **bytes**, value `2` refers to **uint** */ - public type: number | bigint; + public type: number; /** * uint value. */ - public uint: number | bigint; + public uint: bigint; /** * Creates a new `TealValue` object. @@ -4047,37 +5754,37 @@ export class TealValue extends BaseModel { type, uint, }: { - bytes: string; + bytes: string | Uint8Array; 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}`); + this.bytes = typeof bytes === 'string' ? base64ToBytes(bytes) : bytes; + this.type = ensureSafeInteger(type); + this.uint = ensureBigInt(uint); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TealValue.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['bytes', this.bytes], + ['type', this.type], + ['uint', this.uint], + ]); + } + + static fromEncodingData(data: unknown): TealValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TealValue: ${data}`); + } return new TealValue({ - bytes: data['bytes'], - type: data['type'], - uint: data['uint'], + bytes: data.get('bytes'), + type: data.get('type'), + uint: data.get('uint'), }); - /* eslint-enable dot-notation */ } } @@ -4088,21 +5795,206 @@ export class TealValue extends BaseModel { * data/transactions/signedtxn.go : SignedTxn * data/transactions/transaction.go : Transaction */ -export class Transaction extends BaseModel { +export class Transaction implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'fee', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'first-valid', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'last-valid', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'sender', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'application-transaction', + valueSchema: new OptionalSchema( + TransactionApplication.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'asset-config-transaction', + valueSchema: new OptionalSchema( + TransactionAssetConfig.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'asset-freeze-transaction', + valueSchema: new OptionalSchema( + TransactionAssetFreeze.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'asset-transfer-transaction', + valueSchema: new OptionalSchema( + TransactionAssetTransfer.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'auth-addr', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'close-rewards', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'closing-amount', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'confirmed-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'created-application-index', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'created-asset-index', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'genesis-hash', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'genesis-id', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'global-state-delta', + valueSchema: new OptionalSchema( + new ArraySchema(EvalDeltaKeyValue.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'group', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'id', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'inner-txns', + valueSchema: new OptionalSchema( + new ArraySchema(Transaction.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'intra-round-offset', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'keyreg-transaction', + valueSchema: new OptionalSchema(TransactionKeyreg.encodingSchema), + omitEmpty: true, + }, + { + key: 'lease', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'local-state-delta', + valueSchema: new OptionalSchema( + new ArraySchema(AccountStateDelta.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'logs', + valueSchema: new OptionalSchema( + new ArraySchema(new ByteArraySchema()) + ), + omitEmpty: true, + }, + { + key: 'note', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'payment-transaction', + valueSchema: new OptionalSchema(TransactionPayment.encodingSchema), + omitEmpty: true, + }, + { + key: 'receiver-rewards', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'rekey-to', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'round-time', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'sender-rewards', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'signature', + valueSchema: new OptionalSchema(TransactionSignature.encodingSchema), + omitEmpty: true, + }, + { + key: 'state-proof-transaction', + valueSchema: new OptionalSchema(TransactionStateProof.encodingSchema), + omitEmpty: true, + }, + { + key: 'tx-type', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (fee) Transaction fee. */ - public fee: number | bigint; + public fee: bigint; /** * (fv) First valid round for this transaction. */ - public firstValid: number | bigint; + public firstValid: bigint; /** * (lv) Last valid round for this transaction. */ - public lastValid: number | bigint; + public lastValid: bigint; /** * (snd) Sender's address. @@ -4144,33 +6036,33 @@ export class Transaction extends BaseModel { * not equal the sender. The backend can use this to ensure that auth addr is equal * to the accounts auth addr. */ - public authAddr?: string; + public authAddr?: Address; /** * (rc) rewards applied to close-remainder-to account. */ - public closeRewards?: number | bigint; + public closeRewards?: bigint; /** * (ca) closing amount for transaction. */ - public closingAmount?: number | bigint; + public closingAmount?: bigint; /** * Round when the transaction was confirmed. */ - public confirmedRound?: number | bigint; + public confirmedRound?: bigint; /** * Specifies an application index (ID) if an application was created with this * transaction. */ - public createdApplicationIndex?: number | bigint; + public createdApplicationIndex?: bigint; /** * Specifies an asset index (ID) if an asset was created with this transaction. */ - public createdAssetIndex?: number | bigint; + public createdAssetIndex?: bigint; /** * (gh) Hash of genesis block. @@ -4208,7 +6100,7 @@ export class Transaction extends BaseModel { /** * Offset into the round where this transaction was confirmed. */ - public intraRoundOffset?: number | bigint; + public intraRoundOffset?: number; /** * Fields for a keyreg transaction. @@ -4252,24 +6144,24 @@ export class Transaction extends BaseModel { /** * (rr) rewards applied to receiver account. */ - public receiverRewards?: number | bigint; + public receiverRewards?: 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; + public rekeyTo?: Address; /** * Time when the block this transaction is in was confirmed. */ - public roundTime?: number | bigint; + public roundTime?: number; /** * (rs) rewards applied to sender account. */ - public senderRewards?: number | bigint; + public senderRewards?: bigint; /** * Validation signature associated with some data. Only one of the signatures @@ -4418,7 +6310,7 @@ export class Transaction extends BaseModel { assetConfigTransaction?: TransactionAssetConfig; assetFreezeTransaction?: TransactionAssetFreeze; assetTransferTransaction?: TransactionAssetTransfer; - authAddr?: string; + authAddr?: Address | string; closeRewards?: number | bigint; closingAmount?: number | bigint; confirmedRound?: number | bigint; @@ -4438,200 +6330,279 @@ export class Transaction extends BaseModel { note?: string | Uint8Array; paymentTransaction?: TransactionPayment; receiverRewards?: number | bigint; - rekeyTo?: string; + rekeyTo?: Address | string; roundTime?: number | bigint; senderRewards?: number | bigint; signature?: TransactionSignature; stateProofTransaction?: TransactionStateProof; txType?: string; }) { - super(); - this.fee = fee; - this.firstValid = firstValid; - this.lastValid = lastValid; + this.fee = ensureBigInt(fee); + this.firstValid = ensureBigInt(firstValid); + this.lastValid = ensureBigInt(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.authAddr = + typeof authAddr === 'string' ? Address.fromString(authAddr) : authAddr; + this.closeRewards = + typeof closeRewards === 'undefined' + ? undefined + : ensureBigInt(closeRewards); + this.closingAmount = + typeof closingAmount === 'undefined' + ? undefined + : ensureBigInt(closingAmount); + this.confirmedRound = + typeof confirmedRound === 'undefined' + ? undefined + : ensureBigInt(confirmedRound); + this.createdApplicationIndex = + typeof createdApplicationIndex === 'undefined' + ? undefined + : ensureBigInt(createdApplicationIndex); + this.createdAssetIndex = + typeof createdAssetIndex === 'undefined' + ? undefined + : ensureBigInt(createdAssetIndex); this.genesisHash = typeof genesisHash === 'string' - ? new Uint8Array(Buffer.from(genesisHash, 'base64')) + ? base64ToBytes(genesisHash) : genesisHash; this.genesisId = genesisId; this.globalStateDelta = globalStateDelta; - this.group = - typeof group === 'string' - ? new Uint8Array(Buffer.from(group, 'base64')) - : group; + this.group = typeof group === 'string' ? base64ToBytes(group) : group; this.id = id; this.innerTxns = innerTxns; - this.intraRoundOffset = intraRoundOffset; + this.intraRoundOffset = + typeof intraRoundOffset === 'undefined' + ? undefined + : ensureSafeInteger(intraRoundOffset); this.keyregTransaction = keyregTransaction; - this.lease = - typeof lease === 'string' - ? new Uint8Array(Buffer.from(lease, 'base64')) - : lease; + this.lease = typeof lease === 'string' ? base64ToBytes(lease) : lease; this.localStateDelta = localStateDelta; this.logs = logs; - this.note = - typeof note === 'string' - ? new Uint8Array(Buffer.from(note, 'base64')) - : note; + this.note = typeof note === 'string' ? base64ToBytes(note) : note; this.paymentTransaction = paymentTransaction; - this.receiverRewards = receiverRewards; - this.rekeyTo = rekeyTo; - this.roundTime = roundTime; - this.senderRewards = senderRewards; + this.receiverRewards = + typeof receiverRewards === 'undefined' + ? undefined + : ensureBigInt(receiverRewards); + this.rekeyTo = + typeof rekeyTo === 'string' ? Address.fromString(rekeyTo) : rekeyTo; + this.roundTime = + typeof roundTime === 'undefined' + ? undefined + : ensureSafeInteger(roundTime); + this.senderRewards = + typeof senderRewards === 'undefined' + ? undefined + : ensureBigInt(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}`); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Transaction.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['fee', this.fee], + ['first-valid', this.firstValid], + ['last-valid', this.lastValid], + ['sender', this.sender], + [ + 'application-transaction', + typeof this.applicationTransaction !== 'undefined' + ? this.applicationTransaction.toEncodingData() + : undefined, + ], + [ + 'asset-config-transaction', + typeof this.assetConfigTransaction !== 'undefined' + ? this.assetConfigTransaction.toEncodingData() + : undefined, + ], + [ + 'asset-freeze-transaction', + typeof this.assetFreezeTransaction !== 'undefined' + ? this.assetFreezeTransaction.toEncodingData() + : undefined, + ], + [ + 'asset-transfer-transaction', + typeof this.assetTransferTransaction !== 'undefined' + ? this.assetTransferTransaction.toEncodingData() + : undefined, + ], + [ + 'auth-addr', + typeof this.authAddr !== 'undefined' + ? this.authAddr.toString() + : undefined, + ], + ['close-rewards', this.closeRewards], + ['closing-amount', this.closingAmount], + ['confirmed-round', this.confirmedRound], + ['created-application-index', this.createdApplicationIndex], + ['created-asset-index', this.createdAssetIndex], + ['genesis-hash', this.genesisHash], + ['genesis-id', this.genesisId], + [ + 'global-state-delta', + typeof this.globalStateDelta !== 'undefined' + ? this.globalStateDelta.map((v) => v.toEncodingData()) + : undefined, + ], + ['group', this.group], + ['id', this.id], + [ + 'inner-txns', + typeof this.innerTxns !== 'undefined' + ? this.innerTxns.map((v) => v.toEncodingData()) + : undefined, + ], + ['intra-round-offset', this.intraRoundOffset], + [ + 'keyreg-transaction', + typeof this.keyregTransaction !== 'undefined' + ? this.keyregTransaction.toEncodingData() + : undefined, + ], + ['lease', this.lease], + [ + 'local-state-delta', + typeof this.localStateDelta !== 'undefined' + ? this.localStateDelta.map((v) => v.toEncodingData()) + : undefined, + ], + ['logs', this.logs], + ['note', this.note], + [ + 'payment-transaction', + typeof this.paymentTransaction !== 'undefined' + ? this.paymentTransaction.toEncodingData() + : undefined, + ], + ['receiver-rewards', this.receiverRewards], + [ + 'rekey-to', + typeof this.rekeyTo !== 'undefined' + ? this.rekeyTo.toString() + : undefined, + ], + ['round-time', this.roundTime], + ['sender-rewards', this.senderRewards], + [ + 'signature', + typeof this.signature !== 'undefined' + ? this.signature.toEncodingData() + : undefined, + ], + [ + 'state-proof-transaction', + typeof this.stateProofTransaction !== 'undefined' + ? this.stateProofTransaction.toEncodingData() + : undefined, + ], + ['tx-type', this.txType], + ]); + } + + static fromEncodingData(data: unknown): Transaction { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Transaction: ${data}`); + } return new Transaction({ - fee: data['fee'], - firstValid: data['first-valid'], - lastValid: data['last-valid'], - sender: data['sender'], + fee: data.get('fee'), + firstValid: data.get('first-valid'), + lastValid: data.get('last-valid'), + sender: data.get('sender'), applicationTransaction: - typeof data['application-transaction'] !== 'undefined' - ? TransactionApplication.from_obj_for_encoding( - data['application-transaction'] + typeof data.get('application-transaction') !== 'undefined' + ? TransactionApplication.fromEncodingData( + data.get('application-transaction') ) : undefined, assetConfigTransaction: - typeof data['asset-config-transaction'] !== 'undefined' - ? TransactionAssetConfig.from_obj_for_encoding( - data['asset-config-transaction'] + typeof data.get('asset-config-transaction') !== 'undefined' + ? TransactionAssetConfig.fromEncodingData( + data.get('asset-config-transaction') ) : undefined, assetFreezeTransaction: - typeof data['asset-freeze-transaction'] !== 'undefined' - ? TransactionAssetFreeze.from_obj_for_encoding( - data['asset-freeze-transaction'] + typeof data.get('asset-freeze-transaction') !== 'undefined' + ? TransactionAssetFreeze.fromEncodingData( + data.get('asset-freeze-transaction') ) : undefined, assetTransferTransaction: - typeof data['asset-transfer-transaction'] !== 'undefined' - ? TransactionAssetTransfer.from_obj_for_encoding( - data['asset-transfer-transaction'] + typeof data.get('asset-transfer-transaction') !== 'undefined' + ? TransactionAssetTransfer.fromEncodingData( + data.get('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'], + authAddr: data.get('auth-addr'), + closeRewards: data.get('close-rewards'), + closingAmount: data.get('closing-amount'), + confirmedRound: data.get('confirmed-round'), + createdApplicationIndex: data.get('created-application-index'), + createdAssetIndex: data.get('created-asset-index'), + genesisHash: data.get('genesis-hash'), + genesisId: data.get('genesis-id'), globalStateDelta: - typeof data['global-state-delta'] !== 'undefined' - ? data['global-state-delta'].map( - EvalDeltaKeyValue.from_obj_for_encoding - ) + typeof data.get('global-state-delta') !== 'undefined' + ? data + .get('global-state-delta') + .map((v: unknown) => EvalDeltaKeyValue.fromEncodingData(v)) : undefined, - group: data['group'], - id: data['id'], + group: data.get('group'), + id: data.get('id'), innerTxns: - typeof data['inner-txns'] !== 'undefined' - ? data['inner-txns'].map(Transaction.from_obj_for_encoding) + typeof data.get('inner-txns') !== 'undefined' + ? data + .get('inner-txns') + .map((v: unknown) => Transaction.fromEncodingData(v)) : undefined, - intraRoundOffset: data['intra-round-offset'], + intraRoundOffset: data.get('intra-round-offset'), keyregTransaction: - typeof data['keyreg-transaction'] !== 'undefined' - ? TransactionKeyreg.from_obj_for_encoding(data['keyreg-transaction']) + typeof data.get('keyreg-transaction') !== 'undefined' + ? TransactionKeyreg.fromEncodingData(data.get('keyreg-transaction')) : undefined, - lease: data['lease'], + lease: data.get('lease'), localStateDelta: - typeof data['local-state-delta'] !== 'undefined' - ? data['local-state-delta'].map( - AccountStateDelta.from_obj_for_encoding - ) + typeof data.get('local-state-delta') !== 'undefined' + ? data + .get('local-state-delta') + .map((v: unknown) => AccountStateDelta.fromEncodingData(v)) : undefined, - logs: data['logs'], - note: data['note'], + logs: data.get('logs'), + note: data.get('note'), paymentTransaction: - typeof data['payment-transaction'] !== 'undefined' - ? TransactionPayment.from_obj_for_encoding( - data['payment-transaction'] - ) + typeof data.get('payment-transaction') !== 'undefined' + ? TransactionPayment.fromEncodingData(data.get('payment-transaction')) : undefined, - receiverRewards: data['receiver-rewards'], - rekeyTo: data['rekey-to'], - roundTime: data['round-time'], - senderRewards: data['sender-rewards'], + receiverRewards: data.get('receiver-rewards'), + rekeyTo: data.get('rekey-to'), + roundTime: data.get('round-time'), + senderRewards: data.get('sender-rewards'), signature: - typeof data['signature'] !== 'undefined' - ? TransactionSignature.from_obj_for_encoding(data['signature']) + typeof data.get('signature') !== 'undefined' + ? TransactionSignature.fromEncodingData(data.get('signature')) : undefined, stateProofTransaction: - typeof data['state-proof-transaction'] !== 'undefined' - ? TransactionStateProof.from_obj_for_encoding( - data['state-proof-transaction'] + typeof data.get('state-proof-transaction') !== 'undefined' + ? TransactionStateProof.fromEncodingData( + data.get('state-proof-transaction') ) : undefined, - txType: data['tx-type'], + txType: data.get('tx-type'), }); - /* eslint-enable dot-notation */ } } @@ -4640,17 +6611,85 @@ export class Transaction extends BaseModel { * Definition: * data/transactions/application.go : ApplicationCallTxnFields */ -export class TransactionApplication extends BaseModel { +export class TransactionApplication implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'application-id', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'accounts', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + }, + { + key: 'application-args', + valueSchema: new OptionalSchema( + new ArraySchema(new ByteArraySchema()) + ), + omitEmpty: true, + }, + { + key: 'approval-program', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'clear-state-program', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'extra-program-pages', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'foreign-apps', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + omitEmpty: true, + }, + { + key: 'foreign-assets', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + omitEmpty: true, + }, + { + key: 'global-state-schema', + valueSchema: new OptionalSchema(StateSchema.encodingSchema), + omitEmpty: true, + }, + { + key: 'local-state-schema', + valueSchema: new OptionalSchema(StateSchema.encodingSchema), + omitEmpty: true, + }, + { + key: 'on-completion', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (apid) ID of the application being configured or empty if creating. */ - public applicationId: number | bigint; + public applicationId: 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[]; + public accounts?: Address[]; /** * (apaa) transaction specific arguments accessed from the application's @@ -4677,20 +6716,20 @@ export class TransactionApplication extends BaseModel { /** * (epp) specifies the additional app program len requested in pages. */ - public extraProgramPages?: number | bigint; + public extraProgramPages?: number; /** * (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)[]; + public foreignApps?: 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)[]; + public foreignAssets?: bigint[]; /** * Represents a (apls) local-state or (apgs) global-state schema. These schemas @@ -4774,7 +6813,7 @@ export class TransactionApplication extends BaseModel { onCompletion, }: { applicationId: number | bigint; - accounts?: string[]; + accounts?: (Address | string)[]; applicationArgs?: Uint8Array[]; approvalProgram?: string | Uint8Array; clearStateProgram?: string | Uint8Array; @@ -4785,69 +6824,98 @@ export class TransactionApplication extends BaseModel { localStateSchema?: StateSchema; onCompletion?: string; }) { - super(); - this.applicationId = applicationId; - this.accounts = accounts; + this.applicationId = ensureBigInt(applicationId); + this.accounts = + typeof accounts !== 'undefined' + ? accounts.map((addr) => + typeof addr === 'string' ? Address.fromString(addr) : addr + ) + : undefined; this.applicationArgs = applicationArgs; this.approvalProgram = typeof approvalProgram === 'string' - ? new Uint8Array(Buffer.from(approvalProgram, 'base64')) + ? base64ToBytes(approvalProgram) : approvalProgram; this.clearStateProgram = typeof clearStateProgram === 'string' - ? new Uint8Array(Buffer.from(clearStateProgram, 'base64')) + ? base64ToBytes(clearStateProgram) : clearStateProgram; - this.extraProgramPages = extraProgramPages; - this.foreignApps = foreignApps; - this.foreignAssets = foreignAssets; + this.extraProgramPages = + typeof extraProgramPages === 'undefined' + ? undefined + : ensureSafeInteger(extraProgramPages); + this.foreignApps = + typeof foreignApps === 'undefined' + ? undefined + : foreignApps.map(ensureBigInt); + this.foreignAssets = + typeof foreignAssets === 'undefined' + ? undefined + : foreignAssets.map(ensureBigInt); 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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionApplication.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['application-id', this.applicationId], + [ + 'accounts', + typeof this.accounts !== 'undefined' + ? this.accounts.map((v) => v.toString()) + : undefined, + ], + ['application-args', this.applicationArgs], + ['approval-program', this.approvalProgram], + ['clear-state-program', this.clearStateProgram], + ['extra-program-pages', this.extraProgramPages], + ['foreign-apps', this.foreignApps], + ['foreign-assets', this.foreignAssets], + [ + 'global-state-schema', + typeof this.globalStateSchema !== 'undefined' + ? this.globalStateSchema.toEncodingData() + : undefined, + ], + [ + 'local-state-schema', + typeof this.localStateSchema !== 'undefined' + ? this.localStateSchema.toEncodingData() + : undefined, + ], + ['on-completion', this.onCompletion], + ]); + } + + static fromEncodingData(data: unknown): TransactionApplication { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionApplication: ${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'], + applicationId: data.get('application-id'), + accounts: data.get('accounts'), + applicationArgs: data.get('application-args'), + approvalProgram: data.get('approval-program'), + clearStateProgram: data.get('clear-state-program'), + extraProgramPages: data.get('extra-program-pages'), + foreignApps: data.get('foreign-apps'), + foreignAssets: data.get('foreign-assets'), globalStateSchema: - typeof data['global-state-schema'] !== 'undefined' - ? StateSchema.from_obj_for_encoding(data['global-state-schema']) + typeof data.get('global-state-schema') !== 'undefined' + ? StateSchema.fromEncodingData(data.get('global-state-schema')) : undefined, localStateSchema: - typeof data['local-state-schema'] !== 'undefined' - ? StateSchema.from_obj_for_encoding(data['local-state-schema']) + typeof data.get('local-state-schema') !== 'undefined' + ? StateSchema.fromEncodingData(data.get('local-state-schema')) : undefined, - onCompletion: data['on-completion'], + onCompletion: data.get('on-completion'), }); - /* eslint-enable dot-notation */ } } @@ -4858,11 +6926,32 @@ export class TransactionApplication extends BaseModel { * Definition: * data/transactions/asset.go : AssetConfigTxnFields */ -export class TransactionAssetConfig extends BaseModel { +export class TransactionAssetConfig implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'asset-id', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'params', + valueSchema: new OptionalSchema(AssetParams.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (xaid) ID of the asset being configured or empty if creating. */ - public assetId?: number | bigint; + public assetId?: bigint; /** * AssetParams specifies the parameters for an asset. @@ -4887,29 +6976,39 @@ export class TransactionAssetConfig extends BaseModel { assetId?: number | bigint; params?: AssetParams; }) { - super(); - this.assetId = assetId; + this.assetId = + typeof assetId === 'undefined' ? undefined : ensureBigInt(assetId); this.params = params; + } - this.attribute_map = { - assetId: 'asset-id', - params: 'params', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionAssetConfig.encodingSchema; } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): TransactionAssetConfig { - /* eslint-disable dot-notation */ + toEncodingData(): Map { + return new Map([ + ['asset-id', this.assetId], + [ + 'params', + typeof this.params !== 'undefined' + ? this.params.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): TransactionAssetConfig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionAssetConfig: ${data}`); + } return new TransactionAssetConfig({ - assetId: data['asset-id'], + assetId: data.get('asset-id'), params: - typeof data['params'] !== 'undefined' - ? AssetParams.from_obj_for_encoding(data['params']) + typeof data.get('params') !== 'undefined' + ? AssetParams.fromEncodingData(data.get('params')) : undefined, }); - /* eslint-enable dot-notation */ } } @@ -4918,7 +7017,25 @@ export class TransactionAssetConfig extends BaseModel { * Definition: * data/transactions/asset.go : AssetFreezeTxnFields */ -export class TransactionAssetFreeze extends BaseModel { +export class TransactionAssetFreeze implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'asset-id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'new-freeze-status', + valueSchema: new BooleanSchema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (fadd) Address of the account whose asset is being frozen or thawed. */ @@ -4927,7 +7044,7 @@ export class TransactionAssetFreeze extends BaseModel { /** * (faid) ID of the asset being frozen or thawed. */ - public assetId: number | bigint; + public assetId: bigint; /** * (afrz) The new freeze status. @@ -4949,37 +7066,33 @@ export class TransactionAssetFreeze extends BaseModel { assetId: number | bigint; newFreezeStatus: boolean; }) { - super(); this.address = address; - this.assetId = assetId; + this.assetId = ensureBigInt(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}` - ); + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionAssetFreeze.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['address', this.address], + ['asset-id', this.assetId], + ['new-freeze-status', this.newFreezeStatus], + ]); + } + + static fromEncodingData(data: unknown): TransactionAssetFreeze { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionAssetFreeze: ${data}`); + } return new TransactionAssetFreeze({ - address: data['address'], - assetId: data['asset-id'], - newFreezeStatus: data['new-freeze-status'], + address: data.get('address'), + assetId: data.get('asset-id'), + newFreezeStatus: data.get('new-freeze-status'), }); - /* eslint-enable dot-notation */ } } @@ -4988,17 +7101,46 @@ export class TransactionAssetFreeze extends BaseModel { * Definition: * data/transactions/asset.go : AssetTransferTxnFields */ -export class TransactionAssetTransfer extends BaseModel { +export class TransactionAssetTransfer implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'asset-id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'receiver', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'close-amount', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'close-to', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'sender', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (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; + public amount: bigint; /** * (xaid) ID of the asset being transferred. */ - public assetId: number | bigint; + public assetId: bigint; /** * (arcv) Recipient address of the transfer. @@ -5008,7 +7150,7 @@ export class TransactionAssetTransfer extends BaseModel { /** * Number of assets transferred to the close-to account as part of the transaction. */ - public closeAmount?: number | bigint; + public closeAmount?: bigint; /** * (aclose) Indicates that the asset should be removed from the account's Assets @@ -5053,44 +7195,45 @@ export class TransactionAssetTransfer extends BaseModel { closeTo?: string; sender?: string; }) { - super(); - this.amount = amount; - this.assetId = assetId; + this.amount = ensureBigInt(amount); + this.assetId = ensureBigInt(assetId); this.receiver = receiver; - this.closeAmount = closeAmount; + this.closeAmount = + typeof closeAmount === 'undefined' + ? undefined + : ensureBigInt(closeAmount); this.closeTo = closeTo; this.sender = sender; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionAssetTransfer.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['amount', this.amount], + ['asset-id', this.assetId], + ['receiver', this.receiver], + ['close-amount', this.closeAmount], + ['close-to', this.closeTo], + ['sender', this.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}`); + static fromEncodingData(data: unknown): TransactionAssetTransfer { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionAssetTransfer: ${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'], + amount: data.get('amount'), + assetId: data.get('asset-id'), + receiver: data.get('receiver'), + closeAmount: data.get('close-amount'), + closeTo: data.get('close-to'), + sender: data.get('sender'), }); - /* eslint-enable dot-notation */ } } @@ -5099,7 +7242,53 @@ export class TransactionAssetTransfer extends BaseModel { * Definition: * data/transactions/keyreg.go : KeyregTxnFields */ -export class TransactionKeyreg extends BaseModel { +export class TransactionKeyreg implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'non-participation', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'selection-participation-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'state-proof-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'vote-first-valid', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'vote-key-dilution', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'vote-last-valid', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'vote-participation-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (nonpart) Mark the account as participating or non-participating. */ @@ -5119,17 +7308,17 @@ export class TransactionKeyreg extends BaseModel { /** * (votefst) First round this participation key is valid. */ - public voteFirstValid?: number | bigint; + public voteFirstValid?: bigint; /** * (votekd) Number of subkeys in each batch of participation keys. */ - public voteKeyDilution?: number | bigint; + public voteKeyDilution?: bigint; /** * (votelst) Last round this participation key is valid. */ - public voteLastValid?: number | bigint; + public voteLastValid?: bigint; /** * (votekey) Participation public key used in key registration transactions. @@ -5164,48 +7353,63 @@ export class TransactionKeyreg extends BaseModel { voteLastValid?: number | bigint; voteParticipationKey?: string | Uint8Array; }) { - super(); this.nonParticipation = nonParticipation; this.selectionParticipationKey = typeof selectionParticipationKey === 'string' - ? new Uint8Array(Buffer.from(selectionParticipationKey, 'base64')) + ? base64ToBytes(selectionParticipationKey) : selectionParticipationKey; this.stateProofKey = typeof stateProofKey === 'string' - ? new Uint8Array(Buffer.from(stateProofKey, 'base64')) + ? base64ToBytes(stateProofKey) : stateProofKey; - this.voteFirstValid = voteFirstValid; - this.voteKeyDilution = voteKeyDilution; - this.voteLastValid = voteLastValid; + this.voteFirstValid = + typeof voteFirstValid === 'undefined' + ? undefined + : ensureBigInt(voteFirstValid); + this.voteKeyDilution = + typeof voteKeyDilution === 'undefined' + ? undefined + : ensureBigInt(voteKeyDilution); + this.voteLastValid = + typeof voteLastValid === 'undefined' + ? undefined + : ensureBigInt(voteLastValid); this.voteParticipationKey = typeof voteParticipationKey === 'string' - ? new Uint8Array(Buffer.from(voteParticipationKey, 'base64')) + ? base64ToBytes(voteParticipationKey) : voteParticipationKey; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionKeyreg.encodingSchema; + } - 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', - }; + toEncodingData(): Map { + return new Map([ + ['non-participation', this.nonParticipation], + ['selection-participation-key', this.selectionParticipationKey], + ['state-proof-key', this.stateProofKey], + ['vote-first-valid', this.voteFirstValid], + ['vote-key-dilution', this.voteKeyDilution], + ['vote-last-valid', this.voteLastValid], + ['vote-participation-key', this.voteParticipationKey], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding(data: Record): TransactionKeyreg { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): TransactionKeyreg { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionKeyreg: ${data}`); + } 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'], + nonParticipation: data.get('non-participation'), + selectionParticipationKey: data.get('selection-participation-key'), + stateProofKey: data.get('state-proof-key'), + voteFirstValid: data.get('vote-first-valid'), + voteKeyDilution: data.get('vote-key-dilution'), + voteLastValid: data.get('vote-last-valid'), + voteParticipationKey: data.get('vote-participation-key'), }); - /* eslint-enable dot-notation */ } } @@ -5214,11 +7418,34 @@ export class TransactionKeyreg extends BaseModel { * Definition: * data/transactions/payment.go : PaymentTxnFields */ -export class TransactionPayment extends BaseModel { +export class TransactionPayment implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'receiver', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'close-amount', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'close-remainder-to', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (amt) number of MicroAlgos intended to be transferred. */ - public amount: number | bigint; + public amount: bigint; /** * (rcv) receiver's address. @@ -5229,7 +7456,7 @@ export class TransactionPayment extends BaseModel { * Number of MicroAlgos that were sent to the close-remainder-to address when * closing the sender account. */ - public closeAmount?: number | bigint; + public closeAmount?: bigint; /** * (close) when set, indicates that the sending account should be closed and all @@ -5257,45 +7484,71 @@ export class TransactionPayment extends BaseModel { closeAmount?: number | bigint; closeRemainderTo?: string; }) { - super(); - this.amount = amount; + this.amount = ensureBigInt(amount); this.receiver = receiver; - this.closeAmount = closeAmount; + this.closeAmount = + typeof closeAmount === 'undefined' + ? undefined + : ensureBigInt(closeAmount); this.closeRemainderTo = closeRemainderTo; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionPayment.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['amount', this.amount], + ['receiver', this.receiver], + ['close-amount', this.closeAmount], + ['close-remainder-to', this.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}`); + static fromEncodingData(data: unknown): TransactionPayment { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionPayment: ${data}`); + } return new TransactionPayment({ - amount: data['amount'], - receiver: data['receiver'], - closeAmount: data['close-amount'], - closeRemainderTo: data['close-remainder-to'], + amount: data.get('amount'), + receiver: data.get('receiver'), + closeAmount: data.get('close-amount'), + closeRemainderTo: data.get('close-remainder-to'), }); - /* eslint-enable dot-notation */ } } /** * */ -export class TransactionResponse extends BaseModel { +export class TransactionResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'transaction', + valueSchema: Transaction.encodingSchema, + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; /** * Contains all fields common to all transactions and serves as an envelope to all @@ -5322,32 +7575,32 @@ export class TransactionResponse extends BaseModel { currentRound: number | bigint; transaction: Transaction; }) { - super(); - this.currentRound = currentRound; + this.currentRound = ensureBigInt(currentRound); this.transaction = transaction; + } - this.attribute_map = { - currentRound: 'current-round', - transaction: 'transaction', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['current-round', this.currentRound], + ['transaction', this.transaction.toEncodingData()], + ]); + } + + static fromEncodingData(data: unknown): TransactionResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionResponse: ${data}`); + } return new TransactionResponse({ - currentRound: data['current-round'], - transaction: Transaction.from_obj_for_encoding(data['transaction']), + currentRound: data.get('current-round'), + transaction: Transaction.fromEncodingData( + data.get('transaction') ?? new Map() + ), }); - /* eslint-enable dot-notation */ } } @@ -5355,7 +7608,37 @@ export class TransactionResponse extends BaseModel { * Validation signature associated with some data. Only one of the signatures * should be provided. */ -export class TransactionSignature extends BaseModel { +export class TransactionSignature implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'logicsig', + valueSchema: new OptionalSchema( + TransactionSignatureLogicsig.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'multisig', + valueSchema: new OptionalSchema( + TransactionSignatureMultisig.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'sig', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (lsig) Programatic transaction signature. * Definition: @@ -5394,38 +7677,49 @@ export class TransactionSignature extends BaseModel { 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 */ + this.sig = typeof sig === 'string' ? base64ToBytes(sig) : sig; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionSignature.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'logicsig', + typeof this.logicsig !== 'undefined' + ? this.logicsig.toEncodingData() + : undefined, + ], + [ + 'multisig', + typeof this.multisig !== 'undefined' + ? this.multisig.toEncodingData() + : undefined, + ], + ['sig', this.sig], + ]); + } + + static fromEncodingData(data: unknown): TransactionSignature { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionSignature: ${data}`); + } return new TransactionSignature({ logicsig: - typeof data['logicsig'] !== 'undefined' - ? TransactionSignatureLogicsig.from_obj_for_encoding(data['logicsig']) + typeof data.get('logicsig') !== 'undefined' + ? TransactionSignatureLogicsig.fromEncodingData(data.get('logicsig')) : undefined, multisig: - typeof data['multisig'] !== 'undefined' - ? TransactionSignatureMultisig.from_obj_for_encoding(data['multisig']) + typeof data.get('multisig') !== 'undefined' + ? TransactionSignatureMultisig.fromEncodingData(data.get('multisig')) : undefined, - sig: data['sig'], + sig: data.get('sig'), }); - /* eslint-enable dot-notation */ } } @@ -5434,7 +7728,38 @@ export class TransactionSignature extends BaseModel { * Definition: * data/transactions/logicsig.go */ -export class TransactionSignatureLogicsig extends BaseModel { +export class TransactionSignatureLogicsig implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'logic', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { + key: 'args', + valueSchema: new OptionalSchema( + new ArraySchema(new ByteArraySchema()) + ), + omitEmpty: true, + }, + { + key: 'multisig-signature', + valueSchema: new OptionalSchema( + TransactionSignatureMultisig.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'signature', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (l) Program signed by a signature or multi signature, or hashed to be the * address of ana ccount. Base64 encoded TEAL program. @@ -5479,45 +7804,47 @@ export class TransactionSignatureLogicsig extends BaseModel { multisigSignature?: TransactionSignatureMultisig; signature?: string | Uint8Array; }) { - super(); - this.logic = - typeof logic === 'string' - ? new Uint8Array(Buffer.from(logic, 'base64')) - : logic; + this.logic = typeof logic === 'string' ? base64ToBytes(logic) : 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}`); + typeof signature === 'string' ? base64ToBytes(signature) : signature; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionSignatureLogicsig.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['logic', this.logic], + ['args', this.args], + [ + 'multisig-signature', + typeof this.multisigSignature !== 'undefined' + ? this.multisigSignature.toEncodingData() + : undefined, + ], + ['signature', this.signature], + ]); + } + + static fromEncodingData(data: unknown): TransactionSignatureLogicsig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionSignatureLogicsig: ${data}`); + } return new TransactionSignatureLogicsig({ - logic: data['logic'], - args: data['args'], + logic: data.get('logic'), + args: data.get('args'), multisigSignature: - typeof data['multisig-signature'] !== 'undefined' - ? TransactionSignatureMultisig.from_obj_for_encoding( - data['multisig-signature'] + typeof data.get('multisig-signature') !== 'undefined' + ? TransactionSignatureMultisig.fromEncodingData( + data.get('multisig-signature') ) : undefined, - signature: data['signature'], + signature: data.get('signature'), }); - /* eslint-enable dot-notation */ } } @@ -5526,7 +7853,37 @@ export class TransactionSignatureLogicsig extends BaseModel { * Definition: * crypto/multisig.go : MultisigSig */ -export class TransactionSignatureMultisig extends BaseModel { +export class TransactionSignatureMultisig implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'subsignature', + valueSchema: new OptionalSchema( + new ArraySchema( + TransactionSignatureMultisigSubsignature.encodingSchema + ) + ), + omitEmpty: true, + }, + { + key: 'threshold', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'version', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (subsig) holds pairs of public key and signatures. */ @@ -5535,12 +7892,12 @@ export class TransactionSignatureMultisig extends BaseModel { /** * (thr) */ - public threshold?: number | bigint; + public threshold?: number; /** * (v) */ - public version?: number | bigint; + public version?: number; /** * Creates a new `TransactionSignatureMultisig` object. @@ -5557,38 +7914,74 @@ export class TransactionSignatureMultisig extends BaseModel { threshold?: number | bigint; version?: number | bigint; }) { - super(); this.subsignature = subsignature; - this.threshold = threshold; - this.version = version; + this.threshold = + typeof threshold === 'undefined' + ? undefined + : ensureSafeInteger(threshold); + this.version = + typeof version === 'undefined' ? undefined : ensureSafeInteger(version); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionSignatureMultisig.encodingSchema; + } - this.attribute_map = { - subsignature: 'subsignature', - threshold: 'threshold', - version: 'version', - }; + toEncodingData(): Map { + return new Map([ + [ + 'subsignature', + typeof this.subsignature !== 'undefined' + ? this.subsignature.map((v) => v.toEncodingData()) + : undefined, + ], + ['threshold', this.threshold], + ['version', this.version], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): TransactionSignatureMultisig { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): TransactionSignatureMultisig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionSignatureMultisig: ${data}`); + } return new TransactionSignatureMultisig({ subsignature: - typeof data['subsignature'] !== 'undefined' - ? data['subsignature'].map( - TransactionSignatureMultisigSubsignature.from_obj_for_encoding - ) + typeof data.get('subsignature') !== 'undefined' + ? data + .get('subsignature') + .map((v: unknown) => + TransactionSignatureMultisigSubsignature.fromEncodingData(v) + ) : undefined, - threshold: data['threshold'], - version: data['version'], + threshold: data.get('threshold'), + version: data.get('version'), }); - /* eslint-enable dot-notation */ } } -export class TransactionSignatureMultisigSubsignature extends BaseModel { +export class TransactionSignatureMultisigSubsignature implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'public-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'signature', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (pk) */ @@ -5611,32 +8004,36 @@ export class TransactionSignatureMultisigSubsignature extends BaseModel { publicKey?: string | Uint8Array; signature?: string | Uint8Array; }) { - super(); this.publicKey = - typeof publicKey === 'string' - ? new Uint8Array(Buffer.from(publicKey, 'base64')) - : publicKey; + typeof publicKey === 'string' ? base64ToBytes(publicKey) : publicKey; this.signature = - typeof signature === 'string' - ? new Uint8Array(Buffer.from(signature, 'base64')) - : signature; + typeof signature === 'string' ? base64ToBytes(signature) : signature; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionSignatureMultisigSubsignature.encodingSchema; + } - this.attribute_map = { - publicKey: 'public-key', - signature: 'signature', - }; + toEncodingData(): Map { + return new Map([ + ['public-key', this.publicKey], + ['signature', this.signature], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record + static fromEncodingData( + data: unknown ): TransactionSignatureMultisigSubsignature { - /* eslint-disable dot-notation */ + if (!(data instanceof Map)) { + throw new Error( + `Invalid decoded TransactionSignatureMultisigSubsignature: ${data}` + ); + } return new TransactionSignatureMultisigSubsignature({ - publicKey: data['public-key'], - signature: data['signature'], + publicKey: data.get('public-key'), + signature: data.get('signature'), }); - /* eslint-enable dot-notation */ } } @@ -5645,7 +8042,35 @@ export class TransactionSignatureMultisigSubsignature extends BaseModel { * Definition: * data/transactions/stateproof.go : StateProofTxnFields */ -export class TransactionStateProof extends BaseModel { +export class TransactionStateProof implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'message', + valueSchema: new OptionalSchema( + IndexerStateProofMessage.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'state-proof', + valueSchema: new OptionalSchema(StateProofFields.encodingSchema), + omitEmpty: true, + }, + { + key: 'state-proof-type', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * (spmsg) */ @@ -5662,7 +8087,7 @@ export class TransactionStateProof extends BaseModel { * (sptype) Type of the state proof. Integer representing an entry defined in * protocol/stateproof.go */ - public stateProofType?: number | bigint; + public stateProofType?: number; /** * Creates a new `TransactionStateProof` object. @@ -5682,46 +8107,89 @@ export class TransactionStateProof extends BaseModel { stateProof?: StateProofFields; stateProofType?: number | bigint; }) { - super(); this.message = message; this.stateProof = stateProof; - this.stateProofType = stateProofType; + this.stateProofType = + typeof stateProofType === 'undefined' + ? undefined + : ensureSafeInteger(stateProofType); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionStateProof.encodingSchema; + } - this.attribute_map = { - message: 'message', - stateProof: 'state-proof', - stateProofType: 'state-proof-type', - }; + toEncodingData(): Map { + return new Map([ + [ + 'message', + typeof this.message !== 'undefined' + ? this.message.toEncodingData() + : undefined, + ], + [ + 'state-proof', + typeof this.stateProof !== 'undefined' + ? this.stateProof.toEncodingData() + : undefined, + ], + ['state-proof-type', this.stateProofType], + ]); } - // eslint-disable-next-line camelcase - static from_obj_for_encoding( - data: Record - ): TransactionStateProof { - /* eslint-disable dot-notation */ + static fromEncodingData(data: unknown): TransactionStateProof { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionStateProof: ${data}`); + } return new TransactionStateProof({ message: - typeof data['message'] !== 'undefined' - ? IndexerStateProofMessage.from_obj_for_encoding(data['message']) + typeof data.get('message') !== 'undefined' + ? IndexerStateProofMessage.fromEncodingData(data.get('message')) : undefined, stateProof: - typeof data['state-proof'] !== 'undefined' - ? StateProofFields.from_obj_for_encoding(data['state-proof']) + typeof data.get('state-proof') !== 'undefined' + ? StateProofFields.fromEncodingData(data.get('state-proof')) : undefined, - stateProofType: data['state-proof-type'], + stateProofType: data.get('state-proof-type'), }); - /* eslint-enable dot-notation */ } } /** * */ -export class TransactionsResponse extends BaseModel { +export class TransactionsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'transactions', + valueSchema: new ArraySchema(Transaction.encodingSchema), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + /** * Round at which the results were computed. */ - public currentRound: number | bigint; + public currentRound: bigint; public transactions: Transaction[]; @@ -5747,36 +8215,34 @@ export class TransactionsResponse extends BaseModel { transactions: Transaction[]; nextToken?: string; }) { - super(); - this.currentRound = currentRound; + this.currentRound = ensureBigInt(currentRound); this.transactions = transactions; this.nextToken = nextToken; + } - this.attribute_map = { - currentRound: 'current-round', - transactions: 'transactions', - nextToken: 'next-token', - }; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionsResponse.encodingSchema; } - // 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}` - ); + toEncodingData(): Map { + return new Map([ + ['current-round', this.currentRound], + ['transactions', this.transactions.map((v) => v.toEncodingData())], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): TransactionsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionsResponse: ${data}`); + } return new TransactionsResponse({ - currentRound: data['current-round'], - transactions: data['transactions'].map(Transaction.from_obj_for_encoding), - nextToken: data['next-token'], + currentRound: data.get('current-round'), + transactions: (data.get('transactions') ?? []).map((v: unknown) => + Transaction.fromEncodingData(v) + ), + nextToken: data.get('next-token'), }); - /* eslint-enable dot-notation */ } } diff --git a/src/client/v2/indexer/searchAccounts.ts b/src/client/v2/indexer/searchAccounts.ts index d0465e292..8acb55fe2 100644 --- a/src/client/v2/indexer/searchAccounts.ts +++ b/src/client/v2/indexer/searchAccounts.ts @@ -1,4 +1,8 @@ -import JSONRequest from '../jsonrequest'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { AccountsResponse } from './models/types.js'; /** * Returns information about indexed accounts. @@ -11,7 +15,7 @@ import JSONRequest from '../jsonrequest'; * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accounts) * @category GET */ -export default class SearchAccounts extends JSONRequest { +export default class SearchAccounts extends JSONRequest { /** * @returns `/v2/accounts` */ @@ -186,8 +190,8 @@ export default class SearchAccounts extends JSONRequest { * * @param authAddr */ - authAddr(authAddr: string) { - this.query['auth-addr'] = authAddr; + authAddr(authAddr: string | Address) { + this.query['auth-addr'] = authAddr.toString(); return this; } @@ -266,4 +270,9 @@ export default class SearchAccounts extends JSONRequest { this.query.exclude = exclude; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AccountsResponse { + return decodeJSON(response.getJSONText(), AccountsResponse); + } } diff --git a/src/client/v2/indexer/searchForApplicationBoxes.ts b/src/client/v2/indexer/searchForApplicationBoxes.ts index b881de422..b3186290f 100644 --- a/src/client/v2/indexer/searchForApplicationBoxes.ts +++ b/src/client/v2/indexer/searchForApplicationBoxes.ts @@ -1,12 +1,9 @@ -import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; -import IntDecoding from '../../../types/intDecoding'; -import { BoxesResponse } from './models/types'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { BoxesResponse } from './models/types.js'; -export default class SearchForApplicationBoxes extends JSONRequest< - BoxesResponse, - Record -> { +export default class SearchForApplicationBoxes extends JSONRequest { /** * Returns information about indexed application boxes. * @@ -33,9 +30,11 @@ export default class SearchForApplicationBoxes extends JSONRequest< * @oaram index - application index. * @category GET */ - constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { - super(c, intDecoding); - this.index = index; + constructor( + c: HTTPClient, + private index: number + ) { + super(c); } /** @@ -95,7 +94,7 @@ export default class SearchForApplicationBoxes extends JSONRequest< } // eslint-disable-next-line class-methods-use-this - prepare(body: Record): BoxesResponse { - return BoxesResponse.from_obj_for_encoding(body); + prepare(response: HTTPClientResponse): BoxesResponse { + return decodeJSON(response.getJSONText(), BoxesResponse); } } diff --git a/src/client/v2/indexer/searchForApplications.ts b/src/client/v2/indexer/searchForApplications.ts index abf122fef..d18017922 100644 --- a/src/client/v2/indexer/searchForApplications.ts +++ b/src/client/v2/indexer/searchForApplications.ts @@ -1,4 +1,8 @@ -import JSONRequest from '../jsonrequest'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { ApplicationsResponse } from './models/types.js'; /** * Returns information about indexed applications. @@ -11,7 +15,7 @@ import JSONRequest from '../jsonrequest'; * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applications) * @category GET */ -export default class SearchForApplications extends JSONRequest { +export default class SearchForApplications extends JSONRequest { /** * @returns `/v2/applications` */ @@ -54,8 +58,8 @@ export default class SearchForApplications extends JSONRequest { * @param creator * @category query */ - creator(creator: string) { - this.query.creator = creator; + creator(creator: string | Address) { + this.query.creator = creator.toString(); return this; } @@ -131,4 +135,9 @@ export default class SearchForApplications extends JSONRequest { this.query['include-all'] = value; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): ApplicationsResponse { + return decodeJSON(response.getJSONText(), ApplicationsResponse); + } } diff --git a/src/client/v2/indexer/searchForAssets.ts b/src/client/v2/indexer/searchForAssets.ts index 79fa8c66d..7b23bcbcd 100644 --- a/src/client/v2/indexer/searchForAssets.ts +++ b/src/client/v2/indexer/searchForAssets.ts @@ -1,4 +1,8 @@ -import JSONRequest from '../jsonrequest'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { AssetsResponse } from './models/types.js'; /** * Returns information about indexed assets. @@ -11,7 +15,7 @@ import JSONRequest from '../jsonrequest'; * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assets) * @category GET */ -export default class SearchForAssets extends JSONRequest { +export default class SearchForAssets extends JSONRequest { /** * @returns `/v2/assets` */ @@ -55,8 +59,8 @@ export default class SearchForAssets extends JSONRequest { * @param creator * @category query */ - creator(creator: string) { - this.query.creator = creator; + creator(creator: string | Address) { + this.query.creator = creator.toString(); return this; } @@ -172,4 +176,9 @@ export default class SearchForAssets extends JSONRequest { this.query['include-all'] = value; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AssetsResponse { + return decodeJSON(response.getJSONText(), AssetsResponse); + } } diff --git a/src/client/v2/indexer/searchForTransactions.ts b/src/client/v2/indexer/searchForTransactions.ts index 4904aca8b..afb446551 100644 --- a/src/client/v2/indexer/searchForTransactions.ts +++ b/src/client/v2/indexer/searchForTransactions.ts @@ -1,5 +1,9 @@ -import JSONRequest from '../jsonrequest'; -import { base64StringFunnel } from './lookupAccountTransactions'; +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { base64StringFunnel } from './lookupAccountTransactions.js'; +import { Address } from '../../../encoding/address.js'; +import { TransactionsResponse } from './models/types.js'; /** * Returns information about indexed transactions. @@ -12,7 +16,7 @@ import { base64StringFunnel } from './lookupAccountTransactions'; * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2transactions) * @category GET */ -export default class SearchForTransactions extends JSONRequest { +export default class SearchForTransactions extends JSONRequest { /** * @returns `/v2/transactions` */ @@ -214,11 +218,12 @@ export default class SearchForTransactions extends JSONRequest { * .do(); * ``` * - * @param before - rfc3339 string + * @param before - rfc3339 string or Date object * @category query */ - beforeTime(before: string) { - this.query['before-time'] = before; + beforeTime(before: string | Date) { + this.query['before-time'] = + before instanceof Date ? before.toISOString() : before; return this; } @@ -234,11 +239,12 @@ export default class SearchForTransactions extends JSONRequest { * .do(); * ``` * - * @param after - rfc3339 string + * @param after - rfc3339 string or Date object * @category query */ - afterTime(after: string) { - this.query['after-time'] = after; + afterTime(after: string | Date) { + this.query['after-time'] = + after instanceof Date ? after.toISOString() : after; return this; } @@ -279,8 +285,8 @@ export default class SearchForTransactions extends JSONRequest { * @param address * @category query */ - address(address: string) { - this.query.address = address; + address(address: string | Address) { + this.query.address = address.toString(); return this; } @@ -431,4 +437,9 @@ export default class SearchForTransactions extends JSONRequest { this.query['currency-less-than'] = lesser; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): TransactionsResponse { + return decodeJSON(response.getJSONText(), TransactionsResponse); + } } diff --git a/src/client/v2/jsonrequest.ts b/src/client/v2/jsonrequest.ts index 5b1062d6b..c29914e75 100644 --- a/src/client/v2/jsonrequest.ts +++ b/src/client/v2/jsonrequest.ts @@ -1,5 +1,4 @@ -import HTTPClient from '../client'; -import IntDecoding from '../../types/intDecoding'; +import { HTTPClient, HTTPClientResponse } from '../client.js'; /** * Base abstract class for JSON requests. @@ -8,24 +7,16 @@ import IntDecoding from '../../types/intDecoding'; * * Body: The structure of the response's body */ -export default abstract class JSONRequest< - Data = Record, - Body = Data | Uint8Array -> { +export default abstract class JSONRequest { 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) { + constructor(client: HTTPClient) { this.c = client; this.query = {}; - this.intDecoding = intDecoding || IntDecoding.DEFAULT; } /** @@ -37,67 +28,58 @@ export default abstract class JSONRequest< /** * 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 + * Use this method to unpack response ata as needed after receiving it from the `do()` method. + * @param response - Response body received * @category JSONRequest */ - // eslint-disable-next-line class-methods-use-this - prepare(body: Body): Data { - return (body as unknown) as Data; + abstract prepare(response: HTTPClientResponse): Data; + + /** + * Execute the request + */ + protected executeRequest( + headers?: Record, + customOptions?: Record + ): Promise { + return this.c.get({ + relativePath: this.path(), + query: this.query, + requestHeaders: headers, + customOptions, + }); } /** - * Execute the request. + * Execute the request and decode the response. * @param headers - Additional headers to send in the request. Optional. - * @returns A promise which resolves to the parsed response data. + * @param customOptions - Additional options to pass to the underlying BaseHTTPClient. For + * {@link URLTokenBaseHTTPClient}, which is the default client, this will be treated as + * additional options to pass to the network `fetch` method. + * @returns A promise which resolves to the parsed response object. * @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); + async do( + headers?: Record, + customOptions?: Record + ): Promise { + const res = await this.executeRequest(headers, customOptions); + return this.prepare(res); } /** * Execute the request, but do not process the response data in any way. * @param headers - Additional headers to send in the request. Optional. + * @param customOptions - Additional options to pass to the underlying BaseHTTPClient. For + * {@link URLTokenBaseHTTPClient}, which is the default client, this will be treated as + * additional options to pass to the network `fetch` method. * @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); + async doRaw( + headers?: Record, + customOptions?: Record + ): Promise { + const res = await this.executeRequest(headers, customOptions); 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 index 14fae2922..504d74a00 100644 --- a/src/client/v2/serviceClient.ts +++ b/src/client/v2/serviceClient.ts @@ -1,7 +1,6 @@ -import HTTPClient from '../client'; -import IntDecoding from '../../types/intDecoding'; -import { BaseHTTPClient } from '../baseHTTPClient'; -import { TokenHeader } from '../urlTokenBaseHTTPClient'; +import { HTTPClient } from '../client.js'; +import { BaseHTTPClient } from '../baseHTTPClient.js'; +import { TokenHeader } from '../urlTokenBaseHTTPClient.js'; export type TokenHeaderIdentifier = | 'X-Indexer-API-Token' @@ -15,15 +14,15 @@ export type TokenHeaderIdentifier = * @param headerIdentifier - An identifier for the token header */ function convertTokenStringToTokenHeader( - token: string = '', - headerIdentifier: TokenHeaderIdentifier + headerIdentifier: TokenHeaderIdentifier, + token: string = '' ): TokenHeader { - const tokenHeader = {}; + const tokenHeader: TokenHeader = {}; if (token === '') { return tokenHeader; } tokenHeader[headerIdentifier] = token; - return tokenHeader as TokenHeader; + return tokenHeader; } function isBaseHTTPClient( @@ -38,8 +37,6 @@ function isBaseHTTPClient( export default abstract class ServiceClient { /** @ignore */ c: HTTPClient; - /** @ignore */ - intDecoding: IntDecoding; constructor( tokenHeaderIdentifier: TokenHeaderIdentifier, @@ -57,8 +54,8 @@ export default abstract class ServiceClient { let tokenHeader: TokenHeader; if (typeof tokenHeaderOrStrOrBaseClient === 'string') { tokenHeader = convertTokenStringToTokenHeader( - tokenHeaderOrStrOrBaseClient, - tokenHeaderIdentifier + tokenHeaderIdentifier, + tokenHeaderOrStrOrBaseClient ); } else { tokenHeader = tokenHeaderOrStrOrBaseClient; @@ -66,24 +63,5 @@ export default abstract class ServiceClient { 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/client/v2/untypedmodel.ts b/src/client/v2/untypedmodel.ts new file mode 100644 index 000000000..e7b19b5e7 --- /dev/null +++ b/src/client/v2/untypedmodel.ts @@ -0,0 +1,25 @@ +import { Encodable, MsgpackEncodingData } from '../../encoding/encoding.js'; +import { UntypedSchema } from '../../encoding/schema/index.js'; + +export class UntypedValue implements Encodable { + static readonly encodingSchema = new UntypedSchema(); + + public readonly data: MsgpackEncodingData; + + constructor(data: MsgpackEncodingData) { + this.data = data; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): UntypedSchema { + return UntypedValue.encodingSchema; + } + + public toEncodingData(): MsgpackEncodingData { + return this.data; + } + + public static fromEncodingData(data: unknown): UntypedValue { + return new UntypedValue(data as MsgpackEncodingData); + } +} diff --git a/src/composer.ts b/src/composer.ts index fb576e3b4..cd87dfd85 100644 --- a/src/composer.ts +++ b/src/composer.ts @@ -1,4 +1,3 @@ -import { Buffer } from 'buffer'; import { ABIAddressType, abiCheckTransactionType, @@ -10,32 +9,35 @@ import { abiTypeIsTransaction, ABIUintType, ABIValue, -} from './abi'; -import Algodv2 from './client/v2/algod/algod'; +} from './abi/index.js'; +import { AlgodClient } from './client/v2/algod/algod.js'; import { - SimulateResponse, SimulateRequest, SimulateRequestTransactionGroup, -} from './client/v2/algod/models/types'; -import { EncodedSignedTransaction } from './types'; -import { assignGroupID } from './group'; -import { makeApplicationCallTxnFromObject } from './makeTxn'; + PendingTransactionResponse, + SimulateResponse, +} from './client/v2/algod/models/types.js'; +import * as encoding from './encoding/encoding.js'; +import { Address } from './encoding/address.js'; +import { assignGroupID } from './group.js'; +import { makeApplicationCallTxnFromObject } from './makeTxn.js'; import { isTransactionWithSigner, TransactionSigner, TransactionWithSigner, -} from './signer'; -import { decodeSignedTransaction, Transaction } from './transaction'; +} from './signer.js'; +import { Transaction } from './transaction.js'; +import { SignedTransaction } from './signedTransaction.js'; import { BoxReference, OnApplicationComplete, SuggestedParams, -} from './types/transactions/base'; -import { waitForConfirmation } from './wait'; -import * as encoding from './encoding/encoding'; +} from './types/transactions/base.js'; +import { arrayEqual, stringifyJSON, ensureUint64 } from './utils/utils.js'; +import { waitForConfirmation } from './wait.js'; // First 4 bytes of SHA-512/256 hash of "return" -const RETURN_PREFIX = Buffer.from([21, 31, 124, 117]); +const RETURN_PREFIX = new Uint8Array([21, 31, 124, 117]); // The maximum number of arguments for an application call transaction const MAX_APP_ARGS = 16; @@ -63,7 +65,7 @@ export interface ABIResult { /** 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; + txInfo?: PendingTransactionResponse; } export enum AtomicTransactionComposerStatus { @@ -149,15 +151,16 @@ export class AtomicTransactionComposer { 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.transactions = this.transactions.map(({ txn, signer }) => { + const txnMap = txn.toEncodingData(); + // erase the group ID + txnMap.delete('grp'); + return { + // not quite a deep copy, but good enough for our purposes (modifying txn.group in buildGroup) + txn: Transaction.fromEncodingData(txnMap), + signer, + }; + }); theClone.methodCalls = new Map(this.methodCalls); return theClone; @@ -220,13 +223,13 @@ export class AtomicTransactionComposer { signer, }: { /** The ID of the smart contract to call. Set this to 0 to indicate an application creation call. */ - appID: number; + appID: number | bigint; /** 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; + sender: string | Address; /** 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. */ @@ -246,11 +249,11 @@ export class AtomicTransactionComposer { /** 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[]; + appAccounts?: Array; /** 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[]; + appForeignApps?: Array; /** 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[]; + appForeignAssets?: Array; /** The box references for this application call */ boxes?: BoxReference[]; /** The note value for this application call */ @@ -258,7 +261,7 @@ export class AtomicTransactionComposer { /** 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; + rekeyTo?: string | Address; /** A transaction signer that can authorize this application call from sender */ signer: TransactionSigner; }): void { @@ -277,7 +280,7 @@ export class AtomicTransactionComposer { ); } - if (appID === 0) { + if (BigInt(appID) === BigInt(0)) { if ( approvalProgram == null || clearProgram == null || @@ -387,12 +390,13 @@ export class AtomicTransactionComposer { } const resolvedRefIndexes: number[] = []; + // Converting addresses to string form for easier comparison const foreignAccounts: string[] = - appAccounts == null ? [] : appAccounts.slice(); - const foreignApps: number[] = - appForeignApps == null ? [] : appForeignApps.slice(); - const foreignAssets: number[] = - appForeignAssets == null ? [] : appForeignAssets.slice(); + appAccounts == null ? [] : appAccounts.map((addr) => addr.toString()); + const foreignApps: bigint[] = + appForeignApps == null ? [] : appForeignApps.map(ensureUint64); + const foreignAssets: bigint[] = + appForeignAssets == null ? [] : appForeignAssets.map(ensureUint64); for (let i = 0; i < refArgTypes.length; i++) { const refType = refArgTypes[i]; const refValue = refArgValues[i]; @@ -402,7 +406,11 @@ export class AtomicTransactionComposer { case ABIReferenceType.account: { const addressType = new ABIAddressType(); const address = addressType.decode(addressType.encode(refValue)); - resolved = populateForeignArray(address, foreignAccounts, sender); + resolved = populateForeignArray( + address, + foreignAccounts, + sender.toString() + ); break; } case ABIReferenceType.application: { @@ -413,7 +421,11 @@ export class AtomicTransactionComposer { `Expected safe integer for application value, got ${refAppID}` ); } - resolved = populateForeignArray(Number(refAppID), foreignApps, appID); + resolved = populateForeignArray( + refAppID, + foreignApps, + ensureUint64(appID) + ); break; } case ABIReferenceType.asset: { @@ -424,7 +436,7 @@ export class AtomicTransactionComposer { `Expected safe integer for asset value, got ${refAssetID}` ); } - resolved = populateForeignArray(Number(refAssetID), foreignAssets); + resolved = populateForeignArray(refAssetID, foreignAssets); break; } default: @@ -435,7 +447,7 @@ export class AtomicTransactionComposer { } for (let i = 0; i < resolvedRefIndexes.length; i++) { - const basicArgIndex = refArgIndexToBasicArgIndex.get(i); + const basicArgIndex = refArgIndexToBasicArgIndex.get(i)!; basicArgValues[basicArgIndex] = resolvedRefIndexes[i]; } @@ -457,7 +469,7 @@ export class AtomicTransactionComposer { const appCall = { txn: makeApplicationCallTxnFromObject({ - from: sender, + sender, appIndex: appID, appArgs: appArgsEncoded, accounts: foreignAccounts, @@ -533,7 +545,7 @@ export class AtomicTransactionComposer { indexesPerSigner.set(signer, []); } - indexesPerSigner.get(signer).push(i); + indexesPerSigner.get(signer)!.push(i); } const orderedSigners = Array.from(indexesPerSigner); @@ -559,13 +571,17 @@ export class AtomicTransactionComposer { } } - if (!signedTxns.every((sig) => sig != null)) { + function fullyPopulated(a: Array): a is Uint8Array[] { + return a.every((v) => v != null); + } + + if (!fullyPopulated(signedTxns)) { throw new Error(`Missing signatures. Got ${signedTxns}`); } const txIDs = signedTxns.map((stxn, index) => { try { - return decodeSignedTransaction(stxn).txn.txID(); + return encoding.decodeMsgpack(stxn, SignedTransaction).txn.txID(); } catch (err) { throw new Error( `Cannot decode signed transaction at index ${index}. ${err}` @@ -593,7 +609,7 @@ export class AtomicTransactionComposer { * * @returns A promise that, upon success, resolves to a list of TxIDs of the submitted transactions. */ - async submit(client: Algodv2): Promise { + async submit(client: AlgodClient): Promise { if (this.status > AtomicTransactionComposerStatus.SUBMITTED) { throw new Error('Transaction group cannot be resubmitted'); } @@ -624,7 +640,7 @@ export class AtomicTransactionComposer { * in this group (ABIResult[]) and the SimulateResponse object. */ async simulate( - client: Algodv2, + client: AlgodClient, request?: SimulateRequest ): Promise<{ methodResults: ABIResult[]; @@ -637,8 +653,8 @@ export class AtomicTransactionComposer { } const stxns = await this.gatherSignatures(); - const txnObjects: EncodedSignedTransaction[] = stxns.map( - (stxn) => encoding.decode(stxn) as EncodedSignedTransaction + const txnObjects: SignedTransaction[] = stxns.map((stxn) => + encoding.decodeMsgpack(stxn, SignedTransaction) ); const currentRequest: SimulateRequest = @@ -671,7 +687,7 @@ export class AtomicTransactionComposer { AtomicTransactionComposer.parseMethodResponse( method, methodResult, - pendingInfo.get_obj_for_encoding() + pendingInfo ) ); } @@ -697,7 +713,7 @@ export class AtomicTransactionComposer { * one element for each method call transaction in this group. */ async execute( - client: Algodv2, + client: AlgodClient, waitRounds: number ): Promise<{ confirmedRound: number; @@ -725,7 +741,7 @@ export class AtomicTransactionComposer { ); this.status = AtomicTransactionComposerStatus.COMMITTED; - const confirmedRound: number = confirmedTxnInfo['confirmed-round']; + const confirmedRound = Number(confirmedTxnInfo.confirmedRound); const methodResults: ABIResult[] = []; @@ -751,7 +767,7 @@ export class AtomicTransactionComposer { pendingInfo ); } catch (err) { - methodResult.decodeError = err; + methodResult.decodeError = err as Error; } methodResults.push(methodResult); @@ -775,23 +791,30 @@ export class AtomicTransactionComposer { static parseMethodResponse( method: ABIMethod, methodResult: ABIResult, - pendingInfo: Record + pendingInfo: PendingTransactionResponse ): ABIResult { const returnedResult: ABIResult = methodResult; try { returnedResult.txInfo = pendingInfo; if (method.returns.type !== 'void') { - const logs: string[] = pendingInfo.logs || []; + const logs = pendingInfo.logs || []; if (logs.length === 0) { - throw new Error('App call transaction did not log a return value'); + throw new Error( + `App call transaction did not log a return value ${stringifyJSON( + pendingInfo + )}` + ); } - - const lastLog = Buffer.from(logs[logs.length - 1], 'base64'); + const lastLog = logs[logs.length - 1]; if ( lastLog.byteLength < 4 || - !lastLog.slice(0, 4).equals(RETURN_PREFIX) + !arrayEqual(lastLog.slice(0, 4), RETURN_PREFIX) ) { - throw new Error('App call transaction did not log a return value'); + throw new Error( + `App call transaction did not log a ABI return value ${stringifyJSON( + pendingInfo + )}` + ); } returnedResult.rawReturnValue = new Uint8Array(lastLog.slice(4)); @@ -800,7 +823,7 @@ export class AtomicTransactionComposer { ); } } catch (err) { - returnedResult.decodeError = err; + returnedResult.decodeError = err as Error; } return returnedResult; diff --git a/src/dryrun.ts b/src/dryrun.ts index ce9739bc5..f7f2bc6c7 100644 --- a/src/dryrun.ts +++ b/src/dryrun.ts @@ -1,48 +1,24 @@ -import { Buffer } from 'buffer'; -import AlgodClient from './client/v2/algod/algod'; +import { AlgodClient } from './client/v2/algod/algod.js'; import { - AccountStateDelta, + Account, Application, ApplicationParams, ApplicationStateSchema, DryrunRequest, DryrunSource, - EvalDeltaKeyValue, + DryrunTxnResult, + DryrunState, TealValue, -} from './client/v2/algod/models/types'; -import { SignedTransaction } from './transaction'; -import { TransactionType } from './types/transactions'; -import { encodeAddress, getApplicationAddress } from './encoding/address'; +} from './client/v2/algod/models/types.js'; +import { getApplicationAddress } from './encoding/address.js'; +import { bytesToHex } from './encoding/binarydata.js'; +import { SignedTransaction } from './signedTransaction.js'; +import { TransactionType } from './types/transactions/index.js'; +import { stringifyJSON } from './utils/utils.js'; 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 @@ -50,6 +26,8 @@ function decodePrograms(ap: AppWithAppParams): AppWithAppParams { * @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 + * @param round - the round available to some TEAL scripts. Defaults to the current round on the network. + * @param sources - TEAL source text that gets uploaded, compiled, and inserted into transactions or application state. * @returns the DryrunRequest object constructed from the SignedTransactions passed */ export async function createDryrun({ @@ -67,59 +45,59 @@ export async function createDryrun({ round?: number | bigint; sources?: DryrunSource[]; }): Promise { - const appInfos = []; - const acctInfos = []; + const appInfos: Application[] = []; + const acctInfos: Account[] = []; - const apps: number[] = []; - const assets: number[] = []; + const apps: bigint[] = []; + const assets: bigint[] = []; const accts: string[] = []; for (const t of txns) { if (t.txn.type === TransactionType.appl) { - accts.push(encodeAddress(t.txn.from.publicKey)); + accts.push(t.txn.sender.toString()); - if (t.txn.appAccounts) - accts.push(...t.txn.appAccounts.map((a) => encodeAddress(a.publicKey))); + accts.push(...t.txn.applicationCall!.accounts.map((a) => a.toString())); - if (t.txn.appForeignApps) { - apps.push(...t.txn.appForeignApps); - accts.push( - ...t.txn.appForeignApps.map((aidx) => getApplicationAddress(aidx)) - ); - } + apps.push(...t.txn.applicationCall!.foreignApps); + accts.push( + ...t.txn + .applicationCall!.foreignApps.map(getApplicationAddress) + .map((a) => a.toString()) + ); - if (t.txn.appForeignAssets) assets.push(...t.txn.appForeignAssets); + assets.push(...t.txn.applicationCall!.foreignAssets); // Create application, - if (t.txn.appIndex === undefined || t.txn.appIndex === 0) { + if (t.txn.applicationCall!.appIndex === BigInt(0)) { appInfos.push( new Application({ id: defaultAppId, params: new ApplicationParams({ - creator: encodeAddress(t.txn.from.publicKey), - approvalProgram: t.txn.appApprovalProgram, - clearStateProgram: t.txn.appClearProgram, + creator: t.txn.sender.toString(), + approvalProgram: t.txn.applicationCall!.approvalProgram, + clearStateProgram: t.txn.applicationCall!.clearProgram, localStateSchema: new ApplicationStateSchema({ - numUint: t.txn.appLocalInts, - numByteSlice: t.txn.appLocalByteSlices, + numUint: t.txn.applicationCall!.numLocalInts, + numByteSlice: t.txn.applicationCall!.numLocalByteSlices, }), globalStateSchema: new ApplicationStateSchema({ - numUint: t.txn.appGlobalInts, - numByteSlice: t.txn.appGlobalByteSlices, + numUint: t.txn.applicationCall!.numGlobalInts, + numByteSlice: t.txn.applicationCall!.numGlobalByteSlices, }), }), }) ); } else { - apps.push(t.txn.appIndex); - accts.push(getApplicationAddress(t.txn.appIndex)); + const { appIndex } = t.txn.applicationCall!; + apps.push(appIndex); + accts.push(getApplicationAddress(appIndex).toString()); } } } // Dedupe and add creator to accts array const assetPromises = []; - for (const assetId of [...new Set(assets)]) { + for (const assetId of new Set(assets)) { assetPromises.push( client .getAssetByID(assetId) @@ -134,33 +112,26 @@ export async function createDryrun({ // Dedupe and get app info for all apps const appPromises = []; - for (const appId of [...new Set(apps)]) { + 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); + appInfos.push(appInfo); + accts.push(appInfo.params.creator.toString()); }) ); } await Promise.all(appPromises); const acctPromises = []; - for (const acct of [...new Set(accts)]) { + 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); }) ); @@ -168,89 +139,17 @@ export async function createDryrun({ await Promise.all(acctPromises); return new DryrunRequest({ - txns: txns.map((st) => ({ ...st, txn: st.txn.get_obj_for_encoding() })), + txns: txns.slice(), accounts: acctInfos, apps: appInfos, - latestTimestamp, - round, - protocolVersion, - sources, + latestTimestamp: latestTimestamp ?? 0, + round: round ?? 0, + protocolVersion: protocolVersion ?? '', + sources: 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 { +export interface StackPrinterConfig { maxValueWidth: number | undefined; topOfStackFirst: boolean | undefined; } @@ -275,7 +174,7 @@ function scratchToString( continue; } - if (JSON.stringify(prevScratch[idx]) !== JSON.stringify(currScratch[idx])) { + if (stringifyJSON(prevScratch[idx]) !== stringifyJSON(currScratch[idx])) { newScratchIdx = idx; } } @@ -284,23 +183,23 @@ function scratchToString( const newScratch = currScratch[newScratchIdx]; if (newScratch.bytes.length > 0) { - return `${newScratchIdx} = 0x${Buffer.from( - newScratch.bytes, - 'base64' - ).toString('hex')}`; + return `${newScratchIdx} = 0x${bytesToHex(newScratch.bytes)}`; } return `${newScratchIdx} = ${newScratch.uint.toString()}`; } -function stackToString(stack: DryrunStackValue[], reverse: boolean): string { +function stackToString( + stack: TealValue[], + reverse: boolean | undefined +): string { const svs = reverse ? stack.reverse() : stack; return `[${svs - .map((sv: DryrunStackValue) => { + .map((sv) => { switch (sv.type) { case 1: - return `0x${Buffer.from(sv.bytes, 'base64').toString('hex')}`; + return `0x${bytesToHex(sv.bytes)}`; case 2: - return `${sv.uint.toString()}`; + return sv.uint.toString(); default: return ''; } @@ -308,162 +207,86 @@ function stackToString(stack: DryrunStackValue[], reverse: boolean): string { .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') - ); +function dryrunTrace( + trace: DryrunState[], + 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 < trace.length; idx++) { + const { line, error, pc, scratch, stack } = trace[idx]; + + const currScratch = scratch !== undefined ? scratch : []; + const prevScratch = + idx > 0 && trace[idx - 1].scratch !== undefined + ? 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), + ]); } - 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]; } - - // 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 newVal; + }, new Array(lines[0].length).fill(0)); + + return `${lines + .map((line) => + line + .map((v, idx) => v.padEnd(maxLengths[idx] + 1, ' ')) + .join('|') + .trim() ) - return ''; - - let conf = spc; - if (spc === undefined) - conf = { - maxValueWidth: defaultMaxWidth, - topOfStackFirst: true, - } as StackPrinterConfig; - - return DryrunTransactionResult.trace( - this.logicSigTrace, - this.logicSigDisassembly, - conf - ); - } + .join('\n')}\n`; } -interface DryrunResultResponse { - ['error']: string; - ['protocol-version']: string; - ['txns']: DryrunTransactionResultResponse[]; +export function dryrunTxnResultAppTrace( + result: DryrunTxnResult, + spc?: StackPrinterConfig +): string { + if (!result.appCallTrace || !result.disassembly) return ''; + + let conf = spc; + if (spc !== undefined) conf = spc; + else { + conf = { + maxValueWidth: defaultMaxWidth, + topOfStackFirst: false, + }; + } + + return dryrunTrace(result.appCallTrace, result.disassembly, conf); } -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) - ); +export function dryrunTxnResultLogicSigTrace( + result: DryrunTxnResult, + spc?: StackPrinterConfig +): string { + if (!result.logicSigTrace || !result.logicSigDisassembly) return ''; + + let conf: StackPrinterConfig; + if (spc !== undefined) conf = spc; + else { + conf = { + maxValueWidth: defaultMaxWidth, + topOfStackFirst: true, + }; } + + return dryrunTrace(result.logicSigTrace, result.logicSigDisassembly, conf); } diff --git a/src/encoding/address.ts b/src/encoding/address.ts index b1e287ebc..77a2f7509 100644 --- a/src/encoding/address.ts +++ b/src/encoding/address.ts @@ -1,83 +1,145 @@ -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; +import * as nacl from '../nacl/naclWrappers.js'; +import * as utils from '../utils/utils.js'; +import { encodeUint64 } from './uint64.js'; +import { bytesToHex } from './binarydata.js'; + +export const ALGORAND_ADDRESS_BYTE_LENGTH = 36; +export const ALGORAND_CHECKSUM_BYTE_LENGTH = 4; +export 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'; + +function checksumFromPublicKey(pk: Uint8Array): Uint8Array { + return Uint8Array.from( + nacl + .genericHash(pk) + .slice( + nacl.HASH_BYTES_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH, + nacl.HASH_BYTES_LENGTH + ) + ); +} /** - * 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 + * Represents an Algorand address */ -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, +export class Address { + /** + * The binary form of the address. For standard accounts, this is the public key. + */ + public readonly publicKey: Uint8Array; + + /** + * Create a new Address object from its binary form. + * @param publicKey - The binary form of the address. Must be 32 bytes. + */ + constructor(publicKey: Uint8Array) { + if (!(publicKey instanceof Uint8Array)) { + throw new Error( + `${MALFORMED_ADDRESS_ERROR_MSG}: ${publicKey} is not Uint8Array, type ${typeof publicKey}` + ); + } + if ( + publicKey.length !== ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH ) - ); - const cs = new Uint8Array( - decoded.slice(nacl.PUBLIC_KEY_LENGTH, ALGORAND_ADDRESS_BYTE_LENGTH) - ); + throw new Error( + `${MALFORMED_ADDRESS_ERROR_MSG}: 0x${bytesToHex(publicKey)}, length ${publicKey.length}` + ); + this.publicKey = publicKey; + } - // Compute checksum - const checksum = nacl - .genericHash(pk) - .slice( - nacl.HASH_BYTES_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH, - nacl.HASH_BYTES_LENGTH + /** + * Check if the address is equal to another address. + */ + equals(other: Address): boolean { + return ( + other instanceof Address && + utils.arrayEqual(this.publicKey, other.publicKey) ); + } + + /** + * Compute the 4 byte checksum of the address. + */ + checksum(): Uint8Array { + return checksumFromPublicKey(this.publicKey); + } + + /** + * Encode the address into a string form. + */ + toString(): string { + const addr = base32.encode( + utils.concatArrays(this.publicKey, this.checksum()) + ); + return addr.slice(0, ALGORAND_ADDRESS_LENGTH); // removing the extra '====' + } - // Check if the checksum and the address are equal - if (!utils.arrayEqual(checksum, cs)) - throw new Error(CHECKSUM_ADDRESS_ERROR_MSG); + /** + * Decode an address from a string. + * @param address - The address to decode. Must be 58 bytes long. + * @returns An Address object corresponding to the input string. + */ + static fromString(address: string): Address { + if (typeof address !== 'string') + throw new Error( + `${MALFORMED_ADDRESS_ERROR_MSG}: expected string, got ${typeof address}, ${address}` + ); + if (address.length !== ALGORAND_ADDRESS_LENGTH) + throw new Error( + `${MALFORMED_ADDRESS_ERROR_MSG}: expected length ${ALGORAND_ADDRESS_LENGTH}, got ${address.length}: ${address}` + ); + + // try to decode + const decoded = base32.decode.asBytes(address); + // Sanity check + if (decoded.length !== ALGORAND_ADDRESS_BYTE_LENGTH) + throw new Error( + `${MALFORMED_ADDRESS_ERROR_MSG}: expected byte length ${ALGORAND_ADDRESS_BYTE_LENGTH}, got ${decoded.length}` + ); + + // 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) + ); + const checksum = checksumFromPublicKey(pk); + // Check if the checksum and the address are equal + if (!utils.arrayEqual(checksum, cs)) + throw new Error(CHECKSUM_ADDRESS_ERROR_MSG); + + return new Address(pk); + } - return { publicKey: pk, checksum: cs }; + /** + * Get the zero address. + */ + static zeroAddress(): Address { + return new Address( + new Uint8Array( + ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH + ) + ); + } +} + +/** + * 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 { + return Address.fromString(address); } /** @@ -85,10 +147,10 @@ export function decodeAddress(address: string): Address { * @param address - an Algorand address with checksum. * @returns true if valid, false otherwise */ -export function isValidAddress(address: string) { +export function isValidAddress(address: string): boolean { // Try to decode try { - decodeAddress(address); + Address.fromString(address); } catch (e) { return false; } @@ -100,93 +162,19 @@ export function isValidAddress(address: string) { * @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 '====' +export function encodeAddress(address: Uint8Array): string { + return new Address(address).toString(); } -/** - * 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 })); -} +const APP_ID_PREFIX = new TextEncoder().encode('appID'); /** * 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 { +export function getApplicationAddress(appID: number | bigint): Address { const toBeSigned = utils.concatArrays(APP_ID_PREFIX, encodeUint64(appID)); const hash = nacl.genericHash(toBeSigned); - return encodeAddress(new Uint8Array(hash)); + return new Address(Uint8Array.from(hash)); } diff --git a/src/encoding/bigint.ts b/src/encoding/bigint.ts index cc039904d..b29301a49 100644 --- a/src/encoding/bigint.ts +++ b/src/encoding/bigint.ts @@ -1,5 +1,3 @@ -import { Buffer } from 'buffer'; - /** * bigIntToBytes converts a BigInt to a big-endian Uint8Array for encoding. * @param bi - The bigint to convert. @@ -27,9 +25,9 @@ export function bigIntToBytes(bi: bigint | number, size: number) { */ export function bytesToBigInt(bytes: Uint8Array) { let res = BigInt(0); - const buf = Buffer.from(bytes); + const buf = new DataView(bytes.buffer, bytes.byteOffset); for (let i = 0; i < bytes.length; i++) { - res = BigInt(Number(buf.readUIntBE(i, 1))) + res * BigInt(256); + res = BigInt(Number(buf.getUint8(i))) + res * BigInt(256); } return res; } diff --git a/src/encoding/binarydata.ts b/src/encoding/binarydata.ts new file mode 100644 index 000000000..1a6e726fe --- /dev/null +++ b/src/encoding/binarydata.ts @@ -0,0 +1,80 @@ +import { isNode } from '../utils/utils.js'; + +/** + * Convert a base64 string to a Uint8Array for Node.js and browser environments. + * @returns A Uint8Array + */ +export function base64ToBytes(base64String: string): Uint8Array { + if (isNode()) { + return new Uint8Array(Buffer.from(base64String, 'base64')); + } + /* eslint-env browser */ + const binString = atob(base64String); + return Uint8Array.from(binString, (m) => m.codePointAt(0)!); +} + +/** + * Convert a Uint8Array to a base64 string for Node.js and browser environments. + * @returns A base64 string + */ +export function bytesToBase64(byteArray: Uint8Array): string { + if (isNode()) { + return Buffer.from(byteArray).toString('base64'); + } + /* eslint-env browser */ + const binString = Array.from(byteArray, (x) => String.fromCodePoint(x)).join( + '' + ); + return btoa(binString); +} + +/** + * Convert a byte array to a UTF-8 string. Warning: not all byte arrays are valid UTF-8. + * @returns A decoded string + */ +export function bytesToString(byteArray: Uint8Array): string { + return new TextDecoder().decode(byteArray); +} + +/** + * Returns a Uint8Array given an input string or Uint8Array. + * @returns A base64 string + */ +export function coerceToBytes(input: Uint8Array | string): Uint8Array { + if (typeof input === 'string') { + return new TextEncoder().encode(input); + } + return input; +} + +/** + * Convert a Uint8Array to a hex string for Node.js and browser environments. + * @returns A hex string + */ +export function bytesToHex(byteArray: Uint8Array): string { + if (isNode()) { + return Buffer.from(byteArray).toString('hex'); + } + return Array.from(byteArray) + .map((i) => i.toString(16).padStart(2, '0')) + .join(''); +} + +/** + * Convert a hex string to Uint8Array for Node.js and browser environments. + * @returns A Uint8Array + */ +export function hexToBytes(hexString: string): Uint8Array { + if (isNode()) { + return Buffer.from(hexString, 'hex'); + } + let hex = hexString; + if (hexString.length % 2 !== 0) { + hex = hexString.padStart(1, '0'); + } + const byteArray = new Uint8Array(hex.length / 2); + for (let i = 0; i < hex.length / 2; i++) { + byteArray[i] = parseInt(hex.slice(2 * i, 2 * i + 2), 16); + } + return byteArray; +} diff --git a/src/encoding/encoding.ts b/src/encoding/encoding.ts index c770f57ad..b274411ee 100644 --- a/src/encoding/encoding.ts +++ b/src/encoding/encoding.ts @@ -10,7 +10,17 @@ * 5. Binary blob should be used for binary data and string for strings * */ -import * as msgpack from 'algo-msgpack-with-bigint'; +import { + encode as msgpackEncode, + EncoderOptions, + decode as msgpackDecode, + DecoderOptions, + IntMode, + RawBinaryString, +} from 'algorand-msgpack'; +import { bytesToBase64, coerceToBytes } from './binarydata.js'; +import IntDecoding from '../types/intDecoding.js'; +import { stringifyJSON, parseJSON, arrayEqual } from '../utils/utils.js'; // Errors export const ERROR_CONTAINS_EMPTY_STRING = @@ -34,34 +44,569 @@ function containsEmpty(obj: Record) { } /** - * rawEncode encodes objects using msgpack, regardless of whether there are + * msgpackRawEncode 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) { +export function msgpackRawEncode(obj: unknown) { // enable the canonical option - const options = { sortKeys: true }; - return msgpack.encode(obj, options); + const options: EncoderOptions = { sortKeys: true }; + return msgpackEncode(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 + * encodeObj takes a javascript object and returns its msgpack encoding + * Note that the encoding sorts the fields alphabetically + * @param o - js object to be encoded. Must not contain empty or 0 values. + * @returns Uint8Array binary representation * @throws Error containing ERROR_CONTAINS_EMPTY_STRING if the object contains empty or zero values + * + * @deprecated Use {@link msgpackRawEncode} instead. Note that function does not + * check for empty values like this one does. */ -export function encode(obj: Record) { +export function encodeObj(obj: Record) { // Check for empty values const emptyCheck = containsEmpty(obj); if (emptyCheck.containsEmpty) { throw new Error(ERROR_CONTAINS_EMPTY_STRING + emptyCheck.firstEmptyKey); } + return msgpackRawEncode(obj); +} - // enable the canonical option - return rawEncode(obj); +function intDecodingToIntMode(intDecoding: IntDecoding): IntMode { + switch (intDecoding) { + case IntDecoding.UNSAFE: + return IntMode.UNSAFE_NUMBER; + case IntDecoding.SAFE: + return IntMode.SAFE_NUMBER; + case IntDecoding.MIXED: + return IntMode.MIXED; + case IntDecoding.BIGINT: + return IntMode.BIGINT; + default: + throw new Error(`Invalid intDecoding: ${intDecoding}`); + } +} + +/** + * Decodes msgpack bytes into a plain JavaScript object. + * @param buffer - The msgpack bytes to decode + * @param options - Options for decoding, including int decoding mode. See {@link IntDecoding} for more information. + * @returns The decoded object + */ +export function msgpackRawDecode( + buffer: ArrayLike, + options?: { intDecoding: IntDecoding } +) { + const decoderOptions: DecoderOptions = { + intMode: options?.intDecoding + ? intDecodingToIntMode(options?.intDecoding) + : IntMode.BIGINT, + }; + return msgpackDecode(buffer, decoderOptions); +} + +/** + * decodeObj takes a Uint8Array and returns its javascript obj + * @param o - Uint8Array to decode + * @returns object + * + * @deprecated Use {@link msgpackRawDecode} instead. Note that this function uses `IntDecoding.MIXED` + * while `msgpackRawDecode` defaults to `IntDecoding.BIGINT` for int decoding, though it is + * configurable. + */ +export function decodeObj(o: ArrayLike) { + return msgpackRawDecode(o, { intDecoding: IntDecoding.MIXED }); +} + +/** + * Decodes msgpack bytes into a Map object. This supports decoding non-string map keys. + * @param encoded - The msgpack bytes to decode + * @param options - Options for decoding, including int decoding mode. See {@link IntDecoding} for more information. + * @returns The decoded Map object + */ +export function msgpackRawDecodeAsMap( + encoded: ArrayLike, + options?: { intDecoding: IntDecoding } +) { + const decoderOptions: DecoderOptions = { + intMode: options?.intDecoding + ? intDecodingToIntMode(options?.intDecoding) + : IntMode.BIGINT, + useMap: true, + }; + return msgpackDecode(encoded, decoderOptions); +} + +function msgpackRawDecodeAsMapWithRawStrings( + encoded: ArrayLike, + options?: { intDecoding: IntDecoding } +) { + const decoderOptions: DecoderOptions = { + intMode: options?.intDecoding + ? intDecodingToIntMode(options?.intDecoding) + : IntMode.BIGINT, + useMap: true, + rawBinaryStringKeys: true, + rawBinaryStringValues: true, + useRawBinaryStringClass: true, + }; + return msgpackDecode(encoded, decoderOptions); +} + +export type MsgpackEncodingData = + | null + | undefined + | string + | number + | bigint + | boolean + | Uint8Array + | MsgpackEncodingData[] + | Map; + +export type JSONEncodingData = + | null + | undefined + | string + | number + | bigint + | boolean + | JSONEncodingData[] + | { [key: string]: JSONEncodingData }; + +export function msgpackEncodingDataToJSONEncodingData( + e: MsgpackEncodingData +): JSONEncodingData { + if (e === null || e === undefined) { + return e; + } + if (e instanceof Uint8Array) { + return bytesToBase64(e); + } + if (Array.isArray(e)) { + return e.map(msgpackEncodingDataToJSONEncodingData); + } + if (e instanceof Map) { + const obj: { [key: string]: JSONEncodingData } = {}; + for (const [k, v] of e) { + if (typeof k !== 'string') { + throw new Error(`JSON map key must be a string: ${k}`); + } + obj[k] = msgpackEncodingDataToJSONEncodingData(v); + } + return obj; + } + return e; +} + +export function jsonEncodingDataToMsgpackEncodingData( + e: JSONEncodingData +): MsgpackEncodingData { + if (e === null || e === undefined) { + return e; + } + if ( + typeof e === 'string' || // Note, this will not convert base64 to Uint8Array + typeof e === 'number' || + typeof e === 'bigint' || + typeof e === 'boolean' + ) { + return e; + } + if (Array.isArray(e)) { + return e.map(jsonEncodingDataToMsgpackEncodingData); + } + if (typeof e === 'object') { + const obj = new Map(); + for (const [key, value] of Object.entries(e)) { + obj.set(key, jsonEncodingDataToMsgpackEncodingData(value)); + } + return obj; + } + throw new Error(`Invalid JSON encoding data: ${e}`); } -export function decode(buffer: ArrayLike) { - return msgpack.decode(buffer); +/* eslint-disable class-methods-use-this */ +/* eslint-disable no-useless-constructor,no-empty-function */ + +enum MsgpackObjectPathSegmentKind { + MAP_VALUE, + ARRAY_ELEMENT, +} + +interface MsgpackObjectPathSegment { + kind: MsgpackObjectPathSegmentKind; + key: string | number | bigint | Uint8Array | RawBinaryString; +} + +/** + * This class is used to index into an encoded msgpack object and extract raw strings. + */ +export class MsgpackRawStringProvider { + // eslint-disable-next-line no-use-before-define + private readonly parent?: MsgpackRawStringProvider; + + private readonly baseObjectBytes?: ArrayLike; + + private readonly segment?: MsgpackObjectPathSegment; + + private resolvedCache: MsgpackEncodingData = null; + private resolvedCachePresent = false; + + public constructor({ + parent, + segment, + baseObjectBytes, + }: + | { + parent: MsgpackRawStringProvider; + segment: MsgpackObjectPathSegment; + baseObjectBytes?: undefined; + } + | { + parent?: undefined; + segment?: undefined; + baseObjectBytes: ArrayLike; + }) { + this.parent = parent; + this.segment = segment; + this.baseObjectBytes = baseObjectBytes; + } + + /** + * Create a new provider that resolves to the current provider's map value at the given key. + */ + public withMapValue( + key: string | number | bigint | Uint8Array | RawBinaryString + ): MsgpackRawStringProvider { + return new MsgpackRawStringProvider({ + parent: this, + segment: { + kind: MsgpackObjectPathSegmentKind.MAP_VALUE, + key, + }, + }); + } + + /** + * Create a new provider that resolves to the current provider's array element at the given index. + */ + public withArrayElement(index: number): MsgpackRawStringProvider { + return new MsgpackRawStringProvider({ + parent: this, + segment: { + kind: MsgpackObjectPathSegmentKind.ARRAY_ELEMENT, + key: index, + }, + }); + } + + /** + * Get the raw string at the current location. If the current location is not a raw string, an error is thrown. + */ + public getRawStringAtCurrentLocation(): Uint8Array { + const resolved = this.resolve(); + if (resolved instanceof RawBinaryString) { + // Decoded rawBinaryValue will always be a Uint8Array + return resolved.rawBinaryValue as Uint8Array; + } + throw new Error( + `Invalid type. Expected RawBinaryString, got ${resolved} (${typeof resolved})` + ); + } + + /** + * Get the raw string map keys and values at the current location. If the current location is not a map, an error is thrown. + */ + public getRawStringKeysAndValuesAtCurrentLocation(): Map< + Uint8Array, + MsgpackEncodingData + > { + const resolved = this.resolve(); + if (!(resolved instanceof Map)) { + throw new Error( + `Invalid type. Expected Map, got ${resolved} (${typeof resolved})` + ); + } + const keysAndValues = new Map(); + for (const [key, value] of resolved) { + if (key instanceof RawBinaryString) { + // Decoded rawBinaryValue will always be a Uint8Array + keysAndValues.set(key.rawBinaryValue as Uint8Array, value); + } else { + throw new Error( + `Invalid type for map key. Expected RawBinaryString, got ${key} (${typeof key})` + ); + } + } + return keysAndValues; + } + + /** + * Resolve the provider by extracting the value it indicates from the base msgpack object. + */ + private resolve(): MsgpackEncodingData { + if (this.resolvedCachePresent) { + return this.resolvedCache; + } + let parentResolved: MsgpackEncodingData; + if (this.parent) { + parentResolved = this.parent.resolve(); + } else { + // Need to parse baseObjectBytes + parentResolved = msgpackRawDecodeAsMapWithRawStrings( + this.baseObjectBytes! + ) as MsgpackEncodingData; + } + if (!this.segment) { + this.resolvedCache = parentResolved; + this.resolvedCachePresent = true; + return parentResolved; + } + if (this.segment.kind === MsgpackObjectPathSegmentKind.MAP_VALUE) { + if (!(parentResolved instanceof Map)) { + throw new Error( + `Invalid type. Expected Map, got ${parentResolved} (${typeof parentResolved})` + ); + } + // All decoded map keys will be raw strings, and Map objects compare complex values by reference, + // so we must check all the values for value-equality. + if ( + typeof this.segment.key === 'string' || + this.segment.key instanceof Uint8Array || + this.segment.key instanceof RawBinaryString + ) { + const targetBytes = + this.segment.key instanceof RawBinaryString + ? // Decoded rawBinaryValue will always be a Uint8Array + (this.segment.key.rawBinaryValue as Uint8Array) + : coerceToBytes(this.segment.key); + const targetIsRawString = + typeof this.segment.key === 'string' || + this.segment.key instanceof RawBinaryString; + for (const [key, value] of parentResolved) { + let potentialKeyBytes: Uint8Array | undefined; + if (targetIsRawString) { + if (key instanceof RawBinaryString) { + // Decoded rawBinaryValue will always be a Uint8Array + potentialKeyBytes = key.rawBinaryValue as Uint8Array; + } + } else if (key instanceof Uint8Array) { + potentialKeyBytes = key; + } + if (potentialKeyBytes && arrayEqual(targetBytes, potentialKeyBytes)) { + this.resolvedCache = value; + break; + } + } + } else { + this.resolvedCache = parentResolved.get(this.segment.key); + } + this.resolvedCachePresent = true; + return this.resolvedCache; + } + if (this.segment.kind === MsgpackObjectPathSegmentKind.ARRAY_ELEMENT) { + if (!Array.isArray(parentResolved)) { + throw new Error( + `Invalid type. Expected Array, got ${parentResolved} (${typeof parentResolved})` + ); + } + this.resolvedCache = parentResolved[this.segment.key as number]; + this.resolvedCachePresent = true; + return this.resolvedCache; + } + throw new Error(`Invalid segment kind: ${this.segment.kind}`); + } + + /** + * Get the path string of the current location indicated by the provider. Useful for debugging. + */ + public getPathString(): string { + const parentPathString = this.parent ? this.parent.getPathString() : 'root'; + if (!this.segment) { + return parentPathString; + } + if (this.segment.kind === MsgpackObjectPathSegmentKind.MAP_VALUE) { + return `${parentPathString} -> map key "${this.segment.key}" (${typeof this.segment.key})`; + } + if (this.segment.kind === MsgpackObjectPathSegmentKind.ARRAY_ELEMENT) { + return `${parentPathString} -> array index ${this.segment.key} (${typeof this.segment.key})`; + } + return `${parentPathString} -> unknown segment kind ${this.segment.kind}`; + } +} + +/** + * Options for {@link Schema.prepareJSON} + */ +export interface PrepareJSONOptions { + /** + * If true, allows invalid UTF-8 binary strings to be converted to JSON strings. + * + * Otherwise, an error will be thrown if encoding a binary string to a JSON cannot be done losslessly. + */ + lossyBinaryStringConversion?: boolean; +} + +/** + * A Schema is used to prepare objects for encoding and decoding from msgpack and JSON. + * + * Schemas represent a specific type. + */ +export abstract class Schema { + /** + * Get the default value for this type. + */ + public abstract defaultValue(): unknown; + + /** + * Checks if the value is the default value for this type. + * @param data - The value to check + * @returns True if the value is the default value, false otherwise + */ + public abstract isDefaultValue(data: unknown): boolean; + + /** + * Prepares the encoding data for encoding to msgpack. + * @param data - Encoding data to be prepared. + * @returns A value ready to be msgpack encoded. + */ + public abstract prepareMsgpack(data: unknown): MsgpackEncodingData; + + /** + * Restores the encoding data from a msgpack encoding object. + * @param encoded - The msgpack encoding object to restore. + * @param rawStringProvider - A provider for raw strings. + * @returns The original encoding data. + */ + public abstract fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): unknown; + + /** + * Prepares the encoding data for encoding to JSON. + * @param data - The JSON encoding data to be prepared. + * @returns A value ready to be JSON encoded. + */ + public abstract prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData; + + /** + * Restores the encoding data from a JSON encoding object. + * @param encoded - The JSON encoding object to restore. + * @returns The original encoding data. + */ + public abstract fromPreparedJSON(encoded: JSONEncodingData): unknown; +} + +/** + * An interface for objects that can be encoded and decoded to/from msgpack and JSON. + */ +export interface Encodable { + /** + * Extract the encoding data for this object. This data, after being prepared by the encoding + * Schema, can be encoded to msgpack or JSON. + */ + toEncodingData(): unknown; + /** + * Get the encoding Schema for this object, used to prepare the encoding data for msgpack and JSON. + */ + getEncodingSchema(): Schema; +} + +/** + * A type that represents the class of an Encodable object. + */ +export interface EncodableClass { + /** + * Create a new instance of this class from the given encoding data. + * @param data - The encoding data to create the object from + */ + fromEncodingData(data: unknown): T; + /** + * The encoding Schema for this class, used to prepare encoding data from msgpack and JSON. + */ + readonly encodingSchema: Schema; +} + +/** + * Decode a msgpack byte array to an Encodable object. + * @param encoded - The msgpack bytes to decode + * @param c - The class of the object to decode. This class must match the object that was encoded. + * @returns An instance of the class with the decoded data + */ +export function decodeMsgpack( + encoded: ArrayLike, + c: EncodableClass +): T { + const decoded = msgpackRawDecodeAsMap(encoded) as MsgpackEncodingData; + const rawStringProvider = new MsgpackRawStringProvider({ + baseObjectBytes: encoded, + }); + return c.fromEncodingData( + c.encodingSchema.fromPreparedMsgpack(decoded, rawStringProvider) + ); +} + +/** + * Encode an Encodable object to a msgpack byte array. + * @param e - The object to encode + * @returns A msgpack byte array encoding of the object + */ +export function encodeMsgpack(e: Encodable): Uint8Array { + return msgpackRawEncode( + e.getEncodingSchema().prepareMsgpack(e.toEncodingData()) + ); +} + +/** + * Decode a JSON string to an Encodable object. + * @param encoded - The JSON string to decode + * @param c - The class of the object to decode. This class must match the object that was encoded. + * @returns An instance of the class with the decoded data + */ +export function decodeJSON( + encoded: string, + c: EncodableClass +): T { + const decoded: JSONEncodingData = parseJSON(encoded, { + intDecoding: IntDecoding.BIGINT, + }); + return c.fromEncodingData( + c.encodingSchema.fromPreparedJSON(decoded) as JSONEncodingData + ); +} + +export interface EncodeJSONOptions { + /** + * Adds indentation, white space, and line break characters to the return-value JSON text to make + * it easier to read. + */ + space?: string | number; + + /** + * If true, allows invalid UTF-8 binary strings to be converted to JSON strings. + * + * Otherwise, an error will be thrown if encoding a binary string to a JSON cannot be done losslessly. + */ + lossyBinaryStringConversion?: boolean; +} + +/** + * Encode an Encodable object to a JSON string. + * @param e - The object to encode + * @param options - Optional encoding options. See {@link EncodeJSONOptions} for more information. + * @returns A JSON string encoding of the object + */ +export function encodeJSON(e: Encodable, options?: EncodeJSONOptions): string { + const { space, ...prepareJSONOptions } = options ?? {}; + const prepared = e + .getEncodingSchema() + .prepareJSON(e.toEncodingData(), prepareJSONOptions); + return stringifyJSON(prepared, undefined, space); } diff --git a/src/encoding/schema/address.ts b/src/encoding/schema/address.ts new file mode 100644 index 000000000..3eb7f5c7e --- /dev/null +++ b/src/encoding/schema/address.ts @@ -0,0 +1,53 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; +import { Address } from '../address.js'; + +/* eslint-disable class-methods-use-this */ + +export class AddressSchema extends Schema { + public defaultValue(): Address { + return Address.zeroAddress(); + } + + public isDefaultValue(data: unknown): boolean { + // The equals method checks if the input is an Address + return Address.zeroAddress().equals(data as Address); + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data instanceof Address) { + return data.publicKey; + } + throw new Error(`Invalid address: (${typeof data}) ${data}`); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): Address { + // The Address constructor checks that the input is a Uint8Array + return new Address(encoded as Uint8Array); + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + if (data instanceof Address) { + return data.toString(); + } + throw new Error(`Invalid address: (${typeof data}) ${data}`); + } + + public fromPreparedJSON(encoded: JSONEncodingData): Address { + // The Address.fromString method checks that the input is a string + return Address.fromString(encoded as string); + } +} diff --git a/src/encoding/schema/array.ts b/src/encoding/schema/array.ts new file mode 100644 index 000000000..1a90571b6 --- /dev/null +++ b/src/encoding/schema/array.ts @@ -0,0 +1,66 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; + +/* eslint-disable class-methods-use-this */ + +export class ArraySchema extends Schema { + constructor(public readonly itemSchema: Schema) { + super(); + } + + public defaultValue(): unknown[] { + return []; + } + + public isDefaultValue(data: unknown): boolean { + return Array.isArray(data) && data.length === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (Array.isArray(data)) { + return data.map((item) => this.itemSchema.prepareMsgpack(item)); + } + throw new Error('ArraySchema data must be an array'); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): unknown[] { + if (Array.isArray(encoded)) { + return encoded.map((item, index) => + this.itemSchema.fromPreparedMsgpack( + item, + rawStringProvider.withArrayElement(index) + ) + ); + } + throw new Error( + `ArraySchema encoded data must be an array: ${encoded} (${typeof encoded})` + ); + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (Array.isArray(data)) { + return data.map((item) => this.itemSchema.prepareJSON(item, options)); + } + throw new Error('ArraySchema data must be an array'); + } + + public fromPreparedJSON(encoded: JSONEncodingData): unknown[] { + if (Array.isArray(encoded)) { + return encoded.map((item) => this.itemSchema.fromPreparedJSON(item)); + } + throw new Error( + `ArraySchema encoded data must be an array: ${encoded} (${typeof encoded})` + ); + } +} diff --git a/src/encoding/schema/binarystring.ts b/src/encoding/schema/binarystring.ts new file mode 100644 index 000000000..01308732b --- /dev/null +++ b/src/encoding/schema/binarystring.ts @@ -0,0 +1,73 @@ +import { RawBinaryString } from 'algorand-msgpack'; +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; +import { coerceToBytes, bytesToString, bytesToBase64 } from '../binarydata.js'; +import { arrayEqual } from '../../utils/utils.js'; + +/* eslint-disable class-methods-use-this */ + +/** + * SpecialCaseBinaryStringSchema is a schema for byte arrays which are encoded + * as strings in msgpack and JSON. + * + * This schema allows lossless conversion between the in memory representation + * and the msgpack encoded representation, but NOT between the in memory and + * JSON encoded representations if the byte array contains invalid UTF-8 + * sequences. + */ +export class SpecialCaseBinaryStringSchema extends Schema { + public defaultValue(): Uint8Array { + return new Uint8Array(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Uint8Array && data.byteLength === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data instanceof Uint8Array) { + // Cast is needed because RawBinaryString is not part of the standard MsgpackEncodingData + return new RawBinaryString(data) as unknown as MsgpackEncodingData; + } + throw new Error(`Invalid byte array: (${typeof data}) ${data}`); + } + + public fromPreparedMsgpack( + _encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Uint8Array { + return rawStringProvider.getRawStringAtCurrentLocation(); + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (data instanceof Uint8Array) { + // Not safe to convert to string for all binary data + const stringValue = bytesToString(data); + if ( + !options.lossyBinaryStringConversion && + !arrayEqual(coerceToBytes(stringValue), data) + ) { + throw new Error( + `Invalid UTF-8 byte array encountered. Encode with lossyBinaryStringConversion enabled to bypass this check. Base64 value: ${bytesToBase64(data)}` + ); + } + return stringValue; + } + throw new Error(`Invalid byte array: (${typeof data}) ${data}`); + } + + public fromPreparedJSON(encoded: JSONEncodingData): Uint8Array { + if (typeof encoded === 'string') { + return coerceToBytes(encoded); + } + throw new Error(`Invalid byte array: (${typeof encoded}) ${encoded}`); + } +} diff --git a/src/encoding/schema/blockhash.ts b/src/encoding/schema/blockhash.ts new file mode 100644 index 000000000..5c82dd4f4 --- /dev/null +++ b/src/encoding/schema/blockhash.ts @@ -0,0 +1,84 @@ +import base32 from 'hi-base32'; +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; + +/** + * Length of a block hash in bytes + */ +const blockHashByteLength = 32; + +/* eslint-disable class-methods-use-this */ + +/** + * Length of a 32-byte encoded in base32 without padding + */ +const base32Length = 52; + +/** + * BlockHashSchema is a schema for block hashes. + * + * In msgapck, these types are encoded as 32-byte binary strings. In JSON, they + * are encoded as strings prefixed with "blk-" followed by the base32 encoding + * of the 32-byte block hash without any padding. + */ +export class BlockHashSchema extends Schema { + public defaultValue(): Uint8Array { + return new Uint8Array(blockHashByteLength); + } + + public isDefaultValue(data: unknown): boolean { + return ( + data instanceof Uint8Array && + data.byteLength === blockHashByteLength && + data.every((byte) => byte === 0) + ); + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data instanceof Uint8Array && data.byteLength === blockHashByteLength) { + return data; + } + throw new Error(`Invalid block hash: (${typeof data}) ${data}`); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): Uint8Array { + if ( + encoded instanceof Uint8Array && + encoded.byteLength === blockHashByteLength + ) { + return encoded; + } + throw new Error(`Invalid block hash: (${typeof encoded}) ${encoded}`); + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + if (data instanceof Uint8Array && data.byteLength === blockHashByteLength) { + return `blk-${base32.encode(data).slice(0, base32Length)}`; + } + throw new Error(`Invalid block hash: (${typeof data}) ${data}`); + } + + public fromPreparedJSON(encoded: JSONEncodingData): Uint8Array { + if ( + typeof encoded === 'string' && + encoded.length === base32Length + 4 && + encoded.startsWith('blk-') + ) { + return Uint8Array.from(base32.decode.asBytes(encoded.slice(4))); + } + throw new Error(`Invalid block hash: (${typeof encoded}) ${encoded}`); + } +} diff --git a/src/encoding/schema/boolean.ts b/src/encoding/schema/boolean.ts new file mode 100644 index 000000000..090a67545 --- /dev/null +++ b/src/encoding/schema/boolean.ts @@ -0,0 +1,54 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; + +/* eslint-disable class-methods-use-this */ + +export class BooleanSchema extends Schema { + public defaultValue(): boolean { + return false; + } + + public isDefaultValue(data: unknown): boolean { + return data === false; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (typeof data === 'boolean') { + return data; + } + throw new Error('Invalid boolean'); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): boolean { + if (typeof encoded === 'boolean') { + return encoded; + } + throw new Error('Invalid boolean'); + } + + public prepareJSON( + data: unknown, // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + if (typeof data === 'boolean') { + return data; + } + throw new Error('Invalid boolean'); + } + + public fromPreparedJSON(encoded: JSONEncodingData): boolean { + if (typeof encoded === 'boolean') { + return encoded; + } + throw new Error('Invalid boolean'); + } +} diff --git a/src/encoding/schema/bytearray.ts b/src/encoding/schema/bytearray.ts new file mode 100644 index 000000000..5218049ca --- /dev/null +++ b/src/encoding/schema/bytearray.ts @@ -0,0 +1,127 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; +import { base64ToBytes, bytesToBase64 } from '../binarydata.js'; + +/* eslint-disable class-methods-use-this */ + +export class ByteArraySchema extends Schema { + public defaultValue(): Uint8Array { + return new Uint8Array(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Uint8Array && data.byteLength === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data instanceof Uint8Array) { + return data; + } + throw new Error(`Invalid byte array: (${typeof data}) ${data}`); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): Uint8Array { + if (encoded instanceof Uint8Array) { + return encoded; + } + throw new Error(`Invalid byte array: (${typeof encoded}) ${encoded}`); + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + if (data instanceof Uint8Array) { + return bytesToBase64(data); + } + throw new Error(`Invalid byte array: (${typeof data}) ${data}`); + } + + public fromPreparedJSON(encoded: JSONEncodingData): Uint8Array { + if (typeof encoded === 'string') { + return base64ToBytes(encoded); + } + throw new Error(`Invalid byte array: (${typeof encoded}) ${encoded}`); + } +} + +export class FixedLengthByteArraySchema extends Schema { + constructor(public readonly length: number) { + super(); + } + + public defaultValue(): Uint8Array { + return new Uint8Array(this.length); + } + + public isDefaultValue(data: unknown): boolean { + return ( + data instanceof Uint8Array && + data.byteLength === this.length && + data.every((byte) => byte === 0) + ); + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data instanceof Uint8Array) { + if (data.byteLength === this.length) { + return data; + } + throw new Error( + `Invalid byte array length: wanted ${this.length}, got ${data.byteLength}` + ); + } + throw new Error('Invalid byte array'); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): Uint8Array { + if (encoded instanceof Uint8Array) { + if (encoded.byteLength === this.length) { + return encoded; + } + throw new Error( + `Invalid byte array length: wanted ${this.length}, got ${encoded.byteLength}` + ); + } + throw new Error('Invalid byte array'); + } + + public prepareJSON(data: unknown): JSONEncodingData { + if (data instanceof Uint8Array) { + if (data.byteLength === this.length) { + return bytesToBase64(data); + } + throw new Error( + `Invalid byte array length: wanted ${this.length}, got ${data.byteLength}` + ); + } + throw new Error('Invalid byte array'); + } + + public fromPreparedJSON(encoded: JSONEncodingData): Uint8Array { + if (typeof encoded === 'string') { + const bytes = base64ToBytes(encoded); + if (bytes.byteLength === this.length) { + return bytes; + } + throw new Error( + `Invalid byte array length: wanted ${this.length}, got ${bytes.byteLength}` + ); + } + throw new Error('Invalid base64 byte array'); + } +} diff --git a/src/encoding/schema/index.ts b/src/encoding/schema/index.ts new file mode 100644 index 000000000..a18203174 --- /dev/null +++ b/src/encoding/schema/index.ts @@ -0,0 +1,26 @@ +export { BooleanSchema } from './boolean.js'; +export { StringSchema } from './string.js'; +export { Uint64Schema } from './uint64.js'; + +export { AddressSchema } from './address.js'; +export { ByteArraySchema, FixedLengthByteArraySchema } from './bytearray.js'; + +export { BlockHashSchema } from './blockhash.js'; + +export { SpecialCaseBinaryStringSchema } from './binarystring.js'; + +export { ArraySchema } from './array.js'; +export { + NamedMapSchema, + NamedMapEntry, + allOmitEmpty, + combineMaps, + convertMap, + Uint64MapSchema, + StringMapSchema, + ByteArrayMapSchema, + SpecialCaseBinaryStringMapSchema, +} from './map.js'; +export { OptionalSchema } from './optional.js'; + +export { UntypedSchema } from './untyped.js'; diff --git a/src/encoding/schema/map.ts b/src/encoding/schema/map.ts new file mode 100644 index 000000000..5c57dd6f5 --- /dev/null +++ b/src/encoding/schema/map.ts @@ -0,0 +1,713 @@ +import { RawBinaryString } from 'algorand-msgpack'; +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; +import { ensureUint64, arrayEqual } from '../../utils/utils.js'; +import { + bytesToString, + coerceToBytes, + bytesToBase64, + base64ToBytes, +} from '../binarydata.js'; + +/* eslint-disable class-methods-use-this */ + +/** + * Describes a key-value entry in a NamedMapSchema. + */ +export interface NamedMapEntry { + /** + * Key of the entry. Must be unique for this map. + */ + key: string; + /** + * The Schema for the entry's value. + */ + valueSchema: Schema; + /** + * If true, the entry will be omitted from the encoding if the value is the default value. + */ + omitEmpty: boolean; + /** + * If true, valueSchema must be a NamedMapSchema and key must be the empty string. The fields of + * valueSchema will be embedded directly in the parent map. + * + * omitEmpty is ignored for embedded entries. Instead, the individual omitEmpty values of the + * embedded fields are used. + */ + embedded?: boolean; +} + +/** + * Applies the omitEmpty flag to all entries in the array. + * @param entries - The entries to apply the flag to. + * @returns A new array with the omitEmpty flag applied to all entries. + */ +export function allOmitEmpty( + entries: Array> +): NamedMapEntry[] { + return entries.map((entry) => ({ ...entry, omitEmpty: true })); +} + +/** + * Schema for a map/struct with a fixed set of known string fields. + */ +export class NamedMapSchema extends Schema { + private readonly entries: NamedMapEntry[]; + + constructor(entries: NamedMapEntry[]) { + super(); + this.entries = entries; + this.checkEntries(); + } + + /** + * Adds new entries to the map schema. WARNING: this is a mutable operation, and you should be very + * careful when using it. Any error that happens here is non-recoverable and will corrupt the + * NamedMapSchema object; + * @param entries - The entries to add. + */ + public pushEntries(...entries: NamedMapEntry[]) { + this.entries.push(...entries); + this.checkEntries(); + } + + private checkEntries() { + for (const entry of this.entries) { + if (entry.embedded) { + if (entry.key !== '') { + throw new Error('Embedded entries must have an empty key'); + } + if (!(entry.valueSchema instanceof NamedMapSchema)) { + throw new Error( + 'Embedded entry valueSchema must be a NamedMapSchema' + ); + } + } + } + + const keys = new Set(); + for (const entry of this.getEntries()) { + if (keys.has(entry.key)) { + throw new Error(`Duplicate key: ${entry.key}`); + } + keys.add(entry.key); + } + } + + /** + * Returns all top-level entries, properly accounting for fields from embedded entries. + * @returns An array of all top-level entries for this map. + */ + public getEntries(): NamedMapEntry[] { + const entries: NamedMapEntry[] = []; + for (const entry of this.entries) { + if (entry.embedded) { + const embeddedMapSchema = entry.valueSchema as NamedMapSchema; + entries.push(...embeddedMapSchema.getEntries()); + } else { + entries.push(entry); + } + } + return entries; + } + + public defaultValue(): Map { + const map = new Map(); + for (const entry of this.getEntries()) { + map.set(entry.key, entry.valueSchema.defaultValue()); + } + return map; + } + + public isDefaultValue(data: unknown): boolean { + if (!(data instanceof Map)) return false; + for (const entry of this.getEntries()) { + if (!entry.valueSchema.isDefaultValue(data.get(entry.key))) { + return false; + } + } + return true; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `NamedMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const map = new Map(); + for (const entry of this.getEntries()) { + const value = data.get(entry.key); + if (entry.omitEmpty && entry.valueSchema.isDefaultValue(value)) { + continue; + } + map.set(entry.key, entry.valueSchema.prepareMsgpack(value)); + } + return map; + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Map { + if (!(encoded instanceof Map)) { + throw new Error('NamedMapSchema data must be a Map'); + } + const map = new Map(); + for (const entry of this.getEntries()) { + if (encoded.has(entry.key)) { + map.set( + entry.key, + entry.valueSchema.fromPreparedMsgpack( + encoded.get(entry.key), + rawStringProvider.withMapValue(entry.key) + ) + ); + } else if (entry.omitEmpty) { + map.set(entry.key, entry.valueSchema.defaultValue()); + } else { + throw new Error(`Missing key: ${entry.key}`); + } + } + return map; + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (!(data instanceof Map)) { + throw new Error('NamedMapSchema data must be a Map'); + } + const obj: { [key: string]: JSONEncodingData } = {}; + for (const entry of this.getEntries()) { + const value = data.get(entry.key); + if (entry.omitEmpty && entry.valueSchema.isDefaultValue(value)) { + continue; + } + obj[entry.key] = entry.valueSchema.prepareJSON(value, options); + } + return obj; + } + + public fromPreparedJSON(encoded: JSONEncodingData): Map { + if ( + encoded == null || + typeof encoded !== 'object' || + Array.isArray(encoded) + ) { + throw new Error('NamedMapSchema data must be an object'); + } + const map = new Map(); + for (const entry of this.getEntries()) { + if (Object.prototype.hasOwnProperty.call(encoded, entry.key)) { + map.set( + entry.key, + entry.valueSchema.fromPreparedJSON(encoded[entry.key]) + ); + } else if (entry.omitEmpty) { + map.set(entry.key, entry.valueSchema.defaultValue()); + } else { + throw new Error(`Missing key: ${entry.key}`); + } + } + return map; + } +} + +/** + * Combines multiple maps into a single map. Throws an error if any of the maps have duplicate keys. + * @param maps - The maps to combine. + * @returns A new map with all the entries from the input maps. + */ +export function combineMaps(...maps: Array>): Map { + const combined = new Map(); + for (const map of maps) { + for (const [key, value] of map) { + if (combined.has(key)) { + throw new Error(`Duplicate key: ${key}`); + } + combined.set(key, value); + } + } + return combined; +} + +/** + * Converts a map to a new map with different keys and values. + * @param map - The map to convert. + * @param func - The function to convert each entry. + * @returns A new map with the converted entries. + */ +export function convertMap( + map: Map, + func: (k: K1, v: V1) => [K2, V2] +): Map { + const mapped = new Map(); + for (const [key, value] of map) { + const [newKey, newValue] = func(key, value); + mapped.set(newKey, newValue); + } + return mapped; +} + +/** + * Schema for a map with a variable number of uint64 keys. + */ +export class Uint64MapSchema extends Schema { + constructor(public readonly valueSchema: Schema) { + super(); + } + + public defaultValue(): Map { + return new Map(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Map && data.size === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `Uint64MapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + const bigintKey = ensureUint64(key); + if (prepared.has(bigintKey)) { + throw new Error(`Duplicate key: ${bigintKey}`); + } + prepared.set(bigintKey, this.valueSchema.prepareMsgpack(value)); + } + return prepared; + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Map { + if (!(encoded instanceof Map)) { + throw new Error('Uint64MapSchema data must be a Map'); + } + const map = new Map(); + for (const [key, value] of encoded) { + const bigintKey = ensureUint64(key); + if (map.has(bigintKey)) { + throw new Error(`Duplicate key: ${bigintKey}`); + } + map.set( + bigintKey, + this.valueSchema.fromPreparedMsgpack( + value, + rawStringProvider.withMapValue(key) + ) + ); + } + return map; + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `Uint64MapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + const bigintKey = ensureUint64(key); + if (prepared.has(bigintKey)) { + throw new Error(`Duplicate key: ${bigintKey}`); + } + prepared.set(bigintKey, this.valueSchema.prepareJSON(value, options)); + } + // Convert map to object + const obj: { [key: string]: JSONEncodingData } = {}; + for (const [key, value] of prepared) { + obj[key.toString()] = value; + } + return obj; + } + + public fromPreparedJSON(encoded: JSONEncodingData): Map { + if ( + encoded == null || + typeof encoded !== 'object' || + Array.isArray(encoded) + ) { + throw new Error('Uint64MapSchema data must be an object'); + } + const map = new Map(); + for (const [key, value] of Object.entries(encoded)) { + const bigintKey = BigInt(key); + if (map.has(bigintKey)) { + throw new Error(`Duplicate key: ${bigintKey}`); + } + map.set(bigintKey, this.valueSchema.fromPreparedJSON(value)); + } + return map; + } +} + +/** + * Schema for a map with a variable number of string keys. + */ +export class StringMapSchema extends Schema { + constructor(public readonly valueSchema: Schema) { + super(); + } + + public defaultValue(): Map { + return new Map(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Map && data.size === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `StringMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (typeof key !== 'string') { + throw new Error(`Invalid key: ${key}`); + } + if (prepared.has(key)) { + throw new Error(`Duplicate key: ${key}`); + } + prepared.set(key, this.valueSchema.prepareMsgpack(value)); + } + return prepared; + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Map { + if (!(encoded instanceof Map)) { + throw new Error('StringMapSchema data must be a Map'); + } + const map = new Map(); + for (const [key, value] of encoded) { + if (typeof key !== 'string') { + throw new Error(`Invalid key: ${key}`); + } + if (map.has(key)) { + throw new Error(`Duplicate key: ${key}`); + } + map.set( + key, + this.valueSchema.fromPreparedMsgpack( + value, + rawStringProvider.withMapValue(key) + ) + ); + } + return map; + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `StringMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (typeof key !== 'string') { + throw new Error(`Invalid key: ${key}`); + } + if (prepared.has(key)) { + throw new Error(`Duplicate key: ${key}`); + } + prepared.set(key, this.valueSchema.prepareJSON(value, options)); + } + // Convert map to object + const obj: { [key: string]: JSONEncodingData } = {}; + for (const [key, value] of prepared) { + obj[key] = value; + } + return obj; + } + + public fromPreparedJSON(encoded: JSONEncodingData): Map { + if ( + encoded == null || + typeof encoded !== 'object' || + Array.isArray(encoded) + ) { + throw new Error('StringMapSchema data must be an object'); + } + const map = new Map(); + for (const [key, value] of Object.entries(encoded)) { + if (map.has(key)) { + throw new Error(`Duplicate key: ${key}`); + } + map.set(key, this.valueSchema.fromPreparedJSON(value)); + } + return map; + } +} + +/** + * Schema for a map with a variable number of byte array keys. + */ +export class ByteArrayMapSchema extends Schema { + constructor(public readonly valueSchema: Schema) { + super(); + } + + public defaultValue(): Map { + return new Map(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Map && data.size === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `ByteArrayMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (!(key instanceof Uint8Array)) { + throw new Error(`Invalid key: ${key} (${typeof key})`); + } + prepared.set(key, this.valueSchema.prepareMsgpack(value)); + } + return prepared; + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Map { + if (!(encoded instanceof Map)) { + throw new Error('ByteArrayMapSchema data must be a Map'); + } + const map = new Map(); + for (const [key, value] of encoded) { + if (!(key instanceof Uint8Array)) { + throw new Error(`Invalid key: ${key} (${typeof key})`); + } + map.set( + key, + this.valueSchema.fromPreparedMsgpack( + value, + rawStringProvider.withMapValue(key) + ) + ); + } + return map; + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `ByteArrayMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (!(key instanceof Uint8Array)) { + throw new Error(`Invalid key: ${key} (${typeof key})`); + } + const b64Encoded = bytesToBase64(key); + if (prepared.has(b64Encoded)) { + throw new Error(`Duplicate key (base64): ${b64Encoded}`); + } + prepared.set(b64Encoded, this.valueSchema.prepareJSON(value, options)); + } + // Convert map to object + const obj: { [key: string]: JSONEncodingData } = {}; + for (const [key, value] of prepared) { + obj[key] = value; + } + return obj; + } + + public fromPreparedJSON(encoded: JSONEncodingData): Map { + if ( + encoded == null || + typeof encoded !== 'object' || + Array.isArray(encoded) + ) { + throw new Error('ByteArrayMapSchema data must be an object'); + } + const map = new Map(); + for (const [key, value] of Object.entries(encoded)) { + map.set(base64ToBytes(key), this.valueSchema.fromPreparedJSON(value)); + } + return map; + } +} + +/** + * Converts any RawBinaryString values to regular strings in a MsgpackEncodingData object. + * + * Note this conversion may be lossy if the binary data is not valid UTF-8. + * + * @returns A new object with RawBinaryString values converted to strings. + */ +function convertRawStringsInMsgpackValue( + value: MsgpackEncodingData +): MsgpackEncodingData { + if (value instanceof RawBinaryString) { + return bytesToString(value.rawBinaryValue as Uint8Array); + } + if (value instanceof Map) { + const newMap = new Map< + string | number | bigint | Uint8Array, + MsgpackEncodingData + >(); + for (const [key, val] of value) { + newMap.set( + convertRawStringsInMsgpackValue(key) as + | string + | number + | bigint + | Uint8Array, + convertRawStringsInMsgpackValue(val) + ); + } + return newMap; + } + if (Array.isArray(value)) { + return value.map(convertRawStringsInMsgpackValue); + } + return value; +} + +/** + * Schema for a map with a variable number of binary string keys. + * + * See SpecialCaseBinaryStringSchema for more information about the key type. + */ +export class SpecialCaseBinaryStringMapSchema extends Schema { + constructor(public readonly valueSchema: Schema) { + super(); + } + + public defaultValue(): Map { + return new Map(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Map && data.size === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `SpecialCaseBinaryStringMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (!(key instanceof Uint8Array)) { + throw new Error(`Invalid key: ${key} (${typeof key})`); + } + prepared.set( + new RawBinaryString(key), + this.valueSchema.prepareMsgpack(value) + ); + } + // Cast is needed because RawBinaryString is not part of the standard MsgpackEncodingData + return prepared as unknown as Map; + } + + public fromPreparedMsgpack( + _encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Map { + const map = new Map(); + const keysAndValues = + rawStringProvider.getRawStringKeysAndValuesAtCurrentLocation(); + for (const [key, value] of keysAndValues) { + map.set( + key, + this.valueSchema.fromPreparedMsgpack( + convertRawStringsInMsgpackValue(value), + rawStringProvider.withMapValue(new RawBinaryString(key)) + ) + ); + } + return map; + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `SpecialCaseBinaryStringMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (!(key instanceof Uint8Array)) { + throw new Error(`Invalid key: ${key}`); + } + // Not safe to convert to string for all binary data + const keyStringValue = bytesToString(key); + if ( + !options.lossyBinaryStringConversion && + !arrayEqual(coerceToBytes(keyStringValue), key) + ) { + throw new Error( + `Invalid UTF-8 byte array encountered. Encode with lossyBinaryStringConversion enabled to bypass this check. Base64 value: ${bytesToBase64(key)}` + ); + } + prepared.set( + keyStringValue, + this.valueSchema.prepareJSON(value, options) + ); + } + // Convert map to object + const obj: { [key: string]: JSONEncodingData } = {}; + for (const [key, value] of prepared) { + obj[key] = value; + } + return obj; + } + + public fromPreparedJSON(encoded: JSONEncodingData): Map { + if ( + encoded == null || + typeof encoded !== 'object' || + Array.isArray(encoded) + ) { + throw new Error( + 'SpecialCaseBinaryStringMapSchema data must be an object' + ); + } + const map = new Map(); + for (const [key, value] of Object.entries(encoded)) { + map.set(coerceToBytes(key), this.valueSchema.fromPreparedJSON(value)); + } + return map; + } +} diff --git a/src/encoding/schema/optional.ts b/src/encoding/schema/optional.ts new file mode 100644 index 000000000..ae6100e14 --- /dev/null +++ b/src/encoding/schema/optional.ts @@ -0,0 +1,72 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; + +/* eslint-disable class-methods-use-this */ + +/** + * OptionalSchema allows for another schema-defined value to be optional. + * + * This expands the set of values which can be represented by the given schema to include `undefined`. + * + * Note that this schema considers `undefined` _and_ any default values from the underlying schema + * to all be default values. This means that when using NamedMapSchema to omit default values, an + * `undefined` value is indistinguishable from the given schema's default value; in this respect, + * OptionalSchema does not affect the encoding of NamedMapSchema values, but rather allows the + * application to restore omitted values as `undefined` instead of their default value. + * + * Upon decoding, this schema also allows null/undefined values to be acceptable as values. + */ +export class OptionalSchema extends Schema { + constructor(public readonly valueSchema: Schema) { + super(); + } + + public defaultValue(): undefined { + return undefined; + } + + public isDefaultValue(data: unknown): boolean { + return data === undefined || this.valueSchema.isDefaultValue(data); + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data === undefined) { + return undefined; + } + return this.valueSchema.prepareMsgpack(data); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): unknown { + // JS undefined is encoded as msgpack nil, which may be decoded as JS null + if (encoded === undefined || encoded === null) { + return undefined; + } + return this.valueSchema.fromPreparedMsgpack(encoded, rawStringProvider); + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (data === undefined) { + // JSON representation does not have undefined, only null + return null; + } + return this.valueSchema.prepareJSON(data, options); + } + + public fromPreparedJSON(encoded: JSONEncodingData): unknown { + if (encoded === undefined || encoded === null) { + return undefined; + } + return this.valueSchema.fromPreparedJSON(encoded); + } +} diff --git a/src/encoding/schema/string.ts b/src/encoding/schema/string.ts new file mode 100644 index 000000000..4f0081fe4 --- /dev/null +++ b/src/encoding/schema/string.ts @@ -0,0 +1,55 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; + +/* eslint-disable class-methods-use-this */ + +export class StringSchema extends Schema { + public defaultValue(): string { + return ''; + } + + public isDefaultValue(data: unknown): boolean { + return data === ''; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (typeof data === 'string') { + return data; + } + throw new Error(`Invalid string: (${typeof data}) ${data}`); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): string { + if (typeof encoded === 'string') { + return encoded; + } + throw new Error(`Invalid string: (${typeof encoded}) ${encoded}`); + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + if (typeof data === 'string') { + return data; + } + throw new Error(`Invalid string: (${typeof data}) ${data}`); + } + + public fromPreparedJSON(encoded: JSONEncodingData): string { + if (typeof encoded === 'string') { + return encoded; + } + throw new Error(`Invalid string: (${typeof encoded}) ${encoded}`); + } +} diff --git a/src/encoding/schema/uint64.ts b/src/encoding/schema/uint64.ts new file mode 100644 index 000000000..81f1dbbc5 --- /dev/null +++ b/src/encoding/schema/uint64.ts @@ -0,0 +1,46 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; +import { ensureUint64 } from '../../utils/utils.js'; + +/* eslint-disable class-methods-use-this */ + +export class Uint64Schema extends Schema { + public defaultValue(): bigint { + return BigInt(0); + } + + public isDefaultValue(data: unknown): boolean { + if (typeof data === 'bigint') return data === BigInt(0); + if (typeof data === 'number') return data === 0; + return false; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + return ensureUint64(data); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): bigint { + return ensureUint64(encoded); + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + return ensureUint64(data); + } + + public fromPreparedJSON(encoded: JSONEncodingData): bigint { + return ensureUint64(encoded); + } +} diff --git a/src/encoding/schema/untyped.ts b/src/encoding/schema/untyped.ts new file mode 100644 index 000000000..6551d6159 --- /dev/null +++ b/src/encoding/schema/untyped.ts @@ -0,0 +1,47 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, + msgpackEncodingDataToJSONEncodingData, + jsonEncodingDataToMsgpackEncodingData, +} from '../encoding.js'; + +/* eslint-disable class-methods-use-this */ + +export class UntypedSchema extends Schema { + public defaultValue(): undefined { + return undefined; + } + + public isDefaultValue(data: unknown): boolean { + return data === undefined; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + // Value is already MsgpackEncodingData, since it is returned as such from + // fromPreparedMsgpack and fromPreparedJSON + return data as MsgpackEncodingData; + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): MsgpackEncodingData { + return encoded; + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + return msgpackEncodingDataToJSONEncodingData(data as MsgpackEncodingData); + } + + public fromPreparedJSON(encoded: JSONEncodingData): MsgpackEncodingData { + return jsonEncodingDataToMsgpackEncodingData(encoded); + } +} diff --git a/src/encoding/uint64.ts b/src/encoding/uint64.ts index d19099654..3dc0a441e 100644 --- a/src/encoding/uint64.ts +++ b/src/encoding/uint64.ts @@ -1,4 +1,4 @@ -import { concatArrays } from '../utils/utils'; +import { concatArrays } from '../utils/utils.js'; // NOTE: at the moment we specifically do not use Buffer.writeBigUInt64BE and // Buffer.readBigUInt64BE. This is because projects using webpack v4 @@ -50,6 +50,7 @@ export function decodeUint64( decodingMode: 'mixed' ): number | bigint; export function decodeUint64(data: Uint8Array, decodingMode: 'bigint'): bigint; +export function decodeUint64(data: Uint8Array): number; export function decodeUint64(data: any, decodingMode: any = 'safe') { if ( decodingMode !== 'safe' && diff --git a/src/group.ts b/src/group.ts index 0e26384d3..4b0318e2b 100644 --- a/src/group.ts +++ b/src/group.ts @@ -1,98 +1,50 @@ -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'; +import { Transaction } from './transaction.js'; +import * as nacl from './nacl/naclWrappers.js'; +import { msgpackRawEncode } from './encoding/encoding.js'; +import * as utils from './utils/utils.js'; const ALGORAND_MAX_TX_GROUP_SIZE = 16; +const TX_GROUP_TAG = new TextEncoder().encode('TG'); -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; +function txGroupPreimage(txnHashes: Uint8Array[]): Uint8Array { + if (txnHashes.length > ALGORAND_MAX_TX_GROUP_SIZE) { + throw new Error( + `${txnHashes.length} transactions grouped together but max group size is ${ALGORAND_MAX_TX_GROUP_SIZE}` + ); } - - // 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()); + if (txnHashes.length === 0) { + throw new Error('Cannot compute group ID of zero transactions'); } + const bytes = msgpackRawEncode({ + txlist: txnHashes, + }); + return utils.concatArrays(TX_GROUP_TAG, bytes); } /** * computeGroupID returns group ID for a group of transactions - * @param txns - array of transactions (every element is a dict or Transaction) - * @returns Buffer + * @param txns - array of transactions + * @returns Uint8Array */ -export function computeGroupID(txns: txnBuilder.TransactionLike[]) { - const hashes = []; +export function computeGroupID(txns: ReadonlyArray) { + const hashes: Uint8Array[] = []; for (const txn of txns) { - const tx = txnBuilder.instantiateTxnIfNeeded(txn); - hashes.push(tx.rawTxID()); + hashes.push(txn.rawTxID()); } - const txgroup = new TxGroup(hashes); - - const bytes = txgroup.toByte(); - const toBeHashed = Buffer.from(utils.concatArrays(txgroup.tag, bytes)); + const toBeHashed = txGroupPreimage(hashes); const gid = nacl.genericHash(toBeHashed); - return Buffer.from(gid); + return Uint8Array.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 + * @param txns - array of transactions. The array elements will be modified with the group id */ -export function assignGroupID( - txns: txnBuilder.TransactionLike[], - from?: string -) { +export function assignGroupID(txns: Transaction[]) { 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); - } + txn.group = gid; } - return result; + return txns; } - -export default TxGroup; diff --git a/src/index.ts b/src/index.ts index 8ed43c6e2..7549f4d80 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import * as algosdk from './main'; +import * as algosdk from './main.js'; -export * from './main'; +export * from './main.js'; export default algosdk; diff --git a/src/logic/sourcemap.ts b/src/logic/sourcemap.ts index b00564911..3bda453ab 100644 --- a/src/logic/sourcemap.ts +++ b/src/logic/sourcemap.ts @@ -1,13 +1,46 @@ import * as vlq from 'vlq'; -export class SourceMap { - version: number; - sources: string[]; - names: string[]; - mappings: string; +/** + * Represents a location in a source file. + */ +export interface SourceLocation { + line: number; + column: number; + sourceIndex: number; + nameIndex?: number; +} + +/** + * Represents the location of a specific PC in a source line. + */ +export interface PcLineLocation { + pc: number; + column: number; + nameIndex?: number; +} + +/** + * Contains a mapping from TEAL program PC to source file location. + */ +export class ProgramSourceMap { + public readonly version: number; + /** + * A list of original sources used by the "mappings" entry. + */ + public readonly sources: string[]; + /** + * A list of symbol names used by the "mappings" entry. + */ + public readonly names: string[]; + /** + * A string with the encoded mapping data. + */ + public readonly mappings: string; - pcToLine: { [key: number]: number }; - lineToPc: { [key: number]: number[] }; + private pcToLocation: Map; + + // Key is `${sourceIndex}:${line}` + private sourceAndLineToPc: Map; constructor({ version, @@ -33,35 +66,67 @@ export class SourceMap { '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; + const pcList = this.mappings.split(';').map(vlq.decode); + + this.pcToLocation = new Map(); + this.sourceAndLineToPc = new Map(); + + const lastLocation = { + line: 0, + column: 0, + sourceIndex: 0, + nameIndex: 0, + } satisfies SourceLocation; + for (const [pc, data] of pcList.entries()) { + if (data.length < 4) continue; + + const nameDelta = data.length > 4 ? data[4] : undefined; + const [, sourceDelta, lineDelta, columnDelta] = data; + + lastLocation.sourceIndex += sourceDelta; + lastLocation.line += lineDelta; + lastLocation.column += columnDelta; + if (typeof nameDelta !== 'undefined') { + lastLocation.nameIndex += nameDelta; } - if (!(lastLine in this.lineToPc)) this.lineToPc[lastLine] = []; + const sourceAndLineKey = `${lastLocation.sourceIndex}:${lastLocation.line}`; + let pcsForSourceAndLine = this.sourceAndLineToPc.get(sourceAndLineKey); + if (pcsForSourceAndLine === undefined) { + pcsForSourceAndLine = []; + this.sourceAndLineToPc.set(sourceAndLineKey, pcsForSourceAndLine); + } - this.lineToPc[lastLine].push(pc); - this.pcToLine[pc] = lastLine; + const pcInLine: PcLineLocation = { + pc, + column: lastLocation.column, + }; + const pcLocation: SourceLocation = { + line: lastLocation.line, + column: lastLocation.column, + sourceIndex: lastLocation.sourceIndex, + }; + if (typeof nameDelta !== 'undefined') { + pcInLine.nameIndex = lastLocation.nameIndex; + pcLocation.nameIndex = lastLocation.nameIndex; + } + + pcsForSourceAndLine.push(pcInLine); + this.pcToLocation.set(pc, pcLocation); } } - getLineForPc(pc: number): number | undefined { - return this.pcToLine[pc]; + getPcs(): number[] { + return Array.from(this.pcToLocation.keys()); + } + + getLocationForPc(pc: number): SourceLocation | undefined { + return this.pcToLocation.get(pc); } - getPcsForLine(line: number): number[] | undefined { - return this.lineToPc[line]; + getPcsOnSourceLine(sourceIndex: number, line: number): PcLineLocation[] { + const pcs = this.sourceAndLineToPc.get(`${sourceIndex}:${line}`); + if (pcs === undefined) return []; + return pcs; } } diff --git a/src/logicsig.ts b/src/logicsig.ts index 34a35ca30..3267c24d9 100644 --- a/src/logicsig.ts +++ b/src/logicsig.ts @@ -1,25 +1,31 @@ -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 * as nacl from './nacl/naclWrappers.js'; +import { Address, isValidAddress } from './encoding/address.js'; +import * as encoding from './encoding/encoding.js'; +import { + NamedMapSchema, + ArraySchema, + ByteArraySchema, + FixedLengthByteArraySchema, + OptionalSchema, + allOmitEmpty, +} from './encoding/schema/index.js'; +import { + MultisigMetadata, + verifyMultisig, + addressFromMultisigPreImg, + pksFromAddresses, +} from './multisig.js'; +import * as utils from './utils/utils.js'; import { - EncodedLogicSig, - EncodedLogicSigAccount, EncodedMultisig, - EncodedSignedTransaction, -} from './types/transactions/encoded'; -import { MultisigMetadata } from './types/multisig'; + encodedMultiSigToEncodingData, + encodedMultiSigFromEncodingData, + ENCODED_MULTISIG_SCHEMA, +} from './types/transactions/encoded.js'; -interface LogicSigStorageStructure { - logic: Uint8Array; - args: Uint8Array[]; - sig?: Uint8Array; - msig?: EncodedMultisig; -} +// base64regex is the regex to test for base64 strings +const base64regex = + /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/; /** sanityCheckProgram performs heuristic program validation: * check if passed in bytes are Algorand address or is B64 encoded, rather than Teal bytes @@ -39,12 +45,12 @@ export function sanityCheckProgram(program: Uint8Array) { ); if (isAsciiPrintable) { - const programStr = Buffer.from(program).toString(); + const programStr = new TextDecoder().decode(program); if (isValidAddress(programStr)) throw new Error('requesting program bytes, get Algorand address'); - if (Buffer.from(programStr, 'base64').toString('base64') === programStr) + if (base64regex.test(programStr)) throw new Error('program should not be b64 encoded'); throw new Error( @@ -53,34 +59,50 @@ export function sanityCheckProgram(program: Uint8Array) { } } +const programTag = new TextEncoder().encode('Program'); + /** 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'); +export class LogicSig implements encoding.Encodable { + static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'l', + valueSchema: new ByteArraySchema(), + }, + { + key: 'arg', + valueSchema: new ArraySchema(new ByteArraySchema()), + }, + { + key: 'sig', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(64)), + }, + { + key: 'msig', + valueSchema: new OptionalSchema(ENCODED_MULTISIG_SCHEMA), + }, + ]) + ); logic: Uint8Array; args: Uint8Array[]; sig?: Uint8Array; msig?: EncodedMultisig; - constructor( - program: Uint8Array, - programArgs?: Array | null - ) { + constructor(program: Uint8Array, programArgs?: Array | null) { if ( programArgs && (!Array.isArray(programArgs) || - !programArgs.every( - (arg) => arg.constructor === Uint8Array || Buffer.isBuffer(arg) - )) + !programArgs.every((arg) => arg.constructor === Uint8Array)) ) { throw new TypeError('Invalid arguments'); } - let args: Uint8Array[] | undefined; + let args: Uint8Array[] = []; if (programArgs != null) args = programArgs.map((arg) => new Uint8Array(arg)); @@ -92,27 +114,32 @@ export class LogicSig implements LogicSigStorageStructure { 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; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): encoding.Schema { + return LogicSig.encodingSchema; + } + + toEncodingData(): Map { + const data = new Map([ + ['l', this.logic], + ['arg', this.args], + ['sig', this.sig], + ]); + if (this.msig) { + data.set('msig', encodedMultiSigToEncodingData(this.msig)); } - return obj; + return data; } - // 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; + static fromEncodingData(data: unknown): LogicSig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded logic sig: ${data}`); + } + const lsig = new LogicSig(data.get('l'), data.get('arg')); + lsig.sig = data.get('sig'); + if (data.get('msig')) { + lsig.msig = encodedMultiSigFromEncodingData(data.get('msig')); + } return lsig; } @@ -131,7 +158,7 @@ export class LogicSig implements LogicSigStorageStructure { return false; } - const toBeSigned = utils.concatArrays(this.tag, this.logic); + const toBeSigned = utils.concatArrays(programTag, this.logic); if (!this.sig && !this.msig) { const hash = nacl.genericHash(toBeSigned); @@ -142,17 +169,17 @@ export class LogicSig implements LogicSigStorageStructure { return nacl.verify(toBeSigned, this.sig, publicKey); } - return verifyMultisig(toBeSigned, this.msig, 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); + address(): Address { + const toBeSigned = utils.concatArrays(programTag, this.logic); const hash = nacl.genericHash(toBeSigned); - return address.encodeAddress(new Uint8Array(hash)); + return new Address(Uint8Array.from(hash)); } /** @@ -164,9 +191,7 @@ export class LogicSig implements LogicSigStorageStructure { if (msig == null) { this.sig = this.signProgram(secretKey); } else { - const subsigs = msig.addrs.map((addr) => ({ - pk: address.decodeAddress(addr).publicKey, - })); + const subsigs = pksFromAddresses(msig.addrs).map((pk) => ({ pk })); this.msig = { v: msig.version, @@ -192,7 +217,7 @@ export class LogicSig implements LogicSigStorageStructure { } signProgram(secretKey: Uint8Array) { - const toBeSigned = utils.concatArrays(this.tag, this.logic); + const toBeSigned = utils.concatArrays(programTag, this.logic); const sig = nacl.sign(toBeSigned, secretKey); return sig; } @@ -217,20 +242,32 @@ export class LogicSig implements LogicSigStorageStructure { return [sig, index]; } - toByte() { - return encoding.encode(this.get_obj_for_encoding()); + toByte(): Uint8Array { + return encoding.encodeMsgpack(this); } - static fromByte(encoded: ArrayLike) { - const decodedObj = encoding.decode(encoded) as EncodedLogicSig; - return LogicSig.from_obj_for_encoding(decodedObj); + static fromByte(encoded: ArrayLike): LogicSig { + return encoding.decodeMsgpack(encoded, LogicSig); } } /** * Represents an account that can sign with a LogicSig program. */ -export class LogicSigAccount { +export class LogicSigAccount implements encoding.Encodable { + static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'lsig', + valueSchema: LogicSig.encodingSchema, + }, + { + key: 'sigkey', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(32)), + }, + ]) + ); + lsig: LogicSig; sigkey?: Uint8Array; @@ -243,44 +280,48 @@ export class LogicSigAccount { * this LogicSig. * @param args - An optional array of arguments for the program. */ - constructor(program: Uint8Array, args?: Array | null) { + 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 class-methods-use-this + getEncodingSchema(): encoding.Schema { + return LogicSigAccount.encodingSchema; } - // 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; + toEncodingData(): Map { + return new Map([ + ['lsig', this.lsig.toEncodingData()], + ['sigkey', this.sigkey], + ]); + } + + static fromEncodingData(data: unknown): LogicSigAccount { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded logic sig account: ${data}`); + } + const value = data as Map; + const lsig = LogicSig.fromEncodingData(value.get('lsig')); + const lsigAccount = new LogicSigAccount(lsig.logic, lsig.args); + lsigAccount.lsig = lsig; // Restore other properties of the lsig + lsigAccount.sigkey = value.get('sigkey') as Uint8Array; return lsigAccount; } /** * Encode this object into msgpack. */ - toByte() { - return encoding.encode(this.get_obj_for_encoding()); + toByte(): Uint8Array { + return encoding.encodeMsgpack(this); } /** * 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); + static fromByte(encoded: ArrayLike): LogicSigAccount { + return encoding.decodeMsgpack(encoded, LogicSigAccount); } /** @@ -300,7 +341,7 @@ export class LogicSigAccount { */ verify() { const addr = this.address(); - return this.lsig.verify(address.decodeAddress(addr).publicKey); + return this.lsig.verify(addr.publicKey); } /** @@ -312,7 +353,7 @@ export class LogicSigAccount { * 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() { + address(): 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' @@ -323,7 +364,7 @@ export class LogicSigAccount { if (!this.sigkey) { throw new Error('Signing key for delegated account is missing'); } - return address.encodeAddress(this.sigkey); + return new Address(this.sigkey); } if (this.lsig.msig) { @@ -332,7 +373,7 @@ export class LogicSigAccount { threshold: this.lsig.msig.thr, pks: this.lsig.msig.subsig.map((subsig) => subsig.pk), }; - return address.encodeAddress(address.fromMultisigPreImg(msigMetadata)); + return addressFromMultisigPreImg(msigMetadata); } return this.lsig.address(); @@ -378,156 +419,67 @@ export class LogicSigAccount { } } -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); +export function logicSigFromByte(encoded: Uint8Array): LogicSig { + return encoding.decodeMsgpack(encoded, LogicSig); } -const SIGN_PROGRAM_DATA_PREFIX = Buffer.from('ProgData'); +const SIGN_PROGRAM_DATA_PREFIX = new TextEncoder().encode('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 sk - Uint8Array with secret key + * @param data - Uint8Array 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 + data: Uint8Array, + programHash: string | Address ) { - const parts = utils.concatArrays( - address.decodeAddress(programHash).publicKey, - data - ); - const toBeSigned = Buffer.from( - utils.concatArrays(SIGN_PROGRAM_DATA_PREFIX, parts) - ); + const programAddr = + typeof programHash === 'string' + ? Address.fromString(programHash) + : programHash; + const parts = utils.concatArrays(programAddr.publicKey, data); + const toBeSigned = 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 data - Uint8Array 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, + data: Uint8Array, + programHash: string | Address, sig: Uint8Array, pk: Uint8Array ) { - const parts = utils.concatArrays( - address.decodeAddress(programHash).publicKey, - data - ); - const toBeSigned = Buffer.from( - utils.concatArrays(SIGN_PROGRAM_DATA_PREFIX, parts) - ); + const programAddr = + typeof programHash === 'string' + ? Address.fromString(programHash) + : programHash; + const parts = utils.concatArrays(programAddr.publicKey, data); + const toBeSigned = 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 + * @param data - Uint8Array with data to sign + * @param program - Uint8Array with teal program */ export function tealSignFromProgram( sk: Uint8Array, - data: Uint8Array | Buffer, + data: Uint8Array, program: Uint8Array ) { const lsig = new LogicSig(program); diff --git a/src/main.ts b/src/main.ts index aadb49086..f5cfd0bb5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,13 +1,10 @@ -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'; +import * as nacl from './nacl/naclWrappers.js'; +import { Address } from './encoding/address.js'; +import { Transaction } from './transaction.js'; +import * as convert from './convert.js'; +import * as utils from './utils/utils.js'; -const SIGN_BYTES_PREFIX = Buffer.from([77, 88]); // "MX" +const SIGN_BYTES_PREFIX = Uint8Array.from([77, 88]); // "MX" // Errors export const MULTISIG_BAD_SENDER_ERROR_MSG = @@ -17,10 +14,10 @@ export const MULTISIG_BAD_SENDER_ERROR_MSG = * 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, + * Payment transaction fields: from, to, amount, fee, firstValid, lastValid, genesisHash, * note(optional), GenesisID(optional), closeRemainderTo(optional) * - * Key registration fields: fee, firstRound, lastRound, voteKey, selectionKey, voteFirst, + * Key registration fields: fee, firstValid, lastValid, 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. @@ -28,36 +25,13 @@ export const MULTISIG_BAD_SENDER_ERROR_MSG = * @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); - +export function signTransaction(txn: Transaction, sk: Uint8Array) { return { - txID: algoTxn.txID().toString(), - blob: algoTxn.signTxn(sk), + txID: txn.txID(), + blob: txn.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. @@ -66,7 +40,7 @@ export function signBid(bid: BidOptions, sk: Uint8Array) { * @returns binary signature */ export function signBytes(bytes: Uint8Array, sk: Uint8Array) { - const toBeSigned = Buffer.from(utils.concatArrays(SIGN_BYTES_PREFIX, bytes)); + const toBeSigned = utils.concatArrays(SIGN_BYTES_PREFIX, bytes); const sig = nacl.sign(toBeSigned, sk); return sig; } @@ -82,32 +56,11 @@ export function signBytes(bytes: Uint8Array, sk: Uint8Array) { export function verifyBytes( bytes: Uint8Array, signature: Uint8Array, - addr: string + addr: string | Address ) { - 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); + const toBeVerified = utils.concatArrays(SIGN_BYTES_PREFIX, bytes); + const addrObj = typeof addr === 'string' ? Address.fromString(addr) : addr; + return nacl.verify(toBeVerified, signature, addrObj.publicKey); } export const ERROR_MULTISIG_BAD_SENDER = new Error( @@ -117,35 +70,65 @@ 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 { AlgodClient as Algodv2 } from './client/v2/algod/algod.js'; +export { KmdClient as Kmd } from './client/kmd.js'; +export { default as IntDecoding } from './types/intDecoding.js'; +export { default as Account } from './types/account.js'; +export { IndexerClient as Indexer } from './client/v2/indexer/indexer.js'; export { BaseHTTPClient, BaseHTTPClientResponse, BaseHTTPClientError, -} from './client/baseHTTPClient'; +} from './client/baseHTTPClient.js'; export { AlgodTokenHeader, IndexerTokenHeader, KMDTokenHeader, CustomTokenHeader, TokenHeader, -} from './client/urlTokenBaseHTTPClient'; -export { waitForConfirmation } from './wait'; +} from './client/urlTokenBaseHTTPClient.js'; +export { waitForConfirmation } from './wait.js'; export { + MsgpackEncodingData, + JSONEncodingData, + Encodable, + EncodableClass, + encodeObj, + decodeObj, + msgpackRawEncode, + msgpackRawDecode, + msgpackRawDecodeAsMap, + encodeMsgpack, + decodeMsgpack, + encodeJSON, + decodeJSON, +} from './encoding/encoding.js'; +export { + Address, 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'; + ALGORAND_ZERO_ADDRESS_STRING, +} from './encoding/address.js'; +export { bytesToBigInt, bigIntToBytes } from './encoding/bigint.js'; +export { + base64ToBytes, + bytesToBase64, + bytesToString, + coerceToBytes, + bytesToHex, + hexToBytes, +} from './encoding/binarydata.js'; +export { encodeUint64, decodeUint64 } from './encoding/uint64.js'; +export { parseJSON, ParseJSONOptions, stringifyJSON } from './utils/utils.js'; +export { default as generateAccount } from './account.js'; +export * from './types/block.js'; +export * from './types/statedelta.js'; +export * from './stateproof.js'; +export { UntypedValue } from './client/v2/untypedmodel.js'; +export * as modelsv2 from './client/v2/algod/models/types.js'; +export * as indexerModels from './client/v2/indexer/models/types.js'; export { mnemonicToMasterDerivationKey, masterDerivationKeyToMnemonic, @@ -153,38 +136,52 @@ export { mnemonicToSecretKey, seedFromMnemonic, mnemonicFromSeed, -} from './mnemonic/mnemonic'; +} from './mnemonic/mnemonic.js'; export { microalgosToAlgos, algosToMicroalgos, INVALID_MICROALGOS_ERROR_MSG, -} from './convert'; -export { computeGroupID, assignGroupID } from './group'; +} from './convert.js'; +export { computeGroupID, assignGroupID } from './group.js'; +export { + SignedTransaction, + decodeSignedTransaction, + encodeUnsignedSimulateTransaction, +} from './signedTransaction.js'; export { - LogicSig, - LogicSigAccount, signLogicSigTransaction, signLogicSigTransactionObject, +} from './signing.js'; +export { + LogicSig, + LogicSigAccount, logicSigFromByte, tealSign, tealSignFromProgram, verifyTealSign, -} from './logicsig'; +} from './logicsig.js'; +export { + MultisigMetadata, + verifyMultisig, + multisigAddress, +} from './multisig.js'; export { signMultisigTransaction, mergeMultisigTransactions, appendSignMultisigTransaction, createMultisigTransaction, appendSignRawMultisigSignature, - verifyMultisig, - multisigAddress, -} from './multisig'; -export { SourceMap } from './logic/sourcemap'; +} from './multisigSigning.js'; +export { + ProgramSourceMap, + SourceLocation, + PcLineLocation, +} from './logic/sourcemap.js'; -export * from './dryrun'; -export * from './makeTxn'; -export * from './transaction'; -export * from './signer'; -export * from './composer'; -export * from './types'; -export * from './abi'; +export * from './dryrun.js'; +export * from './makeTxn.js'; +export * from './transaction.js'; +export * from './signer.js'; +export * from './composer.js'; +export * from './types/transactions/index.js'; +export * from './abi/index.js'; diff --git a/src/makeTxn.ts b/src/makeTxn.ts index 19bbe0883..00bd733ff 100644 --- a/src/makeTxn.ts +++ b/src/makeTxn.ts @@ -1,1476 +1,798 @@ -import * as txnBuilder from './transaction'; -import { OnApplicationComplete } from './types/transactions/base'; +import { Transaction } from './transaction.js'; import { - // Transaction types - PaymentTxn, - KeyRegistrationTxn, - - // Utilities + OnApplicationComplete, TransactionType, - MustHaveSuggestedParams, - AssetCreateTxn, - AssetConfigTxn, - AssetDestroyTxn, - AssetFreezeTxn, - AssetTransferTxn, - AppCreateTxn, - AppUpdateTxn, - AppDeleteTxn, - AppOptInTxn, - AppCloseOutTxn, - AppClearStateTxn, - AppNoOpTxn, -} from './types/transactions'; -import { RenameProperties, RenameProperty, Expand } from './types/utils'; + SuggestedParams, + PaymentTransactionParams, + KeyRegistrationTransactionParams, + AssetConfigurationTransactionParams, + AssetTransferTransactionParams, + AssetFreezeTransactionParams, + ApplicationCallTransactionParams, +} from './types/transactions/base.js'; +import { Address } from './encoding/address.js'; + +/** Contains parameters common to every transaction type */ +export interface CommonTransactionParams { + /** Algorand address of sender */ + sender: string | Address; + /** Suggested parameters relevant to the network that will accept this transaction */ + suggestedParams: SuggestedParams; + /** Optional, arbitrary data to be stored in the transaction's note field */ + note?: Uint8Array; + /** + * Optional, 32-byte lease to associate with this transaction. + * + * The sender cannot send another transaction with the same lease until the last round of original + * transaction has passed. + */ + lease?: Uint8Array; + /** The Algorand address that will be used to authorize all future transactions from the sender, if provided. */ + rekeyTo?: string | Address; +} /** - * 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 + * Create a new payment transaction * - * @deprecated This function will be removed in v3 in favor of {@link makePaymentTxnWithSuggestedParamsFromObject} + * @param options - Payment transaction parameters */ -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, +export function makePaymentTxnWithSuggestedParamsFromObject({ + sender, + receiver, + amount, + closeRemainderTo, + suggestedParams, + note, + lease, + rekeyTo, +}: PaymentTransactionParams & CommonTransactionParams): Transaction { + return new Transaction({ + type: TransactionType.pay, + sender, note, + lease, + rekeyTo, 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 - ); + paymentParams: { + receiver, + amount, + closeRemainderTo, + }, + }); } /** - * makeKeyRegistrationTxnWithSuggestedParams takes key registration arguments and returns a Transaction object for - * that key registration operation + * Create a new key registration transaction * - * @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 - * - * @deprecated This function will be removed in v3 in favor of {@link makeKeyRegistrationTxnWithSuggestedParamsFromObject} + * @param options - Key registration transaction parameters */ -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?: boolean, - 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, +export function makeKeyRegistrationTxnWithSuggestedParamsFromObject({ + sender, + voteKey, + selectionKey, + stateProofKey, + voteFirst, + voteLast, + voteKeyDilution, + nonParticipation, + suggestedParams, + note, + lease, + rekeyTo, +}: KeyRegistrationTransactionParams & CommonTransactionParams): Transaction { + return new Transaction({ + type: TransactionType.keyreg, + sender, note, - voteKey, - selectionKey, - voteFirst, - voteLast, - voteKeyDilution, + lease, + rekeyTo, suggestedParams, - type: TransactionType.keyreg, - reKeyTo: rekeyTo, - nonParticipation, - stateProofKey, - }; - return new txnBuilder.Transaction(o); + keyregParams: { + voteKey, + selectionKey, + stateProofKey, + voteFirst, + voteLast, + voteKeyDilution, + nonParticipation, + }, + }); } -// 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 +/** + * Base function for creating any type of asset config transaction. * - * @deprecated This function will be removed in v3 in favor of {@link makeAssetCreateTxnWithSuggestedParamsFromObject} + * @param options - Asset config transaction parameters */ -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, +export function makeBaseAssetConfigTxn({ + sender, + assetIndex, + total, + decimals, + defaultFrozen, + manager, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + note, + lease, + rekeyTo, + suggestedParams, +}: AssetConfigurationTransactionParams & CommonTransactionParams): Transaction { + return new Transaction({ + type: TransactionType.acfg, + sender, note, + lease, + rekeyTo, suggestedParams, - assetTotal: total, - assetDecimals: decimals, - assetDefaultFrozen: defaultFrozen, - assetUnitName: unitName, + assetConfigParams: { + assetIndex, + total, + decimals, + defaultFrozen, + manager, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + }, + }); +} + +/** + * Create a new asset creation transaction + * + * @param options - Asset creation transaction parameters + */ +export function makeAssetCreateTxnWithSuggestedParamsFromObject({ + sender, + total, + decimals, + defaultFrozen, + manager, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit & + CommonTransactionParams): Transaction { + return makeBaseAssetConfigTxn({ + sender, + total, + decimals, + defaultFrozen, + manager, + reserve, + freeze, + clawback, + unitName, assetName, assetURL, assetMetadataHash, - assetManager: manager, - assetReserve: reserve, - assetFreeze: freeze, - assetClawback: clawback, - type: TransactionType.acfg, - reKeyTo: rekeyTo, - }; - return new txnBuilder.Transaction(o); + note, + lease, + rekeyTo, + suggestedParams, + }); } -// 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 - ); +/** Contains asset modification transaction parameters */ +export interface AssetModificationTransactionParams { + /** + * The unique ID of the asset to be modified + */ + assetIndex: number | bigint; + + /** + * The Algorand address in charge of reserve, freeze, clawback, destruction, etc. + * + * If empty, this role will be irrevocably removed from this asset. + */ + manager?: string | Address; + + /** + * The Algorand address representing asset reserve. + * + * If empty, this role will be irrevocably removed from this asset. + */ + reserve?: string | Address; + + /** + * The Algorand address with power to freeze/unfreeze asset holdings. + * + * If empty, this role will be irrevocably removed from this asset. + */ + freeze?: string | Address; + + /** + * The Algorand address with power to revoke asset holdings. + * + * If empty, this role will be irrevocably removed from this asset. + */ + clawback?: string | Address; + + /** + * This is a safety flag to prevent unintentionally removing a role from an asset. If undefined or + * true, an error will be thrown if any of assetManager, assetReserve, assetFreeze, or + * assetClawback are empty. + * + * Set this to false to allow removing roles by leaving the corresponding address empty. + */ + strictEmptyAddressChecking?: boolean; } -/** 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 +/** + * Create a new asset config transaction. This transaction can be issued by the asset manager to + * change the manager, reserve, freeze, or clawback address. * - * @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 + * 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. * - * @deprecated This function will be removed in v3 in favor of {@link makeAssetConfigTxnWithSuggestedParamsFromObject} + * @param options - Asset modification transaction parameters */ -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'] -) { +export function makeAssetConfigTxnWithSuggestedParamsFromObject({ + sender, + assetIndex, + manager, + reserve, + freeze, + clawback, + strictEmptyAddressChecking, + note, + lease, + rekeyTo, + suggestedParams, +}: AssetModificationTransactionParams & CommonTransactionParams): Transaction { + if (!assetIndex) { + throw Error('assetIndex must be provided'); + } + const strictChecking = strictEmptyAddressChecking ?? true; if ( - strictEmptyAddressChecking && - (manager === undefined || - reserve === undefined || - freeze === undefined || - clawback === undefined) + strictChecking && + (manager == null || reserve == null || freeze == null || clawback == null) ) { throw Error( - 'strict empty address checking was turned on, but at least one empty address was provided' + 'strictEmptyAddressChecking is enabled, but an address is empty. If this is intentional, set strictEmptyAddressChecking to false.' ); } - const o: AssetConfigTxn = { - from, - suggestedParams, + return makeBaseAssetConfigTxn({ + sender, assetIndex, - assetManager: manager, - assetReserve: reserve, - assetFreeze: freeze, - assetClawback: clawback, - type: TransactionType.acfg, + manager, + reserve, + freeze, + clawback, 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 - ); + lease, + rekeyTo, + suggestedParams, + }); } -/** 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 +/** + * Create a new asset destroy transaction. This will allow the asset's manager to remove this asset + * from the ledger, provided all outstanding assets are held by the creator. * - * @deprecated This function will be removed in v3 in favor of {@link makeAssetDestroyTxnWithSuggestedParamsFromObject} + * @param options - Asset destroy transaction parameters */ -export function makeAssetDestroyTxnWithSuggestedParams( - from: AssetDestroyTxn['from'], - note: AssetDestroyTxn['note'], - assetIndex: AssetDestroyTxn['assetIndex'], - suggestedParams: MustHaveSuggestedParams['suggestedParams'], - rekeyTo?: AssetDestroyTxn['reKeyTo'] -) { - const o: AssetDestroyTxn = { - from, - suggestedParams, +export function makeAssetDestroyTxnWithSuggestedParamsFromObject({ + sender, + assetIndex, + note, + lease, + rekeyTo, + suggestedParams, +}: Required> & + CommonTransactionParams): Transaction { + if (!assetIndex) { + throw Error('assetIndex must be provided'); + } + return makeBaseAssetConfigTxn({ + sender, 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 - ); + lease, + rekeyTo, + suggestedParams, + }); } -/** 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 +/** + * Create a new asset freeze transaction. This transaction allows the asset's freeze manager to + * freeze or un-freeze an account, blocking or allowing asset transfers to and from the targeted + * account. * - * @deprecated This function will be removed in v3 in favor of {@link makeAssetFreezeTxnWithSuggestedParamsFromObject} + * @param options - Asset freeze transaction parameters */ -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, +export function makeAssetFreezeTxnWithSuggestedParamsFromObject({ + sender, + assetIndex, + freezeTarget, + frozen, + suggestedParams, + note, + lease, + rekeyTo, +}: AssetFreezeTransactionParams & CommonTransactionParams): Transaction { + return new Transaction({ type: TransactionType.afrz, - freezeAccount: freezeTarget, - assetIndex, - freezeState, + sender, note, + lease, + rekeyTo, suggestedParams, - reKeyTo: rekeyTo, - }; - return new txnBuilder.Transaction(o); + assetFreezeParams: { + assetIndex, + freezeTarget, + frozen, + }, + }); } -// 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. +/** + * Create a new asset transfer transaction. * - * @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 + * Special case: to opt into an assets, set amount=0 and sender=receiver. * - * @deprecated This function will be removed in v3 in favor of {@link makeAssetTransferTxnWithSuggestedParamsFromObject} + * @param options - Asset transfer transaction parameters */ -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 = { +export function makeAssetTransferTxnWithSuggestedParamsFromObject({ + sender, + receiver, + amount, + closeRemainderTo, + assetSender, + note, + assetIndex, + suggestedParams, + rekeyTo, + lease, +}: AssetTransferTransactionParams & CommonTransactionParams): Transaction { + if (!assetIndex) { + throw Error('assetIndex must be provided'); + } + return new Transaction({ type: TransactionType.axfer, - from, - to, - amount, - suggestedParams, - assetIndex, + sender, note, - assetRevocationTarget: revocationTarget, - closeRemainderTo, - reKeyTo: rekeyTo, - }; - return new txnBuilder.Transaction(o); + lease, + rekeyTo, + suggestedParams, + assetTransferParams: { + assetIndex, + receiver, + amount, + assetSender, + closeRemainderTo, + }, + }); } -// 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 - ); +/** + * Base function for creating any application call transaction. + * + * @param options - Application call transaction parameters + */ +export function makeApplicationCallTxnFromObject({ + sender, + appIndex, + onComplete, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + approvalProgram, + clearProgram, + numLocalInts, + numLocalByteSlices, + numGlobalInts, + numGlobalByteSlices, + extraPages, + note, + lease, + rekeyTo, + suggestedParams, +}: ApplicationCallTransactionParams & CommonTransactionParams): Transaction { + if (onComplete == null) { + throw Error('onComplete must be provided'); + } + return new Transaction({ + type: TransactionType.appl, + sender, + note, + lease, + rekeyTo, + suggestedParams, + appCallParams: { + appIndex, + onComplete, + appArgs, + accounts, + foreignAssets, + foreignApps, + boxes, + approvalProgram, + clearProgram, + numLocalInts, + numLocalByteSlices, + numGlobalInts, + numGlobalByteSlices, + extraPages, + }, + }); } /** * 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 * - * @deprecated This function will be removed in v3 in favor of {@link makeApplicationCreateTxnFromObject} + * @param options - Application creation transaction parameters */ -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, +export function makeApplicationCreateTxnFromObject({ + sender, + onComplete, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + approvalProgram, + clearProgram, + numLocalInts, + numLocalByteSlices, + numGlobalInts, + numGlobalByteSlices, + extraPages, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + 'appIndex' | 'approvalProgram' | 'clearProgram' +> & + Required< + Pick + > & + CommonTransactionParams): Transaction { + if (!approvalProgram || !clearProgram) { + throw Error('approvalProgram and clearProgram must be provided'); + } + if (onComplete == null) { + throw Error('onComplete must be provided'); + } + return makeApplicationCallTxnFromObject({ + sender, appIndex: 0, - appOnComplete: onComplete, - appLocalInts: numLocalInts, - appLocalByteSlices: numLocalByteSlices, - appGlobalInts: numGlobalInts, - appGlobalByteSlices: numGlobalByteSlices, - appApprovalProgram: approvalProgram, - appClearProgram: clearProgram, + onComplete, appArgs, - appAccounts: accounts, - appForeignApps: foreignApps, - appForeignAssets: foreignAssets, + accounts, + foreignApps, + foreignAssets, boxes, + approvalProgram, + clearProgram, + numLocalInts, + numLocalByteSlices, + numGlobalInts, + numGlobalByteSlices, + extraPages, 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 - ); + rekeyTo, + suggestedParams, + }); } /** * 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 * - * @deprecated This function will be removed in v3 in favor of {@link makeApplicationUpdateTxnFromObject} + * @param options - Application update transaction parameters */ -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, +export function makeApplicationUpdateTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + approvalProgram, + clearProgram, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + Required< + Pick + > & + CommonTransactionParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided'); + } + if (!approvalProgram || !clearProgram) { + throw Error('approvalProgram and clearProgram must be provided'); + } + return makeApplicationCallTxnFromObject({ + sender, appIndex, - appApprovalProgram: approvalProgram, - appOnComplete: OnApplicationComplete.UpdateApplicationOC, - appClearProgram: clearProgram, + onComplete: OnApplicationComplete.UpdateApplicationOC, appArgs, - appAccounts: accounts, - appForeignApps: foreignApps, - appForeignAssets: foreignAssets, + accounts, + foreignApps, + foreignAssets, boxes, + approvalProgram, + clearProgram, 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 - ); + rekeyTo, + suggestedParams, + }); } /** * 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 * - * @deprecated This function will be removed in v3 in favor of {@link makeApplicationDeleteTxnFromObject} + * @param options - Application deletion transaction parameters */ -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, +export function makeApplicationDeleteTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + CommonTransactionParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided'); + } + return makeApplicationCallTxnFromObject({ + sender, appIndex, - appOnComplete: OnApplicationComplete.DeleteApplicationOC, + onComplete: OnApplicationComplete.DeleteApplicationOC, appArgs, - appAccounts: accounts, - appForeignApps: foreignApps, - appForeignAssets: foreignAssets, + accounts, + foreignApps, + 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 - ); + rekeyTo, + suggestedParams, + }); } /** * 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 * - * @deprecated This function will be removed in v3 in favor of {@link makeApplicationOptInTxnFromObject} + * @param options - Application opt-in transaction parameters */ -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, +export function makeApplicationOptInTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + CommonTransactionParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided'); + } + return makeApplicationCallTxnFromObject({ + sender, appIndex, - appOnComplete: OnApplicationComplete.OptInOC, + onComplete: OnApplicationComplete.OptInOC, appArgs, - appAccounts: accounts, - appForeignApps: foreignApps, - appForeignAssets: foreignAssets, + accounts, + foreignApps, + 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 - ); + rekeyTo, + suggestedParams, + }); } /** * 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 * - * @deprecated This function will be removed in v3 in favor of {@link makeApplicationCloseOutTxnFromObject} + * @param options - Application close-out transaction parameters */ -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, +export function makeApplicationCloseOutTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + CommonTransactionParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided'); + } + return makeApplicationCallTxnFromObject({ + sender, appIndex, - appOnComplete: OnApplicationComplete.CloseOutOC, + onComplete: OnApplicationComplete.CloseOutOC, appArgs, - appAccounts: accounts, - appForeignApps: foreignApps, - appForeignAssets: foreignAssets, + accounts, + foreignApps, + 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 - ); + rekeyTo, + suggestedParams, + }); } /** * 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 * - * @deprecated This function will be removed in v3 in favor of {@link makeApplicationClearStateTxnFromObject} + * @param options - Application clear state transaction parameters */ -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, +export function makeApplicationClearStateTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + CommonTransactionParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided'); + } + return makeApplicationCallTxnFromObject({ + sender, appIndex, - appOnComplete: OnApplicationComplete.ClearStateOC, + onComplete: OnApplicationComplete.ClearStateOC, appArgs, - appAccounts: accounts, - appForeignApps: foreignApps, - appForeignAssets: foreignAssets, + accounts, + foreignApps, + 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 - ); + rekeyTo, + suggestedParams, + }); } /** * 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 * - * @deprecated This function will be removed in v3 in favor of {@link makeApplicationNoOpTxnFromObject} + * @param options - Application no-op transaction parameters */ -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, +export function makeApplicationNoOpTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + CommonTransactionParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided'); + } + return makeApplicationCallTxnFromObject({ + sender, appIndex, - appOnComplete: OnApplicationComplete.NoOpOC, + onComplete: OnApplicationComplete.NoOpOC, appArgs, - appAccounts: accounts, - appForeignApps: foreignApps, - appForeignAssets: foreignAssets, + accounts, + foreignApps, + 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); + rekeyTo, + suggestedParams, + }); } diff --git a/src/mnemonic/mnemonic.ts b/src/mnemonic/mnemonic.ts index 92bfa55d3..0b9adadea 100644 --- a/src/mnemonic/mnemonic.ts +++ b/src/mnemonic/mnemonic.ts @@ -1,19 +1,19 @@ /* 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'; +import english from './wordlists/english.js'; +import * as nacl from '../nacl/naclWrappers.js'; +import { Address } from '../encoding/address.js'; +import Account from '../types/account.js'; 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 = []; +function toUint11Array(buffer8: Uint8Array | number[]): number[] { + const buffer11: number[] = []; let acc = 0; let accBits = 0; - function add(octet) { + function add(octet: number) { acc |= octet << accBits; accBits += 8; if (accBits >= 11) { @@ -33,11 +33,11 @@ function toUint11Array(buffer8: Uint8Array | number[]) { return buffer11; } -function applyWords(nums: number[]) { +function applyWords(nums: number[]): string[] { return nums.map((n) => english[n]); } -function computeChecksum(seed: Uint8Array) { +function computeChecksum(seed: Uint8Array): string { const hashBuffer = nacl.genericHash(seed); const uint11Hash = toUint11Array(hashBuffer); const words = applyWords(uint11Hash); @@ -66,11 +66,11 @@ export function mnemonicFromSeed(seed: Uint8Array) { // from Uint11Array // https://stackoverflow.com/a/51452614 -function toUint8Array(buffer11: number[]) { - const buffer8 = []; +function toUint8Array(buffer11: number[]): Uint8Array { + const buffer8: number[] = []; let acc = 0; let accBits = 0; - function add(ui11) { + function add(ui11: number) { acc |= ui11 << accBits; accBits += 11; while (accBits >= 8) { @@ -146,8 +146,8 @@ export function seedFromMnemonic(mnemonic: string) { 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 }; + const addr = new Address(keys.publicKey); + return { addr, sk: keys.secretKey }; } /** diff --git a/src/multisig.ts b/src/multisig.ts index 7bf821d2d..6bde6f58c 100644 --- a/src/multisig.ts +++ b/src/multisig.ts @@ -1,317 +1,119 @@ -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 * as nacl from './nacl/naclWrappers.js'; import { - EncodedMultisig, - EncodedSignedTransaction, -} from './types/transactions/encoded'; + Address, + ALGORAND_ADDRESS_BYTE_LENGTH, + ALGORAND_CHECKSUM_BYTE_LENGTH, +} from './encoding/address.js'; +import * as utils from './utils/utils.js'; +import { EncodedMultisig } from './types/transactions/encoded.js'; /** 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.'; +/** + * Required options for creating a multisignature + * + * Documentation available at: https://developer.algorand.org/docs/get-details/transactions/signatures/#multisignatures + */ +export interface MultisigMetadata { + /** + * Multisig version + */ + version: number; -interface MultisigOptions { - rawSig: Uint8Array; - myPk: Uint8Array; -} + /** + * Multisig threshold value. Authorization requires a subset of signatures, + * equal to or greater than the threshold value. + */ + threshold: number; -interface MultisigMetadataWithPks extends Omit { - pks: Uint8Array[]; + /** + * A list of Algorand addresses representing possible signers for this multisig. Order is important. + */ + addrs: Array; } -/** - * 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) })); +// 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 msig: EncodedMultisig = { - v: version, - thr: threshold, - subsig: subsigs, - }; - const txnForEncoding = txn.get_obj_for_encoding(); - const signedTxn: EncodedSignedTransaction = { - msig, - txn: txnForEncoding, - }; +const INVALID_MSIG_VERSION_ERROR_MSG = 'invalid multisig version'; +const INVALID_MSIG_THRESHOLD_ERROR_MSG = 'bad multisig threshold'; +const INVALID_MSIG_PK_ERROR_MSG = 'bad multisig public key - wrong length'; +const UNEXPECTED_PK_LEN_ERROR_MSG = 'nacl public key length is not 32 bytes'; - // 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, +export function pksFromAddresses(addrs: Array): Uint8Array[] { + return addrs.map((addr) => { + if (typeof addr === 'string') { + return Address.fromString(addr).publicKey; + } + return addr.publicKey; }); - 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 + * 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 - ordered list of public keys in this multisig - * @returns encoded multisig blob + * @param pks - array of typed array public keys */ -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); +export function addressFromMultisigPreImg({ + version, + threshold, + pks, +}: Omit & { + pks: Uint8Array[]; +}): Address { + 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 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) + threshold === 0 || + pks.length === 0 || + threshold > pks.length || + threshold > 255 ) { - signedTxn.sgnr = Buffer.from(msigAddr); + throw new Error(INVALID_MSIG_THRESHOLD_ERROR_MSG); } - - 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); + const pkLen = ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH; + if (pkLen !== nacl.PUBLIC_KEY_LENGTH) { + throw new Error(UNEXPECTED_PK_LEN_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); + 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); } - 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; + merged.set(pks[i], MULTISIG_PREIMG2ADDR_PREFIX.length + 2 + i * pkLen); } + return new Address(Uint8Array.from(nacl.genericHash(merged))); } /** - * 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 + * 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 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 addressFromMultisigPreImgAddrs({ + version, + threshold, + addrs, +}: MultisigMetadata): Address { + const pks = pksFromAddresses(addrs); + return addressFromMultisigPreImg({ version, threshold, pks }); } export function verifyMultisig( @@ -330,7 +132,7 @@ export function verifyMultisig( let pk: Uint8Array; try { - pk = address.fromMultisigPreImg({ version, threshold, pks }); + pk = addressFromMultisigPreImg({ version, threshold, pks }).publicKey; } catch (e) { return false; } @@ -365,122 +167,6 @@ export function verifyMultisig( 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 @@ -491,6 +177,6 @@ export function multisigAddress({ version, threshold, addrs, -}: MultisigMetadata) { - return address.fromMultisigPreImgAddrs({ version, threshold, addrs }); +}: MultisigMetadata): Address { + return addressFromMultisigPreImgAddrs({ version, threshold, addrs }); } diff --git a/src/multisigSigning.ts b/src/multisigSigning.ts new file mode 100644 index 000000000..eeed88a9e --- /dev/null +++ b/src/multisigSigning.ts @@ -0,0 +1,359 @@ +import * as nacl from './nacl/naclWrappers.js'; +import { Address } from './encoding/address.js'; +import * as encoding from './encoding/encoding.js'; +import { Transaction } from './transaction.js'; +import * as utils from './utils/utils.js'; +import { EncodedMultisig } from './types/transactions/encoded.js'; +import { SignedTransaction } from './signedTransaction.js'; +import { + MultisigMetadata, + addressFromMultisigPreImg, + pksFromAddresses, +} from './multisig.js'; + +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.'; +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.'; +const MULTISIG_KEY_NOT_EXIST_ERROR_MSG = 'Key does not exist'; + +/** + * 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: Transaction, + { version, threshold, addrs }: MultisigMetadata +) { + // construct the appendable multisigned transaction format + const pks = pksFromAddresses(addrs); + const subsigs = pks.map((pk) => ({ pk })); + + const msig: EncodedMultisig = { + v: version, + thr: threshold, + subsig: subsigs, + }; + + // if the address of this multisig is different from the transaction sender, + // we need to add the auth-addr field + const msigAddr = addressFromMultisigPreImg({ + version, + threshold, + pks, + }); + let sgnr: Address | undefined; + if (!txn.sender.equals(msigAddr)) { + sgnr = msigAddr; + } + + const signedTxn = new SignedTransaction({ + txn, + msig, + sgnr, + }); + + return encoding.encodeMsgpack(signedTxn); +} + +interface MultisigOptions { + rawSig: Uint8Array; + myPk: Uint8Array; +} + +interface MultisigMetadataWithPks extends Omit { + pks: Uint8Array[]; +} + +/** + * createMultisigTransactionWithSignature creates a multisig transaction blob with an included signature. + * @param txn - the actual transaction to sign. + * @param rawSig - a Uint8Array 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: Transaction, + { rawSig, myPk }: MultisigOptions, + { version, threshold, pks }: MultisigMetadataWithPks +): Uint8Array { + // Create an empty encoded multisig transaction + const encodedMsig = createMultisigTransaction(txn, { + version, + threshold, + addrs: pks.map((pk) => new Address(pk)), + }); + // note: this is not signed yet, but will be shortly + const signedTxn = encoding.decodeMsgpack(encodedMsig, SignedTransaction); + + 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) { + throw new Error(MULTISIG_KEY_NOT_EXIST_ERROR_MSG); + } + + return encoding.encodeMsgpack(signedTxn); +} + +/** + * partialSignTxn partially signs this transaction and returns a partially-signed multisig transaction, + * encoded with msgpack as a typed array. + * @param transaction - The transaction to sign + * @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. + */ +function partialSignTxn( + transaction: Transaction, + { version, threshold, pks }: MultisigMetadataWithPks, + sk: Uint8Array +) { + // get signature verifier + const myPk = nacl.keyPairFromSecretKey(sk).publicKey; + return createMultisigTransactionWithSignature( + transaction, + { rawSig: transaction.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 transaction - The transaction to sign + * @param metadata - multisig metadata + * @param signerAddr - address of the signer + * @param signature - raw multisig signature + * @returns an encoded, partially signed multisig transaction. + */ +function partialSignWithMultisigSignature( + transaction: Transaction, + metadata: MultisigMetadataWithPks, + signerAddr: string | Address, + signature: Uint8Array +) { + if (!nacl.isValidSignatureLength(signature.length)) { + throw new Error(MULTISIG_SIGNATURE_LENGTH_ERROR_MSG); + } + const signerAddressObj = + typeof signerAddr === 'string' + ? Address.fromString(signerAddr) + : signerAddr; + return createMultisigTransactionWithSignature( + transaction, + { + rawSig: signature, + myPk: signerAddressObj.publicKey, + }, + metadata + ); +} + +/** + * 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.decodeMsgpack( + multisigTxnBlobs[0], + SignedTransaction + ); + if (!refSigTx.msig) { + throw new Error( + 'Invalid multisig transaction, multisig structure missing at index 0' + ); + } + const refTxID = refSigTx.txn.txID(); + const refAuthAddr = refSigTx.sgnr ? refSigTx.sgnr.toString() : undefined; + const refPreImage = { + version: refSigTx.msig.v, + threshold: refSigTx.msig.thr, + pks: refSigTx.msig.subsig.map((subsig) => subsig.pk), + }; + const refMsigAddr = addressFromMultisigPreImg(refPreImage); + + const newSubsigs = refSigTx.msig.subsig.map((sig) => ({ ...sig })); + for (let i = 1; i < multisigTxnBlobs.length; i++) { + const unisig = encoding.decodeMsgpack( + multisigTxnBlobs[i], + SignedTransaction + ); + if (!unisig.msig) { + throw new Error( + `Invalid multisig transaction, multisig structure missing at index ${i}` + ); + } + + if (unisig.txn.txID() !== refTxID) { + throw new Error(MULTISIG_MERGE_MISMATCH_ERROR_MSG); + } + + const authAddr = unisig.sgnr ? unisig.sgnr.toString() : 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 = addressFromMultisigPreImg(preimg); + if (!refMsigAddr.equals(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]; + if (current.s && !utils.arrayEqual(uniSubsig.s, current.s)) { + // 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 refSgnr = + typeof refAuthAddr !== 'undefined' ? refSigTx.sgnr : undefined; + const signedTxn = new SignedTransaction({ + msig, + txn: refSigTx.txn, + sgnr: refSgnr, + }); + return encoding.encodeMsgpack(signedTxn); +} + +/** + * 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: Transaction, + { version, threshold, addrs }: MultisigMetadata, + sk: Uint8Array +) { + // build pks for partialSign + const pks = pksFromAddresses(addrs); + const blob = partialSignTxn(txn, { version, threshold, pks }, sk); + return { + txID: txn.txID(), + 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 = pksFromAddresses(addrs); + // obtain underlying txn, sign it, and merge it + const multisigTxObj = encoding.decodeMsgpack( + multisigTxnBlob, + SignedTransaction + ); + const partialSignedBlob = partialSignTxn( + multisigTxObj.txn, + { version, threshold, pks }, + sk + ); + return { + txID: multisigTxObj.txn.txID(), + 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 | Address, + signature: Uint8Array +) { + const pks = pksFromAddresses(addrs); + // obtain underlying txn, sign it, and merge it + const multisigTxObj = encoding.decodeMsgpack( + multisigTxnBlob, + SignedTransaction + ); + const partialSignedBlob = partialSignWithMultisigSignature( + multisigTxObj.txn, + { version, threshold, pks }, + signerAddr, + signature + ); + return { + txID: multisigTxObj.txn.txID(), + blob: mergeMultisigTransactions([multisigTxnBlob, partialSignedBlob]), + }; +} diff --git a/src/nacl/naclWrappers.ts b/src/nacl/naclWrappers.ts index a80ca5586..00d858119 100644 --- a/src/nacl/naclWrappers.ts +++ b/src/nacl/naclWrappers.ts @@ -1,6 +1,6 @@ import nacl from 'tweetnacl'; import sha512 from 'js-sha512'; -import { isReactNative } from '../utils/utils'; +import { isReactNative } from '../utils/utils.js'; export function genericHash(arr: sha512.Message) { return sha512.sha512_256.array(arr); diff --git a/src/signedTransaction.ts b/src/signedTransaction.ts new file mode 100644 index 000000000..03bf4d44f --- /dev/null +++ b/src/signedTransaction.ts @@ -0,0 +1,163 @@ +import { + Encodable, + encodeMsgpack, + decodeMsgpack, +} from './encoding/encoding.js'; +import { Address } from './encoding/address.js'; +import { Transaction } from './transaction.js'; +import { LogicSig } from './logicsig.js'; +import { + EncodedMultisig, + encodedMultiSigToEncodingData, + encodedMultiSigFromEncodingData, + ENCODED_MULTISIG_SCHEMA, +} from './types/transactions/index.js'; +import { + AddressSchema, + FixedLengthByteArraySchema, + OptionalSchema, + NamedMapSchema, + allOmitEmpty, +} from './encoding/schema/index.js'; + +export class SignedTransaction implements Encodable { + static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'txn', + valueSchema: Transaction.encodingSchema, + }, + { + key: 'sig', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(64)), + }, + { + key: 'msig', + valueSchema: new OptionalSchema(ENCODED_MULTISIG_SCHEMA), + }, + { + key: 'lsig', + valueSchema: new OptionalSchema(LogicSig.encodingSchema), + }, + { + key: 'sgnr', + valueSchema: new OptionalSchema(new AddressSchema()), + }, + ]) + ); + + /** + * The transaction that was signed + */ + public readonly txn: Transaction; + + /** + * Transaction signature + */ + public readonly sig?: Uint8Array; + + /** + * Multisig structure + */ + public readonly msig?: EncodedMultisig; + + /** + * Logic signature + */ + public readonly lsig?: LogicSig; + + /** + * The signer, if signing with a different key than the Transaction type `sender` property indicates + */ + public readonly sgnr?: Address; + + constructor({ + txn, + sig, + msig, + lsig, + sgnr, + }: { + txn: Transaction; + sig?: Uint8Array; + msig?: EncodedMultisig; + lsig?: LogicSig; + sgnr?: Address; + }) { + this.txn = txn; + this.sig = sig; + this.msig = msig; + this.lsig = lsig; + this.sgnr = sgnr; + + let numberOfSigs = 0; + if (sig) numberOfSigs += 1; + if (msig) numberOfSigs += 1; + if (lsig) numberOfSigs += 1; + if (numberOfSigs > 1) { + throw new Error( + `SignedTransaction must not have more than 1 signature. Got ${numberOfSigs}` + ); + } + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema() { + return SignedTransaction.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['txn', this.txn.toEncodingData()], + ['sig', this.sig], + [ + 'msig', + this.msig ? encodedMultiSigToEncodingData(this.msig) : undefined, + ], + ['lsig', this.lsig ? this.lsig.toEncodingData() : undefined], + ['sgnr', this.sgnr], + ]); + } + + public static fromEncodingData(data: unknown): SignedTransaction { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SignedTransaction: ${data}`); + } + return new SignedTransaction({ + txn: Transaction.fromEncodingData(data.get('txn')), + sig: data.get('sig'), + msig: data.get('msig') + ? encodedMultiSigFromEncodingData(data.get('msig')) + : undefined, + lsig: data.get('lsig') + ? LogicSig.fromEncodingData(data.get('lsig')) + : undefined, + sgnr: data.get('sgnr'), + }); + } +} + +/** + * decodeSignedTransaction takes a Uint8Array (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 { + return decodeMsgpack(transactionBuffer, SignedTransaction); +} + +/** + * 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 txn - Transaction object to simulate. + */ +export function encodeUnsignedSimulateTransaction(txn: Transaction) { + const stxn = new SignedTransaction({ txn }); + return encodeMsgpack(stxn); +} diff --git a/src/signer.ts b/src/signer.ts index 80e711234..ac6e82a7d 100644 --- a/src/signer.ts +++ b/src/signer.ts @@ -1,8 +1,13 @@ -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'; +import Account from './types/account.js'; +import { Transaction } from './transaction.js'; +import { encodeUnsignedSimulateTransaction } from './signedTransaction.js'; +import { LogicSigAccount } from './logicsig.js'; +import { signLogicSigTransactionObject } from './signing.js'; +import { MultisigMetadata } from './multisig.js'; +import { + signMultisigTransaction, + mergeMultisigTransactions, +} from './multisigSigning.js'; /** * This type represents a function which can sign transactions from an atomic transaction group. diff --git a/src/signing.ts b/src/signing.ts new file mode 100644 index 000000000..29fd5bcc2 --- /dev/null +++ b/src/signing.ts @@ -0,0 +1,95 @@ +import * as nacl from './nacl/naclWrappers.js'; +import { Address } from './encoding/address.js'; +import * as encoding from './encoding/encoding.js'; +import { SignedTransaction } from './signedTransaction.js'; +import { Transaction } from './transaction.js'; +import { LogicSig, LogicSigAccount } from './logicsig.js'; +import { addressFromMultisigPreImg } from './multisig.js'; + +function signLogicSigTransactionWithAddress( + txn: Transaction, + lsig: LogicSig, + lsigAddress: Address +) { + if (!lsig.verify(lsigAddress.publicKey)) { + throw new Error( + 'Logic signature verification failed. Ensure the program and signature are valid.' + ); + } + + let sgnr: Address | undefined; + if (!nacl.bytesEqual(lsigAddress.publicKey, txn.sender.publicKey)) { + sgnr = lsigAddress; + } + + const signedTxn = new SignedTransaction({ + lsig, + txn, + sgnr, + }); + + return { + txID: txn.txID(), + blob: encoding.encodeMsgpack(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: Transaction, + lsigObject: LogicSig | LogicSigAccount +) { + let lsig: LogicSig; + let lsigAddress: Address; + + if (lsigObject instanceof LogicSigAccount) { + lsig = lsigObject.lsig; + lsigAddress = lsigObject.address(); + } 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 = new Address(txn.sender.publicKey); + } else if (lsig.msig) { + const msigMetadata = { + version: lsig.msig.v, + threshold: lsig.msig.thr, + pks: lsig.msig.subsig.map((subsig) => subsig.pk), + }; + lsigAddress = addressFromMultisigPreImg(msigMetadata); + } else { + lsigAddress = lsig.address(); + } + } + + 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: Transaction, + lsigObject: LogicSig | LogicSigAccount +) { + return signLogicSigTransactionObject(txn, lsigObject); +} diff --git a/src/stateproof.ts b/src/stateproof.ts new file mode 100644 index 000000000..bf1e17d28 --- /dev/null +++ b/src/stateproof.ts @@ -0,0 +1,595 @@ +import { Encodable, Schema } from './encoding/encoding.js'; +import { + Uint64Schema, + ByteArraySchema, + FixedLengthByteArraySchema, + ArraySchema, + NamedMapSchema, + Uint64MapSchema, + allOmitEmpty, + convertMap, +} from './encoding/schema/index.js'; + +export class HashFactory implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 't', valueSchema: new Uint64Schema() }, // hashType + ]) + ); + + public hashType: number; + + public constructor(params: { hashType: number }) { + this.hashType = params.hashType; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return HashFactory.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([['t', this.hashType]]); + } + + public static fromEncodingData(data: unknown): HashFactory { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded HashFactory: ${data}`); + } + return new HashFactory({ + hashType: Number(data.get('t')), + }); + } +} + +export class MerkleArrayProof implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'pth', // path + valueSchema: new ArraySchema(new ByteArraySchema()), + }, + { + key: 'hsh', // hashFactory + valueSchema: HashFactory.encodingSchema, + }, + { + key: 'td', // treeDepth + valueSchema: new Uint64Schema(), + }, + ]) + ); + + /** + * Path is bounded by MaxNumLeavesOnEncodedTree since there could be multiple reveals, and + * given the distribution of the elt positions and the depth of the tree, the path length can + * increase up to 2^MaxEncodedTreeDepth / 2 + */ + public path: Uint8Array[]; + + public hashFactory: HashFactory; + + /** + * TreeDepth represents the depth of the tree that is being proven. It is the number of edges + * from the root to a leaf. + */ + public treeDepth: number; + + public constructor(params: { + path: Uint8Array[]; + hashFactory: HashFactory; + treeDepth: number; + }) { + this.path = params.path; + this.hashFactory = params.hashFactory; + this.treeDepth = params.treeDepth; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return MerkleArrayProof.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['pth', this.path], + ['hsh', this.hashFactory.toEncodingData()], + ['td', this.treeDepth], + ]); + } + + public static fromEncodingData(data: unknown): MerkleArrayProof { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded MerkleArrayProof: ${data}`); + } + return new MerkleArrayProof({ + path: data.get('pth'), + hashFactory: HashFactory.fromEncodingData(data.get('hsh')), + treeDepth: Number(data.get('td')), + }); + } +} + +/** + * MerkleSignatureVerifier is used to verify a merkle signature. + */ +export class MerkleSignatureVerifier implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'cmt', // commitment + valueSchema: new FixedLengthByteArraySchema(64), + }, + { + key: 'lf', // keyLifetime + valueSchema: new Uint64Schema(), + }, + ]) + ); + + public commitment: Uint8Array; + + public keyLifetime: bigint; + + public constructor(params: { commitment: Uint8Array; keyLifetime: bigint }) { + this.commitment = params.commitment; + this.keyLifetime = params.keyLifetime; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return MerkleSignatureVerifier.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['cmt', this.commitment], + ['lf', this.keyLifetime], + ]); + } + + public static fromEncodingData(data: unknown): MerkleSignatureVerifier { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded MerkleSignatureVerifier: ${data}`); + } + return new MerkleSignatureVerifier({ + commitment: data.get('cmt'), + keyLifetime: data.get('lf'), + }); + } +} + +/** + * A Participant corresponds to an account whose AccountData.Status is Online, and for which the + * expected sigRound satisfies AccountData.VoteFirstValid <= sigRound <= AccountData.VoteLastValid. + * + * In the Algorand ledger, it is possible for multiple accounts to have the same PK. Thus, the PK is + * not necessarily unique among Participants. However, each account will produce a unique Participant + * struct, to avoid potential DoS attacks where one account claims to have the same VoteID PK as + * another account. + */ +export class Participant implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'p', // pk + valueSchema: MerkleSignatureVerifier.encodingSchema, + }, + { + key: 'w', // weight + valueSchema: new Uint64Schema(), + }, + ]) + ); + + /** + * pk is the identifier used to verify the signature for a specific participant + */ + public pk: MerkleSignatureVerifier; + + /** + * weight is AccountData.MicroAlgos. + */ + public weight: bigint; + + public constructor(params: { pk: MerkleSignatureVerifier; weight: bigint }) { + this.pk = params.pk; + this.weight = params.weight; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return Participant.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['p', this.pk.toEncodingData()], + ['w', this.weight], + ]); + } + + public static fromEncodingData(data: unknown): Participant { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Participant: ${data}`); + } + return new Participant({ + pk: MerkleSignatureVerifier.fromEncodingData(data.get('p')), + weight: data.get('w'), + }); + } +} + +export class FalconVerifier implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'k', valueSchema: new FixedLengthByteArraySchema(0x701) }, // publicKey + ]) + ); + + public publicKey: Uint8Array; + + public constructor(params: { publicKey: Uint8Array }) { + this.publicKey = params.publicKey; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return FalconVerifier.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([['k', this.publicKey]]); + } + + public static fromEncodingData(data: unknown): FalconVerifier { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded FalconVerifier: ${data}`); + } + return new FalconVerifier({ + publicKey: data.get('k'), + }); + } +} + +/** + * FalconSignatureStruct represents a signature in the merkle signature scheme using falcon signatures + * as an underlying crypto scheme. It consists of an ephemeral public key, a signature, a merkle + * verification path and an index. The merkle signature considered valid only if the Signature is + * verified under the ephemeral public key and the Merkle verification path verifies that the + * ephemeral public key is located at the given index of the tree (for the root given in the + * long-term public key). More details can be found on Algorand's spec + */ +export class FalconSignatureStruct implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'sig', valueSchema: new ByteArraySchema() }, // signature + { key: 'idx', valueSchema: new Uint64Schema() }, // index + { key: 'prf', valueSchema: MerkleArrayProof.encodingSchema }, // proof + { key: 'vkey', valueSchema: FalconVerifier.encodingSchema }, // verifyingKey + ]) + ); + + public signature: Uint8Array; + public vectorCommitmentIndex: bigint; + public proof: MerkleArrayProof; + public verifyingKey: FalconVerifier; + + public constructor(params: { + signature: Uint8Array; + index: bigint; + proof: MerkleArrayProof; + verifyingKey: FalconVerifier; + }) { + this.signature = params.signature; + this.vectorCommitmentIndex = params.index; + this.proof = params.proof; + this.verifyingKey = params.verifyingKey; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return FalconSignatureStruct.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['sig', this.signature], + ['idx', this.vectorCommitmentIndex], + ['prf', this.proof.toEncodingData()], + ['vkey', this.verifyingKey.toEncodingData()], + ]); + } + + public static fromEncodingData(data: unknown): FalconSignatureStruct { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded FalconSignatureStruct: ${data}`); + } + return new FalconSignatureStruct({ + signature: data.get('sig'), + index: data.get('idx'), + proof: MerkleArrayProof.fromEncodingData(data.get('prf')), + verifyingKey: FalconVerifier.fromEncodingData(data.get('vkey')), + }); + } +} + +/** + * A SigslotCommit is a single slot in the sigs array that forms the state proof. + */ +export class SigslotCommit implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 's', valueSchema: FalconSignatureStruct.encodingSchema }, // sigslot + { key: 'l', valueSchema: new Uint64Schema() }, // l + ]) + ); + + /** + * Sig is a signature by the participant on the expected message. + */ + public sig: FalconSignatureStruct; + + /** + * L is the total weight of signatures in lower-numbered slots. This is initialized once the builder + * has collected a sufficient number of signatures. + */ + public l: bigint; + + public constructor(params: { sig: FalconSignatureStruct; l: bigint }) { + this.sig = params.sig; + this.l = params.l; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return SigslotCommit.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['s', this.sig.toEncodingData()], + ['l', this.l], + ]); + } + + public static fromEncodingData(data: unknown): SigslotCommit { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SigslotCommit: ${data}`); + } + return new SigslotCommit({ + sig: FalconSignatureStruct.fromEncodingData(data.get('s')), + l: data.get('l'), + }); + } +} + +/** + * Reveal is a single array position revealed as part of a state proof. It reveals an element of the + * signature array and the corresponding element of the participants array. + */ +export class Reveal implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 's', valueSchema: SigslotCommit.encodingSchema }, // sigslotCommit + { key: 'p', valueSchema: Participant.encodingSchema }, // participant + ]) + ); + + public sigslot: SigslotCommit; + + public participant: Participant; + + public constructor(params: { + sigslot: SigslotCommit; + participant: Participant; + }) { + this.sigslot = params.sigslot; + this.participant = params.participant; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return Reveal.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['s', this.sigslot.toEncodingData()], + ['p', this.participant.toEncodingData()], + ]); + } + + public static fromEncodingData(data: unknown): Reveal { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Reveal: ${data}`); + } + return new Reveal({ + sigslot: SigslotCommit.fromEncodingData(data.get('s')), + participant: Participant.fromEncodingData(data.get('p')), + }); + } +} + +export class StateProof implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'c', // sigCommit + valueSchema: new ByteArraySchema(), + }, + { + key: 'w', // signedWeight + valueSchema: new Uint64Schema(), + }, + { + key: 'S', // sigProofs + valueSchema: MerkleArrayProof.encodingSchema, + }, + { + key: 'P', // partProofs + valueSchema: MerkleArrayProof.encodingSchema, + }, + { + key: 'v', // merkleSignatureSaltVersion + valueSchema: new Uint64Schema(), + }, + { + key: 'r', // reveals + valueSchema: new Uint64MapSchema(Reveal.encodingSchema), + }, + { + key: 'pr', // positionsToReveal + valueSchema: new ArraySchema(new Uint64Schema()), + }, + ]) + ); + + public sigCommit: Uint8Array; + + public signedWeight: bigint; + + public sigProofs: MerkleArrayProof; + + public partProofs: MerkleArrayProof; + + public merkleSignatureSaltVersion: number; + + /** + * Reveals is a sparse map from the position being revealed to the corresponding elements from the + * sigs and participants arrays. + */ + public reveals: Map; + + public positionsToReveal: bigint[]; + + public constructor(params: { + sigCommit: Uint8Array; + signedWeight: bigint; + sigProofs: MerkleArrayProof; + partProofs: MerkleArrayProof; + merkleSignatureSaltVersion: number; + reveals: Map; + positionsToReveal: bigint[]; + }) { + this.sigCommit = params.sigCommit; + this.signedWeight = params.signedWeight; + this.sigProofs = params.sigProofs; + this.partProofs = params.partProofs; + this.merkleSignatureSaltVersion = params.merkleSignatureSaltVersion; + this.reveals = params.reveals; + this.positionsToReveal = params.positionsToReveal; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return StateProof.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['c', this.sigCommit], + ['w', this.signedWeight], + ['S', this.sigProofs.toEncodingData()], + ['P', this.partProofs.toEncodingData()], + ['v', this.merkleSignatureSaltVersion], + [ + 'r', + convertMap(this.reveals, (key, value) => [key, value.toEncodingData()]), + ], + ['pr', this.positionsToReveal], + ]); + } + + public static fromEncodingData(data: unknown): StateProof { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProof: ${data}`); + } + return new StateProof({ + sigCommit: data.get('c'), + signedWeight: data.get('w'), + sigProofs: MerkleArrayProof.fromEncodingData(data.get('S')), + partProofs: MerkleArrayProof.fromEncodingData(data.get('P')), + merkleSignatureSaltVersion: Number(data.get('v')), + reveals: convertMap(data.get('r'), (key, value) => [ + key as bigint, + Reveal.fromEncodingData(value), + ]), + positionsToReveal: data.get('pr'), + }); + } +} + +export class StateProofMessage implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'b', valueSchema: new ByteArraySchema() }, // blockHeadersCommitment + { key: 'v', valueSchema: new ByteArraySchema() }, // votersCommitment + { key: 'P', valueSchema: new Uint64Schema() }, // lnProvenWeight + { key: 'f', valueSchema: new Uint64Schema() }, // firstAttestedRound + { key: 'l', valueSchema: new Uint64Schema() }, // lastAttestedRound + ]) + ); + + public blockHeadersCommitment: Uint8Array; + + public votersCommitment: Uint8Array; + + public lnProvenWeight: bigint; + + public firstAttestedRound: bigint; + + public lastAttestedRound: bigint; + + public constructor(params: { + blockHeadersCommitment: Uint8Array; + votersCommitment: Uint8Array; + lnProvenWeight: bigint; + firstAttestedRound: bigint; + lastAttestedRound: bigint; + }) { + this.blockHeadersCommitment = params.blockHeadersCommitment; + this.votersCommitment = params.votersCommitment; + this.lnProvenWeight = params.lnProvenWeight; + this.firstAttestedRound = params.firstAttestedRound; + this.lastAttestedRound = params.lastAttestedRound; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return StateProofMessage.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['b', this.blockHeadersCommitment], + ['v', this.votersCommitment], + ['P', this.lnProvenWeight], + ['f', this.firstAttestedRound], + ['l', this.lastAttestedRound], + ]); + } + + public static fromEncodingData(data: unknown): StateProofMessage { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofMessage: ${data}`); + } + return new StateProofMessage({ + blockHeadersCommitment: data.get('b'), + votersCommitment: data.get('v'), + lnProvenWeight: data.get('P'), + firstAttestedRound: data.get('f'), + lastAttestedRound: data.get('l'), + }); + } + + public static fromMap(data: Map): StateProofMessage { + return new StateProofMessage({ + blockHeadersCommitment: data.get('b') as Uint8Array, + votersCommitment: data.get('v') as Uint8Array, + lnProvenWeight: data.get('P') as bigint, + firstAttestedRound: data.get('f') as bigint, + lastAttestedRound: data.get('l') as bigint, + }); + } +} diff --git a/src/transaction.ts b/src/transaction.ts index 884de26bf..b27b63617 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -1,1298 +1,1106 @@ -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 { boxReferencesToEncodingData } from './boxStorage.js'; +import { Address } from './encoding/address.js'; +import * as encoding from './encoding/encoding.js'; import { + AddressSchema, + Uint64Schema, + ByteArraySchema, + FixedLengthByteArraySchema, + StringSchema, + ArraySchema, + NamedMapSchema, + BooleanSchema, + OptionalSchema, + allOmitEmpty, +} from './encoding/schema/index.js'; +import * as nacl from './nacl/naclWrappers.js'; +import { + SuggestedParams, + BoxReference, OnApplicationComplete, + isOnApplicationComplete, TransactionParams, TransactionType, isTransactionType, - BoxReference, -} from './types/transactions/base'; -import AnyTransaction, { - MustHaveSuggestedParams, - MustHaveSuggestedParamsInline, - EncodedTransaction, - EncodedSignedTransaction, - EncodedMultisig, - EncodedLogicSig, -} from './types/transactions'; -import { Address } from './types/address'; + PaymentTransactionParams, + AssetConfigurationTransactionParams, + AssetTransferTransactionParams, + AssetFreezeTransactionParams, + KeyRegistrationTransactionParams, + ApplicationCallTransactionParams, + StateProofTransactionParams, +} from './types/transactions/base.js'; +import { StateProof, StateProofMessage } from './stateproof.js'; +import * as utils from './utils/utils.js'; 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; +const ALGORAND_TRANSACTION_GROUP_LENGTH = 32; -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 uint8ArrayIsEmpty(input: Uint8Array): boolean { + return input.every((value) => value === 0); } function getKeyregKey( - input: undefined | string | Uint8Array | Buffer, + input: undefined | string | Uint8Array, inputName: string, length: number -): Buffer | undefined { +): Uint8Array | undefined { if (input == null) { return undefined; } - let inputAsBuffer: Buffer | undefined; + let inputBytes: Uint8Array | undefined; + + if (input instanceof Uint8Array) { + inputBytes = input; + } + + if (inputBytes == null || inputBytes.byteLength !== length) { + throw Error(`${inputName} must be a ${length} byte Uint8Array`); + } + + return inputBytes; +} +function ensureAddress(input: unknown): Address { + if (input == null) { + throw new Error('Address must not be null or 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; + return Address.fromString(input); + } + if (input instanceof Address) { + return input; } + throw new Error(`Not an address: ${input}`); +} - if (inputAsBuffer == null || inputAsBuffer.byteLength !== length) { - throw Error( - `${inputName} must be a ${length} byte Uint8Array or Buffer or base64 string.` +function optionalAddress(input: unknown): Address | undefined { + if (input == null) { + return undefined; + } + let addr: Address; + if (input instanceof Address) { + addr = input; + } else if (typeof input === 'string') { + addr = Address.fromString(input); + } else { + throw new Error(`Not an address: ${input}`); + } + if (uint8ArrayIsEmpty(addr.publicKey)) { + // If it's the zero address, throw an error so that the user won't be surprised that this gets dropped + throw new Error( + 'Invalid use of the zero address. To omit this value, pass in undefined' ); } + return addr; +} + +function optionalUint8Array(input: unknown): Uint8Array | undefined { + if (typeof input === 'undefined') { + return undefined; + } + if (input instanceof Uint8Array) { + return input; + } + throw new Error(`Not a Uint8Array: ${input}`); +} + +function ensureUint8Array(input: unknown): Uint8Array { + if (input instanceof Uint8Array) { + return input; + } + throw new Error(`Not a Uint8Array: ${input}`); +} + +function optionalUint64(input: unknown): bigint | undefined { + if (typeof input === 'undefined') { + return undefined; + } + return utils.ensureUint64(input); +} + +function ensureBoolean(input: unknown): boolean { + if (input === true || input === false) { + return input; + } + throw new Error(`Not a boolean: ${input}`); +} - return inputAsBuffer; +function ensureArray(input: unknown): unknown[] { + if (Array.isArray(input)) { + return input.slice(); + } + throw new Error(`Not an array: ${input}`); +} + +function optionalFixedLengthByteArray( + input: unknown, + length: number, + name: string +): Uint8Array | undefined { + const bytes = optionalUint8Array(input); + if (typeof bytes === 'undefined') { + return undefined; + } + if (bytes.byteLength !== length) { + throw new Error( + `${name} must be ${length} bytes long, was ${bytes.byteLength}` + ); + } + if (uint8ArrayIsEmpty(bytes)) { + // if contains all 0s, omit it + return undefined; + } + return bytes; +} + +export interface TransactionBoxReference { + readonly appIndex: bigint; + readonly name: Uint8Array; +} + +function ensureBoxReference(input: unknown): TransactionBoxReference { + if (input != null && typeof input === 'object') { + const { appIndex, name } = input as BoxReference; + return { + appIndex: utils.ensureUint64(appIndex), + name: ensureUint8Array(name), + }; + } + throw new Error(`Not a box reference: ${input}`); +} + +const TX_TAG = new TextEncoder().encode('TX'); + +export interface PaymentTransactionFields { + readonly receiver: Address; + readonly amount: bigint; + readonly closeRemainderTo?: Address; +} + +export interface KeyRegistrationTransactionFields { + readonly voteKey?: Uint8Array; + readonly selectionKey?: Uint8Array; + readonly stateProofKey?: Uint8Array; + readonly voteFirst?: bigint; + readonly voteLast?: bigint; + readonly voteKeyDilution?: bigint; + readonly nonParticipation: boolean; +} + +export interface AssetConfigTransactionFields { + readonly assetIndex: bigint; + readonly total: bigint; + readonly decimals: number; + readonly defaultFrozen: boolean; + readonly manager?: Address; + readonly reserve?: Address; + readonly freeze?: Address; + readonly clawback?: Address; + readonly unitName?: string; + readonly assetName?: string; + readonly assetURL?: string; + readonly assetMetadataHash?: Uint8Array; +} + +export interface AssetTransferTransactionFields { + readonly assetIndex: bigint; + readonly amount: bigint; + readonly assetSender?: Address; + readonly receiver: Address; + readonly closeRemainderTo?: Address; +} + +export interface AssetFreezeTransactionFields { + readonly assetIndex: bigint; + readonly freezeAccount: Address; + readonly frozen: boolean; +} + +export interface ApplicationTransactionFields { + readonly appIndex: bigint; + readonly onComplete: OnApplicationComplete; + readonly numLocalInts: number; + readonly numLocalByteSlices: number; + readonly numGlobalInts: number; + readonly numGlobalByteSlices: number; + readonly extraPages: number; + readonly approvalProgram: Uint8Array; + readonly clearProgram: Uint8Array; + readonly appArgs: ReadonlyArray; + readonly accounts: ReadonlyArray
; + readonly foreignApps: ReadonlyArray; + readonly foreignAssets: ReadonlyArray; + readonly boxes: ReadonlyArray; +} + +export interface StateProofTransactionFields { + readonly stateProofType: number; + readonly stateProof?: StateProof; + readonly message?: StateProofMessage; } /** * 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; +export class Transaction implements encoding.Encodable { + static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + // Common + { key: 'type', valueSchema: new StringSchema() }, + { key: 'snd', valueSchema: new AddressSchema() }, + { key: 'lv', valueSchema: new Uint64Schema() }, + { key: 'gen', valueSchema: new OptionalSchema(new StringSchema()) }, + { + key: 'gh', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(32)), + }, + { key: 'fee', valueSchema: new Uint64Schema() }, + { key: 'fv', valueSchema: new Uint64Schema() }, + { key: 'note', valueSchema: new ByteArraySchema() }, + { + key: 'lx', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(32)), + }, + { key: 'rekey', valueSchema: new OptionalSchema(new AddressSchema()) }, + { + key: 'grp', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(32)), + }, + // We mark all top-level type-specific fields optional because they will not be present when + // the transaction is not that type. + // Payment + { key: 'amt', valueSchema: new OptionalSchema(new Uint64Schema()) }, + { key: 'rcv', valueSchema: new OptionalSchema(new AddressSchema()) }, + { key: 'close', valueSchema: new OptionalSchema(new AddressSchema()) }, + // Keyreg + { + key: 'votekey', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(32)), + }, + { + key: 'selkey', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(32)), + }, + { + key: 'sprfkey', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(64)), + }, + { key: 'votefst', valueSchema: new OptionalSchema(new Uint64Schema()) }, + { key: 'votelst', valueSchema: new OptionalSchema(new Uint64Schema()) }, + { key: 'votekd', valueSchema: new OptionalSchema(new Uint64Schema()) }, + { key: 'nonpart', valueSchema: new OptionalSchema(new BooleanSchema()) }, + // AssetConfig + { key: 'caid', valueSchema: new OptionalSchema(new Uint64Schema()) }, + { + key: 'apar', + valueSchema: new OptionalSchema( + new NamedMapSchema( + allOmitEmpty([ + { key: 't', valueSchema: new Uint64Schema() }, + { key: 'dc', valueSchema: new Uint64Schema() }, + { key: 'df', valueSchema: new BooleanSchema() }, + { + key: 'm', + valueSchema: new OptionalSchema(new AddressSchema()), + }, + { + key: 'r', + valueSchema: new OptionalSchema(new AddressSchema()), + }, + { + key: 'f', + valueSchema: new OptionalSchema(new AddressSchema()), + }, + { + key: 'c', + valueSchema: new OptionalSchema(new AddressSchema()), + }, + { + key: 'un', + valueSchema: new OptionalSchema(new StringSchema()), + }, + { + key: 'an', + valueSchema: new OptionalSchema(new StringSchema()), + }, + { + key: 'au', + valueSchema: new OptionalSchema(new StringSchema()), + }, + { + key: 'am', + valueSchema: new OptionalSchema( + new FixedLengthByteArraySchema(32) + ), + }, + ]) + ) + ), + }, + // AssetTransfer + { key: 'xaid', valueSchema: new OptionalSchema(new Uint64Schema()) }, + { key: 'aamt', valueSchema: new OptionalSchema(new Uint64Schema()) }, + { key: 'arcv', valueSchema: new OptionalSchema(new AddressSchema()) }, + { key: 'aclose', valueSchema: new OptionalSchema(new AddressSchema()) }, + { key: 'asnd', valueSchema: new OptionalSchema(new AddressSchema()) }, + // AssetFreeze + { key: 'faid', valueSchema: new OptionalSchema(new Uint64Schema()) }, + { key: 'afrz', valueSchema: new OptionalSchema(new BooleanSchema()) }, + { key: 'fadd', valueSchema: new OptionalSchema(new AddressSchema()) }, + // Application + { key: 'apid', valueSchema: new OptionalSchema(new Uint64Schema()) }, + { key: 'apan', valueSchema: new OptionalSchema(new Uint64Schema()) }, + { + key: 'apaa', + valueSchema: new OptionalSchema(new ArraySchema(new ByteArraySchema())), + }, + { + key: 'apat', + valueSchema: new OptionalSchema(new ArraySchema(new AddressSchema())), + }, + { + key: 'apas', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + }, + { + key: 'apfa', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + }, + { + key: 'apbx', + valueSchema: new OptionalSchema( + new ArraySchema( + new NamedMapSchema( + allOmitEmpty([ + { + key: 'i', + valueSchema: new Uint64Schema(), + }, + { + key: 'n', + valueSchema: new ByteArraySchema(), + }, + ]) + ) + ) + ), + }, + { key: 'apap', valueSchema: new OptionalSchema(new ByteArraySchema()) }, + { key: 'apsu', valueSchema: new OptionalSchema(new ByteArraySchema()) }, + { + key: 'apls', + valueSchema: new OptionalSchema( + new NamedMapSchema( + allOmitEmpty([ + { + key: 'nui', + valueSchema: new Uint64Schema(), + }, + { + key: 'nbs', + valueSchema: new Uint64Schema(), + }, + ]) + ) + ), + }, + { + key: 'apgs', + valueSchema: new OptionalSchema( + new NamedMapSchema( + allOmitEmpty([ + { + key: 'nui', + valueSchema: new Uint64Schema(), + }, + { + key: 'nbs', + valueSchema: new Uint64Schema(), + }, + ]) + ) + ), + }, + { key: 'apep', valueSchema: new OptionalSchema(new Uint64Schema()) }, + // StateProof + { key: 'sptype', valueSchema: new OptionalSchema(new Uint64Schema()) }, + { key: 'sp', valueSchema: new OptionalSchema(StateProof.encodingSchema) }, + { + key: 'spmsg', + valueSchema: new OptionalSchema(StateProofMessage.encodingSchema), + }, + ]) + ); + + /** common */ + public readonly type: TransactionType; + public readonly sender: Address; + public readonly note: Uint8Array; + public readonly lease?: Uint8Array; + public readonly rekeyTo?: Address; + + /** group */ + public group?: Uint8Array; + + /** suggested params */ + public fee: bigint; + public readonly firstValid: bigint; + public readonly lastValid: bigint; + public readonly genesisID?: string; + public readonly genesisHash?: Uint8Array; + + /** type-specific fields */ + public readonly payment?: PaymentTransactionFields; + public readonly keyreg?: KeyRegistrationTransactionFields; + public readonly assetConfig?: AssetConfigTransactionFields; + public readonly assetTransfer?: AssetTransferTransactionFields; + public readonly assetFreeze?: AssetFreezeTransactionFields; + public readonly applicationCall?: ApplicationTransactionFields; + public readonly stateProof?: StateProofTransactionFields; + + constructor(params: TransactionParams) { + if (!isTransactionType(params.type)) { + throw new Error(`Invalid transaction type: ${params.type}`); } - /* 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; + + // Common fields + this.type = params.type; // verified above + this.sender = ensureAddress(params.sender); + this.note = ensureUint8Array(params.note ?? new Uint8Array()); + this.lease = optionalFixedLengthByteArray( + params.lease, + ALGORAND_TRANSACTION_LEASE_LENGTH, + 'lease' + ); + this.rekeyTo = optionalAddress(params.rekeyTo); + + // Group + this.group = undefined; + + // Suggested params fields + this.firstValid = utils.ensureUint64(params.suggestedParams.firstValid); + this.lastValid = utils.ensureUint64(params.suggestedParams.lastValid); + if (params.suggestedParams.genesisID) { + if (typeof params.suggestedParams.genesisID !== 'string') { + throw new Error('Genesis ID must be a string if present'); + } + this.genesisID = params.suggestedParams.genesisID; } + this.genesisHash = optionalUint8Array(params.suggestedParams.genesisHash); + // Fee is handled at the end - // 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; + const fieldsPresent: TransactionType[] = []; + if (params.paymentParams) fieldsPresent.push(TransactionType.pay); + if (params.keyregParams) fieldsPresent.push(TransactionType.keyreg); + if (params.assetConfigParams) fieldsPresent.push(TransactionType.acfg); + if (params.assetTransferParams) fieldsPresent.push(TransactionType.axfer); + if (params.assetFreezeParams) fieldsPresent.push(TransactionType.afrz); + if (params.appCallParams) fieldsPresent.push(TransactionType.appl); + if (params.stateProofParams) fieldsPresent.push(TransactionType.stpf); - 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 (fieldsPresent.length !== 1) { + throw new Error( + `Transaction has wrong number of type fields present (${fieldsPresent.length}): ${fieldsPresent}` ); - 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 (this.type !== fieldsPresent[0]) { + throw new Error( + `Transaction has type ${this.type} but fields present for ${fieldsPresent[0]}` ); } - 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 (params.paymentParams) { + this.payment = { + receiver: ensureAddress(params.paymentParams.receiver), + amount: utils.ensureUint64(params.paymentParams.amount), + closeRemainderTo: optionalAddress( + params.paymentParams.closeRemainderTo + ), + }; } - if ( - txn.assetMetadataHash !== undefined && - txn.assetMetadataHash.length !== 0 - ) { - if (typeof txn.assetMetadataHash === 'string') { - txn.assetMetadataHash = new Uint8Array( - Buffer.from(txn.assetMetadataHash) + + if (params.keyregParams) { + this.keyreg = { + voteKey: getKeyregKey( + params.keyregParams.voteKey, + 'voteKey', + KEYREG_VOTE_KEY_LENGTH + )!, + selectionKey: getKeyregKey( + params.keyregParams.selectionKey, + 'selectionKey', + KEYREG_SELECTION_KEY_LENGTH + )!, + stateProofKey: getKeyregKey( + params.keyregParams.stateProofKey, + 'stateProofKey', + KEYREG_STATE_PROOF_KEY_LENGTH + )!, + voteFirst: optionalUint64(params.keyregParams.voteFirst), + voteLast: optionalUint64(params.keyregParams.voteLast), + voteKeyDilution: optionalUint64(params.keyregParams.voteKeyDilution), + nonParticipation: ensureBoolean( + params.keyregParams.nonParticipation ?? false + ), + }; + // Checking non-participation key registration + if ( + this.keyreg.nonParticipation && + (this.keyreg.voteKey || + this.keyreg.selectionKey || + this.keyreg.stateProofKey || + typeof this.keyreg.voteFirst !== 'undefined' || + typeof this.keyreg.voteLast !== 'undefined' || + typeof this.keyreg.voteKeyDilution !== 'undefined') + ) { + throw new Error( + 'nonParticipation is true but participation params are present.' ); } - + // Checking online key registration if ( - txn.assetMetadataHash.constructor !== Uint8Array || - txn.assetMetadataHash.byteLength !== ASSET_METADATA_HASH_LENGTH + // If we are participating + !this.keyreg.nonParticipation && + // And *ANY* participating fields are present + (this.keyreg.voteKey || + this.keyreg.selectionKey || + this.keyreg.stateProofKey || + typeof this.keyreg.voteFirst !== 'undefined' || + typeof this.keyreg.voteLast !== 'undefined' || + typeof this.keyreg.voteKeyDilution !== 'undefined') && + // Then *ALL* participating fields must be present (with an exception for stateProofKey, + // which was introduced later so for backwards compatibility we don't require it) + !( + this.keyreg.voteKey && + this.keyreg.selectionKey && + typeof this.keyreg.voteFirst !== 'undefined' && + typeof this.keyreg.voteLast !== 'undefined' && + typeof this.keyreg.voteKeyDilution !== 'undefined' + ) ) { - throw Error( - `assetMetadataHash must be a ${ASSET_METADATA_HASH_LENGTH} byte Uint8Array or string.` + 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, stateProofKey, voteFirst, voteLast, voteKeyDilution + // are all undefined + } - if (txn.assetMetadataHash.every((value) => value === 0)) { - // if hash contains all 0s, omit it - txn.assetMetadataHash = undefined; - } - } else { - txn.assetMetadataHash = undefined; + if (params.assetConfigParams) { + this.assetConfig = { + assetIndex: utils.ensureUint64( + params.assetConfigParams.assetIndex ?? 0 + ), + total: utils.ensureUint64(params.assetConfigParams.total ?? 0), + decimals: utils.ensureSafeUnsignedInteger( + params.assetConfigParams.decimals ?? 0 + ), + defaultFrozen: ensureBoolean( + params.assetConfigParams.defaultFrozen ?? false + ), + manager: optionalAddress(params.assetConfigParams.manager), + reserve: optionalAddress(params.assetConfigParams.reserve), + freeze: optionalAddress(params.assetConfigParams.freeze), + clawback: optionalAddress(params.assetConfigParams.clawback), + unitName: params.assetConfigParams.unitName, + assetName: params.assetConfigParams.assetName, + assetURL: params.assetConfigParams.assetURL, + assetMetadataHash: optionalFixedLengthByteArray( + params.assetConfigParams.assetMetadataHash, + ASSET_METADATA_HASH_LENGTH, + 'assetMetadataHash' + ), + }; } - if (txn.note !== undefined) { - if (txn.note.constructor !== Uint8Array) - throw Error('note must be a Uint8Array.'); - } else { - txn.note = new Uint8Array(0); + + if (params.assetTransferParams) { + this.assetTransfer = { + assetIndex: utils.ensureUint64(params.assetTransferParams.assetIndex), + amount: utils.ensureUint64(params.assetTransferParams.amount), + assetSender: optionalAddress(params.assetTransferParams.assetSender), + receiver: ensureAddress(params.assetTransferParams.receiver), + closeRemainderTo: optionalAddress( + params.assetTransferParams.closeRemainderTo + ), + }; } - 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); + + if (params.assetFreezeParams) { + this.assetFreeze = { + assetIndex: utils.ensureUint64(params.assetFreezeParams.assetIndex), + freezeAccount: ensureAddress(params.assetFreezeParams.freezeTarget), + frozen: ensureBoolean(params.assetFreezeParams.frozen), + }; } - 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.' - ); + + if (params.appCallParams) { + const { onComplete } = params.appCallParams; + if (!isOnApplicationComplete(onComplete)) { + throw new Error(`Invalid onCompletion value: ${onComplete}`); + } + this.applicationCall = { + appIndex: utils.ensureUint64(params.appCallParams.appIndex), + onComplete, + numLocalInts: utils.ensureSafeUnsignedInteger( + params.appCallParams.numLocalInts ?? 0 + ), + numLocalByteSlices: utils.ensureSafeUnsignedInteger( + params.appCallParams.numLocalByteSlices ?? 0 + ), + numGlobalInts: utils.ensureSafeUnsignedInteger( + params.appCallParams.numGlobalInts ?? 0 + ), + numGlobalByteSlices: utils.ensureSafeUnsignedInteger( + params.appCallParams.numGlobalByteSlices ?? 0 + ), + extraPages: utils.ensureSafeUnsignedInteger( + params.appCallParams.extraPages ?? 0 + ), + approvalProgram: ensureUint8Array( + params.appCallParams.approvalProgram ?? new Uint8Array() + ), + clearProgram: ensureUint8Array( + params.appCallParams.clearProgram ?? new Uint8Array() + ), + appArgs: ensureArray(params.appCallParams.appArgs ?? []).map( + ensureUint8Array + ), + accounts: ensureArray(params.appCallParams.accounts ?? []).map( + ensureAddress + ), + foreignApps: ensureArray(params.appCallParams.foreignApps ?? []).map( + utils.ensureUint64 + ), + foreignAssets: ensureArray( + params.appCallParams.foreignAssets ?? [] + ).map(utils.ensureUint64), + boxes: ensureArray(params.appCallParams.boxes ?? []).map( + ensureBoxReference + ), + }; } - // 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' - ); + + if (params.stateProofParams) { + this.stateProof = { + stateProofType: utils.ensureSafeUnsignedInteger( + params.stateProofParams.stateProofType ?? 0 + ), + stateProof: params.stateProofParams.stateProof, + message: params.stateProofParams.message, + }; } - // 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)); + // Determine fee + this.fee = utils.ensureUint64(params.suggestedParams.fee); - // Modify Fee - if (!txn.flatFee) { - this.fee *= this.estimateSize(); + const feeDependsOnSize = !ensureBoolean( + params.suggestedParams.flatFee ?? false + ); + if (feeDependsOnSize) { + const minFee = utils.ensureUint64(params.suggestedParams.minFee); + this.fee *= BigInt(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; + if (this.fee < minFee) { + this.fee = minFee; } } + } - // say we are aware of groups - this.group = undefined; + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): encoding.Schema { + return Transaction.encodingSchema; + } + + toEncodingData(): Map { + const data = new Map([ + ['type', this.type], + ['fv', this.firstValid], + ['lv', this.lastValid], + ['snd', this.sender], + ['gen', this.genesisID], + ['gh', this.genesisHash], + ['fee', this.fee], + ['note', this.note], + ['lx', this.lease], + ['rekey', this.rekeyTo], + ['grp', this.group], + ]); + + if (this.payment) { + data.set('amt', this.payment.amount); + data.set('rcv', this.payment.receiver); + data.set('close', this.payment.closeRemainderTo); + return data; + } + + if (this.keyreg) { + data.set('votekey', this.keyreg.voteKey); + data.set('selkey', this.keyreg.selectionKey); + data.set('sprfkey', this.keyreg.stateProofKey); + data.set('votefst', this.keyreg.voteFirst); + data.set('votelst', this.keyreg.voteLast); + data.set('votekd', this.keyreg.voteKeyDilution); + data.set('nonpart', this.keyreg.nonParticipation); + return data; + } + + if (this.assetConfig) { + data.set('caid', this.assetConfig.assetIndex); + const assetParams = new Map([ + ['t', this.assetConfig.total], + ['dc', this.assetConfig.decimals], + ['df', this.assetConfig.defaultFrozen], + ['m', this.assetConfig.manager], + ['r', this.assetConfig.reserve], + ['f', this.assetConfig.freeze], + ['c', this.assetConfig.clawback], + ['un', this.assetConfig.unitName], + ['an', this.assetConfig.assetName], + ['au', this.assetConfig.assetURL], + ['am', this.assetConfig.assetMetadataHash], + ]); + data.set('apar', assetParams); + return data; + } - // 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 (this.assetTransfer) { + data.set('xaid', this.assetTransfer.assetIndex); + data.set('aamt', this.assetTransfer.amount); + data.set('arcv', this.assetTransfer.receiver); + data.set('aclose', this.assetTransfer.closeRemainderTo); + data.set('asnd', this.assetTransfer.assetSender); + return data; + } + + if (this.assetFreeze) { + data.set('faid', this.assetFreeze.assetIndex); + data.set('afrz', this.assetFreeze.frozen); + data.set('fadd', this.assetFreeze.freezeAccount); + return data; + } + + if (this.applicationCall) { + data.set('apid', this.applicationCall.appIndex); + data.set('apan', this.applicationCall.onComplete); + data.set('apaa', this.applicationCall.appArgs); + data.set('apat', this.applicationCall.accounts); + data.set('apas', this.applicationCall.foreignAssets); + data.set('apfa', this.applicationCall.foreignApps); + data.set( + 'apbx', + boxReferencesToEncodingData( + this.applicationCall.boxes, + this.applicationCall.foreignApps, + this.applicationCall.appIndex + ) ); - if (txn.stateProofMessage !== undefined) { - if (txn.stateProofMessage.constructor !== Uint8Array) - throw Error('stateProofMessage must be a Uint8Array.'); - } else { - txn.stateProofMessage = new Uint8Array(0); + data.set('apap', this.applicationCall.approvalProgram); + data.set('apsu', this.applicationCall.clearProgram); + data.set( + 'apls', + new Map([ + ['nui', this.applicationCall.numLocalInts], + ['nbs', this.applicationCall.numLocalByteSlices], + ]) + ); + data.set( + 'apgs', + new Map([ + ['nui', this.applicationCall.numGlobalInts], + ['nbs', this.applicationCall.numGlobalByteSlices], + ]) + ); + data.set('apep', this.applicationCall.extraPages); + return data; } - if (txn.stateProof !== undefined) { - if (txn.stateProof.constructor !== Uint8Array) - throw Error('stateProof must be a Uint8Array.'); - } else { - txn.stateProof = new Uint8Array(0); + + if (this.stateProof) { + data.set('sptype', this.stateProof.stateProofType); + data.set( + 'sp', + this.stateProof.stateProof + ? this.stateProof.stateProof.toEncodingData() + : undefined + ); + data.set( + 'spmsg', + this.stateProof.message + ? this.stateProof.message.toEncodingData() + : undefined + ); + return data; } - } - // 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, - }; + throw new Error(`Unexpected transaction type: ${this.type}`); + } - // 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; + static fromEncodingData(data: unknown): Transaction { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded logic sig account: ${data}`); } - 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; + const suggestedParams: SuggestedParams = { + minFee: BigInt(0), + flatFee: true, + fee: data.get('fee') ?? 0, + firstValid: data.get('fv') ?? 0, + lastValid: data.get('lv') ?? 0, + genesisHash: data.get('gh'), + genesisID: data.get('gen'), + }; + + const txnType = data.get('type'); + if (!isTransactionType(txnType)) { + throw new Error(`Unrecognized transaction type: ${txnType}`); } - 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; + const params: TransactionParams = { + type: txnType, + sender: data.get('snd') ?? Address.zeroAddress(), + note: data.get('note'), + lease: data.get('lx'), + suggestedParams, + }; - return txn; + if (data.get('rekey')) { + params.rekeyTo = data.get('rekey'); } - 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 (params.type === TransactionType.pay) { + const paymentParams: PaymentTransactionParams = { + amount: data.get('amt') ?? 0, + receiver: data.get('rcv') ?? Address.zeroAddress(), }; - 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); + if (data.get('close')) { + paymentParams.closeRemainderTo = data.get('close'); } - 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, + params.paymentParams = paymentParams; + } else if (params.type === TransactionType.keyreg) { + const keyregParams: KeyRegistrationTransactionParams = { + voteKey: data.get('votekey'), + selectionKey: data.get('selkey'), + stateProofKey: data.get('sprfkey'), + voteFirst: data.get('votefst'), + voteLast: data.get('votelst'), + voteKeyDilution: data.get('votekd'), + nonParticipation: data.get('nonpart'), }; - 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 - ), + params.keyregParams = keyregParams; + } else if (params.type === TransactionType.acfg) { + const assetConfigParams: AssetConfigurationTransactionParams = { + assetIndex: data.get('caid'), }; - if (this.reKeyTo !== undefined) { - txn.rekey = Buffer.from(this.reKeyTo.publicKey); + if (data.get('apar')) { + const assetParams = data.get('apar') as Map; + assetConfigParams.total = assetParams.get('t'); + assetConfigParams.decimals = assetParams.get('dc'); + assetConfigParams.defaultFrozen = assetParams.get('df'); + assetConfigParams.unitName = assetParams.get('un'); + assetConfigParams.assetName = assetParams.get('an'); + assetConfigParams.assetURL = assetParams.get('au'); + assetConfigParams.assetMetadataHash = assetParams.get('am'); + if (assetParams.get('m')) { + assetConfigParams.manager = assetParams.get('m'); + } + if (assetParams.get('r')) { + assetConfigParams.reserve = assetParams.get('r'); + } + if (assetParams.get('f')) { + assetConfigParams.freeze = assetParams.get('f'); + } + if (assetParams.get('c')) { + assetConfigParams.clawback = assetParams.get('c'); + } } - if (this.appApprovalProgram !== undefined) { - txn.apap = Buffer.from(this.appApprovalProgram); + params.assetConfigParams = assetConfigParams; + } else if (params.type === TransactionType.axfer) { + const assetTransferParams: AssetTransferTransactionParams = { + assetIndex: data.get('xaid') ?? 0, + amount: data.get('aamt') ?? 0, + receiver: data.get('arcv') ?? Address.zeroAddress(), + }; + if (data.get('aclose')) { + assetTransferParams.closeRemainderTo = data.get('aclose'); } - if (this.appClearProgram !== undefined) { - txn.apsu = Buffer.from(this.appClearProgram); + if (data.get('asnd')) { + assetTransferParams.assetSender = data.get('asnd'); } - if (this.appArgs !== undefined) { - txn.apaa = this.appArgs.map((arg) => Buffer.from(arg)); + params.assetTransferParams = assetTransferParams; + } else if (params.type === TransactionType.afrz) { + const assetFreezeParams: AssetFreezeTransactionParams = { + assetIndex: data.get('faid') ?? 0, + freezeTarget: data.get('fadd') ?? Address.zeroAddress(), + frozen: data.get('afrz') ?? false, + }; + params.assetFreezeParams = assetFreezeParams; + } else if (params.type === TransactionType.appl) { + const appCallParams: ApplicationCallTransactionParams = { + appIndex: data.get('apid') ?? 0, + onComplete: utils.ensureSafeUnsignedInteger(data.get('apan') ?? 0), + appArgs: data.get('apaa'), + accounts: data.get('apat'), + foreignAssets: data.get('apas'), + foreignApps: data.get('apfa'), + approvalProgram: data.get('apap'), + clearProgram: data.get('apsu'), + extraPages: data.get('apep'), + }; + const localSchema = data.get('apls') as Map | undefined; + if (localSchema) { + appCallParams.numLocalInts = localSchema.get('nui'); + appCallParams.numLocalByteSlices = localSchema.get('nbs'); } - if (this.appAccounts !== undefined) { - txn.apat = this.appAccounts.map((decodedAddress) => - Buffer.from(decodedAddress.publicKey) - ); + const globalSchema = data.get('apgs') as Map | undefined; + if (globalSchema) { + appCallParams.numGlobalInts = globalSchema.get('nui'); + appCallParams.numGlobalByteSlices = globalSchema.get('nbs'); } - // 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; + const boxes = data.get('apbx') as Array> | undefined; + if (boxes) { + appCallParams.boxes = boxes.map((box) => { + const index = utils.ensureSafeUnsignedInteger(box.get('i') ?? 0); + const name = ensureUint8Array(box.get('n') ?? new Uint8Array()); + if (index === 0) { + // 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. + return { + appIndex: 0, + name, + }; + } + if ( + !appCallParams.foreignApps || + index > appCallParams.foreignApps.length + ) { + throw new Error( + `Cannot find foreign app index ${index} in ${appCallParams.foreignApps}` + ); + } + return { + appIndex: appCallParams.foreignApps[index - 1], + name, + }; + }); } - 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), + params.appCallParams = appCallParams; + } else if (params.type === TransactionType.stpf) { + const stateProofParams: StateProofTransactionParams = { + stateProofType: data.get('sptype'), + stateProof: data.get('sp') + ? StateProof.fromEncodingData(data.get('sp')) + : undefined, + message: data.get('spmsg') + ? StateProofMessage.fromEncodingData(data.get('spmsg')) + : undefined, }; - // 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; + params.stateProofParams = stateProofParams; + } else { + const exhaustiveCheck: never = params.type; + throw new Error(`Unexpected transaction type: ${exhaustiveCheck}`); } - 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'); + const txn = new Transaction(params); - 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; + if (data.get('grp')) { + const group = ensureUint8Array(data.get('grp')); + if (group.byteLength !== ALGORAND_TRANSACTION_GROUP_LENGTH) { + throw new Error(`Invalid group length: ${group.byteLength}`); } + txn.group = group; } + return txn; } - estimateSize() { + private estimateSize() { return this.toByte().length + NUM_ADDL_BYTES_AFTER_SIGNING; } bytesToSign() { const encodedMsg = this.toByte(); - return Buffer.from(utils.concatArrays(this.tag, encodedMsg)); + return utils.concatArrays(TX_TAG, encodedMsg); } toByte() { - return encoding.encode(this.get_obj_for_encoding()); + return encoding.encodeMsgpack(this); } // returns the raw signature - rawSignTxn(sk: Uint8Array) { + rawSignTxn(sk: Uint8Array): Uint8Array { const toBeSigned = this.bytesToSign(); const sig = nacl.sign(toBeSigned, sk); - return Buffer.from(sig); + return 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 + signTxn(sk: Uint8Array): Uint8Array { + // TODO: deprecate in favor of SignedTransaction class 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)); + const signerAddr = new Address(keypair.publicKey); + const sig = this.rawSignTxn(sk); + return this.attachSignature(signerAddr, sig); } - attachSignature(signerAddr: string, signature: Uint8Array) { + attachSignature( + signerAddr: string | Address, + signature: Uint8Array + ): Uint8Array { + // TODO: deprecate in favor of SignedTransaction class if (!nacl.isValidSignatureLength(signature.length)) { throw new Error('Invalid signature length'); } - const sTxn: EncodedSignedTransaction = { - sig: Buffer.from(signature), - txn: this.get_obj_for_encoding(), - }; + const sTxn = new Map([ + ['sig', signature], + ['txn', this.toEncodingData()], + ]); + const signerAddrObj = ensureAddress(signerAddr); // 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); + if (!this.sender.equals(signerAddrObj)) { + sTxn.set('sgnr', signerAddrObj); } - return new Uint8Array(encoding.encode(sTxn)); + + // This is a hack to avoid a circular reference with the SignedTransaction class + const stxnSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'txn', + valueSchema: Transaction.encodingSchema, + }, + { + key: 'sig', + valueSchema: new FixedLengthByteArraySchema(64), + }, + { + key: 'sgnr', + valueSchema: new OptionalSchema(new AddressSchema()), + }, + ]) + ); + + return encoding.msgpackRawEncode(stxnSchema.prepareMsgpack(sTxn)); } - rawTxID() { + rawTxID(): Uint8Array { const enMsg = this.toByte(); - const gh = Buffer.from(utils.concatArrays(this.tag, enMsg)); - return Buffer.from(nacl.genericHash(gh)); + const gh = utils.concatArrays(TX_TAG, enMsg); + return Uint8Array.from(nacl.genericHash(gh)); } - txID() { + txID(): string { 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); } /** @@ -1300,82 +1108,18 @@ export function encodeUnsignedSimulateTransaction( * 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); +export function encodeUnsignedTransaction( + transactionObject: Transaction +): Uint8Array { + return encoding.encodeMsgpack(transactionObject); } /** - * decodeUnsignedTransaction takes a Buffer (as if from encodeUnsignedTransaction) and converts it to a txnBuilder.Transaction object + * decodeUnsignedTransaction takes a Uint8Array (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; +): Transaction { + return encoding.decodeMsgpack(transactionBuffer, Transaction); } - -/** - * 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 index d8bd7063c..209f3968f 100644 --- a/src/types/account.ts +++ b/src/types/account.ts @@ -1,3 +1,5 @@ +import { Address } from '../encoding/address.js'; + /** * An Algorand account object. * @@ -7,7 +9,7 @@ export default interface Account { /** * Algorand address */ - addr: string; + addr: Address; /** * Secret key belonging to the Algorand address diff --git a/src/types/address.ts b/src/types/address.ts deleted file mode 100644 index 364e0307a..000000000 --- a/src/types/address.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Decoded Algorand address. Includes public key and checksum. - */ -export interface Address { - publicKey: Uint8Array; - checksum: Uint8Array; -} diff --git a/src/types/block.ts b/src/types/block.ts new file mode 100644 index 000000000..5cc9edc37 --- /dev/null +++ b/src/types/block.ts @@ -0,0 +1,1278 @@ +import { Encodable, Schema } from '../encoding/encoding.js'; +import { + NamedMapSchema, + Uint64MapSchema, + SpecialCaseBinaryStringMapSchema, + SpecialCaseBinaryStringSchema, + ArraySchema, + StringSchema, + BooleanSchema, + Uint64Schema, + AddressSchema, + ByteArraySchema, + FixedLengthByteArraySchema, + OptionalSchema, + allOmitEmpty, + combineMaps, + convertMap, + BlockHashSchema, +} from '../encoding/schema/index.js'; +import { Address } from '../encoding/address.js'; +import { SignedTransaction } from '../signedTransaction.js'; + +/** + * StateProofTrackingData tracks the status of state proofs. + */ +export class StateProofTrackingData implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'v', // stateProofVotersCommitment + valueSchema: new ByteArraySchema(), + }, + { + key: 't', // stateProofOnlineTotalWeight + valueSchema: new Uint64Schema(), + }, + { + key: 'n', // stateProofNextRound + valueSchema: new Uint64Schema(), + }, + ]) + ); + + /** + * StateProofVotersCommitment is the root of a vector commitment containing the online accounts + * that will help sign a state proof. The VC root, and the state proof, happen on blocks that are + * a multiple of ConsensusParams.StateProofRounds. For blocks that are not a multiple of + * ConsensusParams.StateProofRounds, this value is zero. + */ + public stateProofVotersCommitment: Uint8Array; + + /** + * StateProofOnlineTotalWeight is the total number of microalgos held by the online accounts during + * the StateProof round (or zero, if the merkle root is zero - no commitment for StateProof voters). + * This is intended for computing the threshold of votes to expect from StateProofVotersCommitment. + */ + public stateProofOnlineTotalWeight: bigint; + + /** + * StateProofNextRound is the next round for which we will accept a StateProof transaction. + */ + public stateProofNextRound: bigint; + + public constructor(params: { + stateProofVotersCommitment: Uint8Array; + stateProofOnlineTotalWeight: bigint; + stateProofNextRound: bigint; + }) { + this.stateProofVotersCommitment = params.stateProofVotersCommitment; + this.stateProofOnlineTotalWeight = params.stateProofOnlineTotalWeight; + this.stateProofNextRound = params.stateProofNextRound; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return StateProofTrackingData.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['v', this.stateProofVotersCommitment], + ['t', this.stateProofOnlineTotalWeight], + ['n', this.stateProofNextRound], + ]); + } + + public static fromEncodingData(data: unknown): StateProofTrackingData { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofTrackingData: ${data}`); + } + return new StateProofTrackingData({ + stateProofVotersCommitment: data.get('v'), + stateProofOnlineTotalWeight: data.get('t'), + stateProofNextRound: data.get('n'), + }); + } +} + +/** + * TxnCommitments represents the commitments computed from the transactions in the block. + * It contains multiple commitments based on different algorithms and hash functions, to support + * different use cases. + */ +export class TxnCommitments implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'txn', // nativeSha512_256Commitment + valueSchema: new FixedLengthByteArraySchema(32), + }, + { + key: 'txn256', // sha256Commitment + valueSchema: new FixedLengthByteArraySchema(32), + }, + ]) + ); + + /** + * Root of transaction merkle tree using SHA512_256 hash function. This commitment is computed + * based on the PaysetCommit type specified in the block's consensus protocol. + */ + public nativeSha512_256Commitment: Uint8Array; + + /** + * Root of transaction vector commitment merkle tree using SHA256 hash function + */ + public sha256Commitment: Uint8Array; + + constructor(params: { + nativeSha512_256Commitment: Uint8Array; + sha256Commitment: Uint8Array; + }) { + this.nativeSha512_256Commitment = params.nativeSha512_256Commitment; + this.sha256Commitment = params.sha256Commitment; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return TxnCommitments.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['txn', this.nativeSha512_256Commitment], + ['txn256', this.sha256Commitment], + ]); + } + + public static fromEncodingData(data: unknown): TxnCommitments { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TxnCommitments: ${data}`); + } + return new TxnCommitments({ + nativeSha512_256Commitment: data.get('txn'), + sha256Commitment: data.get('txn256'), + }); + } +} + +/** + * RewardsState represents the global parameters controlling the rate at which accounts accrue rewards. + */ +export class RewardState implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'fees', // feeSink + valueSchema: new AddressSchema(), + }, + { + key: 'rwd', // rewardsPool + valueSchema: new AddressSchema(), + }, + { + key: 'earn', // rewardsLevel + valueSchema: new Uint64Schema(), + }, + { + key: 'rate', // rewardsRate + valueSchema: new Uint64Schema(), + }, + { + key: 'frac', // rewardsResidue + valueSchema: new Uint64Schema(), + }, + { + key: 'rwcalr', // rewardsRecalculationRound + valueSchema: new Uint64Schema(), + }, + ]) + ); + + /** + * The FeeSink address. + */ + public feeSink: Address; + + /** + * The RewardsPool address. + */ + public rewardsPool: Address; + + /** + * RewardsLevel specifies how many rewards, in MicroAlgos, have been distributed to each + * config.Protocol.RewardUnit of MicroAlgos since genesis. + */ + public rewardsLevel: bigint; + + /** + * The number of new MicroAlgos added to the participation stake from rewards at the next round. + */ + public rewardsRate: bigint; + + /** + * The number of leftover MicroAlgos after the distribution of RewardsRate/rewardUnits MicroAlgos for + * every reward unit in the next round. + */ + public rewardsResidue: bigint; + + /** + * The round at which the RewardsRate will be recalculated. + */ + public rewardsRecalculationRound: bigint; + + constructor(params: { + feeSink: Address; + rewardsPool: Address; + rewardsLevel: bigint; + rewardsRate: bigint; + rewardsResidue: bigint; + rewardsRecalculationRound: bigint; + }) { + this.feeSink = params.feeSink; + this.rewardsPool = params.rewardsPool; + this.rewardsLevel = params.rewardsLevel; + this.rewardsRate = params.rewardsRate; + this.rewardsResidue = params.rewardsResidue; + this.rewardsRecalculationRound = params.rewardsRecalculationRound; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return RewardState.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['fees', this.feeSink], + ['rwd', this.rewardsPool], + ['earn', this.rewardsLevel], + ['rate', this.rewardsRate], + ['frac', this.rewardsResidue], + ['rwcalr', this.rewardsRecalculationRound], + ]); + } + + public static fromEncodingData(data: unknown): RewardState { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded RewardState: ${data}`); + } + return new RewardState({ + feeSink: data.get('fees'), + rewardsPool: data.get('rwd'), + rewardsLevel: data.get('earn'), + rewardsRate: data.get('rate'), + rewardsResidue: data.get('frac'), + rewardsRecalculationRound: data.get('rwcalr'), + }); + } +} + +/** + * UpgradeState tracks the protocol upgrade state machine. It is, strictly speaking, computable from + * the history of all UpgradeVotes but we keep it in the block for explicitness and convenience + * (instead of materializing it separately, like balances). + */ +export class UpgradeState implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'proto', // currentProtocol + valueSchema: new StringSchema(), + }, + { + key: 'nextproto', // nextProtocol + valueSchema: new StringSchema(), + }, + { + key: 'nextyes', // nextProtocolApprovals + valueSchema: new Uint64Schema(), + }, + { + key: 'nextbefore', // nextProtocolVoteBefore + valueSchema: new Uint64Schema(), + }, + { + key: 'nextswitch', // nextProtocolSwitchOn + valueSchema: new Uint64Schema(), + }, + ]) + ); + + public currentProtocol: string; + + public nextProtocol: string; + + public nextProtocolApprovals: bigint; + + /** + * NextProtocolVoteBefore specify the last voting round for the next protocol proposal. If there + * is no voting for an upgrade taking place, this would be zero. + */ + public nextProtocolVoteBefore: bigint; + + /** + * NextProtocolSwitchOn specify the round number at which the next protocol would be adopted. If + * there is no upgrade taking place, nor a wait for the next protocol, this would be zero. + */ + public nextProtocolSwitchOn: bigint; + + public constructor(params: { + currentProtocol: string; + nextProtocol: string; + nextProtocolApprovals: bigint; + nextProtocolVoteBefore: bigint; + nextProtocolSwitchOn: bigint; + }) { + this.currentProtocol = params.currentProtocol; + this.nextProtocol = params.nextProtocol; + this.nextProtocolApprovals = params.nextProtocolApprovals; + this.nextProtocolVoteBefore = params.nextProtocolVoteBefore; + this.nextProtocolSwitchOn = params.nextProtocolSwitchOn; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return UpgradeState.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['proto', this.currentProtocol], + ['nextproto', this.nextProtocol], + ['nextyes', this.nextProtocolApprovals], + ['nextbefore', this.nextProtocolVoteBefore], + ['nextswitch', this.nextProtocolSwitchOn], + ]); + } + + public static fromEncodingData(data: unknown): UpgradeState { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded UpgradeState: ${data}`); + } + return new UpgradeState({ + currentProtocol: data.get('proto'), + nextProtocol: data.get('nextproto'), + nextProtocolApprovals: data.get('nextyes'), + nextProtocolVoteBefore: data.get('nextbefore'), + nextProtocolSwitchOn: data.get('nextswitch'), + }); + } +} + +/** + * UpgradeVote represents the vote of the block proposer with respect to protocol upgrades. + */ +export class UpgradeVote implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'upgradeprop', // upgradePropose + valueSchema: new StringSchema(), + }, + { + key: 'upgradedelay', // upgradeDelay + valueSchema: new Uint64Schema(), + }, + { + key: 'upgradeyes', // upgradeApprove + valueSchema: new BooleanSchema(), + }, + ]) + ); + + /** + * UpgradePropose indicates a proposed upgrade + */ + public upgradePropose: string; + + /** + * UpgradeDelay indicates the time between acceptance and execution + */ + public upgradeDelay: bigint; + + /** + * UpgradeApprove indicates a yes vote for the current proposal + */ + public upgradeApprove: boolean; + + public constructor(params: { + upgradePropose: string; + upgradeDelay: bigint; + upgradeApprove: boolean; + }) { + this.upgradePropose = params.upgradePropose; + this.upgradeDelay = params.upgradeDelay; + this.upgradeApprove = params.upgradeApprove; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return UpgradeVote.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['upgradeprop', this.upgradePropose], + ['upgradedelay', this.upgradeDelay], + ['upgradeyes', this.upgradeApprove], + ]); + } + + public static fromEncodingData(data: unknown): UpgradeVote { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded UpgradeVote: ${data}`); + } + return new UpgradeVote({ + upgradePropose: data.get('upgradeprop'), + upgradeDelay: data.get('upgradedelay'), + upgradeApprove: data.get('upgradeyes'), + }); + } +} + +/** + * ParticipationUpdates represents participation account data that needs to be checked/acted on by + * the network + */ +export class ParticipationUpdates implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'partupdrmv', // expiredParticipationAccounts + valueSchema: new ArraySchema(new AddressSchema()), + }, + { + key: 'partupdabs', // absentParticipationAccounts + valueSchema: new ArraySchema(new AddressSchema()), + }, + ]) + ); + + /** + * ExpiredParticipationAccounts contains a list of online accounts that needs to be converted to + * offline since their participation key expired. + */ + public expiredParticipationAccounts: Address[]; + + /** + * AbsentParticipationAccounts contains a list of online accounts that needs to be converted to + * offline since they are not proposing. + */ + public absentParticipationAccounts: Address[]; + + public constructor(params: { + expiredParticipationAccounts: Address[]; + absentParticipationAccounts: Address[]; + }) { + this.expiredParticipationAccounts = params.expiredParticipationAccounts; + this.absentParticipationAccounts = params.absentParticipationAccounts; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return ParticipationUpdates.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['partupdrmv', this.expiredParticipationAccounts], + ['partupdabs', this.absentParticipationAccounts], + ]); + } + + public static fromEncodingData(data: unknown): ParticipationUpdates { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ParticipationUpdates: ${data}`); + } + return new ParticipationUpdates({ + expiredParticipationAccounts: data.get('partupdrmv'), + absentParticipationAccounts: data.get('partupdabs'), + }); + } +} + +/** + * 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 class BlockHeader implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'rnd', // round + valueSchema: new Uint64Schema(), + }, + { + key: 'prev', // branch + valueSchema: new BlockHashSchema(), + }, + { + key: 'seed', // seed + valueSchema: new ByteArraySchema(), + }, + { + key: '', + valueSchema: TxnCommitments.encodingSchema, + embedded: true, + }, + { + key: 'ts', // timestamp + valueSchema: new Uint64Schema(), + }, + { + key: 'gen', // genesisID + valueSchema: new StringSchema(), + }, + { + key: 'gh', // genesisHash + valueSchema: new FixedLengthByteArraySchema(32), + }, + { + key: 'prp', // proposer + valueSchema: new AddressSchema(), + }, + { + key: 'fc', // feesCollected + valueSchema: new Uint64Schema(), + }, + { + key: 'bi', // bonus + valueSchema: new Uint64Schema(), + }, + { + key: 'pp', // proposerPayout + valueSchema: new Uint64Schema(), + }, + { + key: '', + valueSchema: RewardState.encodingSchema, + embedded: true, + }, + { + key: '', + valueSchema: UpgradeState.encodingSchema, + embedded: true, + }, + { + key: '', + valueSchema: UpgradeVote.encodingSchema, + embedded: true, + }, + { + key: 'tc', // txnCounter + valueSchema: new Uint64Schema(), + }, + { + key: 'spt', // stateproofTracking + valueSchema: new Uint64MapSchema(StateProofTrackingData.encodingSchema), + }, + { + key: '', + valueSchema: ParticipationUpdates.encodingSchema, + embedded: true, + }, + ]) + ); + + /** + * Round number + */ + public round: bigint; + + /** + * Previous block hash + */ + public branch: Uint8Array; + + /** + * Sortition seed + */ + public seed: Uint8Array; + + public txnCommitments: TxnCommitments; + + /** + * Timestamp in seconds since epoch + */ + public timestamp: bigint; + + /** + * Genesis ID to which this block belongs. + */ + public genesisID: string; + + /** + * Genesis hash to which this block belongs. + */ + public genesisHash: Uint8Array; + + /** + * Proposer is the proposer of this block. Like the Seed, agreement adds this after the block is + * assembled by the transaction pool, so that the same block can be prepared for multiple + * participating accounts in the same node. Populated if proto.Payouts.Enabled + */ + public proposer: Address; + + /** + * FeesCollected is the sum of all fees paid by transactions in this block. Populated if + * proto.EnableMining. + */ + public feesCollected: bigint; + + /** + * Bonus is the bonus incentive to be paid for proposing this block. It begins as a consensus + * parameter value, and decays periodically. + */ + public bonus: bigint; + + /** + * ProposerPayout is the amount that should be moved from the FeeSink to the Proposer at the start + * of the next block. It is basically the bonus + the payouts percent of FeesCollected, but may + * be zero'd by proposer ineligibility. + */ + public proposerPayout: bigint; + + public rewardState: RewardState; + + public upgradeState: UpgradeState; + + public upgradeVote: UpgradeVote; + + /** + * TxnCounter is the number of the next transaction that will be committed after this block. Genesis + * blocks can start at either 0 or 1000, depending on a consensus parameter (AppForbidLowResources). + */ + public txnCounter: bigint; + + /** + * StateProofTracking tracks the status of the state proofs, potentially for multiple types of + * ASPs (Algorand's State Proofs). + */ + public stateproofTracking: Map; + + public participationUpdates: ParticipationUpdates; + + public constructor(params: { + round: bigint; + branch: Uint8Array; + seed: Uint8Array; + txnCommitments: TxnCommitments; + timestamp: bigint; + genesisID: string; + genesisHash: Uint8Array; + proposer: Address; + feesCollected: bigint; + bonus: bigint; + proposerPayout: bigint; + rewardState: RewardState; + upgradeState: UpgradeState; + upgradeVote: UpgradeVote; + txnCounter: bigint; + stateproofTracking: Map; + participationUpdates: ParticipationUpdates; + }) { + this.round = params.round; + this.branch = params.branch; + this.seed = params.seed; + this.txnCommitments = params.txnCommitments; + this.timestamp = params.timestamp; + this.genesisID = params.genesisID; + this.genesisHash = params.genesisHash; + this.proposer = params.proposer; + this.feesCollected = params.feesCollected; + this.bonus = params.bonus; + this.proposerPayout = params.proposerPayout; + this.rewardState = params.rewardState; + this.upgradeState = params.upgradeState; + this.upgradeVote = params.upgradeVote; + this.txnCounter = params.txnCounter; + this.stateproofTracking = params.stateproofTracking; + this.participationUpdates = params.participationUpdates; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return BlockHeader.encodingSchema; + } + + public toEncodingData(): Map { + const data = new Map([ + ['rnd', this.round], + ['prev', this.branch], + ['seed', this.seed], + ['ts', this.timestamp], + ['gen', this.genesisID], + ['gh', this.genesisHash], + ['prp', this.proposer], + ['fc', this.feesCollected], + ['bi', this.bonus], + ['pp', this.proposerPayout], + ['tc', this.txnCounter], + [ + 'spt', + convertMap(this.stateproofTracking, (key, value) => [ + key, + value.toEncodingData(), + ]), + ], + ]); + return combineMaps( + data, + this.txnCommitments.toEncodingData(), + this.rewardState.toEncodingData(), + this.upgradeState.toEncodingData(), + this.upgradeVote.toEncodingData(), + this.participationUpdates.toEncodingData() + ); + } + + public static fromEncodingData(data: unknown): BlockHeader { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockHeader: ${data}`); + } + return new BlockHeader({ + round: data.get('rnd'), + branch: data.get('prev'), + seed: data.get('seed'), + txnCommitments: TxnCommitments.fromEncodingData(data), + timestamp: data.get('ts'), + genesisID: data.get('gen'), + genesisHash: data.get('gh'), + proposer: data.get('prp'), + feesCollected: data.get('fc'), + bonus: data.get('bi'), + proposerPayout: data.get('pp'), + rewardState: RewardState.fromEncodingData(data), + upgradeState: UpgradeState.fromEncodingData(data), + upgradeVote: UpgradeVote.fromEncodingData(data), + txnCounter: data.get('tc'), + stateproofTracking: convertMap( + data.get('spt') as Map, + (key, value) => [ + Number(key), + StateProofTrackingData.fromEncodingData(value), + ] + ), + participationUpdates: ParticipationUpdates.fromEncodingData(data), + }); + } +} + +export class ValueDelta implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'at', // action + valueSchema: new Uint64Schema(), + }, + { + key: 'bs', // bytes + valueSchema: new SpecialCaseBinaryStringSchema(), + }, + { + key: 'ui', // uint + valueSchema: new Uint64Schema(), + }, + ]) + ); + + public action: number; + public bytes: Uint8Array; + public uint: bigint; + + public constructor(params: { + action: number; + bytes: Uint8Array; + uint: bigint; + }) { + this.action = params.action; + this.bytes = params.bytes; + this.uint = params.uint; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return ValueDelta.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['at', this.action], + ['bs', this.bytes], + ['ui', this.uint], + ]); + } + + public static fromEncodingData(data: unknown): ValueDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ValueDelta: ${data}`); + } + return new ValueDelta({ + action: Number(data.get('at')), + bytes: data.get('bs'), + uint: data.get('ui'), + }); + } +} + +export class EvalDelta implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + public static get encodingSchema(): Schema { + // This is declared like this in order to break the circular dependency of + // SignedTxnWithAD -> ApplyData -> EvalDelta -> SignedTxnWithAD + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + ...allOmitEmpty([ + { + key: 'gd', // globalDelta + valueSchema: new OptionalSchema( + new SpecialCaseBinaryStringMapSchema(ValueDelta.encodingSchema) + ), + }, + { + key: 'ld', // localDeltas + valueSchema: new OptionalSchema( + new Uint64MapSchema( + new SpecialCaseBinaryStringMapSchema(ValueDelta.encodingSchema) + ) + ), + }, + { + key: 'sa', // sharedAccts + valueSchema: new OptionalSchema( + new ArraySchema(new AddressSchema()) + ), + }, + { + key: 'lg', // logs + valueSchema: new OptionalSchema( + new ArraySchema(new SpecialCaseBinaryStringSchema()) + ), + }, + { + key: 'itx', // innerTxns + valueSchema: new OptionalSchema( + // eslint-disable-next-line no-use-before-define + new ArraySchema(SignedTxnWithAD.encodingSchema) + ), + }, + ]) + ); + } + return this.encodingSchemaValue; + } + + public globalDelta: Map; + + /** + * When decoding EvalDeltas, the integer key represents an offset into + * [txn.Sender, txn.Accounts[0], txn.Accounts[1], ...] + */ + public localDeltas: Map>; + + /** + * If a program modifies the local of an account that is not the Sender, or + * in txn.Accounts, it must be recorded here, so that the key in LocalDeltas + * can refer to it. + */ + public sharedAccts: Address[]; + + public logs: Uint8Array[]; + + // eslint-disable-next-line no-use-before-define + public innerTxns: SignedTxnWithAD[]; + + public constructor(params: { + globalDelta?: Map; + localDeltas?: Map>; + sharedAccts?: Address[]; + logs?: Uint8Array[]; + // eslint-disable-next-line no-use-before-define + innerTxns?: SignedTxnWithAD[]; + }) { + this.globalDelta = params.globalDelta ?? new Map(); + this.localDeltas = + params.localDeltas ?? new Map>(); + this.sharedAccts = params.sharedAccts ?? []; + this.logs = params.logs ?? []; + this.innerTxns = params.innerTxns ?? []; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return EvalDelta.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + [ + 'gd', + convertMap(this.globalDelta, (key, value) => [ + key, + value.toEncodingData(), + ]), + ], + [ + 'ld', + convertMap(this.localDeltas, (key, value) => [ + key, + convertMap(value, (k, v) => [k, v.toEncodingData()]), + ]), + ], + ['sa', this.sharedAccts], + ['lg', this.logs], + ['itx', this.innerTxns.map((t) => t.toEncodingData())], + ]); + } + + public static fromEncodingData(data: unknown): EvalDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded EvalDelta: ${data}`); + } + return new EvalDelta({ + globalDelta: data.get('gd') + ? convertMap( + data.get('gd') as Map, + (key, value) => [key, ValueDelta.fromEncodingData(value)] + ) + : undefined, + localDeltas: data.get('ld') + ? convertMap( + data.get('ld') as Map>, + (key, value) => [ + Number(key), + convertMap(value, (k, v) => [k, ValueDelta.fromEncodingData(v)]), + ] + ) + : undefined, + sharedAccts: data.get('sa'), + logs: data.get('lg'), + // eslint-disable-next-line no-use-before-define + innerTxns: (data.get('itx') ?? []).map(SignedTxnWithAD.fromEncodingData), + }); + } +} + +export class ApplyData implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + public static get encodingSchema(): Schema { + // This is declared like this in order to break the circular dependency of + // SignedTxnWithAD -> ApplyData -> EvalDelta -> SignedTxnWithAD + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + ...allOmitEmpty([ + { + key: 'ca', // closingAmount + valueSchema: new OptionalSchema(new Uint64Schema()), + }, + { + key: 'aca', // assetClosingAmount + valueSchema: new OptionalSchema(new Uint64Schema()), + }, + { + key: 'rs', // senderRewards + valueSchema: new OptionalSchema(new Uint64Schema()), + }, + { + key: 'rr', // receiverRewards + valueSchema: new OptionalSchema(new Uint64Schema()), + }, + { + key: 'rc', // closeRewards + valueSchema: new OptionalSchema(new Uint64Schema()), + }, + { + key: 'dt', // evalDelta + valueSchema: new OptionalSchema(EvalDelta.encodingSchema), + }, + { + key: 'caid', // configAsset + valueSchema: new OptionalSchema(new Uint64Schema()), + }, + { + key: 'apid', // applicationID + valueSchema: new OptionalSchema(new Uint64Schema()), + }, + ]) + ); + } + return this.encodingSchemaValue; + } + + /** + * Closing amount for transaction. + */ + public closingAmount?: bigint; + + /** + * Closing amount for asset transaction. + */ + public assetClosingAmount?: bigint; + + /** + * Rewards applied to the Sender. + */ + public senderRewards?: bigint; + + /** + * Rewards applied to the Receiver. + */ + public receiverRewards?: bigint; + + /** + * Rewards applied to the CloseRemainderTo account. + */ + public closeRewards?: bigint; + + public evalDelta?: EvalDelta; + + /** + * If an ASA is being created, this is its newly created ID. Else 0. + */ + public configAsset?: bigint; + + /** + * If an application is being created, this is its newly created ID. Else 0. + */ + public applicationID?: bigint; + + public constructor(params: { + closingAmount?: bigint; + assetClosingAmount?: bigint; + senderRewards?: bigint; + receiverRewards?: bigint; + closeRewards?: bigint; + evalDelta?: EvalDelta; + configAsset?: bigint; + applicationID?: bigint; + }) { + this.closingAmount = params.closingAmount; + this.assetClosingAmount = params.assetClosingAmount; + this.senderRewards = params.senderRewards; + this.receiverRewards = params.receiverRewards; + this.closeRewards = params.closeRewards; + this.evalDelta = params.evalDelta; + this.configAsset = params.configAsset; + this.applicationID = params.applicationID; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return ApplyData.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['ca', this.closingAmount], + ['aca', this.assetClosingAmount], + ['rs', this.senderRewards], + ['rr', this.receiverRewards], + ['rc', this.closeRewards], + ['dt', this.evalDelta ? this.evalDelta.toEncodingData() : undefined], + ['caid', this.configAsset], + ['apid', this.applicationID], + ]); + } + + public static fromEncodingData(data: unknown): ApplyData { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplyData: ${data}`); + } + return new ApplyData({ + closingAmount: data.get('ca'), + assetClosingAmount: data.get('aca'), + senderRewards: data.get('rs'), + receiverRewards: data.get('rr'), + closeRewards: data.get('rc'), + evalDelta: data.get('dt') + ? EvalDelta.fromEncodingData(data.get('dt')) + : undefined, + configAsset: data.get('caid'), + applicationID: data.get('apid'), + }); + } +} + +export class SignedTxnWithAD implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + public static get encodingSchema(): Schema { + // This is declared like this in order to break the circular dependency of + // SignedTxnWithAD -> ApplyData -> EvalDelta -> SignedTxnWithAD + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + ...allOmitEmpty([ + { + key: '', + valueSchema: SignedTransaction.encodingSchema, + embedded: true, + }, + { + key: '', + valueSchema: ApplyData.encodingSchema, + embedded: true, + }, + ]) + ); + } + return this.encodingSchemaValue; + } + + public signedTxn: SignedTransaction; + + public applyData: ApplyData; + + public constructor(params: { + signedTxn: SignedTransaction; + applyData: ApplyData; + }) { + this.signedTxn = params.signedTxn; + this.applyData = params.applyData; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return SignedTxnWithAD.encodingSchema; + } + + public toEncodingData(): Map { + return combineMaps( + this.signedTxn.toEncodingData(), + this.applyData.toEncodingData() + ); + } + + public static fromEncodingData(data: unknown): SignedTxnWithAD { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SignedTxnWithAD: ${data}`); + } + return new SignedTxnWithAD({ + signedTxn: SignedTransaction.fromEncodingData(data), + applyData: ApplyData.fromEncodingData(data), + }); + } +} + +/** + * SignedTxnInBlock is how a signed transaction is encoded in a block. + */ +export class SignedTxnInBlock implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: '', + valueSchema: SignedTxnWithAD.encodingSchema, + embedded: true, + }, + { + key: 'hgi', // hasGenesisID + valueSchema: new BooleanSchema(), + }, + { + key: 'hgh', // hasGenesisHash + valueSchema: new BooleanSchema(), + }, + ]) + ); + + public signedTxn: SignedTxnWithAD; + + public hasGenesisID: boolean; + + public hasGenesisHash: boolean; + + public constructor(params: { + signedTxn: SignedTxnWithAD; + hasGenesisID: boolean; + hasGenesisHash: boolean; + }) { + this.signedTxn = params.signedTxn; + this.hasGenesisID = params.hasGenesisID; + this.hasGenesisHash = params.hasGenesisHash; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return SignedTxnInBlock.encodingSchema; + } + + public toEncodingData(): Map { + const data = new Map([ + ['hgi', this.hasGenesisID], + ['hgh', this.hasGenesisHash], + ]); + return combineMaps(data, this.signedTxn.toEncodingData()); + } + + public static fromEncodingData(data: unknown): SignedTxnInBlock { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SignedTxnInBlock: ${data}`); + } + return new SignedTxnInBlock({ + signedTxn: SignedTxnWithAD.fromEncodingData(data), + hasGenesisID: data.get('hgi'), + hasGenesisHash: data.get('hgh'), + }); + } +} + +/** + * A Block contains the Payset and metadata corresponding to a given Round. + */ +export class Block implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: '', + valueSchema: BlockHeader.encodingSchema, + embedded: true, + }, + { + key: 'txns', // payset + valueSchema: new ArraySchema(SignedTxnInBlock.encodingSchema), + }, + ]) + ); + + public header: BlockHeader; + + public payset: SignedTxnInBlock[]; + + public constructor(params: { + header: BlockHeader; + payset: SignedTxnInBlock[]; + }) { + this.header = params.header; + this.payset = params.payset; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return Block.encodingSchema; + } + + public toEncodingData(): Map { + const data = new Map([ + ['txns', this.payset.map((p) => p.toEncodingData())], + ]); + return combineMaps(data, this.header.toEncodingData()); + } + + public static fromEncodingData(data: unknown): Block { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockHeader: ${data}`); + } + return new Block({ + header: BlockHeader.fromEncodingData(data), + payset: data.get('txns').map(SignedTxnInBlock.fromEncodingData), + }); + } +} diff --git a/src/types/blockHeader.ts b/src/types/blockHeader.ts deleted file mode 100644 index 96f2af265..000000000 --- a/src/types/blockHeader.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * 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 deleted file mode 100644 index d7657e836..000000000 --- a/src/types/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './transactions'; -export * from './multisig'; -export * from './address'; diff --git a/src/types/intDecoding.ts b/src/types/intDecoding.ts index e974b050a..e15659c16 100644 --- a/src/types/intDecoding.ts +++ b/src/types/intDecoding.ts @@ -6,7 +6,7 @@ enum IntDecoding { * All integers will be decoded as Numbers, meaning any values greater than * Number.MAX_SAFE_INTEGER will lose precision. */ - DEFAULT = 'default', + UNSAFE = 'unsafe', /** * All integers will be decoded as Numbers, but if any values are greater than diff --git a/src/types/multisig.ts b/src/types/multisig.ts deleted file mode 100644 index c05e86b3b..000000000 --- a/src/types/multisig.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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/statedelta.ts b/src/types/statedelta.ts new file mode 100644 index 000000000..32be00618 --- /dev/null +++ b/src/types/statedelta.ts @@ -0,0 +1,1717 @@ +import { Encodable, Schema } from '../encoding/encoding.js'; +import { + NamedMapSchema, + Uint64MapSchema, + ByteArrayMapSchema, + SpecialCaseBinaryStringMapSchema, + SpecialCaseBinaryStringSchema, + ArraySchema, + BooleanSchema, + Uint64Schema, + AddressSchema, + ByteArraySchema, + FixedLengthByteArraySchema, + OptionalSchema, + UntypedSchema, + allOmitEmpty, + convertMap, + combineMaps, +} from '../encoding/schema/index.js'; +import { Address } from '../encoding/address.js'; +import { BlockHeader } from './block.js'; +import { UntypedValue } from '../client/v2/untypedmodel.js'; + +// TealValue contains type information and a value, representing a value in a TEAL program +export class TealValue implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'tt', valueSchema: new Uint64Schema() }, // type + { + key: 'tb', // bytes + valueSchema: new OptionalSchema(new SpecialCaseBinaryStringSchema()), + }, + { key: 'ui', valueSchema: new OptionalSchema(new Uint64Schema()) }, // uint + ]) + ); + + /** + * Type determines the type of the value. + * * 1 represents the type of a byte slice in a TEAL program + * * 2 represents the type of an unsigned integer in a TEAL program + */ + public type: number; + public bytes?: Uint8Array; + public uint?: bigint; + + constructor(params: { type: number; bytes?: Uint8Array; uint?: bigint }) { + this.type = params.type; + this.bytes = params.bytes; + this.uint = params.uint; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return TealValue.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['tt', this.type], + ['tb', this.bytes], + ['ui', this.uint], + ]); + } + + public static fromEncodingData(data: unknown): TealValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TealValue: ${data}`); + } + return new TealValue({ + type: Number(data.get('tt')), + bytes: data.get('tb'), + uint: data.get('ui'), + }); + } +} + +/** + * StateSchema sets maximums on the number of each type that may be stored + */ +export class StateSchema implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'nui', // numUints + valueSchema: new Uint64Schema(), + }, + { + key: 'nbs', // numByteSlices + valueSchema: new Uint64Schema(), + }, + ]) + ); + + public numUints: number; + public numByteSlices: number; + + public constructor(params: { numUints: number; numByteSlices: number }) { + this.numUints = params.numUints; + this.numByteSlices = params.numByteSlices; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return StateSchema.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['nui', this.numUints], + ['nbs', this.numByteSlices], + ]); + } + + public static fromEncodingData(data: unknown): StateSchema { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateSchema: ${data}`); + } + return new StateSchema({ + numUints: Number(data.get('nui')), + numByteSlices: Number(data.get('nbs')), + }); + } +} + +/** + * AppParams stores the global information associated with an application + */ +export class AppParams implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'approv', valueSchema: new ByteArraySchema() }, // approvalProgram + { key: 'clearp', valueSchema: new ByteArraySchema() }, // alearStateProgram + { + key: 'gs', + valueSchema: new SpecialCaseBinaryStringMapSchema( + TealValue.encodingSchema + ), + }, // globalState + { key: 'lsch', valueSchema: StateSchema.encodingSchema }, // localStateSchema + { key: 'gsch', valueSchema: StateSchema.encodingSchema }, // globalStateSchema + { key: 'epp', valueSchema: new Uint64Schema() }, // extraProgramPages + ]) + ); + + public approvalProgram: Uint8Array; + public clearStateProgram: Uint8Array; + public globalState: Map; + public localStateSchema: StateSchema; + public globalStateSchema: StateSchema; + public extraProgramPages: number; + + constructor(params: { + approvalProgram: Uint8Array; + clearStateProgram: Uint8Array; + globalState: Map; + localStateSchema: StateSchema; + globalStateSchema: StateSchema; + extraProgramPages: number; + }) { + this.approvalProgram = params.approvalProgram; + this.clearStateProgram = params.clearStateProgram; + this.globalState = params.globalState; + this.localStateSchema = params.localStateSchema; + this.globalStateSchema = params.globalStateSchema; + this.extraProgramPages = params.extraProgramPages; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AppParams.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['approv', this.approvalProgram], + ['clearp', this.clearStateProgram], + ['gs', convertMap(this.globalState, (k, v) => [k, v.toEncodingData()])], + ['lsch', this.localStateSchema.toEncodingData()], + ['gsch', this.globalStateSchema.toEncodingData()], + ['epp', this.extraProgramPages], + ]); + } + + public static fromEncodingData(data: unknown) { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AppParams: ${data}`); + } + return new AppParams({ + approvalProgram: data.get('approv'), + clearStateProgram: data.get('clearp'), + globalState: convertMap( + data.get('gs') as Map, + (k, v) => [k, TealValue.fromEncodingData(v)] + ), + localStateSchema: StateSchema.fromEncodingData(data.get('lsch')), + globalStateSchema: StateSchema.fromEncodingData(data.get('gsch')), + extraProgramPages: Number(data.get('epp')), + }); + } +} + +/** + * AppLocalState stores the LocalState associated with an application. + */ +export class AppLocalState implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'hsch', valueSchema: StateSchema.encodingSchema }, // schema + { + key: 'tkv', // keyValue + valueSchema: new SpecialCaseBinaryStringMapSchema( + TealValue.encodingSchema + ), + }, + ]) + ); + + public schema: StateSchema; + public keyValue: Map; + + constructor(params: { + schema: StateSchema; + keyValue: Map; + }) { + this.schema = params.schema; + this.keyValue = params.keyValue; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AppLocalState.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['hsch', this.schema.toEncodingData()], + ['tkv', convertMap(this.keyValue, (k, v) => [k, v.toEncodingData()])], + ]); + } + + public static fromEncodingData(data: unknown): AppLocalState { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AppLocalState: ${data}`); + } + return new AppLocalState({ + schema: StateSchema.fromEncodingData(data.get('hsch')), + keyValue: convertMap( + data.get('tkv') as Map, + (k, v) => [k, TealValue.fromEncodingData(v)] + ), + }); + } +} + +/** + * AppLocalStateDelta tracks a changed AppLocalState, and whether it was deleted + */ +export class AppLocalStateDelta implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'LocalState', // localState + valueSchema: new OptionalSchema(AppLocalState.encodingSchema), + }, + { key: 'Deleted', valueSchema: new BooleanSchema() }, // deleted + ]) + ); + + public localState?: AppLocalState; + public deleted: boolean; + + constructor(params: { localState?: AppLocalState; deleted: boolean }) { + this.localState = params.localState; + this.deleted = params.deleted; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AppLocalStateDelta.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + [ + 'LocalState', + this.localState ? this.localState.toEncodingData() : undefined, + ], + ['Deleted', this.deleted], + ]); + } + + public static fromEncodingData(data: unknown): AppLocalStateDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AppLocalStateDelta: ${data}`); + } + return new AppLocalStateDelta({ + localState: data.get('LocalState') + ? AppLocalState.fromEncodingData(data.get('LocalState')) + : undefined, + deleted: data.get('Deleted'), + }); + } +} + +/** + * AppParamsDelta tracks a changed AppParams, and whether it was deleted + */ +export class AppParamsDelta implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'Params', // params + valueSchema: new OptionalSchema(AppParams.encodingSchema), + }, + { key: 'Deleted', valueSchema: new BooleanSchema() }, // deleted + ]) + ); + + public params?: AppParams; + public deleted: boolean; + + constructor(params: { params?: AppParams; deleted: boolean }) { + this.params = params.params; + this.deleted = params.deleted; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AppParamsDelta.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['Params', this.params ? this.params.toEncodingData() : undefined], + ['Deleted', this.deleted], + ]); + } + + public static fromEncodingData(data: unknown): AppParamsDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AppParamsDelta: ${data}`); + } + return new AppParamsDelta({ + params: data.get('Params') + ? AppParams.fromEncodingData(data.get('Params')) + : undefined, + deleted: data.get('Deleted'), + }); + } +} + +/** + * AppResourceRecord represents AppParams and AppLocalState in deltas + */ +export class AppResourceRecord implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'Aidx', valueSchema: new Uint64Schema() }, // id + { key: 'Addr', valueSchema: new AddressSchema() }, // address + { + key: 'Params', // params + valueSchema: AppParamsDelta.encodingSchema, + }, + { + key: 'State', // state + valueSchema: AppLocalStateDelta.encodingSchema, + }, + ]) + ); + + public id: bigint; + public address: Address; + public params: AppParamsDelta; + public state: AppLocalStateDelta; + + constructor(params: { + id: bigint; + address: Address; + params: AppParamsDelta; + state: AppLocalStateDelta; + }) { + this.id = params.id; + this.address = params.address; + this.params = params.params; + this.state = params.state; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AppResourceRecord.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['Aidx', this.id], + ['Addr', this.address], + ['Params', this.params.toEncodingData()], + ['State', this.state.toEncodingData()], + ]); + } + + public static fromEncodingData(data: unknown): AppResourceRecord { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AppResourceRecord: ${data}`); + } + return new AppResourceRecord({ + id: data.get('Aidx'), + address: data.get('Addr'), + params: AppParamsDelta.fromEncodingData(data.get('Params')), + state: AppLocalStateDelta.fromEncodingData(data.get('State')), + }); + } +} + +/** + * AssetHolding describes an asset held by an account. + */ +export class AssetHolding implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'a', valueSchema: new Uint64Schema() }, // amount + { key: 'f', valueSchema: new BooleanSchema() }, // frozen + ]) + ); + + public amount: bigint; + public frozen: boolean; + + constructor(params: { amount: bigint; frozen: boolean }) { + this.amount = params.amount; + this.frozen = params.frozen; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AssetHolding.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['a', this.amount], + ['f', this.frozen], + ]); + } + + public static fromEncodingData(data: unknown): AssetHolding { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetHolding: ${data}`); + } + return new AssetHolding({ + amount: data.get('a'), + frozen: data.get('f'), + }); + } +} + +/** + * AssetHoldingDelta records a changed AssetHolding, and whether it was deleted + */ +export class AssetHoldingDelta implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'Holding', // holding + valueSchema: new OptionalSchema(AssetHolding.encodingSchema), + }, + { key: 'Deleted', valueSchema: new BooleanSchema() }, // deleted + ]) + ); + + public holding?: AssetHolding; + public deleted: boolean; + + constructor(params: { holding?: AssetHolding; deleted: boolean }) { + this.holding = params.holding; + this.deleted = params.deleted; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AssetHoldingDelta.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['Holding', this.holding ? this.holding.toEncodingData() : undefined], + ['Deleted', this.deleted], + ]); + } + + public static fromEncodingData(data: unknown): AssetHoldingDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetHoldingDelta: ${data}`); + } + return new AssetHoldingDelta({ + holding: data.get('Holding') + ? AssetHolding.fromEncodingData(data.get('Holding')) + : undefined, + deleted: data.get('Deleted'), + }); + } +} + +/** + * AssetParams describes the parameters of an asset. + */ +export class AssetParams implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 't', valueSchema: new Uint64Schema() }, // total + { key: 'dc', valueSchema: new Uint64Schema() }, // decimals + { key: 'df', valueSchema: new BooleanSchema() }, // defaultFrozen + { + key: 'un', // unitName + valueSchema: new OptionalSchema(new SpecialCaseBinaryStringSchema()), + }, + { + key: 'an', // assetName + valueSchema: new OptionalSchema(new SpecialCaseBinaryStringSchema()), + }, + { + key: 'au', // url + valueSchema: new OptionalSchema(new SpecialCaseBinaryStringSchema()), + }, + { key: 'am', valueSchema: new FixedLengthByteArraySchema(32) }, // metadataHash + { key: 'm', valueSchema: new OptionalSchema(new AddressSchema()) }, // manager + { key: 'r', valueSchema: new OptionalSchema(new AddressSchema()) }, // reserve + { key: 'f', valueSchema: new OptionalSchema(new AddressSchema()) }, // freeze + { key: 'c', valueSchema: new OptionalSchema(new AddressSchema()) }, // clawback + ]) + ); + + /** + * Total specifies the total number of units of this asset created. + */ + public total: bigint; + + /** + * Decimals specifies the number of digits to display after the decimal place when displaying this asset. + * A value of 0 represents an asset that is not divisible, a value of 1 represents an asset divisible into tenths, and so on. + * This value must be between 0 and 19 (inclusive). + */ + public decimals: number; + + /** + * DefaultFrozen specifies whether slots for this asset in user accounts are frozen by default or not. + */ + public defaultFrozen: boolean; + + /** + * UnitName specifies a hint for the name of a unit of this asset. + */ + public unitName?: Uint8Array; + + /** + * AssetName specifies a hint for the name of the asset. + */ + public assetName?: Uint8Array; + + /** + * URL specifies a URL where more information about the asset can be retrieved. + */ + public url?: Uint8Array; + + /** + * MetadataHash specifies a commitment to some unspecified asset metadata. The format of this + * metadata is up to the application. + */ + public metadataHash?: Uint8Array; + + /** + * Manager specifies an account that is allowed to change the non-zero addresses in this AssetParams. + */ + public manager?: Address; + + /** + * Reserve specifies an account whose holdings of this asset should be reported as "not minted". + */ + public reserve?: Address; + + /** + * Freeze specifies an account that is allowed to change the frozen state of holdings of this asset. + */ + public freeze?: Address; + + /** + * Clawback specifies an account that is allowed to take units of this asset from any account. + */ + public clawback?: Address; + + public constructor(params: { + total: bigint; + decimals: number; + defaultFrozen: boolean; + unitName?: Uint8Array; + assetName?: Uint8Array; + url?: Uint8Array; + metadataHash?: Uint8Array; + manager?: Address; + reserve?: Address; + freeze?: Address; + clawback?: Address; + }) { + this.total = params.total; + this.decimals = params.decimals; + this.defaultFrozen = params.defaultFrozen; + this.unitName = params.unitName; + this.assetName = params.assetName; + this.url = params.url; + this.metadataHash = params.metadataHash; + this.manager = params.manager; + this.reserve = params.reserve; + this.freeze = params.freeze; + this.clawback = params.clawback; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AssetParams.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['t', this.total], + ['dc', this.decimals], + ['df', this.defaultFrozen], + ['un', this.unitName], + ['an', this.assetName], + ['au', this.url], + ['am', this.metadataHash], + ['m', this.manager], + ['r', this.reserve], + ['f', this.freeze], + ['c', this.clawback], + ]); + } + + public static fromEncodingData(data: unknown): AssetParams { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetParams: ${data}`); + } + return new AssetParams({ + total: data.get('t'), + decimals: data.get('dc'), + defaultFrozen: data.get('df'), + unitName: data.get('un'), + assetName: data.get('an'), + url: data.get('au'), + metadataHash: data.get('am'), + manager: data.get('m'), + reserve: data.get('r'), + freeze: data.get('f'), + clawback: data.get('c'), + }); + } +} + +/** + * AssetParamsDelta tracks a changed AssetParams, and whether it was deleted + */ +export class AssetParamsDelta implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'Params', // params + valueSchema: new OptionalSchema(AssetParams.encodingSchema), + }, + { key: 'Deleted', valueSchema: new BooleanSchema() }, // deleted + ]) + ); + + public params?: AssetParams; + public deleted: boolean; + + constructor(params: { params?: AssetParams; deleted: boolean }) { + this.params = params.params; + this.deleted = params.deleted; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AssetParamsDelta.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['Params', this.params ? this.params.toEncodingData() : undefined], + ['Deleted', this.deleted], + ]); + } + + public static fromEncodingData(data: unknown): AssetParamsDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetParamsDelta: ${data}`); + } + return new AssetParamsDelta({ + params: data.get('Params') + ? AssetParams.fromEncodingData(data.get('Params')) + : undefined, + deleted: data.get('Deleted'), + }); + } +} + +/** + * AssetResourceRecord represents AssetParams and AssetHolding in deltas + */ +export class AssetResourceRecord implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'Aidx', valueSchema: new Uint64Schema() }, // id + { key: 'Addr', valueSchema: new AddressSchema() }, // address + { + key: 'Params', // params + valueSchema: AssetParamsDelta.encodingSchema, + }, + { + key: 'Holding', // holding + valueSchema: AssetHoldingDelta.encodingSchema, + }, + ]) + ); + + public id: bigint; + public address: Address; + public params: AssetParamsDelta; + public holding: AssetHoldingDelta; + + constructor(params: { + id: bigint; + address: Address; + params: AssetParamsDelta; + holding: AssetHoldingDelta; + }) { + this.id = params.id; + this.address = params.address; + this.params = params.params; + this.holding = params.holding; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AssetResourceRecord.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['Aidx', this.id], + ['Addr', this.address], + ['Params', this.params.toEncodingData()], + ['Holding', this.holding.toEncodingData()], + ]); + } + + public static fromEncodingData(data: unknown): AssetResourceRecord { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetResourceRecord: ${data}`); + } + return new AssetResourceRecord({ + id: data.get('Aidx'), + address: data.get('Addr'), + params: AssetParamsDelta.fromEncodingData(data.get('Params')), + holding: AssetHoldingDelta.fromEncodingData(data.get('Holding')), + }); + } +} + +/** + * VotingData holds participation information + */ +export class VotingData implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'VoteID', // voteID + valueSchema: new FixedLengthByteArraySchema(32), + }, + { + key: 'SelectionID', // selectionID + valueSchema: new FixedLengthByteArraySchema(32), + }, + { + key: 'StateProofID', // stateProofID + valueSchema: new FixedLengthByteArraySchema(64), + }, + { + key: 'VoteFirstValid', // voteFirstValid + valueSchema: new Uint64Schema(), + }, + { + key: 'VoteLastValid', // voteLastValid + valueSchema: new Uint64Schema(), + }, + { + key: 'VoteKeyDilution', // voteKeyDilution + valueSchema: new Uint64Schema(), + }, + ]) + ); + + public voteID: Uint8Array; + public selectionID: Uint8Array; + public stateProofID: Uint8Array; + + public voteFirstValid: bigint; + public voteLastValid: bigint; + public voteKeyDilution: bigint; + + constructor(params: { + voteID: Uint8Array; + selectionID: Uint8Array; + stateProofID: Uint8Array; + voteFirstValid: bigint; + voteLastValid: bigint; + voteKeyDilution: bigint; + }) { + this.voteID = params.voteID; + this.selectionID = params.selectionID; + this.stateProofID = params.stateProofID; + this.voteFirstValid = params.voteFirstValid; + this.voteLastValid = params.voteLastValid; + this.voteKeyDilution = params.voteKeyDilution; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return VotingData.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['VoteID', this.voteID], + ['SelectionID', this.selectionID], + ['StateProofID', this.stateProofID], + ['VoteFirstValid', this.voteFirstValid], + ['VoteLastValid', this.voteLastValid], + ['VoteKeyDilution', this.voteKeyDilution], + ]); + } + + public static fromEncodingData(data: unknown): VotingData { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded VotingData: ${data}`); + } + return new VotingData({ + voteID: data.get('VoteID'), + selectionID: data.get('SelectionID'), + stateProofID: data.get('StateProofID'), + voteFirstValid: data.get('VoteFirstValid'), + voteLastValid: data.get('VoteLastValid'), + voteKeyDilution: data.get('VoteKeyDilution'), + }); + } +} + +/** + * AccountBaseData contains base account info like balance, status and total number of resources + */ +export class AccountBaseData implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'Status', valueSchema: new Uint64Schema() }, // status + { key: 'MicroAlgos', valueSchema: new Uint64Schema() }, // microAlgos + { key: 'RewardsBase', valueSchema: new Uint64Schema() }, // rewardsBase + { + key: 'RewardedMicroAlgos', // rewardedMicroAlgos + valueSchema: new Uint64Schema(), + }, + { key: 'AuthAddr', valueSchema: new AddressSchema() }, // authAddr + { + key: 'IncentiveEligible', // incentiveEligible + valueSchema: new BooleanSchema(), + }, + { + key: 'TotalAppSchema', // totalAppSchema + valueSchema: StateSchema.encodingSchema, + }, + { + key: 'TotalExtraAppPages', // totalExtraAppPages + valueSchema: new Uint64Schema(), + }, + { + key: 'TotalAppParams', // totalAppParams + valueSchema: new Uint64Schema(), + }, + { + key: 'TotalAppLocalStates', // totalAppLocalStates + valueSchema: new Uint64Schema(), + }, + { + key: 'TotalAssetParams', // totalAssetParams + valueSchema: new Uint64Schema(), + }, + { key: 'TotalAssets', valueSchema: new Uint64Schema() }, // totalAssets + { key: 'TotalBoxes', valueSchema: new Uint64Schema() }, // totalBoxes + { + key: 'TotalBoxBytes', // totalBoxBytes + valueSchema: new Uint64Schema(), + }, + { key: 'LastProposed', valueSchema: new Uint64Schema() }, // lastProposed + { + key: 'LastHeartbeat', // lastHeartbeat + valueSchema: new Uint64Schema(), + }, + ]) + ); + + /** + * Account status. Values are: + * * 0: Offline + * * 1: Online + * * 2: NotParticipating + */ + public status: number; + public microAlgos: bigint; + public rewardsBase: bigint; + public rewardedMicroAlgos: bigint; + public authAddr: Address; + public incentiveEligible: boolean; + + /** + * Totals across created globals, and opted in locals. + */ + public totalAppSchema: StateSchema; + /** + * Total number of extra pages across all created apps + */ + public totalExtraAppPages: number; + /** + * Total number of apps this account has created + */ + public totalAppParams: bigint; + /** + * Total number of apps this account is opted into. + */ + public totalAppLocalStates: bigint; + /** + * Total number of assets created by this account + */ + public totalAssetParams: bigint; + /** + * Total of asset creations and optins (i.e. number of holdings) + */ + public totalAssets: bigint; + /** + * Total number of boxes associated to this account + */ + public totalBoxes: bigint; + /** + * Total bytes for this account's boxes. keys _and_ values count + */ + public totalBoxBytes: bigint; + + /** + * The last round that this account proposed the winning block. + */ + public lastProposed: bigint; + /** + * The last round that this account sent a heartbeat to show it was online. + */ + public lastHeartbeat: bigint; + + public constructor(params: { + status: number; + microAlgos: bigint; + rewardsBase: bigint; + rewardedMicroAlgos: bigint; + authAddr: Address; + incentiveEligible: boolean; + totalAppSchema: StateSchema; + totalExtraAppPages: number; + totalAppParams: bigint; + totalAppLocalStates: bigint; + totalAssetParams: bigint; + totalAssets: bigint; + totalBoxes: bigint; + totalBoxBytes: bigint; + lastProposed: bigint; + lastHeartbeat: bigint; + }) { + this.status = params.status; + this.microAlgos = params.microAlgos; + this.rewardsBase = params.rewardsBase; + this.rewardedMicroAlgos = params.rewardedMicroAlgos; + this.authAddr = params.authAddr; + this.incentiveEligible = params.incentiveEligible; + this.totalAppSchema = params.totalAppSchema; + this.totalExtraAppPages = params.totalExtraAppPages; + this.totalAppParams = params.totalAppParams; + this.totalAppLocalStates = params.totalAppLocalStates; + this.totalAssetParams = params.totalAssetParams; + this.totalAssets = params.totalAssets; + this.totalBoxes = params.totalBoxes; + this.totalBoxBytes = params.totalBoxBytes; + this.lastProposed = params.lastProposed; + this.lastHeartbeat = params.lastHeartbeat; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AccountBaseData.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['Status', this.status], + ['MicroAlgos', this.microAlgos], + ['RewardsBase', this.rewardsBase], + ['RewardedMicroAlgos', this.rewardedMicroAlgos], + ['AuthAddr', this.authAddr], + ['IncentiveEligible', this.incentiveEligible], + ['TotalAppSchema', this.totalAppSchema.toEncodingData()], + ['TotalExtraAppPages', this.totalExtraAppPages], + ['TotalAppParams', this.totalAppParams], + ['TotalAppLocalStates', this.totalAppLocalStates], + ['TotalAssetParams', this.totalAssetParams], + ['TotalAssets', this.totalAssets], + ['TotalBoxes', this.totalBoxes], + ['TotalBoxBytes', this.totalBoxBytes], + ['LastProposed', this.lastProposed], + ['LastHeartbeat', this.lastHeartbeat], + ]); + } + + public static fromEncodingData(data: unknown): AccountBaseData { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountBaseData: ${data}`); + } + return new AccountBaseData({ + status: Number(data.get('Status')), + microAlgos: data.get('MicroAlgos'), + rewardsBase: data.get('RewardsBase'), + rewardedMicroAlgos: data.get('RewardedMicroAlgos'), + authAddr: data.get('AuthAddr'), + incentiveEligible: data.get('IncentiveEligible'), + totalAppSchema: StateSchema.fromEncodingData(data.get('TotalAppSchema')), + totalExtraAppPages: Number(data.get('TotalExtraAppPages')), + totalAppParams: data.get('TotalAppParams'), + totalAppLocalStates: data.get('TotalAppLocalStates'), + totalAssetParams: data.get('TotalAssetParams'), + totalAssets: data.get('TotalAssets'), + totalBoxes: data.get('TotalBoxes'), + totalBoxBytes: data.get('TotalBoxBytes'), + lastProposed: data.get('LastProposed'), + lastHeartbeat: data.get('LastHeartbeat'), + }); + } +} + +/** + * AccountData provides per-account data + */ +export class AccountData implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: '', + valueSchema: AccountBaseData.encodingSchema, + embedded: true, + }, + { + key: '', + valueSchema: VotingData.encodingSchema, + embedded: true, + }, + ]) + ); + + public accountBaseData: AccountBaseData; + public votingData: VotingData; + + constructor(params: { + accountBaseData: AccountBaseData; + votingData: VotingData; + }) { + this.accountBaseData = params.accountBaseData; + this.votingData = params.votingData; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AccountData.encodingSchema; + } + + public toEncodingData(): Map { + return combineMaps( + this.accountBaseData.toEncodingData(), + this.votingData.toEncodingData() + ); + } + + public static fromEncodingData(data: unknown): AccountData { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountData: ${data}`); + } + return new AccountData({ + accountBaseData: AccountBaseData.fromEncodingData(data), + votingData: VotingData.fromEncodingData(data), + }); + } +} + +export class BalanceRecord implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'Addr', + valueSchema: new AddressSchema(), + }, + { + key: '', + valueSchema: AccountData.encodingSchema, + embedded: true, + }, + ]) + ); + + public addr: Address; + public accountData: AccountData; + + constructor(params: { addr: Address; accountData: AccountData }) { + this.addr = params.addr; + this.accountData = params.accountData; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return BalanceRecord.encodingSchema; + } + + public toEncodingData(): Map { + return combineMaps( + new Map([['Addr', this.addr]]), + this.accountData.toEncodingData() + ); + } + + public static fromEncodingData(data: unknown): BalanceRecord { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BalanceRecord: ${data}`); + } + return new BalanceRecord({ + addr: data.get('Addr'), + accountData: AccountData.fromEncodingData(data), + }); + } +} + +export class AccountDeltas implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'Accts', // accounts + valueSchema: new ArraySchema(BalanceRecord.encodingSchema), + }, + { + key: 'AppResources', // appResources + valueSchema: new OptionalSchema( + new ArraySchema(AppResourceRecord.encodingSchema) + ), + }, + { + key: 'AssetResources', // assetResources + valueSchema: new OptionalSchema( + new ArraySchema(AssetResourceRecord.encodingSchema) + ), + }, + ]) + ); + + public accounts: BalanceRecord[]; + public appResources: AppResourceRecord[]; + public assetResources: AssetResourceRecord[]; + + constructor(params: { + accounts: BalanceRecord[]; + appResources: AppResourceRecord[]; + assetResources: AssetResourceRecord[]; + }) { + this.accounts = params.accounts; + this.appResources = params.appResources; + this.assetResources = params.assetResources; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AccountDeltas.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['Accts', this.accounts.map((account) => account.toEncodingData())], + [ + 'AppResources', + this.appResources.length === 0 + ? undefined + : this.appResources.map((appResource) => + appResource.toEncodingData() + ), + ], + [ + 'AssetResources', + this.assetResources.length === 0 + ? undefined + : this.assetResources.map((assetResource) => + assetResource.toEncodingData() + ), + ], + ]); + } + + public static fromEncodingData(data: unknown): AccountDeltas { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountDeltas: ${data}`); + } + return new AccountDeltas({ + accounts: (data.get('Accts') ?? []).map(BalanceRecord.fromEncodingData), + appResources: (data.get('AppResources') ?? []).map( + AppResourceRecord.fromEncodingData + ), + assetResources: (data.get('AssetResources') ?? []).map( + AssetResourceRecord.fromEncodingData + ), + }); + } +} + +/** + * A KvValueDelta shows how the Data associated with a key in the kvstore has changed. + */ +export class KvValueDelta implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'Data', + valueSchema: new OptionalSchema(new ByteArraySchema()), + }, + { + key: 'OldData', + valueSchema: new OptionalSchema(new ByteArraySchema()), + }, + ]) + ); + + /** + * Data stores the most recent value (undefined means deleted) + */ + public data?: Uint8Array; + + /** + * OldData stores the previous value (undefined means didn't exist) + */ + public oldData?: Uint8Array; + + constructor(params: { data?: Uint8Array; oldData?: Uint8Array }) { + this.data = params.data; + this.oldData = params.oldData; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return KvValueDelta.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['Data', this.data], + ['OldData', this.oldData], + ]); + } + + public static fromEncodingData(data: unknown): KvValueDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded KvValueDelta: ${data}`); + } + return new KvValueDelta({ + data: data.get('Data'), + oldData: data.get('OldData'), + }); + } +} + +/** + * IncludedTransactions defines the transactions included in a block, their index and last valid round. + */ +export class IncludedTransactions implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'LastValid', + valueSchema: new Uint64Schema(), + }, + { + key: 'Intra', + valueSchema: new Uint64Schema(), + }, + ]) + ); + + public lastValid: bigint; + /** + * The index of the transaction in the block + */ + public intra: number; + + constructor(params: { lastValid: bigint; intra: number }) { + this.lastValid = params.lastValid; + this.intra = params.intra; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return IncludedTransactions.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['LastValid', this.lastValid], + ['Intra', this.intra], + ]); + } + + public static fromEncodingData(data: unknown): IncludedTransactions { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded IncludedTransactions: ${data}`); + } + return new IncludedTransactions({ + lastValid: data.get('LastValid'), + intra: Number(data.get('Intra')), + }); + } +} + +/** + * ModifiedCreatable represents a change to a single creatable state + */ +export class ModifiedCreatable implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'Ctype', // creatableType + valueSchema: new Uint64Schema(), + }, + { + key: 'Created', // created + valueSchema: new BooleanSchema(), + }, + { + key: 'Creator', // creator + valueSchema: new AddressSchema(), + }, + { + key: 'Ndeltas', // ndeltas + valueSchema: new Uint64Schema(), + }, + ]) + ); + + /** + * Type of the creatable. The values are: + * * 0: Asset + * * 1: Application + */ + public creatableType: number; + + /** + * Created if true, deleted if false + */ + public created: boolean; + + /** + * creator of the app/asset + */ + public creator: Address; + + /** + * Keeps track of how many times this app/asset appears in accountUpdates.creatableDeltas + */ + public ndeltas: number; + + public constructor(params: { + creatableType: number; + created: boolean; + creator: Address; + ndeltas: number; + }) { + this.creatableType = params.creatableType; + this.created = params.created; + this.creator = params.creator; + this.ndeltas = params.ndeltas; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return ModifiedCreatable.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['Ctype', this.creatableType], + ['Created', this.created], + ['Creator', this.creator], + ['Ndeltas', this.ndeltas], + ]); + } + + public static fromEncodingData(data: unknown): ModifiedCreatable { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ModifiedCreatable: ${data}`); + } + return new ModifiedCreatable({ + creatableType: Number(data.get('Ctype')), + created: data.get('Created'), + creator: data.get('Creator'), + ndeltas: Number(data.get('Ndeltas')), + }); + } +} + +/** + * AlgoCount represents a total of algos of a certain class of accounts (split up by their Status value). + */ +export class AlgoCount implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'mon', valueSchema: new Uint64Schema() }, // money + { key: 'rwd', valueSchema: new Uint64Schema() }, // rewardUnits + ]) + ); + + /** + * Sum of algos of all accounts in this class. + */ + public money: bigint; + + /** + * Total number of whole reward units in accounts. + */ + public rewardUnits: bigint; + + constructor(params: { money: bigint; rewardUnits: bigint }) { + this.money = params.money; + this.rewardUnits = params.rewardUnits; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AlgoCount.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['mon', this.money], + ['rwd', this.rewardUnits], + ]); + } + + public static fromEncodingData(data: unknown): AlgoCount { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AlgoCount: ${data}`); + } + return new AlgoCount({ + money: data.get('mon'), + rewardUnits: data.get('rwd'), + }); + } +} + +/** + * AccountTotals represents the totals of algos in the system grouped by different account status values. + */ +export class AccountTotals implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'online', valueSchema: AlgoCount.encodingSchema }, // online + { key: 'offline', valueSchema: AlgoCount.encodingSchema }, // offline + { key: 'notpart', valueSchema: AlgoCount.encodingSchema }, // notParticipating + { key: 'rwdlvl', valueSchema: new Uint64Schema() }, // rewardsLevel + ]) + ); + + public online: AlgoCount; + public offline: AlgoCount; + public notParticipating: AlgoCount; + + /** + * Total number of algos received per reward unit since genesis + */ + public rewardsLevel: bigint; + + constructor(params: { + online: AlgoCount; + offline: AlgoCount; + notParticipating: AlgoCount; + rewardsLevel: bigint; + }) { + this.online = params.online; + this.offline = params.offline; + this.notParticipating = params.notParticipating; + this.rewardsLevel = params.rewardsLevel; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return AccountTotals.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['online', this.online.toEncodingData()], + ['offline', this.offline.toEncodingData()], + ['notpart', this.notParticipating.toEncodingData()], + ['rwdlvl', this.rewardsLevel], + ]); + } + + public static fromEncodingData(data: unknown): AccountTotals { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountTotals: ${data}`); + } + return new AccountTotals({ + online: AlgoCount.fromEncodingData(data.get('online')), + offline: AlgoCount.fromEncodingData(data.get('offline')), + notParticipating: AlgoCount.fromEncodingData(data.get('notpart')), + rewardsLevel: data.get('rwdlvl'), + }); + } +} + +/** + * LedgerStateDelta describes the delta between a given round to the previous round + */ +export class LedgerStateDelta implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'Accts', // accounts + valueSchema: AccountDeltas.encodingSchema, + }, + { + key: 'KvMods', // kvMods + valueSchema: new OptionalSchema( + new SpecialCaseBinaryStringMapSchema(KvValueDelta.encodingSchema) + ), + }, + { + key: 'Txids', // txids + valueSchema: new ByteArrayMapSchema( + IncludedTransactions.encodingSchema + ), + }, + { + key: 'Txleases', // txleases + // Note: because txleases is currently just an UntypedSchema and we are expected to decode + // null values for this field, we use OptionalSchema to coerce null values to undefined so + // that the values can be properly omitted during encoding. + valueSchema: new OptionalSchema(new UntypedSchema()), + }, + { + key: 'Creatables', // creatables + valueSchema: new OptionalSchema( + new Uint64MapSchema(ModifiedCreatable.encodingSchema) + ), + }, + { + key: 'Hdr', // blockHeader + valueSchema: BlockHeader.encodingSchema, + }, + { + key: 'StateProofNext', // stateProofNext + valueSchema: new Uint64Schema(), + }, + { + key: 'PrevTimestamp', // prevTimestamp + valueSchema: new Uint64Schema(), + }, + { + key: 'Totals', // totals + valueSchema: AccountTotals.encodingSchema, + }, + ]) + ); + + /** + * modified new accounts + */ + public accounts: AccountDeltas; + + /** + * modified kv pairs (nil == delete) + */ + public kvMods: Map; + + /** + * new Txids for the txtail and TxnCounter, mapped to txn.LastValid + */ + public txids: Map; + + // TODO: properly support txleases once we are able to decode msgpack maps with object keys. + /** + * new txleases for the txtail mapped to expiration + */ + public txleases: UntypedValue; + + /** + * new creatables creator lookup table + */ + public creatables: Map; + + /** + * new block header + */ + public blockHeader: BlockHeader; + + /** + * StateProofNext represents modification on StateProofNextRound field in the block header. If the block contains + * a valid state proof transaction, this field will contain the next round for state proof. + * otherwise it will be set to 0. + */ + public stateProofNext: bigint; + + /** + * previous block timestamp + */ + public prevTimestamp: bigint; + + /** + * The account totals reflecting the changes in this StateDelta object. + */ + public totals: AccountTotals; + + public constructor(params: { + accounts: AccountDeltas; + kvMods: Map; + txids: Map; + txleases: UntypedValue; + creatables: Map; + blockHeader: BlockHeader; + stateProofNext: bigint; + prevTimestamp: bigint; + totals: AccountTotals; + }) { + this.accounts = params.accounts; + this.kvMods = params.kvMods; + this.txids = params.txids; + this.txleases = params.txleases; + this.creatables = params.creatables; + this.blockHeader = params.blockHeader; + this.stateProofNext = params.stateProofNext; + this.prevTimestamp = params.prevTimestamp; + this.totals = params.totals; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return LedgerStateDelta.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['Accts', this.accounts.toEncodingData()], + [ + 'KvMods', + this.kvMods.size === 0 + ? undefined + : convertMap(this.kvMods, (key, value) => [ + key, + value.toEncodingData(), + ]), + ], + [ + 'Txids', + convertMap(this.txids, (key, value) => [key, value.toEncodingData()]), + ], + ['Txleases', this.txleases.toEncodingData()], + [ + 'Creatables', + this.creatables.size === 0 + ? undefined + : convertMap(this.creatables, (key, value) => [ + key, + value.toEncodingData(), + ]), + ], + ['Hdr', this.blockHeader.toEncodingData()], + ['StateProofNext', this.stateProofNext], + ['PrevTimestamp', this.prevTimestamp], + ['Totals', this.totals.toEncodingData()], + ]); + } + + public static fromEncodingData(data: unknown): LedgerStateDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded LedgerStateDelta: ${data}`); + } + return new LedgerStateDelta({ + accounts: AccountDeltas.fromEncodingData(data.get('Accts')), + kvMods: convertMap( + (data.get('KvMods') ?? new Map()) as Map, + (key, value) => [key, KvValueDelta.fromEncodingData(value)] + ), + txids: convertMap( + data.get('Txids') as Map, + (key, value) => [key, IncludedTransactions.fromEncodingData(value)] + ), + txleases: UntypedValue.fromEncodingData(data.get('Txleases')), + creatables: convertMap( + (data.get('Creatables') ?? new Map()) as Map, + (key, value) => [key, ModifiedCreatable.fromEncodingData(value)] + ), + blockHeader: BlockHeader.fromEncodingData(data.get('Hdr')), + stateProofNext: data.get('StateProofNext'), + prevTimestamp: data.get('PrevTimestamp'), + totals: AccountTotals.fromEncodingData(data.get('Totals')), + }); + } +} diff --git a/src/types/transactions/application.ts b/src/types/transactions/application.ts deleted file mode 100644 index 701240f25..000000000 --- a/src/types/transactions/application.ts +++ /dev/null @@ -1,111 +0,0 @@ -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 deleted file mode 100644 index 00db7c714..000000000 --- a/src/types/transactions/asset.ts +++ /dev/null @@ -1,108 +0,0 @@ -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 index fb357a6f4..277c9a3dd 100644 --- a/src/types/transactions/base.ts +++ b/src/types/transactions/base.ts @@ -1,7 +1,8 @@ +import { Address } from '../../encoding/address.js'; +import { StateProof, StateProofMessage } from '../../stateproof.js'; + /** * Enum for application transaction types. - * - * The full list is available at https://developer.algorand.org/docs/reference/transactions/ */ export enum TransactionType { /** @@ -39,6 +40,11 @@ export enum TransactionType { stpf = 'stpf', } +/** + * Check if a string is a valid transaction type + * @param s - string to check + * @returns true if s is a valid transaction type + */ export function isTransactionType(s: string): s is TransactionType { return ( s === TransactionType.pay || @@ -95,7 +101,25 @@ export enum OnApplicationComplete { } /** - * A dict holding common-to-all-txns arguments + * Check if a value is a valid OnApplicationComplete value + * @param v - value to check + * @returns true if v is a valid OnApplicationComplete value + */ +export function isOnApplicationComplete( + v: unknown +): v is OnApplicationComplete { + return ( + v === OnApplicationComplete.NoOpOC || + v === OnApplicationComplete.OptInOC || + v === OnApplicationComplete.CloseOutOC || + v === OnApplicationComplete.ClearStateOC || + v === OnApplicationComplete.UpdateApplicationOC || + v === OnApplicationComplete.DeleteApplicationOC + ); +} + +/** + * Contains parameters relevant to the creation of a new transaction in a specific network at a specific time */ export interface SuggestedParams { /** @@ -107,36 +131,34 @@ export interface SuggestedParams { /** * Integer fee per byte, in microAlgos. For a flat fee, set flatFee to true */ - fee: number; + fee: number | bigint; + + /** + * Minimum fee (not per byte) required for the transaction to be confirmed + */ + minFee: number | bigint; /** * First protocol round on which this txn is valid */ - firstRound: number; + firstValid: number | bigint; /** * Last protocol round on which this txn is valid */ - lastRound: number; + lastValid: number | bigint; /** * Specifies genesis ID of network in use */ - genesisID: string; + genesisID?: string; /** * Specifies hash genesis block of network in use */ - genesisHash: string; + genesisHash?: Uint8Array; } -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 */ @@ -144,7 +166,7 @@ export interface BoxReference { /** * A unique application index */ - appIndex: number; + appIndex: number | bigint; /** * Name of box to reference @@ -153,211 +175,245 @@ export interface BoxReference { } /** - * A full list of all available transaction parameters + * Contains payment transaction parameters. * * The full documentation is available at: - * https://developer.algorand.org/docs/reference/transactions/#common-fields-header-and-type + * https://developer.algorand.org/docs/get-details/transactions/transactions/#payment-transaction */ -export interface TransactionParams { +export interface PaymentTransactionParams { /** - * String representation of Algorand address of sender + * Algorand address of recipient */ - from: string; + receiver: string | Address; /** - * String representation of Algorand address of recipient + * Integer amount to send, in microAlgos. Must be nonnegative. */ - to: string; + amount: number | bigint; /** - * Integer fee per byte, in microAlgos. For a flat fee, set flatFee to true + * Optional, indicates the sender will close their account and the remaining balance will transfer + * to this account */ - fee: number; + closeRemainderTo?: string | Address; +} +/** + * Contains key registration transaction parameters + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#key-registration-transaction + */ +export interface KeyRegistrationTransactionParams { /** - * Integer amount to send + * 32-byte voting key. For key deregistration, leave undefined */ - amount: number | bigint; + voteKey?: Uint8Array | string; /** - * Integer first protocol round on which this txn is valid + * 32-byte selection key. For key deregistration, leave undefined */ - firstRound: number; + selectionKey?: Uint8Array | string; /** - * Integer last protocol round on which this txn is valid + * 64-byte state proof key. For key deregistration, leave undefined */ - lastRound: number; + stateProofKey?: Uint8Array | string; /** - * Arbitrary data for sender to store + * First round on which voting keys are valid */ - note?: Uint8Array; + voteFirst?: number | bigint; /** - * Specifies genesis ID of network in use + * Last round on which voting keys are valid */ - genesisID: string; + voteLast?: number | bigint; /** - * Specifies hash genesis block of network in use + * The dilution fo the 2-level participation key */ - genesisHash: string; + voteKeyDilution?: number | bigint; /** - * Lease a transaction. The sender cannot send another txn with that same lease until the last round of original txn has passed + * Set this value to true to mark this account as nonparticipating. + * + * All new Algorand accounts are participating by default. This means they earn rewards. */ - lease?: Uint8Array; + nonParticipation?: boolean; +} +/** + * Contains asset configuration transaction parameters. + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#asset-configuration-transaction + */ +export interface AssetConfigurationTransactionParams { /** - * Close out remaining account balance to this account + * Asset index uniquely specifying the asset */ - closeRemainderTo?: string; + assetIndex?: number | bigint; /** - * Voting key bytes. For key deregistration, leave undefined + * Total supply of the asset */ - voteKey: Uint8Array | string; + total?: number | bigint; /** - *Selection key bytes. For key deregistration, leave undefined + * Integer number of decimals for asset unit calcuation */ - selectionKey: Uint8Array | string; + decimals?: number | bigint; /** - * State proof key bytes. For key deregistration, leave undefined + * Whether asset accounts should default to being frozen */ - stateProofKey: Uint8Array | string; + defaultFrozen?: boolean; /** - * First round on which voteKey is valid + * The Algorand address in charge of reserve, freeze, clawback, destruction, etc. */ - voteFirst: number; + manager?: string | Address; /** - * Last round on which voteKey is valid + * The Algorand address representing asset reserve */ - voteLast: number; + reserve?: string | Address; /** - * The dilution fo the 2-level participation key + * The Algorand address with power to freeze/unfreeze asset holdings */ - voteKeyDilution: number; + freeze?: string | Address; /** - * Asset index uniquely specifying the asset + * The Algorand address with power to revoke asset holdings */ - assetIndex: number; + clawback?: string | Address; /** - * Total supply of the asset + * Unit name for this asset */ - assetTotal: number | bigint; + unitName?: string; /** - * Integer number of decimals for asset unit calcuation + * Name for this asset */ - assetDecimals: number; + assetName?: string; /** - * Whether asset accounts should default to being frozen + * URL relating to this asset */ - assetDefaultFrozen: boolean; + assetURL?: string; /** - * String representation of Algorand address in charge of reserve, freeze, clawback, destruction, etc. + * Uint8Array containing a hash commitment with respect to the asset. Must be exactly 32 bytes long. */ - assetManager?: string; + assetMetadataHash?: Uint8Array; +} +/** + * Contains asset transfer transaction parameters. + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#asset-transfer-transaction + */ +export interface AssetTransferTransactionParams { /** - * String representation of Algorand address representing asset reserve + * Asset index uniquely specifying the asset */ - assetReserve?: string; + assetIndex: number | bigint; /** - * String representation of Algorand address with power to freeze/unfreeze asset holdings + * String representation of Algorand address – if provided, and if "sender" is + * the asset's revocation manager, then deduct from "assetSender" rather than "sender" */ - assetFreeze?: string; + assetSender?: string | Address; /** - * String representation of Algorand address with power to revoke asset holdings + * The Algorand address of recipient */ - assetClawback?: string; + receiver: string | Address; /** - * Unit name for this asset - */ - assetUnitName?: string; - /** - * Name for this asset + * Integer amount to send */ - assetName?: string; + amount: number | bigint; /** - * URL relating to this asset + * Close out remaining asset balance of the sender to this account */ - assetURL?: string; + closeRemainderTo?: string | Address; +} +/** + * Contains asset freeze transaction parameters. + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#asset-freeze-transaction + */ +export interface AssetFreezeTransactionParams { /** - * Uint8Array or UTF-8 string representation of a hash commitment with respect to the asset. Must be exactly 32 bytes long. + * Asset index uniquely specifying the asset */ - assetMetadataHash?: Uint8Array | string; + assetIndex: number | bigint; /** - * String representation of Algorand address being frozen or unfrozen + * Algorand address being frozen or unfrozen */ - freezeAccount: string; + freezeTarget: string | Address; /** * 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; + frozen: boolean; +} +/** + * Contains application call transaction parameters. + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#application-call-transaction + */ +export interface ApplicationCallTransactionParams { /** - * A unique application index + * A unique application ID */ - appIndex: number; + appIndex: number | bigint; /** * What application should do once the program has been run */ - appOnComplete: OnApplicationComplete; + onComplete: OnApplicationComplete; /** * Restricts number of ints in per-user local state */ - appLocalInts: number; + numLocalInts?: number | bigint; /** * Restricts number of byte slices in per-user local state */ - appLocalByteSlices: number; + numLocalByteSlices?: number | bigint; /** * Restricts number of ints in global state */ - appGlobalInts: number; + numGlobalInts?: number | bigint; /** * Restricts number of byte slices in global state */ - appGlobalByteSlices: number; + numGlobalByteSlices?: number | bigint; /** * The compiled TEAL that approves a transaction */ - appApprovalProgram: Uint8Array; + approvalProgram?: Uint8Array; /** * The compiled TEAL program that runs when clearing state */ - appClearProgram: Uint8Array; + clearProgram?: Uint8Array; /** * Array of Uint8Array, any additional arguments to the application @@ -367,69 +423,121 @@ export interface TransactionParams { /** * Array of Address strings, any additional accounts to supply to the application */ - appAccounts?: string[]; + accounts?: Array; /** * Array of int, any other apps used by the application, identified by index */ - appForeignApps?: number[]; + foreignApps?: Array; /** * Array of int, any assets used by the application, identified by index */ - appForeignAssets?: number[]; + foreignAssets?: Array; + + /** + * Int representing extra pages of memory to rent during an application create transaction. + */ + extraPages?: number | bigint; + + /** + * A grouping of the app ID and name of the box in an Uint8Array + */ + boxes?: BoxReference[]; +} + +/** + * Contains state proof transaction parameters. + */ +export interface StateProofTransactionParams { + /* + * Uint64 identifying a particular configuration of state proofs. + */ + stateProofType?: number | bigint; + + /** + * The state proof. + */ + stateProof?: StateProof; + /** + * The state proof message. + */ + message?: StateProofMessage; +} + +/** + * A full list of all available transaction parameters + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#common-fields-header-and-type + */ +export interface TransactionParams { /** * Transaction type */ - type?: TransactionType; + type: TransactionType; /** - * Set this to true to specify fee as microalgos-per-txn. + * Algorand address of sender + */ + sender: string | Address; + + /** + * Optional, arbitrary data to be included in the transaction's note field + */ + note?: Uint8Array; + + /** + * Optional, 32-byte lease to associate with this transaction. * - * If the final calculated fee is lower than the protocol minimum fee, the fee will be increased to match the minimum + * The sender cannot send another transaction with the same lease until the last round of original + * transaction has passed. */ - flatFee?: boolean; + lease?: Uint8Array; /** - * A dict holding common-to-all-txns arguments + * The Algorand address that will be used to authorize all future transactions from the sender, if provided. + */ + rekeyTo?: string | Address; + + /** + * Suggested parameters relevant to the network that will accept this transaction */ suggestedParams: SuggestedParams; /** - * String representation of the Algorand address that will be used to authorize all future transactions + * Payment transaction parameters. Only set if type is TransactionType.pay */ - reKeyTo?: string; + paymentParams?: PaymentTransactionParams; /** - * Set this value to true to mark this account as nonparticipating. - * - * All new Algorand accounts are participating by default. This means they earn rewards. + * Key registration transaction parameters. Only set if type is TransactionType.keyreg */ - nonParticipation?: boolean; + keyregParams?: KeyRegistrationTransactionParams; /** - * Int representing extra pages of memory to rent during an application create transaction. + * Asset configuration transaction parameters. Only set if type is TransactionType.acfg */ - extraPages?: number; + assetConfigParams?: AssetConfigurationTransactionParams; /** - * A grouping of the app ID and name of the box in an Uint8Array + * Asset transfer transaction parameters. Only set if type is TransactionType.axfer */ - boxes?: BoxReference[]; + assetTransferParams?: AssetTransferTransactionParams; - /* - * Uint64 identifying a particular configuration of state proofs. + /** + * Asset freeze transaction parameters. Only set if type is TransactionType.afrz */ - stateProofType?: number | bigint; + assetFreezeParams?: AssetFreezeTransactionParams; /** - * Byte array containing the state proof. + * Application call transaction parameters. Only set if type is TransactionType.appl */ - stateProof?: Uint8Array; + appCallParams?: ApplicationCallTransactionParams; /** - * Byte array containing the state proof message. + * State proof transaction parameters. Only set if type is TransactionType.stpf */ - stateProofMessage?: Uint8Array; + stateProofParams?: StateProofTransactionParams; } diff --git a/src/types/transactions/builder.ts b/src/types/transactions/builder.ts deleted file mode 100644 index 0a40bb505..000000000 --- a/src/types/transactions/builder.ts +++ /dev/null @@ -1,69 +0,0 @@ -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 index 7864e4e7d..1279a2160 100644 --- a/src/types/transactions/encoded.ts +++ b/src/types/transactions/encoded.ts @@ -1,336 +1,12 @@ -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; -} +import { + NamedMapSchema, + FixedLengthByteArraySchema, + Uint64Schema, + ArraySchema, + OptionalSchema, + allOmitEmpty, +} from '../../encoding/schema/index.js'; +import { ensureSafeUnsignedInteger } from '../../utils/utils.js'; export interface EncodedSubsig { /** @@ -344,6 +20,42 @@ export interface EncodedSubsig { s?: Uint8Array; } +export const ENCODED_SUBSIG_SCHEMA = new NamedMapSchema( + allOmitEmpty([ + { + key: 'pk', + valueSchema: new FixedLengthByteArraySchema(32), + }, + { + key: 's', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(64)), + }, + ]) +); + +export function encodedSubsigFromEncodingData(data: unknown): EncodedSubsig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded EncodedSubsig: ${data}`); + } + const subsig: EncodedSubsig = { + pk: data.get('pk'), + }; + if (data.get('s')) { + subsig.s = data.get('s'); + } + return subsig; +} + +export function encodedSubsigToEncodingData( + subsig: EncodedSubsig +): Map { + const data = new Map([['pk', subsig.pk]]); + if (subsig.s) { + data.set('s', subsig.s); + } + return data; +} + /** * A rough structure for the encoded multi signature transaction object. * Every property is labelled with its associated `MultisigMetadata` type property @@ -365,44 +77,42 @@ export interface EncodedMultisig { subsig: EncodedSubsig[]; } -export interface EncodedLogicSig { - l: Uint8Array; - arg?: Uint8Array[]; - sig?: Uint8Array; - msig?: EncodedMultisig; -} - -export interface EncodedLogicSigAccount { - lsig: EncodedLogicSig; - sigkey?: Uint8Array; +export const ENCODED_MULTISIG_SCHEMA = new NamedMapSchema( + allOmitEmpty([ + { + key: 'v', + valueSchema: new Uint64Schema(), + }, + { + key: 'thr', + valueSchema: new Uint64Schema(), + }, + { + key: 'subsig', + valueSchema: new ArraySchema(ENCODED_SUBSIG_SCHEMA), + }, + ]) +); + +export function encodedMultiSigFromEncodingData( + data: unknown +): EncodedMultisig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded EncodedMultiSig: ${data}`); + } + return { + v: ensureSafeUnsignedInteger(data.get('v')), + thr: ensureSafeUnsignedInteger(data.get('thr')), + subsig: data.get('subsig').map(encodedSubsigFromEncodingData), + }; } -/** - * 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; +export function encodedMultiSigToEncodingData( + msig: EncodedMultisig +): Map { + return new Map([ + ['v', msig.v], + ['thr', msig.thr], + ['subsig', msig.subsig.map(encodedSubsigToEncodingData)], + ]); } diff --git a/src/types/transactions/index.ts b/src/types/transactions/index.ts index fc4d8543b..de3e406ca 100644 --- a/src/types/transactions/index.ts +++ b/src/types/transactions/index.ts @@ -1,72 +1,2 @@ -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; +export * from './base.js'; +export * from './encoded.js'; diff --git a/src/types/transactions/keyreg.ts b/src/types/transactions/keyreg.ts deleted file mode 100644 index 8856849c6..000000000 --- a/src/types/transactions/keyreg.ts +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index 5d5ea3425..000000000 --- a/src/types/transactions/payment.ts +++ /dev/null @@ -1,14 +0,0 @@ -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 deleted file mode 100644 index 7f2e1167f..000000000 --- a/src/types/transactions/stateproof.ts +++ /dev/null @@ -1,17 +0,0 @@ -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 index 83e7e77f5..ff28e48e5 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -5,8 +5,8 @@ export type Expand = T extends (...args: infer A) => infer R ? (...args: Expand) => Expand : T extends infer O - ? { [K in keyof O]: O[K] } - : never; + ? { [K in keyof O]: O[K] } + : never; /** * Same as TypeScript's Pick, but will distribute the Pick over unions @@ -59,5 +59,5 @@ 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 index 620644b3a..65bedde3d 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,32 +1,30 @@ import JSONbigWithoutConfig from 'json-bigint'; -import IntDecoding from '../types/intDecoding'; +import IntDecoding from '../types/intDecoding.js'; -const JSONbig = JSONbigWithoutConfig({ useNativeBigInt: true, strict: true }); +const JSONbig = JSONbigWithoutConfig({ + useNativeBigInt: true, + strict: true, +}); -export interface JSONOptions { - intDecoding?: IntDecoding; +export interface ParseJSONOptions { + 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. + * @param options - Configures how integers in this JSON string will be decoded. See the + * `IntDecoding` enum for more details. */ -export function parseJSON(str: string, options?: JSONOptions) { - const intDecoding = - options && options.intDecoding ? options.intDecoding : IntDecoding.DEFAULT; +export function parseJSON(str: string, { intDecoding }: ParseJSONOptions) { + if ( + intDecoding !== IntDecoding.SAFE && + intDecoding !== IntDecoding.UNSAFE && + intDecoding !== IntDecoding.BIGINT && + intDecoding !== IntDecoding.MIXED + ) { + throw new Error(`Invalid intDecoding option: ${intDecoding}`); + } return JSONbig.parse(str, (_, value) => { if ( value != null && @@ -39,14 +37,14 @@ export function parseJSON(str: string, options?: JSONOptions) { } if (typeof value === 'bigint') { - if (intDecoding === 'safe' && value > Number.MAX_SAFE_INTEGER) { + if (intDecoding === 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) + intDecoding === IntDecoding.BIGINT || + (intDecoding === IntDecoding.MIXED && value > Number.MAX_SAFE_INTEGER) ) { return value; } @@ -56,7 +54,7 @@ export function parseJSON(str: string, options?: JSONOptions) { } if (typeof value === 'number') { - if (intDecoding === 'bigint' && Number.isInteger(value)) { + if (intDecoding === IntDecoding.BIGINT && Number.isInteger(value)) { return BigInt(value); } } @@ -65,10 +63,29 @@ export function parseJSON(str: string, options?: JSONOptions) { }); } +/** + * Converts a JavaScript value to a JavaScript Object Notation (JSON) string. + * + * This functions differs from the built-in JSON.stringify in that it supports serializing BigInts. + * + * This function takes the same arguments as the built-in JSON.stringify function. + * + * @param value - A JavaScript value, usually an object or array, to be converted. + * @param replacer - A function that transforms the results. + * @param space - Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. + */ +export function stringifyJSON( + value: any, + replacer?: (this: any, key: string, value: any) => any, + space?: string | number +): string { + return JSONbig.stringify(value, replacer, space); +} + /** * ArrayEqual takes two arrays and return true if equal, false otherwise */ -export function arrayEqual(a: ArrayLike, b: ArrayLike) { +export function arrayEqual(a: ArrayLike, b: ArrayLike): boolean { if (a.length !== b.length) { return false; } @@ -134,3 +151,57 @@ export function isReactNative() { } return false; } + +export function ensureSafeInteger(value: unknown): number { + if (typeof value === 'undefined') { + throw new Error('Value is undefined'); + } + if (typeof value === 'bigint') { + if ( + value > BigInt(Number.MAX_SAFE_INTEGER) || + value < BigInt(Number.MIN_SAFE_INTEGER) + ) { + throw new Error(`BigInt value ${value} is not a safe integer`); + } + return Number(value); + } + if (typeof value === 'number') { + if (Number.isSafeInteger(value)) { + return value; + } + throw new Error(`Value ${value} is not a safe integer`); + } + throw new Error(`Unexpected type ${typeof value}, ${value}`); +} + +export function ensureSafeUnsignedInteger(value: unknown): number { + const intValue = ensureSafeInteger(value); + if (intValue < 0) { + throw new Error(`Value ${intValue} is negative`); + } + return intValue; +} + +export function ensureBigInt(value: unknown): bigint { + if (typeof value === 'undefined') { + throw new Error('Value is undefined'); + } + if (typeof value === 'bigint') { + return value; + } + if (typeof value === 'number') { + if (!Number.isSafeInteger(value)) { + throw new Error(`Value ${value} is not a safe integer`); + } + return BigInt(value); + } + throw new Error(`Unexpected type ${typeof value}, ${value}`); +} + +export function ensureUint64(value: unknown): bigint { + const bigIntValue = ensureBigInt(value); + if (bigIntValue < 0 || bigIntValue > BigInt('0xffffffffffffffff')) { + throw new Error(`Value ${bigIntValue} is not a uint64`); + } + return bigIntValue; +} diff --git a/src/wait.ts b/src/wait.ts index 5ade09d5e..4b28e867d 100644 --- a/src/wait.ts +++ b/src/wait.ts @@ -1,4 +1,5 @@ -import Algodv2 from './client/v2/algod/algod'; +import { AlgodClient } from './client/v2/algod/algod.js'; +import { PendingTransactionResponse } from './client/v2/algod/models/types.js'; /** * Wait until a transaction has been confirmed or rejected by the network, or @@ -10,10 +11,10 @@ import Algodv2 from './client/v2/algod/algod'; * `pendingTransactionInformation` call for the confirmed transaction. */ export async function waitForConfirmation( - client: Algodv2, + client: AlgodClient, txid: string, waitRounds: number -): Promise> { +): Promise { // Wait until the transaction is confirmed or rejected, or until 'waitRounds' // number of rounds have passed. @@ -21,7 +22,7 @@ export async function waitForConfirmation( if (typeof status === 'undefined') { throw new Error('Unable to get node status'); } - const startRound = status['last-round'] + 1; + const startRound = Number(status.lastRound) + 1; let currentRound = startRound; /* eslint-disable no-await-in-loop */ @@ -30,15 +31,15 @@ export async function waitForConfirmation( try { const pendingInfo = await client.pendingTransactionInformation(txid).do(); - if (pendingInfo['confirmed-round']) { + if (pendingInfo.confirmedRound) { // Got the completed Transaction return pendingInfo; } - if (pendingInfo['pool-error']) { + if (pendingInfo.poolError) { // If there was a pool error, then the transaction has been rejected poolError = true; - throw new Error(`Transaction Rejected: ${pendingInfo['pool-error']}`); + throw new Error(`Transaction Rejected: ${pendingInfo.poolError}`); } } catch (err) { // Ignore errors from PendingTransactionInformation, since it may return 404 if the algod diff --git a/tests/1.Mnemonics_test.js b/tests/1.Mnemonics_test.ts similarity index 71% rename from tests/1.Mnemonics_test.js rename to tests/1.Mnemonics_test.ts index de8b1835a..305b6849f 100644 --- a/tests/1.Mnemonics_test.js +++ b/tests/1.Mnemonics_test.ts @@ -1,8 +1,8 @@ /* eslint-env mocha */ -const assert = require('assert'); -const algosdk = require('../src/index'); -const nacl = require('../src/nacl/naclWrappers'); -const passphrase = require('../src/mnemonic/mnemonic'); +import assert from 'assert'; +import algosdk from '../src/index.js'; +import { randomBytes } from '../src/nacl/naclWrappers.js'; +import * as passphrase from '../src/mnemonic/mnemonic.js'; describe('#mnemonic', () => { it('should return a 25 words passphrase', () => { @@ -13,7 +13,7 @@ describe('#mnemonic', () => { it('should be able to be converted back to key', () => { for (let i = 0; i < 50; i++) { - const seed = nacl.randomBytes(32); + const seed = randomBytes(32); const mn = passphrase.mnemonicFromSeed(seed); const keyTarget = passphrase.seedFromMnemonic(mn); const truncatedKey = new Uint8Array(seed); @@ -32,37 +32,28 @@ describe('#mnemonic', () => { }); it('should fail with the wrong checksum', () => { - const seed = nacl.randomBytes(32); + const seed = 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 - ); + assert.throws(() => { + passphrase.seedFromMnemonic(mn); + }, new Error(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 - ); + assert.throws(() => { + passphrase.seedFromMnemonic(mn); + }, new Error(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 - ); + assert.throws(() => { + passphrase.seedFromMnemonic(mn); + }, new Error(passphrase.NOT_IN_WORDS_LIST_ERROR_MSG)); }); }); diff --git a/tests/10.ABI.ts b/tests/10.ABI.ts index fff072f10..1d931e516 100644 --- a/tests/10.ABI.ts +++ b/tests/10.ABI.ts @@ -8,7 +8,8 @@ import { generateAccount, makeBasicAccountTransactionSigner, makeMultiSigAccountTransactionSigner, - makePaymentTxnWithSuggestedParams, + makePaymentTxnWithSuggestedParamsFromObject, + base64ToBytes, } from '../src'; import { ABIAddressType, @@ -265,20 +266,7 @@ describe('ABI encoding', () => { new ABIStringType(), 'What’s new', new Uint8Array([ - 0, - 12, - 87, - 104, - 97, - 116, - 226, - 128, - 153, - 115, - 32, - 110, - 101, - 119, + 0, 12, 87, 104, 97, 116, 226, 128, 153, 115, 32, 110, 101, 119, ]) ), newTestCase( @@ -319,30 +307,7 @@ describe('ABI encoding', () => { 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, + 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( @@ -491,12 +456,16 @@ describe('ABI encoding', () => { const method = ABIMethod.fromSignature('add(application)uint8'); const account = generateAccount(); const sender = 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA'; + const genesisHash = new Uint8Array(32); + genesisHash[0] = 1; + genesisHash[1] = 2; const sp = { + minFee: 1000, fee: 1000, - firstRound: 1, - lastRound: 1001, + firstValid: 1, + lastValid: 1001, genesisID: 'gi', - genesisHash: 'gh', + genesisHash, }; const foreignAcct = 'E4VCHISDQPLIZWMALIGNPK2B2TERPDMR64MZJXE3UL75MUDXZMADX5OWXM'; @@ -527,15 +496,18 @@ describe('ABI encoding', () => { 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.applicationCall?.foreignApps?.length, 2); + assert.deepStrictEqual(txn.applicationCall?.foreignApps[0], 1n); + assert.deepStrictEqual(txn.applicationCall?.foreignApps[1], 2n); - assert.deepStrictEqual(txn.appForeignAssets?.length, 1); - assert.deepStrictEqual(txn.appForeignAssets[0], 124); + assert.deepStrictEqual(txn.applicationCall?.foreignAssets?.length, 1); + assert.deepStrictEqual(txn.applicationCall?.foreignAssets[0], 124n); - assert.deepStrictEqual(txn.appAccounts?.length, 1); - assert.deepStrictEqual(txn.appAccounts[0], decodeAddress(foreignAcct)); + assert.deepStrictEqual(txn.applicationCall?.accounts?.length, 1); + assert.deepStrictEqual( + txn.applicationCall?.accounts[0], + decodeAddress(foreignAcct) + ); }); it('should accept at least one signature in the multisig', () => { @@ -553,21 +525,22 @@ describe('ABI encoding', () => { // Create a transaction const suggestedParams = { - genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + genesisHash: base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), genesisID: '', - firstRound: 0, - lastRound: 1000, + firstValid: 0, + lastValid: 1000, fee: 1000, flatFee: true, + minFee: 1000, }; - const actualTxn = makePaymentTxnWithSuggestedParams( - account1.addr, - account2.addr, - 1000, - undefined, - undefined, - suggestedParams - ); + const actualTxn = makePaymentTxnWithSuggestedParamsFromObject({ + sender: account1.addr, + receiver: account2.addr, + amount: 1000, + suggestedParams, + }); // A multisig with 1 signature should be accepted signer([actualTxn], [0]).then((signedTxns) => { diff --git a/tests/2.Encoding.js b/tests/2.Encoding.js deleted file mode 100644 index b3f6ca0c1..000000000 --- a/tests/2.Encoding.js +++ /dev/null @@ -1,541 +0,0 @@ -/* 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/2.Encoding.ts b/tests/2.Encoding.ts new file mode 100644 index 000000000..3c1fec7a1 --- /dev/null +++ b/tests/2.Encoding.ts @@ -0,0 +1,3218 @@ +/* eslint-env mocha */ +import assert from 'assert'; +import { RawBinaryString } from 'algorand-msgpack'; +import algosdk, { bytesToString, coerceToBytes } from '../src/index.js'; +import * as utils from '../src/utils/utils.js'; +import { Schema, MsgpackRawStringProvider } from '../src/encoding/encoding.js'; +import { + BooleanSchema, + StringSchema, + Uint64Schema, + AddressSchema, + ByteArraySchema, + FixedLengthByteArraySchema, + BlockHashSchema, + SpecialCaseBinaryStringSchema, + ArraySchema, + NamedMapSchema, + NamedMapEntry, + Uint64MapSchema, + StringMapSchema, + ByteArrayMapSchema, + SpecialCaseBinaryStringMapSchema, + UntypedSchema, + OptionalSchema, + allOmitEmpty, +} from '../src/encoding/schema/index.js'; + +const ERROR_CONTAINS_EMPTY_STRING = + 'The object contains empty or 0 values. First empty or 0 value encountered during encoding: '; + +describe('encoding', () => { + class ExampleEncodable implements algosdk.Encodable { + static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'a', valueSchema: new Uint64Schema() }, + { key: 'b', valueSchema: new StringSchema() }, + { key: 'c', valueSchema: new ByteArraySchema() }, + ]) + ); + + // eslint-disable-next-line no-useless-constructor + constructor( + public a: number | bigint, + public b: string, + public c: Uint8Array + // eslint-disable-next-line no-empty-function + ) {} + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ExampleEncodable.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['a', this.a], + ['b', this.b], + ['c', this.c], + ]); + } + + static fromEncodingData(data: unknown): ExampleEncodable { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SignedTransaction: ${data}`); + } + return new ExampleEncodable(data.get('a'), data.get('b'), data.get('c')); + } + } + describe('msgpack', () => { + it('should encode properly', () => { + const input = new ExampleEncodable( + 123, + 'test', + Uint8Array.from([255, 255, 0]) + ); + const actual = algosdk.encodeMsgpack(input); + const expected = Uint8Array.from([ + 131, 161, 97, 123, 161, 98, 164, 116, 101, 115, 116, 161, 99, 196, 3, + 255, 255, 0, + ]); + assert.deepStrictEqual(actual, expected); + }); + it('should encode properly with default values', () => { + const input = new ExampleEncodable(0, '', Uint8Array.from([])); + const actual = algosdk.encodeMsgpack(input); + const expected = Uint8Array.from([128]); + assert.deepStrictEqual(actual, expected); + }); + it('should decode properly', () => { + const input = Uint8Array.from([ + 131, 161, 97, 123, 161, 98, 164, 116, 101, 115, 116, 161, 99, 196, 3, + 255, 255, 0, + ]); + const actual = algosdk.decodeMsgpack(input, ExampleEncodable); + const expected = new ExampleEncodable( + BigInt(123), + 'test', + Uint8Array.from([255, 255, 0]) + ); + assert.deepStrictEqual(actual, expected); + }); + it('should decode properly with default values', () => { + const input = Uint8Array.from([128]); + const actual = algosdk.decodeMsgpack(input, ExampleEncodable); + const expected = new ExampleEncodable(BigInt(0), '', Uint8Array.from([])); + assert.deepStrictEqual(actual, expected); + }); + }); + describe('JSON', () => { + it('should encode properly', () => { + const input = new ExampleEncodable( + 123, + 'test', + Uint8Array.from([255, 255, 0]) + ); + const actual = algosdk.encodeJSON(input); + const expected = { a: 123, b: 'test', c: '//8A' }; + // Compare parsed JSON because field order and whitespace may be different + assert.deepStrictEqual(JSON.parse(actual), expected); + }); + it('should encode properly with default values', () => { + const input = new ExampleEncodable(0, '', Uint8Array.from([])); + const actual = algosdk.encodeJSON(input); + const expected = '{}'; + assert.deepStrictEqual(actual, expected); + }); + it('should decode properly', () => { + const input = `{ "a": 123, "b": "test", "c": "//8A" }`; + const actual = algosdk.decodeJSON(input, ExampleEncodable); + const expected = new ExampleEncodable( + BigInt(123), + 'test', + Uint8Array.from([255, 255, 0]) + ); + assert.deepStrictEqual(actual, expected); + }); + it('should decode properly with default values', () => { + const input = '{}'; + const actual = algosdk.decodeJSON(input, ExampleEncodable); + const expected = new ExampleEncodable(BigInt(0), '', Uint8Array.from([])); + assert.deepStrictEqual(actual, expected); + }); + }); + describe('Transaction', () => { + it('should be able to encode & decode', () => { + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: '7RQYTPAKBR6R7KR3AN36IW2CSMXTYDO2IIV6USDC24GQXDEC75DVHKZC54', + amount: 17, + receiver: 'VE7563YROD74TOKPJCBNM6CPRLGYPKPS6WOUC5GC675YONMCH7MLGALOFM', + suggestedParams: { + fee: 0, + firstValid: 111, + lastValid: 222, + minFee: 1000, + genesisID: 'testing', + genesisHash: algosdk.base64ToBytes( + 'O4JSEmM8Qn+5phJf2fQPhsLo0PuIpBvHOqCDNCvwmzI=' + ), + }, + }); + const encoded = algosdk.encodeMsgpack(txn); // Uint8Array of msgpack-encoded transaction + const decoded = algosdk.decodeMsgpack(encoded, algosdk.Transaction); // Decoded Transaction instance + assert.deepStrictEqual(txn, decoded); + }); + }); + 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 = new Uint8Array([0x81, 0xa1, 0x41, 0x78]); + let o = { A: 120 }; + assert.notStrictEqual(algosdk.encodeObj(o), golden); + + golden = new Uint8Array([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: Error) => err.toString().includes(ERROR_CONTAINS_EMPTY_STRING) + ); + + const b = { a: 4, B: [] }; + assert.throws( + () => { + algosdk.encodeObj(b); + }, + (err: Error) => err.toString().includes(ERROR_CONTAINS_EMPTY_STRING) + ); + + const c = { a: 4, B: 0 }; + assert.throws( + () => { + algosdk.encodeObj(c); + }, + (err: Error) => 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 = new Uint8Array([ + 0x82, + 0xa1, + 0x4a, + 0xc4, + 0x3, + 0x14, + 0x1e, + 0x28, + 0xa1, + 0x4b, + 0xa3, + 0x61, + 0x61, + 0x61, + ]); + const o = { J: new Uint8Array([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 as any)); + // 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 as any) + ); + assert.strictEqual(beforeLarge, afterLarge); + const tooLarge = BigInt('18446744073709551616'); // larger than even fits into a uint64. we do not want to work with these too-large numbers + assert.throws( + () => algosdk.encodeObj(tooLarge as any), + /Bigint is too large for uint64: 18446744073709551616$/ + ); + }); + + 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: algosdk.decodeAddress(ad).publicKey, + rcv: 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: Array<[number | bigint, Uint8Array]> = [ + [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: Array<[Uint8Array, number | bigint]> = [ + [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: Array<[Uint8Array, number | bigint]> = [ + [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: Array<[Uint8Array, number | bigint]> = [ + [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: Array<[Uint8Array, bigint]> = [ + [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' as any + ) + ); + }); + }); + + describe('JSON BigInt', () => { + it('should parse null', () => { + const input = 'null'; + + for (const intDecoding of [ + algosdk.IntDecoding.UNSAFE, + algosdk.IntDecoding.SAFE, + algosdk.IntDecoding.MIXED, + algosdk.IntDecoding.BIGINT, + ]) { + const actual = utils.parseJSON(input, { intDecoding }); + const expected = null; + + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + + const roundtrip = utils.stringifyJSON(actual); + assert.deepStrictEqual( + roundtrip, + input, + `Error when intDecoding = ${intDecoding}` + ); + } + }); + + it('should parse number', () => { + const inputs = ['17', '9007199254740991']; + for (const input of inputs) { + for (const intDecoding of [ + algosdk.IntDecoding.UNSAFE, + algosdk.IntDecoding.SAFE, + algosdk.IntDecoding.MIXED, + algosdk.IntDecoding.BIGINT, + ]) { + const actual = utils.parseJSON(input, { intDecoding }); + const expected = + intDecoding === algosdk.IntDecoding.BIGINT + ? BigInt(input) + : Number(input); + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + + const roundtrip = utils.stringifyJSON(actual); + assert.deepStrictEqual( + roundtrip, + input, + `Error when intDecoding = ${intDecoding}` + ); + } + } + }); + + it('should parse string', () => { + const input = + '"IRK7XSCO7LPIBQKIUJXTQ5I7XBP3362ACPME5SOUUIJXN77T44RG4FZSLI"'; + + for (const intDecoding of [ + algosdk.IntDecoding.UNSAFE, + algosdk.IntDecoding.SAFE, + algosdk.IntDecoding.MIXED, + algosdk.IntDecoding.BIGINT, + ]) { + const actual = utils.parseJSON(input, { intDecoding }); + + assert.strictEqual(typeof actual, 'string'); + + const expected = + 'IRK7XSCO7LPIBQKIUJXTQ5I7XBP3362ACPME5SOUUIJXN77T44RG4FZSLI'; + + assert.strictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + + const roundtrip = utils.stringifyJSON(actual); + assert.deepStrictEqual( + roundtrip, + input, + `Error when intDecoding = ${intDecoding}` + ); + } + }); + + it('should parse empty object', () => { + const input = '{}'; + + for (const intDecoding of [ + algosdk.IntDecoding.UNSAFE, + algosdk.IntDecoding.SAFE, + algosdk.IntDecoding.MIXED, + algosdk.IntDecoding.BIGINT, + ]) { + const actual = utils.parseJSON(input, { intDecoding }); + const expected = {}; + + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + + const roundtrip = utils.stringifyJSON(actual); + assert.deepStrictEqual( + roundtrip, + input, + `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 [ + algosdk.IntDecoding.UNSAFE, + algosdk.IntDecoding.SAFE, + algosdk.IntDecoding.MIXED, + algosdk.IntDecoding.BIGINT, + ]) { + const actual = utils.parseJSON(input, { intDecoding }); + + let expected; + if (intDecoding === algosdk.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}` + ); + + const roundtrip = utils.stringifyJSON(actual); + assert.deepStrictEqual( + roundtrip, + input, + `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: algosdk.IntDecoding.SAFE }) + ); + + for (const intDecoding of [ + algosdk.IntDecoding.UNSAFE, + algosdk.IntDecoding.MIXED, + algosdk.IntDecoding.BIGINT, + ]) { + const actual = utils.parseJSON(input, { intDecoding }); + + let expected; + if (intDecoding === algosdk.IntDecoding.BIGINT) { + expected = { + a: 0n, + b: 9007199254740991n, + c: 9007199254740992n, + d: 9223372036854775807n, + }; + } else if (intDecoding === algosdk.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}` + ); + + if (intDecoding !== algosdk.IntDecoding.UNSAFE) { + const roundtrip = utils.stringifyJSON(actual); + assert.deepStrictEqual( + roundtrip, + input, + `Error when intDecoding = ${intDecoding}` + ); + } + } + }); + + it('should parse empty array', () => { + const input = '[]'; + + for (const intDecoding of [ + algosdk.IntDecoding.UNSAFE, + algosdk.IntDecoding.SAFE, + algosdk.IntDecoding.MIXED, + algosdk.IntDecoding.BIGINT, + ]) { + const actual = utils.parseJSON(input, { intDecoding }); + const expected: unknown[] = []; + + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + + const roundtrip = utils.stringifyJSON(actual); + assert.deepStrictEqual( + roundtrip, + input, + `Error when intDecoding = ${intDecoding}` + ); + } + }); + + it('should parse populated array', () => { + const input = '["test",2,null,[7],{"a":9.5},true]'; + + for (const intDecoding of [ + algosdk.IntDecoding.UNSAFE, + algosdk.IntDecoding.SAFE, + algosdk.IntDecoding.MIXED, + algosdk.IntDecoding.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}` + ); + + const roundtrip = utils.stringifyJSON(actual); + assert.deepStrictEqual( + roundtrip, + input, + `Error when intDecoding = ${intDecoding}` + ); + } + }); + + it('should parse array with BigInt', () => { + const input = '[0,9007199254740991,9007199254740992,9223372036854775807]'; + + assert.throws(() => + utils.parseJSON(input, { intDecoding: algosdk.IntDecoding.SAFE }) + ); + + for (const intDecoding of [ + algosdk.IntDecoding.UNSAFE, + algosdk.IntDecoding.MIXED, + algosdk.IntDecoding.BIGINT, + ]) { + const actual = utils.parseJSON(input, { intDecoding }); + + let expected; + if (intDecoding === algosdk.IntDecoding.BIGINT) { + expected = [ + 0n, + 9007199254740991n, + 9007199254740992n, + 9223372036854775807n, + ]; + } else if (intDecoding === algosdk.IntDecoding.MIXED) { + expected = [ + 0, + 9007199254740991, + 9007199254740992n, + 9223372036854775807n, + ]; + } else { + expected = [ + 0, + 9007199254740991, + Number(9007199254740992n), + Number(9223372036854775807n), + ]; + } + + assert.deepStrictEqual( + actual, + expected, + `Error when intDecoding = ${intDecoding}` + ); + + if (intDecoding !== algosdk.IntDecoding.UNSAFE) { + const roundtrip = utils.stringifyJSON(actual); + assert.deepStrictEqual( + roundtrip, + input, + `Error when intDecoding = ${intDecoding}` + ); + } + } + }); + + it('should stringify with spacing', () => { + const input = { a: 1, b: 'value', c: [1, 2, 3], d: null, e: {}, f: true }; + const actual = utils.stringifyJSON(input, undefined, 2); + const expected = `{ + "a": 1, + "b": "value", + "c": [ + 1, + 2, + 3 + ], + "d": null, + "e": {}, + "f": true +}`; + assert.strictEqual(actual, expected); + }); + }); + + describe('Base64 decoding utilities', () => { + it('should decode bytes from Base64', () => { + const testCases: Array<[Uint8Array, string]> = [ + [ + Uint8Array.from([ + 97, 32, 196, 128, 32, 240, 144, 128, 128, 32, 230, 150, 135, 32, + 240, 159, 166, 132, + ]), // a Ā 𐀀 文 🦄 + 'YSDEgCDwkICAIOaWhyDwn6aE', + ], + [ + Uint8Array.from([0, 1, 2, 3, 4, 46, 46, 46, 254, 255]), // non UTF-8 bytes + 'AAECAwQuLi7+/w==', + ], + ]; + for (const [expectedBytes, expectedEncoding] of testCases) { + const actualBytes = algosdk.base64ToBytes(expectedEncoding); + assert.deepStrictEqual( + actualBytes, + expectedBytes, + `Incorrect encoding of ${expectedBytes}; got ${actualBytes}` + ); + } + }); + + it('should decode and encode Base64 roundtrip for UTF-8 strings', () => { + const testCases = [ + ['Hello, Algorand!', 'SGVsbG8sIEFsZ29yYW5kIQ=='], + ['a Ā 𐀀 文 🦄', 'YSDEgCDwkICAIOaWhyDwn6aE'], + ['(╯°□°)``` ┻━┻ 00\\', 'KOKVr8Kw4pahwrDvvIlgYGAg4pS74pSB4pS7IDAwXA=='], + ['\uFFFD\uFFFD', '/v8='], // Non UTF-8 bytes should still decode to same (invalid) output + ]; + for (const [testCase, expectedEncoding] of testCases) { + const actualB64Decoding = algosdk.bytesToString( + algosdk.base64ToBytes(expectedEncoding) + ); + assert.deepStrictEqual( + actualB64Decoding, + testCase, + `Incorrect encoding of ${testCase}; got ${actualB64Decoding}` + ); + + const byteArray = new TextEncoder().encode(testCase); + const base64String = algosdk.bytesToBase64(byteArray); + const roundTripString = new TextDecoder().decode( + algosdk.base64ToBytes(base64String) + ); + + assert.deepStrictEqual( + roundTripString, + testCase, + `Incorrect decoding of ${testCase}; got ${roundTripString}` + ); + } + }); + + it('should encode bytes to hex', () => { + const testCases = [ + ['Hello, Algorand!', '48656c6c6f2c20416c676f72616e6421'], + ['a Ā 𐀀 文 🦄', '6120c48020f090808020e6968720f09fa684'], + [ + '(╯°□°)``` ┻━┻ 00\\', + '28e295afc2b0e296a1c2b0efbc8960606020e294bbe29481e294bb2030305c', + ], + ]; + for (const [testCase, expectedEncoding] of testCases) { + const binString = new TextEncoder().encode(testCase); + const actualHexString = algosdk.bytesToHex(binString); + assert.deepStrictEqual( + actualHexString, + expectedEncoding, + `Incorrect encoding of ${testCase}; got ${actualHexString}` + ); + } + }); + }); + describe('Schema', () => { + describe('General', () => { + interface SchemaTestCase { + name: string; + schema: Schema; + values: unknown[]; + preparedMsgpackValues: algosdk.MsgpackEncodingData[]; + // The expected output from calling `fromPreparedMsgpack`. If not provided, `values` will be used. + expectedValuesFromPreparedMsgpack?: unknown[]; + preparedJsonValues: algosdk.JSONEncodingData[]; + // The expected output from calling `fromPreparedJSON`. If not provided, `values` will be used. + expectedValuesFromPreparedJson?: unknown[]; + } + + const testcases: SchemaTestCase[] = [ + { + name: 'BooleanSchema', + schema: new BooleanSchema(), + values: [true, false], + preparedMsgpackValues: [true, false], + preparedJsonValues: [true, false], + }, + { + name: 'StringSchema', + schema: new StringSchema(), + values: ['', 'abc'], + preparedMsgpackValues: ['', 'abc'], + preparedJsonValues: ['', 'abc'], + }, + { + name: 'SpecialCaseBinaryStringSchema', + schema: new SpecialCaseBinaryStringSchema(), + values: [Uint8Array.from([]), Uint8Array.from([97, 98, 99])], + preparedMsgpackValues: [ + // Cast is needed because RawBinaryString is not part of the standard MsgpackEncodingData + new RawBinaryString( + Uint8Array.from([]) + ) as unknown as algosdk.MsgpackEncodingData, + new RawBinaryString( + Uint8Array.from([97, 98, 99]) + ) as unknown as algosdk.MsgpackEncodingData, + ], + preparedJsonValues: ['', 'abc'], + }, + { + name: 'Uint64Schema', + schema: new Uint64Schema(), + values: [0, 1, 255, 256, 0xffffffffffffffffn], + preparedMsgpackValues: [0n, 1n, 255n, 256n, 0xffffffffffffffffn], + // fromPreparedMsgpack will convert numbers to bigints + expectedValuesFromPreparedMsgpack: [ + 0n, + 1n, + 255n, + 256n, + 0xffffffffffffffffn, + ], + preparedJsonValues: [0n, 1n, 255n, 256n, 0xffffffffffffffffn], + // Roundtrip will convert numbers to bigints + expectedValuesFromPreparedJson: [ + 0n, + 1n, + 255n, + 256n, + 0xffffffffffffffffn, + ], + }, + { + name: 'AddressSchema', + schema: new AddressSchema(), + values: [ + algosdk.Address.zeroAddress(), + algosdk.Address.fromString( + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJI' + ), + ], + preparedMsgpackValues: [ + new Uint8Array(32), + Uint8Array.from([ + 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, + ]), + ], + preparedJsonValues: [ + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ', + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJI', + ], + }, + { + name: 'ByteArraySchema', + schema: new ByteArraySchema(), + values: [Uint8Array.from([]), Uint8Array.from([1, 2, 3])], + preparedMsgpackValues: [ + Uint8Array.from([]), + Uint8Array.from([1, 2, 3]), + ], + preparedJsonValues: ['', 'AQID'], + }, + { + name: 'FixedLengthByteArraySchema', + schema: new FixedLengthByteArraySchema(5), + values: [new Uint8Array(5), Uint8Array.from([1, 2, 3, 4, 5])], + preparedMsgpackValues: [ + new Uint8Array(5), + Uint8Array.from([1, 2, 3, 4, 5]), + ], + preparedJsonValues: ['AAAAAAA=', 'AQIDBAU='], + }, + { + name: 'BlockHashSchema', + schema: new BlockHashSchema(), + values: [ + new Uint8Array(32), + Uint8Array.from([ + 236, 203, 188, 96, 194, 35, 246, 94, 227, 223, 92, 185, 6, 143, + 198, 118, 147, 181, 197, 211, 218, 113, 81, 36, 52, 88, 237, 1, + 109, 72, 120, 38, + ]), + ], + preparedMsgpackValues: [ + new Uint8Array(32), + Uint8Array.from([ + 236, 203, 188, 96, 194, 35, 246, 94, 227, 223, 92, 185, 6, 143, + 198, 118, 147, 181, 197, 211, 218, 113, 81, 36, 52, 88, 237, 1, + 109, 72, 120, 38, + ]), + ], + preparedJsonValues: [ + 'blk-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + 'blk-5TF3YYGCEP3F5Y67LS4QND6GO2J3LROT3JYVCJBULDWQC3KIPATA', + ], + }, + { + name: 'UntypedSchema', + schema: new UntypedSchema(), + values: [undefined, null, 0, 'abc', new Map()], + preparedMsgpackValues: [undefined, null, 0, 'abc', new Map()], + preparedJsonValues: [undefined, null, 0, 'abc', {}], + }, + { + name: 'UntypedSchema, binary data', // Special case for Uint8Array + schema: new UntypedSchema(), + values: [new Uint8Array(), Uint8Array.from([1, 2, 3])], + preparedMsgpackValues: [new Uint8Array(), Uint8Array.from([1, 2, 3])], + preparedJsonValues: ['', 'AQID'], + // Roundtrip will convert Uint8Array to base64 strings + expectedValuesFromPreparedJson: ['', 'AQID'], + }, + { + name: 'OptionalSchema of BooleanSchema', + schema: new OptionalSchema(new BooleanSchema()), + values: [undefined, true, false], + preparedMsgpackValues: [undefined, true, false], + preparedJsonValues: [null, true, false], + }, + { + name: 'Uint64MapSchema of BooleanSchema', + schema: new Uint64MapSchema(new BooleanSchema()), + values: [ + new Map(), + new Map([ + [0n, true], + [1n, false], + [2n, true], + [BigInt('18446744073709551615'), true], + ]), + ], + preparedMsgpackValues: [ + new Map(), + new Map([ + [0n, true], + [1n, false], + [2n, true], + [BigInt('18446744073709551615'), true], + ]), + ], + preparedJsonValues: [ + {}, + { + 0: true, + 1: false, + 2: true, + '18446744073709551615': true, + }, + ], + }, + { + name: 'Uint64MapSchema of SpecialCaseBinaryStringSchema', + schema: new Uint64MapSchema(new SpecialCaseBinaryStringSchema()), + values: [ + new Map(), + new Map([ + [0n, Uint8Array.from([])], + [1n, Uint8Array.from([97])], + [2n, Uint8Array.from([98])], + [BigInt('18446744073709551615'), Uint8Array.from([99])], + ]), + ], + preparedMsgpackValues: [ + new Map(), + new Map([ + [0n, new RawBinaryString(Uint8Array.from([]))], + [1n, new RawBinaryString(Uint8Array.from([97]))], + [2n, new RawBinaryString(Uint8Array.from([98]))], + [ + BigInt('18446744073709551615'), + new RawBinaryString(Uint8Array.from([99])), + ], + ]), + ], + preparedJsonValues: [ + {}, + { + 0: '', + 1: 'a', + 2: 'b', + '18446744073709551615': 'c', + }, + ], + }, + { + name: 'StringMapSchema of BooleanSchema', + schema: new StringMapSchema(new BooleanSchema()), + values: [ + new Map(), + new Map([ + ['a', true], + ['b', false], + ['c', true], + ['', true], + ]), + ], + preparedMsgpackValues: [ + new Map(), + new Map([ + ['a', true], + ['b', false], + ['c', true], + ['', true], + ]), + ], + preparedJsonValues: [ + {}, + { + a: true, + b: false, + c: true, + '': true, + }, + ], + }, + { + name: 'ByteArrayMapSchema of BooleanSchema', + schema: new ByteArrayMapSchema(new BooleanSchema()), + values: [ + new Map(), + new Map([ + [Uint8Array.from([]), true], + [Uint8Array.from([0]), false], + [Uint8Array.from([1]), true], + [Uint8Array.from([2, 3, 4, 5]), true], + ]), + ], + preparedMsgpackValues: [ + new Map(), + new Map([ + [Uint8Array.from([]), true], + [Uint8Array.from([0]), false], + [Uint8Array.from([1]), true], + [Uint8Array.from([2, 3, 4, 5]), true], + ]), + ], + preparedJsonValues: [ + {}, + { + '': true, + 'AA==': false, + 'AQ==': true, + 'AgMEBQ==': true, + }, + ], + }, + { + name: 'ByteArrayMapSchema of SpecialCaseBinaryStringSchema', + schema: new ByteArrayMapSchema(new SpecialCaseBinaryStringSchema()), + values: [ + new Map(), + new Map([ + [Uint8Array.from([]), Uint8Array.from([])], + [Uint8Array.from([0]), Uint8Array.from([97])], + [Uint8Array.from([1]), Uint8Array.from([98])], + [Uint8Array.from([2, 3, 4, 5]), Uint8Array.from([99])], + ]), + ], + preparedMsgpackValues: [ + new Map(), + new Map([ + [Uint8Array.from([]), new RawBinaryString(Uint8Array.from([]))], + [ + Uint8Array.from([0]), + new RawBinaryString(Uint8Array.from([97])), + ], + [ + Uint8Array.from([1]), + new RawBinaryString(Uint8Array.from([98])), + ], + [ + Uint8Array.from([2, 3, 4, 5]), + new RawBinaryString(Uint8Array.from([99])), + ], + ]), + ], + preparedJsonValues: [ + {}, + { + '': '', + 'AA==': 'a', + 'AQ==': 'b', + 'AgMEBQ==': 'c', + }, + ], + }, + { + name: 'SpecialCaseBinaryStringMapSchema of BooleanSchema', + schema: new SpecialCaseBinaryStringMapSchema(new BooleanSchema()), + values: [ + new Map(), + new Map([ + [Uint8Array.from([97]), true], + [Uint8Array.from([98]), false], + [Uint8Array.from([99]), true], + [Uint8Array.from([]), true], + ]), + ], + preparedMsgpackValues: [ + new Map(), + new Map([ + [new RawBinaryString(Uint8Array.from([97])), true], + [new RawBinaryString(Uint8Array.from([98])), false], + [new RawBinaryString(Uint8Array.from([99])), true], + [new RawBinaryString(Uint8Array.from([])), true], + ]), + ], + preparedJsonValues: [ + {}, + { + a: true, + b: false, + c: true, + '': true, + }, + ], + }, + { + name: 'SpecialCaseBinaryStringMapSchema of SpecialCaseBinaryStringSchema', + schema: new SpecialCaseBinaryStringMapSchema( + new SpecialCaseBinaryStringSchema() + ), + values: [ + new Map(), + new Map([ + [Uint8Array.from([97]), Uint8Array.from([120])], + [Uint8Array.from([98]), Uint8Array.from([121])], + [Uint8Array.from([99]), Uint8Array.from([122])], + [Uint8Array.from([]), Uint8Array.from([])], + ]), + ], + preparedMsgpackValues: [ + new Map(), + new Map([ + [ + new RawBinaryString(Uint8Array.from([97])), + new RawBinaryString(Uint8Array.from([120])), + ], + [ + new RawBinaryString(Uint8Array.from([98])), + new RawBinaryString(Uint8Array.from([121])), + ], + [ + new RawBinaryString(Uint8Array.from([99])), + new RawBinaryString(Uint8Array.from([122])), + ], + [ + new RawBinaryString(Uint8Array.from([])), + new RawBinaryString(Uint8Array.from([])), + ], + ]), + ], + preparedJsonValues: [ + {}, + { + a: 'x', + b: 'y', + c: 'z', + '': '', + }, + ], + }, + ]; + + const primitiveTestcases = testcases.slice(); + + // Add ArraySchema test cases + for (const testcase of primitiveTestcases) { + const arrayTestcase: SchemaTestCase = { + name: `ArraySchema containing ${testcase.name}`, + schema: new ArraySchema(testcase.schema), + values: [[], ...testcase.values.map((v) => [v]), testcase.values], + preparedMsgpackValues: [ + [], + ...testcase.preparedMsgpackValues.map((v) => [v]), + testcase.preparedMsgpackValues, + ], + expectedValuesFromPreparedMsgpack: + testcase.expectedValuesFromPreparedMsgpack, + preparedJsonValues: [ + [], + ...testcase.preparedJsonValues.map((v) => [v]), + testcase.preparedJsonValues, + ], + }; + + if (testcase.expectedValuesFromPreparedMsgpack) { + arrayTestcase.expectedValuesFromPreparedMsgpack = [ + [], + ...testcase.expectedValuesFromPreparedMsgpack.map((v) => [v]), + testcase.expectedValuesFromPreparedMsgpack, + ]; + } + + if (testcase.expectedValuesFromPreparedJson) { + arrayTestcase.expectedValuesFromPreparedJson = [ + [], + ...testcase.expectedValuesFromPreparedJson.map((v) => [v]), + testcase.expectedValuesFromPreparedJson, + ]; + } + + testcases.push(arrayTestcase); + } + + const primitiveAndArrayTestcases = testcases.slice(); + + // Add NamedMapSchema test cases + for (const testcase of primitiveAndArrayTestcases) { + const mapTestcase: SchemaTestCase = { + name: `NamedMapSchema containing ${testcase.name}`, + schema: new NamedMapSchema([ + { + key: 'key', + valueSchema: testcase.schema, + // Testing with omitEmpty=false for simplicity + omitEmpty: false, + }, + ]), + values: testcase.values.map((v) => new Map([['key', v]])), + preparedMsgpackValues: testcase.preparedMsgpackValues.map( + (v) => new Map([['key', v]]) + ), + preparedJsonValues: testcase.preparedJsonValues.map((v) => ({ + key: v, + })), + }; + + if (testcase.expectedValuesFromPreparedMsgpack) { + mapTestcase.expectedValuesFromPreparedMsgpack = + testcase.expectedValuesFromPreparedMsgpack.map( + (v) => new Map([['key', v]]) + ); + } + + if (testcase.expectedValuesFromPreparedJson) { + mapTestcase.expectedValuesFromPreparedJson = + testcase.expectedValuesFromPreparedJson.map( + (v) => new Map([['key', v]]) + ); + } + + testcases.push(mapTestcase); + } + + for (const testcase of testcases) { + it(`should correctly prepare values for encoding and decoding with schema ${testcase.name}`, () => { + for (let i = 0; i < testcase.values.length; i++) { + const value = testcase.values[i]; + const preparedMsgpackValue = testcase.preparedMsgpackValues[i]; + const preparedJsonValue = testcase.preparedJsonValues[i]; + + const actualMsgpack = testcase.schema.prepareMsgpack(value); + assert.deepStrictEqual(actualMsgpack, preparedMsgpackValue); + + const msgpackBytes = algosdk.msgpackRawEncode(actualMsgpack); + const rawStringProvider = new MsgpackRawStringProvider({ + baseObjectBytes: msgpackBytes, + }); + + const roundtripMsgpackValue = testcase.schema.fromPreparedMsgpack( + actualMsgpack, + rawStringProvider + ); + const roundtripMsgpackExpectedValue = + testcase.expectedValuesFromPreparedMsgpack + ? testcase.expectedValuesFromPreparedMsgpack[i] + : value; + assert.deepStrictEqual( + roundtripMsgpackValue, + roundtripMsgpackExpectedValue + ); + + const actualJson = testcase.schema.prepareJSON(value, {}); + assert.deepStrictEqual(actualJson, preparedJsonValue); + + const roundtripJsonValue = + testcase.schema.fromPreparedJSON(actualJson); + const roundtripJsonExpectedValue = + testcase.expectedValuesFromPreparedJson + ? testcase.expectedValuesFromPreparedJson[i] + : value; + assert.deepStrictEqual( + roundtripJsonValue, + roundtripJsonExpectedValue + ); + } + }); + } + }); + describe('NamedMapSchema', () => { + it('correctly handles omitEmpty', () => { + const testValues: Array<{ + schema: Schema; + emptyValue: unknown; + emptyValueRestored?: unknown; + nonemptyValue: unknown; + }> = [ + { + schema: new BooleanSchema(), + emptyValue: false, + nonemptyValue: true, + }, + { + schema: new Uint64Schema(), + emptyValue: 0n, + nonemptyValue: 1n, + }, + { + schema: new StringSchema(), + emptyValue: '', + nonemptyValue: 'abc', + }, + { + schema: new SpecialCaseBinaryStringSchema(), + emptyValue: Uint8Array.from([]), + nonemptyValue: Uint8Array.from([97, 98, 99]), + }, + { + schema: new AddressSchema(), + emptyValue: algosdk.Address.zeroAddress(), + nonemptyValue: algosdk.Address.fromString( + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJI' + ), + }, + { + schema: new ByteArraySchema(), + emptyValue: Uint8Array.from([]), + nonemptyValue: Uint8Array.from([1, 2, 3]), + }, + { + schema: new FixedLengthByteArraySchema(5), + emptyValue: new Uint8Array(5), + nonemptyValue: Uint8Array.from([1, 2, 3, 4, 5]), + }, + { + schema: new BlockHashSchema(), + emptyValue: new Uint8Array(32), + nonemptyValue: Uint8Array.from([ + 236, 203, 188, 96, 194, 35, 246, 94, 227, 223, 92, 185, 6, 143, + 198, 118, 147, 181, 197, 211, 218, 113, 81, 36, 52, 88, 237, 1, + 109, 72, 120, 38, + ]), + }, + { + schema: new UntypedSchema(), + emptyValue: undefined, + nonemptyValue: 0, + }, + { + schema: new ArraySchema(new BooleanSchema()), + emptyValue: [], + nonemptyValue: [false], + }, + { + schema: new NamedMapSchema([ + { + key: 'key', + valueSchema: new BooleanSchema(), + omitEmpty: true, + }, + ]), + emptyValue: new Map([['key', false]]), + nonemptyValue: new Map([['key', true]]), + }, + { + schema: new NamedMapSchema([ + { + key: 'key', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + ]), + emptyValue: new Map([['key', undefined]]), + nonemptyValue: new Map([['key', true]]), + }, + { + schema: new NamedMapSchema([ + { + key: 'key', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + ]), + // Same case as previous, expect testing that 'false' is also an empty value for the key + emptyValue: new Map([['key', false]]), + // false gets restored as undefined + emptyValueRestored: new Map([['key', undefined]]), + nonemptyValue: new Map([['key', true]]), + }, + { + schema: new Uint64MapSchema(new BooleanSchema()), + emptyValue: new Map(), + nonemptyValue: new Map([ + [0n, true], + [1n, false], + [2n, true], + [BigInt('18446744073709551615'), true], + ]), + }, + { + schema: new StringMapSchema(new BooleanSchema()), + emptyValue: new Map(), + nonemptyValue: new Map([ + ['a', true], + ['b', false], + ['c', true], + ['', true], + ]), + }, + { + schema: new ByteArrayMapSchema(new BooleanSchema()), + emptyValue: new Map(), + nonemptyValue: new Map([ + [Uint8Array.from([]), true], + [Uint8Array.from([0]), false], + [Uint8Array.from([1]), true], + [Uint8Array.from([2, 3, 4, 5]), true], + ]), + }, + { + schema: new SpecialCaseBinaryStringMapSchema(new BooleanSchema()), + emptyValue: new Map(), + nonemptyValue: new Map([ + [Uint8Array.from([97]), true], + [Uint8Array.from([98]), false], + [Uint8Array.from([99]), true], + [Uint8Array.from([]), true], + ]), + }, + ]; + + for (const testValue of testValues.slice()) { + testValues.push( + { + schema: new OptionalSchema(testValue.schema), + emptyValue: undefined, + nonemptyValue: testValue.nonemptyValue, + }, + { + schema: new OptionalSchema(testValue.schema), + // Same case as previous, expect testing that the regular empty value is also an empty + // value for the optional schema + emptyValue: testValue.emptyValue, + // The empty value gets restored as undefined + emptyValueRestored: undefined, + nonemptyValue: testValue.nonemptyValue, + } + ); + } + + const schema = new NamedMapSchema( + testValues.map((testValue, index) => ({ + key: index.toString(), + valueSchema: testValue.schema, + required: true, + omitEmpty: true, + })) + ); + + const allEmptyValues = new Map( + testValues.map((testValue, index) => [ + index.toString(), + testValue.emptyValue, + ]) + ); + const allEmptyValuesRestored = new Map( + testValues.map((testValue, index) => [ + index.toString(), + // Cannot just check if emptyValueRestored is not undefined, since undefined is a valid value + Object.prototype.hasOwnProperty.call( + testValue, + 'emptyValueRestored' + ) + ? testValue.emptyValueRestored + : testValue.emptyValue, + ]) + ); + + let prepareMsgpackResult = schema.prepareMsgpack(allEmptyValues); + // All empty values should be omitted + assert.deepStrictEqual(prepareMsgpackResult, new Map()); + let msgpackBytes = algosdk.msgpackRawEncode(prepareMsgpackResult); + let rawStringProvider = new MsgpackRawStringProvider({ + baseObjectBytes: msgpackBytes, + }); + let fromPreparedMsgpackResult = schema.fromPreparedMsgpack( + prepareMsgpackResult, + rawStringProvider + ); + // Omitted values should be restored with their default/empty values + assert.deepStrictEqual( + fromPreparedMsgpackResult, + allEmptyValuesRestored + ); + + let prepareJsonResult = schema.prepareJSON(allEmptyValues, {}); + // All empty values should be omitted + assert.deepStrictEqual(prepareJsonResult, {}); + let fromPreparedJsonResult = schema.fromPreparedJSON(prepareJsonResult); + // Omitted values should be restored with their default/empty values + assert.deepStrictEqual(fromPreparedJsonResult, allEmptyValuesRestored); + + const allNonemptyValues = new Map( + testValues.map((testValue, index) => [ + index.toString(), + testValue.nonemptyValue, + ]) + ); + + prepareMsgpackResult = schema.prepareMsgpack(allNonemptyValues); + assert.ok(prepareMsgpackResult instanceof Map); + // All values are present + assert.strictEqual(prepareMsgpackResult.size, testValues.length); + msgpackBytes = algosdk.msgpackRawEncode(prepareMsgpackResult); + rawStringProvider = new MsgpackRawStringProvider({ + baseObjectBytes: msgpackBytes, + }); + fromPreparedMsgpackResult = schema.fromPreparedMsgpack( + prepareMsgpackResult, + rawStringProvider + ); + // Values are restored properly + assert.deepStrictEqual(fromPreparedMsgpackResult, allNonemptyValues); + + prepareJsonResult = schema.prepareJSON(allNonemptyValues, {}); + // All values are present + assert.strictEqual( + Object.keys(prepareJsonResult as object).length, + testValues.length + ); + fromPreparedJsonResult = schema.fromPreparedJSON(prepareJsonResult); + // Omitted values should be restored with their default/empty values + assert.deepStrictEqual(fromPreparedJsonResult, allNonemptyValues); + }); + + it('ignores unknown keys', () => { + const schema = new NamedMapSchema([ + { + key: 'a', + omitEmpty: true, + valueSchema: new StringSchema(), + }, + { + key: 'b', + omitEmpty: true, + valueSchema: new StringSchema(), + }, + ]); + + assert.deepStrictEqual( + schema.prepareMsgpack( + new Map([ + ['a', ''], + ['b', ''], + ['c', ''], + ]) + ), + new Map() + ); + assert.deepStrictEqual( + schema.prepareJSON( + new Map([ + ['a', ''], + ['b', ''], + ['c', ''], + ]), + {} + ), + {} + ); + + assert.deepStrictEqual( + schema.prepareMsgpack( + new Map([ + ['a', '1'], + ['b', '2'], + ['c', '3'], + ]) + ), + new Map([ + ['a', '1'], + ['b', '2'], + ]) + ); + assert.deepStrictEqual( + schema.prepareJSON( + new Map([ + ['a', '1'], + ['b', '2'], + ['c', '3'], + ]), + {} + ), + { + a: '1', + b: '2', + } + ); + + const mapSchemaOfMap = new NamedMapSchema([ + { + key: 'map', + omitEmpty: true, + valueSchema: schema, + }, + ]); + + assert.deepStrictEqual( + mapSchemaOfMap.prepareMsgpack( + new Map([ + [ + 'map', + new Map([ + ['a', ''], + ['b', ''], + ['c', ''], + ]), + ], + ]) + ), + new Map() + ); + assert.deepStrictEqual( + mapSchemaOfMap.prepareJSON( + new Map([ + [ + 'map', + new Map([ + ['a', ''], + ['b', ''], + ['c', ''], + ]), + ], + ]), + {} + ), + {} + ); + + assert.deepStrictEqual( + mapSchemaOfMap.prepareMsgpack( + new Map([ + [ + 'map', + new Map([ + ['a', '1'], + ['b', '2'], + ['c', '3'], + ]), + ], + ]) + ), + new Map([ + [ + 'map', + new Map([ + ['a', '1'], + ['b', '2'], + ]), + ], + ]) + ); + assert.deepStrictEqual( + mapSchemaOfMap.prepareJSON( + new Map([ + [ + 'map', + new Map([ + ['a', '1'], + ['b', '2'], + ['c', '3'], + ]), + ], + ]), + {} + ), + { + map: { a: '1', b: '2' }, + } + ); + }); + + it('correctly embeds other maps', () => { + const bSchema = new NamedMapSchema([ + { + key: 'b', + omitEmpty: true, + valueSchema: new Uint64Schema(), + }, + ]); + + const abSchema = new NamedMapSchema([ + { + key: 'a', + omitEmpty: true, + valueSchema: new StringSchema(), + }, + { + key: '', + omitEmpty: true, + valueSchema: bSchema, + embedded: true, + }, + ]); + + const emptySchema = new NamedMapSchema([]); + + const abcdSchema = new NamedMapSchema([ + { + key: '', + omitEmpty: true, + valueSchema: abSchema, + embedded: true, + }, + { + key: 'c', + omitEmpty: true, + valueSchema: new BooleanSchema(), + }, + { + key: 'd', + omitEmpty: true, + valueSchema: new ArraySchema(new StringSchema()), + }, + { + key: '', + omitEmpty: true, + valueSchema: emptySchema, + embedded: true, + }, + ]); + + const actualEntries = abcdSchema.getEntries(); + const expectedEntries: NamedMapEntry[] = [ + { key: 'a', omitEmpty: true, valueSchema: new StringSchema() }, + { key: 'b', omitEmpty: true, valueSchema: new Uint64Schema() }, + { key: 'c', omitEmpty: true, valueSchema: new BooleanSchema() }, + { + key: 'd', + omitEmpty: true, + valueSchema: new ArraySchema(new StringSchema()), + }, + ]; + assert.deepStrictEqual(actualEntries, expectedEntries); + + const acutalDefaultValue = abcdSchema.defaultValue(); + const expectedDefaultValue = new Map([ + ['a', ''], + ['b', BigInt(0)], + ['c', false], + ['d', []], + ]); + assert.deepStrictEqual(acutalDefaultValue, expectedDefaultValue); + }); + + it('correctly pushes new entries', () => { + const schema = new NamedMapSchema([ + { + key: 'a', + omitEmpty: true, + valueSchema: new StringSchema(), + }, + ]); + + schema.pushEntries({ + key: 'b', + omitEmpty: true, + valueSchema: new Uint64Schema(), + }); + + const actualEntries = schema.getEntries(); + const expectedEntries: NamedMapEntry[] = [ + { key: 'a', omitEmpty: true, valueSchema: new StringSchema() }, + { key: 'b', omitEmpty: true, valueSchema: new Uint64Schema() }, + ]; + assert.deepStrictEqual(actualEntries, expectedEntries); + + assert.throws( + () => + schema.pushEntries({ + key: 'a', + omitEmpty: true, + valueSchema: new StringSchema(), + }), + new Error('Duplicate key: a') + ); + }); + + it('errors on invalid constructor args', () => { + assert.throws( + () => + new NamedMapSchema([ + { + key: 'a', + omitEmpty: true, + valueSchema: new StringSchema(), + }, + { + key: 'a', + omitEmpty: true, + valueSchema: new StringSchema(), + }, + ]), + new Error('Duplicate key: a') + ); + + assert.throws( + () => + new NamedMapSchema([ + { + key: 'a', + omitEmpty: true, + valueSchema: new StringSchema(), + }, + { + key: '', + omitEmpty: true, + valueSchema: new NamedMapSchema([ + { + key: 'a', + omitEmpty: true, + valueSchema: new StringSchema(), + }, + ]), + embedded: true, + }, + ]), + new Error('Duplicate key: a') + ); + + assert.throws( + () => + new NamedMapSchema([ + { + key: 'a', + omitEmpty: true, + valueSchema: new StringSchema(), + }, + { + key: 'x', + omitEmpty: true, + valueSchema: new NamedMapSchema([]), + embedded: true, + }, + ]), + new Error('Embedded entries must have an empty key') + ); + + assert.throws( + () => + new NamedMapSchema([ + { + key: 'a', + omitEmpty: true, + valueSchema: new StringSchema(), + }, + { + key: '', + omitEmpty: true, + valueSchema: new StringSchema(), + embedded: true, + }, + ]), + new Error('Embedded entry valueSchema must be a NamedMapSchema') + ); + }); + }); + describe('lossyBinaryStringConversion', () => { + const invalidUtf8String = Uint8Array.from([ + 61, 180, 118, 220, 39, 166, 43, 68, 219, 116, 105, 84, 121, 46, 122, + 136, 233, 221, 15, 174, 247, 19, 50, 176, 184, 221, 66, 188, 171, 36, + 135, 121, + ]); + + const invalidUtf8StringEncoded = bytesToString(invalidUtf8String); + const invalidUtf8StringDecoded = coerceToBytes(invalidUtf8StringEncoded); + + it('should have lossy string conversion for invalid UTF-8 string', () => { + assert.notStrictEqual(invalidUtf8String, invalidUtf8StringDecoded); + }); + + it('should lossily prepare invalid UTF-8 strings by default and when enabled', () => { + const options = { + lossyBinaryStringConversion: true, + }; + + const schema = new SpecialCaseBinaryStringSchema(); + const prepared = schema.prepareJSON(invalidUtf8String, options); + assert.strictEqual(prepared, invalidUtf8StringEncoded); + assert.deepStrictEqual( + schema.fromPreparedJSON(prepared), + invalidUtf8StringDecoded + ); + + const mapSchema = new SpecialCaseBinaryStringMapSchema(schema); + const preparedMap = mapSchema.prepareJSON( + new Map([[invalidUtf8String, invalidUtf8String]]), + options + ); + const expectedPreparedMap: Record = {}; + expectedPreparedMap[invalidUtf8StringEncoded] = + invalidUtf8StringEncoded; + assert.deepStrictEqual(preparedMap, expectedPreparedMap); + assert.deepStrictEqual( + mapSchema.fromPreparedJSON(preparedMap), + new Map([[invalidUtf8StringDecoded, invalidUtf8StringDecoded]]) + ); + }); + it('should error when preparing invalid UTF-8 strings when disabled and by default', () => { + for (const options of [{}, { lossyBinaryStringConversion: false }]) { + const schema = new SpecialCaseBinaryStringSchema(); + assert.throws( + () => schema.prepareJSON(invalidUtf8String, options), + /Invalid UTF-8 byte array encountered/ + ); + + const mapSchema = new SpecialCaseBinaryStringMapSchema(schema); + assert.throws( + () => + mapSchema.prepareJSON( + new Map([[Uint8Array.from([97]), invalidUtf8String]]), + options + ), + /Invalid UTF-8 byte array encountered/ + ); + + assert.throws( + () => + mapSchema.prepareJSON( + new Map([[invalidUtf8String, Uint8Array.from([97])]]), + options + ), + /Invalid UTF-8 byte array encountered/ + ); + + assert.throws( + () => + mapSchema.prepareJSON( + new Map([[invalidUtf8String, invalidUtf8String]]), + options + ), + /Invalid UTF-8 byte array encountered/ + ); + } + }); + }); + describe('MsgpackRawStringProvider', () => { + it('correctly records paths and provides raw strings', () => { + const baseObject = new Map([ + ['a', new Map([['a1', 'abc']])], + ['b', [new Map(), new Map([[BigInt(17), 'def']])]], + ]); + const baseProvider = new MsgpackRawStringProvider({ + baseObjectBytes: algosdk.msgpackRawEncode(baseObject), + }); + assert.strictEqual(baseProvider.getPathString(), 'root'); + assert.throws( + () => baseProvider.getRawStringAtCurrentLocation(), + /Invalid type\. Expected RawBinaryString, got/ + ); + assert.deepStrictEqual( + baseProvider.getRawStringKeysAndValuesAtCurrentLocation(), + new Map([ + [ + Uint8Array.from([97]), + new Map([ + [ + new RawBinaryString(Uint8Array.from([97, 49])), + new RawBinaryString(Uint8Array.from([97, 98, 99])), + ], + ]), + ], + [ + Uint8Array.from([98]), + [ + new Map(), + new Map([ + [ + BigInt(17), + new RawBinaryString(Uint8Array.from([100, 101, 102])), + ], + ]), + ], + ], + ]) + ); + + // Test with both string and raw string form + for (const firstKey of [ + 'a', + new RawBinaryString(Uint8Array.from([97])), + ]) { + const firstValueProvider = baseProvider.withMapValue(firstKey); + assert.strictEqual( + firstValueProvider.getPathString(), + `root -> map key "${firstKey}" (${typeof firstKey})` + ); + assert.throws( + () => firstValueProvider.getRawStringAtCurrentLocation(), + /Invalid type\. Expected RawBinaryString, got/ + ); + assert.deepStrictEqual( + firstValueProvider.getRawStringKeysAndValuesAtCurrentLocation(), + new Map([ + [ + Uint8Array.from([97, 49]), + new RawBinaryString(Uint8Array.from([97, 98, 99])), + ], + ]) + ); + + // Test with both string and raw string form + for (const firstFirstKey of [ + 'a1', + new RawBinaryString(Uint8Array.from([97, 49])), + ]) { + const firstFirstValueProvider = + firstValueProvider.withMapValue(firstFirstKey); + assert.strictEqual( + firstFirstValueProvider.getPathString(), + `root -> map key "${firstKey}" (${typeof firstKey}) -> map key "${firstFirstKey}" (${typeof firstFirstKey})` + ); + assert.deepStrictEqual( + firstFirstValueProvider.getRawStringAtCurrentLocation(), + Uint8Array.from([97, 98, 99]) + ); + assert.throws( + () => + firstFirstValueProvider.getRawStringKeysAndValuesAtCurrentLocation(), + /Invalid type\. Expected Map, got/ + ); + } + } + + // Test with both string and raw string form + for (const secondKey of [ + 'b', + new RawBinaryString(Uint8Array.from([98])), + ]) { + const secondValueProvider = baseProvider.withMapValue(secondKey); + assert.strictEqual( + secondValueProvider.getPathString(), + `root -> map key "${secondKey}" (${typeof secondKey})` + ); + assert.throws( + () => secondValueProvider.getRawStringAtCurrentLocation(), + /Invalid type\. Expected RawBinaryString, got/ + ); + assert.throws( + () => + secondValueProvider.getRawStringKeysAndValuesAtCurrentLocation(), + /Invalid type\. Expected Map, got/ + ); + + const secondIndex0Provider = secondValueProvider.withArrayElement(0); + assert.strictEqual( + secondIndex0Provider.getPathString(), + `root -> map key "${secondKey}" (${typeof secondKey}) -> array index 0 (number)` + ); + assert.throws( + () => secondIndex0Provider.getRawStringAtCurrentLocation(), + /Invalid type\. Expected RawBinaryString, got/ + ); + assert.deepStrictEqual( + secondIndex0Provider.getRawStringKeysAndValuesAtCurrentLocation(), + new Map() + ); + + const secondIndex1Provider = secondValueProvider.withArrayElement(1); + assert.strictEqual( + secondIndex1Provider.getPathString(), + `root -> map key "${secondKey}" (${typeof secondKey}) -> array index 1 (number)` + ); + assert.throws( + () => secondIndex1Provider.getRawStringAtCurrentLocation(), + /Invalid type\. Expected RawBinaryString, got/ + ); + assert.throws( + () => + secondIndex1Provider.getRawStringKeysAndValuesAtCurrentLocation(), + /Invalid type for map key\. Expected RawBinaryString, got 17 \(bigint\)/ + ); + + const secondIndex1FirstProvider = secondIndex1Provider.withMapValue( + BigInt(17) + ); + assert.strictEqual( + secondIndex1FirstProvider.getPathString(), + `root -> map key "${secondKey}" (${typeof secondKey}) -> array index 1 (number) -> map key "17" (bigint)` + ); + assert.deepStrictEqual( + secondIndex1FirstProvider.getRawStringAtCurrentLocation(), + Uint8Array.from([100, 101, 102]) + ); + assert.throws( + () => + secondIndex1FirstProvider.getRawStringKeysAndValuesAtCurrentLocation(), + /Invalid type\. Expected Map, got/ + ); + } + }); + }); + }); + describe('BlockResponse', () => { + it('should decode block response correctly', () => { + const encodedBlockResponse = algosdk.base64ToBytes( + 'gqVibG9ja94AEqJiac4AmJaAomZjzQPopGZlZXPEIAfay0ttntFBsXV2vUWa5kIdSG2j1O8iR8QJo5a4LqIho2dlbqd0ZXN0LXYxomdoxCBAkI9g4Zidj7KmD7u3TupPRyxIUOsvsCzO1IlIiDKoqKRwcmV2xCDwEylSD3iD5mcCkowyEagukv+yiXVP06RyaMykuKPaqaVwcm90b6ZmdXR1cmWjcHJwxCB/k/5DqCp+P1WW/80hkucvP2neluTb4OL7yjQQnAxn6aNybmRepnJ3Y2Fscs4AB6Ego3J3ZMQg//////////////////////////////////////////+kc2VlZMQgADr2hmA6p7J28mz5Xje3TrogRklc+wKrrknYFoSPXIqjc3B0gQCBoW7NAgCidGPNA+6idHPOZmyEZaN0eG7EILLaFZI8ZSO3lpCwDTjv6JdxgLRqnLkOCthaTJyfoaSipnR4bjI1NsQgyA7uIgAR0IxH57DVsL4snrEy5FdvuTtWPvyPiJfzZGSkdHhuc5GEomNhzwAAJGE4t9aGo2hnacOjc2lnxEAT74Xeryh/ZJtRxGqcKf8UueJmWXmHH9NuQYTIrJqzKI1kKsFHn7smLAwoa0hDSUeUGI5kvWZvM28ggFiOxykMo3R4boelY2xvc2XEIAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo2ZlZc0D6KJmdlyibHbNBESjcmN2xCABAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKNzbmTEIH+T/kOoKn4/VZb/zSGS5y8/ad6W5Nvg4vvKNBCcDGfppHR5cGWjcGF5pGNlcnSEpHByb3CDo2RpZ8Qgl8J9zpVrkwvOVTZlN3p6b0iKuUpWii4Z0ga04n/XeFGmZW5jZGlnxCCG+emw6b9UVPNwpFN+l3F4SDOSkIKxkgpWSiBi0O62q6VvcHJvcMQgf5P+Q6gqfj9Vlv/NIZLnLz9p3pbk2+Di+8o0EJwMZ+mjcm5kXqRzdGVwAqR2b3RlkYOkY3JlZIGicGbEUIcs4SBw5LBFVDrqyGzHbeuh/PsY5Fr/1oZ+DoPl+N8aAs2ZiEsuPoE/+6oNsiX6YJNFVSBQKaRQBWdPDndXc9w3jq6WR6cEEoi4rCAyyv8Io3NpZ4ahcMQgmOdtSatJuUOlf8qRypCU3uEm3AewgEq+xVOIhtmWUZejcDFzxEAbCsynu50W2/vt6HPCCTAf37rvW1RHk1Y8EnLvBrFIzxi6nZRPeoYdgr4D8yIEKB4Gc7BMFrQbzd1HGKxLKS8GonAyxCCaal9Yfd6VseKV5WCb5lFEeYo3J0X1uOxEspMS8z9uaKNwMnPEQGmZZYbLBiwlzCbu7pdhDl9jSsyWCKW5aM5u0jeSVJGdYzC5SwpVGXlasYN8yj0Z5DKqwweY0ATwg02PCK1xkw2icHPEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChc8RAD7PBhIk/Yl40TpUojfBQj79CHOXwpmAToXgmRWG2rkDvmIh4sUjAaXVHdFla1uBMqgLrOk1rEo12QUthenYrB6NzbmTEIH+T/kOoKn4/VZb/zSGS5y8/ad6W5Nvg4vvKNBCcDGfp' + ); + const blockResponse = algosdk.decodeMsgpack( + encodedBlockResponse, + algosdk.modelsv2.BlockResponse + ); + const expectedBlockResponse = new algosdk.modelsv2.BlockResponse({ + block: new algosdk.Block({ + header: new algosdk.BlockHeader({ + round: BigInt(94), + branch: algosdk.base64ToBytes( + '8BMpUg94g+ZnApKMMhGoLpL/sol1T9OkcmjMpLij2qk=' + ), + seed: algosdk.base64ToBytes( + 'ADr2hmA6p7J28mz5Xje3TrogRklc+wKrrknYFoSPXIo=' + ), + txnCommitments: new algosdk.TxnCommitments({ + nativeSha512_256Commitment: algosdk.base64ToBytes( + 'stoVkjxlI7eWkLANOO/ol3GAtGqcuQ4K2FpMnJ+hpKI=' + ), + sha256Commitment: algosdk.base64ToBytes( + 'yA7uIgAR0IxH57DVsL4snrEy5FdvuTtWPvyPiJfzZGQ=' + ), + }), + timestamp: BigInt(1718387813), + genesisID: 'test-v1', + genesisHash: algosdk.base64ToBytes( + 'QJCPYOGYnY+ypg+7t07qT0csSFDrL7AsztSJSIgyqKg=' + ), + proposer: new algosdk.Address( + algosdk.base64ToBytes( + 'f5P+Q6gqfj9Vlv/NIZLnLz9p3pbk2+Di+8o0EJwMZ+k=' + ) + ), + feesCollected: BigInt(1000), + bonus: BigInt(10000000), + proposerPayout: BigInt(0), + rewardState: new algosdk.RewardState({ + feeSink: new algosdk.Address( + algosdk.base64ToBytes( + 'B9rLS22e0UGxdXa9RZrmQh1IbaPU7yJHxAmjlrguoiE=' + ) + ), + rewardsPool: new algosdk.Address( + algosdk.base64ToBytes( + '//////////////////////////////////////////8=' + ) + ), + rewardsLevel: BigInt(0), + rewardsRate: BigInt(0), + rewardsResidue: BigInt(0), + rewardsRecalculationRound: BigInt(500000), + }), + upgradeState: new algosdk.UpgradeState({ + currentProtocol: 'future', + nextProtocol: '', + nextProtocolApprovals: BigInt(0), + nextProtocolVoteBefore: BigInt(0), + nextProtocolSwitchOn: BigInt(0), + }), + upgradeVote: new algosdk.UpgradeVote({ + upgradePropose: '', + upgradeDelay: BigInt(0), + upgradeApprove: false, + }), + txnCounter: BigInt(1006), + stateproofTracking: new Map( + [ + [ + 0, + new algosdk.StateProofTrackingData({ + stateProofVotersCommitment: new Uint8Array(), + stateProofOnlineTotalWeight: BigInt(0), + stateProofNextRound: BigInt(512), + }), + ], + ] + ), + participationUpdates: new algosdk.ParticipationUpdates({ + expiredParticipationAccounts: [], + absentParticipationAccounts: [], + }), + }), + payset: [ + new algosdk.SignedTxnInBlock({ + hasGenesisID: true, + hasGenesisHash: false, + signedTxn: new algosdk.SignedTxnWithAD({ + signedTxn: new algosdk.SignedTransaction({ + txn: new algosdk.Transaction({ + sender: new algosdk.Address( + algosdk.base64ToBytes( + 'f5P+Q6gqfj9Vlv/NIZLnLz9p3pbk2+Di+8o0EJwMZ+k=' + ) + ), + type: algosdk.TransactionType.pay, + suggestedParams: { + flatFee: true, + fee: BigInt(1000), + firstValid: BigInt(92), + lastValid: BigInt(1092), + minFee: BigInt(1000), + }, + paymentParams: { + amount: 0, + receiver: new algosdk.Address( + algosdk.base64ToBytes( + 'AQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' + ) + ), + closeRemainderTo: new algosdk.Address( + algosdk.base64ToBytes( + 'AQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' + ) + ), + }, + }), + sig: algosdk.base64ToBytes( + 'E++F3q8of2SbUcRqnCn/FLniZll5hx/TbkGEyKyasyiNZCrBR5+7JiwMKGtIQ0lHlBiOZL1mbzNvIIBYjscpDA==' + ), + }), + applyData: new algosdk.ApplyData({ + closingAmount: BigInt('39999981999750'), + }), + }), + }), + ], + }), + cert: new algosdk.UntypedValue( + new Map([ + [ + 'prop', + new Map([ + [ + 'dig', + algosdk.base64ToBytes( + 'l8J9zpVrkwvOVTZlN3p6b0iKuUpWii4Z0ga04n/XeFE=' + ), + ], + [ + 'encdig', + algosdk.base64ToBytes( + 'hvnpsOm/VFTzcKRTfpdxeEgzkpCCsZIKVkogYtDutqs=' + ), + ], + [ + 'oprop', + algosdk.base64ToBytes( + 'f5P+Q6gqfj9Vlv/NIZLnLz9p3pbk2+Di+8o0EJwMZ+k=' + ), + ], + ]), + ], + ['rnd', BigInt(94)], + ['step', BigInt(2)], + [ + 'vote', + [ + new Map([ + [ + 'cred', + new Map([ + [ + 'pf', + algosdk.base64ToBytes( + 'hyzhIHDksEVUOurIbMdt66H8+xjkWv/Whn4Og+X43xoCzZmISy4+gT/7qg2yJfpgk0VVIFAppFAFZ08Od1dz3DeOrpZHpwQSiLisIDLK/wg=' + ), + ], + ]), + ], + [ + 'sig', + new Map([ + [ + 'p1s', + algosdk.base64ToBytes( + 'GwrMp7udFtv77ehzwgkwH9+671tUR5NWPBJy7waxSM8Yup2UT3qGHYK+A/MiBCgeBnOwTBa0G83dRxisSykvBg==' + ), + ], + [ + 'p2', + algosdk.base64ToBytes( + 'mmpfWH3elbHileVgm+ZRRHmKNydF9bjsRLKTEvM/bmg=' + ), + ], + [ + 'p2s', + algosdk.base64ToBytes( + 'aZllhssGLCXMJu7ul2EOX2NKzJYIpblozm7SN5JUkZ1jMLlLClUZeVqxg3zKPRnkMqrDB5jQBPCDTY8IrXGTDQ==' + ), + ], + [ + 'p', + algosdk.base64ToBytes( + 'mOdtSatJuUOlf8qRypCU3uEm3AewgEq+xVOIhtmWUZc=' + ), + ], + [ + 'ps', + algosdk.base64ToBytes( + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==' + ), + ], + [ + 's', + algosdk.base64ToBytes( + 'D7PBhIk/Yl40TpUojfBQj79CHOXwpmAToXgmRWG2rkDvmIh4sUjAaXVHdFla1uBMqgLrOk1rEo12QUthenYrBw==' + ), + ], + ]), + ], + [ + 'snd', + algosdk.base64ToBytes( + 'f5P+Q6gqfj9Vlv/NIZLnLz9p3pbk2+Di+8o0EJwMZ+k=' + ), + ], + ]), + ], + ], + ]) + ), + }); + assert.deepStrictEqual(blockResponse, expectedBlockResponse); + const reencoded = algosdk.encodeMsgpack(blockResponse); + assert.deepStrictEqual(reencoded, encodedBlockResponse); + }); + it('should decode ApplyData correctly', () => { + const encodedApplyData = algosdk.base64ToBytes( + 'iKNhY2HP//////////+kYXBpZM0iuKJjYc8AACRhOLfWhqRjYWlkzR5homR0haJnZIKqZ2xvYmFsS2V5MYKiYXQBomJzo2FiY6pnbG9iYWxLZXkygqJhdAKidWkyo2l0eJGComR0gaJsZ5KkbG9nM6Rsb2c0o3R4boakYXBpZM0eYaNmZWXNA+iiZnZcomx2zQREo3NuZMQgf5P+Q6gqfj9Vlv/NIZLnLz9p3pbk2+Di+8o0EJwMZ+mkdHlwZaRhcHBsomxkggCBqWxvY2FsS2V5MYKiYXQBomJzo2RlZgKBqWxvY2FsS2V5MoKiYXQConVpM6JsZ5KkbG9nMaRsb2cyonNhksQgCbEzlTT2uNiZwobypXnCOg5IqgxtO92MuwR8vJwv3ePEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAonJjEaJycgSicnN7' + ); + const applyData = algosdk.decodeMsgpack( + encodedApplyData, + algosdk.ApplyData + ); + const expectedApplyData = new algosdk.ApplyData({ + closingAmount: BigInt('39999981999750'), + assetClosingAmount: BigInt('0xffffffffffffffff'), + senderRewards: BigInt(123), + receiverRewards: BigInt(4), + closeRewards: BigInt(17), + configAsset: BigInt(7777), + applicationID: BigInt(8888), + evalDelta: new algosdk.EvalDelta({ + globalDelta: new Map([ + [ + algosdk.coerceToBytes('globalKey1'), + new algosdk.ValueDelta({ + action: 1, + uint: BigInt(0), + bytes: algosdk.coerceToBytes('abc'), + }), + ], + [ + algosdk.coerceToBytes('globalKey2'), + new algosdk.ValueDelta({ + action: 2, + uint: BigInt(50), + bytes: new Uint8Array(), + }), + ], + ]), + localDeltas: new Map>([ + [ + 0, + new Map([ + [ + algosdk.coerceToBytes('localKey1'), + new algosdk.ValueDelta({ + action: 1, + uint: BigInt(0), + bytes: algosdk.coerceToBytes('def'), + }), + ], + ]), + ], + [ + 2, + new Map([ + [ + algosdk.coerceToBytes('localKey2'), + new algosdk.ValueDelta({ + action: 2, + uint: BigInt(51), + bytes: new Uint8Array(), + }), + ], + ]), + ], + ]), + sharedAccts: [ + algosdk.Address.fromString( + 'BGYTHFJU624NRGOCQ3ZKK6OCHIHERKQMNU553DF3AR6LZHBP3XR5JLNCUI' + ), + algosdk.Address.zeroAddress(), + ], + logs: [algosdk.coerceToBytes('log1'), algosdk.coerceToBytes('log2')], + innerTxns: [ + new algosdk.SignedTxnWithAD({ + signedTxn: new algosdk.SignedTransaction({ + txn: new algosdk.Transaction({ + sender: new algosdk.Address( + algosdk.base64ToBytes( + 'f5P+Q6gqfj9Vlv/NIZLnLz9p3pbk2+Di+8o0EJwMZ+k=' + ) + ), + type: algosdk.TransactionType.appl, + suggestedParams: { + flatFee: true, + fee: BigInt(1000), + firstValid: BigInt(92), + lastValid: BigInt(1092), + minFee: BigInt(1000), + }, + appCallParams: { + appIndex: BigInt(7777), + onComplete: algosdk.OnApplicationComplete.NoOpOC, + }, + }), + }), + applyData: new algosdk.ApplyData({ + evalDelta: new algosdk.EvalDelta({ + logs: [ + algosdk.coerceToBytes('log3'), + algosdk.coerceToBytes('log4'), + ], + }), + }), + }), + ], + }), + }); + assert.deepStrictEqual(applyData, expectedApplyData); + const reencoded = algosdk.encodeMsgpack(applyData); + assert.deepStrictEqual(reencoded, encodedApplyData); + }); + it('should decode EvalDelta with invalid UTF-8 strings correctly', () => { + const encodedEvalDelta = algosdk.base64ToBytes( + 'haJnZIKkZzH+/4KiYXQBomJzpHYx/v+kZzL+/4KiYXQConVpMqNpdHiRgqJkdIGibGeSpmxvZzP+/6b+/2xvZzSjdHhuhqRhcGlkzR5ho2ZlZc0D6KJmdlyibHbNBESjc25kxCB/k/5DqCp+P1WW/80hkucvP2neluTb4OL7yjQQnAxn6aR0eXBlpGFwcGyibGSCAIGkbDH+/4KiYXQBomJzpHYy/v8CgaRsMv7/gqJhdAKidWkzomxnkqZsb2cx/v+m/v9sb2cyonNhksQgCbEzlTT2uNiZwobypXnCOg5IqgxtO92MuwR8vJwv3ePEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ); + const evalDelta = algosdk.decodeMsgpack( + encodedEvalDelta, + algosdk.EvalDelta + ); + const invalidUtf8 = algosdk.base64ToBytes('/v8='); + const expectedEvalDelta = new algosdk.EvalDelta({ + globalDelta: new Map([ + [ + utils.concatArrays(algosdk.coerceToBytes('g1'), invalidUtf8), + new algosdk.ValueDelta({ + action: 1, + uint: BigInt(0), + bytes: utils.concatArrays( + algosdk.coerceToBytes('v1'), + invalidUtf8 + ), + }), + ], + [ + utils.concatArrays(algosdk.coerceToBytes('g2'), invalidUtf8), + new algosdk.ValueDelta({ + action: 2, + uint: BigInt(50), + bytes: new Uint8Array(), + }), + ], + ]), + localDeltas: new Map>([ + [ + 0, + new Map([ + [ + utils.concatArrays(algosdk.coerceToBytes('l1'), invalidUtf8), + new algosdk.ValueDelta({ + action: 1, + uint: BigInt(0), + bytes: utils.concatArrays( + algosdk.coerceToBytes('v2'), + invalidUtf8 + ), + }), + ], + ]), + ], + [ + 2, + new Map([ + [ + utils.concatArrays(algosdk.coerceToBytes('l2'), invalidUtf8), + new algosdk.ValueDelta({ + action: 2, + uint: BigInt(51), + bytes: new Uint8Array(), + }), + ], + ]), + ], + ]), + sharedAccts: [ + algosdk.Address.fromString( + 'BGYTHFJU624NRGOCQ3ZKK6OCHIHERKQMNU553DF3AR6LZHBP3XR5JLNCUI' + ), + algosdk.Address.zeroAddress(), + ], + logs: [ + utils.concatArrays(algosdk.coerceToBytes('log1'), invalidUtf8), + utils.concatArrays(invalidUtf8, algosdk.coerceToBytes('log2')), + ], + innerTxns: [ + new algosdk.SignedTxnWithAD({ + signedTxn: new algosdk.SignedTransaction({ + txn: new algosdk.Transaction({ + sender: new algosdk.Address( + algosdk.base64ToBytes( + 'f5P+Q6gqfj9Vlv/NIZLnLz9p3pbk2+Di+8o0EJwMZ+k=' + ) + ), + type: algosdk.TransactionType.appl, + suggestedParams: { + flatFee: true, + fee: BigInt(1000), + firstValid: BigInt(92), + lastValid: BigInt(1092), + minFee: BigInt(1000), + }, + appCallParams: { + appIndex: BigInt(7777), + onComplete: algosdk.OnApplicationComplete.NoOpOC, + }, + }), + }), + applyData: new algosdk.ApplyData({ + evalDelta: new algosdk.EvalDelta({ + logs: [ + utils.concatArrays( + algosdk.coerceToBytes('log3'), + invalidUtf8 + ), + utils.concatArrays( + invalidUtf8, + algosdk.coerceToBytes('log4') + ), + ], + }), + }), + }), + ], + }); + assert.deepStrictEqual(evalDelta, expectedEvalDelta); + const reencoded = algosdk.encodeMsgpack(evalDelta); + assert.deepStrictEqual(reencoded, encodedEvalDelta); + }); + }); + describe('LedgerStateDelta', () => { + it('should decode LedgerStateDelta correctly', async () => { + async function loadResource(name: string): Promise { + const res = await fetch( + `http://localhost:8080/tests/resources/${name}` + ); + if (!res.ok) { + throw new Error(`Failed to load resource (${res.status}): ${name}`); + } + return new Uint8Array(await res.arrayBuffer()); + } + + const stateDeltaBytes = await loadResource( + 'groupdelta-betanet_23963123_2.msgp' + ); + const stateDelta = algosdk.decodeMsgpack( + stateDeltaBytes, + algosdk.LedgerStateDelta + ); + + const expectedStateDelta = new algosdk.LedgerStateDelta({ + accounts: new algosdk.AccountDeltas({ + accounts: [ + new algosdk.BalanceRecord({ + addr: algosdk.Address.fromString( + 'TILB4MAJIUF56ZBE7CDOWOXDR57IXZFJUHJARQPW3JDEVKMU56HP3A6A54' + ), + accountData: new algosdk.AccountData({ + accountBaseData: new algosdk.AccountBaseData({ + status: 0, + microAlgos: BigInt(377962010), + rewardsBase: BigInt(12595), + rewardedMicroAlgos: BigInt(0), + authAddr: algosdk.Address.zeroAddress(), + incentiveEligible: false, + totalAppSchema: new algosdk.StateSchema({ + numUints: 2675, + numByteSlices: 2962, + }), + totalExtraAppPages: 740, + totalAppParams: BigInt(459), + totalAppLocalStates: BigInt(37), + totalAssetParams: BigInt(23), + totalAssets: BigInt(110), + totalBoxes: BigInt(0), + totalBoxBytes: BigInt(0), + lastProposed: BigInt(0), + lastHeartbeat: BigInt(0), + }), + votingData: new algosdk.VotingData({ + voteID: new Uint8Array(32), + selectionID: new Uint8Array(32), + stateProofID: new Uint8Array(64), + voteFirstValid: BigInt(0), + voteLastValid: BigInt(0), + voteKeyDilution: BigInt(0), + }), + }), + }), + new algosdk.BalanceRecord({ + addr: algosdk.Address.fromString( + 'A7NMWS3NT3IUDMLVO26ULGXGIIOUQ3ND2TXSER6EBGRZNOBOUIQXHIBGDE' + ), + accountData: new algosdk.AccountData({ + accountBaseData: new algosdk.AccountBaseData({ + status: 2, + microAlgos: BigInt(1529589813809), + rewardsBase: BigInt(0), + rewardedMicroAlgos: BigInt(0), + authAddr: algosdk.Address.zeroAddress(), + incentiveEligible: false, + totalAppSchema: new algosdk.StateSchema({ + numUints: 0, + numByteSlices: 0, + }), + totalExtraAppPages: 0, + totalAppParams: BigInt(0), + totalAppLocalStates: BigInt(0), + totalAssetParams: BigInt(0), + totalAssets: BigInt(0), + totalBoxes: BigInt(0), + totalBoxBytes: BigInt(0), + lastProposed: BigInt(0), + lastHeartbeat: BigInt(0), + }), + votingData: new algosdk.VotingData({ + voteID: new Uint8Array(32), + selectionID: new Uint8Array(32), + stateProofID: new Uint8Array(64), + voteFirstValid: BigInt(0), + voteLastValid: BigInt(0), + voteKeyDilution: BigInt(0), + }), + }), + }), + new algosdk.BalanceRecord({ + addr: algosdk.Address.fromString( + 'DSR7TNPLYXGPINSZOC76OYLXNAH6VITLH7BYO5HWLLWUOUI365LD62IHSA' + ), + accountData: new algosdk.AccountData({ + accountBaseData: new algosdk.AccountBaseData({ + status: 0, + microAlgos: BigInt(100000), + rewardsBase: BigInt(12595), + rewardedMicroAlgos: BigInt(0), + authAddr: algosdk.Address.zeroAddress(), + incentiveEligible: false, + totalAppSchema: new algosdk.StateSchema({ + numUints: 0, + numByteSlices: 0, + }), + totalExtraAppPages: 0, + totalAppParams: BigInt(0), + totalAppLocalStates: BigInt(0), + totalAssetParams: BigInt(0), + totalAssets: BigInt(0), + totalBoxes: BigInt(0), + totalBoxBytes: BigInt(0), + lastProposed: BigInt(0), + lastHeartbeat: BigInt(0), + }), + votingData: new algosdk.VotingData({ + voteID: new Uint8Array(32), + selectionID: new Uint8Array(32), + stateProofID: new Uint8Array(64), + voteFirstValid: BigInt(0), + voteLastValid: BigInt(0), + voteKeyDilution: BigInt(0), + }), + }), + }), + new algosdk.BalanceRecord({ + addr: algosdk.Address.fromString( + '5UA72YDDTT7VLRMVHDRWCUOTWMWBH5XOB4MJRYTMKDNV3GEVYY5JMT5KXM' + ), + accountData: new algosdk.AccountData({ + accountBaseData: new algosdk.AccountBaseData({ + status: 0, + microAlgos: BigInt(243300), + rewardsBase: BigInt(12595), + rewardedMicroAlgos: BigInt(0), + authAddr: algosdk.Address.zeroAddress(), + incentiveEligible: false, + totalAppSchema: new algosdk.StateSchema({ + numUints: 0, + numByteSlices: 0, + }), + totalExtraAppPages: 0, + totalAppParams: BigInt(0), + totalAppLocalStates: BigInt(0), + totalAssetParams: BigInt(0), + totalAssets: BigInt(0), + totalBoxes: BigInt(1), + totalBoxBytes: BigInt(331), + lastProposed: BigInt(0), + lastHeartbeat: BigInt(0), + }), + votingData: new algosdk.VotingData({ + voteID: new Uint8Array(32), + selectionID: new Uint8Array(32), + stateProofID: new Uint8Array(64), + voteFirstValid: BigInt(0), + voteLastValid: BigInt(0), + voteKeyDilution: BigInt(0), + }), + }), + }), + ], + appResources: [ + new algosdk.AppResourceRecord({ + id: BigInt(1508981233), + address: algosdk.Address.fromString( + 'TILB4MAJIUF56ZBE7CDOWOXDR57IXZFJUHJARQPW3JDEVKMU56HP3A6A54' + ), + params: new algosdk.AppParamsDelta({ + deleted: false, + params: new algosdk.AppParams({ + approvalProgram: algosdk.base64ToBytes( + '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' + ), + clearStateProgram: algosdk.base64ToBytes( + 'CCADAQAIJgMKTnVtU3Rha2VycwtUb3RhbFN0YWtlZAxUb3RhbFJld2FyZHMxGyMSQAABAIgAAiJDMQCABlN0YWtlZGI1AikpZDQCCWc0AiOIAEwqZDUAMQCADkFjY3J1ZWRSZXdhcmRzYjUBIzUDNAOBBwxBACI0ADQDJAs0ADQDJAtbNAE0AyQLWwkWXTUANAMiCDUDQv/WKjQAZyJDNQU1BDQENAUUEEAAEjQEFDQFEEEADygoZCIIZ0IABigoZCIJZ4k=' + ), + globalState: new Map([ + [ + algosdk.coerceToBytes('Admin'), + new algosdk.TealValue({ + type: 1, + bytes: algosdk.base64ToBytes( + 'mhYeMAlFC99kJPiG6zrjj36L5Kmh0gjB9tpGSqmU744=' + ), + }), + ], + [ + algosdk.coerceToBytes('CONTRACT_NAME'), + new algosdk.TealValue({ + type: 1, + bytes: algosdk.coerceToBytes('PACT FARM'), + }), + ], + [ + algosdk.coerceToBytes('ClaimedRewards'), + new algosdk.TealValue({ + type: 1, + bytes: algosdk.base64ToBytes( + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' + ), + }), + ], + [ + algosdk.coerceToBytes('Duration'), + new algosdk.TealValue({ + type: 2, + }), + ], + [ + algosdk.coerceToBytes('NextDuration'), + new algosdk.TealValue({ + type: 2, + }), + ], + [ + algosdk.coerceToBytes('NextRewards'), + new algosdk.TealValue({ + type: 1, + bytes: algosdk.base64ToBytes( + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' + ), + }), + ], + [ + algosdk.coerceToBytes('NumStakers'), + new algosdk.TealValue({ + type: 2, + }), + ], + [ + algosdk.coerceToBytes('PendingRewards'), + new algosdk.TealValue({ + type: 1, + bytes: algosdk.base64ToBytes( + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' + ), + }), + ], + [ + algosdk.coerceToBytes('RPT'), + new algosdk.TealValue({ + type: 1, + bytes: algosdk.base64ToBytes( + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' + ), + }), + ], + [ + algosdk.coerceToBytes('RPT_frac'), + new algosdk.TealValue({ + type: 1, + bytes: algosdk.base64ToBytes( + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' + ), + }), + ], + [ + algosdk.coerceToBytes('RewardAssetIDs'), + new algosdk.TealValue({ + type: 1, + }), + ], + [ + algosdk.coerceToBytes('StakedAssetID'), + new algosdk.TealValue({ + type: 2, + uint: BigInt(156390370), + }), + ], + [ + algosdk.coerceToBytes('TotalRewards'), + new algosdk.TealValue({ + type: 1, + bytes: algosdk.base64ToBytes( + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' + ), + }), + ], + [ + algosdk.coerceToBytes('TotalStaked'), + new algosdk.TealValue({ + type: 2, + }), + ], + [ + algosdk.coerceToBytes('UpdatedAt'), + new algosdk.TealValue({ + type: 2, + uint: BigInt(1675257832), + }), + ], + [ + algosdk.coerceToBytes('Updater'), + new algosdk.TealValue({ + type: 1, + bytes: algosdk.base64ToBytes( + 'mhYeMAlFC99kJPiG6zrjj36L5Kmh0gjB9tpGSqmU744=' + ), + }), + ], + [ + algosdk.coerceToBytes('VERSION'), + new algosdk.TealValue({ + type: 2, + uint: BigInt(100), + }), + ], + ]), + localStateSchema: new algosdk.StateSchema({ + numByteSlices: 4, + numUints: 2, + }), + globalStateSchema: new algosdk.StateSchema({ + numByteSlices: 10, + numUints: 7, + }), + extraProgramPages: 2, + }), + }), + state: new algosdk.AppLocalStateDelta({ + deleted: false, + }), + }), + ], + assetResources: [], + }), + kvMods: new Map([ + [ + algosdk.base64ToBytes('Yng6AAAAAFnxOfFFc2Nyb3c='), + new algosdk.KvValueDelta({ + data: algosdk.base64ToBytes( + 'CCAEAAEEBiYDC01hc3RlckFwcElEAQEBADIJMQASRDEZQADyMRhAAEU2GgCABDiIGnESRCg2GgEXwDJnsSWyEDYaAhfAMrIYgAS3NV/RshoisgGzsSSyEDIKshQishI2GgMXwDCyESKyAbNCANU2GgCABHiCLPASQABONhoAgAS3WNjREkAAKTYaAIAEm+QoGxJAAAEAsSOyEDYaARfAHLIHIrIBNhoCVwIAsgWzQgCSsSOyEDIJsgcyCmAyCngJsggisgGzQgB6sSSyEDIJshQ2GgIXshI2GgEXwDCyESKyAbOxJbIQKGSyGIAEwxQK57IaKbIaKrIaKbIaKrIaMgmyHDIIsjI2GgEXwDCyMCKyAbNCAC0xGYEFEkAAAQAyCShkYRREsSSyEDYwALIRMgmyFSKyAbOxI7IQMgmyCSKyAbMjQw==' + ), + oldData: algosdk.base64ToBytes( + 'CCAEAAEEBiYDC01hc3RlckFwcElEAQEBADIJMQASRDEZQADyMRhAAEU2GgCABDiIGnESRCg2GgEXwDJnsSWyEDYaAhfAMrIYgAS3NV/RshoisgGzsSSyEDIKshQishI2GgMXwDCyESKyAbNCANU2GgCABHiCLPASQABONhoAgAS3WNjREkAAKTYaAIAEm+QoGxJAAAEAsSOyEDYaARfAHLIHIrIBNhoCVwIAsgWzQgCSsSOyEDIJsgcyCmAyCngJsggisgGzQgB6sSSyEDIJshQ2GgIXshI2GgEXwDCyESKyAbOxJbIQKGSyGIAEwxQK57IaKbIaKrIaKbIaKrIaMgmyHDIIsjI2GgEXwDCyMCKyAbNCAC0xGYEFEkAAAQAyCShkYRREsSSyEDYwALIRMgmyFSKyAbOxI7IQMgmyCSKyAbMjQw==' + ), + }), + ], + ]), + txids: new Map([ + [ + algosdk.base64ToBytes( + 'g3NWme3GAy5uHfd8BQO06da2MjdGJ9EuuikeSD3Nuqk=' + ), + new algosdk.IncludedTransactions({ + lastValid: BigInt(23964120), + intra: 1, + }), + ], + [ + algosdk.base64ToBytes( + 'j6CIOjVZijXyqqTJA4xJjoA4oSmiM6Il5qsV/O3H3+Q=' + ), + new algosdk.IncludedTransactions({ + lastValid: BigInt(23964120), + intra: 0, + }), + ], + ]), + txleases: new algosdk.UntypedValue(undefined), + creatables: new Map([ + [ + BigInt(1508981233), + new algosdk.ModifiedCreatable({ + creatableType: 1, + created: true, + creator: algosdk.Address.fromString( + 'TILB4MAJIUF56ZBE7CDOWOXDR57IXZFJUHJARQPW3JDEVKMU56HP3A6A54' + ), + ndeltas: 0, + }), + ], + ]), + blockHeader: new algosdk.BlockHeader({ + round: BigInt(23963123), + branch: algosdk.base64ToBytes( + 'NPCkBgM/t8nRvRaaVqSeWHCyYUdxEghgQglgtERCuqE=' + ), + seed: algosdk.base64ToBytes( + 'yxhfocGJCuC+DKVcfgwo0juV9jNEUvMiU1uJl0Y1MNk=' + ), + txnCommitments: new algosdk.TxnCommitments({ + nativeSha512_256Commitment: algosdk.base64ToBytes( + 'FIrR4OYcMHA4fhT2vEScSvbaCkETZd+BPtttEQi8DiI=' + ), + sha256Commitment: algosdk.base64ToBytes( + 'Hj1OQRa1jURkxJkRtXOKTrKSrm/MIrP5wmTnUuNq3ew=' + ), + }), + timestamp: BigInt(1675257836), + genesisID: 'betanet-v1.0', + genesisHash: algosdk.base64ToBytes( + 'mFgazF+2uRS1tMiL9dsj01hJGySEmPN28B/TjjvpVW0=' + ), + proposer: algosdk.Address.zeroAddress(), + feesCollected: BigInt(0), + bonus: BigInt(0), + proposerPayout: BigInt(0), + rewardState: new algosdk.RewardState({ + feeSink: algosdk.Address.fromString( + 'A7NMWS3NT3IUDMLVO26ULGXGIIOUQ3ND2TXSER6EBGRZNOBOUIQXHIBGDE' + ), + rewardsPool: algosdk.Address.fromString( + '7777777777777777777777777777777777777777777777777774MSJUVU' + ), + rewardsLevel: BigInt(12595), + rewardsRate: BigInt(0), + rewardsResidue: BigInt(3846799357), + rewardsRecalculationRound: BigInt(24000000), + }), + upgradeState: new algosdk.UpgradeState({ + currentProtocol: + 'https://github.com/algorandfoundation/specs/tree/44fa607d6051730f5264526bf3c108d51f0eadb6', + nextProtocol: '', + nextProtocolApprovals: BigInt(0), + nextProtocolVoteBefore: BigInt(0), + nextProtocolSwitchOn: BigInt(0), + }), + upgradeVote: new algosdk.UpgradeVote({ + upgradePropose: '', + upgradeDelay: BigInt(0), + upgradeApprove: false, + }), + txnCounter: BigInt(1508981323), + stateproofTracking: new Map([ + [ + 0, + new algosdk.StateProofTrackingData({ + stateProofVotersCommitment: new Uint8Array(), + stateProofOnlineTotalWeight: BigInt(0), + stateProofNextRound: BigInt(23963136), + }), + ], + ]), + participationUpdates: new algosdk.ParticipationUpdates({ + expiredParticipationAccounts: [], + absentParticipationAccounts: [], + }), + }), + stateProofNext: BigInt(0), + prevTimestamp: BigInt(0), + totals: new algosdk.AccountTotals({ + online: new algosdk.AlgoCount({ + money: BigInt(0), + rewardUnits: BigInt(0), + }), + offline: new algosdk.AlgoCount({ + money: BigInt(0), + rewardUnits: BigInt(0), + }), + notParticipating: new algosdk.AlgoCount({ + money: BigInt(0), + rewardUnits: BigInt(0), + }), + rewardsLevel: BigInt(0), + }), + }); + + assert.deepStrictEqual(stateDelta, expectedStateDelta); + + // Avoid comparing reencoded to stateDeltaBytes because this SDK uses omit empty for the fields, + // so the produced encoding will be different. Instead we decode and compare again. + const reencoded = algosdk.encodeMsgpack(stateDelta); + const roundTripDecoded = algosdk.decodeMsgpack( + reencoded, + algosdk.LedgerStateDelta + ); + assert.deepStrictEqual(roundTripDecoded, expectedStateDelta); + }); + }); +}); diff --git a/tests/3.Address.js b/tests/3.Address.ts similarity index 69% rename from tests/3.Address.js rename to tests/3.Address.ts index 73ac2b2c1..71f261eca 100644 --- a/tests/3.Address.js +++ b/tests/3.Address.ts @@ -1,8 +1,8 @@ /* eslint-env mocha */ -const assert = require('assert'); -const nacl = require('../src/nacl/naclWrappers'); -const algosdk = require('../src/index'); -const address = require('../src/encoding/address'); +import assert from 'assert'; +import * as nacl from '../src/nacl/naclWrappers.js'; +import algosdk from '../src/index.js'; +import * as address from '../src/encoding/address.js'; describe('address', () => { describe('#isValid', () => { @@ -13,7 +13,7 @@ describe('address', () => { const correctChecksum = new Uint8Array([122, 240, 2, 74]); const malformedAddress1 = 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJ'; - const maldformedAddress2 = 123; + const malformedAddress2 = 123 as any; const malformedAddress3 = 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACererZQGI113RBSRVYHV4ACJI'; const wrongChecksumAddress = @@ -23,7 +23,7 @@ describe('address', () => { it('should verify a valid Algorand address', () => { const decodedAddress = algosdk.decodeAddress(correctCase); assert.deepStrictEqual(decodedAddress.publicKey, correctPublicKey); - assert.deepStrictEqual(decodedAddress.checksum, correctChecksum); + assert.deepStrictEqual(decodedAddress.checksum(), correctChecksum); }); it('should fail to verify a malformed Algorand address', () => { @@ -31,30 +31,26 @@ describe('address', () => { () => { algosdk.decodeAddress(malformedAddress1); }, - (err) => err.message === address.MALFORMED_ADDRESS_ERROR_MSG + (err: Error) => + err.message.includes(address.MALFORMED_ADDRESS_ERROR_MSG) ); assert.throws( () => { - algosdk.decodeAddress(maldformedAddress2); + algosdk.decodeAddress(malformedAddress2); }, - (err) => err.message === address.MALFORMED_ADDRESS_ERROR_MSG + (err: Error) => + err.message.includes(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' - ); + assert.throws(() => { + algosdk.decodeAddress(malformedAddress3); + }, new Error('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 - ); + assert.throws(() => { + algosdk.decodeAddress(wrongChecksumAddress); + }, new Error(address.CHECKSUM_ADDRESS_ERROR_MSG)); }); // Check helper functions @@ -74,6 +70,17 @@ describe('address', () => { assert.ok(algosdk.isValidAddress(addr)); }); + it('should throw an error for addresses with incorrect length', () => { + const pk = nacl.randomBytes(15); + assert.throws( + () => { + algosdk.encodeAddress(pk); + }, + (err: Error) => + err.message.includes(address.MALFORMED_ADDRESS_ERROR_MSG) + ); + }); + it('should be able to encode and decode an address', () => { const pk = nacl.randomBytes(32); const addr = algosdk.encodeAddress(pk); @@ -95,10 +102,10 @@ describe('address', () => { threshold: 2, addrs: [addr1, addr2, addr3], }; - const expectAddr = - 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM'; + const expectAddr = algosdk.Address.fromString( + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM' + ); const actualAddr = algosdk.multisigAddress(params); - assert.ok(algosdk.isValidAddress(actualAddr)); assert.deepStrictEqual(actualAddr, expectAddr); }); }); @@ -106,10 +113,20 @@ describe('address', () => { describe('#getApplicationAddress', () => { it('should produce the correct address', () => { const appID = 77; - const expected = - 'PCYUFPA2ZTOYWTP43MX2MOX2OWAIAXUDNC2WFCXAGMRUZ3DYD6BWFDL5YM'; + const expected = algosdk.Address.fromString( + 'PCYUFPA2ZTOYWTP43MX2MOX2OWAIAXUDNC2WFCXAGMRUZ3DYD6BWFDL5YM' + ); const actual = algosdk.getApplicationAddress(appID); - assert.strictEqual(actual, expected); + assert.deepStrictEqual(actual, expected); + }); + }); + + describe('Zero address', () => { + it('should be correct', () => { + assert.strictEqual( + algosdk.ALGORAND_ZERO_ADDRESS_STRING, + algosdk.encodeAddress(new Uint8Array(32)) + ); }); }); }); diff --git a/tests/4.Utils.ts b/tests/4.Utils.ts index a2b589d4b..824e521bd 100644 --- a/tests/4.Utils.ts +++ b/tests/4.Utils.ts @@ -1,7 +1,8 @@ /* eslint-env mocha */ import assert from 'assert'; -import * as utils from '../src/utils/utils'; -import * as nacl from '../src/nacl/naclWrappers'; +import * as utils from '../src/utils/utils.js'; +import * as nacl from '../src/nacl/naclWrappers.js'; +import { combineMaps, convertMap } from '../src/encoding/schema/index.js'; describe('utils', () => { describe('concatArrays', () => { @@ -34,6 +35,386 @@ describe('utils', () => { assert.deepStrictEqual(expected, actual); }); }); + + describe('ensureSafeInteger', () => { + it('should error on undefined', () => { + assert.throws( + () => utils.ensureSafeInteger(undefined), + new Error('Value is undefined') + ); + }); + + it('should accept bigints in range', () => { + assert.strictEqual( + utils.ensureSafeInteger(BigInt(Number.MIN_SAFE_INTEGER)), + Number.MIN_SAFE_INTEGER + ); + assert.strictEqual(utils.ensureSafeInteger(BigInt(-100)), -100); + assert.strictEqual(utils.ensureSafeInteger(BigInt(0)), 0); + assert.strictEqual(utils.ensureSafeInteger(BigInt(7)), 7); + assert.strictEqual( + utils.ensureSafeInteger(BigInt(Number.MAX_SAFE_INTEGER)), + Number.MAX_SAFE_INTEGER + ); + }); + + it('should error on bigints outside of range', () => { + assert.throws( + () => + utils.ensureSafeInteger(BigInt(Number.MIN_SAFE_INTEGER) - BigInt(1)), + new Error('BigInt value -9007199254740992 is not a safe integer') + ); + assert.throws( + () => + utils.ensureSafeInteger(BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)), + new Error('BigInt value 9007199254740992 is not a safe integer') + ); + }); + + it('should accept safe integers', () => { + assert.strictEqual( + utils.ensureSafeInteger(Number.MIN_SAFE_INTEGER), + Number.MIN_SAFE_INTEGER + ); + assert.strictEqual(utils.ensureSafeInteger(-100), -100); + assert.strictEqual(utils.ensureSafeInteger(0), 0); + assert.strictEqual(utils.ensureSafeInteger(7), 7); + assert.strictEqual( + utils.ensureSafeInteger(Number.MAX_SAFE_INTEGER), + Number.MAX_SAFE_INTEGER + ); + }); + + it('should error on unsafe integers', () => { + assert.throws( + () => utils.ensureSafeInteger(0.5), + new Error('Value 0.5 is not a safe integer') + ); + assert.throws( + () => utils.ensureSafeInteger(Number.MIN_SAFE_INTEGER - 1), + new Error('Value -9007199254740992 is not a safe integer') + ); + assert.throws( + () => utils.ensureSafeInteger(Number.MAX_SAFE_INTEGER + 1), + new Error('Value 9007199254740992 is not a safe integer') + ); + assert.throws( + () => utils.ensureSafeInteger(NaN), + new Error('Value NaN is not a safe integer') + ); + }); + + it('should error on unexpected types', () => { + assert.throws( + () => utils.ensureSafeInteger('0'), + new Error('Unexpected type string, 0') + ); + + assert.throws( + () => utils.ensureSafeInteger(true), + new Error('Unexpected type boolean, true') + ); + + assert.throws( + () => utils.ensureSafeInteger(null), + new Error('Unexpected type object, null') + ); + }); + }); + + describe('ensureSafeUnsignedInteger', () => { + it('should error on undefined', () => { + assert.throws( + () => utils.ensureSafeUnsignedInteger(undefined), + new Error('Value is undefined') + ); + }); + + it('should accept positive bigints in range', () => { + assert.strictEqual(utils.ensureSafeUnsignedInteger(BigInt(0)), 0); + assert.strictEqual(utils.ensureSafeUnsignedInteger(BigInt(7)), 7); + assert.strictEqual( + utils.ensureSafeUnsignedInteger(BigInt(Number.MAX_SAFE_INTEGER)), + Number.MAX_SAFE_INTEGER + ); + }); + + it('should error on negative bigints in range', () => { + assert.throws( + () => utils.ensureSafeUnsignedInteger(BigInt(Number.MIN_SAFE_INTEGER)), + new Error('Value -9007199254740991 is negative') + ); + assert.throws( + () => utils.ensureSafeUnsignedInteger(BigInt(-100)), + new Error('Value -100 is negative') + ); + }); + + it('should error on bigints outside of range', () => { + assert.throws( + () => + utils.ensureSafeUnsignedInteger( + BigInt(Number.MIN_SAFE_INTEGER) - BigInt(1) + ), + new Error('BigInt value -9007199254740992 is not a safe integer') + ); + assert.throws( + () => + utils.ensureSafeUnsignedInteger( + BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1) + ), + new Error('BigInt value 9007199254740992 is not a safe integer') + ); + }); + + it('should accept positive safe integers', () => { + assert.strictEqual(utils.ensureSafeUnsignedInteger(0), 0); + assert.strictEqual(utils.ensureSafeUnsignedInteger(7), 7); + assert.strictEqual( + utils.ensureSafeUnsignedInteger(Number.MAX_SAFE_INTEGER), + Number.MAX_SAFE_INTEGER + ); + }); + + it('should error on negative safe integers', () => { + assert.throws( + () => utils.ensureSafeUnsignedInteger(Number.MIN_SAFE_INTEGER), + new Error('Value -9007199254740991 is negative') + ); + assert.throws( + () => utils.ensureSafeUnsignedInteger(-100), + new Error('Value -100 is negative') + ); + }); + + it('should error on unsafe integers', () => { + assert.throws( + () => utils.ensureSafeUnsignedInteger(0.5), + new Error('Value 0.5 is not a safe integer') + ); + assert.throws( + () => utils.ensureSafeUnsignedInteger(Number.MIN_SAFE_INTEGER - 1), + new Error('Value -9007199254740992 is not a safe integer') + ); + assert.throws( + () => utils.ensureSafeUnsignedInteger(Number.MAX_SAFE_INTEGER + 1), + new Error('Value 9007199254740992 is not a safe integer') + ); + assert.throws( + () => utils.ensureSafeUnsignedInteger(NaN), + new Error('Value NaN is not a safe integer') + ); + }); + + it('should error on unexpected types', () => { + assert.throws( + () => utils.ensureSafeUnsignedInteger('0'), + new Error('Unexpected type string, 0') + ); + + assert.throws( + () => utils.ensureSafeUnsignedInteger(true), + new Error('Unexpected type boolean, true') + ); + + assert.throws( + () => utils.ensureSafeUnsignedInteger(null), + new Error('Unexpected type object, null') + ); + }); + }); + + describe('ensureBigInt', () => { + it('should error on undefined', () => { + assert.throws( + () => utils.ensureBigInt(undefined), + new Error('Value is undefined') + ); + }); + + it('should accept bigints', () => { + assert.strictEqual( + utils.ensureBigInt( + BigInt(-1) * BigInt('0xffffffffffffffff') - BigInt(1) + ), + BigInt(-1) * BigInt('0xffffffffffffffff') - BigInt(1) + ); + assert.strictEqual( + utils.ensureBigInt(BigInt(-1) * BigInt('0xffffffffffffffff')), + BigInt(-1) * BigInt('0xffffffffffffffff') + ); + assert.strictEqual( + utils.ensureBigInt(BigInt(Number.MIN_SAFE_INTEGER) - BigInt(1)), + BigInt(Number.MIN_SAFE_INTEGER) - BigInt(1) + ); + assert.strictEqual( + utils.ensureBigInt(BigInt(Number.MIN_SAFE_INTEGER)), + BigInt(Number.MIN_SAFE_INTEGER) + ); + assert.strictEqual(utils.ensureBigInt(BigInt(-100)), BigInt(-100)); + assert.strictEqual(utils.ensureBigInt(BigInt(0)), BigInt(0)); + assert.strictEqual(utils.ensureBigInt(BigInt(7)), BigInt(7)); + assert.strictEqual( + utils.ensureBigInt(BigInt(Number.MAX_SAFE_INTEGER)), + BigInt(Number.MAX_SAFE_INTEGER) + ); + assert.strictEqual( + utils.ensureBigInt(BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)), + BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1) + ); + assert.strictEqual( + utils.ensureBigInt(BigInt('0xffffffffffffffff')), + BigInt('0xffffffffffffffff') + ); + assert.strictEqual( + utils.ensureBigInt(BigInt('0xffffffffffffffff') + BigInt(1)), + BigInt('0xffffffffffffffff') + BigInt(1) + ); + }); + + it('should accept safe integers', () => { + assert.strictEqual( + utils.ensureBigInt(Number.MIN_SAFE_INTEGER), + BigInt(Number.MIN_SAFE_INTEGER) + ); + assert.strictEqual(utils.ensureBigInt(-100), BigInt(-100)); + assert.strictEqual(utils.ensureBigInt(0), BigInt(0)); + assert.strictEqual(utils.ensureBigInt(7), BigInt(7)); + assert.strictEqual( + utils.ensureBigInt(Number.MAX_SAFE_INTEGER), + BigInt(Number.MAX_SAFE_INTEGER) + ); + }); + + it('should error on unsafe integers', () => { + assert.throws( + () => utils.ensureBigInt(0.5), + new Error('Value 0.5 is not a safe integer') + ); + assert.throws( + () => utils.ensureBigInt(Number.MIN_SAFE_INTEGER - 1), + new Error('Value -9007199254740992 is not a safe integer') + ); + assert.throws( + () => utils.ensureBigInt(Number.MAX_SAFE_INTEGER + 1), + new Error('Value 9007199254740992 is not a safe integer') + ); + assert.throws( + () => utils.ensureBigInt(NaN), + new Error('Value NaN is not a safe integer') + ); + }); + + it('should error on unexpected types', () => { + assert.throws( + () => utils.ensureBigInt('0'), + new Error('Unexpected type string, 0') + ); + + assert.throws( + () => utils.ensureBigInt(true), + new Error('Unexpected type boolean, true') + ); + + assert.throws( + () => utils.ensureBigInt(null), + new Error('Unexpected type object, null') + ); + }); + }); + + describe('ensureUint64', () => { + it('should error on undefined', () => { + assert.throws( + () => utils.ensureUint64(undefined), + new Error('Value is undefined') + ); + }); + + it('should accept bigints in range', () => { + assert.strictEqual(utils.ensureUint64(BigInt(0)), BigInt(0)); + assert.strictEqual(utils.ensureUint64(BigInt(7)), BigInt(7)); + assert.strictEqual( + utils.ensureUint64(BigInt(Number.MAX_SAFE_INTEGER)), + BigInt(Number.MAX_SAFE_INTEGER) + ); + assert.strictEqual( + utils.ensureUint64(BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)), + BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1) + ); + assert.strictEqual( + utils.ensureUint64(BigInt('0xffffffffffffffff')), + BigInt('0xffffffffffffffff') + ); + }); + + it('should error on bigints out of range', () => { + assert.throws( + () => utils.ensureUint64(BigInt(-100)), + new Error('Value -100 is not a uint64') + ); + assert.throws( + () => utils.ensureUint64(BigInt('0xffffffffffffffff') + BigInt(1)), + new Error('Value 18446744073709551616 is not a uint64') + ); + }); + + it('should accept positive safe integers', () => { + assert.strictEqual(utils.ensureUint64(0), BigInt(0)); + assert.strictEqual(utils.ensureUint64(7), BigInt(7)); + assert.strictEqual( + utils.ensureUint64(Number.MAX_SAFE_INTEGER), + BigInt(Number.MAX_SAFE_INTEGER) + ); + }); + + it('should error on negative safe integers', () => { + assert.throws( + () => utils.ensureUint64(Number.MIN_SAFE_INTEGER), + new Error('Value -9007199254740991 is not a uint64') + ); + assert.throws( + () => utils.ensureUint64(-100), + new Error('Value -100 is not a uint64') + ); + }); + + it('should error on unsafe integers', () => { + assert.throws( + () => utils.ensureUint64(0.5), + new Error('Value 0.5 is not a safe integer') + ); + assert.throws( + () => utils.ensureUint64(Number.MIN_SAFE_INTEGER - 1), + new Error('Value -9007199254740992 is not a safe integer') + ); + assert.throws( + () => utils.ensureUint64(Number.MAX_SAFE_INTEGER + 1), + new Error('Value 9007199254740992 is not a safe integer') + ); + assert.throws( + () => utils.ensureUint64(NaN), + new Error('Value NaN is not a safe integer') + ); + }); + + it('should error on unexpected types', () => { + assert.throws( + () => utils.ensureUint64('0'), + new Error('Unexpected type string, 0') + ); + + assert.throws( + () => utils.ensureUint64(true), + new Error('Unexpected type boolean, true') + ); + + assert.throws( + () => utils.ensureUint64(null), + new Error('Unexpected type object, null') + ); + }); + }); }); describe('nacl wrapper', () => { @@ -42,3 +423,143 @@ describe('nacl wrapper', () => { assert.strictEqual(nacl.isValidSignatureLength(64), true); }); }); + +describe('encoding utils', () => { + describe('combineMaps', () => { + it('should work on no inputs', () => { + const actual = combineMaps(); + const expected = new Map(); + assert.deepStrictEqual(actual, expected); + }); + + it('should work on one input', () => { + const a = new Map([ + ['a', 1], + ['b', 2], + ]); + + const actual = combineMaps(a); + const expected = new Map([ + ['a', 1], + ['b', 2], + ]); + assert.deepStrictEqual(actual, expected); + + assert.notEqual(actual, a); + }); + + it('should combine two maps', () => { + const a = new Map([ + ['a', 1], + ['b', 2], + ]); + const b = new Map([ + ['c', 3], + ['d', 4], + ]); + + const actual = combineMaps(a, b); + const expected = new Map([ + ['a', 1], + ['b', 2], + ['c', 3], + ['d', 4], + ]); + assert.deepStrictEqual(actual, expected); + + assert.notEqual(actual, a); + assert.notEqual(actual, b); + }); + + it('should combine three maps', () => { + const a = new Map([ + ['a', 1], + ['b', 2], + ]); + const b = new Map([ + ['c', 3], + ['d', 4], + ]); + const c = new Map([ + ['e', 5], + ['f', 6], + ]); + + const actual = combineMaps(a, b, c); + const expected = new Map([ + ['a', 1], + ['b', 2], + ['c', 3], + ['d', 4], + ['e', 5], + ['f', 6], + ]); + assert.deepStrictEqual(actual, expected); + + assert.notEqual(actual, a); + assert.notEqual(actual, b); + }); + + it('should error on duplicate keys', () => { + const a = new Map([ + ['a', 1], + ['b', 2], + ]); + const b = new Map([ + ['c', 3], + ['d', 4], + ['a', 5], + ]); + + assert.throws(() => combineMaps(a, b), new Error('Duplicate key: a')); + }); + }); + + describe('convertMap', () => { + it('should produce correct results', () => { + const map = new Map([ + ['a', 1], + ['b', 2], + ['c', 3], + ]); + + const func = (key: string, value: number): [number, string] => [ + value + 1, + key.toUpperCase(), + ]; + + const actual = convertMap(map, func); + const expected = new Map([ + [2, 'A'], + [3, 'B'], + [4, 'C'], + ]); + assert.deepStrictEqual(actual, expected); + + assert.notEqual(actual, map); + }); + + it('should produce correct results even under a key collision', () => { + const map = new Map([ + [2, 'a'], + [3, 'b'], + [4, 'c'], + ]); + + const func = (key: number, value: string): [number, string] => [ + Math.floor(key / 2), + value, + ]; + + const actual = convertMap(map, func); + const expected = new Map([ + // The 'a' value also gets mapped to the 1 key, but it is overwritten + [1, 'b'], + [2, 'c'], + ]); + assert.deepStrictEqual(actual, expected); + + assert.notEqual(actual, map); + }); + }); +}); diff --git a/tests/5.Transaction.js b/tests/5.Transaction.js deleted file mode 100644 index 7069d4b1a..000000000 --- a/tests/5.Transaction.js +++ /dev/null @@ -1,1648 +0,0 @@ -/* 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/5.Transaction.ts b/tests/5.Transaction.ts new file mode 100644 index 000000000..a516a1466 --- /dev/null +++ b/tests/5.Transaction.ts @@ -0,0 +1,2144 @@ +/* eslint-env mocha */ +import assert from 'assert'; +import algosdk from '../src/index.js'; +import { boxReferencesToEncodingData } from '../src/boxStorage.js'; + +describe('Sign', () => { + it('should not modify input arrays', () => { + const appArgs = [Uint8Array.from([1, 2]), Uint8Array.from([3, 4])]; + const accounts = [ + '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + ]; + const foreignApps = [17, 200]; + const foreignAssets = [7, 8, 9]; + const boxes = [{ appIndex: 0, name: Uint8Array.from([0]) }]; + const txn = new algosdk.Transaction({ + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + note: new Uint8Array(0), + type: algosdk.TransactionType.appl, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + appCallParams: { + appIndex: 5, + onComplete: algosdk.OnApplicationComplete.NoOpOC, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + }, + }); + assert.deepStrictEqual(appArgs, [ + Uint8Array.from([1, 2]), + Uint8Array.from([3, 4]), + ]); + assert.deepStrictEqual(accounts, [ + '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + ]); + assert.deepStrictEqual(foreignApps, [17, 200]); + assert.deepStrictEqual(foreignAssets, [7, 8, 9]); + assert.ok(txn.applicationCall); + assert.ok(txn.applicationCall.appArgs !== appArgs); + assert.ok((txn.applicationCall.accounts as any) !== accounts); + assert.ok((txn.applicationCall.foreignApps as any) !== foreignApps); + assert.ok((txn.applicationCall.foreignAssets as any) !== foreignAssets); + assert.ok((txn.applicationCall.boxes as any) !== boxes); + }); + + it('should not complain on a missing note', () => { + for (const note of [undefined, new Uint8Array()]) { + const txn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + paymentParams: { + receiver: + '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note, + }); + assert.deepStrictEqual(txn.note, new Uint8Array()); + } + }); + + it('should respect min tx fee', () => { + for (const minFee of [1000n, 1001n]) { + const params: algosdk.TransactionParams = { + type: algosdk.TransactionType.pay, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + paymentParams: { + receiver: + '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + amount: 847, + }, + suggestedParams: { + minFee, + fee: 0, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + }; + const zeroFee = new algosdk.Transaction(params); + assert.strictEqual(zeroFee.fee, minFee); + assert.strictEqual(zeroFee.toEncodingData().get('fee'), minFee); + + params.suggestedParams.fee = minFee; // since this is fee per byte, it will be far greater than minFee + const excessFee = new algosdk.Transaction(params); + assert.ok(excessFee.fee > minFee); + assert.strictEqual(excessFee.toEncodingData().get('fee'), excessFee.fee); + } + }); + + it('should accept 0 fee', () => { + const txn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + paymentParams: { + receiver: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 0, + flatFee: true, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + }); + assert.strictEqual(txn.fee, 0n); + // Should be omitted from encodings + const encRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + txn.toEncodingData() + ); + assert.ok(encRep instanceof Map && !encRep.has('fee')); + }); + + it('should accept lower than min fee', () => { + const txn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + paymentParams: { + receiver: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + flatFee: true, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + assert.strictEqual(txn.fee, 10n); + assert.strictEqual(txn.toEncodingData().get('fee'), 10n); + }); + + it('should not complain on a missing genesisID', () => { + const o: algosdk.TransactionParams = { + type: algosdk.TransactionType.pay, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + paymentParams: { + receiver: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + }, + note: new Uint8Array([123, 12, 200]), + }; + + assert.doesNotThrow(() => new algosdk.Transaction(o)); + }); + + it('should not complain on an empty genesisID', () => { + const o: algosdk.TransactionParams = { + type: algosdk.TransactionType.pay, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + paymentParams: { + receiver: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: '', + }, + note: new Uint8Array([123, 12, 200]), + }; + + assert.doesNotThrow(() => new algosdk.Transaction(o)); + }); + + it('should complain if note is not Uint8Array', () => { + const o: algosdk.TransactionParams = { + type: algosdk.TransactionType.pay, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + paymentParams: { + receiver: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: 'abcdefg' as any, + }; + assert.throws( + () => new algosdk.Transaction(o), + new Error('Not a Uint8Array: abcdefg') + ); + }); + + it('should not drop a note of all zeros', () => { + const txnWithNote = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + paymentParams: { + receiver: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array(32), + }); + + const txnWithoutNote = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + paymentParams: { + receiver: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + }); + + 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({ + type: algosdk.TransactionType.pay, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + paymentParams: { + receiver: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + lease: new Uint8Array(32), + }); + + const txnWithoutLease = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + paymentParams: { + receiver: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + lease: new Uint8Array(32), + }); + + 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({ + type: algosdk.TransactionType.acfg, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + assetConfigParams: { + assetIndex: 1234, + manager: address, + reserve: address, + freeze: address, + clawback: address, + assetMetadataHash: new Uint8Array(32), + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + }); + + const txnWithoutHash = new algosdk.Transaction({ + type: algosdk.TransactionType.acfg, + sender: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', + assetConfigParams: { + assetIndex: 1234, + manager: address, + reserve: address, + freeze: address, + clawback: address, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + }); + + const serializedWithHash = algosdk.encodeUnsignedTransaction(txnWithHash); + const serializedWithoutHash = + algosdk.encodeUnsignedTransaction(txnWithoutHash); + + assert.deepStrictEqual(serializedWithHash, serializedWithoutHash); + }); + + it('should error when the zero address is used for an optional field', () => { + const sender = 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }; + + const expectedError = new Error( + 'Invalid use of the zero address. To omit this value, pass in undefined' + ); + + assert.throws( + () => + new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender, + paymentParams: { + receiver: sender, + amount: 0, + }, + rekeyTo: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + suggestedParams, + }), + expectedError + ); + + assert.throws( + () => + new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender, + paymentParams: { + receiver: sender, + amount: 0, + closeRemainderTo: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + }, + suggestedParams, + }), + expectedError + ); + + assert.throws( + () => + new algosdk.Transaction({ + type: algosdk.TransactionType.axfer, + sender, + assetTransferParams: { + assetIndex: 9999, + receiver: sender, + amount: 0, + closeRemainderTo: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + }, + suggestedParams, + }), + expectedError + ); + + assert.throws( + () => + new algosdk.Transaction({ + type: algosdk.TransactionType.axfer, + sender, + assetTransferParams: { + assetIndex: 9999, + receiver: sender, + amount: 0, + assetSender: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + }, + suggestedParams, + }), + expectedError + ); + + assert.throws( + () => + new algosdk.Transaction({ + type: algosdk.TransactionType.acfg, + sender, + assetConfigParams: { + assetIndex: 9999, + manager: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + reserve: sender, + freeze: sender, + clawback: sender, + }, + suggestedParams, + }), + expectedError + ); + + assert.throws( + () => + new algosdk.Transaction({ + type: algosdk.TransactionType.acfg, + sender, + assetConfigParams: { + assetIndex: 9999, + manager: sender, + reserve: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + freeze: sender, + clawback: sender, + }, + suggestedParams, + }), + expectedError + ); + + assert.throws( + () => + new algosdk.Transaction({ + type: algosdk.TransactionType.acfg, + sender, + assetConfigParams: { + assetIndex: 9999, + manager: sender, + reserve: sender, + freeze: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + clawback: sender, + }, + suggestedParams, + }), + expectedError + ); + + assert.throws( + () => + new algosdk.Transaction({ + type: algosdk.TransactionType.acfg, + sender, + assetConfigParams: { + assetIndex: 9999, + manager: sender, + reserve: sender, + freeze: sender, + clawback: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + }, + suggestedParams, + }), + expectedError + ); + }); + + describe('should correctly serialize and deserialize from msgpack representation', () => { + it('should correctly serialize and deserialize from msgpack representation', () => { + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + paymentParams: { + receiver: + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize from msgpack representation with flat fee', () => { + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + paymentParams: { + receiver: + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 2063, + flatFee: true, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize a state proof transaction from msgpack representation', async () => { + async function loadResource(name: string): Promise { + const res = await fetch( + `http://localhost:8080/tests/resources/${name}` + ); + if (!res.ok) { + throw new Error(`Failed to load resource (${res.status}): ${name}`); + } + return new Uint8Array(await res.arrayBuffer()); + } + + const stateProofBytes = await loadResource('stateproof.msgp'); + const stateProof = algosdk.decodeMsgpack( + stateProofBytes, + algosdk.StateProof + ); + const stateProofMessageBytes = algosdk.base64ToBytes( + 'haFQzgAhmcOhYsQg2yjiUJZ8n0Dj9ElQ166GrxpvvoRCn0L/Z6QuAXpXDoShZs4CY10BoWzOAmNeAKF2xEB5RkiWhqppJ50rMDHrQ1tiQUskmFvyFDo3AoC+Z6RxNMzyD7QsXIm6oEFcI0We/ANEyffmfHIs8nNCbWcGdgmI' + ); + const stateProofMessage = algosdk.decodeMsgpack( + stateProofMessageBytes, + algosdk.StateProofMessage + ); + + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.stpf, + sender: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + stateProofParams: { + stateProofType: 0, + stateProof, + message: stateProofMessage, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize a key registration transaction from msgpack representation', () => { + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.keyreg, + sender: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + keyregParams: { + voteKey: algosdk.base64ToBytes( + '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE=' + ), + selectionKey: algosdk.base64ToBytes( + 'oImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX4=' + ), + stateProofKey: algosdk.base64ToBytes( + 'mgh7ddGf7dF1Z5/9RDzN/JZZF9yA7XYCKJXvqhwPdvI7pLKh7hizaM5rTC2kizVOpVRIU9PXSLeapvBJ/OxQYA==' + ), + voteFirst: 123, + voteLast: 456, + voteKeyDilution: 1234, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an offline key registration transaction from msgpack representation', () => { + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.keyreg, + sender: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + keyregParams: {}, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an offline key registration transaction from msgpack representation with explicit nonParticipation=false', () => { + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.keyreg, + sender: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + keyregParams: { + nonParticipation: false, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize a nonparticipating key registration transaction from msgpack representation', () => { + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.keyreg, + sender: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + keyregParams: { + nonParticipation: true, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an asset configuration transaction from msgpack representation', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.acfg, + sender: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + assetConfigParams: { + assetIndex: 1234, + manager: address, + reserve: address, + freeze: address, + clawback: address, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323575, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an asset creation transaction from msgpack representation', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.acfg, + sender: address, + assetConfigParams: { + manager: address, + reserve: address, + freeze: address, + clawback: address, + total: 2n ** 64n - 1n, + decimals: 5, + defaultFrozen: true, + unitName: 'tests', + assetName: 'testcoin', + assetURL: 'https://example.com', + assetMetadataHash: algosdk.base64ToBytes( + 'ZkFDUE80blJnTzU1ajFuZEFLM1c2U2djNEFQa2N5Rmg=' + ), + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323575, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an asset transfer transaction from msgpack representation', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.axfer, + sender: address, + assetTransferParams: { + assetIndex: 1234, + receiver: address, + amount: 100, + closeRemainderTo: address, + assetSender: address, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323575, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an application create transaction from msgpack representation', () => { + const expectedTxn = algosdk.makeApplicationCreateTxnFromObject({ + sender: '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 TextEncoder().encode('note value'), + rekeyTo: 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + suggestedParams: { + minFee: 1000, + fee: 0, + firstValid: 322575, + lastValid: 323575, + genesisID: 'testnet-v1.0', + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + }, + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an asset freeze transaction from msgpack representation', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.afrz, + sender: address, + assetFreezeParams: { + assetIndex: 1, + frozen: true, + freezeTarget: address, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323575, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize a payment transaction when the receiver is the zero address', () => { + const txn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + paymentParams: { + receiver: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 1, + lastValid: 1001, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + txn.toEncodingData() + ); + assert.ok(encRep instanceof Map && !encRep.has('rcv')); + + const encTxn = algosdk.encodeMsgpack(txn); + const golden = algosdk.base64ToBytes( + 'iaNhbXTNA0+jZmVlzQgqomZ2AaNnZW6sbW9jay1uZXR3b3JromdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds0D6aRub3RlxAN7DMijc25kxCCgiappIuO5mPrf9s1ICN354CHklE44nqPVxjh4ZokZfqR0eXBlo3BheQ==' + ); + assert.deepStrictEqual(encTxn, golden); + + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, txn); + + const reencRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + decTxn.toEncodingData() + ); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an asset transfer transaction when the receiver is the zero address', () => { + const txn = new algosdk.Transaction({ + type: algosdk.TransactionType.axfer, + sender: 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + assetTransferParams: { + assetIndex: 9999, + receiver: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 1, + lastValid: 1001, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + txn.toEncodingData() + ); + assert.ok(encRep instanceof Map && !encRep.has('arcv')); + + const encTxn = algosdk.encodeMsgpack(txn); + const golden = algosdk.base64ToBytes( + 'iqRhYW10zQNPo2ZlZc0ImKJmdgGjZ2VurG1vY2stbmV0d29ya6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbNA+mkbm90ZcQDewzIo3NuZMQgoImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX6kdHlwZaVheGZlcqR4YWlkzScP' + ); + assert.deepStrictEqual(encTxn, golden); + + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, txn); + + const reencRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + decTxn.toEncodingData() + ); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize an asset freeze transaction when the freeze account is the zero address', () => { + const txn = new algosdk.Transaction({ + type: algosdk.TransactionType.afrz, + sender: 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + assetFreezeParams: { + assetIndex: 9999, + freezeTarget: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + frozen: true, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 1, + lastValid: 1001, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + txn.toEncodingData() + ); + assert.ok(encRep instanceof Map && !encRep.has('fadd')); + + const encTxn = algosdk.msgpackRawEncode(encRep); + const golden = algosdk.base64ToBytes( + 'iqRhZnJ6w6RmYWlkzScPo2ZlZc0IeqJmdgGjZ2VurG1vY2stbmV0d29ya6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbNA+mkbm90ZcQDewzIo3NuZMQgoImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX6kdHlwZaRhZnJ6' + ); + assert.deepStrictEqual(encTxn, golden); + + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, txn); + + const reencRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + decTxn.toEncodingData() + ); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize a first round of 0', () => { + const address = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.afrz, + sender: address, + assetFreezeParams: { + assetIndex: 1, + frozen: true, + freezeTarget: address, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 0, + lastValid: 1000, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + expectedTxn.toEncodingData() + ); + assert.ok(encRep instanceof Map && !encRep.has('fv')); + + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const reencRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + decTxn.toEncodingData() + ); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize when the sender is the zero address', () => { + const txn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: algosdk.ALGORAND_ZERO_ADDRESS_STRING, + paymentParams: { + receiver: + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 1, + lastValid: 1001, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + txn.toEncodingData() + ); + assert.ok(encRep instanceof Map && !encRep.has('snd')); + + const encTxn = algosdk.encodeMsgpack(txn); + const golden = algosdk.base64ToBytes( + 'iaNhbXTNA0+jZmVlzQgqomZ2AaNnZW6sbW9jay1uZXR3b3JromdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds0D6aRub3RlxAN7DMijcmN2xCCgiappIuO5mPrf9s1ICN354CHklE44nqPVxjh4ZokZfqR0eXBlo3BheQ==' + ); + assert.deepStrictEqual(encTxn, golden); + + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, txn); + + const reencRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + decTxn.toEncodingData() + ); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('reserializes correctly no genesis ID', () => { + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + paymentParams: { + receiver: + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + }, + note: new Uint8Array([123, 12, 200]), + }); + const encRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + expectedTxn.toEncodingData() + ); + assert.ok(encRep instanceof Map && !encRep.has('gen')); + + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const reencRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + decTxn.toEncodingData() + ); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('reserializes correctly zero amount', () => { + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + paymentParams: { + receiver: + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + amount: 0, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + const encRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + expectedTxn.toEncodingData() + ); + assert.ok(encRep instanceof Map && !encRep.has('amt')); + + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const reencRep = algosdk.Transaction.encodingSchema.prepareMsgpack( + decTxn.toEncodingData() + ); + assert.deepStrictEqual(reencRep, encRep); + }); + + it('should correctly serialize and deserialize group object', () => { + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender: 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU', + paymentParams: { + receiver: + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM', + amount: 847, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 51, + lastValid: 61, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'mock-network', + }, + note: new Uint8Array([123, 12, 200]), + }); + + expectedTxn.group = algosdk.computeGroupID([expectedTxn]); + const encTxn = algosdk.encodeMsgpack(expectedTxn); + const decTxn = algosdk.decodeMsgpack(encTxn, algosdk.Transaction); + assert.deepStrictEqual(decTxn, expectedTxn); + + const encRep = expectedTxn.toEncodingData(); + const reencRep = decTxn.toEncodingData(); + assert.deepStrictEqual(reencRep, encRep); + }); + }); + + describe('transaction making functions', () => { + it('should be able to use helper to make a payment transaction', () => { + const sender = + 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const receiver = + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM'; + const amount = 847; + const closeRemainderTo = + 'RJB34GFP2BR5YJHKXDUMA2W4UX7DFUUR7QZ4AU5TWVKNA2KTNZIYB4BMRM'; + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const note = new Uint8Array([123, 12, 200]); + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender, + paymentParams: { + receiver, + amount, + closeRemainderTo, + }, + note, + rekeyTo, + suggestedParams, + }); + const actualTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender, + receiver, + amount, + closeRemainderTo, + note, + suggestedParams, + rekeyTo, + }); + assert.deepStrictEqual(actualTxn, expectedTxn); + }); + + it('should be able to use helper to make a payment transaction with BigInt amount', () => { + const sender = + 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const receiver = + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM'; + const amount = 0xffffffffffffffffn; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const closeRemainderTo = + 'RJB34GFP2BR5YJHKXDUMA2W4UX7DFUUR7QZ4AU5TWVKNA2KTNZIYB4BMRM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.pay, + sender, + paymentParams: { + receiver, + amount, + closeRemainderTo, + }, + note, + rekeyTo, + suggestedParams, + }); + const actualTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender, + receiver, + amount, + closeRemainderTo, + note, + suggestedParams, + rekeyTo, + }); + assert.deepStrictEqual(actualTxn, expectedTxn); + }); + + it('should throw if payment amount is too large', () => { + const sender = + 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const receiver = + 'UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM'; + const amount = 0x10000000000000000n; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const closeRemainderTo = + 'RJB34GFP2BR5YJHKXDUMA2W4UX7DFUUR7QZ4AU5TWVKNA2KTNZIYB4BMRM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + const o: algosdk.TransactionParams = { + type: algosdk.TransactionType.pay, + sender, + paymentParams: { + receiver, + amount, + closeRemainderTo, + }, + note, + rekeyTo, + suggestedParams, + }; + assert.throws( + () => new algosdk.Transaction(o), + new Error('Value 18446744073709551616 is not a uint64') + ); + }); + + it('should be able to use helper to make a keyreg transaction', () => { + const sender = + 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const voteKey = algosdk.base64ToBytes( + '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE=' + ); + const selectionKey = algosdk.base64ToBytes( + 'oImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX4=' + ); + const stateProofKey = algosdk.base64ToBytes( + 'mgh7ddGf7dF1Z5/9RDzN/JZZF9yA7XYCKJXvqhwPdvI7pLKh7hizaM5rTC2kizVOpVRIU9PXSLeapvBJ/OxQYA==' + ); + const voteKeyDilution = 1234; + const voteFirst = 123; + const voteLast = 456; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.keyreg, + sender, + keyregParams: { + voteKey, + selectionKey, + stateProofKey, + voteFirst, + voteLast, + voteKeyDilution, + }, + suggestedParams, + note, + rekeyTo, + }); + const actualTxn = + algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ + sender, + note, + voteKey, + selectionKey, + stateProofKey, + voteFirst, + voteLast, + voteKeyDilution, + suggestedParams, + rekeyTo, + }); + assert.deepStrictEqual(actualTxn, expectedTxn); + }); + + it('should be able to use helper to make an offline keyreg transaction', () => { + const sender = + 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + + assert.throws( + () => + new algosdk.Transaction({ + type: algosdk.TransactionType.keyreg, + sender, + keyregParams: { + voteKey: algosdk.base64ToBytes( + '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE=' + ), + }, + suggestedParams, + note, + rekeyTo, + }), + new Error( + 'Online key registration missing at least one of the following fields: ' + + 'voteKey, selectionKey, voteFirst, voteLast, voteKeyDilution' + ) + ); + + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.keyreg, + sender, + keyregParams: {}, + suggestedParams, + note, + rekeyTo, + }); + const actualTxn = + algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ + sender, + note, + suggestedParams, + rekeyTo, + }); + assert.deepStrictEqual(actualTxn, expectedTxn); + }); + + it('should be able to use helper to make a nonparticipating keyreg transaction', () => { + const sender = + 'XMHLMNAVJIMAW2RHJXLXKKK4G3J3U6VONNO3BTAQYVDC3MHTGDP3J5OCRU'; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + + assert.throws( + () => + new algosdk.Transaction({ + type: algosdk.TransactionType.keyreg, + sender, + keyregParams: { + voteKey: algosdk.base64ToBytes( + '5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKE=' + ), + selectionKey: algosdk.base64ToBytes( + 'oImqaSLjuZj63/bNSAjd+eAh5JROOJ6j1cY4eGaJGX4=' + ), + voteFirst: 123, + voteLast: 456, + voteKeyDilution: 1234, + nonParticipation: true, + }, + suggestedParams, + note, + rekeyTo, + }), + new Error( + 'nonParticipation is true but participation params are present.' + ) + ); + + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.keyreg, + sender, + keyregParams: { + nonParticipation: true, + }, + suggestedParams, + note, + rekeyTo, + }); + const actualTxn = + algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ + sender, + note, + suggestedParams, + rekeyTo, + nonParticipation: true, + }); + assert.deepStrictEqual(actualTxn, expectedTxn); + }); + + it('should be able to use helper to make an asset create transaction', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const defaultFrozen = false; + const total = 100; + const decimals = 1; + const manager = addr; + const reserve = addr; + const freeze = addr; + const clawback = addr; + const unitName = 'tst'; + const assetName = 'testcoin'; + const assetURL = 'testURL'; + const assetMetadataHash = new Uint8Array( + algosdk.base64ToBytes('dGVzdGhhc2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=') + ); + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.acfg, + sender: addr, + assetConfigParams: { + defaultFrozen, + total, + decimals, + manager, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + }, + suggestedParams, + note, + rekeyTo, + }); + const actualTxn = algosdk.makeAssetCreateTxnWithSuggestedParamsFromObject( + { + sender: addr, + note, + total, + decimals, + defaultFrozen, + manager, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + suggestedParams, + rekeyTo, + } + ); + assert.deepStrictEqual(actualTxn, expectedTxn); + }); + + it('should be able to use helper to make an asset create transaction with BigInt total', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const defaultFrozen = false; + const total = 0xffffffffffffffffn; + const decimals = 1; + const manager = addr; + const reserve = addr; + const freeze = addr; + const clawback = addr; + const unitName = 'tst'; + const assetName = 'testcoin'; + const assetURL = 'testURL'; + const assetMetadataHash = new Uint8Array( + algosdk.base64ToBytes('dGVzdGhhc2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=') + ); + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.acfg, + sender: addr, + assetConfigParams: { + defaultFrozen, + total, + decimals, + manager, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + }, + suggestedParams, + note, + rekeyTo, + }); + const actualTxn = algosdk.makeAssetCreateTxnWithSuggestedParamsFromObject( + { + sender: addr, + note, + total, + decimals, + defaultFrozen, + manager, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + suggestedParams, + rekeyTo, + } + ); + assert.deepStrictEqual(actualTxn, expectedTxn); + }); + + it('should throw if asset creation total is too large', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const defaultFrozen = false; + const total = 0x10000000000000000n; + const decimals = 1; + const manager = addr; + const reserve = addr; + const freeze = addr; + const clawback = addr; + const unitName = 'tst'; + const assetName = 'testcoin'; + const assetURL = 'testURL'; + const assetMetadataHash = new Uint8Array( + algosdk.base64ToBytes('dGVzdGhhc2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=') + ); + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + const params: algosdk.TransactionParams = { + type: algosdk.TransactionType.acfg, + sender: addr, + assetConfigParams: { + defaultFrozen, + total, + decimals, + manager, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + }, + suggestedParams, + note, + rekeyTo, + }; + assert.throws( + () => new algosdk.Transaction(params), + new Error('Value 18446744073709551616 is not a uint64') + ); + }); + + it('should fail to make an asset create transaction with an invalid assetMetadataHash', () => { + function paramsWithMetadataHash( + assetMetadataHash: any + ): algosdk.TransactionParams { + const addr = + 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + return { + type: algosdk.TransactionType.acfg, + sender: addr, + assetConfigParams: { + defaultFrozen: false, + total: 100, + decimals: 0, + manager: addr, + reserve: addr, + freeze: addr, + clawback: addr, + unitName: 'tst', + assetName: 'testcoin', + assetURL: 'https://example.com', + assetMetadataHash, + }, + suggestedParams: { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }, + note: new Uint8Array([123, 12, 200]), + rekeyTo: 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM', + }; + } + assert.doesNotThrow(() => { + const txnParams = paramsWithMetadataHash(undefined); + return new algosdk.Transaction(txnParams); + }); + assert.throws(() => { + const txnParams = paramsWithMetadataHash(new Uint8Array()); + return new algosdk.Transaction(txnParams); + }); + assert.throws(() => { + const txnParams = paramsWithMetadataHash(Uint8Array.from([1, 2, 3])); + return new algosdk.Transaction(txnParams); + }); + assert.doesNotThrow(() => { + const txnParams = paramsWithMetadataHash(new Uint8Array(32)); + return new algosdk.Transaction(txnParams); + }); + assert.throws(() => { + const txnParams = paramsWithMetadataHash(new Uint8Array(33)); + return new algosdk.Transaction(txnParams); + }); + assert.throws(() => { + const txnParams = paramsWithMetadataHash(''); + return new algosdk.Transaction(txnParams); + }); + assert.throws(() => { + const txnParams = paramsWithMetadataHash( + 'fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh' + ); + return new algosdk.Transaction(txnParams); + }); + }); + + it('should be able to use helper to make an asset config transaction', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const assetIndex = 1234; + const manager = addr; + const reserve = addr; + const freeze = addr; + const clawback = addr; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.acfg, + sender: addr, + assetConfigParams: { + assetIndex, + manager, + reserve, + freeze, + clawback, + }, + suggestedParams, + note, + rekeyTo, + }); + const actualTxn = algosdk.makeAssetConfigTxnWithSuggestedParamsFromObject( + { + sender: addr, + note, + assetIndex, + manager, + reserve, + freeze, + clawback, + suggestedParams, + rekeyTo, + } + ); + assert.deepStrictEqual(actualTxn, expectedTxn); + }); + + it('should throw when disobeying strict address checking in make asset config', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const assetIndex = 1234; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + assert.throws( + () => + algosdk.makeAssetConfigTxnWithSuggestedParamsFromObject({ + sender: addr, + assetIndex, + manager: addr, + reserve: addr, + freeze: undefined, + clawback: undefined, + suggestedParams, + }), + new Error( + 'strictEmptyAddressChecking is enabled, but an address is empty. If this is intentional, set strictEmptyAddressChecking to false.' + ) + ); + + // does not throw when flag enabled + algosdk.makeAssetConfigTxnWithSuggestedParamsFromObject({ + sender: addr, + assetIndex, + manager: addr, + reserve: addr, + freeze: undefined, + clawback: undefined, + suggestedParams, + strictEmptyAddressChecking: false, + }); + }); + + it('should be able to use helper to make an asset destroy transaction', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const assetIndex = 1234; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.acfg, + sender: addr, + assetConfigParams: { + assetIndex, + }, + suggestedParams, + note, + rekeyTo, + }); + const actualTxn = + algosdk.makeAssetDestroyTxnWithSuggestedParamsFromObject({ + sender: addr, + note, + assetIndex, + suggestedParams, + rekeyTo, + }); + assert.deepStrictEqual(actualTxn, expectedTxn); + }); + + it('should be able to use helper to make an asset transfer transaction', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const sender = addr; + const receiver = addr; + const assetSender = addr; + const closeRemainderTo = addr; + const assetIndex = 1234; + const amount = 100; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.axfer, + sender, + assetTransferParams: { + receiver, + assetSender, + closeRemainderTo, + assetIndex, + amount, + }, + suggestedParams, + note, + rekeyTo, + }); + const actualTxn = + algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ + sender, + receiver, + closeRemainderTo, + assetSender, + amount, + note, + assetIndex, + suggestedParams, + rekeyTo, + }); + assert.deepStrictEqual(actualTxn, expectedTxn); + }); + + it('should be able to use helper to make an asset freeze transaction', () => { + const addr = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4'; + const assetIndex = 1234; + const freezeTarget = addr; + const frozen = true; + const note = new Uint8Array([123, 12, 200]); + const rekeyTo = + 'GAQVB24XEPYOPBQNJQAE4K3OLNYTRYD65ZKR3OEW5TDOOGL7MDKABXHHTM'; + const suggestedParams: algosdk.SuggestedParams = { + minFee: 1000, + fee: 10, + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + genesisID: 'testnet-v1.0', + firstValid: 51, + lastValid: 61, + }; + const expectedTxn = new algosdk.Transaction({ + type: algosdk.TransactionType.afrz, + sender: addr, + assetFreezeParams: { + freezeTarget, + frozen, + assetIndex, + }, + suggestedParams, + note, + rekeyTo, + }); + const actualTxn = algosdk.makeAssetFreezeTxnWithSuggestedParamsFromObject( + { + sender: addr, + note, + assetIndex, + freezeTarget, + frozen, + suggestedParams, + rekeyTo, + } + ); + assert.deepStrictEqual(expectedTxn, actualTxn); + }); + + it('should be able to translate box references to encoded references', () => { + const testCases: Array< + [algosdk.BoxReference[], number[], number, Array>] + > = [ + [ + [{ appIndex: 100, name: Uint8Array.from([0, 1, 2, 3]) }], + [100], + 9999, + [ + new Map([ + ['i', 1], + ['n', Uint8Array.from([0, 1, 2, 3])], + ]), + ], + ], + [[], [], 9999, []], + [ + [ + { appIndex: 0, name: Uint8Array.from([0, 1, 2, 3]) }, + { appIndex: 9999, name: Uint8Array.from([4, 5, 6, 7]) }, + ], + [100], + 9999, + [ + new Map([ + ['i', 0], + ['n', Uint8Array.from([0, 1, 2, 3])], + ]), + new Map([ + ['i', 0], + ['n', Uint8Array.from([4, 5, 6, 7])], + ]), + ], + ], + [ + [{ appIndex: 100, name: Uint8Array.from([0, 1, 2, 3]) }], + [100], + 100, + [ + new Map([ + ['i', 1], + ['n', Uint8Array.from([0, 1, 2, 3])], + ]), + ], + ], + [ + [ + { appIndex: 7777, name: Uint8Array.from([0, 1, 2, 3]) }, + { appIndex: 8888, name: Uint8Array.from([4, 5, 6, 7]) }, + ], + [100, 7777, 8888, 9999], + 9999, + [ + new Map([ + ['i', 2], + ['n', Uint8Array.from([0, 1, 2, 3])], + ]), + new Map([ + ['i', 3], + ['n', Uint8Array.from([4, 5, 6, 7])], + ]), + ], + ], + [ + [{ appIndex: 0, name: Uint8Array.from([]) }], + [], + 1, + [ + new Map([ + ['i', 0], + ['n', Uint8Array.from([])], + ]), + ], + ], + ]; + for (const testCase of testCases) { + const expected = testCase[3]; + const actual = boxReferencesToEncodingData( + testCase[0], + testCase[1], + testCase[2] + ); + assert.deepStrictEqual(actual, expected); + } + }); + }); +}); diff --git a/tests/6.Multisig.ts b/tests/6.Multisig.ts index a2577e98a..9d9e7a8ec 100644 --- a/tests/6.Multisig.ts +++ b/tests/6.Multisig.ts @@ -1,13 +1,7 @@ /* 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'; +import { MULTISIG_SIGNATURE_LENGTH_ERROR_MSG } from '../src/multisigSigning'; 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' @@ -30,21 +24,29 @@ const sampleMultisigAddr = algosdk.multisigAddress(sampleMultisigParams); describe('Sample Multisig Info', () => { it('is correct', () => { - assert.strictEqual( + assert.deepStrictEqual( sampleAccount1.addr, - 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA' + algosdk.Address.fromString( + 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA' + ) ); - assert.strictEqual( + assert.deepStrictEqual( sampleAccount2.addr, - 'BFRTECKTOOE7A5LHCF3TTEOH2A7BW46IYT2SX5VP6ANKEXHZYJY77SJTVM' + algosdk.Address.fromString( + 'BFRTECKTOOE7A5LHCF3TTEOH2A7BW46IYT2SX5VP6ANKEXHZYJY77SJTVM' + ) ); - assert.strictEqual( + assert.deepStrictEqual( sampleAccount3.addr, - '47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU' + algosdk.Address.fromString( + '47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU' + ) ); - assert.strictEqual( + assert.deepStrictEqual( sampleMultisigAddr, - 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + algosdk.Address.fromString( + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + ) ); }); }); @@ -53,19 +55,22 @@ describe('Multisig Functionality', () => { describe('signMultisigTransaction', () => { it('should match golden main repo result', () => { const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: sampleMultisigAddr, - to: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', + sender: sampleMultisigAddr, + receiver: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', amount: 1000, - note: new Uint8Array(Buffer.from('RSYiABhShvs=', 'base64')), + note: algosdk.base64ToBytes('RSYiABhShvs='), closeRemainderTo: 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA', suggestedParams: { + minFee: 1000, fee: 1000, flatFee: true, - firstRound: 62229, - lastRound: 63229, + firstValid: 62229, + lastValid: 63229, genesisID: 'devnet-v38.0', - genesisHash: '/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp0=', + genesisHash: algosdk.base64ToBytes( + '/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp0=' + ), }, }); @@ -79,28 +84,30 @@ describe('Multisig Functionality', () => { 'MANN3ESOHQVHFZBAGD6UK6XFVWEFZQJPWO5SQ2J5LZRCF5E2VVQQ'; assert.strictEqual(txID, expectedTxID); - const expectedSignedTxn = Buffer.from( - 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==', - 'base64' + const expectedSignedTxn = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==' ); - assert.deepStrictEqual(Buffer.from(blob), expectedSignedTxn); + assert.deepStrictEqual(blob, expectedSignedTxn); }); it('should correctly handle a different sender', () => { const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: 'EHGMQCXBIFBE364DEKWQVVNCTCTVCGQL3BR2Q5I7CFTRXWIVTF4SYA3GHU', - to: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', + sender: 'EHGMQCXBIFBE364DEKWQVVNCTCTVCGQL3BR2Q5I7CFTRXWIVTF4SYA3GHU', + receiver: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', amount: 1000, - note: new Uint8Array(Buffer.from('RSYiABhShvs=', 'base64')), + note: algosdk.base64ToBytes('RSYiABhShvs='), closeRemainderTo: 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA', suggestedParams: { + minFee: 1000, fee: 1000, flatFee: true, - firstRound: 62229, - lastRound: 63229, + firstValid: 62229, + lastValid: 63229, genesisID: 'devnet-v38.0', - genesisHash: '/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp0=', + genesisHash: algosdk.base64ToBytes( + '/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp0=' + ), }, }); @@ -114,19 +121,17 @@ describe('Multisig Functionality', () => { 'YQOXQNNO56WXQSU3IDUM2C4J7IZI6WMSCMKN5UCP5ZK6GWA6BXKQ'; assert.strictEqual(txID, expectedTxID); - const expectedSignedTxn = Buffer.from( - 'g6Rtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAWLcyn6nB68yo/PUHXB21wyTy+PhHgMQNOIXPTGB96faZP2xqpQ8IFlIR2LPotlX68ylK8MCl82SUfMR4FYyzAIGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaRzZ25yxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCAhzMgK4UFCTfuDIq0K1aKYp1EaC9hjqHUfEWcb2RWZeaR0eXBlo3BheQ==', - 'base64' + const expectedSignedTxn = algosdk.base64ToBytes( + 'g6Rtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAWLcyn6nB68yo/PUHXB21wyTy+PhHgMQNOIXPTGB96faZP2xqpQ8IFlIR2LPotlX68ylK8MCl82SUfMR4FYyzAIGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaRzZ25yxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCAhzMgK4UFCTfuDIq0K1aKYp1EaC9hjqHUfEWcb2RWZeaR0eXBlo3BheQ==' ); - assert.deepStrictEqual(Buffer.from(blob), expectedSignedTxn); + assert.deepStrictEqual(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 oneSigTxn = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==' ); const { txID, blob } = algosdk.appendSignMultisigTransaction( @@ -139,17 +144,15 @@ describe('Multisig Functionality', () => { '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' + const expectedBlob = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQBAhuyRjsOrnHp3s/xI+iMKiL7QPsh8iJZ22YOJJP0aFUwedMr+a6wfdBXk1OefyrAN1wqJ9rq6O+DrWV1fH0ASBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNA+iiZnbN8xWjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbN9v2kbm90ZcQIRSYiABhShvujcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWjcGF5' ); - assert.deepStrictEqual(Buffer.from(blob), expectedBlob); + assert.deepStrictEqual(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 oneSigTxn = algosdk.base64ToBytes( + 'g6Rtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAWLcyn6nB68yo/PUHXB21wyTy+PhHgMQNOIXPTGB96faZP2xqpQ8IFlIR2LPotlX68ylK8MCl82SUfMR4FYyzAIGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaRzZ25yxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCAhzMgK4UFCTfuDIq0K1aKYp1EaC9hjqHUfEWcb2RWZeaR0eXBlo3BheQ==' ); const { txID, blob } = algosdk.appendSignMultisigTransaction( @@ -162,19 +165,17 @@ describe('Multisig Functionality', () => { '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' + const expectedBlob = algosdk.base64ToBytes( + 'g6Rtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAWLcyn6nB68yo/PUHXB21wyTy+PhHgMQNOIXPTGB96faZP2xqpQ8IFlIR2LPotlX68ylK8MCl82SUfMR4FYyzAIKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQPuH2WlM2x1tflA6LGMhKXBiuO7dsRzjXYPgcfjvDth9ZsCazyOKHqQmtYjD+pXLM8fJbrRhoUGTkyLxINiT9wCBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBpHNnbnLEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfpo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNA+iiZnbN8xWjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbN9v2kbm90ZcQIRSYiABhShvujcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEICHMyArhQUJN+4MirQrVopinURoL2GOodR8RZxvZFZl5pHR5cGWjcGF5' ); - assert.deepStrictEqual(Buffer.from(blob), expectedBlob); + assert.deepStrictEqual(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 oneSigTxn = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==' ); const signerAddr = sampleAccount2.addr; @@ -210,18 +211,16 @@ describe('Multisig Functionality', () => { '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' + const expectedBlob = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQBAhuyRjsOrnHp3s/xI+iMKiL7QPsh8iJZ22YOJJP0aFUwedMr+a6wfdBXk1OefyrAN1wqJ9rq6O+DrWV1fH0ASBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNA+iiZnbN8xWjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbN9v2kbm90ZcQIRSYiABhShvujcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWjcGF5' ); - assert.deepStrictEqual(Buffer.from(blob), expectedBlob); + assert.deepStrictEqual(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 oneSigTxn = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAuLAFE0oma0skOoAmOzEwfPuLYpEWl4LINtsiLrUqWQkDxh4WHb29//YCpj4MFbiSgD2jKYt0XKRD86zKCF4RDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQPoomZ2zfMVo2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zfb9pG5vdGXECEUmIgAYUob7o3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==' ); const signerAddr = sampleAccount2.addr; @@ -261,9 +260,8 @@ describe('Multisig Functionality', () => { }); it('should append signature to created raw multisig transaction', () => { - const rawTxBlob = Buffer.from( - 'jKNmZWXOAAPIwKJmds4ADvnao2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zgAO/cKmc2Vsa2V5xCAyEisr1j3cUzGWF6WqU8Sxwm/j3MryjTYitWl3oUBchqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWma2V5cmVnp3ZvdGVmc3TOAA27oKZ2b3Rla2TNJxCndm90ZWtlecQgcBvX+5ErB7MIEf8oHZ/ulWPlgC4gJokjGSWPd/qTHoindm90ZWxzdM4AD0JA', - 'base64' + const rawTxBlob = algosdk.base64ToBytes( + 'jKNmZWXOAAPIwKJmds4ADvnao2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zgAO/cKmc2Vsa2V5xCAyEisr1j3cUzGWF6WqU8Sxwm/j3MryjTYitWl3oUBchqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWma2V5cmVnp3ZvdGVmc3TOAA27oKZ2b3Rla2TNJxCndm90ZWtlecQgcBvX+5ErB7MIEf8oHZ/ulWPlgC4gJokjGSWPd/qTHoindm90ZWxzdM4AD0JA' ); const decRawTx = algosdk.decodeUnsignedTransaction(rawTxBlob); @@ -286,7 +284,7 @@ describe('Multisig Functionality', () => { ) as ExpectedMultisigTxStructure; assert.deepStrictEqual( unsignedMultisigTxBlob.msig.subsig[0].pk, - algosdk.decodeAddress(sampleAccount1.addr).publicKey + sampleAccount1.addr.publicKey ); assert.strictEqual(unsignedMultisigTxBlob.msig.subsig[1].s, undefined); @@ -324,20 +322,18 @@ describe('Multisig Functionality', () => { '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' + const expectedBlob = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAcT0s17wJbvnza+NpyHwM0RWbQ+HwKmsT1PLs+w6d6MpdTH3tra+yKZE0K0qEyhSE7Y56+B9oaf2orEbjc/njDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Mo2ZlZc4AA8jAomZ2zgAO+dqjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbOAA79wqZzZWxrZXnEIDISKyvWPdxTMZYXpapTxLHCb+PcyvKNNiK1aXehQFyGo3NuZMQgjZK0iZABc6BN+kNZo2ZqavzqLEKgXdnB9z7rpUeAN+mkdHlwZaZrZXlyZWendm90ZWZzdM4ADbugpnZvdGVrZM0nEKd2b3Rla2V5xCBwG9f7kSsHswgR/ygdn+6VY+WALiAmiSMZJY93+pMeiKd2b3RlbHN0zgAPQkA=' ); - assert.deepStrictEqual(Buffer.from(blob), expectedBlob); + assert.deepStrictEqual(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 rawTxBlob = algosdk.base64ToBytes( + 'jKNmZWXOAAPIwKJmds4ADvnao2dlbqxkZXZuZXQtdjM4LjCiZ2jEIP6zbDkQFDkAw9pVQsoYNrAP0vgZWRJXzSP2BC+YyDadomx2zgAO/cKmc2Vsa2V5xCAyEisr1j3cUzGWF6WqU8Sxwm/j3MryjTYitWl3oUBchqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWma2V5cmVnp3ZvdGVmc3TOAA27oKZ2b3Rla2TNJxCndm90ZWtlecQgcBvX+5ErB7MIEf8oHZ/ulWPlgC4gJokjGSWPd/qTHoindm90ZWxzdM4AD0JA' ); const decRawTx = algosdk.decodeUnsignedTransaction(rawTxBlob); @@ -351,18 +347,16 @@ describe('Multisig Functionality', () => { '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' + const oneSigTxBlob = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAcT0s17wJbvnza+NpyHwM0RWbQ+HwKmsT1PLs+w6d6MpdTH3tra+yKZE0K0qEyhSE7Y56+B9oaf2orEbjc/njDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Mo2ZlZc4AA8jAomZ2zgAO+dqjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbOAA79wqZzZWxrZXnEIDISKyvWPdxTMZYXpapTxLHCb+PcyvKNNiK1aXehQFyGo3NuZMQgjZK0iZABc6BN+kNZo2ZqavzqLEKgXdnB9z7rpUeAN+mkdHlwZaZrZXlyZWendm90ZWZzdM4ADbugpnZvdGVrZM0nEKd2b3Rla2V5xCBwG9f7kSsHswgR/ygdn+6VY+WALiAmiSMZJY93+pMeiKd2b3RlbHN0zgAPQkA=' ); - assert.deepStrictEqual(Buffer.from(blob), oneSigTxBlob); + assert.deepStrictEqual(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 rawOneSigTxBlob = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAcT0s17wJbvnza+NpyHwM0RWbQ+HwKmsT1PLs+w6d6MpdTH3tra+yKZE0K0qEyhSE7Y56+B9oaf2orEbjc/njDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AaN0eG6Mo2ZlZc4AA8jAomZ2zgAO+dqjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbOAA79wqZzZWxrZXnEIDISKyvWPdxTMZYXpapTxLHCb+PcyvKNNiK1aXehQFyGo3NuZMQgjZK0iZABc6BN+kNZo2ZqavzqLEKgXdnB9z7rpUeAN+mkdHlwZaZrZXlyZWendm90ZWZzdM4ADbugpnZvdGVrZM0nEKd2b3Rla2V5xCBwG9f7kSsHswgR/ygdn+6VY+WALiAmiSMZJY93+pMeiKd2b3RlbHN0zgAPQkA=' ); const decRawTx = algosdk.decodeSignedTransaction(rawOneSigTxBlob).txn; @@ -382,23 +376,22 @@ describe('Multisig Functionality', () => { 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' + const twoSigTxBlob = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAcT0s17wJbvnza+NpyHwM0RWbQ+HwKmsT1PLs+w6d6MpdTH3tra+yKZE0K0qEyhSE7Y56+B9oaf2orEbjc/njDYGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgqJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGhc8RAfScXfzBLaE42NbF9IR0qMa3NBQnhY6kQsV+6htqBsw/bWmc2vBla65CJLSNCNS236BsUMlvbwrs3knF+6bqhD6N0aHICoXYBo3R4boyjZmVlzgADyMCiZnbOAA752qNnZW6sZGV2bmV0LXYzOC4womdoxCD+s2w5EBQ5AMPaVULKGDawD9L4GVkSV80j9gQvmMg2naJsds4ADv3CpnNlbGtlecQgMhIrK9Y93FMxlhelqlPEscJv49zK8o02IrVpd6FAXIajc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlpmtleXJlZ6d2b3RlZnN0zgANu6Cmdm90ZWtkzScQp3ZvdGVrZXnEIHAb1/uRKwezCBH/KB2f7pVj5YAuICaJIxklj3f6kx6Ip3ZvdGVsc3TOAA9CQA==' ); - assert.deepStrictEqual(Buffer.from(finMsigBlob), twoSigTxBlob); + assert.deepStrictEqual(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]); + const oneAndThreeBlob = Uint8Array.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]); + const twoAndThreeBlob = Uint8Array.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 allThreeBlob = Uint8Array.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), @@ -408,19 +401,19 @@ describe('Multisig Functionality', () => { new Uint8Array(oneAndThreeBlob), new Uint8Array(twoAndThreeBlob), ]); - assert.deepStrictEqual(Buffer.from(finMsigBlob), allThreeBlob); - assert.deepStrictEqual(Buffer.from(finMsigBlobTwo), allThreeBlob); + assert.deepStrictEqual(finMsigBlob, allThreeBlob); + assert.deepStrictEqual(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, ]); + const blobSignedByFirst = Uint8Array.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, ]); + const blobSignedBySecond = Uint8Array.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, ]); + const blobSignedByThird = Uint8Array.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 blobSignedByAllThree = Uint8Array.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), @@ -447,22 +440,19 @@ describe('Multisig Functionality', () => { ); // let's check the merged transactions against our reference blob - assert.deepStrictEqual(Buffer.from(finMsigBlob), blobSignedByAllThree); - assert.deepStrictEqual(Buffer.from(finMsigBlobTwo), blobSignedByAllThree); + assert.deepStrictEqual(finMsigBlob, blobSignedByAllThree); + assert.deepStrictEqual(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 oneAndThreeBlob = algosdk.base64ToBytes( + 'g6Rtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAWLcyn6nB68yo/PUHXB21wyTy+PhHgMQNOIXPTGB96faZP2xqpQ8IFlIR2LPotlX68ylK8MCl82SUfMR4FYyzAIGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgqJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGhc8RAQ3t8lAipybLbWaRa3EDKl0mLGwUpYzl0iBCHSflhJM6zboJiT10FnWtXqW0dUBzpj9yeqtSuSJyKb8Ml3YJkCqN0aHICoXYBpHNnbnLEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfpo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNA+iiZnbN8xWjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbN9v2kbm90ZcQIRSYiABhShvujcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEICHMyArhQUJN+4MirQrVopinURoL2GOodR8RZxvZFZl5pHR5cGWjcGF5' ); - const twoAndThreeBlob = Buffer.from( - 'g6Rtc2lng6ZzdWJzaWeTgaJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXiConBrxCAJYzIJU3OJ8HVnEXc5kcfQPhtzyMT1K/av8BqiXPnCcaFzxED7h9lpTNsdbX5QOixjISlwYrju3bEc412D4HH47w7YfWbAms8jih6kJrWIw/qVyzPHyW60YaFBk5Mi8SDYk/cAgqJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGhc8RAQ3t8lAipybLbWaRa3EDKl0mLGwUpYzl0iBCHSflhJM6zboJiT10FnWtXqW0dUBzpj9yeqtSuSJyKb8Ml3YJkCqN0aHICoXYBpHNnbnLEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfpo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNA+iiZnbN8xWjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbN9v2kbm90ZcQIRSYiABhShvujcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEICHMyArhQUJN+4MirQrVopinURoL2GOodR8RZxvZFZl5pHR5cGWjcGF5', - 'base64' + const twoAndThreeBlob = algosdk.base64ToBytes( + 'g6Rtc2lng6ZzdWJzaWeTgaJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXiConBrxCAJYzIJU3OJ8HVnEXc5kcfQPhtzyMT1K/av8BqiXPnCcaFzxED7h9lpTNsdbX5QOixjISlwYrju3bEc412D4HH47w7YfWbAms8jih6kJrWIw/qVyzPHyW60YaFBk5Mi8SDYk/cAgqJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGhc8RAQ3t8lAipybLbWaRa3EDKl0mLGwUpYzl0iBCHSflhJM6zboJiT10FnWtXqW0dUBzpj9yeqtSuSJyKb8Ml3YJkCqN0aHICoXYBpHNnbnLEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfpo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNA+iiZnbN8xWjZ2VurGRldm5ldC12MzguMKJnaMQg/rNsORAUOQDD2lVCyhg2sA/S+BlZElfNI/YEL5jINp2ibHbN9v2kbm90ZcQIRSYiABhShvujcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEICHMyArhQUJN+4MirQrVopinURoL2GOodR8RZxvZFZl5pHR5cGWjcGF5' ); - 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 allThreeBlob = algosdk.base64ToBytes( + 'g6Rtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAWLcyn6nB68yo/PUHXB21wyTy+PhHgMQNOIXPTGB96faZP2xqpQ8IFlIR2LPotlX68ylK8MCl82SUfMR4FYyzAIKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQPuH2WlM2x1tflA6LGMhKXBiuO7dsRzjXYPgcfjvDth9ZsCazyOKHqQmtYjD+pXLM8fJbrRhoUGTkyLxINiT9wCConBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaFzxEBDe3yUCKnJsttZpFrcQMqXSYsbBSljOXSIEIdJ+WEkzrNugmJPXQWda1epbR1QHOmP3J6q1K5InIpvwyXdgmQKo3RocgKhdgGkc2ducsQgjZK0iZABc6BN+kNZo2ZqavzqLEKgXdnB9z7rpUeAN+mjdHhui6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0D6KJmds3zFaNnZW6sZGV2bmV0LXYzOC4womdoxCD+s2w5EBQ5AMPaVULKGDawD9L4GVkSV80j9gQvmMg2naJsds32/aRub3RlxAhFJiIAGFKG+6NyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQgIczICuFBQk37gyKtCtWimKdRGgvYY6h1HxFnG9kVmXmkdHlwZaNwYXk=' ); const finMsigBlob = algosdk.mergeMultisigTransactions([ @@ -473,275 +463,36 @@ describe('Multisig Functionality', () => { 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() - ); + assert.deepStrictEqual(finMsigBlob, allThreeBlob); + assert.deepStrictEqual(finMsigBlobTwo, allThreeBlob); }); - 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('should error when msig is missing', () => { + // prettier-ignore + const oneAndThreeBlob = Uint8Array.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]); + + const decoded = algosdk.decodeMsgpack( + oneAndThreeBlob, + algosdk.SignedTransaction + ); + // copies everything except the msig property + const decodedNoMsig = new algosdk.SignedTransaction({ + txn: decoded.txn, + sig: decoded.sig, + msig: undefined, + lsig: decoded.lsig, + sgnr: decoded.sgnr, + }); - 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 - ); - }); - }); + const noMsigBlob = algosdk.encodeMsgpack(decodedNoMsig); - 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); - }); + assert.throws(() => { + algosdk.mergeMultisigTransactions([noMsigBlob, oneAndThreeBlob]); + }, new Error('Invalid multisig transaction, multisig structure missing at index 0')); - 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 - ); + assert.throws(() => { + algosdk.mergeMultisigTransactions([oneAndThreeBlob, noMsigBlob]); + }, new Error('Invalid multisig transaction, multisig structure missing at index 1')); }); }); }); diff --git a/tests/7.AlgoSDK.js b/tests/7.AlgoSDK.ts similarity index 58% rename from tests/7.AlgoSDK.js rename to tests/7.AlgoSDK.ts index e54a59734..aec20ad69 100644 --- a/tests/7.AlgoSDK.js +++ b/tests/7.AlgoSDK.ts @@ -1,9 +1,8 @@ /* 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'); +import assert from 'assert'; +import algosdk from '../src/index.js'; +import * as nacl from '../src/nacl/naclWrappers.js'; +import * as utils from '../src/utils/utils.js'; describe('Algosdk (AKA end to end)', () => { describe('#mnemonic', () => { @@ -21,86 +20,106 @@ describe('Algosdk (AKA end to end)', () => { 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))); + assert.deepStrictEqual( + o, + algosdk.msgpackRawDecode(algosdk.msgpackRawEncode(o), { + intDecoding: algosdk.IntDecoding.MIXED, + }) + ); }); it('should encode and decode strings', () => { const o = 'Hi there'; - assert.deepStrictEqual(o, algosdk.decodeObj(algosdk.encodeObj(o))); + assert.deepStrictEqual( + o, + algosdk.msgpackRawDecode(algosdk.msgpackRawEncode(o)) + ); }); it('should not mutate unsigned transaction when going to or from encoded buffer', () => { - const to = 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI'; + const receiver = + 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI'; + const minFee = 1000; const fee = 4; const amount = 1000; - const firstRound = 12466; - const lastRound = 13466; + const firstValid = 12466; + const lastValid = 13466; const genesisID = 'devnet-v33.0'; - const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const genesisHash = algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ); const closeRemainderTo = 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA'; - const note = new Uint8Array(Buffer.from('6gAVR0Nsv5Y=', 'base64')); - const from = to; + const note = algosdk.base64ToBytes('6gAVR0Nsv5Y='); + const sender = receiver; const suggestedParams = { genesisHash, genesisID, - firstRound, - lastRound, + firstValid, + lastValid, fee, + minFee, }; - const txnAsObj = algosdk.makePaymentTxnWithSuggestedParams( - from, - to, + const txnAsObj = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender, + receiver, amount, closeRemainderTo, note, - suggestedParams - ); + suggestedParams, + }); const txnAsBuffer = algosdk.encodeUnsignedTransaction(txnAsObj); const txnAsObjRecovered = algosdk.decodeUnsignedTransaction(txnAsBuffer); - const txnAsBufferRecovered = algosdk.encodeUnsignedTransaction( - txnAsObjRecovered - ); + 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' + algosdk.base64ToBytes( + 'i6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0EmKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqRub3RlxAjqABVHQ2y/lqNyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKkdHlwZaNwYXk=' ) ); + const goldenDecoded = + algosdk.decodeUnsignedTransaction(txnAsBufferGolden); + assert.deepStrictEqual(txnAsObj, goldenDecoded); + assert.deepStrictEqual(txnAsBufferGolden, txnAsBufferRecovered); }); it('should not mutate signed transaction when going to or from encoded buffer', () => { - const to = 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI'; + const receiver = + 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI'; + const minFee = 1000; const fee = 4; const amount = 1000; - const firstRound = 12466; - const lastRound = 13466; + const firstValid = 12466; + const lastValid = 13466; const genesisID = 'devnet-v33.0'; - const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const genesisHash = algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ); const closeRemainderTo = 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA'; - const note = new Uint8Array(Buffer.from('6gAVR0Nsv5Y=', 'base64')); - const from = to; + const note = new Uint8Array(algosdk.base64ToBytes('6gAVR0Nsv5Y=')); + const sender = receiver; const suggestedParams = { genesisHash, genesisID, - firstRound, - lastRound, + firstValid, + lastValid, fee, + minFee, }; - const txnAsObj = algosdk.makePaymentTxnWithSuggestedParams( - from, - to, + const txnAsObj = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender, + receiver, amount, closeRemainderTo, note, - suggestedParams + suggestedParams, + }); + const sk = 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' ); - 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 @@ -109,9 +128,8 @@ describe('Algosdk (AKA end to end)', () => { 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' + algosdk.base64ToBytes( + 'g6RzZ25yxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaNzaWfEQDJHtrytU9p3nhRH1XS8tX+KmeKGyekigG7M704dOkBMTqiOJFuukbK2gUViJtivsPrKNiV0+WIrdbBk7gmNkgGjdHhui6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0EmKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqRub3RlxAjqABVHQ2y/lqNyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKkdHlwZaNwYXk=' ) ); assert.deepStrictEqual(signedTxnBytesGolden, recoveredSignedTxnBytes); @@ -120,62 +138,68 @@ describe('Algosdk (AKA end to end)', () => { 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, + const account = 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' + ); + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: account.addr, + receiver: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', amount: 1000, - firstRound: 12466, - lastRound: 13466, - genesisID: 'devnet-v33.0', - genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', closeRemainderTo: 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA', - note: new Uint8Array(Buffer.from('6gAVR0Nsv5Y=', 'base64')), - }; + note: algosdk.base64ToBytes('6gAVR0Nsv5Y='), + suggestedParams: { + minFee: 1000, + fee: 4, + firstValid: 12466, + lastValid: 13466, + genesisID: 'devnet-v33.0', + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + }, + }); - sk = algosdk.mnemonicToSecretKey(sk); + const signed = algosdk.signTransaction(txn, account.sk); - const jsDec = algosdk.signTransaction(o, sk.sk); - assert.deepStrictEqual( - Buffer.from(jsDec.blob), - Buffer.from(golden, 'base64') - ); + const golden = + 'gqNzaWfEQPhUAZ3xkDDcc8FvOVo6UinzmKBCqs0woYSfodlmBMfQvGbeUx3Srxy3dyJDzv7rLm26BRv9FnL2/AuT7NYfiAWjdHhui6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0EmKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqRub3RlxAjqABVHQ2y/lqNyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGkdHlwZaNwYXk='; + + assert.deepStrictEqual(signed.blob, algosdk.base64ToBytes(golden)); // // Check txid const txGolden = '5FJDJD5LMZC3EHUYYJNH5I23U4X6H2KXABNDGPIL557ZMJ33GZHQ'; - assert.deepStrictEqual(jsDec.txID, txGolden); + assert.deepStrictEqual(signed.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 { addr, sk } = 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' + ); const golden = 'gqNzaWfEQPhUAZ3xkDDcc8FvOVo6UinzmKBCqs0woYSfodlmBMfQvGbeUx3Srxy3dyJDzv7rLm26BRv9FnL2/AuT7NYfiAWjdHhui6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0EmKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqRub3RlxAjqABVHQ2y/lqNyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGkdHlwZaNwYXk='; - const o = { - to: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', - fee: 1176, + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: addr, + receiver: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', 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); + note: new Uint8Array(algosdk.base64ToBytes('6gAVR0Nsv5Y=')), + suggestedParams: { + minFee: 1000, + fee: 1176, + flatFee: true, + firstValid: 12466, + lastValid: 13466, + genesisID: 'devnet-v33.0', + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + }, + }); - const jsDec = algosdk.signTransaction(o, sk.sk); - assert.deepStrictEqual( - Buffer.from(jsDec.blob), - Buffer.from(golden, 'base64') - ); + const jsDec = algosdk.signTransaction(txn, sk); + assert.deepStrictEqual(jsDec.blob, algosdk.base64ToBytes(golden)); // // Check txid const txGolden = '5FJDJD5LMZC3EHUYYJNH5I23U4X6H2KXABNDGPIL557ZMJ33GZHQ'; @@ -183,99 +207,107 @@ describe('Algosdk (AKA end to end)', () => { }); 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'; + const account = 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' + ); // 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, + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: account.addr, + receiver: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', amount: 1000, - firstRound: 12466, - lastRound: 13466, - genesisID: 'devnet-v33.0', - genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', closeRemainderTo: 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA', - note: new Uint8Array(Buffer.from('6gAVR0Nsv5Y=', 'base64')), + note: new Uint8Array(algosdk.base64ToBytes('6gAVR0Nsv5Y=')), lease, - }; - - sk = algosdk.mnemonicToSecretKey(sk); + suggestedParams: { + minFee: 1000, + fee: 4, + firstValid: 12466, + lastValid: 13466, + genesisID: 'devnet-v33.0', + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + }, + }); + const signed = algosdk.signTransaction(txn, account.sk); - const jsDec = algosdk.signTransaction(o, sk.sk); - assert.deepStrictEqual( - Buffer.from(jsDec.blob), - Buffer.from(golden, 'base64') + const golden = algosdk.base64ToBytes( + 'gqNzaWfEQOMmFSIKsZvpW0txwzhmbgQjxv6IyN7BbV5sZ2aNgFbVcrWUnqPpQQxfPhV/wdu9jzEPUU1jAujYtcNCxJ7ONgejdHhujKNhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0FLKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqJseMQgAQIDBAECAwQBAgMEAQIDBAECAwQBAgMEAQIDBAECAwSkbm90ZcQI6gAVR0Nsv5ajcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEIOfw+E0GgR358xyNh4sRVfRnHVGhhcIAkIZn9ElYcGihpHR5cGWjcGF5' ); + const goldenDecoded = algosdk.decodeObj(golden); + const actualDecoded = algosdk.decodeObj(signed.blob); + assert.deepStrictEqual(actualDecoded, goldenDecoded); + assert.deepStrictEqual(signed.blob, golden); // Check txid const txGolden = '7BG6COBZKF6I6W5XY72ZE4HXV6LLZ6ENSR6DASEGSTXYXR4XJOOQ'; - assert.deepStrictEqual(jsDec.txID, txGolden); + assert.deepStrictEqual(signed.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 sk = 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' + ); 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 receiver = + 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI'; + const minFee = 1000; const fee = 4; const amount = 1000; - const firstRound = 12466; - const lastRound = 13466; + const firstValid = 12466; + const lastValid = 13466; const genesisID = 'devnet-v33.0'; - const genesisHash = 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI='; + const genesisHash = algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ); const closeRemainderTo = 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA'; - const note = new Uint8Array(Buffer.from('6gAVR0Nsv5Y=', 'base64')); - sk = algosdk.mnemonicToSecretKey(sk); + const note = new Uint8Array(algosdk.base64ToBytes('6gAVR0Nsv5Y=')); const key = nacl.keyPairFromSecretKey(sk.sk); - const from = algosdk.encodeAddress(key.publicKey); + const sender = algosdk.encodeAddress(key.publicKey); const suggestedParams = { genesisHash, genesisID, - firstRound, - lastRound, + firstValid, + lastValid, fee, + minFee, }; - const txn = algosdk.makePaymentTxnWithSuggestedParams( - from, - to, + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender, + receiver, amount, closeRemainderTo, note, - suggestedParams - ); - txn.addLease(lease, fee); + suggestedParams, + lease, + }); const txnBytes = txn.signTxn(sk.sk); - assert.deepStrictEqual( - Buffer.from(txnBytes), - Buffer.from(golden, 'base64') - ); + assert.deepStrictEqual(txnBytes, algosdk.base64ToBytes(golden)); // Check txid const txGolden = '7BG6COBZKF6I6W5XY72ZE4HXV6LLZ6ENSR6DASEGSTXYXR4XJOOQ'; - assert.deepStrictEqual(txn.txID().toString(), txGolden); + assert.deepStrictEqual(txn.txID(), 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 toSign = Uint8Array.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 toSign = Uint8Array.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)); @@ -287,33 +319,39 @@ describe('Algosdk (AKA end to end)', () => { // Create a transaction const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: sender.addr, - to: signer.addr, + sender: sender.addr, + receiver: signer.addr, amount: 1000, suggestedParams: { - firstRound: 12466, - lastRound: 13466, + firstValid: 12466, + lastValid: 13466, genesisID: 'devnet-v33.0', - genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), fee: 4, + minFee: 1000, }, }); // Sign it directly to get a signature const signedWithSk = txn.signTxn(signer.sk); - const decoded = algosdk.decodeObj(signedWithSk); - const signature = decoded.sig; + const decoded = algosdk.decodeMsgpack( + signedWithSk, + algosdk.SignedTransaction + ); + const signature = decoded.sig!; // Attach the signature to the transaction indirectly, and compare const signedWithSignature = txn.attachSignature(signer.addr, signature); - assert.deepEqual(signedWithSk, signedWithSignature); + assert.deepStrictEqual(signedWithSk, signedWithSignature); // Check that signer was set - const decodedWithSigner = algosdk.decodeObj(signedWithSignature); - assert.deepEqual( - decodedWithSigner.sgnr, - algosdk.decodeAddress(signer.addr).publicKey + const decodedWithSigner = algosdk.decodeMsgpack( + signedWithSignature, + algosdk.SignedTransaction ); + assert.deepStrictEqual(decodedWithSigner.sgnr, signer.addr); }); it('should not attach signature with incorrect length', () => { @@ -322,22 +360,28 @@ describe('Algosdk (AKA end to end)', () => { // Create a transaction const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: sender.addr, - to: signer.addr, + sender: sender.addr, + receiver: signer.addr, amount: 1000, suggestedParams: { - firstRound: 12466, - lastRound: 13466, + firstValid: 12466, + lastValid: 13466, genesisID: 'devnet-v33.0', - genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), fee: 4, + minFee: 1000, }, }); // 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 + const decoded = algosdk.decodeMsgpack( + signedWithSk, + algosdk.SignedTransaction + ); + const signature = decoded.sig!.slice(0, -1); // without the last byte // Check that the signature is not attached assert.throws( @@ -357,99 +401,43 @@ describe('Algosdk (AKA end to end)', () => { 'BFRTECKTOOE7A5LHCF3TTEOH2A7BW46IYT2SX5VP6ANKEXHZYJY77SJTVM', '47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU', ], - }; // msig address - RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM + }; + const msigAddr = algosdk.multisigAddress(params); // 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, + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: msigAddr, + receiver: 'PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI', amount: 1000, - firstRound: 12466, - lastRound: 13466, - genesisID: 'devnet-v33.0', - genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', closeRemainderTo: 'IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA', - note: new Uint8Array(Buffer.from('X4Bl4wQ9rCo=', 'base64')), - }; + note: algosdk.base64ToBytes('X4Bl4wQ9rCo='), + suggestedParams: { + minFee: 1000, + fee: 4, + firstValid: 12466, + lastValid: 13466, + genesisID: 'devnet-v33.0', + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), + }, + }); - const jsDec = algosdk.signMultisigTransaction(o, params, sk); + const jsDec = algosdk.signMultisigTransaction(txn, 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' + const golden = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgaJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXiBonBrxCAJYzIJU3OJ8HVnEXc5kcfQPhtzyMT1K/av8BqiXPnCcYKicGvEIOfw+E0GgR358xyNh4sRVfRnHVGhhcIAkIZn9ElYcGihoXPEQF6nXZ7CgInd1h7NVspIPFZNhkPL+vGFpTNwH3Eh9gwPM8pf1EPTHfPvjf14sS7xN7mTK+wrz7Odhp4rdWBNUASjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQSYomZ2zTCyo2dlbqxkZXZuZXQtdjMzLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zTSapG5vdGXECF+AZeMEPawqo3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==' ); - assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + assert.deepStrictEqual(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', () => { @@ -468,17 +456,15 @@ describe('Algosdk (AKA end to end)', () => { 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 o = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgaJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXiBonBrxCAJYzIJU3OJ8HVnEXc5kcfQPhtzyMT1K/av8BqiXPnCcYKicGvEIOfw+E0GgR358xyNh4sRVfRnHVGhhcIAkIZn9ElYcGihoXPEQF6nXZ7CgInd1h7NVspIPFZNhkPL+vGFpTNwH3Eh9gwPM8pf1EPTHfPvjf14sS7xN7mTK+wrz7Odhp4rdWBNUASjdGhyAqF2AaN0eG6Lo2FtdM0D6KVjbG9zZcQgQOk0koglZMvOnFmmm2dUJonpocOiqepbZabopEIf/FejZmVlzQSYomZ2zTCyo2dlbqxkZXZuZXQtdjMzLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zTSapG5vdGXECF+AZeMEPawqo3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCCNkrSJkAFzoE36Q1mjZmpq/OosQqBd2cH3PuulR4A36aR0eXBlo3BheQ==' ); 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' + const golden = algosdk.base64ToBytes( + 'gqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RAjmG2MILQVLoKg8q7jAYpu0r42zu9edYHrkkuSAikJAnDPplY1Pq90/ssyFhpKLrmvDDcSwNAwTGBjqtSOFYUAIGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgqJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGhc8RAXqddnsKAid3WHs1Wykg8Vk2GQ8v68YWlM3AfcSH2DA8zyl/UQ9Md8++N/XixLvE3uZMr7CvPs52Gnit1YE1QBKN0aHICoXYBo3R4boujYW10zQPopWNsb3NlxCBA6TSSiCVky86cWaabZ1Qmiemhw6Kp6ltlpuikQh/8V6NmZWXNBJiiZnbNMLKjZ2VurGRldm5ldC12MzMuMKJnaMQgJgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dKibHbNNJqkbm90ZcQIX4Bl4wQ9rCqjcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWjcGF5' ); - assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + assert.deepStrictEqual(jsDec.blob, golden); // Check txid const txGolden = 'TDIO6RJWJIVDDJZELMSX5CPJW7MUNM3QR4YAHYAKHF3W2CFRTI7A'; @@ -500,7 +486,9 @@ describe('Algosdk (AKA end to end)', () => { const outAddr = algosdk.multisigAddress(params); assert.deepStrictEqual( outAddr, - 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + algosdk.Address.fromString( + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + ) ); }); }); @@ -510,101 +498,88 @@ describe('Algosdk (AKA end to end)', () => { const address = 'UPYAFLHSIPMJOHVXU2MPLQ46GXJKSDCEMZ6RLCQ7GWB5PRDKJUWKKXECXI'; const [fromAddress, toAddress] = [address, address]; + const minFee = 1000; 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, + const genesisHash = algosdk.base64ToBytes( + 'sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E' + ); + const firstValid1 = 710399; + const note1 = algosdk.base64ToBytes('wRKw5cJ0CMo='); + const tx1 = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + receiver: toAddress, + sender: fromAddress, amount, - firstRound: firstRound1, - lastRound: firstRound1 + 1000, - genesisID, - genesisHash, note: note1, - flatFee: true, - }; + suggestedParams: { + minFee, + fee, + flatFee: true, + firstValid: firstValid1, + lastValid: firstValid1 + 1000, + genesisID, + genesisHash, + }, + }); - const firstRound2 = 710515; - const note2 = new Uint8Array(Buffer.from('dBlHI6BdrIg=', 'base64')); + const firstValid2 = 710515; + const note2 = algosdk.base64ToBytes('dBlHI6BdrIg='); - const o2 = { - to: toAddress, - from: fromAddress, - fee, + const tx2 = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + receiver: toAddress, + sender: fromAddress, amount, - firstRound: firstRound2, - lastRound: firstRound2 + 1000, - genesisID, - genesisHash, note: note2, - flatFee: true, - }; + suggestedParams: { + minFee, + fee, + flatFee: true, + firstValid: firstValid2, + lastValid: firstValid2 + 1000, + genesisID, + genesisHash, + }, + }); 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 stx1 = algosdk.encodeMsgpack( + new algosdk.SignedTransaction({ txn: tx1 }) ); - let stx2 = Buffer.from( - algosdk.encodeObj({ txn: tx2.get_obj_for_encoding() }) + let stx2 = algosdk.encodeMsgpack( + new algosdk.SignedTransaction({ txn: tx2 }) ); - assert.deepStrictEqual(stx1, Buffer.from(goldenTx1, 'base64')); - assert.deepStrictEqual(stx2, Buffer.from(goldenTx2, 'base64')); + assert.deepStrictEqual(stx1, algosdk.base64ToBytes(goldenTx1)); + assert.deepStrictEqual(stx2, algosdk.base64ToBytes(goldenTx2)); // 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); + const gid = algosdk.computeGroupID([tx1, tx2]); + tx1.group = gid; + tx2.group = gid; + stx1 = algosdk.encodeMsgpack(new algosdk.SignedTransaction({ txn: tx1 })); + stx2 = algosdk.encodeMsgpack(new algosdk.SignedTransaction({ txn: tx2 })); + const concat = utils.concatArrays(stx1, stx2); + assert.deepStrictEqual(concat, algosdk.base64ToBytes(goldenTxg)); + + // check assignGroupID + tx1.group = undefined; + tx2.group = undefined; + + const input = [tx1, tx2]; + const result = algosdk.assignGroupID(input); + assert.strictEqual(result.length, 2); + assert.strictEqual(result, input); + + assert.deepStrictEqual(tx1.group, gid); + assert.deepStrictEqual(tx2.group, gid); }); }); @@ -614,32 +589,37 @@ describe('Algosdk (AKA end to end)', () => { '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') + const sk = algosdk.mnemonicToSecretKey( + '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 = algosdk.makeAssetCreateTxnWithSuggestedParamsFromObject( + { + sender: address, + total: 100, + defaultFrozen: false, + manager: address, + reserve: address, + freeze: address, + clawback: address, + unitName: 'tst', + assetName: 'testcoin', + assetURL: 'website', + assetMetadataHash: new TextEncoder().encode( + 'fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh' + ), + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323575, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + }, + } ); + const jsDecCreate = algosdk.signTransaction(createTxn, sk.sk); + assert.deepStrictEqual(jsDecCreate.blob, algosdk.base64ToBytes(golden)); }); it('should return a blob that matches the go code for asset create with decimals', () => { @@ -647,33 +627,38 @@ describe('Algosdk (AKA end to end)', () => { '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') + const sk = algosdk.mnemonicToSecretKey( + '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 = algosdk.makeAssetCreateTxnWithSuggestedParamsFromObject( + { + sender: address, + total: 100, + decimals: 1, + defaultFrozen: false, + manager: address, + reserve: address, + freeze: address, + clawback: address, + unitName: 'tst', + assetName: 'testcoin', + assetURL: 'website', + assetMetadataHash: new TextEncoder().encode( + 'fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh' + ), + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323575, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + }, + } + ); + const jsDecCreate = algosdk.signTransaction(createTxn, sk.sk); + assert.deepStrictEqual(jsDecCreate.blob, algosdk.base64ToBytes(golden)); }); it('should return a blob that matches the go code for asset configuration', () => { @@ -681,27 +666,28 @@ describe('Algosdk (AKA end to end)', () => { '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') + const sk = algosdk.mnemonicToSecretKey( + '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 txn = algosdk.makeAssetConfigTxnWithSuggestedParamsFromObject({ + sender: address, + assetIndex: 1234, + manager: address, + reserve: address, + freeze: address, + clawback: address, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323575, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + }, + }); + const jsDec = algosdk.signTransaction(txn, sk.sk); + assert.deepStrictEqual(jsDec.blob, algosdk.base64ToBytes(golden)); }); it('should return a blob that matches the go code for asset destroy', () => { @@ -709,124 +695,143 @@ describe('Algosdk (AKA end to end)', () => { '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') + const sk = algosdk.mnemonicToSecretKey( + '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 txn = algosdk.makeAssetDestroyTxnWithSuggestedParamsFromObject({ + sender: address, + assetIndex: 1, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323575, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + }, + }); + const jsDec = algosdk.signTransaction(txn, sk.sk); + assert.deepStrictEqual(jsDec.blob, algosdk.base64ToBytes(golden)); }); 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, + const txn = algosdk.makeAssetFreezeTxnWithSuggestedParamsFromObject({ + sender: addr, + freezeTarget: addr, assetIndex: 1, - freezeState: true, - }; + frozen: true, + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323576, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + }, + }); 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' + const jsDec = algosdk.signTransaction(txn, sk); + const golden = algosdk.base64ToBytes( + 'gqNzaWfEQAhru5V2Xvr19s4pGnI0aslqwY4lA2skzpYtDTAN9DKSH5+qsfQQhm4oq+9VHVj7e1rQC49S28vQZmzDTVnYDQGjdHhuiaRhZnJ6w6RmYWRkxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRmYWlkAaNmZWXNCRqiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv+KNzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWkYWZyeg==' ); - assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + assert.deepStrictEqual(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, + const txn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ + sender: addr, + receiver: addr, amount: 1, - fee: 10, - firstRound: 322575, - lastRound: 323576, - genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', assetIndex: 1, closeRemainderTo: addr, - }; + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323576, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + }, + }); 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' + const jsDec = algosdk.signTransaction(txn, sk); + const golden = algosdk.base64ToBytes( + 'gqNzaWfEQNkEs3WdfFq6IQKJdF1n0/hbV9waLsvojy9pM1T4fvwfMNdjGQDy+LeesuQUfQVTneJD4VfMP7zKx4OUlItbrwSjdHhuiqRhYW10AaZhY2xvc2XEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pGFyY3bEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9o2ZlZc0KvqJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/4o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaVheGZlcqR4YWlkAQ==' ); - assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + assert.deepStrictEqual(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, + const txn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ + sender: addr, + receiver: addr, amount: 0, - fee: 10, - firstRound: 322575, - lastRound: 323575, - genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', assetIndex: 1, - }; + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323575, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + }, + }); 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' + const jsDec = algosdk.signTransaction(txn, sk); + const golden = algosdk.base64ToBytes( + 'gqNzaWfEQJ7q2rOT8Sb/wB0F87ld+1zMprxVlYqbUbe+oz0WM63FctIi+K9eYFSqT26XBZ4Rr3+VTJpBE+JLKs8nctl9hgijdHhuiKRhcmN2xCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aNmZWXNCOiiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWlYXhmZXKkeGFpZAE=' ); - assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + const goldenDecoded = algosdk.decodeObj(golden); + const actualDecoded = algosdk.decodeObj(jsDec.blob); + assert.deepStrictEqual(actualDecoded, goldenDecoded); + assert.deepStrictEqual(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, + const txn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ + sender: addr, + receiver: addr, + assetSender: addr, amount: 1, - fee: 10, - firstRound: 322575, - lastRound: 323575, - genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', assetIndex: 1, - }; + suggestedParams: { + minFee: 1000, + fee: 10, + firstValid: 322575, + lastValid: 323575, + genesisHash: algosdk.base64ToBytes( + 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=' + ), + }, + }); 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' + const jsDec = algosdk.signTransaction(txn, sk); + const golden = algosdk.base64ToBytes( + 'gqNzaWfEQHsgfEAmEHUxLLLR9s+Y/yq5WeoGo/jAArCbany+7ZYwExMySzAhmV7M7S8+LBtJalB4EhzEUMKmt3kNKk6+vAWjdHhuiqRhYW10AaRhcmN2xCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRhc25kxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aNmZWXNCqqiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWlYXhmZXKkeGFpZAE=' ); - assert.deepStrictEqual(Buffer.from(jsDec.blob), golden); + const goldenDecoded = algosdk.decodeObj(golden); + const actualDecoded = algosdk.decodeObj(jsDec.blob); + assert.deepStrictEqual(actualDecoded, goldenDecoded); + assert.deepStrictEqual(jsDec.blob, golden); }); }); @@ -834,15 +839,15 @@ describe('Algosdk (AKA end to end)', () => { 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); + assert.strictEqual(lsig.logic, program); + assert.deepStrictEqual(lsig.args, []); + assert.strictEqual(lsig.sig, undefined); + assert.strictEqual(lsig.msig, undefined); - const args = [Uint8Array.from('123'), Uint8Array.from('456')]; + const args = [Uint8Array.from([1, 2, 3]), Uint8Array.from([4, 5, 6])]; lsig = new algosdk.LogicSig(program, args); - assert.equal(lsig.logic, program); - assert.deepEqual(lsig.args, args); + assert.strictEqual(lsig.logic, program); + assert.deepStrictEqual(lsig.args, args); }); }); describe('Single logic sig', () => { @@ -851,7 +856,7 @@ describe('Algosdk (AKA end to end)', () => { const keys = algosdk.generateAccount(); const lsig = new algosdk.LogicSig(program); lsig.sign(keys.sk); - const verified = lsig.verify(algosdk.decodeAddress(keys.addr).publicKey); + const verified = lsig.verify(keys.addr.publicKey); assert.equal(verified, true); // check serialization @@ -878,7 +883,7 @@ describe('Algosdk (AKA end to end)', () => { ], }; const outAddr = algosdk.multisigAddress(params); - const msigPk = algosdk.decodeAddress(outAddr).publicKey; + const msigPk = 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 = @@ -922,24 +927,30 @@ describe('Algosdk (AKA end to end)', () => { 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 minFee = 1000; const amount = 2000; - const firstRound = 2063137; + const firstValid = 2063137; const genesisID = 'devnet-v1.0'; - const genesisHash = 'sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E='; - const note = new Uint8Array(Buffer.from('8xMCTuLQ810=', 'base64')); + const genesisHash = algosdk.base64ToBytes( + 'sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E=' + ); + const note = algosdk.base64ToBytes('8xMCTuLQ810='); - const txn = { - to: toAddress, - from: fromAddress, - fee, + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + receiver: toAddress, + sender: fromAddress, amount, - firstRound, - lastRound: firstRound + 1000, - genesisID, - genesisHash, note, - flatFee: true, - }; + suggestedParams: { + flatFee: true, + fee, + minFee, + firstValid, + lastValid: firstValid + 1000, + genesisID, + genesisHash, + }, + }); const program = Uint8Array.from([1, 32, 1, 1, 34]); // int 1 const args = [ @@ -958,10 +969,7 @@ describe('Algosdk (AKA end to end)', () => { const golden = 'gqRsc2lng6NhcmeSxAMxMjPEAzQ1NqFsxAUBIAEBIqNzaWfEQE6HXaI5K0lcq50o/y3bWOYsyw9TLi/oorZB4xaNdn1Z14351u2f6JTON478fl+JhIP4HNRRAIh/I8EWXBPpJQ2jdHhuiqNhbXTNB9CjZmVlzQPoomZ2zgAfeyGjZ2Vuq2Rldm5ldC12MS4womdoxCCwLc/t7ZJ1uookrS1uIJ0r211Klt7pd4IYp2g3OaWPQaJsds4AH38JpG5vdGXECPMTAk7i0PNdo3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaR0eXBlo3BheQ=='; - assert.deepStrictEqual( - Buffer.from(jsDec.blob), - Buffer.from(golden, 'base64') - ); + assert.deepStrictEqual(jsDec.blob, algosdk.base64ToBytes(golden)); const senderPk = algosdk.decodeAddress(fromAddress).publicKey; const verified = lsig.verify(senderPk); assert.equal(verified, true); @@ -969,25 +977,22 @@ describe('Algosdk (AKA end to end)', () => { }); describe('tealSign', () => { - const data = Buffer.from('Ux8jntyBJQarjKGF8A==', 'base64'); - const prog = Buffer.from('ASABASI=', 'base64'); + const data = algosdk.base64ToBytes('Ux8jntyBJQarjKGF8A=='); + const prog = algosdk.base64ToBytes('ASABASI='); const addr = new algosdk.LogicSig(prog).address(); - const seed = Buffer.from( - '5Pf7eGMA52qfMT4R4/vYCt7con/7U3yejkdXkrcb26Q=', - 'base64' + const seed = algosdk.base64ToBytes( + '5Pf7eGMA52qfMT4R4/vYCt7con/7U3yejkdXkrcb26Q=' ); 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 parts = utils.concatArrays(addr.publicKey, data); + const toBeVerified = utils.concatArrays( + new TextEncoder().encode('ProgData'), + parts ); const verified = nacl.verify(toBeVerified, sig, pk); assert.equal(verified, true); @@ -1017,11 +1022,16 @@ describe('Algosdk (AKA end to end)', () => { address: 'UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M', amount: 5002280000000000, amountWithoutPendingRewards: 5000000000000000, + minBalance: 100000, pendingRewards: 2280000000000, rewardBase: 456, rewards: 2280000000000, round: 18241, status: 'Online', + totalAppsOptedIn: 0, + totalAssetsOptedIn: 0, + totalCreatedApps: 0, + totalCreatedAssets: 0, }); const params = new algosdk.modelsv2.ApplicationParams({ creator: 'UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M', @@ -1034,44 +1044,107 @@ describe('Algosdk (AKA end to end)', () => { 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', - }; + // make a txn + const txn = algosdk.makeApplicationCallTxnFromObject({ + sender: 'UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M', + appIndex: 0, + onComplete: algosdk.OnApplicationComplete.NoOpOC, + clearProgram: algosdk.base64ToBytes('AiABASI='), + note: algosdk.base64ToBytes('tjpNge78JD8='), + suggestedParams: { + minFee: 1000, + flatFee: true, + fee: 1000, + firstValid: 18242, + lastValid: 19242, + genesisHash: algosdk.base64ToBytes( + 'ZIkPs8pTDxbRJsFB1yJ7gvnpDu0Q85FRkl2NCkEAQLU=' + ), + }, + }); const req = new algosdk.modelsv2.DryrunRequest({ accounts: [acc], apps: [app], round: 18241, protocolVersion: 'future', latestTimestamp: 1592537757, - txns: [{ txn }], + txns: [new algosdk.SignedTransaction({ txn })], + sources: [], }); 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); + const forEncoding = + algosdk.modelsv2.DryrunRequest.encodingSchema.prepareJSON( + req.toEncodingData(), + {} + ); + const actual = algosdk.stringifyJSON(forEncoding, undefined, 2); + + const expected = `{ + "accounts": [ + { + "address": "UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M", + "amount": 5002280000000000, + "amount-without-pending-rewards": 5000000000000000, + "min-balance": 100000, + "pending-rewards": 2280000000000, + "reward-base": 456, + "rewards": 2280000000000, + "round": 18241, + "status": "Online" + } + ], + "apps": [ + { + "id": 1380011588, + "params": { + "creator": "UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M", + "approval-program": "AiABASI=", + "clear-state-program": "AiABASI=", + "global-state-schema": { + "num-byte-slice": 5, + "num-uint": 5 + }, + "local-state-schema": { + "num-byte-slice": 5, + "num-uint": 5 + } + } + } + ], + "latest-timestamp": 1592537757, + "protocol-version": "future", + "round": 18241, + "txns": [ + { + "txn": { + "apsu": "AiABASI=", + "fee": 1000, + "fv": 18242, + "gh": "ZIkPs8pTDxbRJsFB1yJ7gvnpDu0Q85FRkl2NCkEAQLU=", + "lv": 19242, + "note": "tjpNge78JD8=", + "snd": "UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M", + "type": "appl" + } + } + ] +}`; + // Cannot directly compare JSON strings because order of keys is not guaranteed + const actualDecoded = algosdk.parseJSON(actual, { + intDecoding: algosdk.IntDecoding.BIGINT, + }); + const expectedDecoded = algosdk.parseJSON(expected, { + intDecoding: algosdk.IntDecoding.BIGINT, + }); + assert.deepStrictEqual(actualDecoded, expectedDecoded); }); 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); - + const actual = algosdk.encodeMsgpack(req); + const expected = algosdk.base64ToBytes( + 'hqhhY2NvdW50c5GJp2FkZHJlc3PZOlVBUEpFMzU1SzdCRzdSUVZNVFpPVzdRVzRJQ1pKRUlDM1JaR1lHNUxTSFo2NUs2TENORlBKRFNSN02mYW1vdW50zwARxYwSd5AAvmFtb3VudC13aXRob3V0LXBlbmRpbmctcmV3YXJkc88AEcN5N+CAAKttaW4tYmFsYW5jZc4AAYagr3BlbmRpbmctcmV3YXJkc88AAAIS2pcQAKtyZXdhcmQtYmFzZc0ByKdyZXdhcmRzzwAAAhLalxAApXJvdW5kzUdBpnN0YXR1c6ZPbmxpbmWkYXBwc5GComlkzlJBTkSmcGFyYW1zhbBhcHByb3ZhbC1wcm9ncmFtxAUCIAEBIrNjbGVhci1zdGF0ZS1wcm9ncmFtxAUCIAEBIqdjcmVhdG9y2TpVQVBKRTM1NUs3Qkc3UlFWTVRaT1c3UVc0SUNaSkVJQzNSWkdZRzVMU0haNjVLNkxDTkZQSkRTUjdNs2dsb2JhbC1zdGF0ZS1zY2hlbWGCrm51bS1ieXRlLXNsaWNlBahudW0tdWludAWybG9jYWwtc3RhdGUtc2NoZW1hgq5udW0tYnl0ZS1zbGljZQWobnVtLXVpbnQFsGxhdGVzdC10aW1lc3RhbXDOXuwynbBwcm90b2NvbC12ZXJzaW9upmZ1dHVyZaVyb3VuZM1HQaR0eG5zkYGjdHhuiKRhcHN1xAUCIAEBIqNmZWXNA+iiZnbNR0KiZ2jEIGSJD7PKUw8W0SbBQdcie4L56Q7tEPORUZJdjQpBAEC1omx2zUsqpG5vdGXECLY6TYHu/CQ/o3NuZMQgoB6Sb71Xwm/GFWTy634W4gWUkQLccmwbq5Hz7qvLE0qkdHlwZaRhcHBs' + ); assert.deepStrictEqual(actual, expected); }); }); diff --git a/tests/8.LogicSig.ts b/tests/8.LogicSig.ts index f9c38a33a..c13e61c88 100644 --- a/tests/8.LogicSig.ts +++ b/tests/8.LogicSig.ts @@ -1,5 +1,4 @@ /* eslint-env mocha */ -import { Buffer } from 'buffer'; import assert from 'assert'; import algosdk from '../src/index'; @@ -14,11 +13,11 @@ const sampleAccount3 = algosdk.mnemonicToSecretKey( ); // Multisig Golden Params -const sampleMultisigParams: algosdk.MultisigMetadata = { +const sampleMultisigParams = { version: 1, threshold: 2, addrs: [sampleAccount1.addr, sampleAccount2.addr, sampleAccount3.addr], -}; +} satisfies algosdk.MultisigMetadata; const sampleMultisigAddr = algosdk.multisigAddress(sampleMultisigParams); @@ -26,15 +25,16 @@ 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; + const programHash = algosdk.Address.fromString( + '6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY' + ); + const pk = programHash.publicKey; let lsig = new algosdk.LogicSig(program); assert.strictEqual(lsig.logic, program); - assert.strictEqual(lsig.args, undefined); + assert.deepStrictEqual(lsig.args, []); assert.strictEqual(lsig.sig, undefined); assert.strictEqual(lsig.msig, undefined); - assert.strictEqual(lsig.address(), programHash); + assert.deepStrictEqual(lsig.address(), programHash); let verified = lsig.verify(pk); assert.strictEqual(verified, true); @@ -76,7 +76,8 @@ describe('LogicSig', () => { const lsig = new algosdk.LogicSig(program); const address = lsig.address(); - assert.deepStrictEqual(address, programHash); + assert.strictEqual(address.toString(), programHash); + assert.ok(address.equals(algosdk.Address.fromString(programHash))); }); }); }); @@ -88,7 +89,7 @@ describe('LogicSigAccount', () => { const lsigAccount = new algosdk.LogicSigAccount(program); assert.deepStrictEqual(lsigAccount.lsig.logic, program); - assert.strictEqual(lsigAccount.lsig.args, undefined); + assert.deepStrictEqual(lsigAccount.lsig.args, []); assert.strictEqual(lsigAccount.lsig.sig, undefined); assert.strictEqual(lsigAccount.lsig.msig, undefined); assert.strictEqual(lsigAccount.sigkey, undefined); @@ -96,7 +97,7 @@ describe('LogicSigAccount', () => { // check serialization const encoded = lsigAccount.toByte(); const expectedEncoded = new Uint8Array( - Buffer.from('gaRsc2lngaFsxAUBIAEBIg==', 'base64') + algosdk.base64ToBytes('gaRsc2lngaFsxAUBIAEBIg==') ); assert.deepStrictEqual(encoded, expectedEncoded); @@ -117,7 +118,7 @@ describe('LogicSigAccount', () => { // check serialization const encoded = lsigAccount.toByte(); const expectedEncoded = new Uint8Array( - Buffer.from('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==', 'base64') + algosdk.base64ToBytes('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==') ); assert.deepStrictEqual(encoded, expectedEncoded); @@ -135,13 +136,11 @@ describe('LogicSigAccount', () => { lsigAccount.sign(sampleAccount1.sk); const expectedSig = new Uint8Array( - Buffer.from( - 'SRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9Ag==', - 'base64' + algosdk.base64ToBytes( + 'SRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9Ag==' ) ); - const expectedSigKey = algosdk.decodeAddress(sampleAccount1.addr) - .publicKey; + const expectedSigKey = sampleAccount1.addr.publicKey; assert.deepStrictEqual(lsigAccount.lsig.logic, program); assert.deepStrictEqual(lsigAccount.lsig.args, args); @@ -152,9 +151,8 @@ describe('LogicSigAccount', () => { // check serialization const encoded = lsigAccount.toByte(); const expectedEncoded = new Uint8Array( - Buffer.from( - 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==', - 'base64' + algosdk.base64ToBytes( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==' ) ); assert.deepStrictEqual(encoded, expectedEncoded); @@ -173,16 +171,15 @@ describe('LogicSigAccount', () => { lsigAccount.signMultisig(sampleMultisigParams, sampleAccount1.sk); const expectedSig = new Uint8Array( - Buffer.from( - 'SRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9Ag==', - 'base64' + algosdk.base64ToBytes( + 'SRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9Ag==' ) ); const expectedMsig: algosdk.EncodedMultisig = { v: sampleMultisigParams.version, thr: sampleMultisigParams.threshold, subsig: sampleMultisigParams.addrs.map((addr) => ({ - pk: algosdk.decodeAddress(addr).publicKey, + pk: addr.publicKey, })), }; expectedMsig.subsig[0].s = expectedSig; @@ -196,9 +193,8 @@ describe('LogicSigAccount', () => { // check serialization const encoded = lsigAccount.toByte(); const expectedEncoded = new Uint8Array( - Buffer.from( - 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AQ==', - 'base64' + algosdk.base64ToBytes( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AQ==' ) ); assert.deepStrictEqual(encoded, expectedEncoded); @@ -210,33 +206,24 @@ describe('LogicSigAccount', () => { 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 msig1of3Encoded = algosdk.base64ToBytes( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AQ==' ); const lsigAccount = algosdk.LogicSigAccount.fromByte(msig1of3Encoded); lsigAccount.appendToMultisig(sampleAccount2.sk); - const expectedSig1 = new Uint8Array( - Buffer.from( - 'SRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9Ag==', - 'base64' - ) + const expectedSig1 = algosdk.base64ToBytes( + 'SRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9Ag==' ); - const expectedSig2 = new Uint8Array( - Buffer.from( - 'ZLxV2+2RokHUKrZg9+FKuZmaUrOxcVjO/D9P58siQRStqT1ehAUCChemaYMDIk6Go4tqNsVUviBQ/9PuqLMECQ==', - 'base64' - ) + const expectedSig2 = algosdk.base64ToBytes( + 'ZLxV2+2RokHUKrZg9+FKuZmaUrOxcVjO/D9P58siQRStqT1ehAUCChemaYMDIk6Go4tqNsVUviBQ/9PuqLMECQ==' ); const expectedMsig: algosdk.EncodedMultisig = { v: sampleMultisigParams.version, thr: sampleMultisigParams.threshold, subsig: sampleMultisigParams.addrs.map((addr) => ({ - pk: algosdk.decodeAddress(addr).publicKey, + pk: addr.publicKey, })), }; expectedMsig.subsig[0].s = expectedSig1; @@ -249,9 +236,8 @@ describe('LogicSigAccount', () => { // 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' + algosdk.base64ToBytes( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYB' ) ); assert.deepStrictEqual(encoded, expectedEncoded); @@ -264,7 +250,7 @@ describe('LogicSigAccount', () => { describe('verify', () => { it('should verify valid escrow', () => { const escrowEncoded = new Uint8Array( - Buffer.from('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==', 'base64') + algosdk.base64ToBytes('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==') ); const lsigAccount = algosdk.LogicSigAccount.fromByte(escrowEncoded); @@ -273,9 +259,8 @@ describe('LogicSigAccount', () => { it('should verify valid single sig', () => { const sigEncoded = new Uint8Array( - Buffer.from( - 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==', - 'base64' + algosdk.base64ToBytes( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==' ) ); const lsigAccount = algosdk.LogicSigAccount.fromByte(sigEncoded); @@ -285,9 +270,8 @@ describe('LogicSigAccount', () => { it('should fail single sig with wrong sig', () => { const sigEncoded = new Uint8Array( - Buffer.from( - 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==', - 'base64' + algosdk.base64ToBytes( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==' ) ); const lsigAccount = algosdk.LogicSigAccount.fromByte(sigEncoded); @@ -300,9 +284,8 @@ describe('LogicSigAccount', () => { 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' + algosdk.base64ToBytes( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYB' ) ); const lsigAccount = algosdk.LogicSigAccount.fromByte(msigEncoded); @@ -312,24 +295,22 @@ describe('LogicSigAccount', () => { 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' + algosdk.base64ToBytes( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYB' ) ); const lsigAccount = algosdk.LogicSigAccount.fromByte(msigEncoded); // modify signature - lsigAccount.lsig.msig!.subsig[0].s[0] = 0; + 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' + algosdk.base64ToBytes( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoGicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxgaJwa8Qg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGjdGhyAqF2AQ==' ) ); const lsigAccount = algosdk.LogicSigAccount.fromByte( @@ -343,7 +324,7 @@ describe('LogicSigAccount', () => { describe('isDelegated', () => { it('should be correct for escrow', () => { const escrowEncoded = new Uint8Array( - Buffer.from('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==', 'base64') + algosdk.base64ToBytes('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==') ); const lsigAccount = algosdk.LogicSigAccount.fromByte(escrowEncoded); @@ -352,9 +333,8 @@ describe('LogicSigAccount', () => { it('should be correct for single sig', () => { const sigEncoded = new Uint8Array( - Buffer.from( - 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==', - 'base64' + algosdk.base64ToBytes( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==' ) ); const lsigAccount = algosdk.LogicSigAccount.fromByte(sigEncoded); @@ -364,9 +344,8 @@ describe('LogicSigAccount', () => { 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' + algosdk.base64ToBytes( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYB' ) ); const lsigAccount = algosdk.LogicSigAccount.fromByte(msigEncoded); @@ -378,50 +357,47 @@ describe('LogicSigAccount', () => { describe('address', () => { it('should be correct for escrow', () => { const escrowEncoded = new Uint8Array( - Buffer.from('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==', 'base64') + algosdk.base64ToBytes('gaRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIg==') ); const lsigAccount = algosdk.LogicSigAccount.fromByte(escrowEncoded); const addr = lsigAccount.address(); - const expectedAddr = - '6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY'; + const expectedAddr = algosdk.Address.fromString( + '6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY' + ); - assert.strictEqual(addr, expectedAddr); + assert.deepStrictEqual(addr, expectedAddr); }); it('should be correct for single sig', () => { - const sigEncoded = new Uint8Array( - Buffer.from( - 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==', - 'base64' - ) + const sigEncoded = algosdk.base64ToBytes( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQEkTuAXRnn8sEID2M34YVKfO6u4Q3b0TZYS/k7dfMGMVkcojDO3vI9F0G1KdsP/vN1TWRvS1YfyLvC17TmNcvQKmc2lna2V5xCAbfsCwS+pht5aQl+bL9AfhCKcFNR0LyYq+sSIJqKuBeA==' ); const lsigAccount = algosdk.LogicSigAccount.fromByte(sigEncoded); const addr = lsigAccount.address(); - const expectedAddr = - 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA'; + const expectedAddr = algosdk.Address.fromString( + 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA' + ); - assert.strictEqual(addr, expectedAddr); + assert.deepStrictEqual(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 msigEncoded = algosdk.base64ToBytes( + 'gaRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYB' ); const lsigAccount = algosdk.LogicSigAccount.fromByte(msigEncoded); const addr = lsigAccount.address(); - const expectedAddr = - 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM'; + const expectedAddr = algosdk.Address.fromString( + 'RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM' + ); - assert.strictEqual(addr, expectedAddr); + assert.deepStrictEqual(addr, expectedAddr); }); }); }); @@ -435,20 +411,23 @@ describe('signLogicSigTransaction', () => { function testSign( lsigObject: algosdk.LogicSig | algosdk.LogicSigAccount, - sender: string, + sender: string | algosdk.Address, expected: { txID: string; blob: Uint8Array } ) { const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: sender, - to: otherAddr, + sender, + receiver: otherAddr, amount: 5000, suggestedParams: { + minFee: 1000, flatFee: true, fee: 217000, - firstRound: 972508, - lastRound: 973508, + firstValid: 972508, + lastValid: 973508, genesisID: 'testnet-v31.0', - genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), }, note: new Uint8Array([180, 81, 121, 57, 252, 250, 210, 113]), }); @@ -467,9 +446,8 @@ describe('signLogicSigTransaction', () => { const expected = { txID: 'SV3GD4AKRRX43F3V4V7GYYB6YCQEPULGUI6GKZO6GPJDKOO75NFA', blob: new Uint8Array( - Buffer.from( - 'gqRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraR0eXBlo3BheQ==', - 'base64' + algosdk.base64ToBytes( + 'gqRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraR0eXBlo3BheQ==' ) ), }; @@ -481,9 +459,8 @@ describe('signLogicSigTransaction', () => { const expected = { txID: 'DRBC5KBOYEUCL6L6H45GQSRKCCUTPNELUHUSQO4ZWCEODJEXQBBQ', blob: new Uint8Array( - Buffer.from( - 'g6Rsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqRzZ25yxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqR0eXBlo3BheQ==', - 'base64' + algosdk.base64ToBytes( + 'g6Rsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqRzZ25yxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqR0eXBlo3BheQ==' ) ), }; @@ -503,9 +480,8 @@ describe('signLogicSigTransaction', () => { const expected = { txID: 'EZB2N2TEFR5OOL76Z46ZMRUL3ZQQOYKRFIX6WSHQ5FWESHU4LZPA', blob: new Uint8Array( - Buffer.from( - 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQD4FPTlN+xK8ZXmf6jGKe46iUYtVLIq+bNenZS3YsBh+IQUtuSRiiRblYXTNDxmsuWxFpCmRmREd5Hzk/BLszgKjdHhuiqNhbXTNE4ijZmVlzgADT6iiZnbOAA7W3KNnZW6tdGVzdG5ldC12MzEuMKJnaMQgJgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dKibHbOAA7axKRub3RlxAi0UXk5/PrScaNyY3bEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKo3NuZMQgXmdPHAru7DdxiY9hx2/10koZeT4skfoIUWJj44Vz6kKkdHlwZaNwYXk=', - 'base64' + algosdk.base64ToBytes( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQD4FPTlN+xK8ZXmf6jGKe46iUYtVLIq+bNenZS3YsBh+IQUtuSRiiRblYXTNDxmsuWxFpCmRmREd5Hzk/BLszgKjdHhuiqNhbXTNE4ijZmVlzgADT6iiZnbOAA7W3KNnZW6tdGVzdG5ldC12MzEuMKJnaMQgJgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dKibHbOAA7axKRub3RlxAi0UXk5/PrScaNyY3bEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKo3NuZMQgXmdPHAru7DdxiY9hx2/10koZeT4skfoIUWJj44Vz6kKkdHlwZaNwYXk=' ) ), }; @@ -514,25 +490,28 @@ describe('signLogicSigTransaction', () => { it('should throw an error when sender is not LogicSig address', () => { const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: otherAddr, - to: otherAddr, + sender: otherAddr, + receiver: otherAddr, amount: 5000, suggestedParams: { + minFee: 1000, flatFee: true, fee: 217000, - firstRound: 972508, - lastRound: 973508, + firstValid: 972508, + lastValid: 973508, genesisID: 'testnet-v31.0', - genesisHash: 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=', + genesisHash: algosdk.base64ToBytes( + 'JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=' + ), }, note: new Uint8Array([180, 81, 121, 57, 252, 250, 210, 113]), }); assert.throws( () => algosdk.signLogicSigTransaction(txn, lsig), - (err) => - err.message === + new Error( 'Logic signature verification failed. Ensure the program and signature are valid.' + ) ); }); }); @@ -547,9 +526,8 @@ describe('signLogicSigTransaction', () => { 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' + algosdk.base64ToBytes( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBo3R4boqjYW10zROIo2ZlZc4AA0+oomZ2zgAO1tyjZ2VurXRlc3RuZXQtdjMxLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zgAO2sSkbm90ZcQItFF5Ofz60nGjcmN2xCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWjcGF5' ) ), }; @@ -561,9 +539,8 @@ describe('signLogicSigTransaction', () => { 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' + algosdk.base64ToBytes( + 'g6Rsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBpHNnbnLEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfpo3R4boqjYW10zROIo2ZlZc4AA0+oomZ2zgAO1tyjZ2VurXRlc3RuZXQtdjMxLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zgAO2sSkbm90ZcQItFF5Ofz60nGjcmN2xCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqNzbmTEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKpHR5cGWjcGF5' ) ), }; @@ -581,9 +558,8 @@ describe('signLogicSigTransaction', () => { const expected = { txID: 'SV3GD4AKRRX43F3V4V7GYYB6YCQEPULGUI6GKZO6GPJDKOO75NFA', blob: new Uint8Array( - Buffer.from( - 'gqRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraR0eXBlo3BheQ==', - 'base64' + algosdk.base64ToBytes( + 'gqRsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraR0eXBlo3BheQ==' ) ), }; @@ -595,9 +571,8 @@ describe('signLogicSigTransaction', () => { const expected = { txID: 'DRBC5KBOYEUCL6L6H45GQSRKCCUTPNELUHUSQO4ZWCEODJEXQBBQ', blob: new Uint8Array( - Buffer.from( - 'g6Rsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqRzZ25yxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqR0eXBlo3BheQ==', - 'base64' + algosdk.base64ToBytes( + 'g6Rsc2lngqNhcmeSxAEBxAICA6FsxAUBIAEBIqRzZ25yxCD2di2sdbGZfWwslhgGgFB0kNeVES/+f7dgsnOK+cfxraN0eG6Ko2FtdM0TiKNmZWXOAANPqKJmds4ADtbco2dlbq10ZXN0bmV0LXYzMS4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds4ADtrEpG5vdGXECLRReTn8+tJxo3JjdsQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+Yqjc25kxCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqR0eXBlo3BheQ==' ) ), }; @@ -617,9 +592,8 @@ describe('signLogicSigTransaction', () => { const expected = { txID: 'EZB2N2TEFR5OOL76Z46ZMRUL3ZQQOYKRFIX6WSHQ5FWESHU4LZPA', blob: new Uint8Array( - Buffer.from( - 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQD4FPTlN+xK8ZXmf6jGKe46iUYtVLIq+bNenZS3YsBh+IQUtuSRiiRblYXTNDxmsuWxFpCmRmREd5Hzk/BLszgKjdHhuiqNhbXTNE4ijZmVlzgADT6iiZnbOAA7W3KNnZW6tdGVzdG5ldC12MzEuMKJnaMQgJgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dKibHbOAA7axKRub3RlxAi0UXk5/PrScaNyY3bEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKo3NuZMQgXmdPHAru7DdxiY9hx2/10koZeT4skfoIUWJj44Vz6kKkdHlwZaNwYXk=', - 'base64' + algosdk.base64ToBytes( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQD4FPTlN+xK8ZXmf6jGKe46iUYtVLIq+bNenZS3YsBh+IQUtuSRiiRblYXTNDxmsuWxFpCmRmREd5Hzk/BLszgKjdHhuiqNhbXTNE4ijZmVlzgADT6iiZnbOAA7W3KNnZW6tdGVzdG5ldC12MzEuMKJnaMQgJgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dKibHbOAA7axKRub3RlxAi0UXk5/PrScaNyY3bEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKo3NuZMQgXmdPHAru7DdxiY9hx2/10koZeT4skfoIUWJj44Vz6kKkdHlwZaNwYXk=' ) ), }; @@ -631,9 +605,8 @@ describe('signLogicSigTransaction', () => { const expected = { txID: 'DRBC5KBOYEUCL6L6H45GQSRKCCUTPNELUHUSQO4ZWCEODJEXQBBQ', blob: new Uint8Array( - Buffer.from( - 'g6Rsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQD4FPTlN+xK8ZXmf6jGKe46iUYtVLIq+bNenZS3YsBh+IQUtuSRiiRblYXTNDxmsuWxFpCmRmREd5Hzk/BLszgKkc2ducsQgXmdPHAru7DdxiY9hx2/10koZeT4skfoIUWJj44Vz6kKjdHhuiqNhbXTNE4ijZmVlzgADT6iiZnbOAA7W3KNnZW6tdGVzdG5ldC12MzEuMKJnaMQgJgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dKibHbOAA7axKRub3RlxAi0UXk5/PrScaNyY3bEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKo3NuZMQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+YqkdHlwZaNwYXk=', - 'base64' + algosdk.base64ToBytes( + 'g6Rsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqNzaWfEQD4FPTlN+xK8ZXmf6jGKe46iUYtVLIq+bNenZS3YsBh+IQUtuSRiiRblYXTNDxmsuWxFpCmRmREd5Hzk/BLszgKkc2ducsQgXmdPHAru7DdxiY9hx2/10koZeT4skfoIUWJj44Vz6kKjdHhuiqNhbXTNE4ijZmVlzgADT6iiZnbOAA7W3KNnZW6tdGVzdG5ldC12MzEuMKJnaMQgJgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dKibHbOAA7axKRub3RlxAi0UXk5/PrScaNyY3bEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKo3NuZMQgtMYiaKTDNVD1im3UuMojnJ8dELNBqn4aNuPOYfv8+YqkdHlwZaNwYXk=' ) ), }; @@ -651,9 +624,8 @@ describe('signLogicSigTransaction', () => { 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' + algosdk.base64ToBytes( + 'gqRsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBo3R4boqjYW10zROIo2ZlZc4AA0+oomZ2zgAO1tyjZ2VurXRlc3RuZXQtdjMxLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zgAO2sSkbm90ZcQItFF5Ofz60nGjcmN2xCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqNzbmTEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfppHR5cGWjcGF5' ) ), }; @@ -665,9 +637,8 @@ describe('signLogicSigTransaction', () => { 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' + algosdk.base64ToBytes( + 'g6Rsc2lng6NhcmeSxAEBxAICA6FsxAUBIAEBIqRtc2lng6ZzdWJzaWeTgqJwa8QgG37AsEvqYbeWkJfmy/QH4QinBTUdC8mKvrEiCairgXihc8RASRO4BdGefywQgPYzfhhUp87q7hDdvRNlhL+Tt18wYxWRyiMM7e8j0XQbUp2w/+83VNZG9LVh/Iu8LXtOY1y9AoKicGvEIAljMglTc4nwdWcRdzmRx9A+G3PIxPUr9q/wGqJc+cJxoXPEQGS8VdvtkaJB1Cq2YPfhSrmZmlKzsXFYzvw/T+fLIkEUrak9XoQFAgoXpmmDAyJOhqOLajbFVL4gUP/T7qizBAmBonBrxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaN0aHICoXYBpHNnbnLEII2StImQAXOgTfpDWaNmamr86ixCoF3Zwfc+66VHgDfpo3R4boqjYW10zROIo2ZlZc4AA0+oomZ2zgAO1tyjZ2VurXRlc3RuZXQtdjMxLjCiZ2jEICYLIAmgk6iGi3lYci+l5Ubt5+0X5NhcTHivsEUmkO3Somx2zgAO2sSkbm90ZcQItFF5Ofz60nGjcmN2xCC0xiJopMM1UPWKbdS4yiOcnx0Qs0Gqfho2485h+/z5iqNzbmTEILTGImikwzVQ9Ypt1LjKI5yfHRCzQap+GjbjzmH7/PmKpHR5cGWjcGF5' ) ), }; @@ -675,47 +646,117 @@ describe('signLogicSigTransaction', () => { }); }); }); +}); - 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' - ) - ), - }; +describe('ProgramSourceMap', () => { + const input = { + version: 3, + sources: ['test/scripts/e2e_subs/tealprogs/sourcemap-test.teal'], + names: [], + mappings: + ';;;;AAGA;;AAAmB;;AAAO;AAAI;;;AAE9B;;AAAO;;AAAO;AACd;;AAAO;AAAO;AALO;AAAI;AASrB;AACA', + }; + + const expectedLocations = new Map([ + [4, { sourceIndex: 0, line: 3, column: 0 }], + [6, { sourceIndex: 0, line: 3, column: 19 }], + [8, { sourceIndex: 0, line: 3, column: 26 }], + [9, { sourceIndex: 0, line: 3, column: 30 }], + [12, { sourceIndex: 0, line: 5, column: 0 }], + [14, { sourceIndex: 0, line: 5, column: 7 }], + [16, { sourceIndex: 0, line: 5, column: 14 }], + [17, { sourceIndex: 0, line: 6, column: 0 }], + [19, { sourceIndex: 0, line: 6, column: 7 }], + [20, { sourceIndex: 0, line: 6, column: 14 }], + [21, { sourceIndex: 0, line: 1, column: 21 }], + [22, { sourceIndex: 0, line: 1, column: 25 }], + [23, { sourceIndex: 0, line: 10, column: 4 }], + [24, { sourceIndex: 0, line: 11, column: 4 }], + ]); + + const expectedPcsForLine = new Map([ + [ + 3, + [ + { pc: 4, column: 0 }, + { pc: 6, column: 19 }, + { pc: 8, column: 26 }, + { pc: 9, column: 30 }, + ], + ], + [ + 5, + [ + { pc: 12, column: 0 }, + { pc: 14, column: 7 }, + { pc: 16, column: 14 }, + ], + ], + [ + 6, + [ + { pc: 17, column: 0 }, + { pc: 19, column: 7 }, + { pc: 20, column: 14 }, + ], + ], + [ + 1, + [ + { pc: 21, column: 21 }, + { pc: 22, column: 25 }, + ], + ], + [10, [{ pc: 23, column: 4 }]], + [11, [{ pc: 24, column: 4 }]], + ]); + + it('should be able to read a ProgramSourceMap', () => { + const sourceMap = new algosdk.ProgramSourceMap(input); + + assert.strictEqual(sourceMap.version, input.version); + assert.deepStrictEqual(sourceMap.sources, input.sources); + assert.deepStrictEqual(sourceMap.names, input.names); + assert.strictEqual(sourceMap.mappings, input.mappings); + }); - assert.deepStrictEqual(actual, expected); + describe('getLocationForPc', () => { + it('should return the correct location for all pcs', () => { + const sourceMap = new algosdk.ProgramSourceMap(input); + const maxPcToCheck = 30; + + for (let pc = 0; pc < maxPcToCheck; pc++) { + const expected = expectedLocations.get(pc); + assert.deepStrictEqual( + sourceMap.getLocationForPc(pc), + expected, + `pc=${pc}` + ); + } + }); + }); + + describe('getPcs', () => { + it('should return the correct pcs', () => { + const sourceMap = new algosdk.ProgramSourceMap(input); + const expectedPcs = Array.from(expectedLocations.keys()); + assert.deepStrictEqual(sourceMap.getPcs(), expectedPcs); + }); + }); + + describe('getPcsForLine', () => { + it('should return the correct pcs for all lines', () => { + const sourceMap = new algosdk.ProgramSourceMap(input); + const maxLineToCheck = 15; + + for (let line = 1; line <= maxLineToCheck; line++) { + const expected = expectedPcsForLine.get(line) || []; + assert.deepStrictEqual( + sourceMap.getPcsOnSourceLine(0, line), + expected, + `line=${line}` + ); + } + }); }); }); diff --git a/tests/9.Client.ts b/tests/9.Client.ts index f47e36838..1244e6ced 100644 --- a/tests/9.Client.ts +++ b/tests/9.Client.ts @@ -1,9 +1,9 @@ /* eslint-env mocha */ import assert from 'assert'; -import HTTPClient from '../src/client/client'; +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 { AlgodClient } from '../src/client/v2/algod/algod'; import * as utils from '../src/utils/utils'; describe('client', () => { @@ -68,29 +68,31 @@ describe('client', () => { it('should encode and decode values correctly', () => { const j = '{"total":18446744073709551615, "base":42}'; - let options = { - // intDecoding: IntDecoding.DEFAULT, + let options: utils.ParseJSONOptions = { + intDecoding: IntDecoding.BIGINT, }; let actual = HTTPClient.parseJSON(j, 200, options); - let expected = JSON.parse(j); - assert.strictEqual(actual.total, expected.total); - assert.strictEqual(typeof actual.total, 'number'); + let expected = utils.parseJSON(j, options); + assert.deepStrictEqual(actual, expected); + assert.strictEqual(typeof actual.total, 'bigint'); + assert.strictEqual(typeof actual.base, 'bigint'); options = { - intDecoding: IntDecoding.BIGINT, + intDecoding: IntDecoding.MIXED, }; actual = HTTPClient.parseJSON(j, 200, options); expected = utils.parseJSON(j, options); - assert.strictEqual(actual.total, expected.total); + assert.deepStrictEqual(actual, expected); assert.strictEqual(typeof actual.total, 'bigint'); + assert.strictEqual(typeof actual.base, 'number'); options = { - intDecoding: IntDecoding.MIXED, + intDecoding: IntDecoding.UNSAFE, }; actual = HTTPClient.parseJSON(j, 200, options); expected = utils.parseJSON(j, options); - assert.strictEqual(actual.total, expected.total); - assert.strictEqual(typeof actual.total, 'bigint'); + assert.deepStrictEqual(actual, expected); + assert.strictEqual(typeof actual.total, 'number'); assert.strictEqual(typeof actual.base, 'number'); options = { @@ -139,34 +141,45 @@ describe('client', () => { }); }); describe('AlgodClient construction', () => { - /* eslint-disable dot-notation */ + function getBaseClient(client: AlgodClient): URLTokenBaseHTTPClient { + // eslint-disable-next-line dot-notation + return client.c['bc'] as URLTokenBaseHTTPClient; + } + + function getBaseUrl(client: AlgodClient): URL { + // eslint-disable-next-line dot-notation + return getBaseClient(client)['baseURL']; + } + const baseServer = 'http://localhost'; it('should not assign a bogus port', () => { const client = new AlgodClient('', baseServer); - assert.strictEqual(client.c['bc']['baseURL']['port'], ''); + assert.strictEqual(getBaseUrl(client).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'); + assert.strictEqual(getBaseUrl(client).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'); + assert.strictEqual(getBaseUrl(client).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'); + assert.strictEqual(getBaseUrl(client).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-next-line dot-notation + ...getBaseClient(client)['tokenHeader'], + // eslint-disable-next-line dot-notation + ...getBaseClient(client)['defaultHeaders'], }, {} ); @@ -174,4 +187,28 @@ describe('client', () => { /* eslint-disable dot-notation */ }); + describe('Additional fetch options', () => { + // eslint-disable-next-line func-names + it('should pass additional options to the fetch method', async function () { + this.timeout(3_000); + + const client = new AlgodClient('', 'http://localhost:8080/neverreturn/'); + + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, 2_000); + + try { + await client + .healthCheck() + .do(undefined, { signal: abortController.signal }); + throw new Error('Request should have failed but did not'); + } catch (err) { + const errorString = (err as Error).toString(); + assert.ok(errorString.includes('aborted'), errorString); + } + }); + }); }); diff --git a/tests/cucumber/browser/test.js b/tests/cucumber/browser/test.js index e276efcb3..d37fc6991 100644 --- a/tests/cucumber/browser/test.js +++ b/tests/cucumber/browser/test.js @@ -1,11 +1,9 @@ /* 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); @@ -25,7 +23,16 @@ window.loadResource = async function loadResource(resource) { throw new Error(`Failed to load resource (${res.status}): ${resource}`); } - return Buffer.from(await res.arrayBuffer()); + return new Uint8Array(await res.arrayBuffer()); +}; + +window.loadResourceAsJson = async function loadResourceAsJson(resource) { + const res = await fetch(`/features/resources/${resource}`); + if (!res.ok) { + throw new Error(`Failed to load resource (${res.status}): ${resource}`); + } + + return res.json(); }; window.steps = { @@ -63,6 +70,10 @@ window.makeObject = function makeObject(obj) { return { ...obj }; }; +window.makeMap = function makeMap(m) { + return new Map(m); +}; + window.parseJSON = function parseJSON(json) { return JSON.parse(json); }; diff --git a/tests/cucumber/browser/webpack.config.js b/tests/cucumber/browser/webpack.config.js index d39c5439b..0a41899c5 100644 --- a/tests/cucumber/browser/webpack.config.js +++ b/tests/cucumber/browser/webpack.config.js @@ -1,10 +1,14 @@ const path = require('path'); module.exports = { + mode: 'development', entry: path.resolve(__dirname, 'test.js'), output: { filename: 'test.js', path: path.resolve(__dirname, 'build'), }, devtool: 'source-map', + optimization: { + minimize: false, + }, }; diff --git a/tests/cucumber/integration.tags b/tests/cucumber/integration.tags index eefd3bdd0..ed8782d44 100644 --- a/tests/cucumber/integration.tags +++ b/tests/cucumber/integration.tags @@ -3,7 +3,6 @@ @applications.boxes @applications.verified @assets -@auction @c2c @compile @compile.disassemble diff --git a/tests/cucumber/steps/index.js b/tests/cucumber/steps/index.js index dc996d339..42a760472 100644 --- a/tests/cucumber/steps/index.js +++ b/tests/cucumber/steps/index.js @@ -1,6 +1,5 @@ /* 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 { diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index c14fad91f..82b8b59b3 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -1,5 +1,4 @@ /* eslint-disable func-names,radix */ -const { Buffer } = require('buffer'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); @@ -41,6 +40,10 @@ async function loadResource(res) { }); } +async function loadResourceAsJson(res) { + return JSON.parse((await loadResource(res)).toString()); +} + // START OBJECT CREATION FUNCTIONS /** @@ -71,6 +74,10 @@ function makeObject(obj) { return { ...obj }; } +function makeMap(m) { + return new Map(m); +} + function parseJSON(json) { return JSON.parse(json); } @@ -123,17 +130,27 @@ module.exports = function getSteps(options) { const { algod_token: algodToken, kmd_token: kmdToken } = options; + function bytesEqual(a, b) { + if (a.length !== b.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + return true; + } + // String parsing helper methods function processAppArgs(subArg) { switch (subArg[0]) { case 'str': - return makeUint8Array(Buffer.from(subArg[1])); + return makeUint8Array(new TextEncoder().encode(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')); + return makeUint8Array(algosdk.base64ToBytes(subArg[1])); default: throw Error(`did not recognize app arg of type ${subArg[0]}`); } @@ -177,6 +194,35 @@ module.exports = function getSteps(options) { return boxRefArray; } + /* + doRaw and the associated functions are used to allow test servers to return unexpected response data in cases where + we're not testing the functionality of the response. It is the responsibility of the mocking step to set the doRaw + variable to true if it intends to send bad responses. + By default, we you `do` which will throw an exception if malformed response data is returned. + */ + let doRaw = false; + + async function doOrDoRaw(req) { + if (doRaw === true) { + doRaw = false; + return req.doRaw(); + } + return req.do(); + } + + function concatArrays(...arrs) { + 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; + } + Given('a kmd client', function () { this.kcl = new algosdk.Kmd(kmdToken, 'http://localhost', 60001); return this.kcl; @@ -234,12 +280,12 @@ module.exports = function getSteps(options) { Given( 'payment transaction parameters {int} {int} {int} {string} {string} {string} {int} {string} {string}', - function (fee, fv, lv, gh, to, close, amt, gen, note) { + function (fee, fv, lv, gh, receiver, close, amt, gen, note) { this.fee = parseInt(fee); this.fv = parseInt(fv); this.lv = parseInt(lv); - this.gh = gh; - this.to = to; + this.gh = algosdk.base64ToBytes(gh); + this.receiver = receiver; if (close !== 'none') { this.close = close; } @@ -248,7 +294,7 @@ module.exports = function getSteps(options) { this.gen = gen; } if (note !== 'none') { - this.note = makeUint8Array(Buffer.from(note, 'base64')); + this.note = makeUint8Array(algosdk.base64ToBytes(note)); } } ); @@ -293,9 +339,9 @@ module.exports = function getSteps(options) { const addrs = []; for (let i = 0; i < this.msig.addrs.length; i++) { addrs.push( - Buffer.from( + algosdk.bytesToBase64( algosdk.decodeAddress(this.msig.addrs[i]).publicKey - ).toString('base64') + ) ); } await this.kcl.importMultisig( @@ -320,34 +366,29 @@ module.exports = function getSteps(options) { Then( 'the signed transaction should equal the golden {string}', function (golden) { - assert.deepStrictEqual( - Buffer.from(golden, 'base64'), - Buffer.from(this.stx) - ); + assert.deepStrictEqual(algosdk.base64ToBytes(golden), this.stx); } ); Then( 'the signed transaction should equal the kmd signed transaction', function () { - assert.deepStrictEqual(Buffer.from(this.stx), Buffer.from(this.stxKmd)); + assert.deepStrictEqual(this.stx, this.stxKmd); } ); Then( 'the multisig address should equal the golden {string}', function (golden) { - assert.deepStrictEqual(algosdk.multisigAddress(this.msig), golden); + const goldenAddr = algosdk.Address.fromString(golden); + assert.deepStrictEqual(algosdk.multisigAddress(this.msig), goldenAddr); } ); Then( 'the multisig transaction should equal the golden {string}', function (golden) { - assert.deepStrictEqual( - Buffer.from(golden, 'base64'), - Buffer.from(this.stx) - ); + assert.deepStrictEqual(algosdk.base64ToBytes(golden), this.stx); } ); @@ -357,14 +398,11 @@ module.exports = function getSteps(options) { await this.kcl.deleteMultisig( this.handle, this.wallet_pswd, - algosdk.multisigAddress(this.msig) + algosdk.multisigAddress(this.msig).toString() ); const s = algosdk.decodeObj(this.stx); const m = algosdk.encodeObj(s.msig); - assert.deepStrictEqual( - Buffer.from(m), - Buffer.from(this.stxKmd, 'base64') - ); + assert.deepStrictEqual(m, algosdk.base64ToBytes(this.stxKmd)); } ); @@ -381,17 +419,14 @@ module.exports = function getSteps(options) { 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, - }; + if (sp.firstValid === 0) sp.firstValid = 1; + const fundingTxnArgs = + algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: this.accounts[0], + receiver: this.rekey, + amount: DEV_MODE_INITIAL_MICROALGOS, + suggestedParams: sp, + }); const stxKmd = await this.kcl.signTransaction( this.handle, @@ -406,7 +441,7 @@ module.exports = function getSteps(options) { 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); + assert.ok(keys.indexOf(this.pk) >= 0); return keys; }); @@ -417,13 +452,13 @@ module.exports = function getSteps(options) { 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); + assert.ok(keys.indexOf(this.pk) < 0); return keys; }); When('I generate a key', function () { const result = algosdk.generateAccount(); - this.pk = result.addr; + this.pk = result.addr.toString(); this.sk = result.sk; }); @@ -441,8 +476,8 @@ module.exports = function getSteps(options) { ); exp = exp.private_key; assert.deepStrictEqual( - Buffer.from(exp).toString('base64'), - Buffer.from(this.sk).toString('base64') + algosdk.bytesToBase64(exp), + algosdk.bytesToBase64(this.sk) ); return this.kcl.deleteKey(this.handle, this.wallet_pswd, this.pk); } @@ -460,11 +495,11 @@ module.exports = function getSteps(options) { [this.pk] = this.accounts; const result = await this.v2Client.getTransactionParams().do(); this.txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from: this.accounts[0], - to: this.accounts[1], + sender: this.accounts[0], + receiver: this.accounts[1], amount: parseInt(amt), suggestedParams: result, - note: makeUint8Array(Buffer.from(note, 'base64')), + note: makeUint8Array(algosdk.base64ToBytes(note)), }); return this.txn; } @@ -475,18 +510,14 @@ module.exports = function getSteps(options) { 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')), + this.lastValid = result.lastValid; + this.txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: this.rekey, + receiver: this.accounts[1], + note: makeUint8Array(algosdk.base64ToBytes(note)), amount: parseInt(amt), - }; + suggestedParams: result, + }); return this.txn; } ); @@ -502,17 +533,13 @@ module.exports = function getSteps(options) { 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')), + this.txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: algosdk.multisigAddress(this.msig), + receiver: this.accounts[1], + note: makeUint8Array(algosdk.base64ToBytes(note)), amount: parseInt(amt), - }; + suggestedParams: result, + }); return this.txn; } ); @@ -521,9 +548,9 @@ module.exports = function getSteps(options) { const addrs = []; for (let i = 0; i < this.msig.addrs.length; i++) { addrs.push( - Buffer.from( + algosdk.bytesToBase64( algosdk.decodeAddress(this.msig.addrs[i]).publicKey - ).toString('base64') + ) ); } return this.kcl.importMultisig( @@ -535,12 +562,10 @@ module.exports = function getSteps(options) { }); 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 - ); + const resp = await this.kcl.listMultisig(this.handle); + const keys = resp.addresses; + const addr = algosdk.multisigAddress(this.msig).toString(); + assert.ok(keys.indexOf(addr) >= 0, `Can't find address ${addr} in ${keys}`); return keys; }); @@ -551,17 +576,14 @@ module.exports = function getSteps(options) { } keys = keys.addresses; - assert.deepStrictEqual( - false, - keys.indexOf(algosdk.multisigAddress(this.msig)) >= 0 - ); + assert.ok(keys.indexOf(algosdk.multisigAddress(this.msig).toString()) < 0); return keys; }); When('I export the multisig', async function () { this.msigExp = await this.kcl.exportMultisig( this.handle, - algosdk.multisigAddress(this.msig) + algosdk.multisigAddress(this.msig).toString() ); return this.msigExp; }); @@ -570,22 +592,21 @@ module.exports = function getSteps(options) { return this.kcl.deleteMultisig( this.handle, this.wallet_pswd, - algosdk.multisigAddress(this.msig) + algosdk.multisigAddress(this.msig).toString() ); }); 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')), + algosdk.encodeAddress(algosdk.base64ToBytes(this.msigExp[i])), this.msig.addrs[i] ); } }); Then('the node should be healthy', async function () { - const health = await this.v2Client.healthCheck().do(); - assert.deepStrictEqual(health, makeObject({})); + await this.v2Client.healthCheck().do(); }); Then('I get the ledger supply', async function () { @@ -654,30 +675,26 @@ module.exports = function getSteps(options) { }); 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; - } + this.txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: this.pk, + receiver: this.receiver, + amount: this.amt ?? 0, + closeRemainderTo: this.close, + note: this.note, + suggestedParams: { + minFee: 1000, // Shouldn't matter because flatFee=true + flatFee: true, + fee: this.fee, + firstValid: this.fv, + lastValid: this.lv, + genesisHash: this.gh, + genesisID: this.gen, + }, + }); }); Given('encoded multisig transaction {string}', function (encTxn) { - this.mtx = Buffer.from(encTxn, 'base64'); + this.mtx = algosdk.base64ToBytes(encTxn); this.stx = algosdk.decodeObj(this.mtx); }); @@ -719,69 +736,59 @@ module.exports = function getSteps(options) { this.mtxs = []; const mtxs = encTxns.split(' '); for (let i = 0; i < mtxs.length; i++) { - this.mtxs.push(Buffer.from(mtxs[i], 'base64')); + this.mtxs.push(algosdk.base64ToBytes(mtxs[i])); } }); 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; - } + this.txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: algosdk.multisigAddress(this.msig), + receiver: this.receiver, + amount: this.amt ?? 0, + closeRemainderTo: this.close, + note: this.note, + suggestedParams: { + minFee: 1000, // Hardcoding, but it would be nice to take this as an argument + fee: this.fee, + firstValid: this.fv, + lastValid: this.lv, + genesisHash: this.gh, + genesisID: this.gen, + }, + }); 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; - } + this.txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: algosdk.multisigAddress(this.msig), + receiver: this.receiver, + amount: this.amt ?? 0, + closeRemainderTo: this.close, + note: this.note, + suggestedParams: { + minFee: 1000, // Shouldn't matter because flatFee=true + fee: this.fee, + flatFee: true, + firstValid: this.fv, + lastValid: this.lv, + genesisHash: this.gh, + genesisID: this.gen, + }, + }); return this.txn; }); When('I send the transaction', async function () { const txid = await this.v2Client.sendRawTransaction(this.stx).do(); - this.txid = txid.txId; + 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.txid = txid.txid; this.appTxid = txid; // Alias to use in waitForTransaction. return this.txid; }); @@ -912,15 +919,14 @@ module.exports = function getSteps(options) { 'default V2 key registration transaction {string}', async function (type) { const voteKey = makeUint8Array( - Buffer.from('9mr13Ri8rFepxN3ghIUrZNui6LqqM5hEzB45Rri5lkU=', 'base64') + algosdk.base64ToBytes('9mr13Ri8rFepxN3ghIUrZNui6LqqM5hEzB45Rri5lkU=') ); const selectionKey = makeUint8Array( - Buffer.from('dx717L3uOIIb/jr9OIyls1l5Ei00NFgRa380w7TnPr4=', 'base64') + algosdk.base64ToBytes('dx717L3uOIIb/jr9OIyls1l5Ei00NFgRa380w7TnPr4=') ); const stateProofKey = makeUint8Array( - Buffer.from( - 'mYR0GVEObMTSNdsKM6RwYywHYPqVDqg3E4JFzxZOreH9NU8B+tKzUanyY8AQ144hETgSMX7fXWwjBdHz6AWk9w==', - 'base64' + algosdk.base64ToBytes( + 'mYR0GVEObMTSNdsKM6RwYywHYPqVDqg3E4JFzxZOreH9NU8B+tKzUanyY8AQ144hETgSMX7fXWwjBdHz6AWk9w==' ) ); @@ -928,36 +934,29 @@ module.exports = function getSteps(options) { 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; + this.lastValid = result.lastValid; if (type === 'online') { this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ - from, + sender: from, voteKey, selectionKey, stateProofKey, voteFirst: 1, voteLast: 2000, voteKeyDilution: 10, - suggestedParams, + suggestedParams: result, }); } else if (type === 'offline') { this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ - from, - suggestedParams, + sender: from, + suggestedParams: result, }); } else if (type === 'nonparticipation') { this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ - from, + sender: from, nonParticipation: true, - suggestedParams, + suggestedParams: result, }); } else { throw new Error(`Unrecognized keyreg type: ${type}`); @@ -976,7 +975,9 @@ module.exports = function getSteps(options) { name: 'testcoin', unitname: 'coins', url: 'http://test', - metadataHash: 'fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh', + metadataHash: new TextEncoder().encode( + 'fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh' + ), expectedParams: undefined, queriedParams: undefined, lastTxn: undefined, @@ -989,11 +990,11 @@ module.exports = function getSteps(options) { [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.fv = this.params.firstValid; + this.lv = this.params.lastValid; this.note = undefined; this.gh = this.params.genesisHash; - const parsedIssuance = parseInt(issuance); + const parsedIssuance = BigInt(issuance); const decimals = 0; const defaultFrozen = false; const assetName = this.assetTestFixture.name; @@ -1004,47 +1005,41 @@ module.exports = function getSteps(options) { 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, - }; + this.assetTestFixture.lastTxn = + algosdk.makeAssetCreateTxnWithSuggestedParamsFromObject({ + sender: this.assetTestFixture.creator, + note: this.note, + total: parsedIssuance, + decimals, + defaultFrozen, + unitName, + assetName, + assetURL, + assetMetadataHash: metadataHash, + manager, + reserve, + freeze, + clawback, + suggestedParams: this.params, + }); // update vars used by other helpers this.assetTestFixture.expectedParams = { creator: this.assetTestFixture.creator, total: parsedIssuance, - defaultfrozen: defaultFrozen, - unitname: unitName, - assetname: assetName, + defaultFrozen, + unitName, + name: assetName, url: assetURL, - metadatahash: Buffer.from(metadataHash).toString('base64'), - managerkey: manager, - reserveaddr: reserve, - freezeaddr: freeze, - clawbackaddr: clawback, + metadataHash, + manager, + reserve, + freeze, + clawback, decimals, }; this.txn = this.assetTestFixture.lastTxn; - this.lastRound = this.params.lastRound; + this.lastValid = this.params.lastValid; [this.pk] = this.accounts; } ); @@ -1055,11 +1050,11 @@ module.exports = function getSteps(options) { [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.fv = this.params.firstValid; + this.lv = this.params.lastValid; this.note = undefined; this.gh = this.params.genesisHash; - const parsedIssuance = parseInt(issuance); + const parsedIssuance = BigInt(issuance); const decimals = 0; const defaultFrozen = true; const assetName = this.assetTestFixture.name; @@ -1070,47 +1065,41 @@ module.exports = function getSteps(options) { 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, - }; + this.assetTestFixture.lastTxn = + algosdk.makeAssetCreateTxnWithSuggestedParamsFromObject({ + sender: this.assetTestFixture.creator, + note: this.note, + total: parsedIssuance, + decimals, + defaultFrozen, + unitName, + assetName, + assetURL, + assetMetadataHash: metadataHash, + manager, + reserve, + freeze, + clawback, + suggestedParams: this.params, + }); // update vars used by other helpers this.assetTestFixture.expectedParams = { creator: this.assetTestFixture.creator, total: parsedIssuance, - defaultfrozen: defaultFrozen, - unitname: unitName, - assetname: assetName, + defaultFrozen, + unitName, + name: assetName, url: assetURL, - metadatahash: Buffer.from(metadataHash).toString('base64'), - managerkey: manager, - reserveaddr: reserve, - freezeaddr: freeze, - clawbackaddr: clawback, + metadataHash, + manager, + reserve, + freeze, + clawback, decimals, }; this.txn = this.assetTestFixture.lastTxn; - this.lastRound = this.params.lastRound; + this.lastValid = this.params.lastValid; [this.pk] = this.accounts; } ); @@ -1131,7 +1120,7 @@ module.exports = function getSteps(options) { const accountResponse = await this.v2Client .accountInformation(this.assetTestFixture.creator) .do(); - const heldAssets = accountResponse['created-assets']; + const heldAssets = accountResponse.createdAssets; let assetIds = heldAssets.map((asset) => asset.index); assetIds = assetIds.sort(sortKeysAscending); const assetIndex = assetIds[assetIds.length - 1]; @@ -1147,15 +1136,16 @@ module.exports = function getSteps(options) { }); 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' + for (const [key, expectedValue] of Object.entries( + this.assetTestFixture.expectedParams + )) { + const actualValue = this.assetTestFixture.queriedParams.params[key]; + assert.deepStrictEqual( + actualValue, + expectedValue, + `Asset params do not match for ${key}. Actual: ${actualValue}, Expected: ${expectedValue}` ); - }); + } }); When( @@ -1164,8 +1154,8 @@ module.exports = function getSteps(options) { [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.fv = this.params.firstValid; + this.lv = this.params.lastValid; this.note = undefined; this.gh = this.params.genesisHash; // if we truly supplied no managers at all, it would be an asset destroy txn @@ -1174,30 +1164,25 @@ module.exports = function getSteps(options) { 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, - }; + this.assetTestFixture.lastTxn = + algosdk.makeAssetConfigTxnWithSuggestedParamsFromObject({ + sender: this.assetTestFixture.creator, + note: this.note, + manager, + reserve, + freeze, + clawback, + assetIndex: parseInt(this.assetTestFixture.index), + suggestedParams: this.params, + strictEmptyAddressChecking: false, + }); // update vars used by other helpers - this.assetTestFixture.expectedParams.reserveaddr = ''; - this.assetTestFixture.expectedParams.freezeaddr = ''; - this.assetTestFixture.expectedParams.clawbackaddr = ''; + this.assetTestFixture.expectedParams.reserve = undefined; + this.assetTestFixture.expectedParams.freeze = undefined; + this.assetTestFixture.expectedParams.clawback = undefined; this.txn = this.assetTestFixture.lastTxn; - this.lastRound = this.params.lastRound; + this.lastValid = this.params.lastValid; [this.pk] = this.accounts; } ); @@ -1206,27 +1191,21 @@ module.exports = function getSteps(options) { [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.fv = this.params.firstValid; + this.lv = this.params.lastValid; 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, - }; + + this.assetTestFixture.lastTxn = + algosdk.makeAssetDestroyTxnWithSuggestedParamsFromObject({ + sender: this.assetTestFixture.creator, + note: this.note, + assetIndex: parseInt(this.assetTestFixture.index), + suggestedParams: this.params, + }); // update vars used by other helpers this.txn = this.assetTestFixture.lastTxn; - this.lastRound = this.params.lastRound; + this.lastValid = this.params.lastValid; [this.pk] = this.accounts; }); @@ -1246,29 +1225,23 @@ module.exports = function getSteps(options) { 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.fv = this.params.firstValid; + this.lv = this.params.lastValid; 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, - }; + this.assetTestFixture.lastTxn = + algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ + sender: accountToUse, + receiver: accountToUse, + amount: 0, + note: this.note, + assetIndex: parseInt(this.assetTestFixture.index), + suggestedParams: this.params, + }); // update vars used by other helpers this.txn = this.assetTestFixture.lastTxn; - this.lastRound = this.params.lastRound; + this.lastValid = this.params.lastValid; this.pk = accountToUse; } ); @@ -1278,29 +1251,23 @@ module.exports = function getSteps(options) { 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.fv = this.params.firstValid; + this.lv = this.params.lastValid; 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, - }; + this.assetTestFixture.lastTxn = + algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ + sender: this.assetTestFixture.creator, + receiver: this.accounts[1], + amount: parseInt(amount), + note: this.note, + assetIndex: parseInt(this.assetTestFixture.index), + suggestedParams: this.params, + }); // update vars used by other helpers this.txn = this.assetTestFixture.lastTxn; - this.lastRound = this.params.lastRound; + this.lastValid = this.params.lastValid; this.pk = this.assetTestFixture.creator; } ); @@ -1310,29 +1277,23 @@ module.exports = function getSteps(options) { 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.fv = this.params.firstValid; + this.lv = this.params.lastValid; 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, - }; + this.assetTestFixture.lastTxn = + algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ + receiver: this.assetTestFixture.creator, + sender: this.accounts[1], + amount: parseInt(amount), + note: this.note, + assetIndex: parseInt(this.assetTestFixture.index), + suggestedParams: this.params, + }); // update vars used by other helpers this.txn = this.assetTestFixture.lastTxn; - this.lastRound = this.params.lastRound; + this.lastValid = this.params.lastValid; [this.pk] = this.accounts; } ); @@ -1344,7 +1305,7 @@ module.exports = function getSteps(options) { .accountInformation(this.assetTestFixture.creator) .do(); for (const asset of accountInformation.assets) { - if (asset['asset-id'] === this.assetTestFixture.index) { + if (asset.assetId === this.assetTestFixture.index) { assert.deepStrictEqual(asset.amount, parseInt(expectedTotal)); } } @@ -1365,27 +1326,24 @@ module.exports = function getSteps(options) { 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.fv = this.params.firstValid; + this.lv = this.params.lastValid; 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, - }; + this.assetTestFixture.lastTxn = + algosdk.makeAssetFreezeTxnWithSuggestedParamsFromObject({ + sender: freezer, + freezeTarget: this.accounts[1], + assetIndex: parseInt(this.assetTestFixture.index), + frozen: false, + note: this.note, + suggestedParams: this.params, + }); // update vars used by other helpers this.txn = this.assetTestFixture.lastTxn; - this.lastRound = this.params.lastRound; + this.lastValid = this.params.lastValid; this.pk = this.assetTestFixture.creator; } ); @@ -1395,27 +1353,24 @@ module.exports = function getSteps(options) { 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.fv = this.params.firstValid; + this.lv = this.params.lastValid; 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, - }; + this.assetTestFixture.lastTxn = + algosdk.makeAssetFreezeTxnWithSuggestedParamsFromObject({ + sender: freezer, + freezeTarget: this.accounts[1], + assetIndex: parseInt(this.assetTestFixture.index), + frozen: true, + note: this.note, + suggestedParams: this.params, + }); // update vars used by other helpers this.txn = this.assetTestFixture.lastTxn; - this.lastRound = this.params.lastRound; + this.lastValid = this.params.lastValid; this.pk = this.assetTestFixture.creator; } ); @@ -1425,30 +1380,25 @@ module.exports = function getSteps(options) { 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.fv = this.params.firstValid; + this.lv = this.params.lastValid; 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, - }; + + this.assetTestFixture.lastTxn = + algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ + sender: this.assetTestFixture.creator, + receiver: this.assetTestFixture.creator, + assetSender: this.accounts[1], + amount: parseInt(amount), + note: this.note, + genesisHash: this.gh, + assetIndex: parseInt(this.assetTestFixture.index), + suggestedParams: this.params, + }); // update vars used by other helpers this.txn = this.assetTestFixture.lastTxn; - this.lastRound = this.params.lastRound; + this.lastValid = this.params.lastValid; this.pk = this.assetTestFixture.creator; } ); @@ -1473,11 +1423,12 @@ module.exports = function getSteps(options) { Given( 'mock http responses in {string} loaded from {string}', function (expectedBody, format) { + doRaw = false; if (expectedBody !== null) { expectedMockResponse = expectedBody; if (format === 'msgp') { expectedMockResponse = new Uint8Array( - Buffer.from(expectedMockResponse, 'base64') + algosdk.base64ToBytes(expectedMockResponse) ); } } @@ -1500,11 +1451,12 @@ module.exports = function getSteps(options) { Given( 'mock http responses in {string} loaded from {string} with status {int}.', function (expectedBody, status, format) { + doRaw = false; if (expectedBody !== null) { expectedMockResponse = expectedBody; if (format === 'msgp') { expectedMockResponse = new Uint8Array( - Buffer.from(expectedMockResponse, 'base64') + algosdk.base64ToBytes(expectedMockResponse) ); } } @@ -1528,57 +1480,447 @@ module.exports = function getSteps(options) { 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 { + async function (client, endpoint) { + if (client === 'algod') { + switch (endpoint) { + case 'GetStatus': this.actualMockResponse = await this.v2Client.status().do(); + break; + case 'GetBlock': + this.actualMockResponse = await this.v2Client.block(10).do(); + break; + case 'WaitForBlock': + this.actualMockResponse = await this.v2Client + .statusAfterBlock(10) + .do(); + break; + case 'TealCompile': + this.actualMockResponse = await this.v2Client + .compile(makeUint8Array()) + .do(); + break; + case 'RawTransaction': + this.actualMockResponse = await this.v2Client + .sendRawTransaction(makeUint8Array()) + .do(); + break; + case 'GetSupply': + this.actualMockResponse = await this.v2Client.supply().do(); + break; + case 'TransactionParams': { + const response = await this.v2Client.getTransactionParams().do(); + this.actualMockResponse = + new algosdk.modelsv2.TransactionParametersResponse({ + consensusVersion: response.consensusVersion, + fee: response.fee, + genesisHash: response.genesisHash, + genesisId: response.genesisID, + lastRound: response.firstValid, + minFee: response.minFee, + }); + break; } - } 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; + case 'AccountInformation': + this.actualMockResponse = await this.v2Client + .accountInformation(algosdk.Address.zeroAddress()) + .do(); + break; + case 'GetApplicationByID': + this.actualMockResponse = await this.v2Client + .getApplicationByID(10) + .do(); + break; + case 'GetAssetByID': + this.actualMockResponse = await this.v2Client.getAssetByID(10).do(); + break; + case 'PendingTransactionInformation': + this.actualMockResponse = await this.v2Client + .pendingTransactionInformation('transaction') + .do(); + break; + case 'GetPendingTransactions': + this.actualMockResponse = await this.v2Client + .pendingTransactionsInformation() + .do(); + break; + case 'GetPendingTransactionsByAddress': + this.actualMockResponse = await this.v2Client + .pendingTransactionByAddress(algosdk.Address.zeroAddress()) + .do(); + break; + case 'DryRun': + this.actualMockResponse = await this.v2Client + .dryrun( + new algosdk.modelsv2.DryrunRequest({ + accounts: [], + apps: [], + latestTimestamp: 0, + protocolVersion: '', + round: 0, + sources: [], + txns: [], + }) + ) + .do(); + break; + case 'GetTransactionProof': + // fallthrough + case 'Proof': + this.actualMockResponse = await this.v2Client + .getTransactionProof(10, 'asdf') + .do(); + break; + case 'GetGenesis': + this.actualMockResponse = await this.v2Client.genesis().do(); + break; + case 'AccountApplicationInformation': + this.actualMockResponse = await this.v2Client + .accountApplicationInformation(algosdk.Address.zeroAddress(), 10) + .do(); + break; + case 'AccountAssetInformation': + this.actualMockResponse = await this.v2Client + .accountAssetInformation(algosdk.Address.zeroAddress(), 10) + .do(); + break; + case 'GetLightBlockHeaderProof': + this.actualMockResponse = await this.v2Client + .getLightBlockHeaderProof(123) + .do(); + break; + case 'GetStateProof': + this.actualMockResponse = await this.v2Client + .getStateProof(123) + .do(); + break; + case 'GetBlockHash': + this.actualMockResponse = await this.v2Client + .getBlockHash(123) + .do(); + break; + case 'GetSyncRound': + this.actualMockResponse = await this.v2Client.getSyncRound().do(); + break; + case 'GetBlockTimeStampOffset': + this.actualMockResponse = await this.v2Client + .getBlockOffsetTimestamp() + .do(); + break; + case 'GetLedgerStateDelta': + this.actualMockResponse = await this.v2Client + .getLedgerStateDelta(123) + .do(); + break; + case 'GetTransactionGroupLedgerStateDeltaForRound': + this.actualMockResponse = await this.v2Client + .getTransactionGroupLedgerStateDeltasForRound(123) + .do(); + break; + case 'GetLedgerStateDeltaForTransactionGroup': + this.actualMockResponse = await this.v2Client + .getLedgerStateDeltaForTransactionGroup('someID') + .do(); + break; + case 'GetBlockTxids': + this.actualMockResponse = await this.v2Client + .getBlockTxids(123) + .do(); + break; + case 'any': { + // This is an error case + let caughtError = false; + try { + await this.v2Client.status().do(); + } catch (err) { + assert.strictEqual(this.expectedMockResponseCode, 500); + assert.ok( + err.toString().includes('Received status 500'), + `expected response code 500 implies error Internal Server Error but instead had error: ${err}` + ); + + assert.ok(err.response.body); + this.actualMockResponse = err.response.parseBodyAsJSON({ + intDecoding: algosdk.IntDecoding.MIXED, + }); + caughtError = true; + } + if (!caughtError) { + throw new Error('Expected error response, got none.'); + } + break; + } + default: + throw new Error(`Unrecognized algod endpoint: ${endpoint}`); } - 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}` - ); + } else if (client === 'indexer') { + switch (endpoint) { + case 'lookupAccountByID': + this.actualMockResponse = await this.indexerClient + .lookupAccountByID(algosdk.Address.zeroAddress()) + .do(); + break; + case 'searchForAccounts': + this.actualMockResponse = await this.indexerClient + .searchAccounts() + .do(); + break; + case 'lookupApplicationByID': + this.actualMockResponse = await this.indexerClient + .lookupApplications(10) + .do(); + break; + case 'searchForApplications': + this.actualMockResponse = await this.indexerClient + .searchForApplications() + .do(); + break; + case 'lookupAssetBalances': + this.actualMockResponse = await this.indexerClient + .lookupAssetBalances(10) + .do(); + break; + case 'lookupAssetByID': + this.actualMockResponse = await this.indexerClient + .lookupAssetByID(10) + .do(); + break; + case 'searchForAssets': + this.actualMockResponse = await this.indexerClient + .searchForAssets() + .do(); + break; + case 'lookupAccountTransactions': + this.actualMockResponse = await this.indexerClient + .lookupAccountTransactions(algosdk.Address.zeroAddress()) + .do(); + break; + case 'lookupAssetTransactions': + this.actualMockResponse = await this.indexerClient + .lookupAssetTransactions(10) + .do(); + break; + case 'searchForTransactions': + this.actualMockResponse = await this.indexerClient + .searchForTransactions() + .do(); + break; + case 'lookupBlock': + this.actualMockResponse = await this.indexerClient + .lookupBlock(10) + .do(); + break; + case 'lookupTransaction': + this.actualMockResponse = await this.indexerClient + .lookupTransactionByID('') + .do(); + break; + case 'lookupAccountAppLocalStates': + this.actualMockResponse = await this.indexerClient + .lookupAccountAppLocalStates(algosdk.Address.zeroAddress()) + .do(); + break; + case 'lookupAccountCreatedApplications': + this.actualMockResponse = await this.indexerClient + .lookupAccountCreatedApplications(algosdk.Address.zeroAddress()) + .do(); + break; + case 'lookupAccountAssets': + this.actualMockResponse = await this.indexerClient + .lookupAccountAssets(algosdk.Address.zeroAddress()) + .do(); + break; + case 'lookupAccountCreatedAssets': + this.actualMockResponse = await this.indexerClient + .lookupAccountCreatedAssets(algosdk.Address.zeroAddress()) + .do(); + break; + case 'lookupApplicationLogsByID': + this.actualMockResponse = await this.indexerClient + .lookupApplicationLogs(10) + .do(); + break; + case 'any': { + // This is an error case + let caughtError = false; + try { + await this.indexerClient.searchAccounts().do(); + } catch (err) { + assert.strictEqual(this.expectedMockResponseCode, 500); + assert.ok( + err.toString().includes('Received status 500'), + `expected response code 500 implies error Internal Server Error but instead had error: ${err}` + ); + + assert.ok(err.response.body); + this.actualMockResponse = err.response.parseBodyAsJSON({ + intDecoding: algosdk.IntDecoding.MIXED, + }); + caughtError = true; + } + if (!caughtError) { + throw new Error('Expected error response, got none.'); + } + break; } + default: + throw new Error(`Unrecognized indexer endpoint: ${endpoint}`); } + } else { + throw Error(`did not recognize desired client "${client}"`); } } ); + function pruneDefaultValuesFromObject(object) { + if ( + typeof object !== 'object' || + object === null || + Array.isArray(object) + ) { + throw new Error('pruneDefaultValuesFromObject expects an object.'); + } + const prunedObject = makeObject(object); + for (const [key, value] of Object.entries(prunedObject)) { + if ( + value === undefined || + value === null || + value === 0 || + value === BigInt(0) || + value === '' || + value === false || + (Array.isArray(value) && value.length === 0) || + (typeof value === 'object' && Object.keys(value).length === 0) + ) { + delete prunedObject[key]; + continue; + } + if (Array.isArray(value)) { + prunedObject[key] = value.map((element) => + typeof element === 'object' && + !Array.isArray(element) && + element !== null + ? pruneDefaultValuesFromObject(element) + : element + ); + continue; + } + if (typeof value === 'object') { + prunedObject[key] = pruneDefaultValuesFromObject(value); + if (Object.keys(prunedObject[key]).length === 0) { + delete prunedObject[key]; + } + } + } + return prunedObject; + } + + function pruneDefaultValuesFromMap(m) { + function isMap(x) { + // workaround for firefox + const other = makeMap([]); + return x instanceof other.constructor; + } + + function isUint8Array(x) { + // workaround for firefox + const other = makeUint8Array(); + return x instanceof other.constructor; + } + + if (!isMap(m)) { + throw new Error('pruneDefaultValuesFromMap expects a map.'); + } + const prunedMap = makeMap(m); + for (const [key, value] of Array.from(prunedMap.entries())) { + if ( + value === undefined || + value === null || + value === 0 || + value === BigInt(0) || + value === '' || + value === false || + (Array.isArray(value) && value.length === 0) || + (isMap(value) && value.size === 0) || + (isUint8Array(value) && + (value.byteLength === 0 || value.every((byte) => byte === 0))) + ) { + prunedMap.delete(key); + continue; + } + if (Array.isArray(value)) { + prunedMap.set( + key, + value.map((element) => + isMap(element) ? pruneDefaultValuesFromMap(element) : element + ) + ); + continue; + } + if (isMap(value)) { + const prunedValue = pruneDefaultValuesFromMap(value); + if (prunedValue.size === 0) { + prunedMap.delete(key); + } else { + prunedMap.set(key, prunedValue); + } + } + } + return prunedMap; + } + Then('the parsed response should equal the mock response.', function () { + let expectedJsonNeedsPruning = true; + + let encodedResponseObject; 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) - ); + if (typeof this.actualMockResponse.toEncodingData === 'function') { + encodedResponseObject = algosdk.encodeJSON(this.actualMockResponse); + } else { + // Handles responses which don't implement Encodable + encodedResponseObject = algosdk.stringifyJSON( + this.actualMockResponse + ); + expectedJsonNeedsPruning = false; + } } else { - assert.deepStrictEqual( - algosdk.decodeObj( - new Uint8Array(algosdk.encodeObj(this.actualMockResponse)) - ), - algosdk.decodeObj(expectedMockResponse) + encodedResponseObject = algosdk.encodeMsgpack(this.actualMockResponse); + } + } else { + encodedResponseObject = algosdk.stringifyJSON(this.actualMockResponse); + } + + // We chain encoding/decoding below to normalize the objects for comparison. This helps deal + // with type differences such as bigint vs number and Uint8Array vs Buffer. + + let actualResponseObject; + let parsedExpectedMockResponse; + if (responseFormat === 'json') { + actualResponseObject = algosdk.parseJSON(encodedResponseObject, { + intDecoding: algosdk.IntDecoding.MIXED, + }); + parsedExpectedMockResponse = algosdk.parseJSON(expectedMockResponse, { + intDecoding: algosdk.IntDecoding.MIXED, + }); + if (expectedJsonNeedsPruning) { + // Prune default values from the actual response object to match the expected response object. + parsedExpectedMockResponse = pruneDefaultValuesFromObject( + parsedExpectedMockResponse ); } + } else { + actualResponseObject = algosdk.msgpackRawDecodeAsMap( + encodedResponseObject + ); + parsedExpectedMockResponse = + algosdk.msgpackRawDecodeAsMap(expectedMockResponse); + + parsedExpectedMockResponse = pruneDefaultValuesFromMap( + parsedExpectedMockResponse + ); } + + assert.deepStrictEqual(actualResponseObject, parsedExpectedMockResponse); }); Then('expect error string to contain {string}', (expectedErrorString) => { @@ -1590,6 +1932,7 @@ module.exports = function getSteps(options) { }); Given('mock server recording request paths', function () { + doRaw = true; this.v2Client = new algosdk.Algodv2( '', `http://${mockAlgodPathRecorderHost}`, @@ -1686,7 +2029,7 @@ module.exports = function getSteps(options) { if (format !== 'msgpack') { assert.fail('this SDK only supports format msgpack for this function'); } - await this.v2Client.pendingTransactionInformation(txid).do(); + await doOrDoRaw(this.v2Client.pendingTransactionInformation(txid)); } ); @@ -1696,14 +2039,16 @@ module.exports = function getSteps(options) { if (format !== 'msgpack') { assert.fail('this SDK only supports format msgpack for this function'); } - await this.v2Client.pendingTransactionsInformation().max(max).do(); + await doOrDoRaw(this.v2Client.pendingTransactionsInformation().max(max)); } ); 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(); + async function (account, max) { + await doOrDoRaw( + this.v2Client.pendingTransactionByAddress(account).max(max) + ); } ); @@ -1713,51 +2058,55 @@ module.exports = function getSteps(options) { if (format !== 'msgpack') { assert.fail('this SDK only supports format msgpack for this function'); } - await this.v2Client.pendingTransactionByAddress(account).max(max).do(); + await doOrDoRaw( + this.v2Client.pendingTransactionByAddress(account).max(max) + ); } ); When( 'we make a Status after Block call with round {int}', async function (round) { - await this.v2Client.statusAfterBlock(round).do(); + await doOrDoRaw(this.v2Client.statusAfterBlock(round)); } ); 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(); + await doOrDoRaw( + this.v2Client.accountInformation(account).exclude(exclude) + ); } ); When( 'we make an Account Information call against account {string}', async function (account) { - await this.v2Client.accountInformation(account).do(); + await doOrDoRaw(this.v2Client.accountInformation(account)); } ); When( 'we make an Account Asset Information call against account {string} assetID {int}', async function (account, assetID) { - await this.v2Client.accountAssetInformation(account, assetID).do(); + await doOrDoRaw(this.v2Client.accountAssetInformation(account, assetID)); } ); When( 'we make an Account Application Information call against account {string} applicationID {int}', async function (account, applicationID) { - await this.v2Client - .accountApplicationInformation(account, applicationID) - .do(); + await doOrDoRaw( + this.v2Client.accountApplicationInformation(account, applicationID) + ); } ); When( 'we make a Get Block call against block number {int}', - function (blockNum) { - this.v2Client.block(blockNum).do(); + async function (blockNum) { + await doOrDoRaw(this.v2Client.block(blockNum)); } ); @@ -1767,18 +2116,18 @@ module.exports = function getSteps(options) { if (format !== 'msgpack') { assert.fail('this SDK only supports format msgpack for this function'); } - await this.v2Client.block(blockNum).do(); + await doOrDoRaw(this.v2Client.block(blockNum)); } ); When('we make a GetAssetByID call for assetID {int}', async function (index) { - await this.v2Client.getAssetByID(index).do(); + await doOrDoRaw(this.v2Client.getAssetByID(index)); }); When( 'we make a GetApplicationByID call for applicationID {int}', async function (index) { - await this.v2Client.getApplicationByID(index).do(); + await doOrDoRaw(this.v2Client.getApplicationByID(index)); } ); @@ -1800,27 +2149,26 @@ module.exports = function getSteps(options) { let anyPendingTransactionInfoResponse; When('we make any Pending Transaction Information call', async function () { - anyPendingTransactionInfoResponse = await this.v2Client - .pendingTransactionInformation() - .do(); + anyPendingTransactionInfoResponse = await doOrDoRaw( + this.v2Client.pendingTransactionInformation() + ); }); Then( 'the parsed Pending Transaction Information response should have sender {string}', (sender) => { - const actualSender = algosdk.encodeAddress( - anyPendingTransactionInfoResponse.txn.txn.snd - ); - assert.strictEqual(sender, actualSender); + const actualSender = + anyPendingTransactionInfoResponse.txn.txn.sender.toString(); + assert.strictEqual(actualSender, sender); } ); let anyPendingTransactionsInfoResponse; When('we make any Pending Transactions Information call', async function () { - anyPendingTransactionsInfoResponse = await this.v2Client - .pendingTransactionsInformation() - .do(); + anyPendingTransactionsInfoResponse = await doOrDoRaw( + this.v2Client.pendingTransactionsInformation() + ); }); Then( @@ -1828,14 +2176,14 @@ module.exports = function getSteps(options) { (len, idx, sender) => { assert.strictEqual( len, - anyPendingTransactionsInfoResponse['top-transactions'].length + anyPendingTransactionsInfoResponse.topTransactions.length ); if (len !== 0) { assert.strictEqual( - sender, - algosdk.encodeAddress( - anyPendingTransactionsInfoResponse['top-transactions'][idx].txn.snd - ) + anyPendingTransactionsInfoResponse.topTransactions[ + idx + ].txn.sender.toString(), + sender ); } } @@ -1844,24 +2192,26 @@ module.exports = function getSteps(options) { let anySendRawTransactionResponse; When('we make any Send Raw Transaction call', async function () { - anySendRawTransactionResponse = await this.v2Client - .sendRawTransaction(makeUint8Array(0)) - .do(); + anySendRawTransactionResponse = await doOrDoRaw( + this.v2Client.sendRawTransaction(makeUint8Array(0)) + ); }); Then( 'the parsed Send Raw Transaction response should have txid {string}', (txid) => { - assert.strictEqual(txid, anySendRawTransactionResponse.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(); + anyPendingTransactionsByAddressResponse = await doOrDoRaw( + this.v2Client.pendingTransactionByAddress( + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJI' + ) + ); }); Then( @@ -1869,15 +2219,15 @@ module.exports = function getSteps(options) { (len, idx, sender) => { assert.strictEqual( len, - anyPendingTransactionsByAddressResponse['total-transactions'] + anyPendingTransactionsByAddressResponse.totalTransactions ); if (len === 0) { return; } - let actualSender = - anyPendingTransactionsByAddressResponse['top-transactions'][idx].txn - .snd; - actualSender = algosdk.encodeAddress(actualSender); + const actualSender = + anyPendingTransactionsByAddressResponse.topTransactions[ + idx + ].txn.sender.toString(); assert.strictEqual(sender, actualSender); } ); @@ -1885,48 +2235,56 @@ module.exports = function getSteps(options) { let anyNodeStatusResponse; When('we make any Node Status call', async function () { - anyNodeStatusResponse = await this.v2Client.status().do(); + anyNodeStatusResponse = await doOrDoRaw(this.v2Client.status()); }); Then( 'the parsed Node Status response should have a last round of {int}', (lastRound) => { - assert.strictEqual(lastRound, anyNodeStatusResponse['last-round']); + assert.strictEqual(BigInt(lastRound), anyNodeStatusResponse.lastRound); } ); let anyLedgerSupplyResponse; When('we make any Ledger Supply call', async function () { - anyLedgerSupplyResponse = await this.v2Client.supply().do(); + anyLedgerSupplyResponse = await doOrDoRaw(this.v2Client.supply()); }); 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); + assert.strictEqual( + BigInt(totalMoney), + anyLedgerSupplyResponse.totalMoney + ); + assert.strictEqual( + BigInt(onlineMoney), + anyLedgerSupplyResponse.onlineMoney + ); + assert.strictEqual(BigInt(round), anyLedgerSupplyResponse.currentRound); } ); When('we make any Status After Block call', async function () { - anyNodeStatusResponse = await this.v2Client.statusAfterBlock(1).do(); + anyNodeStatusResponse = await doOrDoRaw(this.v2Client.statusAfterBlock(1)); }); Then( 'the parsed Status After Block response should have a last round of {int}', (lastRound) => { - assert.strictEqual(lastRound, anyNodeStatusResponse['last-round']); + assert.strictEqual(BigInt(lastRound), anyNodeStatusResponse.lastRound); } ); let anyAccountInformationResponse; When('we make any Account Information call', async function () { - anyAccountInformationResponse = await this.v2Client - .accountInformation() - .do(); + anyAccountInformationResponse = await doOrDoRaw( + this.v2Client.accountInformation( + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJI' + ) + ); }); Then( @@ -1939,15 +2297,19 @@ module.exports = function getSteps(options) { let anyBlockResponse; When('we make any Get Block call', async function () { - anyBlockResponse = await this.v2Client.block(1).do(); + anyBlockResponse = await doOrDoRaw(this.v2Client.block(1)); }); Then( 'the parsed Get Block response should have rewards pool {string}', (rewardsPoolAddress) => { - const rewardsPoolB64String = Buffer.from( - anyBlockResponse.block.rwd - ).toString('base64'); + assert.ok( + anyBlockResponse.block.header.rewardState.rewardsPool instanceof + algosdk.Address + ); + const rewardsPoolB64String = algosdk.bytesToBase64( + anyBlockResponse.block.header.rewardState.rewardsPool.publicKey + ); assert.strictEqual(rewardsPoolAddress, rewardsPoolB64String); } ); @@ -1955,17 +2317,17 @@ module.exports = function getSteps(options) { let anySuggestedTransactionsResponse; When('we make any Suggested Transaction Parameters call', async function () { - anySuggestedTransactionsResponse = await this.v2Client - .getTransactionParams() - .do(); + anySuggestedTransactionsResponse = await doOrDoRaw( + this.v2Client.getTransactionParams() + ); }); Then( 'the parsed Suggested Transaction Parameters response should have first round valid of {int}', - (firstRound) => { + (firstValid) => { assert.strictEqual( - firstRound, - anySuggestedTransactionsResponse.firstRound + BigInt(firstValid), + anySuggestedTransactionsResponse.firstValid ); } ); @@ -1979,12 +2341,13 @@ module.exports = function getSteps(options) { currencyGreater, currencyLesser ) { - await this.indexerClient - .lookupAssetBalances(index) - .currencyGreaterThan(currencyGreater) - .currencyLessThan(currencyLesser) - .limit(limit) - .do(); + await doOrDoRaw( + this.indexerClient + .lookupAssetBalances(index) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .limit(limit) + ); } ); @@ -2012,24 +2375,25 @@ module.exports = function getSteps(options) { if (excludeCloseToAsString === 'true') { excludeCloseTo = true; } - await this.indexerClient - .lookupAssetTransactions(assetIndex) - .beforeTime(beforeTime) - .afterTime(afterTime) - .address(address) - .addressRole(addressRole) - .currencyGreaterThan(currencyGreater) - .currencyLessThan(currencyLesser) - .limit(limit) - .minRound(minRound) - .maxRound(maxRound) - .notePrefix(notePrefix) - .round(round) - .sigType(sigType) - .txid(txid) - .txType(txType) - .excludeCloseTo(excludeCloseTo) - .do(); + await doOrDoRaw( + this.indexerClient + .lookupAssetTransactions(assetIndex) + .beforeTime(beforeTime) + .afterTime(afterTime) + .address(address) + .addressRole(addressRole) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .limit(limit) + .minRound(minRound) + .maxRound(maxRound) + .notePrefix(notePrefix) + .round(round) + .sigType(sigType) + .txid(txid) + .txType(txType) + .excludeCloseTo(excludeCloseTo) + ); } ); @@ -2102,22 +2466,23 @@ module.exports = function getSteps(options) { currencyLesser, assetIndex ) { - await this.indexerClient - .lookupAccountTransactions(account) - .beforeTime(beforeTime) - .afterTime(afterTime) - .assetID(assetIndex) - .currencyGreaterThan(currencyGreater) - .currencyLessThan(currencyLesser) - .limit(limit) - .maxRound(maxRound) - .minRound(minRound) - .notePrefix(notePrefix) - .round(round) - .sigType(sigType) - .txid(txid) - .txType(txType) - .do(); + await doOrDoRaw( + this.indexerClient + .lookupAccountTransactions(account) + .beforeTime(beforeTime) + .afterTime(afterTime) + .assetID(assetIndex) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .limit(limit) + .maxRound(maxRound) + .minRound(minRound) + .notePrefix(notePrefix) + .round(round) + .sigType(sigType) + .txid(txid) + .txType(txType) + ); } ); @@ -2168,35 +2533,39 @@ module.exports = function getSteps(options) { When( 'we make a Lookup Block call against round {int}', async function (round) { - await this.indexerClient.lookupBlock(round).do(); + await doOrDoRaw(this.indexerClient.lookupBlock(round)); } ); 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(); + await doOrDoRaw(this.indexerClient.lookupBlock(int).headerOnly(string)); } ); 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(); + await doOrDoRaw( + this.indexerClient.lookupAccountByID(account).round(round) + ); } ); 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(); + await doOrDoRaw( + this.indexerClient.lookupAccountByID(account).exclude(exclude) + ); } ); When( 'we make a Lookup Asset by ID call against asset index {int}', async function (assetIndex) { - await this.indexerClient.lookupAssetByID(assetIndex).do(); + await doOrDoRaw(this.indexerClient.lookupAssetByID(assetIndex)); } ); @@ -2224,29 +2593,31 @@ module.exports = function getSteps(options) { 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(); + await doOrDoRaw( + this.indexerClient + .lookupApplicationLogs(appID) + .limit(limit) + .maxRound(maxRound) + .minRound(minRound) + .nextToken(nextToken) + .sender(sender) + .txid(txID) + ); } ); 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) - .currencyGreaterThan(currencyGreater) - .currencyLessThan(currencyLesser) - .limit(limit) - .round(round) - .do(); + await doOrDoRaw( + this.indexerClient + .searchAccounts() + .assetID(assetIndex) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .limit(limit) + .round(round) + ); } ); @@ -2275,14 +2646,16 @@ module.exports = function getSteps(options) { When( 'we make a Search Accounts call with exclude {string}', async function (exclude) { - await this.indexerClient.searchAccounts().exclude(exclude).do(); + await doOrDoRaw(this.indexerClient.searchAccounts().exclude(exclude)); } ); When( 'we make a SearchForApplications call with creator {string}', async function (creator) { - await this.indexerClient.searchForApplications().creator(creator).do(); + await doOrDoRaw( + this.indexerClient.searchForApplications().creator(creator) + ); } ); @@ -2310,25 +2683,26 @@ module.exports = function getSteps(options) { if (excludeCloseToAsString === 'true') { excludeCloseTo = true; } - await this.indexerClient - .searchForTransactions() - .address(account) - .addressRole(addressRole) - .assetID(assetIndex) - .beforeTime(beforeTime) - .afterTime(afterTime) - .currencyGreaterThan(currencyGreater) - .currencyLessThan(currencyLesser) - .limit(limit) - .maxRound(maxRound) - .minRound(minRound) - .notePrefix(notePrefix) - .round(round) - .sigType(sigType) - .txid(txid) - .txType(txType) - .excludeCloseTo(excludeCloseTo) - .do(); + await doOrDoRaw( + this.indexerClient + .searchForTransactions() + .address(account) + .addressRole(addressRole) + .assetID(assetIndex) + .beforeTime(beforeTime) + .afterTime(afterTime) + .currencyGreaterThan(currencyGreater) + .currencyLessThan(currencyLesser) + .limit(limit) + .maxRound(maxRound) + .minRound(minRound) + .notePrefix(notePrefix) + .round(round) + .sigType(sigType) + .txid(txid) + .txType(txType) + .excludeCloseTo(excludeCloseTo) + ); } ); @@ -2387,28 +2761,29 @@ module.exports = function getSteps(options) { 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(); + await doOrDoRaw( + this.indexerClient + .searchForAssets() + .limit(limit) + .creator(creator) + .name(name) + .unit(unit) + .index(index) + ); } ); When( 'we make a SearchForApplications call with applicationID {int}', async function (index) { - await this.indexerClient.searchForApplications().index(index).do(); + await doOrDoRaw(this.indexerClient.searchForApplications().index(index)); } ); When( 'we make a LookupApplications call with applicationID {int}', async function (index) { - await this.indexerClient.lookupApplications(index).do(); + await doOrDoRaw(this.indexerClient.lookupApplications(index)); } ); @@ -2417,7 +2792,6 @@ module.exports = function getSteps(options) { When('we make any LookupAssetBalances call', async function () { anyLookupAssetBalancesResponse = await this.indexerClient .lookupAssetBalances() - .setIntDecoding('mixed') .do(); }); @@ -2425,12 +2799,12 @@ module.exports = function getSteps(options) { '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'] + anyLookupAssetBalancesResponse.currentRound, + BigInt(round) ); assert.strictEqual( - length, - anyLookupAssetBalancesResponse.balances.length + anyLookupAssetBalancesResponse.balances.length, + length ); if (length === 0) { return; @@ -2440,12 +2814,12 @@ module.exports = function getSteps(options) { frozenState = true; } assert.strictEqual( - amount, - anyLookupAssetBalancesResponse.balances[idx].amount + anyLookupAssetBalancesResponse.balances[idx].amount, + BigInt(amount) ); assert.strictEqual( - frozenState, - anyLookupAssetBalancesResponse.balances[idx]['is-frozen'] + anyLookupAssetBalancesResponse.balances[idx].isFrozen, + frozenState ); } ); @@ -2453,52 +2827,56 @@ module.exports = function getSteps(options) { 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(); + await doOrDoRaw( + this.indexerClient + .lookupAccountAssets(account) + .assetId(assetID) + .includeAll(includeAll === 'true') + .limit(limit) + .nextToken(next) + ); } ); 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(); + await doOrDoRaw( + this.indexerClient + .lookupAccountCreatedAssets(account) + .assetID(assetID) + .includeAll(includeAll === 'true') + .limit(limit) + .nextToken(next) + ); } ); 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(); + await doOrDoRaw( + this.indexerClient + .lookupAccountAppLocalStates(account) + .applicationID(applicationID) + .includeAll(includeAll === 'true') + .limit(limit) + .nextToken(next) + ); } ); 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(); + await doOrDoRaw( + this.indexerClient + .lookupAccountCreatedApplications(account) + .applicationID(applicationID) + .includeAll(includeAll === 'true') + .limit(limit) + .nextToken(next) + ); } ); @@ -2507,7 +2885,6 @@ module.exports = function getSteps(options) { When('we make any LookupAssetTransactions call', async function () { anyLookupAssetTransactionsResponse = await this.indexerClient .lookupAssetTransactions() - .setIntDecoding('mixed') .do(); }); @@ -2515,19 +2892,19 @@ module.exports = function getSteps(options) { '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'] + anyLookupAssetTransactionsResponse.currentRound, + BigInt(round) ); assert.strictEqual( - length, - anyLookupAssetTransactionsResponse.transactions.length + anyLookupAssetTransactionsResponse.transactions.length, + length ); if (length === 0) { return; } assert.strictEqual( - sender, - anyLookupAssetTransactionsResponse.transactions[idx].sender + anyLookupAssetTransactionsResponse.transactions[idx].sender, + sender ); } ); @@ -2536,7 +2913,9 @@ module.exports = function getSteps(options) { When('we make any LookupAccountTransactions call', async function () { anyLookupAccountTransactionsResponse = await this.indexerClient - .lookupAccountTransactions() + .lookupAccountTransactions( + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJI' + ) .do(); }); @@ -2544,12 +2923,12 @@ module.exports = function getSteps(options) { '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'] + anyLookupAccountTransactionsResponse.currentRound, + BigInt(round) ); assert.strictEqual( - length, - anyLookupAccountTransactionsResponse.transactions.length + anyLookupAccountTransactionsResponse.transactions.length, + length ); if (length === 0) { return; @@ -2571,8 +2950,8 @@ module.exports = function getSteps(options) { 'the parsed LookupBlock response should have previous block hash {string}', (prevHash) => { assert.strictEqual( - prevHash, - anyLookupBlockResponse['previous-block-hash'] + algosdk.bytesToBase64(anyLookupBlockResponse.previousBlockHash), + prevHash ); } ); @@ -2581,14 +2960,16 @@ module.exports = function getSteps(options) { When('we make any LookupAccountByID call', async function () { anyLookupAccountByIDResponse = await this.indexerClient - .lookupAccountByID() + .lookupAccountByID( + 'MO2H6ZU47Q36GJ6GVHUKGEBEQINN7ZWVACMWZQGIYUOE3RBSRVYHV4ACJI' + ) .do(); }); Then( 'the parsed LookupAccountByID response should have address {string}', (address) => { - assert.strictEqual(address, anyLookupAccountByIDResponse.account.address); + assert.strictEqual(anyLookupAccountByIDResponse.account.address, address); } ); @@ -2597,12 +2978,11 @@ module.exports = function getSteps(options) { 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); + assert.strictEqual(anyLookupAssetByIDResponse.asset.index, BigInt(idx)); }); let anySearchAccountsResponse; @@ -2614,14 +2994,14 @@ module.exports = function getSteps(options) { 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); + assert.strictEqual(anySearchAccountsResponse.currentRound, BigInt(round)); + assert.strictEqual(anySearchAccountsResponse.accounts.length, length); if (length === 0) { return; } assert.strictEqual( - address, - anySearchAccountsResponse.accounts[idx].address + anySearchAccountsResponse.accounts[idx].address, + address ); } ); @@ -2629,14 +3009,14 @@ module.exports = function getSteps(options) { 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); + assert.strictEqual(anySearchAccountsResponse.currentRound, BigInt(round)); + assert.strictEqual(anySearchAccountsResponse.accounts.length, length); if (length === 0) { return; } assert.strictEqual( - authAddress, - anySearchAccountsResponse.accounts[idx]['auth-addr'] + anySearchAccountsResponse.accounts[idx].authAddr, + authAddress ); } ); @@ -2653,19 +3033,19 @@ module.exports = function getSteps(options) { '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'] + anySearchForTransactionsResponse.currentRound, + BigInt(round) ); assert.strictEqual( - length, - anySearchForTransactionsResponse.transactions.length + anySearchForTransactionsResponse.transactions.length, + length ); if (length === 0) { return; } assert.strictEqual( - sender, - anySearchForTransactionsResponse.transactions[idx].sender + anySearchForTransactionsResponse.transactions[idx].sender, + sender ); } ); @@ -2674,19 +3054,19 @@ module.exports = function getSteps(options) { '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'] + anySearchForTransactionsResponse.currentRound, + BigInt(round) ); assert.strictEqual( - length, - anySearchForTransactionsResponse.transactions.length + anySearchForTransactionsResponse.transactions.length, + length ); if (length === 0) { return; } assert.strictEqual( - rekeyTo, - anySearchForTransactionsResponse.transactions[idx]['rekey-to'] + anySearchForTransactionsResponse.transactions[idx].rekeyTo, + rekeyTo ); } ); @@ -2702,14 +3082,17 @@ module.exports = function getSteps(options) { 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); + assert.strictEqual( + anySearchForAssetsResponse.currentRound, + BigInt(round) + ); + assert.strictEqual(anySearchForAssetsResponse.assets.length, length); if (length === 0) { return; } assert.strictEqual( - assetIndex, - anySearchForAssetsResponse.assets[idx].index + anySearchForAssetsResponse.assets[idx].index, + BigInt(assetIndex) ); } ); @@ -2719,7 +3102,7 @@ module.exports = function getSteps(options) { /// ///////////////////////////////// When('I add a rekeyTo field with address {string}', function (address) { - this.txn.reKeyTo = address; + this.txn.rekeyTo = algosdk.decodeAddress(address); }); When( @@ -2727,27 +3110,37 @@ module.exports = function getSteps(options) { function () { const keypair = keyPairFromSecretKey(this.sk); const pubKeyFromSk = keypair.publicKey; - this.txn.reKeyTo = algosdk.encodeAddress(pubKeyFromSk); + this.txn.rekeyTo = algosdk.decodeAddress( + algosdk.encodeAddress(pubKeyFromSk) + ); } ); - When('I set the from address to {string}', function (from) { - this.txn.from = from; + When('I set the from address to {string}', function (sender) { + this.txn.sender = algosdk.decodeAddress(sender); }); let dryrunResponse; When('we make any Dryrun call', async function () { - const dr = new algosdk.modelsv2.DryrunRequest({}); + const dr = new algosdk.modelsv2.DryrunRequest({ + accounts: [], + apps: [], + latestTimestamp: 7, + protocolVersion: 'future', + round: 100, + sources: [], + txns: [], + }); 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].globalDelta[0].key, key); assert.strictEqual( - dryrunResponse.txns[0]['global-delta'][0].value.action, + dryrunResponse.txns[0].globalDelta[0].value.action, action ); } @@ -2755,38 +3148,37 @@ module.exports = function getSteps(options) { 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, + const sp = await this.v2Client.getTransactionParams().do(); + const algoTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: 'UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M', + receiver: 'UAPJE355K7BG7RQVMTZOW7QW4ICZJEIC3RZGYG5LSHZ65K6LCNFPJDSR7M', amount: 1000, - firstRound: 1, - lastRound: 1000, - type: 'pay', - genesisHash: 'ZIkPs8pTDxbRJsFB1yJ7gvnpDu0Q85FRkl2NCkEAQLU=', + suggestedParams: sp, }); let txns; - let sources; + let sources = []; switch (kind) { case 'compiled': txns = [ - { + new algosdk.SignedTransaction({ lsig: new algosdk.LogicSig(data), txn: algoTxn, - }, + }), ]; break; case 'source': txns = [ - { + new algosdk.SignedTransaction({ txn: algoTxn, - }, + }), ]; sources = [ new algosdk.modelsv2.DryrunSource({ fieldName: 'lsig', - source: data.toString('utf8'), + source: new TextDecoder().decode(data), txnIndex: 0, + appIndex: 0, }), ]; break; @@ -2795,6 +3187,11 @@ module.exports = function getSteps(options) { } const dr = new algosdk.modelsv2.DryrunRequest({ + accounts: [], + apps: [], + latestTimestamp: 0, + protocolVersion: '', + round: 0, txns, sources, }); @@ -2804,16 +3201,13 @@ module.exports = function getSteps(options) { 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']; + if (res.logicSigMessages !== undefined && res.logicSigMessages.length > 0) { + msgs = res.logicSigMessages; } else if ( - res['app-call-messages'] !== undefined && - res['app-call-messages'].length > 0 + res.appCallMessages !== undefined && + res.appCallMessages.length > 0 ) { - msgs = res['app-call-messages']; + msgs = res.appCallMessages; } assert.ok(msgs.length > 0); assert.strictEqual(msgs[0], result); @@ -2850,7 +3244,7 @@ module.exports = function getSteps(options) { async (program) => { const data = await loadResource(program); const decodedResult = makeUint8Array( - Buffer.from(compileResponse.result, 'base64') + algosdk.base64ToBytes(compileResponse.result) ); assert.deepStrictEqual(makeUint8Array(data), decodedResult); } @@ -2861,7 +3255,7 @@ module.exports = function getSteps(options) { /// ///////////////////////////////// Given('base64 encoded data to sign {string}', function (data) { - this.data = Buffer.from(data, 'base64'); + this.data = algosdk.base64ToBytes(data); }); Given('program hash {string}', function (contractAddress) { @@ -2869,13 +3263,13 @@ module.exports = function getSteps(options) { }); Given('base64 encoded program {string}', function (programEncoded) { - const program = Buffer.from(programEncoded, 'base64'); + const program = algosdk.base64ToBytes(programEncoded); 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 seed = algosdk.base64ToBytes(keyEncoded); const keys = keyPairFromSeed(seed); this.sk = keys.secretKey; }); @@ -2885,7 +3279,7 @@ module.exports = function getSteps(options) { }); Then('the signature should be equal to {string}', function (expectedEncoded) { - const expected = makeUint8Array(Buffer.from(expectedEncoded, 'base64')); + const expected = makeUint8Array(algosdk.base64ToBytes(expectedEncoded)); assert.deepStrictEqual(this.sig, expected); }); @@ -2897,7 +3291,7 @@ module.exports = function getSteps(options) { 'a signing account with address {string} and mnemonic {string}', function (address, mnemonic) { this.signingAccount = algosdk.mnemonicToSecretKey(mnemonic); - if (this.signingAccount.addr !== address) { + if (this.signingAccount.addr.toString() !== address) { throw new Error( `Address does not match mnemonic: ${this.signingAccount.addr} !== ${address}` ); @@ -2920,8 +3314,8 @@ module.exports = function getSteps(options) { receiver === 'transient' ? this.transientAccount.addr : receiver; this.txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ - from, - to, + sender: from, + receiver: to, amount: parseInt(amount, 10), closeRemainderTo: closeTo.length === 0 ? undefined : closeTo, suggestedParams: this.suggestedParams, @@ -2933,15 +3327,13 @@ module.exports = function getSteps(options) { "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 toSign = concatArrays( + new TextEncoder().encode('appID'), + algosdk.encodeUint64(appID) ); + const expected = new algosdk.Address(makeUint8Array(genericHash(toSign))); const actual = algosdk.getApplicationAddress(appID); - assert.strictEqual(expected, actual); + assert.deepStrictEqual(expected, actual); } ); @@ -2949,41 +3341,42 @@ module.exports = function getSteps(options) { "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), + if (sp.firstValid === 0) sp.firstValid = 1; + const fundingTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: this.accounts[0], + receiver: algosdk.getApplicationAddress(this.currentApplicationIndex), amount, suggestedParams: sp, - }; + }); const stxn = await this.kcl.signTransaction( this.handle, this.wallet_pswd, - fundingTxnArgs + fundingTxn ); const fundingResponse = await this.v2Client.sendRawTransaction(stxn).do(); const info = await algosdk.waitForConfirmation( this.v2Client, - fundingResponse.txId, + fundingResponse.txid, 1 ); - assert.ok(info['confirmed-round'] > 0); + assert.ok(info.confirmedRound > 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) { + function (fee, flatFee, firstValid, lastValid, genesisHashB64, genesisID) { assert.ok(['true', 'false'].includes(flatFee)); this.suggestedParams = { + minFee: 1000, // Would be nice to take this as an argument in the future flatFee: flatFee === 'true', fee, - firstRound, - lastRound, + firstValid, + lastValid, genesisID, - genesisHash, + genesisHash: algosdk.base64ToBytes(genesisHashB64), }; } ); @@ -3002,19 +3395,21 @@ module.exports = function getSteps(options) { ) { assert.ok(['true', 'false'].includes(nonpart)); - this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParams( + this.txn = algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject({ sender, - undefined, - votePk.length ? votePk : undefined, - selectionPk.length ? selectionPk : undefined, - voteFirst, - voteLast, - keyDilution, - this.suggestedParams, - undefined, - nonpart === 'true', - stateProofPk.length ? stateProofPk : undefined - ); + voteKey: votePk.length ? algosdk.base64ToBytes(votePk) : undefined, + selectionKey: selectionPk.length + ? algosdk.base64ToBytes(selectionPk) + : undefined, + stateProofKey: stateProofPk.length + ? algosdk.base64ToBytes(stateProofPk) + : undefined, + voteFirst: voteFirst || undefined, + voteLast: voteLast || undefined, + voteKeyDilution: keyDilution || undefined, + nonParticipation: nonpart === 'true', + suggestedParams: this.suggestedParams, + }); } ); @@ -3051,7 +3446,7 @@ module.exports = function getSteps(options) { try { const compiledResponse = await client.compile(data).do(); const compiledProgram = makeUint8Array( - Buffer.from(compiledResponse.result, 'base64') + algosdk.base64ToBytes(compiledResponse.result) ); return compiledProgram; } catch (err) { @@ -3139,124 +3534,107 @@ module.exports = function getSteps(options) { } // build suggested params object const sp = { - genesisHash: genesisHashBase64, - firstRound: firstValid, - lastRound: lastValid, + genesisHash: algosdk.base64ToBytes(genesisHashBase64), + firstValid, + lastValid, fee, flatFee: true, + minFee: 1000, // Shouldn't matter because flatFee=true }; switch (operationString) { case 'call': - this.txn = algosdk.makeApplicationNoOpTxn( + this.txn = algosdk.makeApplicationNoOpTxnFromObject({ sender, - sp, appIndex, appArgs, - appAccounts, + accounts: appAccounts, foreignApps, foreignAssets, - undefined, - undefined, - undefined, - boxes - ); + boxes, + suggestedParams: sp, + }); return; case 'create': - this.txn = algosdk.makeApplicationCreateTxn( + this.txn = algosdk.makeApplicationCreateTxnFromObject({ sender, - sp, - operation, - approvalProgramBytes, - clearProgramBytes, + onComplete: operation, + approvalProgram: approvalProgramBytes, + clearProgram: clearProgramBytes, numLocalInts, numLocalByteSlices, numGlobalInts, numGlobalByteSlices, + extraPages, appArgs, - appAccounts, + accounts: appAccounts, foreignApps, foreignAssets, - undefined, - undefined, - undefined, - extraPages, - boxes - ); + boxes, + suggestedParams: sp, + }); return; case 'update': - this.txn = algosdk.makeApplicationUpdateTxn( + this.txn = algosdk.makeApplicationUpdateTxnFromObject({ sender, - sp, appIndex, - approvalProgramBytes, - clearProgramBytes, + approvalProgram: approvalProgramBytes, + clearProgram: clearProgramBytes, appArgs, - appAccounts, + accounts: appAccounts, foreignApps, foreignAssets, - undefined, - undefined, - undefined, - boxes - ); + boxes, + suggestedParams: sp, + }); return; case 'optin': - this.txn = algosdk.makeApplicationOptInTxn( + this.txn = algosdk.makeApplicationOptInTxnFromObject({ sender, - sp, appIndex, appArgs, - appAccounts, + accounts: appAccounts, foreignApps, foreignAssets, - undefined, - undefined, - undefined, - boxes - ); + boxes, + suggestedParams: sp, + }); return; case 'delete': - this.txn = algosdk.makeApplicationDeleteTxn( + this.txn = algosdk.makeApplicationDeleteTxnFromObject({ sender, - sp, appIndex, appArgs, - appAccounts, + accounts: appAccounts, foreignApps, foreignAssets, - undefined, - undefined, - undefined, - boxes - ); + boxes, + suggestedParams: sp, + }); return; case 'clear': - this.txn = algosdk.makeApplicationClearStateTxn( + this.txn = algosdk.makeApplicationClearStateTxnFromObject({ sender, - sp, appIndex, appArgs, - appAccounts, + accounts: appAccounts, foreignApps, foreignAssets, - boxes - ); + boxes, + suggestedParams: sp, + }); return; case 'closeout': - this.txn = algosdk.makeApplicationCloseOutTxn( + this.txn = algosdk.makeApplicationCloseOutTxnFromObject({ sender, - sp, appIndex, appArgs, - appAccounts, + accounts: appAccounts, foreignApps, foreignAssets, - undefined, - undefined, - undefined, - boxes - ); + boxes, + suggestedParams: sp, + }); return; default: throw Error( @@ -3273,18 +3651,18 @@ module.exports = function getSteps(options) { Then( 'the base64 encoded signed transaction should equal {string}', function (base64golden) { - const actualBase64 = Buffer.from(this.stx).toString('base64'); + const actualBase64 = algosdk.bytesToBase64(this.stx); 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 + // comparing the output of toEncodingData 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() + decoded.txn.toEncodingData(), + this.txn.toEncodingData() ); }); @@ -3330,27 +3708,27 @@ module.exports = function getSteps(options) { 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, + if (sp.firstValid === 0) sp.firstValid = 1; + const fundingTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender: this.accounts[0], + receiver: this.transientAccount.addr, amount: fundingAmount, suggestedParams: sp, - }; + }); const stxKmd = await this.kcl.signTransaction( this.handle, this.wallet_pswd, - fundingTxnArgs + fundingTxn ); const fundingResponse = await this.v2Client .sendRawTransaction(stxKmd) .do(); const info = await algosdk.waitForConfirmation( this.v2Client, - fundingResponse.txId, + fundingResponse.txid, 1 ); - assert.ok(info['confirmed-round'] > 0); + assert.ok(info.confirmedRound > 0); } ); @@ -3432,27 +3810,25 @@ module.exports = function getSteps(options) { 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, + if (sp.firstValid === 0) sp.firstValid = 1; + this.txn = algosdk.makeApplicationCallTxnFromObject({ + sender: this.transientAccount.addr, appIndex: this.currentApplicationIndex, - appOnComplete: operation, - appLocalInts: numLocalInts, - appLocalByteSlices: numLocalByteSlices, - appGlobalInts: numGlobalInts, - appGlobalByteSlices: numGlobalByteSlices, - appApprovalProgram: approvalProgramBytes, - appClearProgram: clearProgramBytes, + onComplete: operation, + numLocalInts, + numLocalByteSlices, + numGlobalInts, + numGlobalByteSlices, + extraPages, + approvalProgram: approvalProgramBytes, + clearProgram: clearProgramBytes, appArgs, - appAccounts, - appForeignApps: foreignApps, - appForeignAssets: foreignAssets, + accounts: appAccounts, + foreignApps, + foreignAssets, boxes, - extraPages, - }; - this.txn = new algosdk.Transaction(o); + suggestedParams: sp, + }); } ); @@ -3465,8 +3841,7 @@ module.exports = function getSteps(options) { } catch (err) { if (errorString !== '') { // error was expected. check that err.message includes expected string. - const errorContainsString = err.message.includes(errorString); - assert.deepStrictEqual(true, errorContainsString); + assert.ok(err.message.includes(errorString), err); } else { // unexpected error, rethrow. throw err; @@ -3478,11 +3853,11 @@ module.exports = function getSteps(options) { Given('I wait for the transaction to be confirmed.', async function () { const info = await algosdk.waitForConfirmation( this.v2Client, - this.appTxid.txId, + this.appTxid.txid, 1 ); - assert.ok(info['confirmed-round'] > 0); - this.lastTxnConfirmedRound = info['confirmed-round']; + assert.ok(info.confirmedRound > 0); + this.lastTxnConfirmedRound = info.confirmedRound; }); Given('I reset the array of application IDs to remember.', async function () { @@ -3491,9 +3866,9 @@ module.exports = function getSteps(options) { Given('I remember the new application ID.', async function () { const info = await this.v2Client - .pendingTransactionInformation(this.appTxid.txId) + .pendingTransactionInformation(this.appTxid.txid) .do(); - this.currentApplicationIndex = info['application-index']; + this.currentApplicationIndex = info.applicationIndex; if (!Object.prototype.hasOwnProperty.call(this, 'appIDs')) { this.appIDs = []; @@ -3509,18 +3884,24 @@ module.exports = function getSteps(options) { numByteSlices, numUints, applicationState, - stateKey, + stateKeyB64, 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 appTotalSchema = accountInfo.appsTotalSchema; + assert.strictEqual( + appTotalSchema.numByteSlice.toString(), + numByteSlices.toString() + ); + assert.strictEqual( + appTotalSchema.numUint.toString(), + numUints.toString() + ); const appCreated = appCreatedBoolAsString === 'true'; - const createdApps = accountInfo['created-apps']; + const { createdApps } = accountInfo; // 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++) { @@ -3535,12 +3916,14 @@ module.exports = function getSteps(options) { let foundApp = false; for (let i = 0; i < createdApps.length; i++) { foundApp = - foundApp || createdApps[i].id === this.currentApplicationIndex; + foundApp || + createdApps[i].id.toString() === + this.currentApplicationIndex.toString(); } assert.ok(foundApp); // If there is no key to check, we're done. - if (stateKey === '') { + if (stateKeyB64 === '') { return; } @@ -3548,20 +3931,24 @@ module.exports = function getSteps(options) { 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']; + for (let i = 0; i < accountInfo.appsLocalState.length; i++) { + const localState = accountInfo.appsLocalState[i]; + if ( + localState.id.toString() === this.currentApplicationIndex.toString() + ) { + keyValues = localState.keyValue; 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']; + for (let i = 0; i < accountInfo.createdApps.length; i++) { + const createdApp = accountInfo.createdApps[i]; + if ( + createdApp.id.toString() === this.currentApplicationIndex.toString() + ) { + keyValues = createdApp.params.globalState; counter += 1; } } @@ -3577,11 +3964,14 @@ module.exports = function getSteps(options) { for (let i = 0; i < keyValues.length; i++) { const keyValue = keyValues[i]; const foundKey = keyValue.key; - if (foundKey === stateKey) { + if (algosdk.bytesToBase64(foundKey) === stateKeyB64) { foundValueForKey = true; const foundValue = keyValue.value; if (foundValue.type === 1) { - assert.strictEqual(foundValue.bytes, stateValue); + assert.deepStrictEqual( + foundValue.bytes, + algosdk.base64ToBytes(stateValue) + ); } else if (foundValue.type === 0) { assert.strictEqual(foundValue.uint, stateValue); } @@ -3687,7 +4077,7 @@ module.exports = function getSteps(options) { function (expectedSelectorHex) { const actualSelector = this.method.getSelector(); const expectedSelector = makeUint8Array( - Buffer.from(expectedSelectorHex, 'hex') + algosdk.hexToBytes(expectedSelectorHex) ); assert.deepStrictEqual(actualSelector, expectedSelector); } @@ -3837,7 +4227,7 @@ module.exports = function getSteps(options) { const appID = this.appIDs[parseInt(b64Arg[1], 10)]; args.push(algosdk.encodeUint64(appID)); } else { - args.push(makeUint8Array(Buffer.from(b64Arg, 'base64'))); + args.push(makeUint8Array(algosdk.base64ToBytes(b64Arg))); } } this.encodedMethodArguments.push(...args); @@ -4109,7 +4499,7 @@ module.exports = function getSteps(options) { 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)); + const nonce = makeUint8Array(new TextEncoder().encode(this.nonce)); await addMethodCallToComposer.call( this, this.transientAccount.addr, @@ -4201,16 +4591,16 @@ module.exports = function getSteps(options) { function (commaSeparatedB64SignedTxns) { const expectedSignedTxns = commaSeparatedB64SignedTxns .split(',') - .map((b64SignedTxn) => Buffer.from(b64SignedTxn, 'base64')); + .map((b64SignedTxn) => algosdk.base64ToBytes(b64SignedTxn)); const actualSignedTxns = this.composerSignedTransactions.map( - (signedTxn) => Buffer.from(signedTxn) + (signedTxn) => signedTxn ); assert.deepStrictEqual( [...actualSignedTxns], [...expectedSignedTxns], `Got ${actualSignedTxns - .map((stxn) => stxn.toString('base64')) + .map((stxn) => algosdk.bytesToBase64(stxn)) .join(',')}` ); } @@ -4238,20 +4628,19 @@ module.exports = function getSteps(options) { for (let i = 0; i < methodResults.length; i++) { const actualResult = methodResults[i]; const { method } = actualResult; - const expectedReturnValue = Buffer.from( - b64ExpectedReturnValues[i], - 'base64' + const expectedReturnValue = algosdk.base64ToBytes( + b64ExpectedReturnValues[i] ); if (actualResult.decodeError) { throw actualResult.decodeError; } assert.deepStrictEqual( - Buffer.from(actualResult.rawReturnValue), + actualResult.rawReturnValue, expectedReturnValue, - `Actual return value for method at index ${i} does not match expected. Actual: ${Buffer.from( + `Actual return value for method at index ${i} does not match expected. Actual: ${algosdk.bytesToBase64( actualResult.rawReturnValue - ).toString('base64')}` + )}` ); const returnType = method.returns.type; @@ -4306,16 +4695,15 @@ module.exports = function getSteps(options) { function (resultIndex, methodArg) { // Return format for randomInt method const methodReturnType = algosdk.ABIType.from('(uint64,byte[17])'); - const actualResult = this.composerExecuteResponse.methodResults[ - resultIndex - ]; + 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 witness = algosdk.bytesToBigInt(Uint8Array.from(witnessHash)); const quotient = witness % BigInt(methodArg); assert.strictEqual(quotient, randomIntResult); } @@ -4326,20 +4714,19 @@ module.exports = function getSteps(options) { function (resultIndex, methodArg) { // Return format for randElement method const methodReturnType = algosdk.ABIType.from('(byte,byte[17])'); - const actualResult = this.composerExecuteResponse.methodResults[ - resultIndex - ]; + 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 witness = algosdk.bytesToBigInt(Uint8Array.from(witnessHash)); const quotient = witness % BigInt(methodArg.length); assert.strictEqual( methodArg[quotient], - Buffer.from(makeUint8Array([randomResult])).toString('utf-8') + new TextDecoder().decode(makeUint8Array([randomResult])) ); } ); @@ -4361,9 +4748,14 @@ module.exports = function getSteps(options) { 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); + let actualResult = + this.composerExecuteResponse.methodResults[index].txInfo; + actualResult = glom( + actualResult + .getEncodingSchema() + .prepareJSON(actualResult.toEncodingData()), + pathString + ); assert.strictEqual(expectedResult, actualResult.toString()); } @@ -4383,16 +4775,15 @@ module.exports = function getSteps(options) { 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); + actualResults = actualResults.innerTxns[itxnIndex]; } } + const thisGroupID = actualResults.txn.txn.group; + if (i === 0) { + groupID = thisGroupID; + } else { + assert.deepStrictEqual(groupID, thisGroupID); + } } } ); @@ -4408,7 +4799,7 @@ module.exports = function getSteps(options) { ); const actualResult = this.composerExecuteResponse.methodResults[index]; let spin = abiType.decode(actualResult.rawReturnValue)[0]; - spin = Buffer.from(spin).toString('utf-8'); + spin = new TextDecoder().decode(Uint8Array.from(spin)); assert.ok(spin.match(regexString)); } @@ -4417,17 +4808,22 @@ module.exports = function getSteps(options) { 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); + const drContents = await loadResourceAsJson(drrFile); + const drr = algosdk.modelsv2.DryrunResponse.fromEncodingData( + algosdk.modelsv2.DryrunResponse.encodingSchema.fromPreparedJSON( + drContents + ) + ); 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); + const traceString = algosdk.dryrunTxnResultAppTrace(this.txtrace); + const expectedString = new TextDecoder().decode( + await loadResource(expected) + ); + assert.strictEqual(traceString, expectedString); }); When( @@ -4497,10 +4893,10 @@ module.exports = function getSteps(options) { 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]; + const boxKey = splitAndProcessAppArgs(boxName)[0]; - let resp = null; + let resp = null; + try { if (fromClient === 'algod') { resp = await this.v2Client .getApplicationBoxByName(this.currentApplicationIndex, boxKey) @@ -4515,25 +4911,22 @@ module.exports = function getSteps(options) { } 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, + assert.ok( err.message.includes(errString), `expected ${errString} got ${err.message}` ); - } else { - throw err; + return; } + throw err; } + assert.ok(!errString, "expected error, didn't get one"); + + const actualName = resp.name; + const actualValue = resp.value; + assert.deepStrictEqual(boxKey, actualName); + assert.deepStrictEqual(algosdk.base64ToBytes(boxValue), actualValue); } ); @@ -4544,7 +4937,7 @@ module.exports = function getSteps(options) { const splitBoxB64Names = boxB64Names.split(':'); const boxNames = []; splitBoxB64Names.forEach((subArg) => { - boxNames.push(makeUint8Array(Buffer.from(subArg, 'base64'))); + boxNames.push(makeUint8Array(algosdk.base64ToBytes(subArg))); }); return boxNames; } @@ -4560,10 +4953,8 @@ module.exports = function getSteps(options) { .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)); + const actualBoxes = new Set(resp.boxes.map((b) => b.name)); + const expectedBoxes = new Set(boxes); assert.deepStrictEqual(expectedBoxes, actualBoxes); } ); @@ -4609,10 +5000,8 @@ module.exports = function getSteps(options) { } 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)); + const actualBoxes = new Set(resp.boxes.map((b) => b.name)); + const expectedBoxes = new Set(boxes); assert.deepStrictEqual(expectedBoxes, actualBoxes); } ); @@ -4620,11 +5009,12 @@ module.exports = function getSteps(options) { Then( 'I wait for indexer to catch up to the round where my most recent transaction was confirmed.', async function () { + // eslint-disable-next-line no-promise-executor-return const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const maxAttempts = 30; const roundToWaitFor = this.lastTxnConfirmedRound; - let indexerRound = 0; + let indexerRound = BigInt(0); let attempts = 0; for (;;) { @@ -4652,33 +5042,42 @@ module.exports = function getSteps(options) { ); Given('a source map json file {string}', async function (srcmap) { - const js = parseJSON(await loadResource(srcmap)); - this.sourcemap = new algosdk.SourceMap(js); + const js = await loadResourceAsJson(srcmap); + this.sourcemap = new algosdk.ProgramSourceMap(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('the source map contains pcs {string}', function (pcsString) { + const expectedPcs = makeArray( + ...pcsString.split(',').map((pc) => parseInt(pc, 10)) + ); + const actualPcs = makeArray(...this.sourcemap.getPcs()); + assert.deepStrictEqual(actualPcs, expectedPcs); + }); 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)); + 'the source map maps pc {int} to line {int} and column {int} of source {string}', + function (pc, expectedLine, expectedColumn, source) { + const actual = this.sourcemap.getLocationForPc(pc); + assert.ok(actual); + assert.strictEqual(actual.line, expectedLine); + assert.strictEqual(actual.column, expectedColumn); + assert.strictEqual(this.sourcemap.sources[actual.sourceIndex], source); } ); 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)); + 'the source map maps source {string} and line {int} to pc {int} at column {int}', + function (source, line, pc, expectedColumn) { + const sourceIndex = this.sourcemap.sources.indexOf(source); + assert.ok(sourceIndex >= 0); + const actualPcs = this.sourcemap.getPcsOnSourceLine(sourceIndex, line); + for (const actualPcInfo of actualPcs) { + if (actualPcInfo.pc === pc) { + assert.strictEqual(actualPcInfo.column, expectedColumn); + return; + } + } + throw new Error(`Could not find pc ${pc}`); } ); @@ -4690,15 +5089,17 @@ module.exports = function getSteps(options) { .compile(tealSrc) .sourcemap(true) .do(); - this.rawSourceMap = JSON.stringify(compiledResponse.sourcemap); + this.rawSourceMap = algosdk.encodeJSON(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()); + const expected = new TextDecoder() + .decode(await loadResource(expectedJsonPath)) + .trim(); + assert.deepStrictEqual(this.rawSourceMap, expected); } ); @@ -4707,30 +5108,29 @@ module.exports = function getSteps(options) { 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') + const expectedSource = new TextDecoder().decode( + await loadResource(sourceFilename) ); + + assert.deepStrictEqual(resp.result.toString('UTF-8'), expectedSource); } ); When( 'we make a GetLightBlockHeaderProof call for round {int}', async function (int) { - await this.v2Client.getLightBlockHeaderProof(int).do(); + await doOrDoRaw(this.v2Client.getLightBlockHeaderProof(int)); } ); When('we make a GetStateProof call for round {int}', async function (int) { - await this.v2Client.getStateProof(int).do(); + await doOrDoRaw(this.v2Client.getStateProof(int)); }); When( 'we make a Lookup Block Hash call against round {int}', async function (int) { - await this.v2Client.getBlockHash(int).do(); + await doOrDoRaw(this.v2Client.getBlockHash(int)); } ); @@ -4738,7 +5138,7 @@ module.exports = function getSteps(options) { 'a base64 encoded program bytes for heuristic sanity check {string}', async function (programByteStr) { this.seeminglyProgram = new Uint8Array( - Buffer.from(programByteStr, 'base64') + algosdk.base64ToBytes(programByteStr) ); } ); @@ -4826,8 +5226,8 @@ module.exports = function getSteps(options) { const stringPath = failAt.split(','); const failPath = stringPath.map((n) => parseInt(n, 10)); - const failedMessage = this.simulateResponse.txnGroups[groupNum] - .failureMessage; + const failedMessage = + this.simulateResponse.txnGroups[groupNum].failureMessage; assert.ok( failedMessage.includes(errorMsg), `Error message: "${failedMessage}" does not contain "${errorMsg}"` @@ -4894,14 +5294,13 @@ module.exports = function getSteps(options) { const optionList = execTraceOptions.split(','); assert.ok(this.simulateRequest); - this.simulateRequest.execTraceConfig = new algosdk.modelsv2.SimulateTraceConfig( - { + this.simulateRequest.execTraceConfig = + new algosdk.modelsv2.SimulateTraceConfig({ enable: true, scratchChange: optionList.includes('scratch'), stackChange: optionList.includes('stack'), stateChange: optionList.includes('state'), - } - ); + }); } ); @@ -4920,9 +5319,9 @@ module.exports = function getSteps(options) { if (expectedValue.length === 0) { assert.equal(actualAvmValue.bytes, undefined); } else { - assert.deepStrictEqual( + assert.deepEqual( actualAvmValue.bytes, - makeUint8Array(Buffer.from(expectedValue, 'base64')) + algosdk.base64ToBytes(expectedValue) ); } } else { @@ -4948,9 +5347,9 @@ module.exports = function getSteps(options) { .map(Number); assert.ok(txnGroupPathSplit.length > 0); - let traces = this.simulateResponse.txnGroups[0].txnResults[ - txnGroupPathSplit[0] - ].execTrace; + let traces = + this.simulateResponse.txnGroups[0].txnResults[txnGroupPathSplit[0]] + .execTrace; assert.ok(traces); for (let i = 1; i < txnGroupPathSplit.length; i++) { @@ -5070,7 +5469,9 @@ module.exports = function getSteps(options) { assert.ok(initialAppState.appLocals); assert.strictEqual(initialAppState.appLocals.length, 1); assert.ok(initialAppState.appLocals[0].account); - algosdk.decodeAddress(initialAppState.appLocals[0].account); + assert.ok( + initialAppState.appLocals[0].account instanceof algosdk.Address + ); assert.ok(initialAppState.appLocals[0].kvs); kvs = initialAppState.appLocals[0].kvs; break; @@ -5091,11 +5492,11 @@ module.exports = function getSteps(options) { } assert.ok(kvs.length > 0); - const binaryKey = Buffer.from(keyStr); + const binaryKey = new TextEncoder().encode(keyStr); let actualValue = null; for (const kv of kvs) { - if (binaryKey.equals(Buffer.from(kv.key))) { + if (bytesEqual(binaryKey, kv.key)) { actualValue = kv.value; break; } @@ -5122,9 +5523,9 @@ module.exports = function getSteps(options) { .map(Number); assert.ok(txnGroupPathSplit.length > 0); - let traces = this.simulateResponse.txnGroups[0].txnResults[ - txnGroupPathSplit[0] - ].execTrace; + let traces = + this.simulateResponse.txnGroups[0].txnResults[txnGroupPathSplit[0]] + .execTrace; assert.ok(traces); for (let i = 1; i < txnGroupPathSplit.length; i++) { @@ -5163,7 +5564,7 @@ module.exports = function getSteps(options) { } else if (stateType === 'local') { assert.strictEqual(stateChange.appStateType, 'l'); assert.ok(stateChange.account); - algosdk.decodeAddress(stateChange.account); + assert.ok(stateChange.account instanceof algosdk.Address); } else if (stateType === 'box') { assert.strictEqual(stateChange.appStateType, 'b'); assert.ok(!stateChange.account); @@ -5175,7 +5576,7 @@ module.exports = function getSteps(options) { assert.deepStrictEqual( stateChange.key, - makeUint8Array(Buffer.from(stateName)) + makeUint8Array(new TextEncoder().encode(stateName)) ); assert.ok(stateChange.newValue); avmValueCheck(newValue, stateChange.newValue); @@ -5191,9 +5592,9 @@ module.exports = function getSteps(options) { .map(Number); assert.ok(txnGroupPathSplit.length > 0); - let traces = this.simulateResponse.txnGroups[0].txnResults[ - txnGroupPathSplit[0] - ].execTrace; + let traces = + this.simulateResponse.txnGroups[0].txnResults[txnGroupPathSplit[0]] + .execTrace; assert.ok(traces); for (let i = 1; i < txnGroupPathSplit.length; i++) { @@ -5212,7 +5613,7 @@ module.exports = function getSteps(options) { } assert.deepStrictEqual( hash, - makeUint8Array(Buffer.from(b64ProgHash, 'base64')) + makeUint8Array(algosdk.base64ToBytes(b64ProgHash)) ); } ); @@ -5281,7 +5682,7 @@ module.exports = function getSteps(options) { When( 'we make a GetBlockTxids call against block number {int}', async function (round) { - await this.v2Client.getBlockTxids(round).do(); + await this.v2Client.getBlockTxids(round).doRaw(); } ); diff --git a/tests/cucumber/unit.tags b/tests/cucumber/unit.tags index 482d9f4b2..48232f779 100644 --- a/tests/cucumber/unit.tags +++ b/tests/cucumber/unit.tags @@ -23,15 +23,14 @@ @unit.responses.unlimited_assets @unit.responses.blocksummary @unit.responses.minbalance -@unit.responses.statedelta.json +@unit.responses.statedelta @unit.responses.sync @unit.responses.timestamp @unit.responses.txid.json -@unit.responses.txngroupdeltas.json -@unit.sourcemap +@unit.responses.txngroupdeltas +@unit.sourcemapv2 @unit.stateproof.paths @unit.stateproof.responses -@unit.stateproof.responses.msgp @unit.sync @unit.tealsign @unit.timestamp diff --git a/tests/mocha.js b/tests/mocha.js index d2202ea72..0888994ff 100644 --- a/tests/mocha.js +++ b/tests/mocha.js @@ -4,11 +4,40 @@ const Mocha = require('mocha'); const webpack = require('webpack'); const fs = require('fs'); const path = require('path'); +const express = require('express'); const webpackConfig = require('../webpack.config'); const browser = process.env.TEST_BROWSER; +const resourceServerPort = 8080; +let resourceServer; + +const resourcePath = path.dirname(__dirname); + +async function startResourceServer() { + const app = express(); + + app.use('/neverreturn/*', (req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('This request will never return'); + }); + + app.use(express.static(resourcePath)); + await new Promise((resolve) => { + resourceServer = app.listen(resourceServerPort, undefined, resolve); + }); + console.log( + `Resource server started on port ${resourceServerPort} serving ${resourcePath}` + ); +} + +function stopResourceServer() { + if (resourceServer) { + resourceServer.close(); + } +} + async function testRunner() { console.log('TEST_BROWSER is', browser); @@ -20,19 +49,24 @@ async function testRunner() { ) .map((file) => path.join(__dirname, file)); + await startResourceServer(); + 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.mode = 'development'; webpackTestConfig.entry = testFiles; webpackTestConfig.output = { filename: path.basename(bundleLocation), path: path.dirname(bundleLocation), }; + webpackTestConfig.optimization = { + minimize: false, + }; webpack(webpackTestConfig, (err, stats) => { if (err || stats.hasErrors()) { @@ -74,7 +108,9 @@ async function testRunner() { .forBrowser(browser) .build(); - await driver.get(`file://${browserEntry}`); + await driver.get( + `http://localhost:${resourceServerPort}/tests/browser/index.html` + ); const title = await driver.getTitle(); @@ -121,13 +157,20 @@ async function testRunner() { } else { console.log('Testing in Node'); - const mocha = new Mocha(); + const mocha = new Mocha({ + timeout: process.env.MOCHA_TIMEOUT, + }); testFiles.forEach((file) => mocha.addFile(file)); - mocha.run((failures) => { - process.exitCode = failures ? 1 : 0; + await new Promise((resolve) => { + mocha.run((failures) => { + process.exitCode = failures ? 1 : 0; + resolve(); + }); }); } + + stopResourceServer(); } testRunner().catch((err) => { diff --git a/tests/resources/groupdelta-betanet_23963123_2.msgp b/tests/resources/groupdelta-betanet_23963123_2.msgp new file mode 100644 index 0000000000000000000000000000000000000000..ddb18cebe126c0b38d770b396bba3cfa51be439d GIT binary patch literal 8475 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 Nch3L>ma$;jkM Date: Tue, 20 Aug 2024 13:56:03 -0400 Subject: [PATCH 2/7] Don't delete dist folder when creating release PR (#890) --- .github/workflows/create-release-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml index 1330c8f42..15e98ec0d 100644 --- a/.github/workflows/create-release-pr.yml +++ b/.github/workflows/create-release-pr.yml @@ -115,7 +115,7 @@ jobs: env: RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} run: | - rm -rf dist node_modules + rm -rf node_modules npm ci npm run build NEW_HASH=$(cat dist/browser/algosdk.min.js | openssl dgst -sha384 -binary | openssl base64 -A) From a0366b97b0d2fc9b19c06d2c1777b00ae6488e19 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Fri, 23 Aug 2024 16:44:22 -0400 Subject: [PATCH 3/7] REST API: Allow bigints for client args (#893) --- examples/app.ts | 5 +- examples/asa.ts | 5 +- examples/atc.ts | 5 +- examples/block_fetcher/index.ts | 47 +++++++------------ examples/utils.ts | 7 ++- src/client/urlTokenBaseHTTPClient.ts | 2 +- .../v2/algod/accountApplicationInformation.ts | 4 +- .../v2/algod/accountAssetInformation.ts | 4 +- src/client/v2/algod/algod.ts | 37 ++++++++------- src/client/v2/algod/block.ts | 6 +-- .../v2/algod/getApplicationBoxByName.ts | 9 ++-- src/client/v2/algod/getApplicationBoxes.ts | 8 ++-- src/client/v2/algod/getApplicationByID.ts | 8 ++-- src/client/v2/algod/getAssetByID.ts | 8 ++-- src/client/v2/algod/getBlockHash.ts | 6 +-- src/client/v2/algod/getBlockTxids.ts | 8 ++-- src/client/v2/algod/getLedgerStateDelta.ts | 8 ++-- ...ansactionGroupLedgerStateDeltasForRound.ts | 8 ++-- src/client/v2/algod/getTransactionProof.ts | 5 +- src/client/v2/algod/lightBlockHeaderProof.ts | 8 ++-- .../v2/algod/setBlockOffsetTimestamp.ts | 8 ++-- src/client/v2/algod/setSyncRound.ts | 8 ++-- src/client/v2/algod/stateproof.ts | 8 ++-- src/client/v2/algod/statusAfterBlock.ts | 9 ++-- src/client/v2/indexer/indexer.ts | 16 +++---- .../v2/indexer/lookupAccountAppLocalStates.ts | 4 +- src/client/v2/indexer/lookupAccountAssets.ts | 4 +- src/client/v2/indexer/lookupAccountByID.ts | 2 +- .../lookupAccountCreatedApplications.ts | 4 +- .../v2/indexer/lookupAccountCreatedAssets.ts | 4 +- .../v2/indexer/lookupAccountTransactions.ts | 12 ++--- .../lookupApplicationBoxByIDandName.ts | 9 ++-- .../v2/indexer/lookupApplicationLogs.ts | 12 ++--- src/client/v2/indexer/lookupApplications.ts | 8 ++-- src/client/v2/indexer/lookupAssetBalances.ts | 12 ++--- src/client/v2/indexer/lookupAssetByID.ts | 8 ++-- .../v2/indexer/lookupAssetTransactions.ts | 18 +++---- src/client/v2/indexer/lookupBlock.ts | 8 ++-- src/client/v2/indexer/searchAccounts.ts | 10 ++-- .../v2/indexer/searchForApplicationBoxes.ts | 8 ++-- .../v2/indexer/searchForApplications.ts | 2 +- src/client/v2/indexer/searchForAssets.ts | 2 +- .../v2/indexer/searchForTransactions.ts | 14 +++--- src/composer.ts | 4 +- src/wait.ts | 7 +-- tests/cucumber/steps/steps.js | 8 ++-- 46 files changed, 205 insertions(+), 202 deletions(-) diff --git a/examples/app.ts b/examples/app.ts index 8d690d9a3..ee77ef019 100644 --- a/examples/app.ts +++ b/examples/app.ts @@ -70,7 +70,10 @@ async function main() { 3 ); // Grab app id from confirmed transaction result - const appId = Number(result.applicationIndex); + const appId = result.applicationIndex; + if (!appId) { + throw new Error('App not created'); + } console.log(`Created app with index: ${appId}`); // example: APP_CREATE diff --git a/examples/asa.ts b/examples/asa.ts index adc337530..9c65725f1 100644 --- a/examples/asa.ts +++ b/examples/asa.ts @@ -37,7 +37,10 @@ async function main() { await algodClient.sendRawTransaction(signedTxn).do(); const result = await algosdk.waitForConfirmation(algodClient, txn.txID(), 3); - const assetIndex = Number(result.assetIndex); + const { assetIndex } = result; + if (!assetIndex) { + throw new Error('Asset not created'); + } console.log(`Asset ID created: ${assetIndex}`); // example: ASSET_CREATE diff --git a/examples/atc.ts b/examples/atc.ts index 4a7e07fd5..7f5e78b3c 100644 --- a/examples/atc.ts +++ b/examples/atc.ts @@ -45,7 +45,10 @@ async function main() { createTxn.txID(), 3 ); - const appIndex = Number(response.applicationIndex); + const appIndex = response.applicationIndex; + if (!appIndex) { + throw new Error('Application not created'); + } // example: ATC_CREATE const atc = new algosdk.AtomicTransactionComposer(); diff --git a/examples/block_fetcher/index.ts b/examples/block_fetcher/index.ts index 786d28a99..ca4ff059d 100644 --- a/examples/block_fetcher/index.ts +++ b/examples/block_fetcher/index.ts @@ -11,57 +11,42 @@ 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 = Number(status.lastRound); + let { lastRound } = status; console.log(`Round: ${lastRound}`); // Fetch block const round = await client.block(lastRound).do(); const { block } = round; - const { txns } = block; + const txns = block.payset; // 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]; + for (const stxnInBlock of txns) { + const { txn } = stxnInBlock.signedTxn.signedTxn; // Skip StateProofs - if (txn.type === 'stpf') continue; - - // Remove nulls (mainly where an appl txn contains a null app arg) - removeNulls(txn); + if (txn.type === algosdk.TransactionType.stpf) continue; // Use Genesis Hash and Genesis ID from the block - const { gh } = block; - let { gen } = block; + let gh: Uint8Array | undefined = block.header.genesisHash; + let gen: string | undefined = block.header.genesisID; - // Unset gen if `hgi` isn't set - if (!tx.hgi) gen = null; + // Unset gh if hasGenesisID isn't set + if (!stxnInBlock.hasGenesisID) gh = undefined; + // Unset gen if hasGenesisID isn't set + if (!stxnInBlock.hasGenesisID) gen = undefined; // Construct Transaction - const transaction = algosdk.Transaction.from_obj_for_encoding({ - ...txn, - gh, - gen, - }); + const encodingData = txn.toEncodingData(); + encodingData.set('gh', gh); + encodingData.set('gen', gen); + const transaction = algosdk.Transaction.fromEncodingData(encodingData); const txid = transaction.txID(); // If set to true, validate the TxID exists against the node @@ -80,6 +65,6 @@ function removeNulls(obj) { // Wait for next round status = await client.statusAfterBlock(lastRound).do(); - lastRound = status['last-round']; + lastRound = status.lastRound; } })(); diff --git a/examples/utils.ts b/examples/utils.ts index f51d8c559..c75674df9 100644 --- a/examples/utils.ts +++ b/examples/utils.ts @@ -130,7 +130,7 @@ export async function getLocalAccounts(): Promise { export async function deployCalculatorApp( algodClient: algosdk.Algodv2, creator: SandboxAccount -): Promise { +): Promise { const approvalProgram = fs.readFileSync( path.join(__dirname, '/calculator/approval.teal'), 'utf8' @@ -164,6 +164,9 @@ export async function deployCalculatorApp( appCreateTxn.txID(), 3 ); - const appId = Number(result.applicationIndex); + const appId = result.applicationIndex; + if (!appId) { + throw new Error('Application not created'); + } return appId; } diff --git a/src/client/urlTokenBaseHTTPClient.ts b/src/client/urlTokenBaseHTTPClient.ts index b172c2d45..9c0af8a53 100644 --- a/src/client/urlTokenBaseHTTPClient.ts +++ b/src/client/urlTokenBaseHTTPClient.ts @@ -95,7 +95,7 @@ export class URLTokenBaseHTTPClient implements BaseHTTPClient { const address = new URL(fixedRelativePath, this.baseURL); if (query) { for (const [key, value] of Object.entries(query)) { - address.searchParams.set(key, value); + address.searchParams.set(key, value.toString()); } } return address.toString(); diff --git a/src/client/v2/algod/accountApplicationInformation.ts b/src/client/v2/algod/accountApplicationInformation.ts index 6b26c3df3..b1dd01cd5 100644 --- a/src/client/v2/algod/accountApplicationInformation.ts +++ b/src/client/v2/algod/accountApplicationInformation.ts @@ -6,14 +6,16 @@ import { Address } from '../../../encoding/address.js'; export default class AccountApplicationInformation extends JSONRequest { private account: string; + private applicationID: bigint; constructor( c: HTTPClient, account: string | Address, - private applicationID: number + applicationID: number | bigint ) { super(c); this.account = account.toString(); + this.applicationID = BigInt(applicationID); } path() { diff --git a/src/client/v2/algod/accountAssetInformation.ts b/src/client/v2/algod/accountAssetInformation.ts index eef272257..43d28005d 100644 --- a/src/client/v2/algod/accountAssetInformation.ts +++ b/src/client/v2/algod/accountAssetInformation.ts @@ -6,14 +6,16 @@ import { Address } from '../../../encoding/address.js'; export default class AccountAssetInformation extends JSONRequest { private account: string; + private assetID: bigint; constructor( c: HTTPClient, account: string | Address, - private assetID: number + assetID: number | bigint ) { super(c); this.account = account.toString(); + this.assetID = BigInt(assetID); } path() { diff --git a/src/client/v2/algod/algod.ts b/src/client/v2/algod/algod.ts index 2a373ba74..103a8a8fc 100644 --- a/src/client/v2/algod/algod.ts +++ b/src/client/v2/algod/algod.ts @@ -96,7 +96,7 @@ export class AlgodClient extends ServiceClient { * * #### Example * ```typescript - * const health = await algodClient.healthCheck().do(); + * await algodClient.healthCheck().do(); * ``` * * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/#get-health) @@ -126,7 +126,7 @@ export class AlgodClient extends ServiceClient { * * #### Example * ```typescript - * const { txId } = await algodClient.sendRawTransaction(signedTxns).do(); + * const { txid } = await algodClient.sendRawTransaction(signedTxns).do(); * const result = await waitForConfirmation(algodClient, txid, 3); * ``` * @@ -173,7 +173,7 @@ export class AlgodClient extends ServiceClient { * @param index - The asset ID to look up. * @category GET */ - accountAssetInformation(account: string | Address, index: number) { + accountAssetInformation(account: string | Address, index: number | bigint) { return new AccountAssetInformation(this.c, account, index); } @@ -192,7 +192,10 @@ export class AlgodClient extends ServiceClient { * @param index - The application ID to look up. * @category GET */ - accountApplicationInformation(account: string | Address, index: number) { + accountApplicationInformation( + account: string | Address, + index: number | bigint + ) { return new AccountApplicationInformation(this.c, account, index); } @@ -209,7 +212,7 @@ export class AlgodClient extends ServiceClient { * @param roundNumber - The round number of the block to get. * @category GET */ - block(roundNumber: number) { + block(roundNumber: number | bigint) { return new Block(this.c, roundNumber); } @@ -226,7 +229,7 @@ export class AlgodClient extends ServiceClient { * @param roundNumber - The round number of the block to get. * @category GET */ - getBlockHash(roundNumber: number) { + getBlockHash(roundNumber: number | bigint) { return new GetBlockHash(this.c, roundNumber); } @@ -243,7 +246,7 @@ export class AlgodClient extends ServiceClient { * @param roundNumber - The round number of the block to get. * @category GET */ - getBlockTxids(roundNumber: number) { + getBlockTxids(roundNumber: number | bigint) { return new GetBlockTxids(this.c, roundNumber); } @@ -355,7 +358,7 @@ export class AlgodClient extends ServiceClient { * @param round - The number of the round to wait for. * @category GET */ - statusAfterBlock(round: number) { + statusAfterBlock(round: number | bigint) { return new StatusAfterBlock(this.c, round); } @@ -504,7 +507,7 @@ export class AlgodClient extends ServiceClient { * @param index - The application ID to look up. * @category GET */ - getApplicationBoxByName(index: number, boxName: Uint8Array) { + getApplicationBoxByName(index: number | bigint, boxName: Uint8Array) { return new GetApplicationBoxByName(this.c, index, boxName); } @@ -522,7 +525,7 @@ export class AlgodClient extends ServiceClient { * @param index - The application ID to look up. * @category GET */ - getApplicationBoxes(index: number) { + getApplicationBoxes(index: number | bigint) { return new GetApplicationBoxes(this.c, index); } @@ -556,7 +559,7 @@ export class AlgodClient extends ServiceClient { * @param txID - The transaction ID for which to generate a proof. * @category GET */ - getTransactionProof(round: number, txID: string) { + getTransactionProof(round: number | bigint, txID: string) { return new GetTransactionProof(this.c, round, txID); } @@ -572,7 +575,7 @@ export class AlgodClient extends ServiceClient { * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/v2#get-v2blocksroundlightheaderproof) * @param round */ - getLightBlockHeaderProof(round: number) { + getLightBlockHeaderProof(round: number | bigint) { return new LightBlockHeaderProof(this.c, round); } @@ -588,7 +591,7 @@ export class AlgodClient extends ServiceClient { * [Response data schema details](https://developer.algorand.org/docs/rest-apis/algod/v2#get-v2stateproofsround) * @param round */ - getStateProof(round: number) { + getStateProof(round: number | bigint) { return new StateProof(this.c, round); } @@ -678,7 +681,7 @@ export class AlgodClient extends ServiceClient { * @param offset * @category POST */ - setBlockOffsetTimestamp(offset: number) { + setBlockOffsetTimestamp(offset: number | bigint) { return new SetBlockOffsetTimestamp(this.c, offset); } @@ -710,7 +713,7 @@ export class AlgodClient extends ServiceClient { * @param round * @category POST */ - setSyncRound(round: number) { + setSyncRound(round: number | bigint) { return new SetSyncRound(this.c, round); } @@ -789,7 +792,7 @@ export class AlgodClient extends ServiceClient { * @param round the round number to be searched for * @category GET */ - getLedgerStateDelta(round: number) { + getLedgerStateDelta(round: number | bigint) { return new GetLedgerStateDelta(this.c, round); } @@ -806,7 +809,7 @@ export class AlgodClient extends ServiceClient { * @param round the round number to be searched for * @category GET */ - getTransactionGroupLedgerStateDeltasForRound(round: number) { + getTransactionGroupLedgerStateDeltasForRound(round: number | bigint) { return new GetTransactionGroupLedgerStateDeltasForRound(this.c, round); } } diff --git a/src/client/v2/algod/block.ts b/src/client/v2/algod/block.ts index 52ac5bfbb..f2f02f64c 100644 --- a/src/client/v2/algod/block.ts +++ b/src/client/v2/algod/block.ts @@ -7,11 +7,11 @@ import { BlockResponse } from './models/types.js'; * block gets the block info for the given round. this call may block */ export default class Block extends JSONRequest { - private round: number; + private round: bigint; - constructor(c: HTTPClient, roundNumber: number) { + constructor(c: HTTPClient, roundNumber: number | bigint) { super(c); - this.round = roundNumber; + this.round = BigInt(roundNumber); this.query = { format: 'msgpack' }; } diff --git a/src/client/v2/algod/getApplicationBoxByName.ts b/src/client/v2/algod/getApplicationBoxByName.ts index 415f88709..bb87a6b94 100644 --- a/src/client/v2/algod/getApplicationBoxByName.ts +++ b/src/client/v2/algod/getApplicationBoxByName.ts @@ -20,12 +20,11 @@ import { Box } from './models/types.js'; * @category GET */ export default class GetApplicationBoxByName extends JSONRequest { - constructor( - c: HTTPClient, - private index: number, - name: Uint8Array - ) { + private index: bigint; + + constructor(c: HTTPClient, index: number | bigint, name: Uint8Array) { super(c); + this.index = BigInt(index); // Encode name in base64 format and append the encoding prefix. const encodedName = bytesToBase64(name); this.query.name = encodeURI(`b64:${encodedName}`); diff --git a/src/client/v2/algod/getApplicationBoxes.ts b/src/client/v2/algod/getApplicationBoxes.ts index 96b9cbd96..a566d4827 100644 --- a/src/client/v2/algod/getApplicationBoxes.ts +++ b/src/client/v2/algod/getApplicationBoxes.ts @@ -18,11 +18,11 @@ import { BoxesResponse } from './models/types.js'; * @category GET */ export default class GetApplicationBoxes extends JSONRequest { - constructor( - c: HTTPClient, - private index: number - ) { + private index: bigint; + + constructor(c: HTTPClient, index: number | bigint) { super(c); + this.index = BigInt(index); this.query.max = 0; } diff --git a/src/client/v2/algod/getApplicationByID.ts b/src/client/v2/algod/getApplicationByID.ts index 60be5ea7c..b82ea89d4 100644 --- a/src/client/v2/algod/getApplicationByID.ts +++ b/src/client/v2/algod/getApplicationByID.ts @@ -4,11 +4,11 @@ import { decodeJSON } from '../../../encoding/encoding.js'; import { Application } from './models/types.js'; export default class GetApplicationByID extends JSONRequest { - constructor( - c: HTTPClient, - private index: number | bigint - ) { + private index: bigint; + + constructor(c: HTTPClient, index: number | bigint) { super(c); + this.index = BigInt(index); } path() { diff --git a/src/client/v2/algod/getAssetByID.ts b/src/client/v2/algod/getAssetByID.ts index 9ad064eaa..e48cfd6a0 100644 --- a/src/client/v2/algod/getAssetByID.ts +++ b/src/client/v2/algod/getAssetByID.ts @@ -4,11 +4,11 @@ import { decodeJSON } from '../../../encoding/encoding.js'; import { Asset } from './models/types.js'; export default class GetAssetByID extends JSONRequest { - constructor( - c: HTTPClient, - private index: number | bigint - ) { + private index: bigint; + + constructor(c: HTTPClient, index: number | bigint) { super(c); + this.index = BigInt(index); } path() { diff --git a/src/client/v2/algod/getBlockHash.ts b/src/client/v2/algod/getBlockHash.ts index 57991a430..9d3e9bf27 100644 --- a/src/client/v2/algod/getBlockHash.ts +++ b/src/client/v2/algod/getBlockHash.ts @@ -4,11 +4,11 @@ import { decodeJSON } from '../../../encoding/encoding.js'; import { BlockHashResponse } from './models/types.js'; export default class GetBlockHash extends JSONRequest { - round: number | bigint; + private round: bigint; - constructor(c: HTTPClient, roundNumber: number) { + constructor(c: HTTPClient, roundNumber: number | bigint) { super(c); - this.round = roundNumber; + this.round = BigInt(roundNumber); } path() { diff --git a/src/client/v2/algod/getBlockTxids.ts b/src/client/v2/algod/getBlockTxids.ts index 479721c71..2b6168174 100644 --- a/src/client/v2/algod/getBlockTxids.ts +++ b/src/client/v2/algod/getBlockTxids.ts @@ -4,13 +4,11 @@ import { decodeJSON } from '../../../encoding/encoding.js'; import { BlockTxidsResponse } from './models/types.js'; export default class GetBlockTxids extends JSONRequest { - round: number; + private round: bigint; - constructor(c: HTTPClient, roundNumber: number) { + constructor(c: HTTPClient, roundNumber: number | bigint) { super(c); - if (!Number.isInteger(roundNumber)) - throw Error('roundNumber should be an integer'); - this.round = roundNumber; + this.round = BigInt(roundNumber); } path() { diff --git a/src/client/v2/algod/getLedgerStateDelta.ts b/src/client/v2/algod/getLedgerStateDelta.ts index 131154c6b..bc151a090 100644 --- a/src/client/v2/algod/getLedgerStateDelta.ts +++ b/src/client/v2/algod/getLedgerStateDelta.ts @@ -4,11 +4,11 @@ import { decodeMsgpack } from '../../../encoding/encoding.js'; import { LedgerStateDelta } from '../../../types/statedelta.js'; export default class GetLedgerStateDelta extends JSONRequest { - constructor( - c: HTTPClient, - private round: number - ) { + private round: bigint; + + constructor(c: HTTPClient, round: number | bigint) { super(c); + this.round = BigInt(round); this.query = { format: 'msgpack' }; } diff --git a/src/client/v2/algod/getTransactionGroupLedgerStateDeltasForRound.ts b/src/client/v2/algod/getTransactionGroupLedgerStateDeltasForRound.ts index 3e17b6ef0..ac48d0341 100644 --- a/src/client/v2/algod/getTransactionGroupLedgerStateDeltasForRound.ts +++ b/src/client/v2/algod/getTransactionGroupLedgerStateDeltasForRound.ts @@ -4,11 +4,11 @@ import { HTTPClient, HTTPClientResponse } from '../../client.js'; import { decodeMsgpack } from '../../../encoding/encoding.js'; export default class GetTransactionGroupLedgerStateDeltasForRound extends JSONRequest { - constructor( - c: HTTPClient, - private round: number - ) { + private round: bigint; + + constructor(c: HTTPClient, round: number | bigint) { super(c); + this.round = BigInt(round); this.query = { format: 'msgpack' }; } diff --git a/src/client/v2/algod/getTransactionProof.ts b/src/client/v2/algod/getTransactionProof.ts index ac3d5c524..371a7871b 100644 --- a/src/client/v2/algod/getTransactionProof.ts +++ b/src/client/v2/algod/getTransactionProof.ts @@ -4,12 +4,15 @@ import { decodeJSON } from '../../../encoding/encoding.js'; import { TransactionProofResponse } from './models/types.js'; export default class GetTransactionProof extends JSONRequest { + private round: bigint; + constructor( c: HTTPClient, - private round: number, + round: number | bigint, private txID: string ) { super(c); + this.round = BigInt(round); } path() { diff --git a/src/client/v2/algod/lightBlockHeaderProof.ts b/src/client/v2/algod/lightBlockHeaderProof.ts index 37e6720f3..66fba7ca2 100644 --- a/src/client/v2/algod/lightBlockHeaderProof.ts +++ b/src/client/v2/algod/lightBlockHeaderProof.ts @@ -4,11 +4,11 @@ import { decodeJSON } from '../../../encoding/encoding.js'; import { LightBlockHeaderProof as LBHP } from './models/types.js'; export default class LightBlockHeaderProof extends JSONRequest { - constructor( - c: HTTPClient, - private round: number - ) { + private round: bigint; + + constructor(c: HTTPClient, round: number | bigint) { super(c); + this.round = BigInt(round); } path() { diff --git a/src/client/v2/algod/setBlockOffsetTimestamp.ts b/src/client/v2/algod/setBlockOffsetTimestamp.ts index 0acd09330..4e4c8ace8 100644 --- a/src/client/v2/algod/setBlockOffsetTimestamp.ts +++ b/src/client/v2/algod/setBlockOffsetTimestamp.ts @@ -2,11 +2,11 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient, HTTPClientResponse } from '../../client.js'; export default class SetBlockOffsetTimestamp extends JSONRequest { - constructor( - c: HTTPClient, - private offset: number - ) { + private offset: bigint; + + constructor(c: HTTPClient, offset: number | bigint) { super(c); + this.offset = BigInt(offset); } path() { diff --git a/src/client/v2/algod/setSyncRound.ts b/src/client/v2/algod/setSyncRound.ts index b150681d9..65074ad64 100644 --- a/src/client/v2/algod/setSyncRound.ts +++ b/src/client/v2/algod/setSyncRound.ts @@ -2,11 +2,11 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient, HTTPClientResponse } from '../../client.js'; export default class SetSyncRound extends JSONRequest { - constructor( - c: HTTPClient, - private round: number - ) { + private round: bigint; + + constructor(c: HTTPClient, round: number | bigint) { super(c); + this.round = BigInt(round); } path() { diff --git a/src/client/v2/algod/stateproof.ts b/src/client/v2/algod/stateproof.ts index c61f692e9..7468e7c2a 100644 --- a/src/client/v2/algod/stateproof.ts +++ b/src/client/v2/algod/stateproof.ts @@ -4,11 +4,11 @@ import { decodeJSON } from '../../../encoding/encoding.js'; import { StateProof as SP } from './models/types.js'; export default class StateProof extends JSONRequest { - constructor( - c: HTTPClient, - private round: number - ) { + private round: bigint; + + constructor(c: HTTPClient, round: number | bigint) { super(c); + this.round = BigInt(round); } path() { diff --git a/src/client/v2/algod/statusAfterBlock.ts b/src/client/v2/algod/statusAfterBlock.ts index 43cba1069..f426e4a2c 100644 --- a/src/client/v2/algod/statusAfterBlock.ts +++ b/src/client/v2/algod/statusAfterBlock.ts @@ -4,12 +4,11 @@ import { decodeJSON } from '../../../encoding/encoding.js'; import { NodeStatusResponse } from './models/types.js'; export default class StatusAfterBlock extends JSONRequest { - constructor( - c: HTTPClient, - private round: number - ) { + private round: bigint; + + constructor(c: HTTPClient, round: number | bigint) { super(c); - if (!Number.isInteger(round)) throw Error('round should be an integer'); + this.round = BigInt(round); } path() { diff --git a/src/client/v2/indexer/indexer.ts b/src/client/v2/indexer/indexer.ts index f88235fd1..e8d038c93 100644 --- a/src/client/v2/indexer/indexer.ts +++ b/src/client/v2/indexer/indexer.ts @@ -104,7 +104,7 @@ export class IndexerClient extends ServiceClient { * @param index - The asset ID to look up. * @category GET */ - lookupAssetBalances(index: number) { + lookupAssetBalances(index: number | bigint) { return new LookupAssetBalances(this.c, index); } @@ -121,7 +121,7 @@ export class IndexerClient extends ServiceClient { * @param index - The asset ID to look up. * @category GET */ - lookupAssetTransactions(index: number) { + lookupAssetTransactions(index: number | bigint) { return new LookupAssetTransactions(this.c, index); } @@ -155,7 +155,7 @@ export class IndexerClient extends ServiceClient { * @param round - The number of the round to look up. * @category GET */ - lookupBlock(round: number) { + lookupBlock(round: number | bigint) { return new LookupBlock(this.c, round); } @@ -274,7 +274,7 @@ export class IndexerClient extends ServiceClient { * @param index - The ID of the asset ot look up. * @category GET */ - lookupAssetByID(index: number) { + lookupAssetByID(index: number | bigint) { return new LookupAssetByID(this.c, index); } @@ -291,7 +291,7 @@ export class IndexerClient extends ServiceClient { * @param index - The ID of the application to look up. * @category GET */ - lookupApplications(index: number) { + lookupApplications(index: number | bigint) { return new LookupApplications(this.c, index); } @@ -308,7 +308,7 @@ export class IndexerClient extends ServiceClient { * @param appID - The ID of the application which generated the logs. * @category GET */ - lookupApplicationLogs(appID: number) { + lookupApplicationLogs(appID: number | bigint) { return new LookupApplicationLogs(this.c, appID); } @@ -398,7 +398,7 @@ export class IndexerClient extends ServiceClient { * @param appID - The ID of the application with boxes. * @category GET */ - searchForApplicationBoxes(appID: number) { + searchForApplicationBoxes(appID: number | bigint) { return new SearchForApplicationBoxes(this.c, appID); } @@ -418,7 +418,7 @@ export class IndexerClient extends ServiceClient { * @param appID - The ID of the application with boxes. * @category GET */ - lookupApplicationBoxByIDandName(appID: number, boxName: Uint8Array) { + lookupApplicationBoxByIDandName(appID: number | bigint, boxName: Uint8Array) { return new LookupApplicationBoxByIDandName(this.c, appID, boxName); } } diff --git a/src/client/v2/indexer/lookupAccountAppLocalStates.ts b/src/client/v2/indexer/lookupAccountAppLocalStates.ts index 20ecf3c61..61901c52d 100644 --- a/src/client/v2/indexer/lookupAccountAppLocalStates.ts +++ b/src/client/v2/indexer/lookupAccountAppLocalStates.ts @@ -68,7 +68,7 @@ export default class LookupAccountAppLocalStates extends JSONRequest { * ``` * @param round */ - round(round: number) { + round(round: number | bigint) { this.query.round = round; return this; } diff --git a/src/client/v2/indexer/lookupAccountCreatedApplications.ts b/src/client/v2/indexer/lookupAccountCreatedApplications.ts index f27ef9a16..1fad042d4 100644 --- a/src/client/v2/indexer/lookupAccountCreatedApplications.ts +++ b/src/client/v2/indexer/lookupAccountCreatedApplications.ts @@ -68,7 +68,7 @@ export default class LookupAccountCreatedApplications extends JSONRequest { + private index: bigint; + /** * Returns information about indexed application boxes. * @@ -21,12 +23,9 @@ export default class LookupApplicationBoxByIDandName extends JSONRequest { * @oaram index - application index. * @category GET */ - constructor( - c: HTTPClient, - private index: number, - boxName: Uint8Array - ) { + constructor(c: HTTPClient, index: number | bigint, boxName: Uint8Array) { super(c); + this.index = BigInt(index); // Encode query in base64 format and append the encoding prefix. const encodedName = bytesToBase64(boxName); this.query.name = encodeURI(`b64:${encodedName}`); diff --git a/src/client/v2/indexer/lookupApplicationLogs.ts b/src/client/v2/indexer/lookupApplicationLogs.ts index 3eb8c2545..b64d8011a 100644 --- a/src/client/v2/indexer/lookupApplicationLogs.ts +++ b/src/client/v2/indexer/lookupApplicationLogs.ts @@ -4,6 +4,8 @@ import { decodeJSON } from '../../../encoding/encoding.js'; import { ApplicationLogsResponse } from './models/types.js'; export default class LookupApplicationLogs extends JSONRequest { + private appID: bigint; + /** * Returns log messages generated by the passed in application. * @@ -17,11 +19,9 @@ export default class LookupApplicationLogs extends JSONRequest { + private index: bigint; + /** * Returns information about the passed application. * @@ -17,11 +19,9 @@ export default class LookupApplications extends JSONRequest * @param index - The ID of the application to look up. * @category GET */ - constructor( - c: HTTPClient, - private index: number - ) { + constructor(c: HTTPClient, index: number | bigint) { super(c); + this.index = BigInt(index); } /** diff --git a/src/client/v2/indexer/lookupAssetBalances.ts b/src/client/v2/indexer/lookupAssetBalances.ts index 1c1eea06b..85c59e108 100644 --- a/src/client/v2/indexer/lookupAssetBalances.ts +++ b/src/client/v2/indexer/lookupAssetBalances.ts @@ -4,6 +4,8 @@ import { decodeJSON } from '../../../encoding/encoding.js'; import { AssetBalancesResponse } from './models/types.js'; export default class LookupAssetBalances extends JSONRequest { + private index: bigint; + /** * Returns the list of accounts which hold the given asset and their balance. * @@ -16,11 +18,9 @@ export default class LookupAssetBalances extends JSONRequest { + private index: bigint; + /** * Returns asset information of the queried asset. * @@ -16,11 +18,9 @@ export default class LookupAssetByID extends JSONRequest { * [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, - private index: number - ) { + constructor(c: HTTPClient, index: number | bigint) { super(c); + this.index = BigInt(index); } /** diff --git a/src/client/v2/indexer/lookupAssetTransactions.ts b/src/client/v2/indexer/lookupAssetTransactions.ts index 6b73a7511..d4a5c77e7 100644 --- a/src/client/v2/indexer/lookupAssetTransactions.ts +++ b/src/client/v2/indexer/lookupAssetTransactions.ts @@ -6,6 +6,8 @@ import { Address } from '../../../encoding/address.js'; import { TransactionsResponse } from './models/types.js'; export default class LookupAssetTransactions extends JSONRequest { + private index: bigint; + /** * Returns transactions relating to the given asset. * @@ -18,11 +20,9 @@ export default class LookupAssetTransactions extends JSONRequest { + private round: bigint; + /** * Returns the block for the passed round. * @@ -17,11 +19,9 @@ export default class LookupBlock extends JSONRequest { * @param round - The number of the round to look up. * @category GET */ - constructor( - c: HTTPClient, - private round: number - ) { + constructor(c: HTTPClient, round: number | bigint) { super(c); + this.round = BigInt(round); } /** diff --git a/src/client/v2/indexer/searchAccounts.ts b/src/client/v2/indexer/searchAccounts.ts index 8acb55fe2..ef3ca8039 100644 --- a/src/client/v2/indexer/searchAccounts.ts +++ b/src/client/v2/indexer/searchAccounts.ts @@ -52,7 +52,7 @@ export default class SearchAccounts extends JSONRequest { * @param greater * @category query */ - currencyGreaterThan(greater: number) { + currencyGreaterThan(greater: number | bigint) { // We convert the following to a string for now to correctly include zero values in request parameters. this.query['currency-greater-than'] = greater.toString(); return this; @@ -84,7 +84,7 @@ export default class SearchAccounts extends JSONRequest { * @param lesser * @category query */ - currencyLessThan(lesser: number) { + currencyLessThan(lesser: number | bigint) { this.query['currency-less-than'] = lesser; return this; } @@ -124,7 +124,7 @@ export default class SearchAccounts extends JSONRequest { * @param id * @category query */ - assetID(id: number) { + assetID(id: number | bigint) { this.query['asset-id'] = id; return this; } @@ -171,7 +171,7 @@ export default class SearchAccounts extends JSONRequest { * @param round * @category query */ - round(round: number) { + round(round: number | bigint) { this.query.round = round; return this; } @@ -210,7 +210,7 @@ export default class SearchAccounts extends JSONRequest { * @param applicationID * @category query */ - applicationID(applicationID: number) { + applicationID(applicationID: number | bigint) { this.query['application-id'] = applicationID; return this; } diff --git a/src/client/v2/indexer/searchForApplicationBoxes.ts b/src/client/v2/indexer/searchForApplicationBoxes.ts index b3186290f..ea75b22a8 100644 --- a/src/client/v2/indexer/searchForApplicationBoxes.ts +++ b/src/client/v2/indexer/searchForApplicationBoxes.ts @@ -4,6 +4,8 @@ import { decodeJSON } from '../../../encoding/encoding.js'; import { BoxesResponse } from './models/types.js'; export default class SearchForApplicationBoxes extends JSONRequest { + private index: bigint; + /** * Returns information about indexed application boxes. * @@ -30,11 +32,9 @@ export default class SearchForApplicationBoxes extends JSONRequest { * @param index * @category query */ - index(index: number) { + index(index: number | bigint) { this.query['asset-id'] = index; return this; } diff --git a/src/client/v2/indexer/searchForTransactions.ts b/src/client/v2/indexer/searchForTransactions.ts index afb446551..a9fb12bd1 100644 --- a/src/client/v2/indexer/searchForTransactions.ts +++ b/src/client/v2/indexer/searchForTransactions.ts @@ -121,7 +121,7 @@ export default class SearchForTransactions extends JSONRequest { @@ -741,7 +741,7 @@ export class AtomicTransactionComposer { ); this.status = AtomicTransactionComposerStatus.COMMITTED; - const confirmedRound = Number(confirmedTxnInfo.confirmedRound); + const confirmedRound = confirmedTxnInfo.confirmedRound!; const methodResults: ABIResult[] = []; diff --git a/src/wait.ts b/src/wait.ts index 4b28e867d..52d5bf800 100644 --- a/src/wait.ts +++ b/src/wait.ts @@ -22,11 +22,12 @@ export async function waitForConfirmation( if (typeof status === 'undefined') { throw new Error('Unable to get node status'); } - const startRound = Number(status.lastRound) + 1; + const startRound = status.lastRound + BigInt(1); + const stopRound = startRound + BigInt(waitRounds); let currentRound = startRound; /* eslint-disable no-await-in-loop */ - while (currentRound < startRound + waitRounds) { + while (currentRound < stopRound) { let poolError = false; try { const pendingInfo = await client.pendingTransactionInformation(txid).do(); @@ -52,7 +53,7 @@ export async function waitForConfirmation( } await client.statusAfterBlock(currentRound).do(); - currentRound += 1; + currentRound += BigInt(1); } /* eslint-enable no-await-in-loop */ throw new Error(`Transaction not confirmed after ${waitRounds} rounds`); diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index 82b8b59b3..2efa21dd6 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -2791,7 +2791,7 @@ module.exports = function getSteps(options) { When('we make any LookupAssetBalances call', async function () { anyLookupAssetBalancesResponse = await this.indexerClient - .lookupAssetBalances() + .lookupAssetBalances(1) .do(); }); @@ -2884,7 +2884,7 @@ module.exports = function getSteps(options) { When('we make any LookupAssetTransactions call', async function () { anyLookupAssetTransactionsResponse = await this.indexerClient - .lookupAssetTransactions() + .lookupAssetTransactions(1) .do(); }); @@ -2943,7 +2943,7 @@ module.exports = function getSteps(options) { let anyLookupBlockResponse; When('we make any LookupBlock call', async function () { - anyLookupBlockResponse = await this.indexerClient.lookupBlock().do(); + anyLookupBlockResponse = await this.indexerClient.lookupBlock(1).do(); }); Then( @@ -2977,7 +2977,7 @@ module.exports = function getSteps(options) { When('we make any LookupAssetByID call', async function () { anyLookupAssetByIDResponse = await this.indexerClient - .lookupAssetByID() + .lookupAssetByID(1) .do(); }); From d2ea927317243d74b7e3c15ce82a56497cae1f41 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Mon, 26 Aug 2024 11:22:31 -0400 Subject: [PATCH 4/7] Use prerelease tag in changelog generation (#892) --- .github/workflows/create-release-pr.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml index 15e98ec0d..9fa321e53 100644 --- a/.github/workflows/create-release-pr.yml +++ b/.github/workflows/create-release-pr.yml @@ -81,6 +81,7 @@ jobs: id: build-changelog env: PREVIOUS_VERSION: ${{ steps.get-release.outputs.latest-tag }} + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} run: | CHANGELOG=$(curl -L \ -X POST \ @@ -88,7 +89,7 @@ jobs: -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"}' \ + -d '{"tag_name":"${{ env.RELEASE_TAG }}","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: From ab55345f7bca0647d32aa4efbdce9351834c691d Mon Sep 17 00:00:00 2001 From: onetechnical Date: Thu, 12 Sep 2024 14:03:07 +0000 Subject: [PATCH 5/7] bump up version to v3.0.0 --- CHANGELOG.md | 18 ++++++++++++++++++ README.md | 8 ++++---- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bde12443..c4f632d39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# v3.0.0 + + + +## What's Changed + +### Bugfixes + +- Fix: Don't delete `dist` folder when creating release PR by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/890 +- Release PR Generation: Use prerelease tag in changelog generation by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/892 + +### Enhancements + +- 3.0.0: Sync changes to develop by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/884 +- REST API: Allow bigints for client args by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/893 + +**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v2.9.0...v3.0.0 + # v2.9.0 diff --git a/README.md b/README.md index a95f68533..66c66ab5c 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ Include a minified browser bundle directly in your HTML like so: ```html ``` @@ -34,8 +34,8 @@ or ```html ``` diff --git a/package-lock.json b/package-lock.json index a91f2a894..560544963 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "algosdk", - "version": "2.9.0", + "version": "3.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "algosdk", - "version": "2.9.0", + "version": "3.0.0", "license": "MIT", "dependencies": { "algorand-msgpack": "^1.1.0", diff --git a/package.json b/package.json index 7c6ba92cf..8ea2e7c09 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "algosdk", - "version": "2.9.0", + "version": "3.0.0", "description": "The official JavaScript SDK for Algorand", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", From 237f3a7cc806aaaf025681365dea131e1ec7ef52 Mon Sep 17 00:00:00 2001 From: John Lee Date: Thu, 12 Sep 2024 11:39:06 -0400 Subject: [PATCH 6/7] Update changelog to include full v3.0.0 release notes. --- CHANGELOG.md | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4f632d39..97227f08c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,44 @@ # v3.0.0 - + ## What's Changed +> ⚠️ **WARNING:** This release is a new major version with breaking changes from the v2.X.X series. For help migrating from v2 releases, see the file `v2_TO_v3_MIGRATION_GUIDE.md`. + +### Breaking Changes + +- Convert algod responses to typed by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/776 +- Align transaction fields to transaction reference spec by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/804 +- TEAL Source Map: Improve SourceMap and support columns by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/834 +- Remove `IntDecoding` as a REST option & support native bigint types in models by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/852 +- Refactor `Transaction` class by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/854 +- Improve object encoding and decoding by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/862 +- Correctly model blocks by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/875 +- Fix stateproof txn representation by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/876 +- Typed indexer responses by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/857 +- Support special case raw binary strings by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/878 +- REST API TEAL bytes fix by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/881 +- Fix remaining REST untyped responses by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/882 + +### Enhancements + +- Remove buffer usage in favor or Uint8Array and Dataview by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/800 +- Remove `Buffer` Usage by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/801 +- Add address bytes length check in encodeAddress by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/809 +- Native esm bundle by @PhearZero in https://github.com/algorand/js-algorand-sdk/pull/836 +- Add ability to pass through fetch options by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/883 +- REST API: Allow bigints for client args by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/893 + ### Bugfixes - Fix: Don't delete `dist` folder when creating release PR by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/890 - Release PR Generation: Use prerelease tag in changelog generation by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/892 -### Enhancements +### Other +- Type and formatting changes by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/853 - 3.0.0: Sync changes to develop by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/884 -- REST API: Allow bigints for client args by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/893 **Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v2.9.0...v3.0.0 From 6973ff583b243ddb0632e91f4c0383021430a789 Mon Sep 17 00:00:00 2001 From: John Lee Date: Thu, 12 Sep 2024 13:01:16 -0400 Subject: [PATCH 7/7] Add additional note about v2 end of life --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97227f08c..80715847c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ > ⚠️ **WARNING:** This release is a new major version with breaking changes from the v2.X.X series. For help migrating from v2 releases, see the file `v2_TO_v3_MIGRATION_GUIDE.md`. +### v2 End of Life + +With the release of v3 of this SDK, v2 is now in maintenance mode. No new features will be added to v2, and only security fixes or critical errors will be addressed. At the end of March 2025, no further updates will be made to the v2 package. + ### Breaking Changes - Convert algod responses to typed by @Eric-Warehime in https://github.com/algorand/js-algorand-sdk/pull/776