From da8f2e6cd616e9430f8bb8d56bb9d25dffe1b98a Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Tue, 20 Aug 2024 11:49:41 -0400 Subject: [PATCH 1/5] 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/5] 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/5] 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/5] 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 a91a29d9538b6f91f4eac936cf328e9ac17df8c1 Mon Sep 17 00:00:00 2001 From: jasonpaulos Date: Mon, 26 Aug 2024 17:45:46 +0000 Subject: [PATCH 5/5] bump up version to v3.0.0-beta.2 --- README.md | 8 ++++---- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a95f68533..9e1b0513b 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..b1ad2bddc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "algosdk", - "version": "2.9.0", + "version": "3.0.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "algosdk", - "version": "2.9.0", + "version": "3.0.0-beta.2", "license": "MIT", "dependencies": { "algorand-msgpack": "^1.1.0", diff --git a/package.json b/package.json index 7c6ba92cf..83f7540bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "algosdk", - "version": "2.9.0", + "version": "3.0.0-beta.2", "description": "The official JavaScript SDK for Algorand", "main": "dist/cjs/index.js", "module": "dist/esm/index.js",