Skip to content

Commit

Permalink
Merge pull request #935 from near/NAJ-156
Browse files Browse the repository at this point in the history
Update viewFunction with only accepting single object as argument
  • Loading branch information
hcho112 committed Oct 4, 2022
2 parents 155dee6 + c2f93a9 commit 37ca222
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 72 deletions.
17 changes: 17 additions & 0 deletions .changeset/great-jobs-destroy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"near-api-js": major
---

`account.viewFunction` now takes a single object argument rather than individual args. Callsites will now need to be updated like so:
```diff
-await account.viewFunction(
- 'wrap.near',
- 'storage_balance_of',
- { account_id: 'example.near' }
-);
+await account.viewFunction({
+ contractId: 'wrap.near',
+ methodName: 'storage_balance_of',
+ args: { account_id: 'example.near' },
+});
```
10 changes: 5 additions & 5 deletions packages/cookbook/utils/wrap-near.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ async function wrapNear(accountId, wrapAmount) {
];

// check if storage has been paid (the account has a wNEAR account)
const storage = await account.viewFunction(
WRAP_NEAR_CONTRACT_ID,
"storage_balance_of",
{ account_id: accountId }
);
const storage = await account.viewFunction({
contractId: WRAP_NEAR_CONTRACT_ID,
methodName: "storage_balance_of",
args: { account_id: accountId },
});

// if storage hasn't been paid, pay for storage (create an account)
if (!storage) {
Expand Down
45 changes: 11 additions & 34 deletions packages/near-api-js/src/account.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import BN from 'bn.js';
import depd from 'depd';

import {
transfer,
createAccount,
Expand Down Expand Up @@ -91,7 +91,7 @@ export interface FunctionCallOptions {
/**
* named arguments to pass the method `{ messageText: 'my message' }`
*/
args: object;
args?: object;
/** max amount of gas that method call can use */
gas?: BN;
/** amount of NEAR (in yoctoNEAR) to send together with the call */
Expand Down Expand Up @@ -499,43 +499,20 @@ export class Account {
* Invoke a contract view function using the RPC API.
* @see [https://docs.near.org/api/rpc/contracts#call-a-contract-function](https://docs.near.org/api/rpc/contracts#call-a-contract-function)
*
* @param contractId NEAR account where the contract is deployed
* @param methodName The view-only method (no state mutations) name on the contract as it is written in the contract code
* @param args Any arguments to the view contract method, wrapped in JSON
* @param options.parse Parse the result of the call. Receives a Buffer (bytes array) and converts it to any object. By default result will be treated as json.
* @param options.stringify Convert input arguments into a bytes array. By default the input is treated as a JSON.
* @param options.jsContract Is contract from JS SDK, automatically encodes args from JS SDK to binary.
* @param options.blockQuery specifies which block to query state at. By default returns last "optimistic" block (i.e. not necessarily finalized).
* @param viewFunctionCallOptions.contractId NEAR account where the contract is deployed
* @param viewFunctionCallOptions.methodName The view-only method (no state mutations) name on the contract as it is written in the contract code
* @param viewFunctionCallOptions.args Any arguments to the view contract method, wrapped in JSON
* @param viewFunctionCallOptions.parse Parse the result of the call. Receives a Buffer (bytes array) and converts it to any object. By default result will be treated as json.
* @param viewFunctionCallOptions.stringify Convert input arguments into a bytes array. By default the input is treated as a JSON.
* @param viewFunctionCallOptions.jsContract Is contract from JS SDK, automatically encodes args from JS SDK to binary.
* @param viewFunctionCallOptions.blockQuery specifies which block to query state at. By default returns last "optimistic" block (i.e. not necessarily finalized).
* @returns {Promise<any>}
*/

async viewFunction(...restArgs: any) {
if (typeof restArgs[0] === 'string') {
const contractId = restArgs[0];
const methodName = restArgs[1];
const args = restArgs[2];
const options = restArgs[3];
return await this.viewFunctionV1(contractId, methodName, args, options);
} else {
return await this.viewFunctionV2(restArgs[0]);
}
}

async viewFunctionV1(
contractId: string,
methodName: string,
args: any = {},
{ parse = parseJsonFromRawResponse, stringify = bytesJsonStringify, jsContract=false, blockQuery = { finality: 'optimistic' } }: { parse?: (response: Uint8Array) => any; stringify?: (input: any) => Buffer; blockQuery?: BlockReference; jsContract?: boolean } = {}
): Promise<any> {
const deprecate = depd('Account.viewFunction(contractId, methodName, args, options)');
deprecate('use `Account.viewFunction(ViewFunctionCallOptions)` instead');
return this.viewFunctionV2({ contractId, methodName, args, parse, stringify, jsContract, blockQuery });
}

async viewFunctionV2({
async viewFunction({
contractId,
methodName,
args,
args = {},
parse = parseJsonFromRawResponse,
stringify = bytesJsonStringify,
jsContract = false,
Expand Down
5 changes: 4 additions & 1 deletion packages/near-api-js/src/account_multisig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,10 @@ export class AccountMultisig extends Account {
async getRequestIds(): Promise<string[]> {
// TODO: Read requests from state to allow filtering by expiration time
// TODO: https://github.com/near/core-contracts/blob/305d1db4f4f2cf5ce4c1ef3479f7544957381f11/multisig/src/lib.rs#L84
return this.viewFunction(this.accountId, 'list_request_ids');
return this.viewFunction({
contractId: this.accountId,
methodName: 'list_request_ids',
});
}

getRequest() {
Expand Down
7 changes: 6 additions & 1 deletion packages/near-api-js/src/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,12 @@ export class Contract {
if (ignored.length || !(isObject(args) || isUint8Array(args)) || !isObject(options)) {
throw new PositionalArgsError();
}
return this.account.viewFunction(this.contractId, methodName, args, options);
return this.account.viewFunction({
contractId: this.contractId,
methodName,
args,
...options,
});
})
});
});
Expand Down
80 changes: 53 additions & 27 deletions packages/near-api-js/test/account.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,11 @@ describe('with deploy contract', () => {
});

test('make function calls via account', async() => {
const result = await workingAccount.viewFunction(
const result = await workingAccount.viewFunction({
contractId,
'hello', // this is the function defined in hello.wasm file that we are calling
{name: 'trex'});
methodName: 'hello', // this is the function defined in hello.wasm file that we are calling
args: {name: 'trex'}
});
expect(result).toEqual('hello trex');

const setCallValue = testUtils.generateUniqueString('setCallPrefix');
Expand All @@ -166,7 +167,10 @@ describe('with deploy contract', () => {
args: { value: setCallValue }
});
expect(providers.getTransactionLastResult(result2)).toEqual(setCallValue);
expect(await workingAccount.viewFunction(contractId, 'getValue', {})).toEqual(setCallValue);
expect(await workingAccount.viewFunction({
contractId,
methodName: 'getValue'
})).toEqual(setCallValue);
});

test('view contract state', async() => {
Expand All @@ -183,12 +187,12 @@ describe('with deploy contract', () => {
});

test('make function calls via account with custom parser', async() => {
const result = await workingAccount.viewFunction(
const result = await workingAccount.viewFunction({
contractId,
'hello', // this is the function defined in hello.wasm file that we are calling
{name: 'trex'},
{ parse: x => JSON.parse(x.toString()).replace('trex', 'friend') }
);
methodName:'hello', // this is the function defined in hello.wasm file that we are calling
args: {name: 'trex'},
parse: x => JSON.parse(x.toString()).replace('trex', 'friend')
});
expect(result).toEqual('hello friend');
});

Expand All @@ -208,54 +212,76 @@ describe('with deploy contract', () => {
expect(result1).toEqual(setCallValue1);
expect(await contract.getValue()).toEqual(setCallValue1);

expect(await workingAccount.viewFunction(contractId, 'getValue', {}, {
blockQuery: { finality: 'optimistic' }
expect(await workingAccount.viewFunction({
contractId,
methodName: 'getValue',
blockQuery: { finality: 'optimistic' },
})).toEqual(setCallValue1);

expect(await workingAccount.viewFunction(contractId, 'getValue', {})).toEqual(setCallValue1);
expect(await workingAccount.viewFunction({
contractId,
methodName: 'getValue'
})).toEqual(setCallValue1);

const block1 = await workingAccount.connection.provider.block({ finality: 'optimistic' });
const blockHash1 = block1.header.hash;
const blockIndex1 = block1.header.height;

expect(await workingAccount.viewFunction(contractId, 'getValue', {}, {
blockQuery: { blockId: blockHash1 }
expect(await workingAccount.viewFunction({
contractId,
methodName: 'getValue',
blockQuery: { blockId: blockHash1 },
})).toEqual(setCallValue1);

expect(await workingAccount.viewFunction(contractId, 'getValue', {}, {
blockQuery: { blockId: blockIndex1 }
expect(await workingAccount.viewFunction({
contractId,
methodName: 'getValue',
blockQuery: { blockId: blockIndex1 },
})).toEqual(setCallValue1);

const setCallValue2 = testUtils.generateUniqueString('setCallPrefix');
const result2 = await contract.setValue({ args: { value: setCallValue2 } });
expect(result2).toEqual(setCallValue2);
expect(await contract.getValue()).toEqual(setCallValue2);

expect(await workingAccount.viewFunction(contractId, 'getValue', {}, {
blockQuery: { finality: 'optimistic' }
expect(await workingAccount.viewFunction({
contractId,
methodName: 'getValue',
blockQuery: { finality: 'optimistic' },
})).toEqual(setCallValue2);

expect(await workingAccount.viewFunction(contractId, 'getValue', {})).toEqual(setCallValue2);
expect(await workingAccount.viewFunction({
contractId,
methodName: 'getValue'
})).toEqual(setCallValue2);

// Old blockHash should still be value #1
expect(await workingAccount.viewFunction(contractId, 'getValue', {}, {
blockQuery: { blockId: blockHash1 }
expect(await workingAccount.viewFunction({
contractId,
methodName: 'getValue',
blockQuery: { blockId: blockHash1 },
})).toEqual(setCallValue1);

expect(await workingAccount.viewFunction(contractId, 'getValue', {}, {
blockQuery: { blockId: blockIndex1 }
expect(await workingAccount.viewFunction({
contractId,
methodName: 'getValue',
blockQuery: { blockId: blockIndex1 },
})).toEqual(setCallValue1);

const block2 = await workingAccount.connection.provider.block({ finality: 'optimistic' });
const blockHash2 = block2.header.hash;
const blockIndex2 = block2.header.height;

expect(await workingAccount.viewFunction(contractId, 'getValue', {}, {
blockQuery: { blockId: blockHash2 }
expect(await workingAccount.viewFunction({
contractId,
methodName: 'getValue',
blockQuery: { blockId: blockHash2 },
})).toEqual(setCallValue2);

expect(await workingAccount.viewFunction(contractId, 'getValue', {}, {
blockQuery: { blockId: blockIndex2 }
expect(await workingAccount.viewFunction({
contractId,
methodName: 'getValue',
blockQuery: { blockId: blockIndex2 },
})).toEqual(setCallValue2);
});

Expand Down
8 changes: 4 additions & 4 deletions packages/near-api-js/test/contract.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const { Contract } = require('../src/contract');
const { PositionalArgsError } = require('../src/utils/errors');

const account = {
viewFunction(contractId, methodName, args, options) {
return { this: this, contractId, methodName, args, options };
viewFunction({ contractId, methodName, args, parse, stringify, jsContract, blockQuery}) {
return { this: this, contractId, methodName, args, parse, stringify, jsContract, blockQuery };
},
functionCall() {
return this;
Expand Down Expand Up @@ -50,8 +50,8 @@ const contract = new Contract(account, 'contractId', {
describe('viewMethod', () => {
test('passes options through to account viewFunction', async () => {
function customParser () {}
const stubbedReturnValue = await contract.viewMethod({}, { parse: customParser });
expect(stubbedReturnValue.options.parse).toBe(customParser);
const stubbedReturnValue = await account.viewFunction({ parse: customParser });
expect(stubbedReturnValue.parse).toBe(customParser);
});

describe.each([
Expand Down

0 comments on commit 37ca222

Please sign in to comment.