From 89124959975e3bee5a5c5004266a2d9b4dbc816f Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Tue, 20 Feb 2024 17:08:07 -0500 Subject: [PATCH 01/13] Typed indexer responses --- examples/indexer.ts | 4 +- .../v2/indexer/lookupAccountAppLocalStates.ts | 11 ++- src/client/v2/indexer/lookupAccountAssets.ts | 11 ++- src/client/v2/indexer/lookupAccountByID.ts | 11 ++- .../lookupAccountCreatedApplications.ts | 11 ++- .../v2/indexer/lookupAccountCreatedAssets.ts | 11 ++- .../v2/indexer/lookupAccountTransactions.ts | 25 +++++-- .../v2/indexer/lookupApplicationLogs.ts | 11 ++- src/client/v2/indexer/lookupApplications.ts | 11 ++- src/client/v2/indexer/lookupAssetBalances.ts | 11 ++- src/client/v2/indexer/lookupAssetByID.ts | 11 ++- .../v2/indexer/lookupAssetTransactions.ts | 25 +++++-- src/client/v2/indexer/lookupBlock.ts | 11 ++- .../v2/indexer/lookupTransactionByID.ts | 11 ++- src/client/v2/indexer/makeHealthCheck.ts | 11 ++- src/client/v2/indexer/searchAccounts.ts | 11 ++- .../v2/indexer/searchForApplications.ts | 11 ++- src/client/v2/indexer/searchForAssets.ts | 11 ++- .../v2/indexer/searchForTransactions.ts | 25 +++++-- tests/cucumber/steps/steps.js | 72 +++++++++---------- 20 files changed, 239 insertions(+), 77 deletions(-) diff --git a/examples/indexer.ts b/examples/indexer.ts index d187a28ac..f853400f4 100644 --- a/examples/indexer.ts +++ b/examples/indexer.ts @@ -39,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) { @@ -51,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)}`); diff --git a/src/client/v2/indexer/lookupAccountAppLocalStates.ts b/src/client/v2/indexer/lookupAccountAppLocalStates.ts index 71980ad4d..a10f7de9e 100644 --- a/src/client/v2/indexer/lookupAccountAppLocalStates.ts +++ b/src/client/v2/indexer/lookupAccountAppLocalStates.ts @@ -1,8 +1,12 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; import { Address } from '../../../encoding/address.js'; +import { ApplicationLocalStatesResponse } from './models/types.js'; -export default class LookupAccountAppLocalStates extends JSONRequest { +export default class LookupAccountAppLocalStates extends JSONRequest< + ApplicationLocalStatesResponse, + Record +> { private account: string | Address; /** @@ -135,4 +139,9 @@ export default class LookupAccountAppLocalStates extends JSONRequest { this.query['application-id'] = index; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): ApplicationLocalStatesResponse { + return ApplicationLocalStatesResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupAccountAssets.ts b/src/client/v2/indexer/lookupAccountAssets.ts index 9062a94ca..e17359202 100644 --- a/src/client/v2/indexer/lookupAccountAssets.ts +++ b/src/client/v2/indexer/lookupAccountAssets.ts @@ -1,8 +1,12 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; import { Address } from '../../../encoding/address.js'; +import { AssetsResponse } from './models/types.js'; -export default class LookupAccountAssets extends JSONRequest { +export default class LookupAccountAssets extends JSONRequest< + AssetsResponse, + Record +> { private account: string; /** @@ -136,4 +140,9 @@ export default class LookupAccountAssets extends JSONRequest { this.query['asset-id'] = index; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): AssetsResponse { + return AssetsResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupAccountByID.ts b/src/client/v2/indexer/lookupAccountByID.ts index 0eb2e8b89..3e531eed0 100644 --- a/src/client/v2/indexer/lookupAccountByID.ts +++ b/src/client/v2/indexer/lookupAccountByID.ts @@ -1,8 +1,12 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; import { Address } from '../../../encoding/address.js'; +import { AccountResponse } from './models/types.js'; -export default class LookupAccountByID extends JSONRequest { +export default class LookupAccountByID extends JSONRequest< + AccountResponse, + Record +> { private account: string; /** @@ -104,4 +108,9 @@ export default class LookupAccountByID extends JSONRequest { this.query.exclude = exclude; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): AccountResponse { + return AccountResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupAccountCreatedApplications.ts b/src/client/v2/indexer/lookupAccountCreatedApplications.ts index 635f45c35..c5c18f155 100644 --- a/src/client/v2/indexer/lookupAccountCreatedApplications.ts +++ b/src/client/v2/indexer/lookupAccountCreatedApplications.ts @@ -1,8 +1,12 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; import { Address } from '../../../encoding/address.js'; +import { ApplicationsResponse } from './models/types.js'; -export default class LookupAccountCreatedApplications extends JSONRequest { +export default class LookupAccountCreatedApplications extends JSONRequest< + ApplicationsResponse, + Record +> { private account: string; /** @@ -136,4 +140,9 @@ export default class LookupAccountCreatedApplications extends JSONRequest { this.query['application-id'] = index; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): ApplicationsResponse { + return ApplicationsResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupAccountCreatedAssets.ts b/src/client/v2/indexer/lookupAccountCreatedAssets.ts index 5d668a5d2..ad9a569ab 100644 --- a/src/client/v2/indexer/lookupAccountCreatedAssets.ts +++ b/src/client/v2/indexer/lookupAccountCreatedAssets.ts @@ -1,8 +1,12 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; import { Address } from '../../../encoding/address.js'; +import { AssetsResponse } from './models/types.js'; -export default class LookupAccountCreatedAssets extends JSONRequest { +export default class LookupAccountCreatedAssets extends JSONRequest< + AssetsResponse, + Record +> { private account: string; /** @@ -137,4 +141,9 @@ export default class LookupAccountCreatedAssets extends JSONRequest { this.query['asset-id'] = index; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): AssetsResponse { + return AssetsResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupAccountTransactions.ts b/src/client/v2/indexer/lookupAccountTransactions.ts index 1f3c150ac..8f6a32d5c 100644 --- a/src/client/v2/indexer/lookupAccountTransactions.ts +++ b/src/client/v2/indexer/lookupAccountTransactions.ts @@ -2,6 +2,7 @@ import { bytesToBase64 } from '../../../encoding/binarydata.js'; import { HTTPClient } from '../../client.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 @@ -15,7 +16,10 @@ export function base64StringFunnel(data: Uint8Array | string) { return bytesToBase64(data); } -export default class LookupAccountTransactions extends JSONRequest { +export default class LookupAccountTransactions extends JSONRequest< + TransactionsResponse, + Record +> { private account: string; /** @@ -245,11 +249,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; } @@ -266,11 +271,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; } @@ -388,4 +394,9 @@ export default class LookupAccountTransactions extends JSONRequest { this.query['rekey-to'] = rekeyTo; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): TransactionsResponse { + return TransactionsResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupApplicationLogs.ts b/src/client/v2/indexer/lookupApplicationLogs.ts index e2133edfc..b23cfe01c 100644 --- a/src/client/v2/indexer/lookupApplicationLogs.ts +++ b/src/client/v2/indexer/lookupApplicationLogs.ts @@ -1,7 +1,11 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; +import { ApplicationLogsResponse } from './models/types.js'; -export default class LookupApplicationLogs extends JSONRequest { +export default class LookupApplicationLogs extends JSONRequest< + ApplicationLogsResponse, + Record +> { /** * Returns log messages generated by the passed in application. * @@ -154,4 +158,9 @@ export default class LookupApplicationLogs extends JSONRequest { this.query.txid = txid; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): ApplicationLogsResponse { + return ApplicationLogsResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupApplications.ts b/src/client/v2/indexer/lookupApplications.ts index c717e83d1..449e27ec0 100644 --- a/src/client/v2/indexer/lookupApplications.ts +++ b/src/client/v2/indexer/lookupApplications.ts @@ -1,7 +1,11 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; +import { ApplicationResponse } from './models/types.js'; -export default class LookupApplications extends JSONRequest { +export default class LookupApplications extends JSONRequest< + ApplicationResponse, + Record +> { /** * Returns information about the passed application. * @@ -57,4 +61,9 @@ export default class LookupApplications extends JSONRequest { this.query['include-all'] = value; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): ApplicationResponse { + return ApplicationResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupAssetBalances.ts b/src/client/v2/indexer/lookupAssetBalances.ts index 31b73b026..cd32fe833 100644 --- a/src/client/v2/indexer/lookupAssetBalances.ts +++ b/src/client/v2/indexer/lookupAssetBalances.ts @@ -1,7 +1,11 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; +import { AssetBalancesResponse } from './models/types.js'; -export default class LookupAssetBalances extends JSONRequest { +export default class LookupAssetBalances extends JSONRequest< + AssetBalancesResponse, + Record +> { /** * Returns the list of accounts which hold the given asset and their balance. * @@ -145,4 +149,9 @@ export default class LookupAssetBalances extends JSONRequest { this.query['include-all'] = value; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): AssetBalancesResponse { + return AssetBalancesResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupAssetByID.ts b/src/client/v2/indexer/lookupAssetByID.ts index f04c6aef9..5a2e3e0b2 100644 --- a/src/client/v2/indexer/lookupAssetByID.ts +++ b/src/client/v2/indexer/lookupAssetByID.ts @@ -1,7 +1,11 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; +import { AssetResponse } from './models/types.js'; -export default class LookupAssetByID extends JSONRequest { +export default class LookupAssetByID extends JSONRequest< + AssetResponse, + Record +> { /** * Returns asset information of the queried asset. * @@ -56,4 +60,9 @@ export default class LookupAssetByID extends JSONRequest { this.query['include-all'] = value; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): AssetResponse { + return AssetResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupAssetTransactions.ts b/src/client/v2/indexer/lookupAssetTransactions.ts index 1dc3ebb2e..5a9103e2e 100644 --- a/src/client/v2/indexer/lookupAssetTransactions.ts +++ b/src/client/v2/indexer/lookupAssetTransactions.ts @@ -2,8 +2,12 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.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< + TransactionsResponse, + Record +> { /** * Returns transactions relating to the given asset. * @@ -212,11 +216,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; } @@ -233,11 +238,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; } @@ -395,4 +401,9 @@ export default class LookupAssetTransactions extends JSONRequest { this.query['rekey-to'] = rekeyTo; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): TransactionsResponse { + return TransactionsResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupBlock.ts b/src/client/v2/indexer/lookupBlock.ts index b0e014512..251423da4 100644 --- a/src/client/v2/indexer/lookupBlock.ts +++ b/src/client/v2/indexer/lookupBlock.ts @@ -1,7 +1,11 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; +import { Block } from './models/types.js'; -export default class LookupBlock extends JSONRequest { +export default class LookupBlock extends JSONRequest< + Block, + Record +> { /** * Returns the block for the passed round. * @@ -37,4 +41,9 @@ export default class LookupBlock extends JSONRequest { this.query['header-only'] = headerOnly; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): Block { + return Block.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/lookupTransactionByID.ts b/src/client/v2/indexer/lookupTransactionByID.ts index f03a95863..80bbc4149 100644 --- a/src/client/v2/indexer/lookupTransactionByID.ts +++ b/src/client/v2/indexer/lookupTransactionByID.ts @@ -1,7 +1,11 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; +import { TransactionResponse } from './models/types.js'; -export default class LookupTransactionByID extends JSONRequest { +export default class LookupTransactionByID extends JSONRequest< + TransactionResponse, + Record +> { /** * Returns information about the given transaction. * @@ -28,4 +32,9 @@ export default class LookupTransactionByID extends JSONRequest { path() { return `/v2/transactions/${this.txID}`; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): TransactionResponse { + return TransactionResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/makeHealthCheck.ts b/src/client/v2/indexer/makeHealthCheck.ts index c6d3ee14c..2ff40a876 100644 --- a/src/client/v2/indexer/makeHealthCheck.ts +++ b/src/client/v2/indexer/makeHealthCheck.ts @@ -1,4 +1,5 @@ import JSONRequest from '../jsonrequest.js'; +import { HealthCheck } from './models/types.js'; /** * Returns the health object for the service. @@ -12,7 +13,10 @@ import JSONRequest from '../jsonrequest.js'; * [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< + HealthCheck, + Record +> { /** * @returns `/health` */ @@ -20,4 +24,9 @@ export default class MakeHealthCheck extends JSONRequest { path() { return '/health'; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): HealthCheck { + return HealthCheck.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/searchAccounts.ts b/src/client/v2/indexer/searchAccounts.ts index 956e53a3c..7dfbed6bf 100644 --- a/src/client/v2/indexer/searchAccounts.ts +++ b/src/client/v2/indexer/searchAccounts.ts @@ -1,5 +1,6 @@ import JSONRequest from '../jsonrequest.js'; import { Address } from '../../../encoding/address.js'; +import { AccountsResponse } from './models/types.js'; /** * Returns information about indexed accounts. @@ -12,7 +13,10 @@ import { Address } from '../../../encoding/address.js'; * [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< + AccountsResponse, + Record +> { /** * @returns `/v2/accounts` */ @@ -267,4 +271,9 @@ export default class SearchAccounts extends JSONRequest { this.query.exclude = exclude; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): AccountsResponse { + return AccountsResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/searchForApplications.ts b/src/client/v2/indexer/searchForApplications.ts index b971a0673..379a884a4 100644 --- a/src/client/v2/indexer/searchForApplications.ts +++ b/src/client/v2/indexer/searchForApplications.ts @@ -1,5 +1,6 @@ import JSONRequest from '../jsonrequest.js'; import { Address } from '../../../encoding/address.js'; +import { ApplicationsResponse } from './models/types.js'; /** * Returns information about indexed applications. @@ -12,7 +13,10 @@ import { Address } from '../../../encoding/address.js'; * [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< + ApplicationsResponse, + Record +> { /** * @returns `/v2/applications` */ @@ -132,4 +136,9 @@ export default class SearchForApplications extends JSONRequest { this.query['include-all'] = value; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): ApplicationsResponse { + return ApplicationsResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/searchForAssets.ts b/src/client/v2/indexer/searchForAssets.ts index c232db531..759a0c3b3 100644 --- a/src/client/v2/indexer/searchForAssets.ts +++ b/src/client/v2/indexer/searchForAssets.ts @@ -1,5 +1,6 @@ import JSONRequest from '../jsonrequest.js'; import { Address } from '../../../encoding/address.js'; +import { AssetsResponse } from './models/types.js'; /** * Returns information about indexed assets. @@ -12,7 +13,10 @@ import { Address } from '../../../encoding/address.js'; * [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< + AssetsResponse, + Record +> { /** * @returns `/v2/assets` */ @@ -173,4 +177,9 @@ export default class SearchForAssets extends JSONRequest { this.query['include-all'] = value; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): AssetsResponse { + return AssetsResponse.from_obj_for_encoding(body); + } } diff --git a/src/client/v2/indexer/searchForTransactions.ts b/src/client/v2/indexer/searchForTransactions.ts index d96ba3945..eb8c69e71 100644 --- a/src/client/v2/indexer/searchForTransactions.ts +++ b/src/client/v2/indexer/searchForTransactions.ts @@ -1,6 +1,7 @@ import JSONRequest from '../jsonrequest.js'; import { base64StringFunnel } from './lookupAccountTransactions.js'; import { Address } from '../../../encoding/address.js'; +import { TransactionsResponse } from './models/types.js'; /** * Returns information about indexed transactions. @@ -13,7 +14,10 @@ import { Address } from '../../../encoding/address.js'; * [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< + TransactionsResponse, + Record +> { /** * @returns `/v2/transactions` */ @@ -215,11 +219,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; } @@ -235,11 +240,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; } @@ -432,4 +438,9 @@ export default class SearchForTransactions extends JSONRequest { this.query['currency-less-than'] = lesser; return this; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): TransactionsResponse { + return TransactionsResponse.from_obj_for_encoding(body); + } } diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index 829931cf2..a5fd7db13 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -2395,7 +2395,7 @@ 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( - anyLookupAssetBalancesResponse['current-round'], + anyLookupAssetBalancesResponse.currentRound, BigInt(round) ); assert.strictEqual( @@ -2484,7 +2484,7 @@ 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( - anyLookupAssetTransactionsResponse['current-round'], + anyLookupAssetTransactionsResponse.currentRound, BigInt(round) ); assert.strictEqual( @@ -2515,12 +2515,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.toString(), - anyLookupAccountTransactionsResponse['current-round'].toString() + anyLookupAccountTransactionsResponse.currentRound, + BigInt(round) ); assert.strictEqual( - length, - anyLookupAccountTransactionsResponse.transactions.length + anyLookupAccountTransactionsResponse.transactions.length, + length ); if (length === 0) { return; @@ -2541,10 +2541,7 @@ module.exports = function getSteps(options) { Then( 'the parsed LookupBlock response should have previous block hash {string}', (prevHash) => { - assert.strictEqual( - prevHash, - anyLookupBlockResponse['previous-block-hash'] - ); + assert.strictEqual(anyLookupBlockResponse.previousBlockHash, prevHash); } ); @@ -2561,7 +2558,7 @@ module.exports = function getSteps(options) { Then( 'the parsed LookupAccountByID response should have address {string}', (address) => { - assert.strictEqual(address, anyLookupAccountByIDResponse.account.address); + assert.strictEqual(anyLookupAccountByIDResponse.account.address, address); } ); @@ -2586,17 +2583,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.toString(), - anySearchAccountsResponse['current-round'].toString() - ); - 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 ); } ); @@ -2604,14 +2598,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 ); } ); @@ -2628,19 +2622,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.toString(), - anySearchForTransactionsResponse['current-round'].toString() + 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 ); } ); @@ -2649,19 +2643,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 ); } ); @@ -2678,16 +2672,16 @@ module.exports = function getSteps(options) { '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.toString(), - anySearchForAssetsResponse['current-round'].toString() + anySearchForAssetsResponse.currentRound, + BigInt(round) ); - assert.strictEqual(length, anySearchForAssetsResponse.assets.length); + assert.strictEqual(anySearchForAssetsResponse.assets.length, length); if (length === 0) { return; } assert.strictEqual( - assetIndex.toString(), - anySearchForAssetsResponse.assets[idx].index.toString() + anySearchForAssetsResponse.assets[idx].index, + BigInt(assetIndex) ); } ); From a2f13bd137ebcc91afccc24ac4039f6cede7f0b2 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Thu, 22 Feb 2024 09:48:41 -0500 Subject: [PATCH 02/13] Changes for cucumber tests --- .test-env | 2 +- tests/cucumber/steps/steps.js | 261 ++++++++++++++++++---------------- 2 files changed, 142 insertions(+), 121 deletions(-) diff --git a/.test-env b/.test-env index a321a7f1e..e4abd2b12 100644 --- a/.test-env +++ b/.test-env @@ -1,6 +1,6 @@ # Configs for testing repo download: SDK_TESTING_URL="https://github.com/algorand/algorand-sdk-testing" -SDK_TESTING_BRANCH="V2" +SDK_TESTING_BRANCH="update-indexer-mocks" SDK_TESTING_HARNESS="test-harness" INSTALL_ONLY=0 diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index a5fd7db13..39256a810 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -1489,9 +1489,11 @@ module.exports = function getSteps(options) { } } else if (client === 'indexer') { // endpoints are ignored by mock server, see setupMockServerForResponses - this.actualMockResponse = await this.indexerClient - .makeHealthCheck() - .do(); + const response = await this.indexerClient.makeHealthCheck().doRaw(); + const responseString = new TextDecoder().decode(response); + this.actualMockResponse = algosdk.parseJSON(responseString, { + intDecoding: algosdk.IntDecoding.BIGINT, + }); } else { throw Error(`did not recognize desired client "${client}"`); } @@ -1983,24 +1985,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) + ); } ); @@ -2073,22 +2076,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) + ); } ); @@ -2139,35 +2143,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)); } ); @@ -2195,29 +2203,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) + ); } ); @@ -2246,14 +2256,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) + ); } ); @@ -2281,25 +2293,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) + ); } ); @@ -2358,28 +2371,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)); } ); @@ -2414,7 +2428,7 @@ module.exports = function getSteps(options) { BigInt(amount) ); assert.strictEqual( - anyLookupAssetBalancesResponse.balances[idx]['is-frozen'], + anyLookupAssetBalancesResponse.balances[idx].isFrozen, frozenState ); } @@ -2423,52 +2437,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) + ); } ); @@ -2541,7 +2559,10 @@ module.exports = function getSteps(options) { Then( 'the parsed LookupBlock response should have previous block hash {string}', (prevHash) => { - assert.strictEqual(anyLookupBlockResponse.previousBlockHash, prevHash); + assert.strictEqual( + algosdk.bytesToBase64(anyLookupBlockResponse.previousBlockHash), + prevHash + ); } ); From f4ecc4c8d9f25cafc041fa0e5ad9a7c8669fdf2d Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Thu, 22 Feb 2024 14:00:51 -0500 Subject: [PATCH 03/13] slight modification to step --- tests/cucumber/steps/steps.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index 39256a810..2fb09ac2b 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -4493,10 +4493,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) @@ -4511,22 +4511,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(boxKey, actualName); - assert.deepStrictEqual(algosdk.base64ToBytes(boxValue), 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); } ); From 92ed8293aab46d4e7ab8688bd13c3704d4fd4d96 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Thu, 29 Feb 2024 16:05:16 -0500 Subject: [PATCH 04/13] Be consistent with bigint round type --- examples/utils.ts | 2 +- tests/cucumber/steps/steps.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/utils.ts b/examples/utils.ts index 180b26a9a..3bfab61d5 100644 --- a/examples/utils.ts +++ b/examples/utils.ts @@ -54,7 +54,7 @@ export async function indexerWaitForRound( round: number | bigint, maxAttempts: number ) { - let indexerRound = 0; + let indexerRound = BigInt(0); let attempts = 0; for (;;) { diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index 2fb09ac2b..63e62d3b3 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -4614,7 +4614,7 @@ module.exports = function getSteps(options) { const maxAttempts = 30; const roundToWaitFor = this.lastTxnConfirmedRound; - let indexerRound = 0; + let indexerRound = BigInt(0); let attempts = 0; for (;;) { From 41c11a85d1ebc384eaa201af9bae992499441bbb Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Thu, 7 Mar 2024 16:32:59 -0500 Subject: [PATCH 05/13] WIP --- src/client/v2/algod/compile.ts | 2 +- src/client/v2/algod/models/types.ts | 760 +++++-------------------- src/client/v2/algod/suggestedParams.ts | 6 + src/client/v2/indexer/models/types.ts | 607 +++++--------------- src/client/v2/request.ts | 45 ++ src/encoding/objects.ts | 15 + tests/cucumber/steps/steps.js | 362 ++++++++++-- tests/cucumber/unit.tags | 39 +- 8 files changed, 682 insertions(+), 1154 deletions(-) create mode 100644 src/client/v2/request.ts create mode 100644 src/encoding/objects.ts diff --git a/src/client/v2/algod/compile.ts b/src/client/v2/algod/compile.ts index c692baf67..c1bd09fdc 100644 --- a/src/client/v2/algod/compile.ts +++ b/src/client/v2/algod/compile.ts @@ -55,7 +55,7 @@ export default class Compile extends JSONRequest< query: this.query, requestHeaders: txHeaders, }); - return res.body; + return this.prepare(res.body); } // eslint-disable-next-line class-methods-use-this diff --git a/src/client/v2/algod/models/types.ts b/src/client/v2/algod/models/types.ts index 3b21b91c6..70e1d9468 100644 --- a/src/client/v2/algod/models/types.ts +++ b/src/client/v2/algod/models/types.ts @@ -330,57 +330,19 @@ export class Account extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): Account { /* eslint-disable dot-notation */ - if (typeof data['address'] === 'undefined') - throw new Error(`Response is missing required field 'address': ${data}`); - if (typeof data['amount'] === 'undefined') - throw new Error(`Response is missing required field 'amount': ${data}`); - if (typeof data['amount-without-pending-rewards'] === 'undefined') - throw new Error( - `Response is missing required field 'amount-without-pending-rewards': ${data}` - ); - if (typeof data['min-balance'] === 'undefined') - throw new Error( - `Response is missing required field 'min-balance': ${data}` - ); - if (typeof data['pending-rewards'] === 'undefined') - throw new Error( - `Response is missing required field 'pending-rewards': ${data}` - ); - if (typeof data['rewards'] === 'undefined') - throw new Error(`Response is missing required field 'rewards': ${data}`); - if (typeof data['round'] === 'undefined') - throw new Error(`Response is missing required field 'round': ${data}`); - if (typeof data['status'] === 'undefined') - throw new Error(`Response is missing required field 'status': ${data}`); - if (typeof data['total-apps-opted-in'] === 'undefined') - throw new Error( - `Response is missing required field 'total-apps-opted-in': ${data}` - ); - if (typeof data['total-assets-opted-in'] === 'undefined') - throw new Error( - `Response is missing required field 'total-assets-opted-in': ${data}` - ); - if (typeof data['total-created-apps'] === 'undefined') - throw new Error( - `Response is missing required field 'total-created-apps': ${data}` - ); - if (typeof data['total-created-assets'] === 'undefined') - throw new Error( - `Response is missing required field 'total-created-assets': ${data}` - ); return new Account({ - address: data['address'], - amount: data['amount'], - amountWithoutPendingRewards: data['amount-without-pending-rewards'], - minBalance: data['min-balance'], - pendingRewards: data['pending-rewards'], - rewards: data['rewards'], - round: data['round'], - status: data['status'], - totalAppsOptedIn: data['total-apps-opted-in'], - totalAssetsOptedIn: data['total-assets-opted-in'], - totalCreatedApps: data['total-created-apps'], - totalCreatedAssets: data['total-created-assets'], + address: data['address'] ?? '', + amount: data['amount'] ?? 0, + amountWithoutPendingRewards: data['amount-without-pending-rewards'] ?? 0, + minBalance: data['min-balance'] ?? 0, + pendingRewards: data['pending-rewards'] ?? 0, + rewards: data['rewards'] ?? 0, + round: data['round'] ?? 0, + status: data['status'] ?? '', + totalAppsOptedIn: data['total-apps-opted-in'] ?? 0, + totalAssetsOptedIn: data['total-assets-opted-in'] ?? 0, + totalCreatedApps: data['total-created-apps'] ?? 0, + totalCreatedAssets: data['total-created-assets'] ?? 0, appsLocalState: typeof data['apps-local-state'] !== 'undefined' ? data['apps-local-state'].map( @@ -480,10 +442,8 @@ export class AccountApplicationResponse extends BaseModel { data: Record ): AccountApplicationResponse { /* eslint-disable dot-notation */ - if (typeof data['round'] === 'undefined') - throw new Error(`Response is missing required field 'round': ${data}`); return new AccountApplicationResponse({ - round: data['round'], + round: data['round'] ?? 0, appLocalState: typeof data['app-local-state'] !== 'undefined' ? ApplicationLocalState.from_obj_for_encoding(data['app-local-state']) @@ -554,10 +514,8 @@ export class AccountAssetResponse extends BaseModel { data: Record ): AccountAssetResponse { /* eslint-disable dot-notation */ - if (typeof data['round'] === 'undefined') - throw new Error(`Response is missing required field 'round': ${data}`); return new AccountAssetResponse({ - round: data['round'], + round: data['round'] ?? 0, assetHolding: typeof data['asset-holding'] !== 'undefined' ? AssetHolding.from_obj_for_encoding(data['asset-holding']) @@ -664,32 +622,13 @@ export class AccountParticipation extends BaseModel { data: Record ): AccountParticipation { /* eslint-disable dot-notation */ - if (typeof data['selection-participation-key'] === 'undefined') - throw new Error( - `Response is missing required field 'selection-participation-key': ${data}` - ); - if (typeof data['vote-first-valid'] === 'undefined') - throw new Error( - `Response is missing required field 'vote-first-valid': ${data}` - ); - if (typeof data['vote-key-dilution'] === 'undefined') - throw new Error( - `Response is missing required field 'vote-key-dilution': ${data}` - ); - if (typeof data['vote-last-valid'] === 'undefined') - throw new Error( - `Response is missing required field 'vote-last-valid': ${data}` - ); - if (typeof data['vote-participation-key'] === 'undefined') - throw new Error( - `Response is missing required field 'vote-participation-key': ${data}` - ); return new AccountParticipation({ - selectionParticipationKey: data['selection-participation-key'], - voteFirstValid: data['vote-first-valid'], - voteKeyDilution: data['vote-key-dilution'], - voteLastValid: data['vote-last-valid'], - voteParticipationKey: data['vote-participation-key'], + selectionParticipationKey: + data['selection-participation-key'] ?? new Uint8Array(), + voteFirstValid: data['vote-first-valid'] ?? 0, + voteKeyDilution: data['vote-key-dilution'] ?? 0, + voteLastValid: data['vote-last-valid'] ?? 0, + voteParticipationKey: data['vote-participation-key'] ?? new Uint8Array(), stateProofKey: data['state-proof-key'], }); /* eslint-enable dot-notation */ @@ -732,15 +671,9 @@ export class AccountStateDelta extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): AccountStateDelta { /* eslint-disable dot-notation */ - if (typeof data['address'] === 'undefined') - throw new Error(`Response is missing required field 'address': ${data}`); - if (!Array.isArray(data['delta'])) - throw new Error( - `Response is missing required array field 'delta': ${data}` - ); return new AccountStateDelta({ - address: data['address'], - delta: data['delta'].map(EvalDeltaKeyValue.from_obj_for_encoding), + address: data['address'] ?? '', + delta: (data['delta'] ?? []).map(EvalDeltaKeyValue.from_obj_for_encoding), }); /* eslint-enable dot-notation */ } @@ -785,13 +718,9 @@ export class Application extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): Application { /* eslint-disable dot-notation */ - if (typeof data['id'] === 'undefined') - throw new Error(`Response is missing required field 'id': ${data}`); - if (typeof data['params'] === 'undefined') - throw new Error(`Response is missing required field 'params': ${data}`); return new Application({ - id: data['id'], - params: ApplicationParams.from_obj_for_encoding(data['params']), + id: data['id'] ?? 0, + params: ApplicationParams.from_obj_for_encoding(data['params'] ?? {}), }); /* eslint-enable dot-notation */ } @@ -859,10 +788,8 @@ export class ApplicationInitialStates extends BaseModel { data: Record ): ApplicationInitialStates { /* eslint-disable dot-notation */ - if (typeof data['id'] === 'undefined') - throw new Error(`Response is missing required field 'id': ${data}`); return new ApplicationInitialStates({ - id: data['id'], + id: data['id'] ?? 0, appBoxes: typeof data['app-boxes'] !== 'undefined' ? ApplicationKVStorage.from_obj_for_encoding(data['app-boxes']) @@ -915,12 +842,8 @@ export class ApplicationKVStorage extends BaseModel { data: Record ): ApplicationKVStorage { /* eslint-disable dot-notation */ - if (!Array.isArray(data['kvs'])) - throw new Error( - `Response is missing required array field 'kvs': ${data}` - ); return new ApplicationKVStorage({ - kvs: data['kvs'].map(AvmKeyValue.from_obj_for_encoding), + kvs: (data['kvs'] ?? []).map(AvmKeyValue.from_obj_for_encoding), account: data['account'], }); /* eslint-enable dot-notation */ @@ -962,13 +885,9 @@ export class ApplicationLocalReference extends BaseModel { 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}`); return new ApplicationLocalReference({ - account: data['account'], - app: data['app'], + account: data['account'] ?? '', + app: data['app'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -1025,13 +944,11 @@ export class ApplicationLocalState extends BaseModel { data: Record ): ApplicationLocalState { /* eslint-disable dot-notation */ - if (typeof data['id'] === 'undefined') - throw new Error(`Response is missing required field 'id': ${data}`); - if (typeof data['schema'] === 'undefined') - throw new Error(`Response is missing required field 'schema': ${data}`); return new ApplicationLocalState({ - id: data['id'], - schema: ApplicationStateSchema.from_obj_for_encoding(data['schema']), + id: data['id'] ?? 0, + schema: ApplicationStateSchema.from_obj_for_encoding( + data['schema'] ?? {} + ), keyValue: typeof data['key-value'] !== 'undefined' ? data['key-value'].map(TealKeyValue.from_obj_for_encoding) @@ -1141,20 +1058,10 @@ export class ApplicationParams extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): ApplicationParams { /* eslint-disable dot-notation */ - if (typeof data['approval-program'] === 'undefined') - throw new Error( - `Response is missing required field 'approval-program': ${data}` - ); - if (typeof data['clear-state-program'] === 'undefined') - throw new Error( - `Response is missing required field 'clear-state-program': ${data}` - ); - if (typeof data['creator'] === 'undefined') - throw new Error(`Response is missing required field 'creator': ${data}`); return new ApplicationParams({ - approvalProgram: data['approval-program'], - clearStateProgram: data['clear-state-program'], - creator: data['creator'], + approvalProgram: data['approval-program'] ?? new Uint8Array(), + clearStateProgram: data['clear-state-program'] ?? new Uint8Array(), + creator: data['creator'] ?? '', extraProgramPages: data['extra-program-pages'], globalState: typeof data['global-state'] !== 'undefined' @@ -1252,20 +1159,10 @@ export class ApplicationStateOperation extends BaseModel { 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}` - ); return new ApplicationStateOperation({ - appStateType: data['app-state-type'], - key: data['key'], - operation: data['operation'], + appStateType: data['app-state-type'] ?? '', + key: data['key'] ?? new Uint8Array(), + operation: data['operation'] ?? '', account: data['account'], newValue: typeof data['new-value'] !== 'undefined' @@ -1317,15 +1214,9 @@ export class ApplicationStateSchema extends BaseModel { data: Record ): ApplicationStateSchema { /* eslint-disable dot-notation */ - if (typeof data['num-byte-slice'] === 'undefined') - throw new Error( - `Response is missing required field 'num-byte-slice': ${data}` - ); - if (typeof data['num-uint'] === 'undefined') - throw new Error(`Response is missing required field 'num-uint': ${data}`); return new ApplicationStateSchema({ - numByteSlice: data['num-byte-slice'], - numUint: data['num-uint'], + numByteSlice: data['num-byte-slice'] ?? 0, + numUint: data['num-uint'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -1376,13 +1267,9 @@ export class Asset extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): Asset { /* eslint-disable dot-notation */ - if (typeof data['index'] === 'undefined') - throw new Error(`Response is missing required field 'index': ${data}`); - if (typeof data['params'] === 'undefined') - throw new Error(`Response is missing required field 'params': ${data}`); return new Asset({ - index: data['index'], - params: AssetParams.from_obj_for_encoding(data['params']), + index: data['index'] ?? 0, + params: AssetParams.from_obj_for_encoding(data['params'] ?? {}), }); /* eslint-enable dot-notation */ } @@ -1439,18 +1326,10 @@ export class AssetHolding extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): AssetHolding { /* eslint-disable dot-notation */ - if (typeof data['amount'] === 'undefined') - throw new Error(`Response is missing required field 'amount': ${data}`); - if (typeof data['asset-id'] === 'undefined') - throw new Error(`Response is missing required field 'asset-id': ${data}`); - if (typeof data['is-frozen'] === 'undefined') - throw new Error( - `Response is missing required field 'is-frozen': ${data}` - ); return new AssetHolding({ - amount: data['amount'], - assetId: data['asset-id'], - isFrozen: data['is-frozen'], + amount: data['amount'] ?? 0, + assetId: data['asset-id'] ?? 0, + isFrozen: data['is-frozen'] ?? false, }); /* eslint-enable dot-notation */ } @@ -1491,13 +1370,9 @@ export class AssetHoldingReference extends BaseModel { 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}`); return new AssetHoldingReference({ - account: data['account'], - asset: data['asset'], + account: data['account'] ?? '', + asset: data['asset'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -1704,16 +1579,10 @@ export class AssetParams extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): AssetParams { /* eslint-disable dot-notation */ - if (typeof data['creator'] === 'undefined') - throw new Error(`Response is missing required field 'creator': ${data}`); - if (typeof data['decimals'] === 'undefined') - throw new Error(`Response is missing required field 'decimals': ${data}`); - if (typeof data['total'] === 'undefined') - throw new Error(`Response is missing required field 'total': ${data}`); return new AssetParams({ - creator: data['creator'], - decimals: data['decimals'], - total: data['total'], + creator: data['creator'] ?? '', + decimals: data['decimals'] ?? 0, + total: data['total'] ?? 0, clawback: data['clawback'], defaultFrozen: data['default-frozen'], freeze: data['freeze'], @@ -1761,13 +1630,9 @@ export class AvmKeyValue extends BaseModel { // 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}`); return new AvmKeyValue({ - key: data['key'], - value: AvmValue.from_obj_for_encoding(data['value']), + key: data['key'] ?? new Uint8Array(), + value: AvmValue.from_obj_for_encoding(data['value'] ?? {}), }); /* eslint-enable dot-notation */ } @@ -1822,10 +1687,8 @@ export class AvmValue extends BaseModel { // 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}`); return new AvmValue({ - type: data['type'], + type: data['type'] ?? 0, bytes: data['bytes'], uint: data['uint'], }); @@ -1858,12 +1721,8 @@ export class BlockHashResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): BlockHashResponse { /* eslint-disable dot-notation */ - if (typeof data['blockHash'] === 'undefined') - throw new Error( - `Response is missing required field 'blockHash': ${data}` - ); return new BlockHashResponse({ - blockhash: data['blockHash'], + blockhash: data['blockHash'] ?? '', }); /* eslint-enable dot-notation */ } @@ -1910,10 +1769,8 @@ export class BlockResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): BlockResponse { /* eslint-disable dot-notation */ - if (typeof data['block'] === 'undefined') - throw new Error(`Response is missing required field 'block': ${data}`); return new BlockResponse({ - block: data['block'], + block: data['block'] ?? {}, cert: data['cert'], }); /* eslint-enable dot-notation */ @@ -1945,12 +1802,8 @@ export class BlockTxidsResponse extends BaseModel { // 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}` - ); return new BlockTxidsResponse({ - blocktxids: data['blockTxids'], + blocktxids: data['blockTxids'] ?? [], }); /* eslint-enable dot-notation */ } @@ -2005,16 +1858,10 @@ export class Box extends BaseModel { // 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}`); return new Box({ - name: data['name'], - round: data['round'], - value: data['value'], + name: data['name'] ?? new Uint8Array(), + round: data['round'] ?? 0, + value: data['value'] ?? new Uint8Array(), }); /* eslint-enable dot-notation */ } @@ -2045,10 +1892,8 @@ export class BoxDescriptor extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): BoxDescriptor { /* eslint-disable dot-notation */ - if (typeof data['name'] === 'undefined') - throw new Error(`Response is missing required field 'name': ${data}`); return new BoxDescriptor({ - name: data['name'], + name: data['name'] ?? new Uint8Array(), }); /* eslint-enable dot-notation */ } @@ -2093,13 +1938,9 @@ export class BoxReference extends BaseModel { // 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}`); return new BoxReference({ - app: data['app'], - name: data['name'], + app: data['app'] ?? 0, + name: data['name'] ?? new Uint8Array(), }); /* eslint-enable dot-notation */ } @@ -2127,12 +1968,8 @@ export class BoxesResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): BoxesResponse { /* eslint-disable dot-notation */ - if (!Array.isArray(data['boxes'])) - throw new Error( - `Response is missing required array field 'boxes': ${data}` - ); return new BoxesResponse({ - boxes: data['boxes'].map(BoxDescriptor.from_obj_for_encoding), + boxes: (data['boxes'] ?? []).map(BoxDescriptor.from_obj_for_encoding), }); /* eslint-enable dot-notation */ } @@ -2196,29 +2033,13 @@ export class BuildVersion extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): BuildVersion { /* eslint-disable dot-notation */ - if (typeof data['branch'] === 'undefined') - throw new Error(`Response is missing required field 'branch': ${data}`); - if (typeof data['build_number'] === 'undefined') - throw new Error( - `Response is missing required field 'build_number': ${data}` - ); - if (typeof data['channel'] === 'undefined') - throw new Error(`Response is missing required field 'channel': ${data}`); - if (typeof data['commit_hash'] === 'undefined') - throw new Error( - `Response is missing required field 'commit_hash': ${data}` - ); - if (typeof data['major'] === 'undefined') - throw new Error(`Response is missing required field 'major': ${data}`); - if (typeof data['minor'] === 'undefined') - throw new Error(`Response is missing required field 'minor': ${data}`); return new BuildVersion({ - branch: data['branch'], - buildNumber: data['build_number'], - channel: data['channel'], - commitHash: data['commit_hash'], - major: data['major'], - minor: data['minor'], + branch: data['branch'] ?? '', + buildNumber: data['build_number'] ?? 0, + channel: data['channel'] ?? '', + commitHash: data['commit_hash'] ?? '', + major: data['major'] ?? 0, + minor: data['minor'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -2273,13 +2094,9 @@ export class CompileResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): CompileResponse { /* eslint-disable dot-notation */ - if (typeof data['hash'] === 'undefined') - throw new Error(`Response is missing required field 'hash': ${data}`); - if (typeof data['result'] === 'undefined') - throw new Error(`Response is missing required field 'result': ${data}`); return new CompileResponse({ - hash: data['hash'], - result: data['result'], + hash: data['hash'] ?? '', + result: data['result'] ?? '', sourcemap: data['sourcemap'], }); /* eslint-enable dot-notation */ @@ -2311,10 +2128,8 @@ export class DisassembleResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): DisassembleResponse { /* eslint-disable dot-notation */ - if (typeof data['result'] === 'undefined') - throw new Error(`Response is missing required field 'result': ${data}`); return new DisassembleResponse({ - result: data['result'], + result: data['result'] ?? '', }); /* eslint-enable dot-notation */ } @@ -2404,40 +2219,14 @@ export class DryrunRequest extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): DryrunRequest { /* eslint-disable dot-notation */ - if (!Array.isArray(data['accounts'])) - throw new Error( - `Response is missing required array field 'accounts': ${data}` - ); - if (!Array.isArray(data['apps'])) - throw new Error( - `Response is missing required array field 'apps': ${data}` - ); - if (typeof data['latest-timestamp'] === 'undefined') - throw new Error( - `Response is missing required field 'latest-timestamp': ${data}` - ); - if (typeof data['protocol-version'] === 'undefined') - throw new Error( - `Response is missing required field 'protocol-version': ${data}` - ); - if (typeof data['round'] === 'undefined') - throw new Error(`Response is missing required field 'round': ${data}`); - if (!Array.isArray(data['sources'])) - throw new Error( - `Response is missing required array field 'sources': ${data}` - ); - if (!Array.isArray(data['txns'])) - throw new Error( - `Response is missing required array field 'txns': ${data}` - ); return new DryrunRequest({ - accounts: data['accounts'].map(Account.from_obj_for_encoding), - apps: data['apps'].map(Application.from_obj_for_encoding), - latestTimestamp: data['latest-timestamp'], - protocolVersion: data['protocol-version'], - round: data['round'], - sources: data['sources'].map(DryrunSource.from_obj_for_encoding), - txns: data['txns'], + accounts: (data['accounts'] ?? []).map(Account.from_obj_for_encoding), + apps: (data['apps'] ?? []).map(Application.from_obj_for_encoding), + latestTimestamp: data['latest-timestamp'] ?? 0, + protocolVersion: data['protocol-version'] ?? '', + round: data['round'] ?? 0, + sources: (data['sources'] ?? []).map(DryrunSource.from_obj_for_encoding), + txns: data['txns'] ?? [], }); /* eslint-enable dot-notation */ } @@ -2486,20 +2275,10 @@ export class DryrunResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): DryrunResponse { /* eslint-disable dot-notation */ - if (typeof data['error'] === 'undefined') - throw new Error(`Response is missing required field 'error': ${data}`); - if (typeof data['protocol-version'] === 'undefined') - throw new Error( - `Response is missing required field 'protocol-version': ${data}` - ); - if (!Array.isArray(data['txns'])) - throw new Error( - `Response is missing required array field 'txns': ${data}` - ); return new DryrunResponse({ - error: data['error'], - protocolVersion: data['protocol-version'], - txns: data['txns'].map(DryrunTxnResult.from_obj_for_encoding), + error: data['error'] ?? '', + protocolVersion: data['protocol-version'] ?? '', + txns: (data['txns'] ?? []).map(DryrunTxnResult.from_obj_for_encoding), }); /* eslint-enable dot-notation */ } @@ -2560,25 +2339,11 @@ export class DryrunSource extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): DryrunSource { /* eslint-disable dot-notation */ - if (typeof data['app-index'] === 'undefined') - throw new Error( - `Response is missing required field 'app-index': ${data}` - ); - 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}` - ); return new DryrunSource({ - appIndex: data['app-index'], - fieldName: data['field-name'], - source: data['source'], - txnIndex: data['txn-index'], + appIndex: data['app-index'] ?? 0, + fieldName: data['field-name'] ?? '', + source: data['source'] ?? '', + txnIndex: data['txn-index'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -2647,18 +2412,10 @@ export class DryrunState extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): DryrunState { /* eslint-disable dot-notation */ - if (typeof data['line'] === 'undefined') - throw new Error(`Response is missing required field 'line': ${data}`); - if (typeof data['pc'] === 'undefined') - throw new Error(`Response is missing required field 'pc': ${data}`); - if (!Array.isArray(data['stack'])) - throw new Error( - `Response is missing required array field 'stack': ${data}` - ); return new DryrunState({ - line: data['line'], - pc: data['pc'], - stack: data['stack'].map(TealValue.from_obj_for_encoding), + line: data['line'] ?? 0, + pc: data['pc'] ?? 0, + stack: (data['stack'] ?? []).map(TealValue.from_obj_for_encoding), error: data['error'], scratch: typeof data['scratch'] !== 'undefined' @@ -2787,12 +2544,8 @@ export class DryrunTxnResult extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): DryrunTxnResult { /* eslint-disable dot-notation */ - if (!Array.isArray(data['disassembly'])) - throw new Error( - `Response is missing required array field 'disassembly': ${data}` - ); return new DryrunTxnResult({ - disassembly: data['disassembly'], + disassembly: data['disassembly'] ?? [], appCallMessages: data['app-call-messages'], appCallTrace: typeof data['app-call-trace'] !== 'undefined' @@ -2853,10 +2606,8 @@ export class ErrorResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): ErrorResponse { /* eslint-disable dot-notation */ - if (typeof data['message'] === 'undefined') - throw new Error(`Response is missing required field 'message': ${data}`); return new ErrorResponse({ - message: data['message'], + message: data['message'] ?? '', data: data['data'], }); /* eslint-enable dot-notation */ @@ -2912,10 +2663,8 @@ export class EvalDelta extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): EvalDelta { /* eslint-disable dot-notation */ - if (typeof data['action'] === 'undefined') - throw new Error(`Response is missing required field 'action': ${data}`); return new EvalDelta({ - action: data['action'], + action: data['action'] ?? 0, bytes: data['bytes'], uint: data['uint'], }); @@ -2953,13 +2702,9 @@ export class EvalDeltaKeyValue extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): EvalDeltaKeyValue { /* eslint-disable dot-notation */ - if (typeof data['key'] === 'undefined') - throw new Error(`Response is missing required field 'key': ${data}`); - if (typeof data['value'] === 'undefined') - throw new Error(`Response is missing required field 'value': ${data}`); return new EvalDeltaKeyValue({ - key: data['key'], - value: EvalDelta.from_obj_for_encoding(data['value']), + key: data['key'] ?? '', + value: EvalDelta.from_obj_for_encoding(data['value'] ?? {}), }); /* eslint-enable dot-notation */ } @@ -2992,10 +2737,8 @@ export class GetBlockTimeStampOffsetResponse extends BaseModel { data: Record ): GetBlockTimeStampOffsetResponse { /* eslint-disable dot-notation */ - if (typeof data['offset'] === 'undefined') - throw new Error(`Response is missing required field 'offset': ${data}`); return new GetBlockTimeStampOffsetResponse({ - offset: data['offset'], + offset: data['offset'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -3028,10 +2771,8 @@ export class GetSyncRoundResponse extends BaseModel { data: Record ): GetSyncRoundResponse { /* eslint-disable dot-notation */ - if (typeof data['round'] === 'undefined') - throw new Error(`Response is missing required field 'round': ${data}`); return new GetSyncRoundResponse({ - round: data['round'], + round: data['round'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -3117,15 +2858,9 @@ export class LedgerStateDeltaForTransactionGroup extends BaseModel { data: Record ): LedgerStateDeltaForTransactionGroup { /* eslint-disable dot-notation */ - if (typeof data['Delta'] === 'undefined') - throw new Error(`Response is missing required field 'Delta': ${data}`); - if (!Array.isArray(data['Ids'])) - throw new Error( - `Response is missing required array field 'Ids': ${data}` - ); return new LedgerStateDeltaForTransactionGroup({ - delta: data['Delta'], - ids: data['Ids'], + delta: data['Delta'] ?? {}, + ids: data['Ids'] ?? [], }); /* eslint-enable dot-notation */ } @@ -3184,18 +2919,10 @@ export class LightBlockHeaderProof extends BaseModel { data: Record ): LightBlockHeaderProof { /* eslint-disable dot-notation */ - if (typeof data['index'] === 'undefined') - throw new Error(`Response is missing required field 'index': ${data}`); - if (typeof data['proof'] === 'undefined') - throw new Error(`Response is missing required field 'proof': ${data}`); - if (typeof data['treedepth'] === 'undefined') - throw new Error( - `Response is missing required field 'treedepth': ${data}` - ); return new LightBlockHeaderProof({ - index: data['index'], - proof: data['proof'], - treedepth: data['treedepth'], + index: data['index'] ?? 0, + proof: data['proof'] ?? new Uint8Array(), + treedepth: data['treedepth'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -3541,47 +3268,15 @@ export class NodeStatusResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): NodeStatusResponse { /* eslint-disable dot-notation */ - if (typeof data['catchup-time'] === 'undefined') - throw new Error( - `Response is missing required field 'catchup-time': ${data}` - ); - if (typeof data['last-round'] === 'undefined') - throw new Error( - `Response is missing required field 'last-round': ${data}` - ); - if (typeof data['last-version'] === 'undefined') - throw new Error( - `Response is missing required field 'last-version': ${data}` - ); - if (typeof data['next-version'] === 'undefined') - throw new Error( - `Response is missing required field 'next-version': ${data}` - ); - if (typeof data['next-version-round'] === 'undefined') - throw new Error( - `Response is missing required field 'next-version-round': ${data}` - ); - if (typeof data['next-version-supported'] === 'undefined') - throw new Error( - `Response is missing required field 'next-version-supported': ${data}` - ); - if (typeof data['stopped-at-unsupported-round'] === 'undefined') - throw new Error( - `Response is missing required field 'stopped-at-unsupported-round': ${data}` - ); - if (typeof data['time-since-last-round'] === 'undefined') - throw new Error( - `Response is missing required field 'time-since-last-round': ${data}` - ); return new NodeStatusResponse({ - catchupTime: data['catchup-time'], - lastRound: data['last-round'], - lastVersion: data['last-version'], - nextVersion: data['next-version'], - nextVersionRound: data['next-version-round'], - nextVersionSupported: data['next-version-supported'], - stoppedAtUnsupportedRound: data['stopped-at-unsupported-round'], - timeSinceLastRound: data['time-since-last-round'], + catchupTime: data['catchup-time'] ?? 0, + lastRound: data['last-round'] ?? 0, + lastVersion: data['last-version'] ?? '', + nextVersion: data['next-version'] ?? '', + nextVersionRound: data['next-version-round'] ?? 0, + nextVersionSupported: data['next-version-supported'] ?? false, + stoppedAtUnsupportedRound: data['stopped-at-unsupported-round'] ?? false, + timeSinceLastRound: data['time-since-last-round'] ?? 0, catchpoint: data['catchpoint'], catchpointAcquiredBlocks: data['catchpoint-acquired-blocks'], catchpointProcessedAccounts: data['catchpoint-processed-accounts'], @@ -3799,15 +3494,9 @@ export class PendingTransactionResponse extends BaseModel { data: Record ): PendingTransactionResponse { /* eslint-disable dot-notation */ - if (typeof data['pool-error'] === 'undefined') - throw new Error( - `Response is missing required field 'pool-error': ${data}` - ); - if (typeof data['txn'] === 'undefined') - throw new Error(`Response is missing required field 'txn': ${data}`); return new PendingTransactionResponse({ - poolError: data['pool-error'], - txn: data['txn'], + poolError: data['pool-error'] ?? '', + txn: data['txn'] ?? {}, applicationIndex: data['application-index'], assetClosingAmount: data['asset-closing-amount'], assetIndex: data['asset-index'], @@ -3883,17 +3572,9 @@ export class PendingTransactionsResponse extends BaseModel { data: Record ): PendingTransactionsResponse { /* eslint-disable dot-notation */ - if (!Array.isArray(data['top-transactions'])) - throw new Error( - `Response is missing required array field 'top-transactions': ${data}` - ); - if (typeof data['total-transactions'] === 'undefined') - throw new Error( - `Response is missing required field 'total-transactions': ${data}` - ); return new PendingTransactionsResponse({ - topTransactions: data['top-transactions'], - totalTransactions: data['total-transactions'], + topTransactions: data['top-transactions'] ?? [], + totalTransactions: data['total-transactions'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -3926,10 +3607,8 @@ export class PostTransactionsResponse extends BaseModel { data: Record ): PostTransactionsResponse { /* eslint-disable dot-notation */ - if (typeof data['txId'] === 'undefined') - throw new Error(`Response is missing required field 'txId': ${data}`); return new PostTransactionsResponse({ - txid: data['txId'], + txid: data['txId'] ?? '', }); /* eslint-enable dot-notation */ } @@ -3974,15 +3653,9 @@ export class ScratchChange extends BaseModel { // 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}`); return new ScratchChange({ - newValue: AvmValue.from_obj_for_encoding(data['new-value']), - slot: data['slot'], + newValue: AvmValue.from_obj_for_encoding(data['new-value'] ?? {}), + slot: data['slot'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -4133,12 +3806,8 @@ export class SimulateRequest extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): SimulateRequest { /* eslint-disable dot-notation */ - if (!Array.isArray(data['txn-groups'])) - throw new Error( - `Response is missing required array field 'txn-groups': ${data}` - ); return new SimulateRequest({ - txnGroups: data['txn-groups'].map( + txnGroups: (data['txn-groups'] ?? []).map( SimulateRequestTransactionGroup.from_obj_for_encoding ), allowEmptySignatures: data['allow-empty-signatures'], @@ -4182,12 +3851,8 @@ export class SimulateRequestTransactionGroup extends BaseModel { data: Record ): SimulateRequestTransactionGroup { /* eslint-disable dot-notation */ - if (!Array.isArray(data['txns'])) - throw new Error( - `Response is missing required array field 'txns': ${data}` - ); return new SimulateRequestTransactionGroup({ - txns: data['txns'], + txns: data['txns'] ?? [], }); /* eslint-enable dot-notation */ } @@ -4278,22 +3943,12 @@ export class SimulateResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): SimulateResponse { /* eslint-disable dot-notation */ - if (typeof data['last-round'] === 'undefined') - throw new Error( - `Response is missing required field 'last-round': ${data}` - ); - if (!Array.isArray(data['txn-groups'])) - throw new Error( - `Response is missing required array field 'txn-groups': ${data}` - ); - if (typeof data['version'] === 'undefined') - throw new Error(`Response is missing required field 'version': ${data}`); return new SimulateResponse({ - lastRound: data['last-round'], - txnGroups: data['txn-groups'].map( + lastRound: data['last-round'] ?? 0, + txnGroups: (data['txn-groups'] ?? []).map( SimulateTransactionGroupResult.from_obj_for_encoding ), - version: data['version'], + version: data['version'] ?? 0, evalOverrides: typeof data['eval-overrides'] !== 'undefined' ? SimulationEvalOverrides.from_obj_for_encoding( @@ -4502,12 +4157,8 @@ export class SimulateTransactionGroupResult extends BaseModel { data: Record ): SimulateTransactionGroupResult { /* eslint-disable dot-notation */ - if (!Array.isArray(data['txn-results'])) - throw new Error( - `Response is missing required array field 'txn-results': ${data}` - ); return new SimulateTransactionGroupResult({ - txnResults: data['txn-results'].map( + txnResults: (data['txn-results'] ?? []).map( SimulateTransactionResult.from_obj_for_encoding ), appBudgetAdded: data['app-budget-added'], @@ -4624,13 +4275,9 @@ export class SimulateTransactionResult extends BaseModel { data: Record ): SimulateTransactionResult { /* eslint-disable dot-notation */ - if (typeof data['txn-result'] === 'undefined') - throw new Error( - `Response is missing required field 'txn-result': ${data}` - ); return new SimulateTransactionResult({ txnResult: PendingTransactionResponse.from_obj_for_encoding( - data['txn-result'] + data['txn-result'] ?? {} ), appBudgetConsumed: data['app-budget-consumed'], execTrace: @@ -4973,10 +4620,8 @@ export class SimulationOpcodeTraceUnit extends BaseModel { data: Record ): SimulationOpcodeTraceUnit { /* eslint-disable dot-notation */ - if (typeof data['pc'] === 'undefined') - throw new Error(`Response is missing required field 'pc': ${data}`); return new SimulationOpcodeTraceUnit({ - pc: data['pc'], + pc: data['pc'] ?? 0, scratchChanges: typeof data['scratch-changes'] !== 'undefined' ? data['scratch-changes'].map(ScratchChange.from_obj_for_encoding) @@ -5202,15 +4847,9 @@ export class StateProof extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): StateProof { /* eslint-disable dot-notation */ - if (typeof data['Message'] === 'undefined') - throw new Error(`Response is missing required field 'Message': ${data}`); - if (typeof data['StateProof'] === 'undefined') - throw new Error( - `Response is missing required field 'StateProof': ${data}` - ); return new StateProof({ - message: StateProofMessage.from_obj_for_encoding(data['Message']), - stateproof: data['StateProof'], + message: StateProofMessage.from_obj_for_encoding(data['Message'] ?? {}), + stateproof: data['StateProof'] ?? new Uint8Array(), }); /* eslint-enable dot-notation */ } @@ -5295,32 +4934,13 @@ export class StateProofMessage extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): StateProofMessage { /* eslint-disable dot-notation */ - if (typeof data['BlockHeadersCommitment'] === 'undefined') - throw new Error( - `Response is missing required field 'BlockHeadersCommitment': ${data}` - ); - if (typeof data['FirstAttestedRound'] === 'undefined') - throw new Error( - `Response is missing required field 'FirstAttestedRound': ${data}` - ); - if (typeof data['LastAttestedRound'] === 'undefined') - throw new Error( - `Response is missing required field 'LastAttestedRound': ${data}` - ); - if (typeof data['LnProvenWeight'] === 'undefined') - throw new Error( - `Response is missing required field 'LnProvenWeight': ${data}` - ); - if (typeof data['VotersCommitment'] === 'undefined') - throw new Error( - `Response is missing required field 'VotersCommitment': ${data}` - ); return new StateProofMessage({ - blockheaderscommitment: data['BlockHeadersCommitment'], - firstattestedround: data['FirstAttestedRound'], - lastattestedround: data['LastAttestedRound'], - lnprovenweight: data['LnProvenWeight'], - voterscommitment: data['VotersCommitment'], + blockheaderscommitment: + data['BlockHeadersCommitment'] ?? new Uint8Array(), + firstattestedround: data['FirstAttestedRound'] ?? 0, + lastattestedround: data['LastAttestedRound'] ?? 0, + lnprovenweight: data['LnProvenWeight'] ?? 0, + voterscommitment: data['VotersCommitment'] ?? new Uint8Array(), }); /* eslint-enable dot-notation */ } @@ -5375,22 +4995,10 @@ export class SupplyResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): SupplyResponse { /* eslint-disable dot-notation */ - if (typeof data['current_round'] === 'undefined') - throw new Error( - `Response is missing required field 'current_round': ${data}` - ); - if (typeof data['online-money'] === 'undefined') - throw new Error( - `Response is missing required field 'online-money': ${data}` - ); - if (typeof data['total-money'] === 'undefined') - throw new Error( - `Response is missing required field 'total-money': ${data}` - ); return new SupplyResponse({ - currentRound: data['current_round'], - onlineMoney: data['online-money'], - totalMoney: data['total-money'], + currentRound: data['current_round'] ?? 0, + onlineMoney: data['online-money'] ?? 0, + totalMoney: data['total-money'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -5426,13 +5034,9 @@ export class TealKeyValue extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): TealKeyValue { /* eslint-disable dot-notation */ - if (typeof data['key'] === 'undefined') - throw new Error(`Response is missing required field 'key': ${data}`); - if (typeof data['value'] === 'undefined') - throw new Error(`Response is missing required field 'value': ${data}`); return new TealKeyValue({ - key: data['key'], - value: TealValue.from_obj_for_encoding(data['value']), + key: data['key'] ?? '', + value: TealValue.from_obj_for_encoding(data['value'] ?? {}), }); /* eslint-enable dot-notation */ } @@ -5487,16 +5091,10 @@ export class TealValue extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): TealValue { /* eslint-disable dot-notation */ - if (typeof data['bytes'] === 'undefined') - throw new Error(`Response is missing required field 'bytes': ${data}`); - if (typeof data['type'] === 'undefined') - throw new Error(`Response is missing required field 'type': ${data}`); - if (typeof data['uint'] === 'undefined') - throw new Error(`Response is missing required field 'uint': ${data}`); return new TealValue({ - bytes: data['bytes'], - type: data['type'], - uint: data['uint'], + bytes: data['bytes'] ?? '', + type: data['type'] ?? 0, + uint: data['uint'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -5527,12 +5125,8 @@ export class TransactionGroupLedgerStateDeltasForRoundResponse extends BaseModel data: Record ): TransactionGroupLedgerStateDeltasForRoundResponse { /* eslint-disable dot-notation */ - if (!Array.isArray(data['Deltas'])) - throw new Error( - `Response is missing required array field 'Deltas': ${data}` - ); return new TransactionGroupLedgerStateDeltasForRoundResponse({ - deltas: data['Deltas'].map( + deltas: (data['Deltas'] ?? []).map( LedgerStateDeltaForTransactionGroup.from_obj_for_encoding ), }); @@ -5635,33 +5229,13 @@ export class TransactionParametersResponse extends BaseModel { data: Record ): TransactionParametersResponse { /* eslint-disable dot-notation */ - if (typeof data['consensus-version'] === 'undefined') - throw new Error( - `Response is missing required field 'consensus-version': ${data}` - ); - if (typeof data['fee'] === 'undefined') - throw new Error(`Response is missing required field 'fee': ${data}`); - if (typeof data['genesis-hash'] === 'undefined') - throw new Error( - `Response is missing required field 'genesis-hash': ${data}` - ); - if (typeof data['genesis-id'] === 'undefined') - throw new Error( - `Response is missing required field 'genesis-id': ${data}` - ); - if (typeof data['last-round'] === 'undefined') - throw new Error( - `Response is missing required field 'last-round': ${data}` - ); - if (typeof data['min-fee'] === 'undefined') - throw new Error(`Response is missing required field 'min-fee': ${data}`); return new TransactionParametersResponse({ - consensusVersion: data['consensus-version'], - fee: data['fee'], - genesisHash: data['genesis-hash'], - genesisId: data['genesis-id'], - lastRound: data['last-round'], - minFee: data['min-fee'], + consensusVersion: data['consensus-version'] ?? '', + fee: data['fee'] ?? 0, + genesisHash: data['genesis-hash'] ?? new Uint8Array(), + genesisId: data['genesis-id'] ?? '', + lastRound: data['last-round'] ?? 0, + minFee: data['min-fee'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -5745,21 +5319,11 @@ export class TransactionProofResponse extends BaseModel { data: Record ): TransactionProofResponse { /* eslint-disable dot-notation */ - if (typeof data['idx'] === 'undefined') - throw new Error(`Response is missing required field 'idx': ${data}`); - if (typeof data['proof'] === 'undefined') - throw new Error(`Response is missing required field 'proof': ${data}`); - if (typeof data['stibhash'] === 'undefined') - throw new Error(`Response is missing required field 'stibhash': ${data}`); - if (typeof data['treedepth'] === 'undefined') - throw new Error( - `Response is missing required field 'treedepth': ${data}` - ); return new TransactionProofResponse({ - idx: data['idx'], - proof: data['proof'], - stibhash: data['stibhash'], - treedepth: data['treedepth'], + idx: data['idx'] ?? 0, + proof: data['proof'] ?? new Uint8Array(), + stibhash: data['stibhash'] ?? new Uint8Array(), + treedepth: data['treedepth'] ?? 0, hashtype: data['hashtype'], }); /* eslint-enable dot-notation */ @@ -5816,25 +5380,11 @@ export class Version extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): Version { /* eslint-disable dot-notation */ - if (typeof data['build'] === 'undefined') - throw new Error(`Response is missing required field 'build': ${data}`); - if (typeof data['genesis_hash_b64'] === 'undefined') - throw new Error( - `Response is missing required field 'genesis_hash_b64': ${data}` - ); - if (typeof data['genesis_id'] === 'undefined') - throw new Error( - `Response is missing required field 'genesis_id': ${data}` - ); - if (!Array.isArray(data['versions'])) - throw new Error( - `Response is missing required array field 'versions': ${data}` - ); return new Version({ - build: BuildVersion.from_obj_for_encoding(data['build']), - genesisHashB64: data['genesis_hash_b64'], - genesisId: data['genesis_id'], - versions: data['versions'], + build: BuildVersion.from_obj_for_encoding(data['build'] ?? {}), + genesisHashB64: data['genesis_hash_b64'] ?? new Uint8Array(), + genesisId: data['genesis_id'] ?? '', + versions: data['versions'] ?? [], }); /* eslint-enable dot-notation */ } diff --git a/src/client/v2/algod/suggestedParams.ts b/src/client/v2/algod/suggestedParams.ts index cde86875a..20912d440 100644 --- a/src/client/v2/algod/suggestedParams.ts +++ b/src/client/v2/algod/suggestedParams.ts @@ -17,6 +17,11 @@ export interface SuggestedParamsFromAlgod extends SuggestedParams { lastValid: bigint; genesisID: string; genesisHash: Uint8Array; + + /** + * TODO description + */ + consensusVersion: string; } /** @@ -40,6 +45,7 @@ export default class SuggestedParamsRequest extends JSONRequest< genesisID: body['genesis-id'], genesisHash: base64ToBytes(body['genesis-hash']), minFee: BigInt(body['min-fee']), + consensusVersion: body['consensus-version'], }; } /* eslint-enable class-methods-use-this */ diff --git a/src/client/v2/indexer/models/types.ts b/src/client/v2/indexer/models/types.ts index 390edf56c..a69b9d1e8 100644 --- a/src/client/v2/indexer/models/types.ts +++ b/src/client/v2/indexer/models/types.ts @@ -28,6 +28,12 @@ export class Account extends BaseModel { */ public amountWithoutPendingRewards: bigint; + /** + * MicroAlgo balance required by the account. + * The requirement grows based on asset and application usage. + */ + public minBalance: number; + /** * amount of MicroAlgos of pending rewards in this account. */ @@ -173,6 +179,8 @@ export class Account extends BaseModel { * @param address - the account public key * @param amount - (algo) total number of MicroAlgos in the account * @param amountWithoutPendingRewards - specifies the amount of MicroAlgos in the account, without the pending rewards. + * @param minBalance - MicroAlgo balance required by the account. + * The requirement grows based on asset and application usage. * @param pendingRewards - amount of MicroAlgos of pending rewards in this account. * @param rewards - (ern) total rewards of MicroAlgos the account has received, including pending * rewards. @@ -226,6 +234,7 @@ export class Account extends BaseModel { address, amount, amountWithoutPendingRewards, + minBalance, pendingRewards, rewards, round, @@ -253,6 +262,7 @@ export class Account extends BaseModel { address: string; amount: number | bigint; amountWithoutPendingRewards: number | bigint; + minBalance: number | bigint; pendingRewards: number | bigint; rewards: number | bigint; round: number | bigint; @@ -283,6 +293,7 @@ export class Account extends BaseModel { this.amountWithoutPendingRewards = ensureBigInt( amountWithoutPendingRewards ); + this.minBalance = ensureSafeInteger(minBalance); this.pendingRewards = ensureBigInt(pendingRewards); this.rewards = ensureBigInt(rewards); this.round = ensureBigInt(round); @@ -321,6 +332,7 @@ export class Account extends BaseModel { address: 'address', amount: 'amount', amountWithoutPendingRewards: 'amount-without-pending-rewards', + minBalance: 'min-balance', pendingRewards: 'pending-rewards', rewards: 'rewards', round: 'round', @@ -350,62 +362,21 @@ export class Account extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): Account { /* eslint-disable dot-notation */ - if (typeof data['address'] === 'undefined') - throw new Error(`Response is missing required field 'address': ${data}`); - if (typeof data['amount'] === 'undefined') - throw new Error(`Response is missing required field 'amount': ${data}`); - if (typeof data['amount-without-pending-rewards'] === 'undefined') - throw new Error( - `Response is missing required field 'amount-without-pending-rewards': ${data}` - ); - if (typeof data['pending-rewards'] === 'undefined') - throw new Error( - `Response is missing required field 'pending-rewards': ${data}` - ); - if (typeof data['rewards'] === 'undefined') - throw new Error(`Response is missing required field 'rewards': ${data}`); - if (typeof data['round'] === 'undefined') - throw new Error(`Response is missing required field 'round': ${data}`); - if (typeof data['status'] === 'undefined') - throw new Error(`Response is missing required field 'status': ${data}`); - if (typeof data['total-apps-opted-in'] === 'undefined') - throw new Error( - `Response is missing required field 'total-apps-opted-in': ${data}` - ); - if (typeof data['total-assets-opted-in'] === 'undefined') - throw new Error( - `Response is missing required field 'total-assets-opted-in': ${data}` - ); - if (typeof data['total-box-bytes'] === 'undefined') - throw new Error( - `Response is missing required field 'total-box-bytes': ${data}` - ); - if (typeof data['total-boxes'] === 'undefined') - throw new Error( - `Response is missing required field 'total-boxes': ${data}` - ); - if (typeof data['total-created-apps'] === 'undefined') - throw new Error( - `Response is missing required field 'total-created-apps': ${data}` - ); - if (typeof data['total-created-assets'] === 'undefined') - throw new Error( - `Response is missing required field 'total-created-assets': ${data}` - ); return new Account({ - address: data['address'], - amount: data['amount'], - amountWithoutPendingRewards: data['amount-without-pending-rewards'], - pendingRewards: data['pending-rewards'], - rewards: data['rewards'], - round: data['round'], - status: data['status'], - totalAppsOptedIn: data['total-apps-opted-in'], - totalAssetsOptedIn: data['total-assets-opted-in'], - totalBoxBytes: data['total-box-bytes'], - totalBoxes: data['total-boxes'], - totalCreatedApps: data['total-created-apps'], - totalCreatedAssets: data['total-created-assets'], + address: data['address'] ?? '', + amount: data['amount'] ?? 0, + amountWithoutPendingRewards: data['amount-without-pending-rewards'] ?? 0, + minBalance: data['min-balance'] ?? 0, + pendingRewards: data['pending-rewards'] ?? 0, + rewards: data['rewards'] ?? 0, + round: data['round'] ?? 0, + status: data['status'] ?? '', + totalAppsOptedIn: data['total-apps-opted-in'] ?? 0, + totalAssetsOptedIn: data['total-assets-opted-in'] ?? 0, + totalBoxBytes: data['total-box-bytes'] ?? 0, + totalBoxes: data['total-boxes'] ?? 0, + totalCreatedApps: data['total-created-apps'] ?? 0, + totalCreatedAssets: data['total-created-assets'] ?? 0, appsLocalState: typeof data['apps-local-state'] !== 'undefined' ? data['apps-local-state'].map( @@ -539,32 +510,13 @@ export class AccountParticipation extends BaseModel { data: Record ): AccountParticipation { /* eslint-disable dot-notation */ - if (typeof data['selection-participation-key'] === 'undefined') - throw new Error( - `Response is missing required field 'selection-participation-key': ${data}` - ); - if (typeof data['vote-first-valid'] === 'undefined') - throw new Error( - `Response is missing required field 'vote-first-valid': ${data}` - ); - if (typeof data['vote-key-dilution'] === 'undefined') - throw new Error( - `Response is missing required field 'vote-key-dilution': ${data}` - ); - if (typeof data['vote-last-valid'] === 'undefined') - throw new Error( - `Response is missing required field 'vote-last-valid': ${data}` - ); - if (typeof data['vote-participation-key'] === 'undefined') - throw new Error( - `Response is missing required field 'vote-participation-key': ${data}` - ); return new AccountParticipation({ - selectionParticipationKey: data['selection-participation-key'], - voteFirstValid: data['vote-first-valid'], - voteKeyDilution: data['vote-key-dilution'], - voteLastValid: data['vote-last-valid'], - voteParticipationKey: data['vote-participation-key'], + selectionParticipationKey: + data['selection-participation-key'] ?? new Uint8Array(), + voteFirstValid: data['vote-first-valid'] ?? 0, + voteKeyDilution: data['vote-key-dilution'] ?? 0, + voteLastValid: data['vote-last-valid'] ?? 0, + voteParticipationKey: data['vote-participation-key'] ?? new Uint8Array(), stateProofKey: data['state-proof-key'], }); /* eslint-enable dot-notation */ @@ -614,15 +566,9 @@ export class AccountResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): AccountResponse { /* eslint-disable dot-notation */ - if (typeof data['account'] === 'undefined') - throw new Error(`Response is missing required field 'account': ${data}`); - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); return new AccountResponse({ - account: Account.from_obj_for_encoding(data['account']), - currentRound: data['current-round'], + account: Account.from_obj_for_encoding(data['account'] ?? {}), + currentRound: data['current-round'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -664,15 +610,9 @@ export class AccountStateDelta extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): AccountStateDelta { /* eslint-disable dot-notation */ - if (typeof data['address'] === 'undefined') - throw new Error(`Response is missing required field 'address': ${data}`); - if (!Array.isArray(data['delta'])) - throw new Error( - `Response is missing required array field 'delta': ${data}` - ); return new AccountStateDelta({ - address: data['address'], - delta: data['delta'].map(EvalDeltaKeyValue.from_obj_for_encoding), + address: data['address'] ?? '', + delta: (data['delta'] ?? []).map(EvalDeltaKeyValue.from_obj_for_encoding), }); /* eslint-enable dot-notation */ } @@ -726,17 +666,9 @@ export class AccountsResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): AccountsResponse { /* eslint-disable dot-notation */ - if (!Array.isArray(data['accounts'])) - throw new Error( - `Response is missing required array field 'accounts': ${data}` - ); - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); return new AccountsResponse({ - accounts: data['accounts'].map(Account.from_obj_for_encoding), - currentRound: data['current-round'], + accounts: (data['accounts'] ?? []).map(Account.from_obj_for_encoding), + currentRound: data['current-round'] ?? 0, nextToken: data['next-token'], }); /* eslint-enable dot-notation */ @@ -818,13 +750,9 @@ export class Application extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): Application { /* eslint-disable dot-notation */ - if (typeof data['id'] === 'undefined') - throw new Error(`Response is missing required field 'id': ${data}`); - if (typeof data['params'] === 'undefined') - throw new Error(`Response is missing required field 'params': ${data}`); return new Application({ - id: data['id'], - params: ApplicationParams.from_obj_for_encoding(data['params']), + id: data['id'] ?? 0, + params: ApplicationParams.from_obj_for_encoding(data['params'] ?? {}), createdAtRound: data['created-at-round'], deleted: data['deleted'], deletedAtRound: data['deleted-at-round'], @@ -922,13 +850,11 @@ export class ApplicationLocalState extends BaseModel { data: Record ): ApplicationLocalState { /* eslint-disable dot-notation */ - if (typeof data['id'] === 'undefined') - throw new Error(`Response is missing required field 'id': ${data}`); - if (typeof data['schema'] === 'undefined') - throw new Error(`Response is missing required field 'schema': ${data}`); return new ApplicationLocalState({ - id: data['id'], - schema: ApplicationStateSchema.from_obj_for_encoding(data['schema']), + id: data['id'] ?? 0, + schema: ApplicationStateSchema.from_obj_for_encoding( + data['schema'] ?? {} + ), closedOutAtRound: data['closed-out-at-round'], deleted: data['deleted'], keyValue: @@ -991,19 +917,11 @@ export class ApplicationLocalStatesResponse extends BaseModel { data: Record ): ApplicationLocalStatesResponse { /* eslint-disable dot-notation */ - if (!Array.isArray(data['apps-local-states'])) - throw new Error( - `Response is missing required array field 'apps-local-states': ${data}` - ); - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); return new ApplicationLocalStatesResponse({ - appsLocalStates: data['apps-local-states'].map( + appsLocalStates: (data['apps-local-states'] ?? []).map( ApplicationLocalState.from_obj_for_encoding ), - currentRound: data['current-round'], + currentRound: data['current-round'] ?? 0, nextToken: data['next-token'], }); /* eslint-enable dot-notation */ @@ -1043,15 +961,9 @@ export class ApplicationLogData extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): ApplicationLogData { /* eslint-disable dot-notation */ - if (!Array.isArray(data['logs'])) - throw new Error( - `Response is missing required array field 'logs': ${data}` - ); - if (typeof data['txid'] === 'undefined') - throw new Error(`Response is missing required field 'txid': ${data}`); return new ApplicationLogData({ - logs: data['logs'], - txid: data['txid'], + logs: data['logs'] ?? [], + txid: data['txid'] ?? '', }); /* eslint-enable dot-notation */ } @@ -1117,17 +1029,9 @@ export class ApplicationLogsResponse extends BaseModel { data: Record ): ApplicationLogsResponse { /* eslint-disable dot-notation */ - if (typeof data['application-id'] === 'undefined') - throw new Error( - `Response is missing required field 'application-id': ${data}` - ); - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); return new ApplicationLogsResponse({ - applicationId: data['application-id'], - currentRound: data['current-round'], + applicationId: data['application-id'] ?? 0, + currentRound: data['current-round'] ?? 0, logData: typeof data['log-data'] !== 'undefined' ? data['log-data'].map(ApplicationLogData.from_obj_for_encoding) @@ -1238,17 +1142,9 @@ export class ApplicationParams extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): ApplicationParams { /* eslint-disable dot-notation */ - if (typeof data['approval-program'] === 'undefined') - throw new Error( - `Response is missing required field 'approval-program': ${data}` - ); - if (typeof data['clear-state-program'] === 'undefined') - throw new Error( - `Response is missing required field 'clear-state-program': ${data}` - ); return new ApplicationParams({ - approvalProgram: data['approval-program'], - clearStateProgram: data['clear-state-program'], + approvalProgram: data['approval-program'] ?? new Uint8Array(), + clearStateProgram: data['clear-state-program'] ?? new Uint8Array(), creator: data['creator'], extraProgramPages: data['extra-program-pages'], globalState: @@ -1311,12 +1207,8 @@ export class ApplicationResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): ApplicationResponse { /* eslint-disable dot-notation */ - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); return new ApplicationResponse({ - currentRound: data['current-round'], + currentRound: data['current-round'] ?? 0, application: typeof data['application'] !== 'undefined' ? Application.from_obj_for_encoding(data['application']) @@ -1367,15 +1259,9 @@ export class ApplicationStateSchema extends BaseModel { data: Record ): ApplicationStateSchema { /* eslint-disable dot-notation */ - if (typeof data['num-byte-slice'] === 'undefined') - throw new Error( - `Response is missing required field 'num-byte-slice': ${data}` - ); - if (typeof data['num-uint'] === 'undefined') - throw new Error(`Response is missing required field 'num-uint': ${data}`); return new ApplicationStateSchema({ - numByteSlice: data['num-byte-slice'], - numUint: data['num-uint'], + numByteSlice: data['num-byte-slice'] ?? 0, + numUint: data['num-uint'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -1431,17 +1317,11 @@ export class ApplicationsResponse extends BaseModel { data: Record ): ApplicationsResponse { /* eslint-disable dot-notation */ - if (!Array.isArray(data['applications'])) - throw new Error( - `Response is missing required array field 'applications': ${data}` - ); - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); return new ApplicationsResponse({ - applications: data['applications'].map(Application.from_obj_for_encoding), - currentRound: data['current-round'], + applications: (data['applications'] ?? []).map( + Application.from_obj_for_encoding + ), + currentRound: data['current-round'] ?? 0, nextToken: data['next-token'], }); /* eslint-enable dot-notation */ @@ -1529,13 +1409,9 @@ export class Asset extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): Asset { /* eslint-disable dot-notation */ - if (typeof data['index'] === 'undefined') - throw new Error(`Response is missing required field 'index': ${data}`); - if (typeof data['params'] === 'undefined') - throw new Error(`Response is missing required field 'params': ${data}`); return new Asset({ - index: data['index'], - params: AssetParams.from_obj_for_encoding(data['params']), + index: data['index'] ?? 0, + params: AssetParams.from_obj_for_encoding(data['params'] ?? {}), createdAtRound: data['created-at-round'], deleted: data['deleted'], destroyedAtRound: data['destroyed-at-round'], @@ -1594,17 +1470,11 @@ export class AssetBalancesResponse extends BaseModel { data: Record ): AssetBalancesResponse { /* eslint-disable dot-notation */ - if (!Array.isArray(data['balances'])) - throw new Error( - `Response is missing required array field 'balances': ${data}` - ); - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); return new AssetBalancesResponse({ - balances: data['balances'].map(MiniAssetHolding.from_obj_for_encoding), - currentRound: data['current-round'], + balances: (data['balances'] ?? []).map( + MiniAssetHolding.from_obj_for_encoding + ), + currentRound: data['current-round'] ?? 0, nextToken: data['next-token'], }); /* eslint-enable dot-notation */ @@ -1698,18 +1568,10 @@ export class AssetHolding extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): AssetHolding { /* eslint-disable dot-notation */ - if (typeof data['amount'] === 'undefined') - throw new Error(`Response is missing required field 'amount': ${data}`); - if (typeof data['asset-id'] === 'undefined') - throw new Error(`Response is missing required field 'asset-id': ${data}`); - if (typeof data['is-frozen'] === 'undefined') - throw new Error( - `Response is missing required field 'is-frozen': ${data}` - ); return new AssetHolding({ - amount: data['amount'], - assetId: data['asset-id'], - isFrozen: data['is-frozen'], + amount: data['amount'] ?? 0, + assetId: data['asset-id'] ?? 0, + isFrozen: data['is-frozen'] ?? false, deleted: data['deleted'], optedInAtRound: data['opted-in-at-round'], optedOutAtRound: data['opted-out-at-round'], @@ -1768,17 +1630,9 @@ export class AssetHoldingsResponse extends BaseModel { data: Record ): AssetHoldingsResponse { /* eslint-disable dot-notation */ - if (!Array.isArray(data['assets'])) - throw new Error( - `Response is missing required array field 'assets': ${data}` - ); - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); return new AssetHoldingsResponse({ - assets: data['assets'].map(AssetHolding.from_obj_for_encoding), - currentRound: data['current-round'], + assets: (data['assets'] ?? []).map(AssetHolding.from_obj_for_encoding), + currentRound: data['current-round'] ?? 0, nextToken: data['next-token'], }); /* eslint-enable dot-notation */ @@ -1986,16 +1840,10 @@ export class AssetParams extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): AssetParams { /* eslint-disable dot-notation */ - if (typeof data['creator'] === 'undefined') - throw new Error(`Response is missing required field 'creator': ${data}`); - if (typeof data['decimals'] === 'undefined') - throw new Error(`Response is missing required field 'decimals': ${data}`); - if (typeof data['total'] === 'undefined') - throw new Error(`Response is missing required field 'total': ${data}`); return new AssetParams({ - creator: data['creator'], - decimals: data['decimals'], - total: data['total'], + creator: data['creator'] ?? '', + decimals: data['decimals'] ?? 0, + total: data['total'] ?? 0, clawback: data['clawback'], defaultFrozen: data['default-frozen'], freeze: data['freeze'], @@ -2052,15 +1900,9 @@ export class AssetResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): AssetResponse { /* eslint-disable dot-notation */ - if (typeof data['asset'] === 'undefined') - throw new Error(`Response is missing required field 'asset': ${data}`); - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); return new AssetResponse({ - asset: Asset.from_obj_for_encoding(data['asset']), - currentRound: data['current-round'], + asset: Asset.from_obj_for_encoding(data['asset'] ?? {}), + currentRound: data['current-round'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -2114,17 +1956,9 @@ export class AssetsResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): AssetsResponse { /* eslint-disable dot-notation */ - if (!Array.isArray(data['assets'])) - throw new Error( - `Response is missing required array field 'assets': ${data}` - ); - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); return new AssetsResponse({ - assets: data['assets'].map(Asset.from_obj_for_encoding), - currentRound: data['current-round'], + assets: (data['assets'] ?? []).map(Asset.from_obj_for_encoding), + currentRound: data['current-round'] ?? 0, nextToken: data['next-token'], }); /* eslint-enable dot-notation */ @@ -2341,43 +2175,16 @@ export class Block extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): Block { /* eslint-disable dot-notation */ - if (typeof data['genesis-hash'] === 'undefined') - throw new Error( - `Response is missing required field 'genesis-hash': ${data}` - ); - if (typeof data['genesis-id'] === 'undefined') - throw new Error( - `Response is missing required field 'genesis-id': ${data}` - ); - if (typeof data['previous-block-hash'] === 'undefined') - throw new Error( - `Response is missing required field 'previous-block-hash': ${data}` - ); - if (typeof data['round'] === 'undefined') - throw new Error(`Response is missing required field 'round': ${data}`); - if (typeof data['seed'] === 'undefined') - throw new Error(`Response is missing required field 'seed': ${data}`); - if (typeof data['timestamp'] === 'undefined') - throw new Error( - `Response is missing required field 'timestamp': ${data}` - ); - if (typeof data['transactions-root'] === 'undefined') - throw new Error( - `Response is missing required field 'transactions-root': ${data}` - ); - if (typeof data['transactions-root-sha256'] === 'undefined') - throw new Error( - `Response is missing required field 'transactions-root-sha256': ${data}` - ); return new Block({ - genesisHash: data['genesis-hash'], - genesisId: data['genesis-id'], - previousBlockHash: data['previous-block-hash'], - round: data['round'], - seed: data['seed'], - timestamp: data['timestamp'], - transactionsRoot: data['transactions-root'], - transactionsRootSha256: data['transactions-root-sha256'], + genesisHash: data['genesis-hash'] ?? new Uint8Array(), + genesisId: data['genesis-id'] ?? '', + previousBlockHash: data['previous-block-hash'] ?? new Uint8Array(), + round: data['round'] ?? 0, + seed: data['seed'] ?? new Uint8Array(), + timestamp: data['timestamp'] ?? 0, + transactionsRoot: data['transactions-root'] ?? new Uint8Array(), + transactionsRootSha256: + data['transactions-root-sha256'] ?? new Uint8Array(), participationUpdates: typeof data['participation-updates'] !== 'undefined' ? ParticipationUpdates.from_obj_for_encoding( @@ -2501,35 +2308,13 @@ export class BlockRewards extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): BlockRewards { /* eslint-disable dot-notation */ - if (typeof data['fee-sink'] === 'undefined') - throw new Error(`Response is missing required field 'fee-sink': ${data}`); - if (typeof data['rewards-calculation-round'] === 'undefined') - throw new Error( - `Response is missing required field 'rewards-calculation-round': ${data}` - ); - if (typeof data['rewards-level'] === 'undefined') - throw new Error( - `Response is missing required field 'rewards-level': ${data}` - ); - if (typeof data['rewards-pool'] === 'undefined') - throw new Error( - `Response is missing required field 'rewards-pool': ${data}` - ); - if (typeof data['rewards-rate'] === 'undefined') - throw new Error( - `Response is missing required field 'rewards-rate': ${data}` - ); - if (typeof data['rewards-residue'] === 'undefined') - throw new Error( - `Response is missing required field 'rewards-residue': ${data}` - ); return new BlockRewards({ - feeSink: data['fee-sink'], - rewardsCalculationRound: data['rewards-calculation-round'], - rewardsLevel: data['rewards-level'], - rewardsPool: data['rewards-pool'], - rewardsRate: data['rewards-rate'], - rewardsResidue: data['rewards-residue'], + feeSink: data['fee-sink'] ?? '', + rewardsCalculationRound: data['rewards-calculation-round'] ?? 0, + rewardsLevel: data['rewards-level'] ?? 0, + rewardsPool: data['rewards-pool'] ?? '', + rewardsRate: data['rewards-rate'] ?? 0, + rewardsResidue: data['rewards-residue'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -2615,12 +2400,8 @@ export class BlockUpgradeState extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): BlockUpgradeState { /* eslint-disable dot-notation */ - if (typeof data['current-protocol'] === 'undefined') - throw new Error( - `Response is missing required field 'current-protocol': ${data}` - ); return new BlockUpgradeState({ - currentProtocol: data['current-protocol'], + currentProtocol: data['current-protocol'] ?? '', nextProtocol: data['next-protocol'], nextProtocolApprovals: data['next-protocol-approvals'], nextProtocolSwitchOn: data['next-protocol-switch-on'], @@ -2740,16 +2521,10 @@ export class Box extends BaseModel { // 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}`); return new Box({ - name: data['name'], - round: data['round'], - value: data['value'], + name: data['name'] ?? new Uint8Array(), + round: data['round'] ?? 0, + value: data['value'] ?? new Uint8Array(), }); /* eslint-enable dot-notation */ } @@ -2780,10 +2555,8 @@ export class BoxDescriptor extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): BoxDescriptor { /* eslint-disable dot-notation */ - if (typeof data['name'] === 'undefined') - throw new Error(`Response is missing required field 'name': ${data}`); return new BoxDescriptor({ - name: data['name'], + name: data['name'] ?? new Uint8Array(), }); /* eslint-enable dot-notation */ } @@ -2837,17 +2610,9 @@ export class BoxesResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): BoxesResponse { /* eslint-disable dot-notation */ - if (typeof data['application-id'] === 'undefined') - throw new Error( - `Response is missing required field 'application-id': ${data}` - ); - if (!Array.isArray(data['boxes'])) - throw new Error( - `Response is missing required array field 'boxes': ${data}` - ); return new BoxesResponse({ - applicationId: data['application-id'], - boxes: data['boxes'].map(BoxDescriptor.from_obj_for_encoding), + applicationId: data['application-id'] ?? 0, + boxes: (data['boxes'] ?? []).map(BoxDescriptor.from_obj_for_encoding), nextToken: data['next-token'], }); /* eslint-enable dot-notation */ @@ -2887,10 +2652,8 @@ export class ErrorResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): ErrorResponse { /* eslint-disable dot-notation */ - if (typeof data['message'] === 'undefined') - throw new Error(`Response is missing required field 'message': ${data}`); return new ErrorResponse({ - message: data['message'], + message: data['message'] ?? '', data: data['data'], }); /* eslint-enable dot-notation */ @@ -2946,10 +2709,8 @@ export class EvalDelta extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): EvalDelta { /* eslint-disable dot-notation */ - if (typeof data['action'] === 'undefined') - throw new Error(`Response is missing required field 'action': ${data}`); return new EvalDelta({ - action: data['action'], + action: data['action'] ?? 0, bytes: data['bytes'], uint: data['uint'], }); @@ -2987,13 +2748,9 @@ export class EvalDeltaKeyValue extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): EvalDeltaKeyValue { /* eslint-disable dot-notation */ - if (typeof data['key'] === 'undefined') - throw new Error(`Response is missing required field 'key': ${data}`); - if (typeof data['value'] === 'undefined') - throw new Error(`Response is missing required field 'value': ${data}`); return new EvalDeltaKeyValue({ - key: data['key'], - value: EvalDelta.from_obj_for_encoding(data['value']), + key: data['key'] ?? '', + value: EvalDelta.from_obj_for_encoding(data['value'] ?? {}), }); /* eslint-enable dot-notation */ } @@ -3100,26 +2857,12 @@ export class HealthCheck extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): HealthCheck { /* eslint-disable dot-notation */ - if (typeof data['db-available'] === 'undefined') - throw new Error( - `Response is missing required field 'db-available': ${data}` - ); - if (typeof data['is-migrating'] === 'undefined') - throw new Error( - `Response is missing required field 'is-migrating': ${data}` - ); - if (typeof data['message'] === 'undefined') - throw new Error(`Response is missing required field 'message': ${data}`); - if (typeof data['round'] === 'undefined') - throw new Error(`Response is missing required field 'round': ${data}`); - if (typeof data['version'] === 'undefined') - throw new Error(`Response is missing required field 'version': ${data}`); return new HealthCheck({ - dbAvailable: data['db-available'], - isMigrating: data['is-migrating'], - message: data['message'], - round: data['round'], - version: data['version'], + dbAvailable: data['db-available'] ?? false, + isMigrating: data['is-migrating'] ?? false, + message: data['message'] ?? '', + round: data['round'] ?? 0, + version: data['version'] ?? '', data: data['data'], errors: data['errors'], }); @@ -3355,18 +3098,10 @@ export class MiniAssetHolding extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): MiniAssetHolding { /* eslint-disable dot-notation */ - if (typeof data['address'] === 'undefined') - throw new Error(`Response is missing required field 'address': ${data}`); - if (typeof data['amount'] === 'undefined') - throw new Error(`Response is missing required field 'amount': ${data}`); - if (typeof data['is-frozen'] === 'undefined') - throw new Error( - `Response is missing required field 'is-frozen': ${data}` - ); return new MiniAssetHolding({ - address: data['address'], - amount: data['amount'], - isFrozen: data['is-frozen'], + address: data['address'] ?? '', + amount: data['amount'] ?? 0, + isFrozen: data['is-frozen'] ?? false, deleted: data['deleted'], optedInAtRound: data['opted-in-at-round'], optedOutAtRound: data['opted-out-at-round'], @@ -3938,15 +3673,9 @@ export class StateSchema extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): StateSchema { /* eslint-disable dot-notation */ - if (typeof data['num-byte-slice'] === 'undefined') - throw new Error( - `Response is missing required field 'num-byte-slice': ${data}` - ); - if (typeof data['num-uint'] === 'undefined') - throw new Error(`Response is missing required field 'num-uint': ${data}`); return new StateSchema({ - numByteSlice: data['num-byte-slice'], - numUint: data['num-uint'], + numByteSlice: data['num-byte-slice'] ?? 0, + numUint: data['num-uint'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -3982,13 +3711,9 @@ export class TealKeyValue extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): TealKeyValue { /* eslint-disable dot-notation */ - if (typeof data['key'] === 'undefined') - throw new Error(`Response is missing required field 'key': ${data}`); - if (typeof data['value'] === 'undefined') - throw new Error(`Response is missing required field 'value': ${data}`); return new TealKeyValue({ - key: data['key'], - value: TealValue.from_obj_for_encoding(data['value']), + key: data['key'] ?? '', + value: TealValue.from_obj_for_encoding(data['value'] ?? {}), }); /* eslint-enable dot-notation */ } @@ -4043,16 +3768,10 @@ export class TealValue extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): TealValue { /* eslint-disable dot-notation */ - if (typeof data['bytes'] === 'undefined') - throw new Error(`Response is missing required field 'bytes': ${data}`); - if (typeof data['type'] === 'undefined') - throw new Error(`Response is missing required field 'type': ${data}`); - if (typeof data['uint'] === 'undefined') - throw new Error(`Response is missing required field 'uint': ${data}`); return new TealValue({ - bytes: data['bytes'], - type: data['type'], - uint: data['uint'], + bytes: data['bytes'] ?? '', + type: data['type'] ?? 0, + uint: data['uint'] ?? 0, }); /* eslint-enable dot-notation */ } @@ -4529,23 +4248,11 @@ export class Transaction extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): Transaction { /* eslint-disable dot-notation */ - if (typeof data['fee'] === 'undefined') - throw new Error(`Response is missing required field 'fee': ${data}`); - if (typeof data['first-valid'] === 'undefined') - throw new Error( - `Response is missing required field 'first-valid': ${data}` - ); - if (typeof data['last-valid'] === 'undefined') - throw new Error( - `Response is missing required field 'last-valid': ${data}` - ); - if (typeof data['sender'] === 'undefined') - throw new Error(`Response is missing required field 'sender': ${data}`); return new Transaction({ - fee: data['fee'], - firstValid: data['first-valid'], - lastValid: data['last-valid'], - sender: data['sender'], + fee: data['fee'] ?? 0, + firstValid: data['first-valid'] ?? 0, + lastValid: data['last-valid'] ?? 0, + sender: data['sender'] ?? '', applicationTransaction: typeof data['application-transaction'] !== 'undefined' ? TransactionApplication.from_obj_for_encoding( @@ -4828,12 +4535,8 @@ export class TransactionApplication extends BaseModel { data: Record ): TransactionApplication { /* eslint-disable dot-notation */ - if (typeof data['application-id'] === 'undefined') - throw new Error( - `Response is missing required field 'application-id': ${data}` - ); return new TransactionApplication({ - applicationId: data['application-id'], + applicationId: data['application-id'] ?? 0, accounts: data['accounts'], applicationArgs: data['application-args'], approvalProgram: data['approval-program'], @@ -4971,18 +4674,10 @@ export class TransactionAssetFreeze extends BaseModel { data: Record ): TransactionAssetFreeze { /* eslint-disable dot-notation */ - if (typeof data['address'] === 'undefined') - throw new Error(`Response is missing required field 'address': ${data}`); - if (typeof data['asset-id'] === 'undefined') - throw new Error(`Response is missing required field 'asset-id': ${data}`); - if (typeof data['new-freeze-status'] === 'undefined') - throw new Error( - `Response is missing required field 'new-freeze-status': ${data}` - ); return new TransactionAssetFreeze({ - address: data['address'], - assetId: data['asset-id'], - newFreezeStatus: data['new-freeze-status'], + address: data['address'] ?? '', + assetId: data['asset-id'] ?? 0, + newFreezeStatus: data['new-freeze-status'] ?? false, }); /* eslint-enable dot-notation */ } @@ -5084,16 +4779,10 @@ export class TransactionAssetTransfer extends BaseModel { data: Record ): TransactionAssetTransfer { /* eslint-disable dot-notation */ - if (typeof data['amount'] === 'undefined') - throw new Error(`Response is missing required field 'amount': ${data}`); - if (typeof data['asset-id'] === 'undefined') - throw new Error(`Response is missing required field 'asset-id': ${data}`); - if (typeof data['receiver'] === 'undefined') - throw new Error(`Response is missing required field 'receiver': ${data}`); return new TransactionAssetTransfer({ - amount: data['amount'], - assetId: data['asset-id'], - receiver: data['receiver'], + amount: data['amount'] ?? 0, + assetId: data['asset-id'] ?? 0, + receiver: data['receiver'] ?? '', closeAmount: data['close-amount'], closeTo: data['close-to'], sender: data['sender'], @@ -5294,13 +4983,9 @@ export class TransactionPayment extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): TransactionPayment { /* eslint-disable dot-notation */ - if (typeof data['amount'] === 'undefined') - throw new Error(`Response is missing required field 'amount': ${data}`); - if (typeof data['receiver'] === 'undefined') - throw new Error(`Response is missing required field 'receiver': ${data}`); return new TransactionPayment({ - amount: data['amount'], - receiver: data['receiver'], + amount: data['amount'] ?? 0, + receiver: data['receiver'] ?? '', closeAmount: data['close-amount'], closeRemainderTo: data['close-remainder-to'], }); @@ -5355,17 +5040,9 @@ export class TransactionResponse extends BaseModel { // eslint-disable-next-line camelcase static from_obj_for_encoding(data: Record): TransactionResponse { /* eslint-disable dot-notation */ - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); - if (typeof data['transaction'] === 'undefined') - throw new Error( - `Response is missing required field 'transaction': ${data}` - ); return new TransactionResponse({ - currentRound: data['current-round'], - transaction: Transaction.from_obj_for_encoding(data['transaction']), + currentRound: data['current-round'] ?? 0, + transaction: Transaction.from_obj_for_encoding(data['transaction'] ?? {}), }); /* eslint-enable dot-notation */ } @@ -5516,10 +5193,8 @@ export class TransactionSignatureLogicsig extends BaseModel { data: Record ): TransactionSignatureLogicsig { /* eslint-disable dot-notation */ - if (typeof data['logic'] === 'undefined') - throw new Error(`Response is missing required field 'logic': ${data}`); return new TransactionSignatureLogicsig({ - logic: data['logic'], + logic: data['logic'] ?? new Uint8Array(), args: data['args'], multisigSignature: typeof data['multisig-signature'] !== 'undefined' @@ -5779,17 +5454,11 @@ export class TransactionsResponse extends BaseModel { data: Record ): TransactionsResponse { /* eslint-disable dot-notation */ - if (typeof data['current-round'] === 'undefined') - throw new Error( - `Response is missing required field 'current-round': ${data}` - ); - if (!Array.isArray(data['transactions'])) - throw new Error( - `Response is missing required array field 'transactions': ${data}` - ); return new TransactionsResponse({ - currentRound: data['current-round'], - transactions: data['transactions'].map(Transaction.from_obj_for_encoding), + currentRound: data['current-round'] ?? 0, + transactions: (data['transactions'] ?? []).map( + Transaction.from_obj_for_encoding + ), nextToken: data['next-token'], }); /* eslint-enable dot-notation */ diff --git a/src/client/v2/request.ts b/src/client/v2/request.ts new file mode 100644 index 000000000..e8f239e5b --- /dev/null +++ b/src/client/v2/request.ts @@ -0,0 +1,45 @@ +// import { HTTPClient } from '../client.js'; +// import IntDecoding from '../../types/intDecoding.js'; + +// /** +// * Base abstract class for JSON requests. +// * +// * Data: The type returned from the `do()` method +// * +// * Body: The structure of the response's body +// */ +// export default abstract class HTTPRequest { +// private c: HTTPClient; +// private queryParameters: Record; + +// /** +// * @param client - HTTPClient object. +// */ +// constructor(client: HTTPClient) { +// this.c = client; +// this.queryParameters = {}; +// } + +// /** +// * @returns The path of this request. +// * @category HTTPRequest +// */ +// abstract path(): string; + +// /** +// * Execute the request. +// * @param headers - Additional headers to send in the request. Optional. +// * @returns A promise which resolves to the parsed response data. +// * @category HTTPRequest +// */ +// async execute(headers: Record = {}): Promise { +// const res = await this.c.get({ +// relativePath: this.path(), +// parseBody: false, +// jsonOptions: { intDecoding: IntDecoding.BIGINT }, +// query: this.queryParameters, +// requestHeaders: headers, +// }); +// return res.body; +// } +// } diff --git a/src/encoding/objects.ts b/src/encoding/objects.ts new file mode 100644 index 000000000..e80b82ae2 --- /dev/null +++ b/src/encoding/objects.ts @@ -0,0 +1,15 @@ +const toObjectEncoding: unique symbol = Symbol('toObjectEncoding'); +const fromObjectEncoding: unique symbol = Symbol('fromObjectEncoding'); + +enum EncodingFormat { + JSON = 'json', + Msgpack = 'msgpack', +} + +export interface Encodable { + [toObjectEncoding](format: EncodingFormat): Record; + [fromObjectEncoding]( + obj: Record, + format: EncodingFormat + ): void; +} diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index 63e62d3b3..393667f42 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -1477,64 +1477,344 @@ 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') { - const response = await this.v2Client.block(0).do(); - this.actualMockResponse = response.get_obj_for_encoding(true); - } 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(new Uint8Array()) + .do(); + break; + case 'RawTransaction': + this.actualMockResponse = await this.v2Client + .sendRawTransaction(new Uint8Array()) + .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; + } + 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.body; + caughtError = true; + } + if (!caughtError) { + throw new Error('Expected error response, got none.'); + } + break; } - } else if (client === 'indexer') { - // endpoints are ignored by mock server, see setupMockServerForResponses - const response = await this.indexerClient.makeHealthCheck().doRaw(); - const responseString = new TextDecoder().decode(response); - this.actualMockResponse = algosdk.parseJSON(responseString, { - intDecoding: algosdk.IntDecoding.BIGINT, - }); - } else { - throw Error(`did not recognize desired client "${client}"`); - } - } catch (err) { - if (this.expectedMockResponseCode === 200) { - throw err; + 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}` - ); + // if (responseFormat === 'msgp') { + // const response = await this.v2Client.block(0).do(); + // this.actualMockResponse = response.get_obj_for_encoding(true); + // } else { + // this.actualMockResponse = await this.v2Client.genesis().do(); + // } + } 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.body; + caughtError = true; + } + if (!caughtError) { + throw new Error('Expected error response, got none.'); + } + break; } + default: + throw new Error(`Unrecognized indexer endpoint: ${endpoint}`); } + // const response = await this.indexerClient.makeHealthCheck().doRaw(); + // const responseString = new TextDecoder().decode(response); + // this.actualMockResponse = algosdk.parseJSON(responseString, { + // intDecoding: algosdk.IntDecoding.BIGINT, + // }); + } else { + throw Error(`did not recognize desired client "${client}"`); } } ); Then('the parsed response should equal the mock response.', function () { + let actualResponseObject; 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( - algosdk.stringifyJSON( - algosdk.parseJSON(expectedMockResponse, { - intDecoding: algosdk.IntDecoding.MIXED, - }) - ), - algosdk.stringifyJSON(this.actualMockResponse) + actualResponseObject = this.actualMockResponse.get_obj_for_encoding( + false, + true ); } else { - assert.deepStrictEqual( - algosdk.decodeObj( - new Uint8Array(algosdk.encodeObj(this.actualMockResponse)) - ), - algosdk.decodeObj(expectedMockResponse) + actualResponseObject = expectedMockResponse.get_obj_for_encoding( + true, + true ); } + } else { + actualResponseObject = 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 parsedExpectedMockResponse; + if (responseFormat === 'json') { + actualResponseObject = algosdk.parseJSON( + algosdk.stringifyJSON(actualResponseObject), + { + intDecoding: algosdk.IntDecoding.MIXED, + } + ); + parsedExpectedMockResponse = algosdk.parseJSON(expectedMockResponse, { + intDecoding: algosdk.IntDecoding.MIXED, + }); + } else { + actualResponseObject = algosdk.decodeObj( + algosdk.encodeObj(actualResponseObject) + ); + parsedExpectedMockResponse = algosdk.decodeObj(expectedMockResponse); + } + + assert.deepStrictEqual(actualResponseObject, parsedExpectedMockResponse); }); Then('expect error string to contain {string}', (expectedErrorString) => { diff --git a/tests/cucumber/unit.tags b/tests/cucumber/unit.tags index 1dd481160..65d36a249 100644 --- a/tests/cucumber/unit.tags +++ b/tests/cucumber/unit.tags @@ -1,39 +1,2 @@ -@unit.abijson -@unit.abijson.byname -@unit.algod -@unit.algod.ledger_refactoring -@unit.applications -@unit.applications.boxes -@unit.atomic_transaction_composer -@unit.blocksummary -@unit.blocktxids -@unit.dryrun -@unit.dryrun.trace.application -@unit.feetest -@unit.indexer -@unit.indexer.ledger_refactoring -@unit.indexer.logs -@unit.offline -@unit.program_sanity_check -@unit.ready -@unit.rekey @unit.responses -@unit.responses.231 -@unit.responses.participationupdates -@unit.responses.unlimited_assets -@unit.responses.blocksummary -@unit.responses.statedelta.json -@unit.responses.sync -@unit.responses.timestamp -@unit.responses.txid.json -@unit.responses.txngroupdeltas.json -@unit.sourcemapv2 -@unit.stateproof.paths -@unit.stateproof.responses -@unit.stateproof.responses.msgp -@unit.sync -@unit.tealsign -@unit.timestamp -@unit.transactions -@unit.transactions.keyreg -@unit.transactions.payment +@unit.responses.231 \ No newline at end of file From f60a65cb29187748373a057612f50bf55da7cd75 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 5 Jun 2024 15:31:32 -0400 Subject: [PATCH 06/13] Fixes post merge --- .../v2/indexer/lookupAccountAppLocalStates.ts | 4 ++- src/client/v2/indexer/lookupAccountAssets.ts | 4 ++- src/client/v2/indexer/lookupAccountByID.ts | 4 ++- .../lookupAccountCreatedApplications.ts | 4 ++- .../v2/indexer/lookupAccountCreatedAssets.ts | 4 ++- .../v2/indexer/lookupAccountTransactions.ts | 4 ++- .../v2/indexer/lookupApplicationLogs.ts | 4 ++- src/client/v2/indexer/lookupApplications.ts | 4 ++- src/client/v2/indexer/lookupAssetBalances.ts | 4 ++- src/client/v2/indexer/lookupAssetByID.ts | 4 ++- .../v2/indexer/lookupAssetTransactions.ts | 4 ++- src/client/v2/indexer/lookupBlock.ts | 2 +- .../v2/indexer/lookupTransactionByID.ts | 4 ++- src/client/v2/indexer/makeHealthCheck.ts | 4 ++- src/client/v2/indexer/searchAccounts.ts | 4 ++- .../v2/indexer/searchForApplications.ts | 4 ++- src/client/v2/indexer/searchForAssets.ts | 4 ++- .../v2/indexer/searchForTransactions.ts | 4 ++- tests/cucumber/steps/steps.js | 28 ++++++------------- 19 files changed, 61 insertions(+), 37 deletions(-) diff --git a/src/client/v2/indexer/lookupAccountAppLocalStates.ts b/src/client/v2/indexer/lookupAccountAppLocalStates.ts index a10f7de9e..1eb2722e2 100644 --- a/src/client/v2/indexer/lookupAccountAppLocalStates.ts +++ b/src/client/v2/indexer/lookupAccountAppLocalStates.ts @@ -142,6 +142,8 @@ export default class LookupAccountAppLocalStates extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): ApplicationLocalStatesResponse { - return ApplicationLocalStatesResponse.from_obj_for_encoding(body); + return ApplicationLocalStatesResponse.fromEncodingData( + ApplicationLocalStatesResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/lookupAccountAssets.ts b/src/client/v2/indexer/lookupAccountAssets.ts index e17359202..1f4ebc986 100644 --- a/src/client/v2/indexer/lookupAccountAssets.ts +++ b/src/client/v2/indexer/lookupAccountAssets.ts @@ -143,6 +143,8 @@ export default class LookupAccountAssets extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): AssetsResponse { - return AssetsResponse.from_obj_for_encoding(body); + return AssetsResponse.fromEncodingData( + AssetsResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/lookupAccountByID.ts b/src/client/v2/indexer/lookupAccountByID.ts index 3e531eed0..b9f72a2bf 100644 --- a/src/client/v2/indexer/lookupAccountByID.ts +++ b/src/client/v2/indexer/lookupAccountByID.ts @@ -111,6 +111,8 @@ export default class LookupAccountByID extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): AccountResponse { - return AccountResponse.from_obj_for_encoding(body); + return AccountResponse.fromEncodingData( + AccountResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/lookupAccountCreatedApplications.ts b/src/client/v2/indexer/lookupAccountCreatedApplications.ts index c5c18f155..605cd3136 100644 --- a/src/client/v2/indexer/lookupAccountCreatedApplications.ts +++ b/src/client/v2/indexer/lookupAccountCreatedApplications.ts @@ -143,6 +143,8 @@ export default class LookupAccountCreatedApplications extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): ApplicationsResponse { - return ApplicationsResponse.from_obj_for_encoding(body); + return ApplicationsResponse.fromEncodingData( + ApplicationsResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/lookupAccountCreatedAssets.ts b/src/client/v2/indexer/lookupAccountCreatedAssets.ts index ad9a569ab..52a568da4 100644 --- a/src/client/v2/indexer/lookupAccountCreatedAssets.ts +++ b/src/client/v2/indexer/lookupAccountCreatedAssets.ts @@ -144,6 +144,8 @@ export default class LookupAccountCreatedAssets extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): AssetsResponse { - return AssetsResponse.from_obj_for_encoding(body); + return AssetsResponse.fromEncodingData( + AssetsResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/lookupAccountTransactions.ts b/src/client/v2/indexer/lookupAccountTransactions.ts index 8f6a32d5c..d39b7dcfc 100644 --- a/src/client/v2/indexer/lookupAccountTransactions.ts +++ b/src/client/v2/indexer/lookupAccountTransactions.ts @@ -397,6 +397,8 @@ export default class LookupAccountTransactions extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): TransactionsResponse { - return TransactionsResponse.from_obj_for_encoding(body); + return TransactionsResponse.fromEncodingData( + TransactionsResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/lookupApplicationLogs.ts b/src/client/v2/indexer/lookupApplicationLogs.ts index b23cfe01c..eb3c81d94 100644 --- a/src/client/v2/indexer/lookupApplicationLogs.ts +++ b/src/client/v2/indexer/lookupApplicationLogs.ts @@ -161,6 +161,8 @@ export default class LookupApplicationLogs extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): ApplicationLogsResponse { - return ApplicationLogsResponse.from_obj_for_encoding(body); + return ApplicationLogsResponse.fromEncodingData( + ApplicationLogsResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/lookupApplications.ts b/src/client/v2/indexer/lookupApplications.ts index 449e27ec0..08e9da26d 100644 --- a/src/client/v2/indexer/lookupApplications.ts +++ b/src/client/v2/indexer/lookupApplications.ts @@ -64,6 +64,8 @@ export default class LookupApplications extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): ApplicationResponse { - return ApplicationResponse.from_obj_for_encoding(body); + return ApplicationResponse.fromEncodingData( + ApplicationResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/lookupAssetBalances.ts b/src/client/v2/indexer/lookupAssetBalances.ts index cd32fe833..94438c254 100644 --- a/src/client/v2/indexer/lookupAssetBalances.ts +++ b/src/client/v2/indexer/lookupAssetBalances.ts @@ -152,6 +152,8 @@ export default class LookupAssetBalances extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): AssetBalancesResponse { - return AssetBalancesResponse.from_obj_for_encoding(body); + return AssetBalancesResponse.fromEncodingData( + AssetBalancesResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/lookupAssetByID.ts b/src/client/v2/indexer/lookupAssetByID.ts index 5a2e3e0b2..19d5d95ca 100644 --- a/src/client/v2/indexer/lookupAssetByID.ts +++ b/src/client/v2/indexer/lookupAssetByID.ts @@ -63,6 +63,8 @@ export default class LookupAssetByID extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): AssetResponse { - return AssetResponse.from_obj_for_encoding(body); + return AssetResponse.fromEncodingData( + AssetResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/lookupAssetTransactions.ts b/src/client/v2/indexer/lookupAssetTransactions.ts index 5a9103e2e..4fafe7e65 100644 --- a/src/client/v2/indexer/lookupAssetTransactions.ts +++ b/src/client/v2/indexer/lookupAssetTransactions.ts @@ -404,6 +404,8 @@ export default class LookupAssetTransactions extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): TransactionsResponse { - return TransactionsResponse.from_obj_for_encoding(body); + return TransactionsResponse.fromEncodingData( + TransactionsResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/lookupBlock.ts b/src/client/v2/indexer/lookupBlock.ts index 251423da4..98f459574 100644 --- a/src/client/v2/indexer/lookupBlock.ts +++ b/src/client/v2/indexer/lookupBlock.ts @@ -44,6 +44,6 @@ export default class LookupBlock extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): Block { - return Block.from_obj_for_encoding(body); + return Block.fromEncodingData(Block.encodingSchema.fromPreparedJSON(body)); } } diff --git a/src/client/v2/indexer/lookupTransactionByID.ts b/src/client/v2/indexer/lookupTransactionByID.ts index 80bbc4149..fbb647631 100644 --- a/src/client/v2/indexer/lookupTransactionByID.ts +++ b/src/client/v2/indexer/lookupTransactionByID.ts @@ -35,6 +35,8 @@ export default class LookupTransactionByID extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): TransactionResponse { - return TransactionResponse.from_obj_for_encoding(body); + return TransactionResponse.fromEncodingData( + TransactionResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/makeHealthCheck.ts b/src/client/v2/indexer/makeHealthCheck.ts index 2ff40a876..baa3a6956 100644 --- a/src/client/v2/indexer/makeHealthCheck.ts +++ b/src/client/v2/indexer/makeHealthCheck.ts @@ -27,6 +27,8 @@ export default class MakeHealthCheck extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): HealthCheck { - return HealthCheck.from_obj_for_encoding(body); + return HealthCheck.fromEncodingData( + HealthCheck.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/searchAccounts.ts b/src/client/v2/indexer/searchAccounts.ts index 7dfbed6bf..456f26f38 100644 --- a/src/client/v2/indexer/searchAccounts.ts +++ b/src/client/v2/indexer/searchAccounts.ts @@ -274,6 +274,8 @@ export default class SearchAccounts extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): AccountsResponse { - return AccountsResponse.from_obj_for_encoding(body); + return AccountsResponse.fromEncodingData( + AccountsResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/searchForApplications.ts b/src/client/v2/indexer/searchForApplications.ts index 379a884a4..4ec9a98fc 100644 --- a/src/client/v2/indexer/searchForApplications.ts +++ b/src/client/v2/indexer/searchForApplications.ts @@ -139,6 +139,8 @@ export default class SearchForApplications extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): ApplicationsResponse { - return ApplicationsResponse.from_obj_for_encoding(body); + return ApplicationsResponse.fromEncodingData( + ApplicationsResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/searchForAssets.ts b/src/client/v2/indexer/searchForAssets.ts index 759a0c3b3..0ffe0baf3 100644 --- a/src/client/v2/indexer/searchForAssets.ts +++ b/src/client/v2/indexer/searchForAssets.ts @@ -180,6 +180,8 @@ export default class SearchForAssets extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): AssetsResponse { - return AssetsResponse.from_obj_for_encoding(body); + return AssetsResponse.fromEncodingData( + AssetsResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/src/client/v2/indexer/searchForTransactions.ts b/src/client/v2/indexer/searchForTransactions.ts index eb8c69e71..9b2c26ba8 100644 --- a/src/client/v2/indexer/searchForTransactions.ts +++ b/src/client/v2/indexer/searchForTransactions.ts @@ -441,6 +441,8 @@ export default class SearchForTransactions extends JSONRequest< // eslint-disable-next-line class-methods-use-this prepare(body: Record): TransactionsResponse { - return TransactionsResponse.from_obj_for_encoding(body); + return TransactionsResponse.fromEncodingData( + TransactionsResponse.encodingSchema.fromPreparedJSON(body) + ); } } diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index c1ed1c224..cffd2c7d0 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -1776,41 +1776,31 @@ module.exports = function getSteps(options) { ); Then('the parsed response should equal the mock response.', function () { - let actualResponseObject; + let encodedResponseObject; if (this.expectedMockResponseCode === 200) { if (responseFormat === 'json') { - actualResponseObject = this.actualMockResponse.get_obj_for_encoding( - false, - true - ); + encodedResponseObject = algosdk.encodeJSON(this.actualMockResponse); } else { - actualResponseObject = expectedMockResponse.get_obj_for_encoding( - true, - true - ); + encodedResponseObject = algosdk.encodeMsgpack(this.actualMockResponse); } } else { - actualResponseObject = this.actualMockResponse; + 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( - algosdk.stringifyJSON(actualResponseObject), - { - intDecoding: algosdk.IntDecoding.MIXED, - } - ); + actualResponseObject = algosdk.parseJSON(encodedResponseObject, { + intDecoding: algosdk.IntDecoding.MIXED, + }); parsedExpectedMockResponse = algosdk.parseJSON(expectedMockResponse, { intDecoding: algosdk.IntDecoding.MIXED, }); } else { - actualResponseObject = algosdk.decodeObj( - algosdk.encodeObj(actualResponseObject) - ); + actualResponseObject = algosdk.decodeObj(encodedResponseObject); parsedExpectedMockResponse = algosdk.decodeObj(expectedMockResponse); } From b21737b86c9bfa4ec18b25331dcd8f3da67dfb55 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 5 Jun 2024 15:33:32 -0400 Subject: [PATCH 07/13] Remove old file --- src/encoding/objects.ts | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/encoding/objects.ts diff --git a/src/encoding/objects.ts b/src/encoding/objects.ts deleted file mode 100644 index e80b82ae2..000000000 --- a/src/encoding/objects.ts +++ /dev/null @@ -1,15 +0,0 @@ -const toObjectEncoding: unique symbol = Symbol('toObjectEncoding'); -const fromObjectEncoding: unique symbol = Symbol('fromObjectEncoding'); - -enum EncodingFormat { - JSON = 'json', - Msgpack = 'msgpack', -} - -export interface Encodable { - [toObjectEncoding](format: EncodingFormat): Record; - [fromObjectEncoding]( - obj: Record, - format: EncodingFormat - ): void; -} From 745f5aefac57af2cf83c45551e7865319c1631c2 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Thu, 27 Jun 2024 14:04:16 -0400 Subject: [PATCH 08/13] Update test steps --- tests/cucumber/steps/steps.js | 45 +++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index 9f0d3c6d3..0b53f3195 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -1775,6 +1775,40 @@ module.exports = function getSteps(options) { } ); + function pruneDefaultValuesFromObject(object) { + const prunedObject = { ...object }; + for (const [key, value] of Object.entries(prunedObject)) { + if ( + value === undefined || + value === 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; + } + Then('the parsed response should equal the mock response.', function () { let encodedResponseObject; if (this.expectedMockResponseCode === 200) { @@ -1796,9 +1830,12 @@ module.exports = function getSteps(options) { actualResponseObject = algosdk.parseJSON(encodedResponseObject, { intDecoding: algosdk.IntDecoding.MIXED, }); - parsedExpectedMockResponse = algosdk.parseJSON(expectedMockResponse, { - intDecoding: algosdk.IntDecoding.MIXED, - }); + // Prune default values from the actual response object to match the expected response object. + parsedExpectedMockResponse = pruneDefaultValuesFromObject( + algosdk.parseJSON(expectedMockResponse, { + intDecoding: algosdk.IntDecoding.MIXED, + }) + ); } else { actualResponseObject = algosdk.decodeObj(encodedResponseObject); parsedExpectedMockResponse = algosdk.decodeObj(expectedMockResponse); @@ -4970,7 +5007,7 @@ module.exports = function getSteps(options) { .compile(tealSrc) .sourcemap(true) .do(); - this.rawSourceMap = algosdk.stringifyJSON(compiledResponse.sourcemap); + this.rawSourceMap = algosdk.encodeJSON(compiledResponse.sourcemap); } ); From b80eccd452168f557c32f0f13b3228a0db85474f Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Thu, 27 Jun 2024 15:05:39 -0400 Subject: [PATCH 09/13] Fixes for firefox --- tests/cucumber/steps/steps.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index 0b53f3195..e8a61b7a2 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -1493,12 +1493,12 @@ module.exports = function getSteps(options) { break; case 'TealCompile': this.actualMockResponse = await this.v2Client - .compile(new Uint8Array()) + .compile(makeUint8Array()) .do(); break; case 'RawTransaction': this.actualMockResponse = await this.v2Client - .sendRawTransaction(new Uint8Array()) + .sendRawTransaction(makeUint8Array()) .do(); break; case 'GetSupply': @@ -1776,7 +1776,14 @@ module.exports = function getSteps(options) { ); function pruneDefaultValuesFromObject(object) { - const prunedObject = { ...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 || From 769fa874acf176a8321b714155dddbfbd800f155 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Thu, 27 Jun 2024 18:08:04 -0400 Subject: [PATCH 10/13] Fix remaining unit test issues --- src/client/v2/algod/getBlockTxids.ts | 13 ++++++- src/client/v2/indexer/lookupAccountAssets.ts | 10 ++--- tests/cucumber/steps/steps.js | 38 +++++++++++++++---- tests/cucumber/unit.tags | 39 +++++++++++++++++++- 4 files changed, 85 insertions(+), 15 deletions(-) diff --git a/src/client/v2/algod/getBlockTxids.ts b/src/client/v2/algod/getBlockTxids.ts index bba9df96c..724525a65 100644 --- a/src/client/v2/algod/getBlockTxids.ts +++ b/src/client/v2/algod/getBlockTxids.ts @@ -1,7 +1,11 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; +import { BlockTxidsResponse } from './models/types.js'; -export default class GetBlockTxids extends JSONRequest { +export default class GetBlockTxids extends JSONRequest< + BlockTxidsResponse, + Record +> { round: number; constructor(c: HTTPClient, roundNumber: number) { @@ -14,4 +18,11 @@ export default class GetBlockTxids extends JSONRequest { path() { return `/v2/blocks/${this.round}/txids`; } + + // eslint-disable-next-line class-methods-use-this + prepare(body: Record): BlockTxidsResponse { + return BlockTxidsResponse.fromEncodingData( + BlockTxidsResponse.encodingSchema.fromPreparedJSON(body) + ); + } } diff --git a/src/client/v2/indexer/lookupAccountAssets.ts b/src/client/v2/indexer/lookupAccountAssets.ts index 1f4ebc986..6b7a9c28b 100644 --- a/src/client/v2/indexer/lookupAccountAssets.ts +++ b/src/client/v2/indexer/lookupAccountAssets.ts @@ -1,10 +1,10 @@ import JSONRequest from '../jsonrequest.js'; import { HTTPClient } from '../../client.js'; import { Address } from '../../../encoding/address.js'; -import { AssetsResponse } from './models/types.js'; +import { AssetHoldingsResponse } from './models/types.js'; export default class LookupAccountAssets extends JSONRequest< - AssetsResponse, + AssetHoldingsResponse, Record > { private account: string; @@ -142,9 +142,9 @@ export default class LookupAccountAssets extends JSONRequest< } // eslint-disable-next-line class-methods-use-this - prepare(body: Record): AssetsResponse { - return AssetsResponse.fromEncodingData( - AssetsResponse.encodingSchema.fromPreparedJSON(body) + prepare(body: Record): AssetHoldingsResponse { + return AssetHoldingsResponse.fromEncodingData( + AssetHoldingsResponse.encodingSchema.fromPreparedJSON(body) ); } } diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index e8a61b7a2..8579207ef 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -1787,6 +1787,7 @@ module.exports = function getSteps(options) { for (const [key, value] of Object.entries(prunedObject)) { if ( value === undefined || + value === null || value === 0 || value === '' || value === false || @@ -1817,10 +1818,28 @@ module.exports = function getSteps(options) { } Then('the parsed response should equal the mock response.', function () { + let expectedJsonNeedsPruning = true; + let encodedResponseObject; if (this.expectedMockResponseCode === 200) { if (responseFormat === 'json') { - encodedResponseObject = algosdk.encodeJSON(this.actualMockResponse); + if (typeof this.actualMockResponse.toEncodingData === 'function') { + if ( + this.actualMockResponse instanceof + algosdk.modelsv2.TransactionGroupLedgerStateDeltasForRoundResponse + ) { + // TransactionGroupLedgerStateDeltasForRoundResponse has an UntypedResponse inside of it, + // so the expected JSON response should not be pruned. + expectedJsonNeedsPruning = false; + } + encodedResponseObject = algosdk.encodeJSON(this.actualMockResponse); + } else { + // Handles non-typed responses such as "GetLedgerStateDelta" + encodedResponseObject = algosdk.stringifyJSON( + this.actualMockResponse + ); + expectedJsonNeedsPruning = false; + } } else { encodedResponseObject = algosdk.encodeMsgpack(this.actualMockResponse); } @@ -1837,12 +1856,15 @@ module.exports = function getSteps(options) { actualResponseObject = algosdk.parseJSON(encodedResponseObject, { intDecoding: algosdk.IntDecoding.MIXED, }); - // Prune default values from the actual response object to match the expected response object. - parsedExpectedMockResponse = pruneDefaultValuesFromObject( - algosdk.parseJSON(expectedMockResponse, { - 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.decodeObj(encodedResponseObject); parsedExpectedMockResponse = algosdk.decodeObj(expectedMockResponse); @@ -5607,7 +5629,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 65d36a249..cf48d554a 100644 --- a/tests/cucumber/unit.tags +++ b/tests/cucumber/unit.tags @@ -1,2 +1,39 @@ +@unit.abijson +@unit.abijson.byname +@unit.algod +@unit.algod.ledger_refactoring +@unit.applications +@unit.applications.boxes +@unit.atomic_transaction_composer +@unit.blocksummary +@unit.blocktxids +@unit.dryrun +@unit.dryrun.trace.application +@unit.feetest +@unit.indexer +@unit.indexer.ledger_refactoring +@unit.indexer.logs +@unit.offline +@unit.program_sanity_check +@unit.ready +@unit.rekey @unit.responses -@unit.responses.231 \ No newline at end of file +@unit.responses.231 +@unit.responses.participationupdates +@unit.responses.unlimited_assets +@unit.responses.blocksummary +@unit.responses.minbalance +@unit.responses.statedelta.json +@unit.responses.sync +@unit.responses.timestamp +@unit.responses.txid.json +@unit.responses.txngroupdeltas.json +@unit.sourcemapv2 +@unit.stateproof.paths +@unit.stateproof.responses +@unit.sync +@unit.tealsign +@unit.timestamp +@unit.transactions +@unit.transactions.keyreg +@unit.transactions.payment \ No newline at end of file From 531c5f8e1b18dae7a59a61ed5281702c408178f2 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Fri, 28 Jun 2024 12:59:25 -0400 Subject: [PATCH 11/13] Final clean up --- src/client/v2/algod/suggestedParams.ts | 2 +- src/client/v2/request.ts | 45 -------------------------- tests/cucumber/steps/steps.js | 12 +------ tests/cucumber/unit.tags | 2 +- 4 files changed, 3 insertions(+), 58 deletions(-) delete mode 100644 src/client/v2/request.ts diff --git a/src/client/v2/algod/suggestedParams.ts b/src/client/v2/algod/suggestedParams.ts index 20912d440..c36c224d7 100644 --- a/src/client/v2/algod/suggestedParams.ts +++ b/src/client/v2/algod/suggestedParams.ts @@ -19,7 +19,7 @@ export interface SuggestedParamsFromAlgod extends SuggestedParams { genesisHash: Uint8Array; /** - * TODO description + * ConsensusVersion indicates the consensus protocol version as of the last round. */ consensusVersion: string; } diff --git a/src/client/v2/request.ts b/src/client/v2/request.ts deleted file mode 100644 index e8f239e5b..000000000 --- a/src/client/v2/request.ts +++ /dev/null @@ -1,45 +0,0 @@ -// import { HTTPClient } from '../client.js'; -// import IntDecoding from '../../types/intDecoding.js'; - -// /** -// * Base abstract class for JSON requests. -// * -// * Data: The type returned from the `do()` method -// * -// * Body: The structure of the response's body -// */ -// export default abstract class HTTPRequest { -// private c: HTTPClient; -// private queryParameters: Record; - -// /** -// * @param client - HTTPClient object. -// */ -// constructor(client: HTTPClient) { -// this.c = client; -// this.queryParameters = {}; -// } - -// /** -// * @returns The path of this request. -// * @category HTTPRequest -// */ -// abstract path(): string; - -// /** -// * Execute the request. -// * @param headers - Additional headers to send in the request. Optional. -// * @returns A promise which resolves to the parsed response data. -// * @category HTTPRequest -// */ -// async execute(headers: Record = {}): Promise { -// const res = await this.c.get({ -// relativePath: this.path(), -// parseBody: false, -// jsonOptions: { intDecoding: IntDecoding.BIGINT }, -// query: this.queryParameters, -// requestHeaders: headers, -// }); -// return res.body; -// } -// } diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js index 8579207ef..71959f109 100644 --- a/tests/cucumber/steps/steps.js +++ b/tests/cucumber/steps/steps.js @@ -1647,12 +1647,6 @@ module.exports = function getSteps(options) { default: throw new Error(`Unrecognized algod endpoint: ${endpoint}`); } - // if (responseFormat === 'msgp') { - // const response = await this.v2Client.block(0).do(); - // this.actualMockResponse = response.get_obj_for_encoding(true); - // } else { - // this.actualMockResponse = await this.v2Client.genesis().do(); - // } } else if (client === 'indexer') { switch (endpoint) { case 'lookupAccountByID': @@ -1764,11 +1758,6 @@ module.exports = function getSteps(options) { default: throw new Error(`Unrecognized indexer endpoint: ${endpoint}`); } - // const response = await this.indexerClient.makeHealthCheck().doRaw(); - // const responseString = new TextDecoder().decode(response); - // this.actualMockResponse = algosdk.parseJSON(responseString, { - // intDecoding: algosdk.IntDecoding.BIGINT, - // }); } else { throw Error(`did not recognize desired client "${client}"`); } @@ -1789,6 +1778,7 @@ module.exports = function getSteps(options) { value === undefined || value === null || value === 0 || + value === BigInt(0) || value === '' || value === false || (Array.isArray(value) && value.length === 0) || diff --git a/tests/cucumber/unit.tags b/tests/cucumber/unit.tags index cf48d554a..1dbf5bd09 100644 --- a/tests/cucumber/unit.tags +++ b/tests/cucumber/unit.tags @@ -36,4 +36,4 @@ @unit.timestamp @unit.transactions @unit.transactions.keyreg -@unit.transactions.payment \ No newline at end of file +@unit.transactions.payment From 1e2038a800c3bbbb852b4e1cdb257cbff241f018 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Fri, 28 Jun 2024 15:25:35 -0400 Subject: [PATCH 12/13] Update .test-env --- .test-env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.test-env b/.test-env index e4abd2b12..049289d7b 100644 --- a/.test-env +++ b/.test-env @@ -1,6 +1,6 @@ # Configs for testing repo download: SDK_TESTING_URL="https://github.com/algorand/algorand-sdk-testing" -SDK_TESTING_BRANCH="update-indexer-mocks" +SDK_TESTING_BRANCH="master" SDK_TESTING_HARNESS="test-harness" INSTALL_ONLY=0 From fe737c4b1e178e867b68730808e643b05fb8b308 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Mon, 15 Jul 2024 15:38:51 -0400 Subject: [PATCH 13/13] Update migration guide --- v2_TO_v3_MIGRATION_GUIDE.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/v2_TO_v3_MIGRATION_GUIDE.md b/v2_TO_v3_MIGRATION_GUIDE.md index 8d6b305cd..4126adfbe 100644 --- a/v2_TO_v3_MIGRATION_GUIDE.md +++ b/v2_TO_v3_MIGRATION_GUIDE.md @@ -244,7 +244,7 @@ That type has been removed and this behavior is no longer possible in v3. Instea #### Multisig Transaction Class -The `MultisigTransaction` class have been removed, as it was unnecessary. +The `MultisigTransaction` class has been removed, as it was unnecessary. #### Transaction Group Class @@ -278,7 +278,7 @@ Auction bids have been removed from the library in v3, as they were only relevan In v2, the `Algodv2` and `Indexer` clients, as well as each individual request class, had a `setIntDecoding` method which could be used to configure how integers in the response were parsed into either a JavaScript `number` or `bigint`. These methods have been removed in v3. -Instead, Algod responses are now fully typed, and individual fields are typed as either `number` or `bigint`. The types defined in the `modelsv2` namespace have been updated to reflect this. In v2, these classes used `number | bigint` for all numeric fields, but in v3, they will use either `number` or `bigint` depending on the field. +Instead, Algod and Indexer responses are now fully typed, and individual fields are typed as either `number` or `bigint`. The types defined in the `modelsv2` and `indexerModels` namespaces have been updated to reflect this. In v2, these classes used `number | bigint` for all numeric fields, but in v3, they will use either `number` or `bigint` depending on the field. Generally speaking, the fields will be `bigint` based on the following criteria: @@ -289,9 +289,7 @@ Generally speaking, the fields will be `bigint` based on the following criteria: - If the field can be any value in the uint64 range, it will be `bigint` - Other fields which are guaranteed to be small will be `number` -Indexer responses are not yet typed, and all numeric fields are returned as `bigint`. - -Additionally, Algod and Indexer request and response models used to be subclasses of a `BaseModel` type. This type has been removed in v3, and instead all models adhere to the `Encodable` interface. More information about encoding changes can be found in the [Encoding and Decoding](#encoding-and-decoding) section. +Additionally, Algod and Indexer request and response models used to be subclasses of a `BaseModel` type. This type has been removed in v3, and instead all models adhere to the `Encodable` interface. More information about encoding changes can be found in the [Object Encoding and Decoding](#object-encoding-and-decoding) section. ### JSON Operations @@ -301,7 +299,7 @@ In order to facilitate `bigint` as a first-class type in this SDK, additional JS `stringifyJSON` can be used to convert a JavaScript object containing `bigint`s into a JSON string, something `JSON.stringify` cannot do. -If your v2 code uses `JSON.parse` or `JSON.stringify` on types which can now contain `bigint`s in v3, such as `Transaction` representations or REST API responses, consider using these new functions instead. +If your v2 code uses `JSON.parse` or `JSON.stringify` on types which can now contain `bigint`s in v3, you may receive an error such as `TypeError: Do not know how to serialize a BigInt`. Consider using these new functions instead. Or, if the types are `Encodable`, use the new `encodeJSON` and `decodeJSON` functions described in the [Object Encoding and Decoding](#object-encoding-and-decoding) section. ### IntDecoding @@ -315,7 +313,7 @@ Specifically, the `DryrunResult` class and its dependent types have been removed The `DryrunTransactionResult` class, which made up the elements of the v2 `DryrunResult.txns` array, used to have methods `appTrace` and `lsigTrace`. These have been replaced by the new `dryrunTxnResultAppTrace` and `dryrunTxnResultLogicSigTrace` functions, which accept a `DryrunTxnResult`. These new functions should produce identical results to the old ones. -### Encoding and Decoding +### Object Encoding and Decoding In v2 of the SDK, the `Transaction`, `LogicSig`, `BaseModel` and other classes had `get_obj_for_encoding` methods and `from_obj_for_encoding` static methods. These were used during the process of encoding or decoding objects from msgpack or JSON. These ad-hoc methods have been removed in v3, and in their place a new `Encodable` interface has been introduced, along with functions `encodeMsgpack`, `decodeMsgpack`, `encodeJSON`, and `decodeJSON`.