diff --git a/.github/eosjs-ci/Dockerfile b/.github/eosjs-ci/Dockerfile index ec86bedae..33229e1b3 100644 --- a/.github/eosjs-ci/Dockerfile +++ b/.github/eosjs-ci/Dockerfile @@ -1,13 +1,13 @@ -### Current Version: v0.2.8 +### Current Version: v0.2.9 ### Test: #### docker build --tag eosjs-ci:test ./.github/eosjs-ci #### docker run --publish 8888:8888 eosjs-ci:test ### Deploy: -#### docker build --tag eosio/eosjs-ci:v0.2.8 ./.github/eosjs-ci -#### docker push eosio/eosjs-ci:v0.2.8 +#### docker build --tag eosio/eosjs-ci:v0.2.9 ./.github/eosjs-ci +#### docker push eosio/eosjs-ci:v0.2.9 FROM ubuntu:18.04 -ENTRYPOINT ["nodeos", "--data-dir", "/root/.local/share", "-e", "-p", "eosio", "--replay-blockchain", "--plugin", "eosio::producer_plugin", "--plugin", "eosio::chain_api_plugin", "--plugin", "eosio::http_plugin", "--http-server-address=0.0.0.0:8888", "--access-control-allow-origin=*", "--contracts-console", "--http-validate-host=false", "--verbose-http-errors", "--max-transaction-time=100"] +ENTRYPOINT ["nodeos", "--data-dir", "/root/.local/share", "-e", "-p", "eosio", "--replay-blockchain", "--plugin", "eosio::producer_plugin", "--plugin", "eosio::chain_api_plugin", "--plugin", "eosio::trace_api_plugin", "--trace-no-abis", "--plugin", "eosio::db_size_api_plugin", "--plugin", "eosio::http_plugin", "--http-server-address=0.0.0.0:8888", "--access-control-allow-origin=*", "--contracts-console", "--http-validate-host=false", "--enable-account-queries=true", "--verbose-http-errors", "--max-transaction-time=100"] ### base RUN yes | unminimize \ diff --git a/.github/eosjs-ci/scripts/deploy_contracts.sh b/.github/eosjs-ci/scripts/deploy_contracts.sh index 4975c74ae..00f990f6c 100644 --- a/.github/eosjs-ci/scripts/deploy_contracts.sh +++ b/.github/eosjs-ci/scripts/deploy_contracts.sh @@ -117,10 +117,14 @@ if [ -z "$NODEOS_RUNNING" ]; then --http-validate-host=false \ --plugin eosio::producer_api_plugin \ --plugin eosio::chain_api_plugin \ + --plugin eosio::trace_api_plugin \ + --trace-no-abis \ + --plugin eosio::db_size_api_plugin \ --plugin eosio::http_plugin \ --http-server-address=0.0.0.0:8888 \ --access-control-allow-origin=* \ --contracts-console \ + --enable-account-queries=true \ --max-transaction-time=100000 \ --verbose-http-errors & fi @@ -147,6 +151,15 @@ cleos wallet import --private-key $EXAMPLE_ACCOUNT_PRIVATE_KEY cleos wallet import --private-key $R1_EXAMPLE_ACCOUNT_PRIVATE_KEY cleos wallet import --private-key $CFHELLO_PRIVATE_KEY cleos wallet import --private-key $CFACTOR_PRIVATE_KEY +cleos create account eosio eosio.bpay $SYSTEM_ACCOUNT_PUBLIC_KEY +cleos create account eosio eosio.msig $SYSTEM_ACCOUNT_PUBLIC_KEY +cleos create account eosio eosio.names $SYSTEM_ACCOUNT_PUBLIC_KEY +cleos create account eosio eosio.ram $SYSTEM_ACCOUNT_PUBLIC_KEY +cleos create account eosio eosio.ramfee $SYSTEM_ACCOUNT_PUBLIC_KEY +cleos create account eosio eosio.saving $SYSTEM_ACCOUNT_PUBLIC_KEY +cleos create account eosio eosio.stake $SYSTEM_ACCOUNT_PUBLIC_KEY +cleos create account eosio eosio.vpay $SYSTEM_ACCOUNT_PUBLIC_KEY +cleos create account eosio eosio.rex $SYSTEM_ACCOUNT_PUBLIC_KEY cleos create account eosio eosio.token $SYSTEM_ACCOUNT_PUBLIC_KEY cleos create account eosio returnvalue $SYSTEM_ACCOUNT_PUBLIC_KEY cleos create account eosio todo $SYSTEM_ACCOUNT_PUBLIC_KEY @@ -189,6 +202,14 @@ sleep 1s cleos push action eosio setkvparams '[{"max_key_size":1024, "max_value_size":4096, "max_iterators":1024}]' -p eosio@active cleos push action eosio setpparams '["01110000400100000000"]' -p eosio@active +sleep 1s +setabi eosio $CONTRACTS_DIR/eosio.system/eosio.system.abi +setcode eosio $CONTRACTS_DIR/eosio.system/eosio.system.wasm + +sleep 1s +setabi eosio.msig $CONTRACTS_DIR/eosio.msig/eosio.msig.abi +setcode eosio.msig $CONTRACTS_DIR/eosio.msig/eosio.msig.wasm + sleep 1s setabi cfhello $CONTRACTS_DIR/cfhello/cfhello.abi setcode cfhello $CONTRACTS_DIR/cfhello/cfhello.wasm @@ -216,6 +237,8 @@ cleos push action todo upsert '["bf581bee-9f2c-447b-94ad-78e4984b6f51", "todo", cleos push action todo upsert '["b7b0d09d-a82b-44d9-b067-3bae2d02917e", "todo", "Start Blockchain", false]' -p todo@active cleos push action todo upsert '["ac8acfe7-cd4e-4d22-8400-218b697a4517", "todo", "Deploy Hello World Contract", false]' -p todo@active +cleos push action eosio init '[0, "4,SYS"]' -p eosio@active + echo "All done initializing the blockchain" if [[ -z $NODEOS_RUNNING ]]; then diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 94e3a1bee..85db35228 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,9 @@ jobs: - name: Test Node run: | yarn test-node + - name: Test Types + run: | + yarn test-types - name: Build run: | yarn build-web @@ -73,7 +76,7 @@ jobs: git push origin ${GITHUB_REF#refs/*/} services: nodeos: - image: eosio/eosjs-ci:v0.2.8 + image: eosio/eosjs-ci:v0.2.9 ports: - 8888:8888 diff --git a/.github/workflows/publish-edge.yml b/.github/workflows/publish-edge.yml index 4c1fa4210..ab3dbb4a1 100644 --- a/.github/workflows/publish-edge.yml +++ b/.github/workflows/publish-edge.yml @@ -38,6 +38,9 @@ jobs: - name: Test Node run: | yarn test-node + - name: Test Types + run: | + yarn test-types - name: Build run: | yarn build-web @@ -58,7 +61,7 @@ jobs: services: nodeos: - image: eosio/eosjs-ci:v0.2.8 + image: eosio/eosjs-ci:v0.2.9 ports: - 8888:8888 diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 8b50a6bc3..9111514c7 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -38,6 +38,9 @@ jobs: - name: Test Node run: | yarn test-node + - name: Test Types + run: | + yarn test-types - name: Build run: | yarn build-web @@ -57,7 +60,7 @@ jobs: services: nodeos: - image: eosio/eosjs-ci:v0.2.8 + image: eosio/eosjs-ci:v0.2.9 ports: - 8888:8888 @@ -98,6 +101,9 @@ jobs: - name: Test Node run: | yarn test-node + - name: Test Types + run: | + yarn test-types - name: Build run: | yarn build-web @@ -117,7 +123,7 @@ jobs: services: nodeos: - image: eosio/eosjs-ci:v0.2.8 + image: eosio/eosjs-ci:v0.2.9 ports: - 8888:8888 diff --git a/package.json b/package.json index b8d5701e3..6fb655e6c 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "lint": "eslint --ext .js,.jsx,.ts,.tsx src", "test": "jest src/tests/*eosjs*", "test-node": "jest src/tests/*node*", - "test-all": "yarn test && yarn test-node && yarn cypress", + "test-types": "jest src/tests/type-checks.test.ts", + "test-all": "yarn test && yarn test-node && yarn test-types && yarn cypress", "build": "rimraf dist && tsc -p ./tsconfig.json && node scripts/copy-ripe-md.js", "build-web": "webpack --config webpack.prod.js && webpack --config webpack.debug.js", "build-production": "yarn build && yarn build-web && yarn test-all", @@ -35,13 +36,15 @@ "@blockone/eslint-config-blockone": "^3.0.0", "@types/elliptic": "^6.4.12", "@types/jest": "^26.0.20", - "@types/node": "^14.14.25", + "@types/node": "^14.14.27", + "@types/node-fetch": "^2.5.8", "@types/pako": "^1.0.1", "clean-webpack-plugin": "^3.0.0", "cypress": "^6.4.0", "eosjs-ecc": "^4.0.7", "eslint": "^6.8.0", "jest": "^26.6.3", + "jest-extended": "^0.11.5", "jest-fetch-mock": "^3.0.3", "rimraf": "^3.0.2", "ts-jest": "^26.5.1", @@ -55,6 +58,9 @@ "setupFiles": [ "./src/tests/setupJest.js" ], + "setupFilesAfterEnv": [ + "jest-extended" + ], "moduleFileExtensions": [ "ts", "tsx", diff --git a/src/PrivateKey.ts b/src/PrivateKey.ts index 2f344005c..b2de91b37 100644 --- a/src/PrivateKey.ts +++ b/src/PrivateKey.ts @@ -33,7 +33,7 @@ export class PrivateKey { } /** Export private key as `elliptic`-format private key */ - public toElliptic() { + public toElliptic(): EC.KeyPair { return this.ec.keyFromPrivate(this.key.data); } @@ -67,10 +67,10 @@ export class PrivateKey { } let tries = 0; let signature: Signature; - const isCanonical = (sigData: Uint8Array) => + const isCanonical = (sigData: Uint8Array): boolean => !(sigData[1] & 0x80) && !(sigData[1] === 0 && !(sigData[2] & 0x80)) && !(sigData[33] & 0x80) && !(sigData[33] === 0 && !(sigData[34] & 0x80)); - const constructSignature = (options: EC.SignOptions) => { + const constructSignature = (options: EC.SignOptions): Signature => { const ellipticPrivateKey = this.toElliptic(); const ellipticSignature = ellipticPrivateKey.sign(data, options); return Signature.fromElliptic(ellipticSignature, this.getType(), this.ec); diff --git a/src/eosjs-api-interfaces.ts b/src/eosjs-api-interfaces.ts index 91feaa527..2203e3510 100644 --- a/src/eosjs-api-interfaces.ts +++ b/src/eosjs-api-interfaces.ts @@ -5,7 +5,6 @@ import { Abi, PushTransactionArgs } from './eosjs-rpc-interfaces'; import { Anyvar, Authorization, Action, SerializedAction } from './eosjs-serialize'; -import { ActionBuilder } from './eosjs-api'; /** Arguments to `getRequiredKeys` */ export interface AuthorityProviderArgs { @@ -73,11 +72,6 @@ export interface SignatureProvider { sign: (args: SignatureProviderArgs) => Promise; } -export interface Extension { - type: number; - data: string; -} - export interface Transaction { expiration?: string; ref_block_num?: number; @@ -88,7 +82,7 @@ export interface Transaction { context_free_actions?: Action[]; context_free_data?: Uint8Array[]; actions: Action[]; - transaction_extensions?: Extension[]; + transaction_extensions?: [number, string][]; } /** Optional transact configuration object */ @@ -102,6 +96,12 @@ export interface TransactConfig { expireSeconds?: number; } +export interface TransactionHeader { + expiration: string; + ref_block_num: number; + ref_block_prefix: number; +} + export interface AccountDelta { account: string; delta: number; @@ -117,7 +117,7 @@ export interface ActionReceipt { act_digest: string; global_sequence: number; recv_sequence: number; - auth_sequence: AuthSequence[]; + auth_sequence: [ string, number ][]; code_sequence: number; abi_sequence: number; } @@ -140,10 +140,10 @@ export interface ActionTrace { account_disk_deltas: AccountDelta[]; except: any; error_code: number|null; - return_value: any; - return_value_hex?: string; + return_value?: any; + return_value_hex_data?: string; return_value_data?: any; - inline_traces: ActionTrace[]; + inline_traces?: ActionTrace[]; } export interface TransactionReceiptHeader { diff --git a/src/eosjs-api.ts b/src/eosjs-api.ts index d8e69293b..c1399901d 100644 --- a/src/eosjs-api.ts +++ b/src/eosjs-api.ts @@ -31,7 +31,6 @@ import { GetBlockResult } from './eosjs-rpc-interfaces'; import * as ser from './eosjs-serialize'; -import { RpcError } from './eosjs-rpcerror'; const transactionAbi = require('../src/transaction.abi.json'); @@ -390,7 +389,7 @@ export class Api { signatures, compression: 0, serializedTransaction - }); + }) as any; const returnBuffer = new ser.SerialBuffer({ textEncoder: this.textEncoder, @@ -436,7 +435,7 @@ export class Api { blocksBehind: number | undefined, useLastIrreversible: boolean | undefined, expireSeconds: number - ) { + ): Promise { if (!info) { info = await this.rpc.get_info(); } @@ -447,7 +446,7 @@ export class Api { const taposBlockNumber: number = info.head_block_num - blocksBehind; - const refBlock: GetBlockHeaderStateResult | GetBlockResult = + const refBlock: GetBlockHeaderStateResult | GetBlockResult | GetBlockInfoResult = taposBlockNumber <= info.last_irreversible_block_num ? await this.tryGetBlockInfo(taposBlockNumber) : await this.tryGetBlockHeaderState(taposBlockNumber); @@ -456,11 +455,11 @@ export class Api { } // eventually break out into TransactionValidator class - private hasRequiredTaposFields({ expiration, ref_block_num, ref_block_prefix }: any): boolean { + private hasRequiredTaposFields({ expiration, ref_block_num, ref_block_prefix }: Transaction): boolean { return !!(expiration && typeof(ref_block_num) === 'number' && typeof(ref_block_prefix) === 'number'); } - private async tryGetBlockHeaderState(taposBlockNumber: number): Promise + private async tryGetBlockHeaderState(taposBlockNumber: number): Promise { try { return await this.rpc.get_block_header_state(taposBlockNumber); @@ -502,7 +501,7 @@ export class Api { return new ActionBuilder(this, accountName); } - public buildTransaction(cb?: (tx: TransactionBuilder) => void) { + public buildTransaction(cb?: (tx: TransactionBuilder) => void): TransactionBuilder|void { const tx = new TransactionBuilder(this); if (cb) { return cb(tx); @@ -514,7 +513,7 @@ export class Api { export class TransactionBuilder { private api: Api; private actions: ActionBuilder[] = []; - private contextFreeGroups: any[] = []; + private contextFreeGroups: ContextFreeGroupCallback[] = []; constructor(api: Api) { this.api = api; } @@ -525,7 +524,7 @@ export class TransactionBuilder { return actionBuilder; } - public associateContextFree(contextFreeGroup: ContextFreeGroupCallback) { + public associateContextFree(contextFreeGroup: ContextFreeGroupCallback): TransactionBuilder { this.contextFreeGroups.push(contextFreeGroup); return this; } @@ -571,7 +570,7 @@ export class ActionBuilder { this.accountName = accountName; } - public as(actorName: string | ser.Authorization[] = []) { + public as(actorName: string | ser.Authorization[] = []): ActionSerializerType { let authorization: ser.Authorization[] = []; if (actorName && typeof actorName === 'string') { authorization = [{ actor: actorName, permission: 'active'}]; diff --git a/src/eosjs-ecc-migration.ts b/src/eosjs-ecc-migration.ts index 20c2f7298..6f3b82f2e 100644 --- a/src/eosjs-ecc-migration.ts +++ b/src/eosjs-ecc-migration.ts @@ -4,8 +4,8 @@ import {KeyType} from './eosjs-numeric'; import {ec as EC} from 'elliptic'; export const ecc = { - initialize: () => console.error('Method deprecated'), - unsafeRandomKey: () => console.error('Method deprecated'), + initialize: (): void => console.error('Method deprecated'), + unsafeRandomKey: (): void => console.error('Method deprecated'), randomKey: ( cpuEntropyBits?: number, options: { secureEnv?: boolean, ecOptions?: EC.GenKeyPairOptions } = {} ): Promise => { @@ -17,7 +17,7 @@ export const ecc = { const { privateKey } = generateKeyPair(KeyType.k1, options); return Promise.resolve(privateKey.toLegacyString()); }, - seedPrivate: () => console.error('Method deprecated'), + seedPrivate: (): void => console.error('Method deprecated'), privateToPublic: (key: string, pubkey_prefix?: string): string => { if (pubkey_prefix !== undefined) { console.warn('Argument `pubkey_prefix` is deprecated, ' + @@ -54,7 +54,7 @@ export const ecc = { const signature = privKey.sign(data, true, encoding); return signature.toString(); }, - signHash: (dataSha256: string|Buffer, privateKey: string|PrivateKey, encoding: BufferEncoding = 'hex') => { + signHash: (dataSha256: string|Buffer, privateKey: string|PrivateKey, encoding: BufferEncoding = 'hex'): string => { const privKey = typeof privateKey === 'string' ? PrivateKey.fromString(privateKey) : privateKey; const signature = privKey.sign(dataSha256, false, encoding); return signature.toString(); diff --git a/src/eosjs-jsonrpc.ts b/src/eosjs-jsonrpc.ts index 3edd1c122..374a8a613 100644 --- a/src/eosjs-jsonrpc.ts +++ b/src/eosjs-jsonrpc.ts @@ -3,22 +3,43 @@ */ // copyright defined in eosjs/LICENSE.txt -import { AbiProvider, AuthorityProvider, AuthorityProviderArgs, BinaryAbi } from './eosjs-api-interfaces'; +import { AbiProvider, AuthorityProvider, AuthorityProviderArgs, BinaryAbi, TransactResult } from './eosjs-api-interfaces'; import { base64ToBinary, convertLegacyPublicKeys } from './eosjs-numeric'; import { + AbiBinToJsonResult, + AbiJsonToBinResult, GetAbiResult, + GetAccountResult, + GetAccountsByAuthorizersResult, + GetActivatedProtocolFeaturesParams, + GetActivatedProtocolFeaturesResult, GetBlockInfoResult, GetBlockResult, GetCodeResult, + GetCodeHashResult, + GetCurrencyStatsResult, GetInfoResult, + GetProducerScheduleResult, + GetProducersResult, GetRawCodeAndAbiResult, GetRawAbiResult, + GetScheduledTransactionsResult, + GetTableRowsResult, PushTransactionArgs, - GetBlockHeaderStateResult + PackedTrx, + GetBlockHeaderStateResult, + GetTableByScopeResult, + DBSizeGetResult, + TraceApiGetBlockResult, + GetActionsResult, + GetTransactionResult, + GetKeyAccountsResult, + GetControlledAccountsResult, } from './eosjs-rpc-interfaces'; +import { Authorization } from './eosjs-serialize'; import { RpcError } from './eosjs-rpcerror'; -const arrayToHex = (data: Uint8Array) => { +const arrayToHex = (data: Uint8Array): string => { let result = ''; for (const x of data) { result += ('00' + x.toString(16)).slice(-2); @@ -52,7 +73,7 @@ export class JsonRpc implements AuthorityProvider, AbiProvider { } /** Post `body` to `endpoint + path`. Throws detailed error information in `RpcError` when available. */ - public async fetch(path: string, body: any) { + public async fetch(path: string, body: any): Promise { let response; let json; try { @@ -75,16 +96,48 @@ export class JsonRpc implements AuthorityProvider, AbiProvider { return json; } + public async abi_bin_to_json( + code: string, + action: string, + binargs: string, + ): Promise { + return await this.fetch('/v1/chain/abi_bin_to_json', { code, action, binargs }); + } + + public async abi_json_to_bin( + code: string, + action: string, + args: any[], + ): Promise { + return await this.fetch('/v1/chain/abi_json_to_bin', { code, action, args }); + } + /** Raw call to `/v1/chain/get_abi` */ public async get_abi(accountName: string): Promise { return await this.fetch('/v1/chain/get_abi', { account_name: accountName }); } /** Raw call to `/v1/chain/get_account` */ - public async get_account(accountName: string): Promise { + public async get_account(accountName: string): Promise { return await this.fetch('/v1/chain/get_account', { account_name: accountName }); } + /** Raw call to `/v1/chain/get_accounts_by_authorizers` */ + public async get_accounts_by_authorizers(accounts: Authorization[], keys: string[]): Promise { + return await this.fetch('/v1/chain/get_accounts_by_authorizers', { accounts, keys }); + } + + /** Raw call to `get_activated_protocol_features` */ + public async get_activated_protocol_features({ + limit = 10, + search_by_block_num = false, + reverse = false, + lower_bound = null, + upper_bound = null, + }: GetActivatedProtocolFeaturesParams): Promise { + return await this.fetch('/v1/chain/get_activated_protocol_features', { lower_bound, upper_bound, limit, search_by_block_num, reverse }); + } + /** Raw call to `/v1/chain/get_block_header_state` */ public async get_block_header_state(blockNumOrId: number | string): Promise { return await this.fetch('/v1/chain/get_block_header_state', { block_num_or_id: blockNumOrId }); @@ -108,13 +161,18 @@ export class JsonRpc implements AuthorityProvider, AbiProvider { }); } + /** Raw call to `/v1/chain/get_code_hash` */ + public async get_code_hash(accountName: string): Promise { + return await this.fetch('/v1/chain/get_code_hash', { account_name: accountName }); + } + /** Raw call to `/v1/chain/get_currency_balance` */ - public async get_currency_balance(code: string, account: string, symbol: string = null): Promise { + public async get_currency_balance(code: string, account: string, symbol: string = null): Promise { return await this.fetch('/v1/chain/get_currency_balance', { code, account, symbol }); } /** Raw call to `/v1/chain/get_currency_stats` */ - public async get_currency_stats(code: string, symbol: string): Promise { + public async get_currency_stats(code: string, symbol: string): Promise { return await this.fetch('/v1/chain/get_currency_stats', { code, symbol }); } @@ -124,12 +182,12 @@ export class JsonRpc implements AuthorityProvider, AbiProvider { } /** Raw call to `/v1/chain/get_producer_schedule` */ - public async get_producer_schedule(): Promise { + public async get_producer_schedule(): Promise { return await this.fetch('/v1/chain/get_producer_schedule', {}); } /** Raw call to `/v1/chain/get_producers` */ - public async get_producers(json = true, lowerBound = '', limit = 50): Promise { + public async get_producers(json = true, lowerBound = '', limit = 50): Promise { return await this.fetch('/v1/chain/get_producers', { json, lower_bound: lowerBound, limit }); } @@ -151,7 +209,7 @@ export class JsonRpc implements AuthorityProvider, AbiProvider { } /** Raw call to `/v1/chain/get_scheduled_transactions` */ - public async get_scheduled_transactions(json = true, lowerBound = '', limit = 50): Promise { + public async get_scheduled_transactions(json = true, lowerBound = '', limit = 50): Promise { return await this.fetch('/v1/chain/get_scheduled_transactions', { json, lower_bound: lowerBound, limit }); } @@ -168,7 +226,7 @@ export class JsonRpc implements AuthorityProvider, AbiProvider { limit = 10, reverse = false, show_payer = false, - }: any): Promise { + }: any): Promise { return await this.fetch( '/v1/chain/get_table_rows', { json, @@ -192,13 +250,13 @@ export class JsonRpc implements AuthorityProvider, AbiProvider { table, index_name, encode_type = 'bytes', - index_value = '', - lower_bound = '', - upper_bound = '', + index_value, + lower_bound, + upper_bound, limit = 10, reverse = false, show_payer = false, - }: any): Promise { + }: any): Promise { return await this.fetch( '/v1/chain/get_kv_table_rows', { json, @@ -222,7 +280,7 @@ export class JsonRpc implements AuthorityProvider, AbiProvider { lower_bound = '', upper_bound = '', limit = 10, - }: any): Promise { + }: any): Promise { return await this.fetch( '/v1/chain/get_table_by_scope', { code, @@ -244,7 +302,7 @@ export class JsonRpc implements AuthorityProvider, AbiProvider { /** Push a serialized transaction (replaced by send_transaction, but returned format has changed) */ public async push_transaction( { signatures, compression = 0, serializedTransaction, serializedContextFreeData }: PushTransactionArgs - ): Promise { + ): Promise { return await this.fetch('/v1/chain/push_transaction', { signatures, compression, @@ -253,10 +311,22 @@ export class JsonRpc implements AuthorityProvider, AbiProvider { }); } + public async push_transactions(transactions: PushTransactionArgs[]): Promise { + const packedTrxs: PackedTrx[] = transactions.map(({signatures, compression = 0, serializedTransaction, serializedContextFreeData }: PushTransactionArgs) => { + return { + signatures, + compression, + packed_context_free_data: arrayToHex(serializedContextFreeData || new Uint8Array(0)), + packed_trx: arrayToHex(serializedTransaction), + }; + }); + return await this.fetch('/v1/chain/push_transactions', packedTrxs ); + } + /** Send a serialized transaction */ public async send_transaction( { signatures, compression = 0, serializedTransaction, serializedContextFreeData }: PushTransactionArgs - ): Promise { + ): Promise { return await this.fetch('/v1/chain/send_transaction', { signatures, compression, @@ -266,25 +336,30 @@ export class JsonRpc implements AuthorityProvider, AbiProvider { } /** Raw call to `/v1/db_size/get` */ - public async db_size_get() { return await this.fetch('/v1/db_size/get', {}); } + public async db_size_get(): Promise { return await this.fetch('/v1/db_size/get', {}); } + + /** Raw call to `/v1/trace_api/get_block` */ + public async trace_get_block(block_num: number): Promise { + return await this.fetch('/v1/trace_api/get_block', { block_num }); + } /** Raw call to `/v1/history/get_actions` */ - public async history_get_actions(accountName: string, pos: number = null, offset: number = null) { + public async history_get_actions(accountName: string, pos: number = null, offset: number = null): Promise { return await this.fetch('/v1/history/get_actions', { account_name: accountName, pos, offset }); } /** Raw call to `/v1/history/get_transaction` */ - public async history_get_transaction(id: string, blockNumHint: number = null) { + public async history_get_transaction(id: string, blockNumHint: number = null): Promise { return await this.fetch('/v1/history/get_transaction', { id, block_num_hint: blockNumHint }); } /** Raw call to `/v1/history/get_key_accounts` */ - public async history_get_key_accounts(publicKey: string) { + public async history_get_key_accounts(publicKey: string): Promise { return await this.fetch('/v1/history/get_key_accounts', { public_key: publicKey }); } /** Raw call to `/v1/history/get_controlled_accounts` */ - public async history_get_controlled_accounts(controllingAccount: string) { + public async history_get_controlled_accounts(controllingAccount: string): Promise { return await this.fetch('/v1/history/get_controlled_accounts', { controlling_account: controllingAccount }); } } // JsonRpc diff --git a/src/eosjs-jssig.ts b/src/eosjs-jssig.ts index 7d5da89e2..0d1671d9d 100644 --- a/src/eosjs-jssig.ts +++ b/src/eosjs-jssig.ts @@ -6,6 +6,7 @@ import { ec } from 'elliptic'; import { SignatureProvider, SignatureProviderArgs } from './eosjs-api-interfaces'; +import { PushTransactionArgs } from './eosjs-rpc-interfaces'; import { PrivateKey, PublicKey, @@ -21,7 +22,7 @@ const digestFromSerializedData = ( chainId: string, serializedTransaction: Uint8Array, serializedContextFreeData?: Uint8Array, - e = defaultEc) => { + e = defaultEc): string => { const signBuf = Buffer.concat([ Buffer.from(chainId, 'hex'), Buffer.from(serializedTransaction), @@ -54,14 +55,14 @@ class JsSignatureProvider implements SignatureProvider { } /** Public keys associated with the private keys that the `SignatureProvider` holds */ - public async getAvailableKeys() { + public async getAvailableKeys(): Promise { return this.availableKeys; } /** Sign a transaction */ public async sign( { chainId, requiredKeys, serializedTransaction, serializedContextFreeData }: SignatureProviderArgs, - ) { + ): Promise { const digest = digestFromSerializedData( chainId, serializedTransaction, serializedContextFreeData, defaultEc); const signatures = [] as string[]; diff --git a/src/eosjs-key-conversions.ts b/src/eosjs-key-conversions.ts index 7feea5991..980cfde71 100644 --- a/src/eosjs-key-conversions.ts +++ b/src/eosjs-key-conversions.ts @@ -37,6 +37,6 @@ export const generateKeyPair = ( return {publicKey, privateKey}; }; -export const sha256 = (data: string|Buffer) => { +export const sha256 = (data: string|Buffer): number[]|string => { return hash.sha256().update(data).digest(); }; diff --git a/src/eosjs-numeric.ts b/src/eosjs-numeric.ts index 8e1fd7ac9..9c5134bc5 100644 --- a/src/eosjs-numeric.ts +++ b/src/eosjs-numeric.ts @@ -10,7 +10,7 @@ const ripemd160 = require('./ripemd').RIPEMD160.hash as (a: Uint8Array) => Array const base58Chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; -const create_base58_map = () => { +const create_base58_map = (): number[] => { const base58M = Array(256).fill(-1) as number[]; for (let i = 0; i < base58Chars.length; ++i) { base58M[base58Chars.charCodeAt(i)] = i; @@ -20,7 +20,7 @@ const create_base58_map = () => { const base58Map = create_base58_map(); -const create_base64_map = () => { +const create_base64_map = (): number[] => { const base64M = Array(256).fill(-1) as number[]; for (let i = 0; i < base64Chars.length; ++i) { base64M[base64Chars.charCodeAt(i)] = i; @@ -32,12 +32,12 @@ const create_base64_map = () => { const base64Map = create_base64_map(); /** Is `bignum` a negative number? */ -export const isNegative = (bignum: Uint8Array) => { +export const isNegative = (bignum: Uint8Array): boolean => { return (bignum[bignum.length - 1] & 0x80) !== 0; }; /** Negate `bignum` */ -export const negate = (bignum: Uint8Array) => { +export const negate = (bignum: Uint8Array): void => { let carry = 1; for (let i = 0; i < bignum.length; ++i) { const x = (~bignum[i] & 0xff) + carry; @@ -51,7 +51,7 @@ export const negate = (bignum: Uint8Array) => { * * @param size bignum size (bytes) */ -export const decimalToBinary = (size: number, s: string) => { +export const decimalToBinary = (size: number, s: string): Uint8Array => { const result = new Uint8Array(size); for (let i = 0; i < s.length; ++i) { const srcDigit = s.charCodeAt(i); @@ -76,7 +76,7 @@ export const decimalToBinary = (size: number, s: string) => { * * @param size bignum size (bytes) */ -export const signedDecimalToBinary = (size: number, s: string) => { +export const signedDecimalToBinary = (size: number, s: string): Uint8Array => { const negative = s[0] === '-'; if (negative) { s = s.substr(1); @@ -98,7 +98,7 @@ export const signedDecimalToBinary = (size: number, s: string) => { * * @param minDigits 0-pad result to this many digits */ -export const binaryToDecimal = (bignum: Uint8Array, minDigits = 1) => { +export const binaryToDecimal = (bignum: Uint8Array, minDigits = 1): string => { const result = Array(minDigits).fill('0'.charCodeAt(0)) as number[]; for (let i = bignum.length - 1; i >= 0; --i) { let carry = bignum[i]; @@ -121,7 +121,7 @@ export const binaryToDecimal = (bignum: Uint8Array, minDigits = 1) => { * * @param minDigits 0-pad result to this many digits */ -export const signedBinaryToDecimal = (bignum: Uint8Array, minDigits = 1) => { +export const signedBinaryToDecimal = (bignum: Uint8Array, minDigits = 1): string => { if (isNegative(bignum)) { const x = bignum.slice(); negate(x); @@ -130,7 +130,7 @@ export const signedBinaryToDecimal = (bignum: Uint8Array, minDigits = 1) => { return binaryToDecimal(bignum, minDigits); }; -const base58ToBinaryVarSize = (s: string) => { +const base58ToBinaryVarSize = (s: string): Uint8Array => { const result = [] as number[]; for (let i = 0; i < s.length; ++i) { let carry = base58Map[s.charCodeAt(i)]; @@ -162,7 +162,7 @@ const base58ToBinaryVarSize = (s: string) => { * * @param size bignum size (bytes) */ -export const base58ToBinary = (size: number, s: string) => { +export const base58ToBinary = (size: number, s: string): Uint8Array => { if (!size) { return base58ToBinaryVarSize(s); } @@ -190,7 +190,7 @@ export const base58ToBinary = (size: number, s: string) => { * * @param minDigits 0-pad result to this many digits */ -export const binaryToBase58 = (bignum: Uint8Array, minDigits = 1) => { +export const binaryToBase58 = (bignum: Uint8Array, minDigits = 1): string => { const result = [] as number[]; for (const byte of bignum) { let carry = byte; @@ -216,7 +216,7 @@ export const binaryToBase58 = (bignum: Uint8Array, minDigits = 1) => { }; /** Convert an unsigned base-64 number in `s` to a bignum */ -export const base64ToBinary = (s: string) => { +export const base64ToBinary = (s: string): Uint8Array => { let len = s.length; if ((len & 3) === 1 && s[len - 1] === '=') { len -= 1; @@ -273,7 +273,7 @@ export interface Key { data: Uint8Array; } -const digestSuffixRipemd160 = (data: Uint8Array, suffix: string) => { +const digestSuffixRipemd160 = (data: Uint8Array, suffix: string): ArrayBuffer => { const d = new Uint8Array(data.length + suffix.length); for (let i = 0; i < data.length; ++i) { d[i] = data[i]; @@ -295,7 +295,7 @@ const stringToKey = (s: string, type: KeyType, size: number, suffix: string): Ke return result; }; -const keyToString = (key: Key, suffix: string, prefix: string) => { +const keyToString = (key: Key, suffix: string, prefix: string): string => { const digest = new Uint8Array(digestSuffixRipemd160(key.data, suffix)); const whole = new Uint8Array(key.data.length + 4); for (let i = 0; i < key.data.length; ++i) { @@ -336,7 +336,7 @@ export const stringToPublicKey = (s: string): Key => { }; /** Convert public `key` to legacy string (base-58) form */ -export const publicKeyToLegacyString = (key: Key) => { +export const publicKeyToLegacyString = (key: Key): string => { if (key.type === KeyType.k1 && key.data.length === publicKeyDataSize) { return keyToString(key, '', 'EOS'); } else if (key.type === KeyType.r1 || key.type === KeyType.wa) { @@ -347,7 +347,7 @@ export const publicKeyToLegacyString = (key: Key) => { }; /** Convert `key` to string (base-58) form */ -export const publicKeyToString = (key: Key) => { +export const publicKeyToString = (key: Key): string => { if (key.type === KeyType.k1 && key.data.length === publicKeyDataSize) { return keyToString(key, 'K1', 'PUB_K1_'); } else if (key.type === KeyType.r1 && key.data.length === publicKeyDataSize) { @@ -362,7 +362,7 @@ export const publicKeyToString = (key: Key) => { /** If a key is in the legacy format (`EOS` prefix), then convert it to the new format (`PUB_K1_`). * Leaves other formats untouched */ -export const convertLegacyPublicKey = (s: string) => { +export const convertLegacyPublicKey = (s: string): string => { if (s.substr(0, 3) === 'EOS') { return publicKeyToString(stringToPublicKey(s)); } @@ -372,7 +372,7 @@ export const convertLegacyPublicKey = (s: string) => { /** If a key is in the legacy format (`EOS` prefix), then convert it to the new format (`PUB_K1_`). * Leaves other formats untouched */ -export const convertLegacyPublicKeys = (keys: string[]) => { +export const convertLegacyPublicKeys = (keys: string[]): string[] => { return keys.map(convertLegacyPublicKey); }; @@ -402,7 +402,7 @@ export const stringToPrivateKey = (s: string): Key => { }; /** Convert private `key` to legacy string (base-58) form */ -export const privateKeyToLegacyString = (key: Key) => { +export const privateKeyToLegacyString = (key: Key): string => { if (key.type === KeyType.k1 && key.data.length === privateKeyDataSize) { const whole = [] as number[]; whole.push(128); @@ -429,7 +429,7 @@ export const privateKeyToLegacyString = (key: Key) => { }; /** Convert `key` to string (base-58) form */ -export const privateKeyToString = (key: Key) => { +export const privateKeyToString = (key: Key): string => { if (key.type === KeyType.r1) { return keyToString(key, 'R1', 'PVT_R1_'); } else if (key.type === KeyType.k1) { @@ -456,7 +456,7 @@ export const stringToSignature = (s: string): Key => { }; /** Convert `signature` to string (base-58) form */ -export const signatureToString = (signature: Key) => { +export const signatureToString = (signature: Key): string => { if (signature.type === KeyType.k1) { return keyToString(signature, 'K1', 'SIG_K1_'); } else if (signature.type === KeyType.r1) { diff --git a/src/eosjs-rpc-interfaces.ts b/src/eosjs-rpc-interfaces.ts index 80162e867..22ea64e78 100644 --- a/src/eosjs-rpc-interfaces.ts +++ b/src/eosjs-rpc-interfaces.ts @@ -3,6 +3,9 @@ * copyright defined in eosjs/LICENSE.txt */ +import { TransactionReceiptHeader, Transaction } from './eosjs-api-interfaces'; +import { Authorization } from './eosjs-serialize'; + /** Structured format for abis */ export interface Abi { version: string; @@ -11,9 +14,11 @@ export interface Abi { actions: { name: string, type: string, ricardian_contract: string }[]; tables: { name: string, type: string, index_type: string, key_names: string[], key_types: string[] }[]; ricardian_clauses: { id: string, body: string }[]; - error_messages: { error_code: string, error_msg: string }[]; + error_messages: { error_code: number, error_msg: string }[]; abi_extensions: { tag: number, value: string }[]; variants?: { name: string, types: string[] }[]; + action_results?: { name: string, result_type: string }[], + kv_tables?: { [key: string]: { type: string, primary_index: { name: string, type: string }, secondary_indices: { [key: string]: { type: string }}[] } }[], } export interface BlockHeader { @@ -24,18 +29,173 @@ export interface BlockHeader { transaction_mroot: string; action_mroot: string; schedule_version: number; - new_producers: any; - header_extensions: any; + new_producers?: ProducerScheduleType; + header_extensions: [number, string][]; } export interface SignedBlockHeader extends BlockHeader { producer_signature: string; } +export interface AccountResourceInfo { + used: number; + available: number; + max: number; + last_usage_update_time?: string; + current_used?: number; +} + +export interface ResourceOverview { + owner: string; + ram_bytes: number; + net_weight: string; + cpu_weight: string; +} + +export interface ResourceDelegation { + from: string; + to: string; + net_weight: string; + cpu_weight: string; +} + +export interface RefundRequest { + owner: string; + request_time: string; + net_amount: string; + cpu_amount: string; +} + +export interface VoterInfo { + owner: string; + proxy: string; + producers: string[]; + staked: number; + last_vote_weight: string; + proxied_vote_weight: string; + is_proxy: number; + flags1: number; + reserved2: number; + reserved3: string; +} + +export interface RexBalance { + version: number; + owner: string; + vote_stake: string; + rex_balance: string; + matured_rex: number; + rex_maturities: any; +} + +export interface Authority { + threshold: number; + keys: KeyWeight[]; + accounts: PermissionLevelWeight[]; + waits: WaitWeight[]; +} + +export interface KeyWeight { + key: string; + weight: number; +} + +export interface Permission { + perm_name: string; + parent: string; + required_auth: Authority; +} + +export interface PermissionLevel { + actor: string; + permission: string; +} + +export interface PermissionLevelWeight { + permission: PermissionLevel; + weight: number; +} + +export interface WaitWeight { + wait_sec: number; + weight: number; +} + +/** Return value of `/v1/chain/abi_bin_to_json` */ +export interface AbiBinToJsonResult { + args: 'any' +} + +/** Return value of `/v1/chain/abi_json_to_bin` */ +export interface AbiJsonToBinResult { + binargs: 'string' +} + /** Return value of `/v1/chain/get_abi` */ export interface GetAbiResult { account_name: string; - abi: Abi; + abi?: Abi; +} + +/** Return value of `/v1/chain/get_account` */ +export interface GetAccountResult { + account_name: string; + head_block_num: number; + head_block_time: string; + privileged: boolean; + last_code_update: string; + created: string; + core_liquid_balance?: string; + ram_quota: number; + net_weight: number; + cpu_weight: number; + net_limit: AccountResourceInfo; + cpu_limit: AccountResourceInfo; + ram_usage: number; + permissions: Permission[]; + total_resources: ResourceOverview|null; + self_delegated_bandwidth: ResourceDelegation|null; + refund_request: RefundRequest|null; + voter_info: any; + rex_info: any; +} + +export interface AccountResult { + account_name: string; + permission_name: string; + authorizing_account?: Authorization; + authorizing_key?: string; + weight: number; + threshold: number; +} + +/** Return value of `/v1/chain/get_accounts_by_authorizers` */ +export interface GetAccountsByAuthorizersResult { + accounts: AccountResult[]; +} + +export interface GetActivatedProtocolFeaturesParams { + limit?: number; + search_by_block_num?: boolean; + reverse?: boolean; + lower_bound?: number; + upper_bound?: number; +} + +export interface ActivatedProtocolFeature { + feature_digest: string; + activation_ordinal: number; + activation_block_num: number; + description_digest: string; + dependencies: string[]; + protocol_feature_type: string; + specification: { name: string, value: string, }; +} + +/** Return value of `/v1/chain/get_activated_protocol_features` */ +export interface GetActivatedProtocolFeaturesResult { + activated_protocol_features: ActivatedProtocolFeature[]; + more?: number; } /** Return value of `/v1/chain/get_block_info` */ @@ -50,9 +210,31 @@ export interface GetBlockInfoResult { producer_signature: string; id: string; block_num: number; + ref_block_num: number; ref_block_prefix: number; } +export interface PackedTransaction { + id: string; + signatures: string[]; + compression: number|string; + packed_context_free_data: string; + context_free_data: string[]; + packed_trx: string; + transaction: Transaction; +} + +export interface PackedTrx { + signatures: string[]; + compression: number; + packed_trx: string; + packed_context_free_data: string; +} + +export interface TransactionReceipt extends TransactionReceiptHeader { + trx: PackedTransaction; +} + /** Return value of `/v1/chain/get_block` */ export interface GetBlockResult { timestamp: string; @@ -62,7 +244,9 @@ export interface GetBlockResult { transaction_mroot: string; action_mroot: string; schedule_version: number; + new_producers: ProducerScheduleType|null; producer_signature: string; + transactions: any; id: string; block_num: number; ref_block_prefix: number; @@ -76,21 +260,63 @@ export interface BlockTaposInfo { header?: BlockHeader; } -/** Return value of `v1/chain/get_block_header_state */ +export interface ProducerKey { + producer_name: string; + block_signing_key: string; +} + +export interface BlockSigningAuthority { + threshold: number; + keys: KeyWeight[]; +} + +export interface ProducerAuthority { + producer_name: string; + authority: [ number|string, BlockSigningAuthority]; +}; + +export interface ProducerAuthoritySchedule { + version: number; + producers: ProducerAuthority[]; +} + +export interface ProducerScheduleType { + version: number; + producers: ProducerKey[]; +} + +export interface ScheduleInfo { + schedule_lib_num: number; + schedule_hash: string; + schedule: ProducerScheduleType; +} + +export interface IncrementalMerkle { + _active_nodes: string[]; + _node_count: number; +} + +export interface ProtocolFeatureActivationSet { + protocol_features: string[] +} + +/** Return value of `/v1/chain/get_block_header_state` */ export interface GetBlockHeaderStateResult { id: string; header: SignedBlockHeader; - pending_schedule: any; - activated_protocol_features: any; + pending_schedule: ScheduleInfo; + activated_protocol_features: ProtocolFeatureActivationSet; + additional_signatures: string[]; block_num: number; dpos_proposed_irreversible_blocknum: number; dpos_irreversible_blocknum: number; - active_schedule: any; - blockroot_merkle: any; - producer_to_last_produced: any; - producer_to_last_implied_irb: any; - block_signing_key: string; - confirm_count: any; + active_schedule: ProducerAuthoritySchedule; + blockroot_merkle: IncrementalMerkle; + producer_to_last_produced: Map; + producer_to_last_implied_irb: Map; + // valid_block_signing_authority: BlockSigningAuthority; + valid_block_signing_authority: any; + confirm_count: number[]; } /** Subset of `GetBlockHeaderStateResult` used to calculate TAPoS fields in transactions */ @@ -106,7 +332,22 @@ export interface GetCodeResult { code_hash: string; wast: string; wasm: string; - abi: Abi; + abi?: Abi; +} + +/** Return value of `/v1/chain/get_code_hash` */ +export interface GetCodeHashResult { + account_name: string; + code_hash: string; +} + +/** Return value of `/v1/chain/get_currency_stats` */ +export interface GetCurrencyStatsResult { + [key: string]: { + supply: string; + max_supply: string; + issuer: string; + } } /** Return value of `/v1/chain/get_info` */ @@ -116,7 +357,7 @@ export interface GetInfoResult { head_block_num: number; last_irreversible_block_num: number; last_irreversible_block_id: string; - last_irreversible_block_time: string; + last_irreversible_block_time?: string; head_block_id: string; head_block_time: string; head_block_producer: string; @@ -124,6 +365,36 @@ export interface GetInfoResult { virtual_block_net_limit: number; block_cpu_limit: number; block_net_limit: number; + server_version_string?: string; + fork_db_head_block_num?: number; + fork_db_head_block_id?: string; + server_full_version_string?: string; +} + +/** Return value of /v1/chain/get_producer_schedule */ +export interface GetProducerScheduleResult { + active: ProducerAuthoritySchedule|null; + pending: ProducerAuthoritySchedule|null; + proposed: ProducerAuthoritySchedule|null; +} + +export interface ProducerDetails { + owner: string; + producer_authority?: any[]; + url: string; + is_active?: number; + total_votes: string; + producer_key: string; + unpaid_blocks?: number; + last_claim_time?: string; + location?: number; +} + +/** Return value of `/v1/chain/get_producers` */ +export interface GetProducersResult { + rows: ProducerDetails[]; + total_producer_vote_weight: string; + more: string; } /** Return value of `/v1/chain/get_raw_code_and_abi` */ @@ -133,6 +404,7 @@ export interface GetRawCodeAndAbiResult { abi: string; } +/** Return value of `/v1/chain/get_raw_abi` */ export interface GetRawAbiResult { account_name: string; code_hash: string; @@ -140,6 +412,54 @@ export interface GetRawAbiResult { abi: string; } +export interface DeferredTransaction extends Transaction { + deferred_transaction_generation?: { + sender_trx_id: string; + sender_id: string; + sender: string; + } +} + +export interface GeneratedTransaction { + trx_id: string; + sender: string; + sender_id: string; + payer: string; + delay_until: string; + expiration: string; + published: string; + packed_trx?: string[]; + transaction?: DeferredTransaction[] +} + +/** Return value of `/v1/chain/get_scheduled_transactions` */ +export interface GetScheduledTransactionsResult { + transactions: GeneratedTransaction[]; + more: string; +} + +/** Return value of `/v1/chain/get_table_rows` and `/v1/chain/get_kv_table_rows` */ +export interface GetTableRowsResult { + rows: any[]; + more: boolean; + next_key: string; + next_key_bytes: string; +} + +export interface GetTableByScopeResultRow { + code: string; + scope: string; + table: string; + payer: string; + count: number; +} + +/** Return value of `/v1/chain/get_table_by_scope` */ +export interface GetTableByScopeResult { + rows: GetTableByScopeResultRow[]; + more: string; +} + /** Arguments for `push_transaction` */ export interface PushTransactionArgs { signatures: string[]; @@ -147,3 +467,94 @@ export interface PushTransactionArgs { serializedTransaction: Uint8Array; serializedContextFreeData?: Uint8Array; } + +export interface DBSizeIndexCount { + index: string; + row_count: number; +} + +/** Return value of `/v1/db_size/get` */ +export interface DBSizeGetResult { + free_bytes: number; + used_bytes: number; + size: number; + indices: DBSizeIndexCount[]; +} + +export interface TraceApiAction { + global_sequence: number; + receiver: string; + account: string; + action: string; + authorization: Authorization[]; + data: any; + return_value: any; +} + +export interface TraceApiTransactionHeader { + expiration: string; + ref_block_num: number; + ref_block_prefix: number; + max_net_usage_words: number; + max_cpu_usage_ms: number; + delay_sec: number; +} + +export interface TraceApiTransaction { + id: string; + actions: TraceApiAction[]; + status?: string; + cpu_usage_us?: number; + net_usage_words?: number; + signatures?: string[]; + transaction_header?: TraceApiTransactionHeader +} + +/** Return value of `/v1/trace_api/get_block` */ +export interface TraceApiGetBlockResult { + id: string; + number: number; + previous_id: string; + status: string; + timestamp: string; + producer: string; + transaction_mroot?: string; + action_mroot?: string; + schedule_version: number; + transactions: TraceApiTransaction; +} + +export interface OrderedActionResult { + global_action_seq: number; + account_action_seq: number; + block_num: number; + block_time: string; + action_trace: any; +} + +/** Return value of `/v1/history/get_actions` */ +export interface GetActionsResult { + actions: OrderedActionResult[]; + last_irreversible_block: number; + time_limit_exceeded_error?: boolean; +} + +/** Return value of `/v1/history/get_transaction` */ +export interface GetTransactionResult { + id: string; + trx: any; + block_time: string; + block_num: number; + last_irreversible_block: number; + traces: any[]; +} + +/** Return value of `/v1/history/get_key_accounts` */ +export interface GetKeyAccountsResult { + account_names: string[]; +} + +/** Return value of `/v1/history/get_controlled_accounts` */ +export interface GetControlledAccountsResult { + controlled_accounts: string[]; +} diff --git a/src/eosjs-serialize.ts b/src/eosjs-serialize.ts index c326c14db..725054af7 100644 --- a/src/eosjs-serialize.ts +++ b/src/eosjs-serialize.ts @@ -6,7 +6,8 @@ /* eslint-disable jsdoc/check-indentation */ import * as numeric from './eosjs-numeric'; -import { Abi, BlockTaposInfo, BlockHeaderStateTaposInfo } from './eosjs-rpc-interfaces'; +import { TransactionHeader } from './eosjs-api-interfaces'; +import { Abi, BlockTaposInfo } from './eosjs-rpc-interfaces'; import { Query } from './eosjs-api-interfaces'; /** A field in an abi */ @@ -167,7 +168,7 @@ export class SerialBuffer { } /** Resize `array` if needed to have at least `size` bytes free */ - public reserve(size: number) { + public reserve(size: number): void { if (this.length + size <= this.array.length) { return; } @@ -181,34 +182,34 @@ export class SerialBuffer { } /** Is there data available to read? */ - public haveReadData() { + public haveReadData(): boolean { return this.readPos < this.length; } /** Restart reading from the beginning */ - public restartRead() { + public restartRead(): void { this.readPos = 0; } /** Return data with excess storage trimmed away */ - public asUint8Array() { + public asUint8Array(): Uint8Array { return new Uint8Array(this.array.buffer, this.array.byteOffset, this.length); } /** Append bytes */ - public pushArray(v: number[] | Uint8Array) { + public pushArray(v: number[] | Uint8Array): void { this.reserve(v.length); this.array.set(v, this.length); this.length += v.length; } /** Append bytes */ - public push(...v: number[]) { + public push(...v: number[]): void { this.pushArray(v); } /** Get a single byte */ - public get() { + public get(): number { if (this.readPos < this.length) { return this.array[this.readPos++]; } @@ -216,7 +217,7 @@ export class SerialBuffer { } /** Append bytes in `v`. Throws if `len` doesn't match `v.length` */ - public pushUint8ArrayChecked(v: Uint8Array, len: number) { + public pushUint8ArrayChecked(v: Uint8Array, len: number): void { if (v.length !== len) { throw new Error('Binary data has incorrect size'); } @@ -224,7 +225,7 @@ export class SerialBuffer { } /** Get `len` bytes */ - public getUint8Array(len: number) { + public getUint8Array(len: number): Uint8Array { if (this.readPos + len > this.length) { throw new Error('Read past end of buffer'); } @@ -234,7 +235,7 @@ export class SerialBuffer { } /** Skip `len` bytes */ - public skip(len: number) { + public skip(len: number): void { if (this.readPos + len > this.length) { throw new Error('Read past end of buffer'); } @@ -242,12 +243,12 @@ export class SerialBuffer { } /** Append a `uint16` */ - public pushUint16(v: number) { + public pushUint16(v: number): void { this.push((v >> 0) & 0xff, (v >> 8) & 0xff); } /** Get a `uint16` */ - public getUint16() { + public getUint16(): number { let v = 0; v |= this.get() << 0; v |= this.get() << 8; @@ -255,12 +256,12 @@ export class SerialBuffer { } /** Append a `uint32` */ - public pushUint32(v: number) { + public pushUint32(v: number): void { this.push((v >> 0) & 0xff, (v >> 8) & 0xff, (v >> 16) & 0xff, (v >> 24) & 0xff); } /** Get a `uint32` */ - public getUint32() { + public getUint32(): number { let v = 0; v |= this.get() << 0; v |= this.get() << 8; @@ -270,7 +271,7 @@ export class SerialBuffer { } /** Append a `uint64`. *Caution*: `number` only has 53 bits of precision */ - public pushNumberAsUint64(v: number) { + public pushNumberAsUint64(v: number): void { this.pushUint32(v >>> 0); this.pushUint32(Math.floor(v / 0x10000_0000) >>> 0); } @@ -279,14 +280,14 @@ export class SerialBuffer { * Get a `uint64` as a `number`. *Caution*: `number` only has 53 bits of precision; some values will change. * `numeric.binaryToDecimal(serialBuffer.getUint8Array(8))` recommended instead */ - public getUint64AsNumber() { + public getUint64AsNumber(): number { const low = this.getUint32(); const high = this.getUint32(); return (high >>> 0) * 0x10000_0000 + (low >>> 0); } /** Append a `varuint32` */ - public pushVaruint32(v: number) { + public pushVaruint32(v: number): void { while (true) { if (v >>> 7) { this.push(0x80 | (v & 0x7f)); @@ -299,7 +300,7 @@ export class SerialBuffer { } /** Get a `varuint32` */ - public getVaruint32() { + public getVaruint32(): number { let v = 0; let bit = 0; while (true) { @@ -314,12 +315,12 @@ export class SerialBuffer { } /** Append a `varint32` */ - public pushVarint32(v: number) { + public pushVarint32(v: number): void { this.pushVaruint32((v << 1) ^ (v >> 31)); } /** Get a `varint32` */ - public getVarint32() { + public getVarint32(): number { const v = this.getVaruint32(); if (v & 1) { return ((~v) >> 1) | 0x8000_0000; @@ -329,27 +330,27 @@ export class SerialBuffer { } /** Append a `float32` */ - public pushFloat32(v: number) { + public pushFloat32(v: number): void { this.pushArray(new Uint8Array((new Float32Array([v])).buffer)); } /** Get a `float32` */ - public getFloat32() { + public getFloat32(): number { return new Float32Array(this.getUint8Array(4).slice().buffer)[0]; } /** Append a `float64` */ - public pushFloat64(v: number) { + public pushFloat64(v: number): void { this.pushArray(new Uint8Array((new Float64Array([v])).buffer)); } /** Get a `float64` */ - public getFloat64() { + public getFloat64(): number { return new Float64Array(this.getUint8Array(8).slice().buffer)[0]; } /** Append a `name` */ - public pushName(s: string) { + public pushName(s: string): void { if (typeof s !== 'string') { throw new Error('Expected string containing name'); } @@ -357,7 +358,7 @@ export class SerialBuffer { if (!regex.test(s)) { throw new Error('Name should be less than 13 characters, or less than 14 if last character is between 1-5 or a-j, and only contain the following symbols .12345abcdefghijklmnopqrstuvwxyz'); // eslint-disable-line } - const charToSymbol = (c: number) => { + const charToSymbol = (c: number): number => { if (c >= 'a'.charCodeAt(0) && c <= 'z'.charCodeAt(0)) { return (c - 'a'.charCodeAt(0)) + 6; } @@ -384,7 +385,7 @@ export class SerialBuffer { } /** Get a `name` */ - public getName() { + public getName(): string { const a = this.getUint8Array(8); let result = ''; for (let bit = 63; bit >= 0;) { @@ -410,28 +411,28 @@ export class SerialBuffer { } /** Append length-prefixed binary data */ - public pushBytes(v: number[] | Uint8Array) { + public pushBytes(v: number[] | Uint8Array): void { this.pushVaruint32(v.length); this.pushArray(v); } /** Get length-prefixed binary data */ - public getBytes() { + public getBytes(): Uint8Array { return this.getUint8Array(this.getVaruint32()); } /** Append a string */ - public pushString(v: string) { + public pushString(v: string): void { this.pushBytes(this.textEncoder.encode(v)); } /** Get a string */ - public getString() { + public getString(): string { return this.textDecoder.decode(this.getBytes()); } /** Append a `symbol_code`. Unlike `symbol`, `symbol_code` doesn't include a precision. */ - public pushSymbolCode(name: string) { + public pushSymbolCode(name: string): void { if (typeof name !== 'string') { throw new Error('Expected string containing symbol_code'); } @@ -444,7 +445,7 @@ export class SerialBuffer { } /** Get a `symbol_code`. Unlike `symbol`, `symbol_code` doesn't include a precision. */ - public getSymbolCode() { + public getSymbolCode(): string { const a = this.getUint8Array(8); let len; for (len = 0; len < a.length; ++len) { @@ -457,7 +458,7 @@ export class SerialBuffer { } /** Append a `symbol` */ - public pushSymbol({ name, precision }: { name: string, precision: number }) { + public pushSymbol({ name, precision }: { name: string, precision: number }): void { if (!/^[A-Z]{1,7}$/.test(name)) { throw new Error('Expected symbol to be A-Z and between one and seven characters'); } @@ -484,7 +485,7 @@ export class SerialBuffer { } /** Append an asset */ - public pushAsset(s: string) { + public pushAsset(s: string): void { if (typeof s !== 'string') { throw new Error('Expected string containing asset'); } @@ -519,7 +520,7 @@ export class SerialBuffer { } /** Get an asset */ - public getAsset() { + public getAsset(): string { const amount = this.getUint8Array(8); const { name, precision } = this.getSymbol(); let s = numeric.signedBinaryToDecimal(amount, precision + 1); @@ -530,14 +531,14 @@ export class SerialBuffer { } /** Append a public key */ - public pushPublicKey(s: string) { + public pushPublicKey(s: string): void { const key = numeric.stringToPublicKey(s); this.push(key.type); this.pushArray(key.data); } /** Get a public key */ - public getPublicKey() { + public getPublicKey(): string { const type = this.get(); let data: Uint8Array; if (type === numeric.KeyType.wa) { @@ -552,28 +553,28 @@ export class SerialBuffer { } /** Append a private key */ - public pushPrivateKey(s: string) { + public pushPrivateKey(s: string): void { const key = numeric.stringToPrivateKey(s); this.push(key.type); this.pushArray(key.data); } /** Get a private key */ - public getPrivateKey() { + public getPrivateKey(): string { const type = this.get(); const data = this.getUint8Array(numeric.privateKeyDataSize); return numeric.privateKeyToString({ type, data }); } /** Append a signature */ - public pushSignature(s: string) { + public pushSignature(s: string): void { const key = numeric.stringToSignature(s); this.push(key.type); this.pushArray(key.data); } /** Get a signature */ - public getSignature() { + public getSignature(): string { const type = this.get(); let data: Uint8Array; if (type === numeric.KeyType.wa) { @@ -590,11 +591,11 @@ export class SerialBuffer { } // SerialBuffer /** Is this a supported ABI version? */ -export const supportedAbiVersion = (version: string) => { +export const supportedAbiVersion = (version: string): boolean => { return version.startsWith('eosio::abi/1.'); }; -const checkDateParse = (date: string) => { +const checkDateParse = (date: string): number => { const result = Date.parse(date); if (Number.isNaN(result)) { throw new Error('Invalid time format'); @@ -603,34 +604,34 @@ const checkDateParse = (date: string) => { }; /** Convert date in ISO format to `time_point` (miliseconds since epoch) */ -export const dateToTimePoint = (date: string) => { +export const dateToTimePoint = (date: string): number => { return Math.round(checkDateParse(date + 'Z') * 1000); }; /** Convert `time_point` (miliseconds since epoch) to date in ISO format */ -export const timePointToDate = (us: number) => { +export const timePointToDate = (us: number): string => { const s = (new Date(us / 1000)).toISOString(); return s.substr(0, s.length - 1); }; /** Convert date in ISO format to `time_point_sec` (seconds since epoch) */ -export const dateToTimePointSec = (date: string) => { +export const dateToTimePointSec = (date: string): number => { return Math.round(checkDateParse(date + 'Z') / 1000); }; /** Convert `time_point_sec` (seconds since epoch) to to date in ISO format */ -export const timePointSecToDate = (sec: number) => { +export const timePointSecToDate = (sec: number): string => { const s = (new Date(sec * 1000)).toISOString(); return s.substr(0, s.length - 1); }; /** Convert date in ISO format to `block_timestamp_type` (half-seconds since a different epoch) */ -export const dateToBlockTimestamp = (date: string) => { +export const dateToBlockTimestamp = (date: string): number => { return Math.round((checkDateParse(date + 'Z') - 946684800000) / 500); }; /** Convert `block_timestamp_type` (half-seconds since a different epoch) to to date in ISO format */ -export const blockTimestampToDate = (slot: number) => { +export const blockTimestampToDate = (slot: number): string => { const s = (new Date(slot * 500 + 946684800000)).toISOString(); return s.substr(0, s.length - 1); }; @@ -648,12 +649,12 @@ export const stringToSymbol = (s: string): { name: string, precision: number } = }; /** Convert `Symbol` to `string`. format: `precision,NAME`. */ -export const symbolToString = ({ name, precision }: { name: string, precision: number }) => { +export const symbolToString = ({ name, precision }: { name: string, precision: number }): string => { return precision + ',' + name; }; /** Convert binary data to hex */ -export const arrayToHex = (data: Uint8Array) => { +export const arrayToHex = (data: Uint8Array): string => { let result = ''; for (const x of data) { result += ('00' + x.toString(16)).slice(-2); @@ -662,7 +663,7 @@ export const arrayToHex = (data: Uint8Array) => { }; /** Convert hex to binary data */ -export const hexToUint8Array = (hex: string) => { +export const hexToUint8Array = (hex: string): Uint8Array => { if (typeof hex !== 'string') { throw new Error('Expected string containing hex digits'); } @@ -691,7 +692,7 @@ function deserializeUnknown(buffer: SerialBuffer): SerialBuffer { function serializeStruct( this: Type, buffer: SerialBuffer, data: any, state = new SerializerState(), allowExtensions = true -) { +): void { if (typeof data !== 'object') { throw new Error('expected object containing data: ' + JSON.stringify(data)); } @@ -715,7 +716,7 @@ function serializeStruct( } } -function deserializeStruct(this: Type, buffer: SerialBuffer, state = new SerializerState(), allowExtensions = true) { +function deserializeStruct(this: Type, buffer: SerialBuffer, state = new SerializerState(), allowExtensions = true): any { let result; if (this.base) { result = this.base.deserialize(buffer, state, allowExtensions); @@ -734,7 +735,7 @@ function deserializeStruct(this: Type, buffer: SerialBuffer, state = new Seriali function serializeVariant( this: Type, buffer: SerialBuffer, data: any, state?: SerializerState, allowExtensions?: boolean -) { +): void { if (!Array.isArray(data) || data.length !== 2 || typeof data[0] !== 'string') { throw new Error('expected variant: ["type", value]'); } @@ -746,7 +747,7 @@ function serializeVariant( this.fields[i].type.serialize(buffer, data[1], state, allowExtensions); } -function deserializeVariant(this: Type, buffer: SerialBuffer, state?: SerializerState, allowExtensions?: boolean) { +function deserializeVariant(this: Type, buffer: SerialBuffer, state?: SerializerState, allowExtensions?: boolean): any[] { const i = buffer.getVaruint32(); if (i >= this.fields.length) { throw new Error(`type index ${i} is not valid for variant`); @@ -757,14 +758,14 @@ function deserializeVariant(this: Type, buffer: SerialBuffer, state?: Serializer function serializeArray( this: Type, buffer: SerialBuffer, data: any[], state?: SerializerState, allowExtensions?: boolean -) { +): void { buffer.pushVaruint32(data.length); for (const item of data) { this.arrayOf.serialize(buffer, item, state, false); } } -function deserializeArray(this: Type, buffer: SerialBuffer, state?: SerializerState, allowExtensions?: boolean) { +function deserializeArray(this: Type, buffer: SerialBuffer, state?: SerializerState, allowExtensions?: boolean): any[] { const len = buffer.getVaruint32(); const result = []; for (let i = 0; i < len; ++i) { @@ -775,7 +776,7 @@ function deserializeArray(this: Type, buffer: SerialBuffer, state?: SerializerSt function serializeOptional( this: Type, buffer: SerialBuffer, data: any, state?: SerializerState, allowExtensions?: boolean -) { +): void { if (data === null || data === undefined) { buffer.push(0); } else { @@ -784,7 +785,7 @@ function serializeOptional( } } -function deserializeOptional(this: Type, buffer: SerialBuffer, state?: SerializerState, allowExtensions?: boolean) { +function deserializeOptional(this: Type, buffer: SerialBuffer, state?: SerializerState, allowExtensions?: boolean): any { if (buffer.get()) { return this.optionalOf.deserialize(buffer, state, allowExtensions); } else { @@ -794,17 +795,17 @@ function deserializeOptional(this: Type, buffer: SerialBuffer, state?: Serialize function serializeExtension( this: Type, buffer: SerialBuffer, data: any, state?: SerializerState, allowExtensions?: boolean -) { +): void { this.extensionOf.serialize(buffer, data, state, allowExtensions); } -function deserializeExtension(this: Type, buffer: SerialBuffer, state?: SerializerState, allowExtensions?: boolean) { +function deserializeExtension(this: Type, buffer: SerialBuffer, state?: SerializerState, allowExtensions?: boolean): any { return this.extensionOf.deserialize(buffer, state, allowExtensions); } function serializeObject( this: Type, buffer: SerialBuffer, data: any, state?: SerializerState, allowExtensions?: boolean -) { +): void { const entries = Object.entries(data); buffer.pushVaruint32(entries.length); for (const [key, value] of entries) { @@ -815,7 +816,7 @@ function serializeObject( } } -function deserializeObject(this: Type, buffer: SerialBuffer, state?: SerializerState, allowExtensions?: boolean) { +function deserializeObject(this: Type, buffer: SerialBuffer, state?: SerializerState, allowExtensions?: boolean): any { const len = buffer.getVaruint32(); const result = {} as any; for (let i = 0; i < len; ++i) { @@ -856,7 +857,7 @@ const createType = (attrs: CreateTypeArgs): Type => { }; }; -const checkRange = (orig: number, converted: number) => { +const checkRange = (orig: number, converted: number): number => { if (Number.isNaN(+orig) || Number.isNaN(+converted) || (typeof orig !== 'number' && typeof orig !== 'string')) { throw new Error('Expected number'); } @@ -1285,7 +1286,7 @@ export const getType = (types: Map, name: string): Type => { * @param initialTypes Set of types to build on. * In most cases, it's best to fill this from a fresh call to `getTypesFromAbi()`. */ -export const getTypesFromAbi = (initialTypes: Map, abi?: Abi) => { +export const getTypesFromAbi = (initialTypes: Map, abi?: Abi): Map => { const types = new Map(initialTypes); if (abi && abi.types) { for (const { new_type_name, type } of abi.types) { @@ -1325,12 +1326,12 @@ export const getTypesFromAbi = (initialTypes: Map, abi?: Abi) => { return types; }; // getTypesFromAbi -const reverseHex = (h: string) => { +const reverseHex = (h: string): string => { return h.substr(6, 2) + h.substr(4, 2) + h.substr(2, 2) + h.substr(0, 2); }; /** TAPoS: Return transaction fields which reference `refBlock` and expire `expireSeconds` after `timestamp` */ -export const transactionHeader = (refBlock: BlockTaposInfo, expireSeconds: number) => { +export const transactionHeader = (refBlock: BlockTaposInfo, expireSeconds: number): TransactionHeader => { const timestamp = refBlock.header ? refBlock.header.timestamp : refBlock.timestamp; const prefix = parseInt(reverseHex(refBlock.id.substr(16, 8)), 16); @@ -1397,7 +1398,7 @@ export const deserializeAction = ( }; }; -export const serializeAnyvar = (buffer: SerialBuffer, anyvar: Anyvar) => { +export const serializeAnyvar = (buffer: SerialBuffer, anyvar: Anyvar): void => { let def: AnyvarDef; let value: any; if (anyvar === null) { @@ -1419,7 +1420,7 @@ export const serializeAnyvar = (buffer: SerialBuffer, anyvar: Anyvar) => { def.type.serialize(buffer, value); }; -export const deserializeAnyvar = (buffer: SerialBuffer, state?: SerializerState) => { +export const deserializeAnyvar = (buffer: SerialBuffer, state?: SerializerState): any => { const defIndex = buffer.getVaruint32(); if (defIndex >= anyvarDefsByIndex.length) { throw new Error('Tried to deserialize unknown anyvar type'); @@ -1433,11 +1434,11 @@ export const deserializeAnyvar = (buffer: SerialBuffer, state?: SerializerState) } }; -export const deserializeAnyvarShort = (buffer: SerialBuffer) => { +export const deserializeAnyvarShort = (buffer: SerialBuffer): any => { return deserializeAnyvar(buffer, new SerializerState({ useShortForm: true } as any)); }; -export const serializeAnyObject = (buffer: SerialBuffer, obj: any) => { +export const serializeAnyObject = (buffer: SerialBuffer, obj: any): void => { const entries = Object.entries(obj); buffer.pushVaruint32(entries.length); for (const [key, value] of entries) { @@ -1446,7 +1447,7 @@ export const serializeAnyObject = (buffer: SerialBuffer, obj: any) => { } }; -export const deserializeAnyObject = (buffer: SerialBuffer, state?: SerializerState) => { +export const deserializeAnyObject = (buffer: SerialBuffer, state?: SerializerState): any => { const len = buffer.getVaruint32(); const result = {}; for (let i = 0; i < len; ++i) { @@ -1463,14 +1464,14 @@ export const deserializeAnyObject = (buffer: SerialBuffer, state?: SerializerSta return result; }; -export const serializeAnyArray = (buffer: SerialBuffer, arr: Anyvar[]) => { +export const serializeAnyArray = (buffer: SerialBuffer, arr: Anyvar[]): void => { buffer.pushVaruint32(arr.length); for (const x of arr) { serializeAnyvar(buffer, x); } }; -export const deserializeAnyArray = (buffer: SerialBuffer, state?: SerializerState) => { +export const deserializeAnyArray = (buffer: SerialBuffer, state?: SerializerState): any[] => { const len = buffer.getVaruint32(); const result = []; for (let i = 0; i < len; ++i) { @@ -1545,7 +1546,7 @@ const anyvarDefsByIndex = [ anyvarDefs.asset, ]; -export const serializeQuery = (buffer: SerialBuffer, query: Query) => { +export const serializeQuery = (buffer: SerialBuffer, query: Query): void => { let method: string; let arg: Anyvar; let filter: Query[]; diff --git a/src/eosjs-webauthn-sig.ts b/src/eosjs-webauthn-sig.ts index e369f3a93..ff2a122c7 100644 --- a/src/eosjs-webauthn-sig.ts +++ b/src/eosjs-webauthn-sig.ts @@ -4,6 +4,7 @@ // copyright defined in eosjs/LICENSE.txt import { SignatureProvider, SignatureProviderArgs } from './eosjs-api-interfaces'; +import { PushTransactionArgs } from './eosjs-rpc-interfaces'; import * as ser from './eosjs-serialize'; import * as numeric from './eosjs-numeric'; import { ec } from 'elliptic'; @@ -14,14 +15,14 @@ export class WebAuthnSignatureProvider implements SignatureProvider { public keys = new Map(); /** Public keys that the `SignatureProvider` holds */ - public async getAvailableKeys() { + public async getAvailableKeys(): Promise { return Array.from(this.keys.keys()); } /** Sign a transaction */ public async sign( { chainId, requiredKeys, serializedTransaction, serializedContextFreeData }: SignatureProviderArgs, - ) { + ): Promise { const signBuf = new ser.SerialBuffer(); signBuf.pushArray(ser.hexToUint8Array(chainId)); signBuf.pushArray(serializedTransaction); @@ -48,7 +49,7 @@ export class WebAuthnSignatureProvider implements SignatureProvider { const e = new ec('p256') as any; // https://github.com/indutny/elliptic/pull/232 const pubKey = e.keyFromPublic(numeric.stringToPublicKey(key).data.subarray(0, 33)).getPublic(); - const fixup = (x: Uint8Array) => { + const fixup = (x: Uint8Array): Uint8Array => { const a = Array.from(x); while (a.length < 32) { a.unshift(0); diff --git a/src/tests/eosjs-jsonrpc.test.ts b/src/tests/eosjs-jsonrpc.test.ts index 3b02645e7..53e315350 100644 --- a/src/tests/eosjs-jsonrpc.test.ts +++ b/src/tests/eosjs-jsonrpc.test.ts @@ -517,9 +517,6 @@ describe('JSON RPC', () => { const table = 'coffee'; const indexName = 'type'; const encodeType = 'bytes'; - const indexValue = ''; - const lowerBound = ''; - const upperBound = ''; const limit = 10; const reverse = false; const showPayer = false; @@ -536,9 +533,6 @@ describe('JSON RPC', () => { table, index_name: indexName, encode_type: encodeType, - index_value: indexValue, - lower_bound: lowerBound, - upper_bound: upperBound, limit, reverse, show_payer: showPayer, diff --git a/src/tests/type-checks.test.ts b/src/tests/type-checks.test.ts new file mode 100644 index 000000000..6df878f6a --- /dev/null +++ b/src/tests/type-checks.test.ts @@ -0,0 +1,1205 @@ + +import { JsonRpc } from '../eosjs-jsonrpc'; +import { JsSignatureProvider } from '../eosjs-jssig'; +import { Api } from '../eosjs-api'; +import * as ser from '../eosjs-serialize'; +import fetch from 'node-fetch'; +const { TextEncoder, TextDecoder } = require('util'); +import { + AbiJsonToBinResult, + GetAbiResult, + GetAccountResult, + GetAccountsByAuthorizersResult, + GetActivatedProtocolFeaturesResult, + GetBlockHeaderStateResult, + GetBlockInfoResult, + GetBlockResult, + GetCodeResult, + GetCodeHashResult, + GetCurrencyStatsResult, + GetInfoResult, + GetProducerScheduleResult, + GetProducersResult, + GetRawCodeAndAbiResult, + GetRawAbiResult, + GetScheduledTransactionsResult, + GetTableRowsResult, + GetTableByScopeResult, + PushTransactionArgs, + AbiBinToJsonResult, + TraceApiGetBlockResult, + DBSizeGetResult, +} from '../eosjs-rpc-interfaces'; +import { TransactResult } from '../eosjs-api-interfaces'; +import 'jest-extended'; + +const privateKey = '5JuH9fCXmU3xbj8nRmhPZaVrxxXrdPaRmZLW1cznNTmTQR2Kg5Z'; + +const rpc = new JsonRpc('http://localhost:8888', { fetch }); +const signatureProvider = new JsSignatureProvider([privateKey]); +const api = new Api({ rpc, signatureProvider, textDecoder: new TextDecoder(), textEncoder: new TextEncoder() }); + +/** Checking types with verifyType/complexOrPrimitive + * To ensure that the data structure coming from eos matches the declared types in eosjs for developers and documentation + * Since typescript is not a runtime language, it's required to test with javascript format + * Create an object matching the typescript type with some requirements: + * nullable: make the key a string and add a `&` character to the end + * optional: make the key a string and add a `?` character to the end (same as typescript) + * []: remove array symbols from simple/complex types, use arrays for std::pair + * Map<>: use Map<> in the value field + * |: operates the same as typescript but does not work for complex types + */ + +describe('Chain API Plugin Endpoints', () => { + it('validates return type of abi_bin_to_json', async () => { + const result: AbiBinToJsonResult = await rpc.abi_bin_to_json('returnvalue', 'sum', '0500000005000000'); + const abiBinToJsonResult: any = { + args: 'any' + }; + verifyType(result, abiBinToJsonResult); + }); + + it('validates return type of abi_json_to_bin', async () => { + const result: AbiJsonToBinResult = await rpc.abi_json_to_bin('returnvalue', 'sum', [5, 5]); + const abiJsonToBinResult: any = { + binargs: 'string' + }; + verifyType(result, abiJsonToBinResult); + }); + + it('validates return type of get_abi', async () => { + const result: GetAbiResult = await rpc.get_abi('todo'); + const getAbiResult: any = { + account_name: 'string', + 'abi?': { + version: 'string', + types: { + new_type_name: 'string', + type: 'string', + }, + structs: { + name: 'string', + base: 'string', + fields: { + name: 'string', + type: 'string', + }, + }, + actions: { + name: 'string', + type: 'string', + ricardian_contract: 'string', + }, + tables: { + name: 'string', + type: 'string', + index_type: 'string', + key_names: 'string', + key_types: 'string', + }, + ricardian_clauses: { + id: 'string', + body: 'string', + }, + error_messages: { + error_code: 'number', + error_msg: 'string', + }, + abi_extensions: { + tag: 'number', + value: 'string', + }, + 'variants?': { + name: 'string', + types: 'string', + }, + 'action_results?': { + name: 'string', + result_type: 'string', + }, + 'kv_tables?': { + todo: { // key is dynamic, using result from todo account + type: 'string', + primary_index: { + name: 'string', + type: 'string', + }, + secondary_indices: { + 'todo?': { // key is dynamic + type: 'string', + }, + }, + }, + }, + }, + }; + verifyType(result, getAbiResult); + }); + + it('validates return type of get_account', async () => { + const result: GetAccountResult = await rpc.get_account('eosio'); + const getAccountResult: any = { + account_name: 'string', + head_block_num: 'number', + head_block_time: 'string', + privileged: 'boolean', + last_code_update: 'string', + created: 'string', + 'core_liquid_balance?': 'string', + ram_quota: 'number', + net_weight: 'number', + cpu_weight: 'number', + net_limit: { + used: 'number', + available: 'number', + max: 'number', + 'last_usage_update_time?': 'string', + 'current_used?': 'number', + }, + cpu_limit: { + used: 'number', + available: 'number', + max: 'number', + 'last_usage_update_time?': 'string', + 'current_used?': 'number', + }, + ram_usage: 'number', + permissions: { + perm_name: 'string', + parent: 'string', + required_auth: { + threshold: 'number', + keys: { + key: 'string', + weight: 'number', + }, + accounts: { + permission: { + actor: 'string', + permission: 'string', + }, + weight: 'number', + }, + waits: { + wait_sec: 'number', + weight: 'number', + } + } + }, + 'total_resources&': { + owner: 'string', + ram_bytes: 'number', + net_weight: 'string', + cpu_weight: 'string', + }, + 'self_delegated_bandwidth&': { + from: 'string', + to: 'string', + net_weight: 'string', + cpu_weight: 'string', + }, + 'refund_request&': { + owner: 'string', + request_time: 'string', + net_amount: 'string', + cpu_amount: 'string', + }, + 'voter_info&': { + owner: 'string', + proxy: 'string', + producers: 'string', + staked: 'number', + last_vote_weight: 'string', + proxied_vote_weight: 'string', + is_proxy: 'number', + flags1: 'number', + reserved2: 'number', + reserved3: 'string', + }, + 'rex_info&': { + version: 'number', + owner: 'string', + vote_stake: 'string', + rex_balance: 'string', + matured_rex: 'number', + rex_maturities: 'any', + }, + }; + verifyType(result, getAccountResult); + }); + + it('validates return type of get_accounts_by_authorizers', async () => { + const result: GetAccountsByAuthorizersResult = await rpc.get_accounts_by_authorizers([ + { actor: 'bob', permission: 'active' }, + { actor: 'cfhello', permission: 'active' } + ], ['EOS7bxrQUTbQ4mqcoefhWPz1aFieN4fA9RQAiozRz7FrUChHZ7Rb8', 'EOS6nVrBASwwviMy3CntKsb1cD5Ai2gRZnyrxJDqypL3JLL7KCKrK']); + const getAccountsByAuthorizersResult: any = { + accounts: { + account_name: 'string', + permission_name: 'string', + 'authorizing_key?': 'string', + 'authorizing_account?': { + actor: 'string', + permission: 'string', + }, + weight: 'number', + threshold: 'number', + } + }; + verifyType(result, getAccountsByAuthorizersResult); + }); + + it('validates return type of get_activated_protocol_features', async () => { + const result: GetActivatedProtocolFeaturesResult = await rpc.get_activated_protocol_features({}); + const getActivatedProtocolFeaturesResult: any = { + activated_protocol_features: { + feature_digest: 'string', + activation_ordinal: 'number', + activation_block_num: 'number', + description_digest: 'string', + dependencies: 'string', + protocol_feature_type: 'string', + specification: { + name: 'string', + value: 'string', + }, + }, + 'more?': 'number', + }; + verifyType(result, getActivatedProtocolFeaturesResult); + }); + + it('validates return type of get_block_header_state', async () => { + const info: GetInfoResult = await rpc.get_info(); + const result: GetBlockHeaderStateResult = await rpc.get_block_header_state(info.head_block_id); + const getBlockHeaderStateResult: any = { + id: 'string', + header: { + timestamp: 'string', + producer: 'string', + confirmed: 'number', + previous: 'string', + transaction_mroot: 'string', + action_mroot: 'string', + schedule_version: 'number', + 'new_producers?': { + version: 'number', + producers: { + producer_name: 'string', + block_signing_key: 'string', + }, + }, + header_extensions: 'any', + producer_signature: 'string', + }, + pending_schedule: { + schedule_lib_num: 'number', + schedule_hash: 'string', + schedule: { + version: 'number', + producers: { + producer_name: 'string', + block_signing_key: 'string', + }, + }, + }, + activated_protocol_features: { + protocol_features: 'string', + }, + additional_signatures: 'string', + block_num: 'number', + dpos_proposed_irreversible_blocknum: 'number', + dpos_irreversible_blocknum: 'number', + active_schedule: { + version: 'number', + producers: { + producer_name: 'string', + authority: [ 'number|string', { + threshold: 'number', + keys: { + key: 'string', + weight: 'number', + }, + }], + }, + }, + blockroot_merkle: { + _active_nodes: 'string', + _node_count: 'number', + }, + producer_to_last_produced: 'Map', + producer_to_last_implied_irb: 'Map', + valid_block_signing_authority: [ 'number|string', { + threshold: 'number', + keys: { + key: 'string', + weight: 'number', + }, + }], + confirm_count: 'number' + }; + verifyType(result, getBlockHeaderStateResult); + }); + + it('validates return type of get_block_info', async () => { + const info: GetInfoResult = await rpc.get_info(); + const result: GetBlockInfoResult = await rpc.get_block_info(info.last_irreversible_block_num); + const getBlockInfoResult: any = { + timestamp: 'string', + producer: 'string', + confirmed: 'number', + previous: 'string', + transaction_mroot: 'string', + action_mroot: 'string', + schedule_version: 'number', + producer_signature: 'string', + id: 'string', + block_num: 'number', + ref_block_num: 'number', + ref_block_prefix: 'number', + }; + verifyType(result, getBlockInfoResult); + }); + + it('validates return type of get_block', async () => { + const info: GetInfoResult = await rpc.get_info(); + const result: GetBlockResult = await rpc.get_block(info.last_irreversible_block_num); + const getBlockResult: any = { + timestamp: 'string', + producer: 'string', + confirmed: 'number', + previous: 'string', + transaction_mroot: 'string', + action_mroot: 'string', + schedule_version: 'number', + 'new_producers&': { + version: 'number', + producers: { + producer_name: 'string', + block_signing_key: 'string' + } + }, + producer_signature: 'string', + transactions: { + status: 'string', + cpu_usage_us: 'number', + net_usage_words: 'number', + trx: { + id: 'string', + signatures: 'string', + compression: 'number|string', + packed_context_free_data: 'string', + context_free_data: 'string', + packed_trx: 'string', + transaction: { + 'expiration?': 'string', + 'ref_block_num?': 'number', + 'ref_block_prefix?': 'number', + 'max_net_usage_words?': 'number', + 'max_cpu_usage_ms?': 'number', + 'delay_sec?': 'number', + 'context_free_actions?': { + account: 'string', + name: 'string', + authorization: { + actor: 'string', + permission: 'string', + }, + data: 'any', + 'hex_data?': 'string', + }, + 'context_free_data?': 'number', + actions: { + account: 'string', + name: 'string', + authorization: { + actor: 'string', + permission: 'string', + }, + data: 'any', + 'hex_data?': 'string', + }, + 'transaction_extensions?': { + type: 'number', + data: 'string', + }, + }, + }, + }, + id: 'string', + block_num: 'number', + ref_block_prefix: 'number', + }; + verifyType(result, getBlockResult); + }); + + it('validates return type of get_code', async () => { + const result: GetCodeResult = await rpc.get_code('todo'); + const getCodeResult: any = { + account_name: 'string', + code_hash: 'string', + wast: 'string', + wasm: 'string', + 'abi?': { + version: 'string', + types: { + new_type_name: 'string', + type: 'string', + }, + structs: { + name: 'string', + base: 'string', + fields: { + name: 'string', + type: 'string', + }, + }, + actions: { + name: 'string', + type: 'string', + ricardian_contract: 'string', + }, + tables: { + name: 'string', + type: 'string', + index_type: 'string', + key_names: 'string', + key_types: 'string', + }, + ricardian_clauses: { + id: 'string', + body: 'string', + }, + error_messages: { + error_code: 'number', + error_msg: 'string', + }, + abi_extensions: { + tag: 'number', + value: 'string', + }, + 'variants?': { + name: 'string', + types: 'string', + }, + 'action_results?': { + name: 'string', + result_type: 'string', + }, + 'kv_tables?': { + todo: { // key is dynamic, using result from todo account + type: 'string', + primary_index: { + name: 'string', + type: 'string', + }, + secondary_indices: { + 'todo?': { // key is dynamic + type: 'string', + }, + }, + }, + }, + }, + }; + verifyType(result, getCodeResult); + }); + + it('validates return type of get_code_hash', async () => { + const result: GetCodeHashResult = await rpc.get_code_hash('todo'); + const getCodeHashResult: any = { + account_name: 'string', + code_hash: 'string', + }; + verifyType(result, getCodeHashResult); + }); + + it('validates return type of get_currency_balance', async () => { + const result: string[] = await rpc.get_currency_balance('eosio.token', 'bob', 'SYS'); + result.forEach((element: any) => { + expect(typeof element).toEqual('string'); + }); + }); + + it('validates return type of get_currency_stats', async () => { + const result: GetCurrencyStatsResult = await rpc.get_currency_stats('eosio.token', 'SYS'); + const getCurrencyStatsResult: any = { + SYS: { + supply: 'string', + max_supply: 'string', + issuer: 'string', + } + }; + verifyType(result, getCurrencyStatsResult); + }); + + it('validates return type of get_info', async () => { + const result: GetInfoResult = await rpc.get_info(); + const getInfoResult: any = { + server_version: 'string', + chain_id: 'string', + head_block_num: 'number', + last_irreversible_block_num: 'number', + last_irreversible_block_id: 'string', + 'last_irreversible_block_time?': 'string', + head_block_id: 'string', + head_block_time: 'string', + head_block_producer: 'string', + virtual_block_cpu_limit: 'number', + virtual_block_net_limit: 'number', + block_cpu_limit: 'number', + block_net_limit: 'number', + 'server_version_string?': 'string', + 'fork_db_head_block_num?': 'number', + 'fork_db_head_block_id?': 'string', + 'server_full_version_string?': 'string', + }; + verifyType(result, getInfoResult); + }); + + it('validates return type of get_producer_schedule', async () => { + const result: GetProducerScheduleResult = await rpc.get_producer_schedule(); + const getProducerScheduleResult: any = { + 'active&': { + version: 'number', + producers: { + producer_name: 'string', + authority: [ 'number|string', { + threshold: 'number', + keys: { + key: 'string', + weight: 'number', + }, + }], + }, + }, + 'pending&': { + version: 'number', + producers: { + producer_name: 'string', + authority: [ 'number|string', { + threshold: 'number', + keys: { + key: 'string', + weight: 'number', + }, + }], + }, + }, + 'proposed&': { + version: 'number', + producers: { + producer_name: 'string', + authority: [ 'number|string', { + threshold: 'number', + keys: { + key: 'string', + weight: 'number', + }, + }], + }, + }, + }; + verifyType(result, getProducerScheduleResult); + }); + + it('validates return type of get_producers', async () => { + const result: GetProducersResult = await rpc.get_producers(); + const getProducersResult: any = { + rows: { + owner: 'string', + 'producer_authority?': [ 'number|string', { + threshold: 'number', + keys: { + key: 'string', + weight: 'number', + }, + }], + url: 'string', + 'is_active?': 'number', + total_votes: 'string', + producer_key: 'string', + 'unpaid_blocks?': 'number', + 'last_claim_time?': 'string', + 'location?': 'number', + }, + total_producer_vote_weight: 'string', + more: 'string', + }; + verifyType(result, getProducersResult); + }); + + it('validates return type of get_raw_code_and_abi', async () => { + const result: GetRawCodeAndAbiResult = await rpc.get_raw_code_and_abi('eosio'); + const getRawCodeAndAbiResult: any = { + account_name: 'string', + wasm: 'string', + abi: 'string', + }; + verifyType(result, getRawCodeAndAbiResult); + }); + + it('validates return type of get_raw_abi', async () => { + const result: GetRawAbiResult = await rpc.get_raw_abi('eosio'); + const getRawAbiResult: any = { + account_name: 'string', + code_hash: 'string', + abi_hash: 'string', + abi: 'string', + }; + verifyType(result, getRawAbiResult); + }); + + it('validates return type of get_scheduled_transactions', async () => { + const result: GetScheduledTransactionsResult = await rpc.get_scheduled_transactions(); + const getScheduledTransactionsResult: any = { + transactions: { + trx_id: 'string', + sender: 'string', + sender_id: 'string', + payer: 'string', + delay_until: 'string', + expiration: 'string', + published: 'string', + 'packed_trx?': 'string', + 'transaction?': { + 'expiration?': 'string', + 'ref_block_num?': 'number', + 'ref_block_prefix?': 'number', + 'max_net_usage_words?': 'number', + 'max_cpu_usage_ms?': 'number', + 'delay_sec?': 'number', + 'context_free_actions?': { + account: 'string', + name: 'string', + authorization: { + actor: 'string', + permission: 'string', + }, + data: 'any', + 'hex_data?': 'string', + }, + 'context_free_data?': 'number', + 'actions': { + account: 'string', + name: 'string', + authorization: { + actor: 'string', + permission: 'string', + }, + data: 'any', + 'hex_data?': 'string', + }, + 'transaction_extensions?': { + type: 'number', + data: 'string', + }, + 'deferred_transaction_generation?': { + sender_trx_id: 'string', + sender_id: 'string', + sender: 'string', + }, + }, + }, + more: 'string', + }; + verifyType(result, getScheduledTransactionsResult); + }); + + it('validates return type of get_table_rows', async () => { + const result: GetTableRowsResult = await rpc.get_table_rows({ + code: 'eosio.token', + scope: 'eosio.token', + table: 'accounts', + }); + const getTableRowsResult: any = { + rows: 'any', + more: 'boolean', + next_key: 'string', + next_key_bytes: 'string', + }; + verifyType(result, getTableRowsResult); + }); + + it('validates return type of get_kv_table_rows', async () => { + const result: GetTableRowsResult = await rpc.get_kv_table_rows({ + code: 'todo', + table: 'todo', + index_name: 'map.index', + encode_type: 'string', + }); + const getTableRowsResult: any = { + rows: 'any', + more: 'boolean', + next_key: 'string', + next_key_bytes: 'string', + }; + verifyType(result, getTableRowsResult); + }); + + it('validates return type of get_table_by_scope', async () => { + const result: GetTableByScopeResult = await rpc.get_table_by_scope({ + code: 'eosio.token', + table: 'accounts', + }); + const getTableByScopeResult: any = { + rows: 'any', + more: 'string', + }; + verifyType(result, getTableByScopeResult); + }); + + it('validates return type of get_required_keys', async () => { + const info = await rpc.get_info(); + let transaction: any = { + actions: [{ + account: 'eosio.token', + name: 'transfer', + authorization: [{ + actor: 'bob', + permission: 'active', + }], + data: { + from: 'bob', + to: 'alice', + quantity: '0.0001 SYS', + memo: '', + }, + }], + context_free_actions: [] + }; + transaction = { + ...ser.transactionHeader({ + block_num: info.last_irreversible_block_num, + id: info.last_irreversible_block_id, + timestamp: info.last_irreversible_block_time, + }, 30), + context_free_actions: await api.serializeActions(transaction.context_free_actions || []), + actions: await api.serializeActions(transaction.actions), + ...transaction, + }; + + const availableKeys = await signatureProvider.getAvailableKeys(); + const result: string[] = await rpc.getRequiredKeys({ transaction, availableKeys }); + result.forEach((element: any) => { + expect(typeof element).toEqual('string'); + }); + }); + + it('validates return type of push_transaction', async () => { + const transaction: PushTransactionArgs = await api.transact({ + actions: [{ + account: 'eosio.token', + name: 'transfer', + authorization: [{ + actor: 'bob', + permission: 'active', + }], + data: { + from: 'bob', + to: 'alice', + quantity: '0.0001 SYS', + memo: '', + }, + }], + }, { + sign: true, + broadcast: false, + useLastIrreversible: true, + expireSeconds: 30, + }) as PushTransactionArgs; + const result: TransactResult = await rpc.push_transaction(transaction); + const transactResult = { + transaction_id: 'string', + processed: { + id: 'string', + block_num: 'number', + block_time: 'string', + 'producer_block_id&': 'string', + 'receipt&': { + status: 'string', + cpu_usage_us: 'number', + net_usage_words: 'number', + }, + elapsed: 'number', + net_usage: 'number', + scheduled: 'boolean', + action_traces: { + action_ordinal: 'number', + creator_action_ordinal: 'number', + closest_unnotified_ancestor_action_ordinal: 'number', + receipt: { + receiver: 'string', + act_digest: 'string', + global_sequence: 'number', + recv_sequence: 'number', + auth_sequence: [ 'string', 'number' ], + code_sequence: 'number', + abi_sequence: 'number', + }, + receiver: 'string', + act: { + account: 'string', + name: 'string', + authorization: { + actor: 'string', + permission: 'string', + }, + data: 'any', + 'hex_data?': 'string', + }, + context_free: 'boolean', + elapsed: 'number', + console: 'string', + trx_id: 'string', + block_num: 'number', + block_time: 'string', + 'producer_block_id&': 'string', + account_ram_deltas: { + account: 'string', + delta: 'number', + }, + account_disk_deltas: { + account: 'string', + delta: 'number', + }, + except: 'any', + 'error_code&': 'number', + 'return_value?': 'any', + 'return_value_hex_data?': 'string', + 'return_value_data?': 'any', + 'inline_traces?': 'any', // ActionTrace, recursive? + }, + 'account_ram_delta&': { + account: 'string', + delta: 'number', + }, + 'except&': 'string', + 'error_code&': 'number', + }, + }; + verifyType(result, transactResult); + }); + + it('validates return type of push_transactions', async () => { + const transactionA: PushTransactionArgs = await api.transact({ + actions: [{ + account: 'eosio.token', + name: 'transfer', + authorization: [{ + actor: 'bob', + permission: 'active', + }], + data: { + from: 'bob', + to: 'alice', + quantity: '0.0001 SYS', + memo: 'A', + }, + }], + }, { + sign: true, + broadcast: false, + useLastIrreversible: true, + expireSeconds: 30, + }) as PushTransactionArgs; + const transactionB: PushTransactionArgs = await api.transact({ + actions: [{ + account: 'eosio.token', + name: 'transfer', + authorization: [{ + actor: 'bob', + permission: 'active', + }], + data: { + from: 'bob', + to: 'alice', + quantity: '0.0001 SYS', + memo: 'B', + }, + }], + }, { + sign: true, + broadcast: false, + useLastIrreversible: true, + expireSeconds: 30, + }) as PushTransactionArgs; + const result: TransactResult[] = await rpc.push_transactions([ transactionA, transactionB ]); + const transactResult = { + transaction_id: 'string', + processed: { + id: 'string', + block_num: 'number', + block_time: 'string', + 'producer_block_id&': 'string', + 'receipt&': { + status: 'string', + cpu_usage_us: 'number', + net_usage_words: 'number', + }, + elapsed: 'number', + net_usage: 'number', + scheduled: 'boolean', + action_traces: { + action_ordinal: 'number', + creator_action_ordinal: 'number', + closest_unnotified_ancestor_action_ordinal: 'number', + receipt: { + receiver: 'string', + act_digest: 'string', + global_sequence: 'number', + recv_sequence: 'number', + auth_sequence: [ 'string', 'number' ], + code_sequence: 'number', + abi_sequence: 'number', + }, + receiver: 'string', + act: { + account: 'string', + name: 'string', + authorization: { + actor: 'string', + permission: 'string', + }, + data: 'any', + 'hex_data?': 'string', + }, + context_free: 'boolean', + elapsed: 'number', + console: 'string', + trx_id: 'string', + block_num: 'number', + block_time: 'string', + 'producer_block_id&': 'string', + account_ram_deltas: { + account: 'string', + delta: 'number', + }, + account_disk_deltas: { + account: 'string', + delta: 'number', + }, + except: 'any', + 'error_code&': 'number', + 'return_value?': 'any', + 'return_value_hex_data?': 'string', + 'return_value_data?': 'any', + 'inline_traces?': 'any', // ActionTrace, recursive? + }, + 'account_ram_delta&': { + account: 'string', + delta: 'number', + }, + 'except&': 'string', + 'error_code&': 'number', + }, + }; + result.forEach((transaction: TransactResult) => { + verifyType(transaction, transactResult); + }); + }); + + it('validates return type of send_transaction', async () => { + const transaction: PushTransactionArgs = await api.transact({ + actions: [{ + account: 'eosio.token', + name: 'transfer', + authorization: [{ + actor: 'alice', + permission: 'active', + }], + data: { + from: 'alice', + to: 'bob', + quantity: '0.0001 SYS', + memo: '', + }, + }], + }, { + sign: true, + broadcast: false, + useLastIrreversible: true, + expireSeconds: 30, + }) as PushTransactionArgs; + const result: TransactResult = await rpc.send_transaction(transaction); + const transactResult = { + transaction_id: 'string', + processed: { + id: 'string', + block_num: 'number', + block_time: 'string', + 'producer_block_id&': 'string', + 'receipt&': { + status: 'string', + cpu_usage_us: 'number', + net_usage_words: 'number', + }, + elapsed: 'number', + net_usage: 'number', + scheduled: 'boolean', + action_traces: { + action_ordinal: 'number', + creator_action_ordinal: 'number', + closest_unnotified_ancestor_action_ordinal: 'number', + receipt: { + receiver: 'string', + act_digest: 'string', + global_sequence: 'number', + recv_sequence: 'number', + auth_sequence: [ 'string', 'number' ], + code_sequence: 'number', + abi_sequence: 'number', + }, + receiver: 'string', + act: { + account: 'string', + name: 'string', + authorization: { + actor: 'string', + permission: 'string', + }, + data: 'any', + 'hex_data?': 'string', + }, + context_free: 'boolean', + elapsed: 'number', + console: 'string', + trx_id: 'string', + block_num: 'number', + block_time: 'string', + 'producer_block_id&': 'string', + account_ram_deltas: { + account: 'string', + delta: 'number', + }, + account_disk_deltas: { + account: 'string', + delta: 'number', + }, + except: 'any', + 'error_code&': 'number', + 'return_value?': 'any', + 'return_value_hex_data?': 'string', + 'return_value_data?': 'any', + 'inline_traces?': 'any', // ActionTrace, recursive? + }, + 'account_ram_delta&': { + account: 'string', + delta: 'number', + }, + 'except&': 'string', + 'error_code&': 'number', + }, + }; + verifyType(result, transactResult); + }); +}); + +describe('DB Size API Plugin Endpoints', () => { + it('validates return type of get', async () => { + const result: DBSizeGetResult = await rpc.db_size_get(); + const dbSizeGetResult: any = { + free_bytes: 'number', + used_bytes: 'number', + size: 'number', + indices: { + index: 'string', + row_count: 'number', + }, + }; + verifyType(result, dbSizeGetResult); + }); +}); + +describe('Trace API Plugin Endpoints', () => { + it('validates return type of get_block', async () => { + const info: GetInfoResult = await rpc.get_info(); + const result: any = await rpc.trace_get_block(info.last_irreversible_block_num); + const traceApiGetBlockResult: any = { + id: 'string', + number: 'number', + previous_id: 'string', + status: 'string', + timestamp: 'string', + producer: 'string', + transaction_mroot: 'string', + action_mroot: 'string', + schedule_version: 'number', + transactions: { + id: 'string', + actions: { + global_sequence: 'number', + receiver: 'string', + account: 'string', + action: 'string', + authorization: { + account: 'string', + permission: 'string' + }, + data: 'string', + return_value: 'string', + }, + status: 'string', + cpu_usage_us: 'number', + net_usage_words: 'number', + signatures: 'string', + transaction_header: 'any' + }, + }; + verifyType(result, traceApiGetBlockResult); + }); +}); + + +const verifyType = (data: any, type: any): void => { + const verifiedKeys: string[] = Object.keys(type).filter((key: string) => { + const formattedKey = key.replace('?', '').replace('&', ''); + if (key.includes('?')) { + if (!data.hasOwnProperty(formattedKey)) return false; + } + return true; + }).map((key: string) => { + const formattedKey = key.replace('?', '').replace('&', ''); + if (Array.isArray(data[formattedKey])) { + if (Array.isArray(type[key])) { + data[formattedKey].forEach((element: any, index: number) => { + if (Array.isArray(element)) { + element.forEach((secondElement: any, secondIndex: number) => { + complexOrPrimitive(secondElement, type[key][secondIndex], formattedKey); + }); + } else { + complexOrPrimitive(element, type[key][index], formattedKey); + } + }); + } else { + data[formattedKey].forEach((element: any) => { + complexOrPrimitive(element, type[key], formattedKey); + }); + } + } else if (key.includes('&')) { + if (data[formattedKey] !== null) { + complexOrPrimitive(data[formattedKey], type[key], formattedKey); + } + } else { + complexOrPrimitive(data[formattedKey], type[key], formattedKey); + } + return formattedKey; + }); + expect(data).toContainAllKeys(verifiedKeys); +}; + +const complexOrPrimitive = (data: any, type: any, formattedKey: any): void => { + if (typeof type === 'object') { + verifyType(data, type); + } else if (type.includes('Map')) { + const types = type.replace('Map<', '').replace('>', '').split(', '); + data.forEach((value: any, index: number) => { + complexOrPrimitive(value, types[index], formattedKey); + }); + } else if (type.includes('|')) { + const types = type.split('|'); + expect(typeof data).toBeOneOf(types); + } else if (type !== 'any') { + expect(typeof data).toEqual(type); + } +}; diff --git a/yarn.lock b/yarn.lock index 51de003fa..1a4065934 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,15 +10,15 @@ "@babel/highlight" "^7.12.13" "@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.13.tgz#b73a87a3a3e7d142a66248bf6ad88b9ceb093425" - integrity sha512-BQKE9kXkPlXHPeqissfxo0lySWJcYdEP0hdtJOH/iJfDdhOCcgtNCjftCJg3qqauB4h+lz2N6ixM++b9DN1Tcw== + version "7.12.16" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.16.tgz#8c6ba456b23b680a6493ddcfcd9d3c3ad51cab7c" + integrity sha512-t/hHIB504wWceOeaOoONOhu+gX+hpjfeN6YRBT209X/4sibZQfSF1I0HFRRlBe97UZZosGx5XwUg1ZgNbelmNw== dependencies: "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.12.13" + "@babel/generator" "^7.12.15" "@babel/helper-module-transforms" "^7.12.13" "@babel/helpers" "^7.12.13" - "@babel/parser" "^7.12.13" + "@babel/parser" "^7.12.16" "@babel/template" "^7.12.13" "@babel/traverse" "^7.12.13" "@babel/types" "^7.12.13" @@ -30,7 +30,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.12.13": +"@babel/generator@^7.12.13", "@babel/generator@^7.12.15": version "7.12.15" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.15.tgz#4617b5d0b25cc572474cc1aafee1edeaf9b5368f" integrity sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ== @@ -56,9 +56,9 @@ "@babel/types" "^7.12.13" "@babel/helper-member-expression-to-functions@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.13.tgz#c5715695b4f8bab32660dbdcdc2341dec7e3df40" - integrity sha512-B+7nN0gIL8FZ8SvMcF+EPyB21KnCcZHQZFczCxbiNGV/O0rsrSBlWGLzmtBJ3GMjSVMIm4lpFhR+VdVBuIsUcQ== + version "7.12.16" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.16.tgz#41e0916b99f8d5f43da4f05d85f4930fa3d62b22" + integrity sha512-zYoZC1uvebBFmj1wFAlXwt35JLEgecefATtKp20xalwEK8vHAixLBXTGxNrVGEmTT+gzOThUgr8UEdgtalc1BQ== dependencies: "@babel/types" "^7.12.13" @@ -143,10 +143,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.7.0": - version "7.12.15" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.15.tgz#2b20de7f0b4b332d9b119dd9c33409c538b8aacf" - integrity sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA== +"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.12.16", "@babel/parser@^7.7.0": + version "7.12.16" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.16.tgz#cc31257419d2c3189d394081635703f549fc1ed4" + integrity sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -357,6 +357,15 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== +"@jest/console@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" + integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== + dependencies: + "@jest/source-map" "^24.9.0" + chalk "^2.0.1" + slash "^2.0.0" + "@jest/console@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" @@ -466,6 +475,15 @@ optionalDependencies: node-notifier "^8.0.0" +"@jest/source-map@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" + integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.1.15" + source-map "^0.6.0" + "@jest/source-map@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" @@ -475,6 +493,15 @@ graceful-fs "^4.2.4" source-map "^0.6.0" +"@jest/test-result@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" + integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== + dependencies: + "@jest/console" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@jest/test-result@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" @@ -517,6 +544,15 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/types@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" + integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^13.0.0" + "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -615,9 +651,9 @@ "@types/node" "*" "@types/graceful-fs@^4.1.2": - version "4.1.4" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.4.tgz#4ff9f641a7c6d1a3508ff88bc3141b152772e753" - integrity sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg== + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== dependencies: "@types/node" "*" @@ -633,6 +669,14 @@ dependencies: "@types/istanbul-lib-coverage" "*" +"@types/istanbul-reports@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz#e875cc689e47bce549ec81f3df5e6f6f11cfaeb2" + integrity sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw== + dependencies: + "@types/istanbul-lib-coverage" "*" + "@types/istanbul-lib-report" "*" + "@types/istanbul-reports@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" @@ -658,10 +702,18 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/node@*", "@types/node@^14.14.25": - version "14.14.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.25.tgz#15967a7b577ff81383f9b888aa6705d43fbbae93" - integrity sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ== +"@types/node-fetch@^2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb" + integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*", "@types/node@^14.14.27": + version "14.14.27" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.27.tgz#c7127f8da0498993e13b1a42faf1303d3110d2f2" + integrity sha512-Ecfmo4YDQPwuqTCl1yBxLV5ihKfRlkBmzUEDcfIRvDxOTGQEeikr317Ln7Gcv0tjA8dVgKI3rniqW2G1OyKDng== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -693,6 +745,11 @@ resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== +"@types/stack-utils@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" + integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== + "@types/stack-utils@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" @@ -736,6 +793,13 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== +"@types/yargs@^13.0.0": + version "13.0.11" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.11.tgz#def2f0c93e4bdf2c61d7e34899b17e34be28d3b1" + integrity sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ== + dependencies: + "@types/yargs-parser" "*" + "@types/yargs@^15.0.0": version "15.0.13" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" @@ -1016,7 +1080,7 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-regex@^4.1.0: +ansi-regex@^4.0.0, ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== @@ -1596,7 +1660,7 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1824,7 +1888,7 @@ colors@^1.1.2: resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -2209,6 +2273,11 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +diff-sequences@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" + integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== + diff-sequences@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" @@ -2612,6 +2681,18 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" +expect@^24.1.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" + integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== + dependencies: + "@jest/types" "^24.9.0" + ansi-styles "^3.2.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.9.0" + expect@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" @@ -2838,6 +2919,15 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" + integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -3683,6 +3773,16 @@ jest-config@^26.6.3: micromatch "^4.0.2" pretty-format "^26.6.2" +jest-diff@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" + integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== + dependencies: + chalk "^2.0.1" + diff-sequences "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + jest-diff@^26.0.0, jest-diff@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" @@ -3736,6 +3836,15 @@ jest-environment-node@^26.6.2: jest-mock "^26.6.2" jest-util "^26.6.2" +jest-extended@^0.11.5: + version "0.11.5" + resolved "https://registry.yarnpkg.com/jest-extended/-/jest-extended-0.11.5.tgz#f063b3f1eaadad8d7c13a01f0dfe0f538d498ccf" + integrity sha512-3RsdFpLWKScpsLD6hJuyr/tV5iFOrw7v6YjA3tPdda9sJwoHwcMROws5gwiIZfcwhHlJRwFJB2OUvGmF3evV/Q== + dependencies: + expect "^24.1.0" + jest-get-type "^22.4.3" + jest-matcher-utils "^22.0.0" + jest-fetch-mock@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz#31749c456ae27b8919d69824f1c2bd85fe0a1f3b" @@ -3744,6 +3853,16 @@ jest-fetch-mock@^3.0.3: cross-fetch "^3.0.4" promise-polyfill "^8.1.3" +jest-get-type@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" + integrity sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w== + +jest-get-type@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" + integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== + jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" @@ -3802,6 +3921,25 @@ jest-leak-detector@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-matcher-utils@^22.0.0: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz#4632fe428ebc73ebc194d3c7b65d37b161f710ff" + integrity sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA== + dependencies: + chalk "^2.0.1" + jest-get-type "^22.4.3" + pretty-format "^22.4.3" + +jest-matcher-utils@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" + integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== + dependencies: + chalk "^2.0.1" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + jest-matcher-utils@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" @@ -3812,6 +3950,20 @@ jest-matcher-utils@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-message-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" + integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/stack-utils" "^1.0.1" + chalk "^2.0.1" + micromatch "^3.1.10" + slash "^2.0.0" + stack-utils "^1.0.1" + jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -3840,6 +3992,11 @@ jest-pnp-resolver@^1.2.2: resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== +jest-regex-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" + integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== + jest-regex-util@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" @@ -5004,6 +5161,24 @@ pretty-bytes@^5.4.1: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.5.0.tgz#0cecda50a74a941589498011cf23275aa82b339e" integrity sha512-p+T744ZyjjiaFlMUZZv6YPC5JrkNj8maRmPaQCWFJFplUAzpIUTRaTcS+7wmZtUoFXHtESJb23ISliaWyz3SHA== +pretty-format@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f" + integrity sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ== + dependencies: + ansi-regex "^3.0.0" + ansi-styles "^3.2.0" + +pretty-format@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" + integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== + dependencies: + "@jest/types" "^24.9.0" + ansi-regex "^4.0.0" + ansi-styles "^3.2.0" + react-is "^16.8.4" + pretty-format@^26.0.0, pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" @@ -5151,6 +5326,11 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" +react-is@^16.8.4: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + react-is@^17.0.1: version "17.0.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" @@ -5591,6 +5771,11 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -5744,6 +5929,13 @@ ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" +stack-utils@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.4.tgz#4b600971dcfc6aed0cbdf2a8268177cc916c87c8" + integrity sha512-IPDJfugEGbfizBwBZRZ3xpccMdRyP5lqsBWXGQWimVjua/ccLCeMOAVjlc1R7LxFjo5sEDhyNIXd8mo/AiDS9w== + dependencies: + escape-string-regexp "^2.0.0" + stack-utils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277"