Skip to content

Commit

Permalink
fix: extend Contract class first parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
denbite committed Mar 22, 2024
1 parent e8c7e48 commit cc401a6
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 127 deletions.
5 changes: 5 additions & 0 deletions .changeset/little-chefs-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@near-js/accounts": minor
---

Extend Contract class to accept Connection object
109 changes: 10 additions & 99 deletions packages/accounts/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ import {
FinalExecutionOutcome,
TypedError,
ErrorContext,
ViewStateResult,
AccountView,
AccessKeyView,
AccessKeyViewRaw,
CodeResult,
AccessKeyList,
AccessKeyInfoView,
FunctionCallPermissionView,
Expand All @@ -31,11 +29,12 @@ import {
Logger,
parseResultError,
DEFAULT_FUNCTION_CALL_GAS,
printTxOutcomeLogs,
printTxOutcomeLogsAndFailures,
} from '@near-js/utils';

import { Connection } from './connection';
import { viewFunction, viewState } from './utils';
import { ChangeFunctionCallOptions, IntoConnection, ViewFunctionCallOptions } from './interface';

const {
addKey,
Expand Down Expand Up @@ -91,50 +90,6 @@ export interface SignAndSendTransactionOptions {
returnError?: boolean;
}

/**
* Options used to initiate a function call (especially a change function call)
* @see {@link Account#viewFunction | viewFunction} to initiate a view function call
*/
export interface FunctionCallOptions {
/** The NEAR account id where the contract is deployed */
contractId: string;
/** The name of the method to invoke */
methodName: string;
/**
* named arguments to pass the method `{ messageText: 'my message' }`
*/
args?: object;
/** max amount of gas that method call can use */
gas?: bigint;
/** amount of NEAR (in yoctoNEAR) to send together with the call */
attachedDeposit?: bigint;
/**
* Convert input arguments into bytes array.
*/
stringify?: (input: any) => Buffer;
/**
* Is contract from JS SDK, automatically encodes args from JS SDK to binary.
*/
jsContract?: boolean;
}

export interface ChangeFunctionCallOptions extends FunctionCallOptions {
/**
* Metadata to send the NEAR Wallet if using it to sign transactions.
* @see RequestSignTransactionsOptions
*/
walletMeta?: string;
/**
* Callback url to send the NEAR Wallet if using it to sign transactions.
* @see RequestSignTransactionsOptions
*/
walletCallbackUrl?: string;
}
export interface ViewFunctionCallOptions extends FunctionCallOptions {
parse?: (response: Uint8Array) => any;
blockQuery?: BlockReference;
}

interface StakedBalance {
validatorId: string;
amount?: string;
Expand All @@ -153,18 +108,10 @@ interface SignedDelegateOptions {
receiverId: string;
}

function parseJsonFromRawResponse(response: Uint8Array): any {
return JSON.parse(Buffer.from(response).toString());
}

function bytesJsonStringify(input: any): Buffer {
return Buffer.from(JSON.stringify(input));
}

/**
* This class provides common account related RPC calls including signing transactions with a {@link "@near-js/crypto".key_pair.KeyPair | KeyPair}.
*/
export class Account {
export class Account implements IntoConnection {
readonly connection: Connection;
readonly accountId: string;

Expand All @@ -173,6 +120,10 @@ export class Account {
this.accountId = accountId;
}

getConnection(): Connection {
return this.connection;
}

/**
* Returns basic NEAR account information via the `view_account` RPC query method
* @see [https://docs.near.org/api/rpc/contracts#view-account](https://docs.near.org/api/rpc/contracts#view-account)
Expand Down Expand Up @@ -544,38 +495,8 @@ export class Account {
* @returns {Promise<any>}
*/

async viewFunction({
contractId,
methodName,
args = {},
parse = parseJsonFromRawResponse,
stringify = bytesJsonStringify,
jsContract = false,
blockQuery = { finality: 'optimistic' }
}: ViewFunctionCallOptions): Promise<any> {
let encodedArgs;

this.validateArgs(args);

if (jsContract) {
encodedArgs = this.encodeJSContractArgs(contractId, methodName, Object.keys(args).length > 0 ? JSON.stringify(args) : '');
} else {
encodedArgs = stringify(args);
}

const result = await this.connection.provider.query<CodeResult>({
request_type: 'call_function',
...blockQuery,
account_id: jsContract ? this.connection.jsvmAccountId : contractId,
method_name: jsContract ? 'view_js_contract' : methodName,
args_base64: encodedArgs.toString('base64')
});

if (result.logs) {
printTxOutcomeLogs({ contractId, logs: result.logs });
}

return result.result && result.result.length > 0 && parse(Buffer.from(result.result));
async viewFunction(options: ViewFunctionCallOptions): Promise<any> {
return await viewFunction(this.connection, options);
}

/**
Expand All @@ -587,17 +508,7 @@ export class Account {
* @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: BlockReference = { finality: 'optimistic' }): Promise<Array<{ key: Buffer; value: Buffer }>> {
const { values } = await this.connection.provider.query<ViewStateResult>({
request_type: 'view_state',
...blockQuery,
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 await viewState(this.connection, this.accountId, prefix, blockQuery);
}

/**
Expand Down
7 changes: 6 additions & 1 deletion packages/accounts/src/connection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Signer, InMemorySigner } from '@near-js/signers';
import { Provider, JsonRpcProvider } from '@near-js/providers';
import { IntoConnection } from './interface';

/**
* @param config Contains connection info details
Expand Down Expand Up @@ -32,7 +33,7 @@ function getSigner(config: any): Signer {
/**
* Connects an account to a given network via a given provider
*/
export class Connection {
export class Connection implements IntoConnection {
readonly networkId: string;
readonly provider: Provider;
readonly signer: Signer;
Expand All @@ -45,6 +46,10 @@ export class Connection {
this.jsvmAccountId = jsvmAccountId;
}

getConnection(): Connection {
return this;
}

/**
* @param config Contains connection info details
*/
Expand Down
43 changes: 36 additions & 7 deletions packages/accounts/src/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import {
ArgumentSchemaError,
ConflictingOptions,
} from "./errors";
import { IntoConnection } from "./interface";
import { Connection } from "./connection";
import { viewFunction } from "./utils";

// Makes `function.name` return given name
function nameFunction(name: string, body: (args?: any[]) => any) {
Expand Down Expand Up @@ -84,6 +87,7 @@ const isObject = (x: any) =>
Object.prototype.toString.call(x) === "[object Object]";

interface ChangeMethodOptions {
signerAccount?: Account;
args: object;
methodName: string;
gas?: bigint;
Expand Down Expand Up @@ -153,7 +157,9 @@ export interface ContractMethods {
* ```
*/
export class Contract {
readonly account: Account;
/** @deprecated */
readonly account?: Account;
readonly connection: Connection;
readonly contractId: string;
readonly lve: LocalViewExecution;

Expand All @@ -163,13 +169,22 @@ export class Contract {
* @param options NEAR smart contract methods that your application will use. These will be available as `contract.methodName`
*/
constructor(
account: Account,
connection: IntoConnection,
contractId: string,
options: ContractMethods
) {
this.account = account;
this.connection = connection.getConnection();
if (connection instanceof Account) {
const deprecate = depd(
"new Contract(account, contractId, options)"
);
deprecate(
"use `new Contract(connection, contractId, options)` instead"
);
this.account = connection;
}
this.contractId = contractId;
this.lve = new LocalViewExecution(account);
this.lve = new LocalViewExecution(connection);
const {
viewMethods = [],
changeMethods = [],
Expand Down Expand Up @@ -235,7 +250,16 @@ export class Contract {
}
}

return this.account.viewFunction({
if (this.account) {
return this.account.viewFunction({
contractId: this.contractId,
methodName: name,
args,
...options,
});
}

return viewFunction(this.connection, {
contractId: this.contractId,
methodName: name,
args,
Expand Down Expand Up @@ -263,7 +287,7 @@ export class Contract {
"contract.methodName(args, gas, amount)"
);
deprecate(
"use `contract.methodName({ args, gas?, amount?, callbackUrl?, meta? })` instead"
"use `contract.methodName({ signerAccount, args, gas?, amount?, callbackUrl?, meta? })` instead"
);
args[0] = {
args: args[0],
Expand All @@ -283,6 +307,7 @@ export class Contract {
}

private async _changeMethod({
signerAccount,
args,
methodName,
gas,
Expand All @@ -292,7 +317,11 @@ export class Contract {
}: ChangeMethodOptions) {
validateBNLike({ gas, amount });

const rawResult = await this.account.functionCall({
const account = this.account || signerAccount;

if (!account) throw new Error(`signerAccount must be specified`);

const rawResult = await account.functionCall({
contractId: this.contractId,
methodName,
args,
Expand Down
10 changes: 6 additions & 4 deletions packages/accounts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ export {
Account,
AccountBalance,
AccountAuthorizedApp,
SignAndSendTransactionOptions,
FunctionCallOptions,
ChangeFunctionCallOptions,
ViewFunctionCallOptions,
SignAndSendTransactionOptions
} from './account';
export { Account2FA } from './account_2fa';
export {
Expand Down Expand Up @@ -37,3 +34,8 @@ export {
MultisigDeleteRequestRejectionError,
MultisigStateStatus,
} from './types';
export {
FunctionCallOptions,
ChangeFunctionCallOptions,
ViewFunctionCallOptions,
} from './interface';
50 changes: 50 additions & 0 deletions packages/accounts/src/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { BlockReference } from "@near-js/types";
import type { Connection } from "./connection";

export interface IntoConnection {
getConnection(): Connection;
}

/**
* Options used to initiate a function call (especially a change function call)
* @see {@link Account#viewFunction | viewFunction} to initiate a view function call
*/
export interface FunctionCallOptions {
/** The NEAR account id where the contract is deployed */
contractId: string;
/** The name of the method to invoke */
methodName: string;
/**
* named arguments to pass the method `{ messageText: 'my message' }`
*/
args?: object;
/** max amount of gas that method call can use */
gas?: bigint;
/** amount of NEAR (in yoctoNEAR) to send together with the call */
attachedDeposit?: bigint;
/**
* Convert input arguments into bytes array.
*/
stringify?: (input: any) => Buffer;
/**
* Is contract from JS SDK, automatically encodes args from JS SDK to binary.
*/
jsContract?: boolean;
}

export interface ChangeFunctionCallOptions extends FunctionCallOptions {
/**
* Metadata to send the NEAR Wallet if using it to sign transactions.
* @see RequestSignTransactionsOptions
*/
walletMeta?: string;
/**
* Callback url to send the NEAR Wallet if using it to sign transactions.
* @see RequestSignTransactionsOptions
*/
walletCallbackUrl?: string;
}
export interface ViewFunctionCallOptions extends FunctionCallOptions {
parse?: (response: Uint8Array) => any;
blockQuery?: BlockReference;
}
Loading

0 comments on commit cc401a6

Please sign in to comment.