From f7ad042b42a62be79efe7abc581b4fe7e95f7a14 Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Wed, 11 Nov 2020 02:39:52 -0800 Subject: [PATCH 01/10] Prototype account.viewState --- lib/account.d.ts | 2 ++ lib/account.js | 9 +++++++++ lib/providers/json-rpc-provider.d.ts | 4 +--- lib/providers/json-rpc-provider.js | 15 ++++++++++----- lib/providers/provider.d.ts | 1 + src/account.ts | 12 ++++++++++++ src/providers/json-rpc-provider.ts | 14 +++++++++----- src/providers/provider.ts | 2 ++ 8 files changed, 46 insertions(+), 13 deletions(-) diff --git a/lib/account.d.ts b/lib/account.d.ts index 93c08933fe..c0e3f3f481 100644 --- a/lib/account.d.ts +++ b/lib/account.d.ts @@ -3,6 +3,7 @@ import { AccessKey, Action, SignedTransaction } from './transaction'; import { FinalExecutionOutcome } from './providers'; import { Connection } from './connection'; import { PublicKey } from './utils/key_pair'; +import { Finality } from './providers/provider'; export interface AccountState { amount: string; code_hash: string; @@ -118,6 +119,7 @@ export declare class Account { viewFunction(contractId: string, methodName: string, args: any, { parse }?: { parse?: typeof parseJsonFromRawResponse; }): Promise; + viewState(prefix: string | Uint8Array, finality?: Finality): Promise; /** * @returns array of {access_key: AccessKey, public_key: PublicKey} items. */ diff --git a/lib/account.js b/lib/account.js index cf2a2c3075..65bff6026c 100644 --- a/lib/account.js +++ b/lib/account.js @@ -288,6 +288,15 @@ class Account { } return result.result && result.result.length > 0 && parse(Buffer.from(result.result)); } + async viewState(prefix, finality = 'optimistic') { + const { values } = await this.connection.provider.query({ + request_type: 'view_state', + finality, + account_id: this.accountId, + prefix_base64: Buffer.from(prefix).toString('base64') + }); + return values.map(({ key, value }) => ({ key: Buffer.from(key, 'base64'), value: Buffer.from(value, 'base64') })); + } /** * @returns array of {access_key: AccessKey, public_key: PublicKey} items. */ diff --git a/lib/providers/json-rpc-provider.d.ts b/lib/providers/json-rpc-provider.d.ts index 43b4060c9a..ba60265a71 100644 --- a/lib/providers/json-rpc-provider.d.ts +++ b/lib/providers/json-rpc-provider.d.ts @@ -35,10 +35,8 @@ export declare class JsonRpcProvider extends Provider { txStatus(txHash: Uint8Array, accountId: string): Promise; /** * Query the RPC as [shown in the docs](https://docs.nearprotocol.com/docs/interaction/rpc#query) - * @param path Path parameter for the RPC (ex. "contract/my_token") - * @param data Data parameter (ex. "", "AQ4", or whatever is needed) */ - query(path: string, data: string): Promise; + query(...args: any[]): Promise; /** * Query for block info from the RPC * See [docs for more info](https://docs.nearprotocol.com/docs/interaction/rpc#block) diff --git a/lib/providers/json-rpc-provider.js b/lib/providers/json-rpc-provider.js index d15dc3b055..22c4fcb8df 100644 --- a/lib/providers/json-rpc-provider.js +++ b/lib/providers/json-rpc-provider.js @@ -59,13 +59,18 @@ class JsonRpcProvider extends provider_1.Provider { } /** * Query the RPC as [shown in the docs](https://docs.nearprotocol.com/docs/interaction/rpc#query) - * @param path Path parameter for the RPC (ex. "contract/my_token") - * @param data Data parameter (ex. "", "AQ4", or whatever is needed) */ - async query(path, data) { - const result = await this.sendJsonRpc('query', [path, data]); + async query(...args) { + let result; + if (args.length === 1) { + result = await this.sendJsonRpc('query', args[0]); + } + else { + const [path, data] = args; + result = await this.sendJsonRpc('query', [path, data]); + } if (result && result.error) { - throw new Error(`Querying ${path} failed: ${result.error}.\n${JSON.stringify(result, null, 2)}`); + throw new Error(`Querying ${args} failed: ${result.error}.\n${JSON.stringify(result, null, 2)}`); } return result; } diff --git a/lib/providers/provider.d.ts b/lib/providers/provider.d.ts index 05216d3eb9..d14151883b 100644 --- a/lib/providers/provider.d.ts +++ b/lib/providers/provider.d.ts @@ -198,6 +198,7 @@ export declare abstract class Provider { abstract status(): Promise; abstract sendTransaction(signedTransaction: SignedTransaction): Promise; abstract txStatus(txHash: Uint8Array, accountId: string): Promise; + abstract query(params: object): Promise; abstract query(path: string, data: string): Promise; abstract block(blockId: BlockId): Promise; abstract chunk(chunkId: ChunkId): Promise; diff --git a/src/account.ts b/src/account.ts index 315dd677a0..c28779bdfb 100644 --- a/src/account.ts +++ b/src/account.ts @@ -26,6 +26,7 @@ import { parseRpcError } from './utils/rpc_errors'; import { ServerError } from './generated/rpc_error_types'; import exponentialBackoff from './utils/exponential-backoff'; +import { Finality } from './providers/provider'; // Default amount of gas to be sent with the function calls. Used to pay for the fees // incurred while running the contract execution. The unused amount will be refunded back to @@ -373,6 +374,17 @@ export class Account { return result.result && result.result.length > 0 && parse(Buffer.from(result.result)); } + async viewState(prefix: string | Uint8Array, finality: Finality = 'optimistic') { + const { values } = await this.connection.provider.query({ + request_type: 'view_state', + finality, + account_id: this.accountId, + prefix_base64: Buffer.from(prefix,).toString('base64') + }); + + return values.map(({key, value}) => ({ key: Buffer.from(key, 'base64'), value: Buffer.from(value, 'base64')})) + } + /** * @returns array of {access_key: AccessKey, public_key: PublicKey} items. */ diff --git a/src/providers/json-rpc-provider.ts b/src/providers/json-rpc-provider.ts index b4e954af33..82bb83d902 100644 --- a/src/providers/json-rpc-provider.ts +++ b/src/providers/json-rpc-provider.ts @@ -68,13 +68,17 @@ export class JsonRpcProvider extends Provider { /** * Query the RPC as [shown in the docs](https://docs.nearprotocol.com/docs/interaction/rpc#query) - * @param path Path parameter for the RPC (ex. "contract/my_token") - * @param data Data parameter (ex. "", "AQ4", or whatever is needed) */ - async query(path: string, data: string): Promise { - const result = await this.sendJsonRpc('query', [path, data]); + async query(...args: any[]): Promise { + let result + if (args.length === 1) { + result = await this.sendJsonRpc('query', args[0]); + } else { + const [path, data] = args; + result = await this.sendJsonRpc('query', [path, data]); + } if (result && result.error) { - throw new Error(`Querying ${path} failed: ${result.error}.\n${JSON.stringify(result, null, 2)}`); + throw new Error(`Querying ${args} failed: ${result.error}.\n${JSON.stringify(result, null, 2)}`); } return result; } diff --git a/src/providers/provider.ts b/src/providers/provider.ts index 55293a8859..610d68d137 100644 --- a/src/providers/provider.ts +++ b/src/providers/provider.ts @@ -26,6 +26,7 @@ type BlockHash = string; type BlockHeight = number; export type BlockId = BlockHash | BlockHeight; +// TODO: Remove near-final? export type Finality = 'optimistic' | 'near-final' | 'final' export enum ExecutionStatusBasic { @@ -241,6 +242,7 @@ export abstract class Provider { abstract async sendTransaction(signedTransaction: SignedTransaction): Promise; abstract async txStatus(txHash: Uint8Array, accountId: string): Promise; + abstract async query(params: object): Promise; abstract async query(path: string, data: string): Promise; abstract async block(blockId: BlockId): Promise; abstract async chunk(chunkId: ChunkId): Promise; From e77edb601624d7abf5b84bcc33b47beab03809e4 Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Thu, 12 Nov 2020 20:05:15 -0800 Subject: [PATCH 02/10] Support both blockId and finality in viewState --- lib/account.d.ts | 8 ++++++-- lib/account.js | 6 ++++-- src/account.ts | 8 +++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/account.d.ts b/lib/account.d.ts index c0e3f3f481..50de238fff 100644 --- a/lib/account.d.ts +++ b/lib/account.d.ts @@ -1,9 +1,9 @@ import BN from 'bn.js'; import { AccessKey, Action, SignedTransaction } from './transaction'; import { FinalExecutionOutcome } from './providers'; +import { Finality, BlockId } from './providers/provider'; import { Connection } from './connection'; import { PublicKey } from './utils/key_pair'; -import { Finality } from './providers/provider'; export interface AccountState { amount: string; code_hash: string; @@ -119,7 +119,11 @@ export declare class Account { viewFunction(contractId: string, methodName: string, args: any, { parse }?: { parse?: typeof parseJsonFromRawResponse; }): Promise; - viewState(prefix: string | Uint8Array, finality?: Finality): Promise; + viewState(prefix: string | Uint8Array, blockQuery: { + blockId: BlockId; + } | { + finality: Finality; + }): Promise; /** * @returns array of {access_key: AccessKey, public_key: PublicKey} items. */ diff --git a/lib/account.js b/lib/account.js index 65bff6026c..429e7f8387 100644 --- a/lib/account.js +++ b/lib/account.js @@ -288,10 +288,12 @@ class Account { } return result.result && result.result.length > 0 && parse(Buffer.from(result.result)); } - async viewState(prefix, finality = 'optimistic') { + async viewState(prefix, blockQuery) { + const { blockId, finality } = blockQuery || {}; const { values } = await this.connection.provider.query({ request_type: 'view_state', - finality, + block_id: blockId, + finality: blockId ? undefined : finality || 'optimistic', account_id: this.accountId, prefix_base64: Buffer.from(prefix).toString('base64') }); diff --git a/src/account.ts b/src/account.ts index c28779bdfb..9c60121946 100644 --- a/src/account.ts +++ b/src/account.ts @@ -18,6 +18,7 @@ import { SignedTransaction } from './transaction'; import { FinalExecutionOutcome, TypedError, ErrorContext } from './providers'; +import { Finality, BlockId } from './providers/provider'; import { Connection } from './connection'; import {base_decode, base_encode} from './utils/serialize'; import { PublicKey } from './utils/key_pair'; @@ -26,7 +27,6 @@ import { parseRpcError } from './utils/rpc_errors'; import { ServerError } from './generated/rpc_error_types'; import exponentialBackoff from './utils/exponential-backoff'; -import { Finality } from './providers/provider'; // Default amount of gas to be sent with the function calls. Used to pay for the fees // incurred while running the contract execution. The unused amount will be refunded back to @@ -374,10 +374,12 @@ export class Account { return result.result && result.result.length > 0 && parse(Buffer.from(result.result)); } - async viewState(prefix: string | Uint8Array, finality: Finality = 'optimistic') { + async viewState(prefix: string | Uint8Array, blockQuery: { blockId: BlockId } | { finality: Finality } ) { + const { blockId, finality } = blockQuery as any || {}; const { values } = await this.connection.provider.query({ request_type: 'view_state', - finality, + block_id: blockId, + finality: blockId ? undefined : finality || 'optimistic', account_id: this.accountId, prefix_base64: Buffer.from(prefix,).toString('base64') }); From 777229d6bfa9b27ee21f5409dd74a686645786d0 Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Wed, 18 Nov 2020 14:58:59 -0800 Subject: [PATCH 03/10] yarn fix --- src/providers/json-rpc-provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/json-rpc-provider.ts b/src/providers/json-rpc-provider.ts index 82bb83d902..2ddb065632 100644 --- a/src/providers/json-rpc-provider.ts +++ b/src/providers/json-rpc-provider.ts @@ -70,7 +70,7 @@ export class JsonRpcProvider extends Provider { * Query the RPC as [shown in the docs](https://docs.nearprotocol.com/docs/interaction/rpc#query) */ async query(...args: any[]): Promise { - let result + let result; if (args.length === 1) { result = await this.sendJsonRpc('query', args[0]); } else { From 39854c8e91d58c6a525af8e80b7d9a144f98a5aa Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Thu, 19 Nov 2020 17:37:21 -0800 Subject: [PATCH 04/10] Style fixes --- src/account.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/account.ts b/src/account.ts index 9c60121946..159065ec70 100644 --- a/src/account.ts +++ b/src/account.ts @@ -381,10 +381,13 @@ export class Account { block_id: blockId, finality: blockId ? undefined : finality || 'optimistic', account_id: this.accountId, - prefix_base64: Buffer.from(prefix,).toString('base64') + prefix_base64: Buffer.from(prefix).toString('base64') }); - return values.map(({key, value}) => ({ key: Buffer.from(key, 'base64'), value: Buffer.from(value, 'base64')})) + return values.map(({key, value}) => ({ + key: Buffer.from(key, 'base64'), + value: Buffer.from(value, 'base64') + })) } /** From b05dc16f790a9e5acc7d977e896e676e14263082 Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Thu, 19 Nov 2020 21:26:11 -0800 Subject: [PATCH 05/10] Add doc for viewState method --- src/account.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/account.ts b/src/account.ts index 159065ec70..efbdb9d5e8 100644 --- a/src/account.ts +++ b/src/account.ts @@ -374,10 +374,20 @@ export class Account { return result.result && result.result.length > 0 && parse(Buffer.from(result.result)); } + /** + * See https://docs.near.org/docs/api/rpc#view-contract-state + * + * Returns the state (key value pairs) of this account's contract based on the key prefix. + * Pass an empty string for prefix if you would like to return the entire state. + * + * @param prefix allows to filter which keys should be returned. Empty prefix means all keys. String prefix is utf-8 encoded. + * @param blockQuery specifies which block to query state at. By default returns last "optimistic" block (i.e. not necessarily finalized). + * + */ async viewState(prefix: string | Uint8Array, blockQuery: { blockId: BlockId } | { finality: Finality } ) { const { blockId, finality } = blockQuery as any || {}; const { values } = await this.connection.provider.query({ - request_type: 'view_state', + request_type: 'view_state', block_id: blockId, finality: blockId ? undefined : finality || 'optimistic', account_id: this.accountId, From f114089e046d6ac1c25aad4153d2bdd9bf8b4325 Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Thu, 19 Nov 2020 22:01:30 -0800 Subject: [PATCH 06/10] yarn compile --- lib/account.d.ts | 10 ++++++++++ lib/account.js | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/account.d.ts b/lib/account.d.ts index 50de238fff..1663b6c182 100644 --- a/lib/account.d.ts +++ b/lib/account.d.ts @@ -119,6 +119,16 @@ export declare class Account { viewFunction(contractId: string, methodName: string, args: any, { parse }?: { parse?: typeof parseJsonFromRawResponse; }): Promise; + /** + * See https://docs.near.org/docs/api/rpc#view-contract-state + * + * Returns the state (key value pairs) of this account's contract based on the key prefix. + * Pass an empty string for prefix if you would like to return the entire state. + * + * @param prefix allows to filter which keys should be returned. Empty prefix means all keys. String prefix is utf-8 encoded. + * @param blockQuery specifies which block to query state at. By default returns last "optimistic" block (i.e. not necessarily finalized). + * + */ viewState(prefix: string | Uint8Array, blockQuery: { blockId: BlockId; } | { diff --git a/lib/account.js b/lib/account.js index 429e7f8387..e169489e64 100644 --- a/lib/account.js +++ b/lib/account.js @@ -288,6 +288,16 @@ class Account { } return result.result && result.result.length > 0 && parse(Buffer.from(result.result)); } + /** + * See https://docs.near.org/docs/api/rpc#view-contract-state + * + * Returns the state (key value pairs) of this account's contract based on the key prefix. + * Pass an empty string for prefix if you would like to return the entire state. + * + * @param prefix allows to filter which keys should be returned. Empty prefix means all keys. String prefix is utf-8 encoded. + * @param blockQuery specifies which block to query state at. By default returns last "optimistic" block (i.e. not necessarily finalized). + * + */ async viewState(prefix, blockQuery) { const { blockId, finality } = blockQuery || {}; const { values } = await this.connection.provider.query({ @@ -297,7 +307,10 @@ class Account { account_id: this.accountId, prefix_base64: Buffer.from(prefix).toString('base64') }); - return values.map(({ key, value }) => ({ key: Buffer.from(key, 'base64'), value: Buffer.from(value, 'base64') })); + return values.map(({ key, value }) => ({ + key: Buffer.from(key, 'base64'), + value: Buffer.from(value, 'base64') + })); } /** * @returns array of {access_key: AccessKey, public_key: PublicKey} items. From 356ef6fcaf10fce46f532a99f27197e7e3507a82 Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Fri, 20 Nov 2020 00:17:05 -0800 Subject: [PATCH 07/10] Remove TODO --- src/providers/provider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/providers/provider.ts b/src/providers/provider.ts index 610d68d137..49b1a2df5f 100644 --- a/src/providers/provider.ts +++ b/src/providers/provider.ts @@ -26,7 +26,6 @@ type BlockHash = string; type BlockHeight = number; export type BlockId = BlockHash | BlockHeight; -// TODO: Remove near-final? export type Finality = 'optimistic' | 'near-final' | 'final' export enum ExecutionStatusBasic { From b617b5cbfdc195a548e8bce15f5c5fd5b5280459 Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Fri, 20 Nov 2020 00:17:15 -0800 Subject: [PATCH 08/10] Add return type --- src/account.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/account.ts b/src/account.ts index efbdb9d5e8..4d4f02c557 100644 --- a/src/account.ts +++ b/src/account.ts @@ -382,9 +382,8 @@ export class Account { * * @param prefix allows to filter which keys should be returned. Empty prefix means all keys. String prefix is utf-8 encoded. * @param blockQuery specifies which block to query state at. By default returns last "optimistic" block (i.e. not necessarily finalized). - * */ - async viewState(prefix: string | Uint8Array, blockQuery: { blockId: BlockId } | { finality: Finality } ) { + async viewState(prefix: string | Uint8Array, blockQuery: { blockId: BlockId } | { finality: Finality } ): Promise> { const { blockId, finality } = blockQuery as any || {}; const { values } = await this.connection.provider.query({ request_type: 'view_state', From 719fc466d551a59e811a88f7153e7c6a879880c0 Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Fri, 20 Nov 2020 00:17:27 -0800 Subject: [PATCH 09/10] Add test --- test/account.test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/account.test.js b/test/account.test.js index d9f102c112..b5ecb83770 100644 --- a/test/account.test.js +++ b/test/account.test.js @@ -142,6 +142,15 @@ describe('with deploy contract', () => { expect(await workingAccount.viewFunction(contractId, 'getValue', {})).toEqual(setCallValue); }); + test('view contract state', async() => { + const setCallValue = testUtils.generateUniqueString('setCallPrefix'); + await workingAccount.functionCall(contractId, 'setValue', { value: setCallValue }); + + const contractAccount = await nearjs.account(contractId); + const state = (await contractAccount.viewState('')).map(({ key, value }) => [key.toString('utf-8'), value.toString('utf-8')]); + expect(state).toEqual([['name', setCallValue]]); + }); + test('make function calls via account with custom parser', async() => { const result = await workingAccount.viewFunction( contractId, From 47e6b81415ac1f86ad88a24926681f7e7c85948b Mon Sep 17 00:00:00 2001 From: Vladimir Grichina Date: Fri, 20 Nov 2020 00:17:35 -0800 Subject: [PATCH 10/10] yarn compile --- lib/account.d.ts | 7 +++++-- lib/account.js | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/account.d.ts b/lib/account.d.ts index 1663b6c182..d389439a41 100644 --- a/lib/account.d.ts +++ b/lib/account.d.ts @@ -1,3 +1,4 @@ +/// import BN from 'bn.js'; import { AccessKey, Action, SignedTransaction } from './transaction'; import { FinalExecutionOutcome } from './providers'; @@ -127,13 +128,15 @@ export declare class Account { * * @param prefix allows to filter which keys should be returned. Empty prefix means all keys. String prefix is utf-8 encoded. * @param blockQuery specifies which block to query state at. By default returns last "optimistic" block (i.e. not necessarily finalized). - * */ viewState(prefix: string | Uint8Array, blockQuery: { blockId: BlockId; } | { finality: Finality; - }): Promise; + }): Promise>; /** * @returns array of {access_key: AccessKey, public_key: PublicKey} items. */ diff --git a/lib/account.js b/lib/account.js index e169489e64..197136ecdb 100644 --- a/lib/account.js +++ b/lib/account.js @@ -296,7 +296,6 @@ class Account { * * @param prefix allows to filter which keys should be returned. Empty prefix means all keys. String prefix is utf-8 encoded. * @param blockQuery specifies which block to query state at. By default returns last "optimistic" block (i.e. not necessarily finalized). - * */ async viewState(prefix, blockQuery) { const { blockId, finality } = blockQuery || {};