From 9ff900cccf10b57a56991c1524dbca5373717936 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Fri, 9 Aug 2024 13:56:54 -0700 Subject: [PATCH 01/55] feat: client lib init --- packages/client/.eslintrc.yml | 19 +++ packages/client/README.md | 8 ++ packages/client/jest.config.ts | 13 ++ packages/client/package.json | 40 ++++++ packages/client/src/constants.ts | 5 + packages/client/src/index.ts | 3 + packages/client/src/interfaces.ts | 92 +++++++++++++ packages/client/src/provider.ts | 17 +++ packages/client/src/transactions/common.ts | 61 +++++++++ packages/client/src/transactions/composer.ts | 121 ++++++++++++++++++ packages/client/src/transactions/index.ts | 3 + .../client/src/transactions/sign_and_send.ts | 30 +++++ packages/client/src/view.ts | 120 +++++++++++++++++ packages/client/tsconfig.cjs.json | 10 ++ packages/client/tsconfig.json | 9 ++ packages/client/typedoc.json | 9 ++ pnpm-lock.yaml | 37 ++++++ 17 files changed, 597 insertions(+) create mode 100644 packages/client/.eslintrc.yml create mode 100644 packages/client/README.md create mode 100644 packages/client/jest.config.ts create mode 100644 packages/client/package.json create mode 100644 packages/client/src/constants.ts create mode 100644 packages/client/src/index.ts create mode 100644 packages/client/src/interfaces.ts create mode 100644 packages/client/src/provider.ts create mode 100644 packages/client/src/transactions/common.ts create mode 100644 packages/client/src/transactions/composer.ts create mode 100644 packages/client/src/transactions/index.ts create mode 100644 packages/client/src/transactions/sign_and_send.ts create mode 100644 packages/client/src/view.ts create mode 100644 packages/client/tsconfig.cjs.json create mode 100644 packages/client/tsconfig.json create mode 100644 packages/client/typedoc.json diff --git a/packages/client/.eslintrc.yml b/packages/client/.eslintrc.yml new file mode 100644 index 0000000000..3de14a5ee7 --- /dev/null +++ b/packages/client/.eslintrc.yml @@ -0,0 +1,19 @@ +env: + es6: true + node: true +extends: + - 'eslint:recommended' + - 'plugin:@typescript-eslint/eslint-recommended' + - 'plugin:@typescript-eslint/recommended' +parser: '@typescript-eslint/parser' +rules: + no-inner-declarations: off + indent: + - error + - 2 + - SwitchCase: 1 + '@typescript-eslint/no-explicit-any': off + +parserOptions: + ecmaVersion: 2018 + sourceType: module diff --git a/packages/client/README.md b/packages/client/README.md new file mode 100644 index 0000000000..6c52045f90 --- /dev/null +++ b/packages/client/README.md @@ -0,0 +1,8 @@ +# @near-js/client + +TODO + +# License + +This repository is distributed under the terms of both the MIT license and the Apache License (Version 2.0). +See [LICENSE](https://github.com/near/near-api-js/blob/master/LICENSE) and [LICENSE-APACHE](https://github.com/near/near-api-js/blob/master/LICENSE-APACHE) for details. diff --git a/packages/client/jest.config.ts b/packages/client/jest.config.ts new file mode 100644 index 0000000000..3b66313239 --- /dev/null +++ b/packages/client/jest.config.ts @@ -0,0 +1,13 @@ +export default { + preset: 'ts-jest', + collectCoverage: true, + testEnvironment: 'node', + testRegex: "(/tests/.*|(\\.|/)(test|spec))\\.[jt]sx?$", + transform: { + '^.+\\.[tj]s$': ['ts-jest', { + tsconfig: { + allowJs: true, + }, + }], + }, +}; diff --git a/packages/client/package.json b/packages/client/package.json new file mode 100644 index 0000000000..9a4ca7104c --- /dev/null +++ b/packages/client/package.json @@ -0,0 +1,40 @@ +{ + "name": "@near-js/client", + "version": "0.0.1", + "description": "", + "main": "lib/esm/index.js", + "type": "module", + "scripts": { + "build": "pnpm compile:esm && pnpm compile:cjs", + "compile:esm": "tsc -p tsconfig.json", + "compile:cjs": "tsc -p tsconfig.cjs.json && cjsify ./lib/commonjs", + "lint": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc", + "lint:fix": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc --fix", + "test": "jest" + }, + "dependencies": { + "@near-js/crypto": "workspace:*", + "@near-js/keystores-node": "workspace:*", + "@near-js/providers": "workspace:*", + "@near-js/signers": "workspace:*", + "@near-js/transactions": "workspace:*", + "@near-js/types": "workspace:*", + "@near-js/utils": "workspace:*", + "@noble/hashes": "1.3.3" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@types/node": "20.0.0", + "build": "workspace:*", + "typescript": "5.4.5" + }, + "files": [ + "lib" + ], + "exports": { + "require": "./lib/commonjs/index.cjs", + "import": "./lib/esm/index.js" + } +} diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts new file mode 100644 index 0000000000..8d8f06a4fd --- /dev/null +++ b/packages/client/src/constants.ts @@ -0,0 +1,5 @@ +export const ACCOUNT_CREATION_DEPOSIT = BigInt(100); +export const MAINNET_RPC_URL = 'https://rpc.mainnet.near.org'; +export const TESTNET_RPC_URL = 'https://rpc.testnet.near.org'; +export const DEFAULT_FILESYSTEM_KEYSTORE_PATH = '.near-credentials'; +export const DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL = BigInt(100); diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts new file mode 100644 index 0000000000..176b9aed89 --- /dev/null +++ b/packages/client/src/index.ts @@ -0,0 +1,3 @@ +export * from './interfaces'; +export * from './transactions'; +export * from './view'; diff --git a/packages/client/src/interfaces.ts b/packages/client/src/interfaces.ts new file mode 100644 index 0000000000..a76ea0a298 --- /dev/null +++ b/packages/client/src/interfaces.ts @@ -0,0 +1,92 @@ +import type { + BlockReference, + BlockResult, + FinalExecutionOutcome, + QueryResponseKind, +} from '@near-js/types'; +import type { SignedTransaction } from '@near-js/transactions'; +import type { PublicKey } from '@near-js/crypto'; +import type { Transaction } from '@near-js/transactions'; + +import type { TransactionComposer } from './transactions'; + +interface Dependent { + deps: T; +} + +export interface RpcQueryProvider { + block(block: BlockReference): Promise; + query(...args: any[]): Promise; + sendTransaction(transaction: SignedTransaction): Promise; +} + +export interface MessageSigner { + getPublicKey(): Promise; + signMessage(m: Uint8Array): Promise; +} + +interface RpcProviderDependency { + rpcProvider: RpcQueryProvider; +} + +interface SignerDependency { + signer: MessageSigner; +} + +interface RpcProviderQueryParams { + blockReference?: BlockReference; +} + +interface RpcProviderTransactionParams extends RpcProviderQueryParams { + receiver: string; + sender: string; +} + +export interface ViewBaseParams extends Dependent, RpcProviderQueryParams { +} + +export interface ViewAccountParams extends ViewBaseParams { + account: string; +} + +export interface ViewParams extends ViewAccountParams { + method: string; + args?: T; +} + +export interface ViewContractStateParams extends ViewAccountParams { + prefix: string | Uint8Array; +} + +export interface ViewAccessKeyParams extends ViewAccountParams { + publicKey: string; +} + +export interface SignAndSendTransactionDependency extends Dependent {} + +export interface SignAndSendNonceParams extends SignAndSendTransactionDependency { + nonce?: bigint; +} + +export interface SignAndSendComposerParams extends SignAndSendNonceParams, RpcProviderQueryParams { + composer: TransactionComposer; +} + +export interface SignAndSendTransactionParams extends SignAndSendTransactionDependency { + transaction: Transaction; +} + +export interface CallParams extends SignAndSendNonceParams, RpcProviderTransactionParams { + method: string; + args?: T; + deposit?: bigint; + gas?: bigint; +} + +export interface SendParams extends SignAndSendNonceParams, RpcProviderTransactionParams { + amount: bigint; +} + +export interface SignTransactionParams extends Dependent { + transaction: Transaction; +} diff --git a/packages/client/src/provider.ts b/packages/client/src/provider.ts new file mode 100644 index 0000000000..649f8e67a9 --- /dev/null +++ b/packages/client/src/provider.ts @@ -0,0 +1,17 @@ +import type { Finality } from '@near-js/types'; + +import type { ViewBaseParams } from './interfaces'; + +interface DefaultFinality { + defaultFinality?: Finality; +} + +export function getBlock({ blockReference, defaultFinality, deps: { rpcProvider } }: ViewBaseParams & DefaultFinality) { + if (!blockReference && !defaultFinality) { + return Promise.resolve(null); + } + + return rpcProvider.block( + blockReference || { finality: defaultFinality! } + ); +} diff --git a/packages/client/src/transactions/common.ts b/packages/client/src/transactions/common.ts new file mode 100644 index 0000000000..25c800af88 --- /dev/null +++ b/packages/client/src/transactions/common.ts @@ -0,0 +1,61 @@ +import type { CallParams, SendParams, SignAndSendComposerParams } from '../interfaces'; +import { getBlock } from '../provider'; +import { getNonce } from '../view'; +import { TransactionComposer } from './composer'; +import { signAndSendTransaction } from './sign_and_send'; + +async function getSignerNonce({ account, blockReference, deps: { rpcProvider, signer } }) { + return getNonce({ + account, + publicKey: (await signer.getPublicKey()).toString(), + blockReference, + deps: { rpcProvider }, + }); +} + +export async function signAndSendComposer({ composer, nonce, blockReference, deps }: SignAndSendComposerParams) { + const { rpcProvider, signer } = deps; + const block = await getBlock({ blockReference, deps: { rpcProvider } }); + + let signerNonce = nonce; + if (!signerNonce) { + signerNonce = await getSignerNonce({ account: composer.sender, blockReference, deps }); + signerNonce += 1n; + } + + const transaction = composer.toTransaction({ + nonce: signerNonce, + publicKey: await signer.getPublicKey(), + blockHeader: block?.header, + }); + + return signAndSendTransaction({ transaction, deps }); +} + +export async function call({ sender, receiver, method, args, gas, deposit, nonce, blockReference, deps }: CallParams) { + const composer = TransactionComposer.init({ sender, receiver }) + .functionCall(method, args, gas, deposit); + + const { result } = await signAndSendComposer({ + composer, + nonce, + blockReference, + deps, + }); + + return result; +} + +export async function send({ sender, receiver, amount, nonce, blockReference, deps }: SendParams) { + const composer = TransactionComposer.init({ sender, receiver }) + .transfer(amount); + + const { result } = await signAndSendComposer({ + composer, + nonce, + blockReference, + deps, + }); + + return result; +} diff --git a/packages/client/src/transactions/composer.ts b/packages/client/src/transactions/composer.ts new file mode 100644 index 0000000000..2bd3688218 --- /dev/null +++ b/packages/client/src/transactions/composer.ts @@ -0,0 +1,121 @@ +import { PublicKey } from '@near-js/crypto'; +import { + Action, + actionCreators, + buildDelegateAction, + DelegateAction, + Signature, + Transaction, +} from '@near-js/transactions'; +import { baseDecode, DEFAULT_FUNCTION_CALL_GAS } from '@near-js/utils'; +import { DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL } from '../constants'; +import { BlockHeader } from '@near-js/types'; + +interface TransactionOptions { + blockHeader?: BlockHeader; + nonce?: bigint; + publicKey?: PublicKey; + receiver?: string; + sender?: string; +} + +export class TransactionComposer { + private actions: Action[] = []; + receiver: string | undefined; + sender: string | undefined; + blockHeader: BlockHeader | undefined; + nonce: bigint | undefined; + publicKey: PublicKey | undefined; + + static init(transaction: TransactionOptions) { + const composer = new TransactionComposer(); + composer.receiver = transaction.receiver; + composer.sender = transaction.sender; + composer.blockHeader = transaction.blockHeader; + composer.nonce = transaction.nonce; + composer.publicKey = transaction.publicKey; + + return composer; + } + + private buildTransactionObject(transaction?: TransactionOptions) { + const hash = (transaction?.blockHeader?.hash || this.blockHeader?.hash)!; + return { + actions: this.actions, + blockHash: baseDecode(hash), + nonce: (transaction?.nonce || this.nonce)!, + publicKey: (transaction?.publicKey || this.publicKey)!, + receiverId: (transaction?.receiver || this.receiver)!, + signerId: (transaction?.sender || this.sender)!, + }; + } + + toTransaction(transaction?: TransactionOptions): Transaction { + return new Transaction(this.buildTransactionObject(transaction)); + } + + toDelegateAction(transaction?: TransactionOptions): DelegateAction { + const { actions, nonce, publicKey, receiverId, signerId } = this.buildTransactionObject(transaction); + const blockHeader = transaction?.blockHeader || this.blockHeader; + + return buildDelegateAction({ + actions, + maxBlockHeight: BigInt(blockHeader!.height) + DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL, + nonce: BigInt(nonce) + BigInt(1), + publicKey, + receiverId, + senderId: signerId, + }); + } + + addFullAccessKey(publicKey: PublicKey) { + this.actions.push(actionCreators.addKey(publicKey, actionCreators.fullAccessKey())); + return this; + } + + addFunctionCallAccessKey(publicKey: PublicKey, contractId: string, methodNames: string[], allowance?: bigint) { + const accessKey = actionCreators.functionCallAccessKey(contractId, methodNames, allowance) + this.actions.push(actionCreators.addKey(publicKey, accessKey)); + return this; + } + + createAccount() { + this.actions.push(actionCreators.createAccount()); + return this; + } + + deleteAccount(beneficiaryId: string) { + this.actions.push(actionCreators.deleteAccount(beneficiaryId)); + return this; + } + + deleteKey(publicKey: PublicKey) { + this.actions.push(actionCreators.deleteKey(publicKey)); + return this; + } + + deployContract(code: Uint8Array) { + this.actions.push(actionCreators.deployContract(code)); + return this; + } + + functionCall(method: string, args: object, gas: bigint = DEFAULT_FUNCTION_CALL_GAS * BigInt(10), deposit = BigInt(0)) { + this.actions.push(actionCreators.functionCall(method, args, gas, deposit)); + return this; + } + + signedDelegate(delegateAction: DelegateAction, signature: Signature) { + this.actions.push(actionCreators.signedDelegate({ delegateAction, signature })); + return this; + } + + stake(stake: bigint, publicKey: PublicKey) { + this.actions.push(actionCreators.stake(stake, publicKey)); + return this; + } + + transfer(deposit: bigint) { + this.actions.push(actionCreators.transfer(deposit)); + return this; + } +} diff --git a/packages/client/src/transactions/index.ts b/packages/client/src/transactions/index.ts new file mode 100644 index 0000000000..962390c3d4 --- /dev/null +++ b/packages/client/src/transactions/index.ts @@ -0,0 +1,3 @@ +export * from './common'; +export * from './composer'; +export * from './sign_and_send'; diff --git a/packages/client/src/transactions/sign_and_send.ts b/packages/client/src/transactions/sign_and_send.ts new file mode 100644 index 0000000000..ea2c521ecf --- /dev/null +++ b/packages/client/src/transactions/sign_and_send.ts @@ -0,0 +1,30 @@ +import { Signature, SignedTransaction } from '@near-js/transactions'; +import { getTransactionLastResult } from '@near-js/utils'; +import { sha256 } from '@noble/hashes/sha256'; + +import type { SignTransactionParams, SignAndSendTransactionParams } from '../interfaces'; + +export async function signTransaction({ transaction, deps: { signer } }: SignTransactionParams) { + const encodedTx = transaction.encode(); + const signedTransaction = new SignedTransaction({ + transaction, + signature: new Signature({ + keyType: transaction.publicKey.keyType, + data: await signer.signMessage(encodedTx), + }), + }); + + return { + encodedTransactionHash: new Uint8Array(sha256(encodedTx)), + signedTransaction, + }; +} + +export async function signAndSendTransaction({ transaction, deps: { rpcProvider, signer } }: SignAndSendTransactionParams) { + const { signedTransaction } = await signTransaction({ transaction, deps: { signer } }); + const outcome = await rpcProvider.sendTransaction(signedTransaction); + return { + outcome, + result: getTransactionLastResult(outcome), + }; +} diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts new file mode 100644 index 0000000000..e3152e1b69 --- /dev/null +++ b/packages/client/src/view.ts @@ -0,0 +1,120 @@ +import type { AccountView, BlockReference, CodeResult, QueryResponseKind } from '@near-js/types'; + +import type { + RpcQueryProvider, + ViewAccessKeyParams, + ViewAccountParams, + ViewContractStateParams, + ViewParams, +} from './interfaces'; +import { AccessKeyList, AccessKeyView, ViewStateResult } from '@near-js/types'; + +const DEFAULT_VIEW_BLOCK_REFERENCE = { finality: 'optimistic' }; + +interface QueryParams { + account: string; + rpcProvider: RpcQueryProvider; + request: string; + args?: any; + blockReference?: BlockReference; +} + +export function query({ + rpcProvider, + account, + request, + args = {}, + blockReference, +}: QueryParams): Promise { + return rpcProvider.query({ + request_type: request, + account_id: account, + ...(blockReference ? blockReference : DEFAULT_VIEW_BLOCK_REFERENCE), + ...args, + }); +} + +export function callViewMethod({ account, method, args = {}, blockReference, deps: { rpcProvider } }: ViewParams) { + return query({ + request: 'call_function', + rpcProvider, + account, + args: { + args_base64: Buffer.isBuffer(args) ? args : Buffer.from(JSON.stringify(args)).toString('base64'), + method_name: method, + }, + blockReference, + }); +} + +export async function view({ account, method, args = {}, blockReference, deps }: ViewParams) { + const { result } = await callViewMethod({ account, method, args, blockReference, deps }); + const stringResult = Buffer.from(result).toString(); + try { + return JSON.parse(stringResult); + } catch { + return isNaN(+stringResult) ? stringResult : +stringResult; + } +} + +export function getAccessKey({ account, publicKey, blockReference, deps: { rpcProvider } }: ViewAccessKeyParams) { + return query({ + request: 'view_access_key', + rpcProvider, + account, + args: { + publicKey, + }, + blockReference, + }); +} + +export function getAccountState({ account, blockReference, deps: { rpcProvider } }: ViewAccountParams) { + return query({ + request: 'view_account', + rpcProvider, + account, + blockReference, + }); +} + +export function getAccessKeys({ account, blockReference, deps: { rpcProvider } }: ViewAccountParams) { + return query({ + request: 'view_access_key_list', + rpcProvider, + account, + blockReference, + }); +} + +export function getContractCode({ account, blockReference, deps: { rpcProvider } }: ViewAccountParams) { + return query({ + request: 'view_code', + rpcProvider, + account, + blockReference, + }); +} + +export function getContractState({ account, prefix, blockReference, deps: { rpcProvider }}: ViewContractStateParams) { + return query({ + request: 'view_state', + rpcProvider, + account, + args: { + prefix_base64: Buffer.from(prefix).toString('base64'), + }, + blockReference, + }); +} + +export async function getNonce({ account, publicKey, blockReference, deps: { rpcProvider } }: ViewAccessKeyParams) { + const { nonce } = await getAccessKey({ + account, + publicKey, + blockReference, + deps: { rpcProvider }, + }); + + return nonce; +} diff --git a/packages/client/tsconfig.cjs.json b/packages/client/tsconfig.cjs.json new file mode 100644 index 0000000000..83abd57c4f --- /dev/null +++ b/packages/client/tsconfig.cjs.json @@ -0,0 +1,10 @@ +{ + "extends": "tsconfig/cjs.json", + "compilerOptions": { + "outDir": "./lib/commonjs", + "lib": ["es2022", "dom"] + }, + "files": [ + "src/index.ts" + ] +} diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json new file mode 100644 index 0000000000..75b9dc47ae --- /dev/null +++ b/packages/client/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "tsconfig/esm.json", + "compilerOptions": { + "outDir": "./lib/esm", + }, + "files": [ + "src/index.ts" + ] +} \ No newline at end of file diff --git a/packages/client/typedoc.json b/packages/client/typedoc.json new file mode 100644 index 0000000000..61b70ef7b6 --- /dev/null +++ b/packages/client/typedoc.json @@ -0,0 +1,9 @@ +{ + "extends": [ + "../../typedoc.json" + ], + "entryPoints": [ + "src" + ], + "entryPointStrategy": "expand", +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f2be411fb0..9ff2436428 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -191,6 +191,43 @@ importers: packages/build: {} + packages/client: + dependencies: + '@near-js/crypto': + specifier: workspace:* + version: link:../crypto + '@near-js/keystores-node': + specifier: workspace:* + version: link:../keystores-node + '@near-js/providers': + specifier: workspace:* + version: link:../providers + '@near-js/signers': + specifier: workspace:* + version: link:../signers + '@near-js/transactions': + specifier: workspace:* + version: link:../transactions + '@near-js/types': + specifier: workspace:* + version: link:../types + '@near-js/utils': + specifier: workspace:* + version: link:../utils + '@noble/hashes': + specifier: 1.3.3 + version: 1.3.3 + devDependencies: + '@types/node': + specifier: 20.0.0 + version: 20.0.0 + build: + specifier: workspace:* + version: link:../build + typescript: + specifier: 5.4.5 + version: 5.4.5 + packages/cookbook: dependencies: '@near-js/accounts': From f83ea5df82bb82ffb44c6b87e7b41474026ea00b Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Fri, 9 Aug 2024 15:28:22 -0700 Subject: [PATCH 02/55] refactor: organize --- packages/client/package.json | 1 + packages/client/src/constants.ts | 10 ++ packages/client/src/index.ts | 2 + packages/client/src/interfaces.ts | 92 ------------------- .../client/src/interfaces/dependencies.ts | 31 +++++++ packages/client/src/interfaces/index.ts | 3 + .../client/src/interfaces/transactions.ts | 56 +++++++++++ packages/client/src/interfaces/view.ts | 27 ++++++ packages/client/src/provider.ts | 17 ---- packages/client/src/providers.ts | 54 +++++++++++ packages/client/src/signers.ts | 34 +++++++ packages/client/src/transactions/actions.ts | 55 +++++++++++ packages/client/src/transactions/common.ts | 61 ------------ packages/client/src/transactions/index.ts | 2 +- .../client/src/transactions/sign_and_send.ts | 32 +++++++ packages/client/src/view.ts | 61 ++++++------ 16 files changed, 341 insertions(+), 197 deletions(-) delete mode 100644 packages/client/src/interfaces.ts create mode 100644 packages/client/src/interfaces/dependencies.ts create mode 100644 packages/client/src/interfaces/index.ts create mode 100644 packages/client/src/interfaces/transactions.ts create mode 100644 packages/client/src/interfaces/view.ts delete mode 100644 packages/client/src/provider.ts create mode 100644 packages/client/src/providers.ts create mode 100644 packages/client/src/signers.ts create mode 100644 packages/client/src/transactions/actions.ts delete mode 100644 packages/client/src/transactions/common.ts diff --git a/packages/client/package.json b/packages/client/package.json index 9a4ca7104c..c52be9a322 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "@near-js/crypto": "workspace:*", + "@near-js/keystores": "workspace:*", "@near-js/keystores-node": "workspace:*", "@near-js/providers": "workspace:*", "@near-js/signers": "workspace:*", diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts index 8d8f06a4fd..6dc8ea34db 100644 --- a/packages/client/src/constants.ts +++ b/packages/client/src/constants.ts @@ -3,3 +3,13 @@ export const MAINNET_RPC_URL = 'https://rpc.mainnet.near.org'; export const TESTNET_RPC_URL = 'https://rpc.testnet.near.org'; export const DEFAULT_FILESYSTEM_KEYSTORE_PATH = '.near-credentials'; export const DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL = BigInt(100); + +export const PAGODA_RPC_ENDPOINTS_MAINNET = [ + 'https://rpc.near.org', + 'https://rpc.mainnet.pagoda.co', +]; + +export const PAGODA_RPC_ENDPOINTS_TESTNET = [ + 'https://rpc.testnet.near.org', + 'https://rpc.testnet.pagoda.co', +]; diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 176b9aed89..a072414f4e 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1,3 +1,5 @@ export * from './interfaces'; +export * from './providers'; +export * from './signers'; export * from './transactions'; export * from './view'; diff --git a/packages/client/src/interfaces.ts b/packages/client/src/interfaces.ts deleted file mode 100644 index a76ea0a298..0000000000 --- a/packages/client/src/interfaces.ts +++ /dev/null @@ -1,92 +0,0 @@ -import type { - BlockReference, - BlockResult, - FinalExecutionOutcome, - QueryResponseKind, -} from '@near-js/types'; -import type { SignedTransaction } from '@near-js/transactions'; -import type { PublicKey } from '@near-js/crypto'; -import type { Transaction } from '@near-js/transactions'; - -import type { TransactionComposer } from './transactions'; - -interface Dependent { - deps: T; -} - -export interface RpcQueryProvider { - block(block: BlockReference): Promise; - query(...args: any[]): Promise; - sendTransaction(transaction: SignedTransaction): Promise; -} - -export interface MessageSigner { - getPublicKey(): Promise; - signMessage(m: Uint8Array): Promise; -} - -interface RpcProviderDependency { - rpcProvider: RpcQueryProvider; -} - -interface SignerDependency { - signer: MessageSigner; -} - -interface RpcProviderQueryParams { - blockReference?: BlockReference; -} - -interface RpcProviderTransactionParams extends RpcProviderQueryParams { - receiver: string; - sender: string; -} - -export interface ViewBaseParams extends Dependent, RpcProviderQueryParams { -} - -export interface ViewAccountParams extends ViewBaseParams { - account: string; -} - -export interface ViewParams extends ViewAccountParams { - method: string; - args?: T; -} - -export interface ViewContractStateParams extends ViewAccountParams { - prefix: string | Uint8Array; -} - -export interface ViewAccessKeyParams extends ViewAccountParams { - publicKey: string; -} - -export interface SignAndSendTransactionDependency extends Dependent {} - -export interface SignAndSendNonceParams extends SignAndSendTransactionDependency { - nonce?: bigint; -} - -export interface SignAndSendComposerParams extends SignAndSendNonceParams, RpcProviderQueryParams { - composer: TransactionComposer; -} - -export interface SignAndSendTransactionParams extends SignAndSendTransactionDependency { - transaction: Transaction; -} - -export interface CallParams extends SignAndSendNonceParams, RpcProviderTransactionParams { - method: string; - args?: T; - deposit?: bigint; - gas?: bigint; -} - -export interface SendParams extends SignAndSendNonceParams, RpcProviderTransactionParams { - amount: bigint; -} - -export interface SignTransactionParams extends Dependent { - transaction: Transaction; -} diff --git a/packages/client/src/interfaces/dependencies.ts b/packages/client/src/interfaces/dependencies.ts new file mode 100644 index 0000000000..dd408dfbce --- /dev/null +++ b/packages/client/src/interfaces/dependencies.ts @@ -0,0 +1,31 @@ +import type { + BlockReference, + BlockResult, + FinalExecutionOutcome, + QueryResponseKind, +} from '@near-js/types'; +import type { SignedTransaction } from '@near-js/transactions'; +import type { PublicKey } from '@near-js/crypto'; + +export interface Dependent { + deps: T; +} + +export interface RpcQueryProvider { + block(block: BlockReference): Promise; + query(...args: any[]): Promise; + sendTransaction(transaction: SignedTransaction): Promise; +} + +export interface MessageSigner { + getPublicKey(): Promise; + signMessage(m: Uint8Array): Promise; +} + +export interface RpcProviderDependency { + rpcProvider: RpcQueryProvider; +} + +export interface SignerDependency { + signer: MessageSigner; +} diff --git a/packages/client/src/interfaces/index.ts b/packages/client/src/interfaces/index.ts new file mode 100644 index 0000000000..36d525630a --- /dev/null +++ b/packages/client/src/interfaces/index.ts @@ -0,0 +1,3 @@ +export * from './dependencies'; +export * from './transactions'; +export * from './view'; diff --git a/packages/client/src/interfaces/transactions.ts b/packages/client/src/interfaces/transactions.ts new file mode 100644 index 0000000000..ded3202b5c --- /dev/null +++ b/packages/client/src/interfaces/transactions.ts @@ -0,0 +1,56 @@ +import type { Transaction } from '@near-js/transactions'; + +import type { TransactionComposer } from '../transactions'; +import type { Dependent, RpcProviderDependency, SignerDependency } from './dependencies'; +import type { RpcProviderQueryParams } from './view'; +import { PublicKey } from '@near-js/crypto'; + +export interface SignAndSendTransactionDependency extends Dependent {} + +export interface SignAndSendNonceParams extends SignAndSendTransactionDependency, RpcProviderQueryParams { + nonce?: bigint; +} + +export interface ExternalActionTransaction extends SignAndSendNonceParams { + receiver: string; + sender: string; +} + +export interface SelfActionTransaction extends SignAndSendNonceParams { + account: string; +} + +export interface SignAndSendComposerParams extends SignAndSendNonceParams, RpcProviderQueryParams { + composer: TransactionComposer; +} + +export interface SignAndSendTransactionParams extends SignAndSendTransactionDependency { + transaction: Transaction; +} + +export interface FunctionCallParams extends ExternalActionTransaction { + method: string; + args?: T; + deposit?: bigint; + gas?: bigint; +} + +export interface TransferParams extends ExternalActionTransaction { + amount: bigint; +} + +interface AddAccessKeyParams extends SelfActionTransaction { + publicKey: PublicKey; +} + +export interface AddFullAccessKeyParams extends AddAccessKeyParams {} + +export interface AddFunctionCallAccessKeyParams extends AddAccessKeyParams { + contract: string; + methodNames: string[]; + allowance?: bigint; +} + +export interface SignTransactionParams extends Dependent { + transaction: Transaction; +} diff --git a/packages/client/src/interfaces/view.ts b/packages/client/src/interfaces/view.ts new file mode 100644 index 0000000000..3c147ce308 --- /dev/null +++ b/packages/client/src/interfaces/view.ts @@ -0,0 +1,27 @@ +import type { BlockReference } from '@near-js/types'; + +import { Dependent, RpcProviderDependency } from './dependencies'; + +export interface RpcProviderQueryParams { + blockReference?: BlockReference; +} + +export interface ViewBaseParams extends Dependent, RpcProviderQueryParams { +} + +export interface ViewAccountParams extends ViewBaseParams { + account: string; +} + +export interface ViewParams extends ViewAccountParams { + method: string; + args?: T; +} + +export interface ViewContractStateParams extends ViewAccountParams { + prefix: string | Uint8Array; +} + +export interface ViewAccessKeyParams extends ViewAccountParams { + publicKey: string; +} diff --git a/packages/client/src/provider.ts b/packages/client/src/provider.ts deleted file mode 100644 index 649f8e67a9..0000000000 --- a/packages/client/src/provider.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Finality } from '@near-js/types'; - -import type { ViewBaseParams } from './interfaces'; - -interface DefaultFinality { - defaultFinality?: Finality; -} - -export function getBlock({ blockReference, defaultFinality, deps: { rpcProvider } }: ViewBaseParams & DefaultFinality) { - if (!blockReference && !defaultFinality) { - return Promise.resolve(null); - } - - return rpcProvider.block( - blockReference || { finality: defaultFinality! } - ); -} diff --git a/packages/client/src/providers.ts b/packages/client/src/providers.ts new file mode 100644 index 0000000000..a9de793ebf --- /dev/null +++ b/packages/client/src/providers.ts @@ -0,0 +1,54 @@ +import { FailoverRpcProvider, JsonRpcProvider } from '@near-js/providers'; +import type { Finality } from '@near-js/types'; + +import type { ViewBaseParams } from './interfaces'; +import { PAGODA_RPC_ENDPOINTS_MAINNET, PAGODA_RPC_ENDPOINTS_TESTNET } from './constants'; + +interface DefaultFinality { + defaultFinality?: Finality; +} + +export function getBlock({ blockReference, defaultFinality, deps: { rpcProvider } }: ViewBaseParams & DefaultFinality) { + if (!blockReference && !defaultFinality) { + return Promise.resolve(null); + } + + return rpcProvider.block( + blockReference || { finality: defaultFinality! } + ); +} + +export function getEndpointsByNetwork(network: string) { + switch (network) { + case 'testnet': + return PAGODA_RPC_ENDPOINTS_TESTNET; + case 'mainnet': + return PAGODA_RPC_ENDPOINTS_MAINNET; + default: + return null; + } +} + +export function getFailoverRpcProvider(urls: string[]) { + if (!urls) { + throw new Error('at least one RPC endpoint URL required'); + } + + return new FailoverRpcProvider(urls.map((url) => new JsonRpcProvider({ url }))); +} + +export function getProviderByNetwork(network: string) { + return getFailoverRpcProvider(getEndpointsByNetwork(network)); +} + +export function getProviderByEndpoints(...urls: string[]) { + return getFailoverRpcProvider(urls); +} + +export function getTestnetRpcProvider() { + return getProviderByNetwork('testnet'); +} + +export function getMainnetRpcProvider() { + return getProviderByNetwork('mainnet'); +} diff --git a/packages/client/src/signers.ts b/packages/client/src/signers.ts new file mode 100644 index 0000000000..d5fd94888e --- /dev/null +++ b/packages/client/src/signers.ts @@ -0,0 +1,34 @@ +import { KeyPair, KeyPairString, PublicKey } from '@near-js/crypto'; +import { KeyStore } from '@near-js/keystores'; +import { InMemorySigner } from '@near-js/signers'; + +import type { MessageSigner } from './interfaces'; + +export function getSignerFromKeyPair(keyPair: KeyPair): MessageSigner { + return { + getPublicKey(): Promise { + return Promise.resolve(keyPair.getPublicKey()); + }, + async signMessage(m: Uint8Array): Promise { + return keyPair.sign(m).signature; + } + } +} + +export function getSignerFromPrivateKey(privateKey: KeyPairString): MessageSigner { + return getSignerFromKeyPair(KeyPair.fromString(privateKey)) +} + +export async function getSignerFromKeyStore(accountId: string, network: string, keyStore: KeyStore): Promise { + const signer = new InMemorySigner(keyStore); + + return { + getPublicKey(): Promise { + return signer.getPublicKey(accountId, network); + }, + async signMessage(m: Uint8Array): Promise { + const { signature } = await signer.signMessage(m, accountId, network); + return signature; + } + } +} diff --git a/packages/client/src/transactions/actions.ts b/packages/client/src/transactions/actions.ts new file mode 100644 index 0000000000..78804dd4d7 --- /dev/null +++ b/packages/client/src/transactions/actions.ts @@ -0,0 +1,55 @@ +import type { AddFullAccessKeyParams, FunctionCallParams, TransferParams } from '../interfaces'; +import { TransactionComposer } from './composer'; +import { signAndSendFromComposer } from './sign_and_send'; +import { AddFunctionCallAccessKeyParams, SignAndSendNonceParams } from '../interfaces'; + +async function getComposerResult({ composer, nonce, blockReference, deps }: { composer: TransactionComposer } & SignAndSendNonceParams) { + const { result } = await signAndSendFromComposer({ + composer, + nonce, + blockReference, + deps, + }); + + return result; +} + +export async function functionCall({ sender, receiver, method, args, gas, deposit, nonce, blockReference, deps }: FunctionCallParams) { + return getComposerResult({ + composer: TransactionComposer.init({ sender, receiver }) + .functionCall(method, args, gas, deposit), + nonce, + blockReference, + deps, + }); +} + +export async function transfer({ sender, receiver, amount, nonce, blockReference, deps }: TransferParams) { + return getComposerResult({ + composer: TransactionComposer.init({ sender, receiver }) + .transfer(amount), + nonce, + blockReference, + deps, + }); +} + +export async function addFullAccessKey({ account, publicKey, nonce, blockReference, deps }: AddFullAccessKeyParams) { + return getComposerResult({ + composer: TransactionComposer.init({ sender: account, receiver: account }) + .addFullAccessKey(publicKey), + nonce, + blockReference, + deps, + }); +} + +export async function addFunctionCallAccessKey({ account, publicKey, contract, methodNames, allowance, nonce, blockReference, deps }: AddFunctionCallAccessKeyParams) { + return getComposerResult({ + composer: TransactionComposer.init({ sender: account, receiver: account }) + .addFunctionCallAccessKey(publicKey, contract, methodNames, allowance), + nonce, + blockReference, + deps, + }); +} diff --git a/packages/client/src/transactions/common.ts b/packages/client/src/transactions/common.ts deleted file mode 100644 index 25c800af88..0000000000 --- a/packages/client/src/transactions/common.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { CallParams, SendParams, SignAndSendComposerParams } from '../interfaces'; -import { getBlock } from '../provider'; -import { getNonce } from '../view'; -import { TransactionComposer } from './composer'; -import { signAndSendTransaction } from './sign_and_send'; - -async function getSignerNonce({ account, blockReference, deps: { rpcProvider, signer } }) { - return getNonce({ - account, - publicKey: (await signer.getPublicKey()).toString(), - blockReference, - deps: { rpcProvider }, - }); -} - -export async function signAndSendComposer({ composer, nonce, blockReference, deps }: SignAndSendComposerParams) { - const { rpcProvider, signer } = deps; - const block = await getBlock({ blockReference, deps: { rpcProvider } }); - - let signerNonce = nonce; - if (!signerNonce) { - signerNonce = await getSignerNonce({ account: composer.sender, blockReference, deps }); - signerNonce += 1n; - } - - const transaction = composer.toTransaction({ - nonce: signerNonce, - publicKey: await signer.getPublicKey(), - blockHeader: block?.header, - }); - - return signAndSendTransaction({ transaction, deps }); -} - -export async function call({ sender, receiver, method, args, gas, deposit, nonce, blockReference, deps }: CallParams) { - const composer = TransactionComposer.init({ sender, receiver }) - .functionCall(method, args, gas, deposit); - - const { result } = await signAndSendComposer({ - composer, - nonce, - blockReference, - deps, - }); - - return result; -} - -export async function send({ sender, receiver, amount, nonce, blockReference, deps }: SendParams) { - const composer = TransactionComposer.init({ sender, receiver }) - .transfer(amount); - - const { result } = await signAndSendComposer({ - composer, - nonce, - blockReference, - deps, - }); - - return result; -} diff --git a/packages/client/src/transactions/index.ts b/packages/client/src/transactions/index.ts index 962390c3d4..72224e48a2 100644 --- a/packages/client/src/transactions/index.ts +++ b/packages/client/src/transactions/index.ts @@ -1,3 +1,3 @@ -export * from './common'; +export * from './actions'; export * from './composer'; export * from './sign_and_send'; diff --git a/packages/client/src/transactions/sign_and_send.ts b/packages/client/src/transactions/sign_and_send.ts index ea2c521ecf..cfc7f9f05d 100644 --- a/packages/client/src/transactions/sign_and_send.ts +++ b/packages/client/src/transactions/sign_and_send.ts @@ -3,6 +3,9 @@ import { getTransactionLastResult } from '@near-js/utils'; import { sha256 } from '@noble/hashes/sha256'; import type { SignTransactionParams, SignAndSendTransactionParams } from '../interfaces'; +import { getBlock } from '../providers'; +import { getNonce } from '../view'; +import { SignAndSendComposerParams } from '../interfaces'; export async function signTransaction({ transaction, deps: { signer } }: SignTransactionParams) { const encodedTx = transaction.encode(); @@ -28,3 +31,32 @@ export async function signAndSendTransaction({ transaction, deps: { rpcProvider, result: getTransactionLastResult(outcome), }; } + +export async function getSignerNonce({ account, blockReference, deps: { rpcProvider, signer } }) { + return getNonce({ + account, + publicKey: (await signer.getPublicKey()).toString(), + blockReference, + deps: { rpcProvider }, + }); +} + +// this might be more natural as a method on TransactionComposer but would be a major increase in scope +export async function signAndSendFromComposer({ composer, nonce, blockReference, deps }: SignAndSendComposerParams) { + const { rpcProvider, signer } = deps; + const block = await getBlock({ blockReference, deps: { rpcProvider } }); + + let signerNonce = nonce; + if (!signerNonce) { + signerNonce = await getSignerNonce({ account: composer.sender, blockReference, deps }); + signerNonce += 1n; + } + + const transaction = composer.toTransaction({ + nonce: signerNonce, + publicKey: await signer.getPublicKey(), + blockHeader: block?.header, + }); + + return signAndSendTransaction({ transaction, deps }); +} diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index e3152e1b69..34236a53a4 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -1,7 +1,9 @@ -import type { AccountView, BlockReference, CodeResult, QueryResponseKind } from '@near-js/types'; +import type { AccountView, CodeResult, QueryResponseKind } from '@near-js/types'; import type { - RpcQueryProvider, + Dependent, + RpcProviderDependency, + RpcProviderQueryParams, ViewAccessKeyParams, ViewAccountParams, ViewContractStateParams, @@ -11,20 +13,27 @@ import { AccessKeyList, AccessKeyView, ViewStateResult } from '@near-js/types'; const DEFAULT_VIEW_BLOCK_REFERENCE = { finality: 'optimistic' }; -interface QueryParams { +enum RequestType { + CallFunction = 'call_function', + ViewAccessKey = 'view_access_key', + ViewAccessKeyList = 'view_access_key_list', + ViewAccount = 'view_account', + ViewCode = 'view_code', + ViewState = 'view_state', +} + +interface QueryParams extends Dependent, RpcProviderQueryParams { account: string; - rpcProvider: RpcQueryProvider; request: string; args?: any; - blockReference?: BlockReference; } export function query({ - rpcProvider, account, request, args = {}, blockReference, + deps: { rpcProvider }, }: QueryParams): Promise { return rpcProvider.query({ request_type: request, @@ -34,16 +43,16 @@ export function query({ }); } -export function callViewMethod({ account, method, args = {}, blockReference, deps: { rpcProvider } }: ViewParams) { +export function callViewMethod({ account, method, args = {}, blockReference, deps }: ViewParams) { return query({ - request: 'call_function', - rpcProvider, + request: RequestType.CallFunction, account, args: { args_base64: Buffer.isBuffer(args) ? args : Buffer.from(JSON.stringify(args)).toString('base64'), method_name: method, }, blockReference, + deps, }); } @@ -57,63 +66,63 @@ export async function view({ account, method, args = {}, blockReference, deps }: } } -export function getAccessKey({ account, publicKey, blockReference, deps: { rpcProvider } }: ViewAccessKeyParams) { +export function getAccessKey({ account, publicKey, blockReference, deps }: ViewAccessKeyParams) { return query({ - request: 'view_access_key', - rpcProvider, + request: RequestType.ViewAccessKey, account, args: { publicKey, }, blockReference, + deps, }); } -export function getAccountState({ account, blockReference, deps: { rpcProvider } }: ViewAccountParams) { +export function getAccountState({ account, blockReference, deps }: ViewAccountParams) { return query({ - request: 'view_account', - rpcProvider, + request: RequestType.ViewAccount, account, blockReference, + deps, }); } -export function getAccessKeys({ account, blockReference, deps: { rpcProvider } }: ViewAccountParams) { +export function getAccessKeys({ account, blockReference, deps }: ViewAccountParams) { return query({ - request: 'view_access_key_list', - rpcProvider, + request: RequestType.ViewAccessKeyList, account, blockReference, + deps, }); } -export function getContractCode({ account, blockReference, deps: { rpcProvider } }: ViewAccountParams) { +export function getContractCode({ account, blockReference, deps }: ViewAccountParams) { return query({ - request: 'view_code', - rpcProvider, + request: RequestType.ViewCode, account, blockReference, + deps, }); } -export function getContractState({ account, prefix, blockReference, deps: { rpcProvider }}: ViewContractStateParams) { +export function getContractState({ account, prefix, blockReference, deps }: ViewContractStateParams) { return query({ - request: 'view_state', - rpcProvider, + request: RequestType.ViewState, account, args: { prefix_base64: Buffer.from(prefix).toString('base64'), }, blockReference, + deps, }); } -export async function getNonce({ account, publicKey, blockReference, deps: { rpcProvider } }: ViewAccessKeyParams) { +export async function getNonce({ account, publicKey, blockReference, deps }: ViewAccessKeyParams) { const { nonce } = await getAccessKey({ account, publicKey, blockReference, - deps: { rpcProvider }, + deps, }); return nonce; From 22fcef7f39776690abd7b079cfb19bf2ff20580f Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 28 Aug 2024 15:08:02 -0700 Subject: [PATCH 03/55] test: test/lint commands --- packages/client/package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/client/package.json b/packages/client/package.json index c52be9a322..d11952e7f7 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -8,9 +8,8 @@ "build": "pnpm compile:esm && pnpm compile:cjs", "compile:esm": "tsc -p tsconfig.json", "compile:cjs": "tsc -p tsconfig.cjs.json && cjsify ./lib/commonjs", - "lint": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc", - "lint:fix": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc --fix", - "test": "jest" + "lint": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc --no-error-on-unmatched-pattern", + "lint:fix": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc --fix --no-error-on-unmatched-pattern" }, "dependencies": { "@near-js/crypto": "workspace:*", From 9c7464973005afd2aa6bd68cc7e7c1c310aa559c Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 29 Aug 2024 12:35:36 -0700 Subject: [PATCH 04/55] feat: add curve type, reorganize types --- packages/crypto/src/constants.ts | 6 ++++++ packages/crypto/src/index.ts | 4 ++-- packages/crypto/src/key_pair.ts | 5 ++--- packages/crypto/src/key_pair_base.ts | 2 +- packages/crypto/src/key_pair_ed25519.ts | 3 +-- packages/crypto/src/key_pair_secp256k1.ts | 11 ++++++----- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/packages/crypto/src/constants.ts b/packages/crypto/src/constants.ts index d028d3523f..17cec790e1 100644 --- a/packages/crypto/src/constants.ts +++ b/packages/crypto/src/constants.ts @@ -9,3 +9,9 @@ export const KeySize = { ED25519_PUBLIC_KEY: 32, SECP256k1_PUBLIC_KEY: 64, }; + +export type CurveType = + 'ed25519' | 'ED25519' + | 'secp256k1' | 'SECP256K1'; + +export type KeyPairString = `ed25519:${string}` | `secp256k1:${string}`; diff --git a/packages/crypto/src/index.ts b/packages/crypto/src/index.ts index 45e13a274e..4a3af364f8 100644 --- a/packages/crypto/src/index.ts +++ b/packages/crypto/src/index.ts @@ -1,5 +1,5 @@ -export { KeyType } from './constants'; -export { KeyPair, KeyPairString } from './key_pair'; +export { CurveType, KeyPairString, KeyType } from './constants'; +export { KeyPair } from './key_pair'; export { Signature } from './key_pair_base'; export { KeyPairEd25519 } from './key_pair_ed25519'; export { KeyPairSecp256k1 } from './key_pair_secp256k1'; diff --git a/packages/crypto/src/key_pair.ts b/packages/crypto/src/key_pair.ts index 28273b2e71..c8565fee32 100644 --- a/packages/crypto/src/key_pair.ts +++ b/packages/crypto/src/key_pair.ts @@ -1,15 +1,14 @@ +import { CurveType, KeyPairString } from './constants'; import { KeyPairBase } from './key_pair_base'; import { KeyPairEd25519 } from './key_pair_ed25519'; import { KeyPairSecp256k1 } from './key_pair_secp256k1'; -export type KeyPairString = `ed25519:${string}` | `secp256k1:${string}`; - export abstract class KeyPair extends KeyPairBase { /** * @param curve Name of elliptical curve, case-insensitive * @returns Random KeyPair based on the curve */ - static fromRandom(curve: 'ed25519' | 'secp256k1'): KeyPair { + static fromRandom(curve: CurveType): KeyPair { switch (curve.toUpperCase()) { case 'ED25519': return KeyPairEd25519.fromRandom(); case 'SECP256K1': return KeyPairSecp256k1.fromRandom(); diff --git a/packages/crypto/src/key_pair_base.ts b/packages/crypto/src/key_pair_base.ts index f4939d8274..73aa796163 100644 --- a/packages/crypto/src/key_pair_base.ts +++ b/packages/crypto/src/key_pair_base.ts @@ -1,4 +1,4 @@ -import { KeyPairString } from './key_pair'; +import type { KeyPairString } from './constants'; import { PublicKey } from './public_key'; export interface Signature { diff --git a/packages/crypto/src/key_pair_ed25519.ts b/packages/crypto/src/key_pair_ed25519.ts index 2aabb1d7e6..e676710401 100644 --- a/packages/crypto/src/key_pair_ed25519.ts +++ b/packages/crypto/src/key_pair_ed25519.ts @@ -2,10 +2,9 @@ import { baseEncode, baseDecode } from '@near-js/utils'; import { ed25519 } from '@noble/curves/ed25519'; import randombytes from 'randombytes'; -import { KeySize, KeyType } from './constants'; +import { KeyPairString, KeySize, KeyType } from './constants'; import { KeyPairBase, Signature } from './key_pair_base'; import { PublicKey } from './public_key'; -import { KeyPairString } from './key_pair'; /** * This class provides key pair functionality for Ed25519 curve: diff --git a/packages/crypto/src/key_pair_secp256k1.ts b/packages/crypto/src/key_pair_secp256k1.ts index 2983f5cf08..07ee6d592e 100644 --- a/packages/crypto/src/key_pair_secp256k1.ts +++ b/packages/crypto/src/key_pair_secp256k1.ts @@ -1,10 +1,11 @@ -import { KeySize, KeyType } from './constants'; +import { baseDecode, baseEncode } from '@near-js/utils'; +import randombytes from 'randombytes'; +import secp256k1 from 'secp256k1'; + +import { KeyPairString, KeySize, KeyType } from './constants'; import { KeyPairBase, Signature } from './key_pair_base'; import { PublicKey } from './public_key'; -import secp256k1 from 'secp256k1'; -import randombytes from 'randombytes'; -import { KeyPairString } from './key_pair'; -import { baseDecode, baseEncode } from '@near-js/utils'; + /** * This class provides key pair functionality for secp256k1 curve: * generating key pairs, encoding key pairs, signing and verifying. From d6b6955f29e39d025e4cfafc01d41e82aed5beda Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 29 Aug 2024 12:36:42 -0700 Subject: [PATCH 05/55] fix: contract methods --- packages/client/src/view.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index 34236a53a4..d856479d61 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -98,15 +98,21 @@ export function getAccessKeys({ account, blockReference, deps }: ViewAccountPara export function getContractCode({ account, blockReference, deps }: ViewAccountParams) { return query({ +export async function getContractCode({ account, blockReference, deps }: ViewAccountParams) { + const { code_base64, hash } = await query({ request: RequestType.ViewCode, account, blockReference, deps, }); + + return { code: Buffer.from(code_base64, 'base64').toString(), code_base64, hash }; } export function getContractState({ account, prefix, blockReference, deps }: ViewContractStateParams) { return query({ +export async function getContractState({ account, prefix, blockReference, deps }: ViewContractStateParams) { + const { values } = await query({ request: RequestType.ViewState, account, args: { @@ -115,6 +121,11 @@ export function getContractState({ account, prefix, blockReference, deps }: View blockReference, deps, }); + + return values.reduce((state, { key, value }) => ({ + ...state, + [key]: value, + }), {}); } export async function getNonce({ account, publicKey, blockReference, deps }: ViewAccessKeyParams) { From 6a917071a81abad3428f706b9a7d9a12244d22c9 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 29 Aug 2024 12:37:58 -0700 Subject: [PATCH 06/55] docs: jsdocs --- packages/client/src/providers.ts | 29 ++++++++ packages/client/src/transactions/actions.ts | 44 +++++++++++- packages/client/src/view.ts | 75 +++++++++++++++++++-- 3 files changed, 140 insertions(+), 8 deletions(-) diff --git a/packages/client/src/providers.ts b/packages/client/src/providers.ts index a9de793ebf..de389727de 100644 --- a/packages/client/src/providers.ts +++ b/packages/client/src/providers.ts @@ -8,6 +8,13 @@ interface DefaultFinality { defaultFinality?: Finality; } +/** + * Query for a block + * @param account target account/contract being queried + * @param blockReference block ID/finality + * @param defaultFinality finality value to fall back on when blockReference is not specified + * @param rpcProvider RPC provider instance + */ export function getBlock({ blockReference, defaultFinality, deps: { rpcProvider } }: ViewBaseParams & DefaultFinality) { if (!blockReference && !defaultFinality) { return Promise.resolve(null); @@ -18,6 +25,10 @@ export function getBlock({ blockReference, defaultFinality, deps: { rpcProvider ); } +/** + * Get the set of public endpoints for the provided network + * @param network target blockchain network (e.g. `mainnet`) + */ export function getEndpointsByNetwork(network: string) { switch (network) { case 'testnet': @@ -29,6 +40,10 @@ export function getEndpointsByNetwork(network: string) { } } +/** + * Initialize a failover RPC provider capable of retrying requests against a set of endpoints + * @param urls RPC endpoint URLs + */ export function getFailoverRpcProvider(urls: string[]) { if (!urls) { throw new Error('at least one RPC endpoint URL required'); @@ -37,18 +52,32 @@ export function getFailoverRpcProvider(urls: string[]) { return new FailoverRpcProvider(urls.map((url) => new JsonRpcProvider({ url }))); } +/** + * Initialize a failover RPC provider for the given network + * @param network target blockchain network (e.g. `mainnet`) + */ export function getProviderByNetwork(network: string) { return getFailoverRpcProvider(getEndpointsByNetwork(network)); } +/** + * Initialize a failover RPC provider for a set of RPC endpoint URLs + * @param urls RPC endpoint URLs + */ export function getProviderByEndpoints(...urls: string[]) { return getFailoverRpcProvider(urls); } +/** + * Initialize a testnet RPC provider + */ export function getTestnetRpcProvider() { return getProviderByNetwork('testnet'); } +/** + * Initialize a mainnet RPC provider + */ export function getMainnetRpcProvider() { return getProviderByNetwork('mainnet'); } diff --git a/packages/client/src/transactions/actions.ts b/packages/client/src/transactions/actions.ts index 78804dd4d7..4350f5191d 100644 --- a/packages/client/src/transactions/actions.ts +++ b/packages/client/src/transactions/actions.ts @@ -3,6 +3,12 @@ import { TransactionComposer } from './composer'; import { signAndSendFromComposer } from './sign_and_send'; import { AddFunctionCallAccessKeyParams, SignAndSendNonceParams } from '../interfaces'; +/** + * Helper method to compose, sign, and send a transaction from a TransactionComposer instance + * @param composer TransactionComposer instance with (at minimum) sender, receiver, and actions set + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ async function getComposerResult({ composer, nonce, blockReference, deps }: { composer: TransactionComposer } & SignAndSendNonceParams) { const { result } = await signAndSendFromComposer({ composer, @@ -14,6 +20,17 @@ async function getComposerResult({ composer, nonce, blockReference, deps }: { co return result; } +/** + * Make a function call against a contract + * @param sender transaction signer + * @param receiver target account/contract + * @param method method to be invoked + * @param args method arguments + * @param gas attached gas + * @param deposit attached deposit in yN + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ export async function functionCall({ sender, receiver, method, args, gas, deposit, nonce, blockReference, deps }: FunctionCallParams) { return getComposerResult({ composer: TransactionComposer.init({ sender, receiver }) @@ -24,6 +41,14 @@ export async function functionCall({ sender, receiver, method, args, gas, deposi }); } +/** + * Send Near from one account to another + * @param sender account sending Near + * @param receiver account receiving Near + * @param amount Near to send in yN + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ export async function transfer({ sender, receiver, amount, nonce, blockReference, deps }: TransferParams) { return getComposerResult({ composer: TransactionComposer.init({ sender, receiver }) @@ -34,7 +59,14 @@ export async function transfer({ sender, receiver, amount, nonce, blockReference }); } -export async function addFullAccessKey({ account, publicKey, nonce, blockReference, deps }: AddFullAccessKeyParams) { +/** + * Add a full access key to an account + * @param account account to which the FAK is added + * @param publicKey public key string for the new FAK + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ +export async function addFullAccessKey({ account, publicKey, nonce, blockReference, deps }: ModifyAccessKeyParams) { return getComposerResult({ composer: TransactionComposer.init({ sender: account, receiver: account }) .addFullAccessKey(publicKey), @@ -44,6 +76,16 @@ export async function addFullAccessKey({ account, publicKey, nonce, blockReferen }); } +/** + * Add a function call access key to an account + * @param account account to which the access key is added + * @param publicKey public key string for the new access key + * @param contract contract on which methods may be invoked + * @param methodNames set of methods which may be invoked + * @param allowance maximum amount of Near which can be attached to a transaction signed with this key + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ export async function addFunctionCallAccessKey({ account, publicKey, contract, methodNames, allowance, nonce, blockReference, deps }: AddFunctionCallAccessKeyParams) { return getComposerResult({ composer: TransactionComposer.init({ sender: account, receiver: account }) diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index d856479d61..39cc3a0c46 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -9,7 +9,7 @@ import type { ViewContractStateParams, ViewParams, } from './interfaces'; -import { AccessKeyList, AccessKeyView, ViewStateResult } from '@near-js/types'; +import { AccessKeyList, AccessKeyView, ContractCodeView, ViewStateResult } from '@near-js/types'; const DEFAULT_VIEW_BLOCK_REFERENCE = { finality: 'optimistic' }; @@ -25,9 +25,17 @@ enum RequestType { interface QueryParams extends Dependent, RpcProviderQueryParams { account: string; request: string; - args?: any; + args?: object; } +/** + * Make a readonly request to an RPC endpoint targeting a specific account/contract + * @param account target account/contract being queried + * @param request type of request (e.g. `call_function`) + * @param args named arguments passed in the request body + * @param blockReference block ID/finality + * @param rpcProvider RPC provider instance + */ export function query({ account, request, @@ -43,6 +51,14 @@ export function query({ }); } +/** + * Call a view method on an account/contract, returning the raw response + * @param account target account/contract being queried + * @param method name of the method being invoked + * @param args named arguments passed in the request body + * @param blockReference block ID/finality + * @param deps readonly RPC dependencies + */ export function callViewMethod({ account, method, args = {}, blockReference, deps }: ViewParams) { return query({ request: RequestType.CallFunction, @@ -56,7 +72,17 @@ export function callViewMethod({ account, method, args = {}, blockReference, dep }); } -export async function view({ account, method, args = {}, blockReference, deps }: ViewParams) { +/** + * Call a view method on an account/contract, parsing the returned data + * NB if the data returned is a byte array, this method will convert it + * to string - use `await (viewRaw(...)).result` to get the buffer + * @param account target account/contract being queried + * @param method name of the method being invoked + * @param args named arguments passed in the request body + * @param blockReference block ID/finality + * @param deps readonly RPC dependencies + */ +export async function view({ account, method, args = {}, blockReference, deps }: ViewParams): Promise { const { result } = await callViewMethod({ account, method, args, blockReference, deps }); const stringResult = Buffer.from(result).toString(); try { @@ -66,6 +92,13 @@ export async function view({ account, method, args = {}, blockReference, deps }: } } +/** + * Get metadata for the specified access key + * @param account target account/contract being queried + * @param publicKey public key string to be queried + * @param blockReference block ID/finality + * @param deps readonly RPC dependencies + */ export function getAccessKey({ account, publicKey, blockReference, deps }: ViewAccessKeyParams) { return query({ request: RequestType.ViewAccessKey, @@ -78,6 +111,12 @@ export function getAccessKey({ account, publicKey, blockReference, deps }: ViewA }); } +/** + * Get account metadata (e.g. balance, storage) + * @param account target account/contract being queried + * @param blockReference block ID/finality + * @param deps readonly RPC dependencies + */ export function getAccountState({ account, blockReference, deps }: ViewAccountParams) { return query({ request: RequestType.ViewAccount, @@ -87,6 +126,12 @@ export function getAccountState({ account, blockReference, deps }: ViewAccountPa }); } +/** + * Get list of access keys for the specified account/contract + * @param account target account/contract being queried + * @param blockReference block ID/finality + * @param deps readonly RPC dependencies + */ export function getAccessKeys({ account, blockReference, deps }: ViewAccountParams) { return query({ request: RequestType.ViewAccessKeyList, @@ -96,8 +141,12 @@ export function getAccessKeys({ account, blockReference, deps }: ViewAccountPara }); } -export function getContractCode({ account, blockReference, deps }: ViewAccountParams) { - return query({ +/** + * Get the code for the contract deployed to the target account + * @param account target account/contract being queried + * @param blockReference block ID/finality + * @param deps readonly RPC dependencies + */ export async function getContractCode({ account, blockReference, deps }: ViewAccountParams) { const { code_base64, hash } = await query({ request: RequestType.ViewCode, @@ -109,8 +158,13 @@ export async function getContractCode({ account, blockReference, deps }: ViewAcc return { code: Buffer.from(code_base64, 'base64').toString(), code_base64, hash }; } -export function getContractState({ account, prefix, blockReference, deps }: ViewContractStateParams) { - return query({ +/** + * Get the state on the contract deployed to the target account in key-value pairs + * @param account target account/contract being queried + * @param prefix target prefix filter (empty string/buffer returns all keys) + * @param blockReference block ID/finality + * @param deps readonly RPC dependencies + */ export async function getContractState({ account, prefix, blockReference, deps }: ViewContractStateParams) { const { values } = await query({ request: RequestType.ViewState, @@ -128,6 +182,13 @@ export async function getContractState({ account, prefix, blockReference, deps } }), {}); } +/** + * Get the nonce for the specified access key + * @param account target account/contract being queried + * @param publicKey public key string to be queried + * @param blockReference block ID/finality + * @param deps readonly RPC dependencies + */ export async function getNonce({ account, publicKey, blockReference, deps }: ViewAccessKeyParams) { const { nonce } = await getAccessKey({ account, From 524d6dda0433b696117c589a21a6af3451977cdc Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 29 Aug 2024 12:38:39 -0700 Subject: [PATCH 07/55] feat: delete access key method --- .../client/src/interfaces/transactions.ts | 8 +++---- packages/client/src/transactions/actions.ts | 24 ++++++++++++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/packages/client/src/interfaces/transactions.ts b/packages/client/src/interfaces/transactions.ts index ded3202b5c..e9e62bc536 100644 --- a/packages/client/src/interfaces/transactions.ts +++ b/packages/client/src/interfaces/transactions.ts @@ -16,7 +16,7 @@ export interface ExternalActionTransaction extends SignAndSendNonceParams { sender: string; } -export interface SelfActionTransaction extends SignAndSendNonceParams { +export interface ReflexiveActionTransaction extends SignAndSendNonceParams { account: string; } @@ -39,11 +39,11 @@ export interface TransferParams extends ExternalActionTransaction { amount: bigint; } -interface AddAccessKeyParams extends SelfActionTransaction { - publicKey: PublicKey; +interface AddAccessKeyParams extends ReflexiveActionTransaction { + publicKey: string; } -export interface AddFullAccessKeyParams extends AddAccessKeyParams {} +export interface ModifyAccessKeyParams extends AddAccessKeyParams {} export interface AddFunctionCallAccessKeyParams extends AddAccessKeyParams { contract: string; diff --git a/packages/client/src/transactions/actions.ts b/packages/client/src/transactions/actions.ts index 4350f5191d..e52c0de3bf 100644 --- a/packages/client/src/transactions/actions.ts +++ b/packages/client/src/transactions/actions.ts @@ -1,7 +1,8 @@ -import type { AddFullAccessKeyParams, FunctionCallParams, TransferParams } from '../interfaces'; +import type { ModifyAccessKeyParams, FunctionCallParams, TransferParams } from '../interfaces'; import { TransactionComposer } from './composer'; import { signAndSendFromComposer } from './sign_and_send'; import { AddFunctionCallAccessKeyParams, SignAndSendNonceParams } from '../interfaces'; +import { PublicKey } from '@near-js/crypto'; /** * Helper method to compose, sign, and send a transaction from a TransactionComposer instance @@ -69,7 +70,7 @@ export async function transfer({ sender, receiver, amount, nonce, blockReference export async function addFullAccessKey({ account, publicKey, nonce, blockReference, deps }: ModifyAccessKeyParams) { return getComposerResult({ composer: TransactionComposer.init({ sender: account, receiver: account }) - .addFullAccessKey(publicKey), + .addFullAccessKey(PublicKey.from(publicKey)), nonce, blockReference, deps, @@ -89,7 +90,24 @@ export async function addFullAccessKey({ account, publicKey, nonce, blockReferen export async function addFunctionCallAccessKey({ account, publicKey, contract, methodNames, allowance, nonce, blockReference, deps }: AddFunctionCallAccessKeyParams) { return getComposerResult({ composer: TransactionComposer.init({ sender: account, receiver: account }) - .addFunctionCallAccessKey(publicKey, contract, methodNames, allowance), + .addFunctionCallAccessKey(PublicKey.from(publicKey), contract, methodNames, allowance), + nonce, + blockReference, + deps, + }); +} + +/** + * Remove the specified access key from an account + * @param account account from which the access key will be removed + * @param publicKey public key string of the access key to be removed + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ +export async function deleteAccessKey({ account, publicKey, nonce, blockReference, deps }: ModifyAccessKeyParams) { + return getComposerResult({ + composer: TransactionComposer.init({ sender: account, receiver: account }) + .deleteKey(PublicKey.from(publicKey)), nonce, blockReference, deps, From 26d7a176e4cd619cb56bd49526bd8f675b55b13a Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 29 Aug 2024 12:45:20 -0700 Subject: [PATCH 08/55] feat: remove nonce as parameter --- .../client/src/interfaces/transactions.ts | 11 ++++----- packages/client/src/transactions/actions.ts | 23 ++++++++----------- .../client/src/transactions/sign_and_send.ts | 10 +++----- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/packages/client/src/interfaces/transactions.ts b/packages/client/src/interfaces/transactions.ts index e9e62bc536..a4bce47bb5 100644 --- a/packages/client/src/interfaces/transactions.ts +++ b/packages/client/src/interfaces/transactions.ts @@ -3,24 +3,21 @@ import type { Transaction } from '@near-js/transactions'; import type { TransactionComposer } from '../transactions'; import type { Dependent, RpcProviderDependency, SignerDependency } from './dependencies'; import type { RpcProviderQueryParams } from './view'; -import { PublicKey } from '@near-js/crypto'; export interface SignAndSendTransactionDependency extends Dependent {} -export interface SignAndSendNonceParams extends SignAndSendTransactionDependency, RpcProviderQueryParams { - nonce?: bigint; -} +export interface SignAndSendParams extends SignAndSendTransactionDependency, RpcProviderQueryParams {} -export interface ExternalActionTransaction extends SignAndSendNonceParams { +export interface ExternalActionTransaction extends SignAndSendParams { receiver: string; sender: string; } -export interface ReflexiveActionTransaction extends SignAndSendNonceParams { +export interface ReflexiveActionTransaction extends SignAndSendParams { account: string; } -export interface SignAndSendComposerParams extends SignAndSendNonceParams, RpcProviderQueryParams { +export interface SignAndSendComposerParams extends SignAndSendParams, RpcProviderQueryParams { composer: TransactionComposer; } diff --git a/packages/client/src/transactions/actions.ts b/packages/client/src/transactions/actions.ts index e52c0de3bf..ade92234f5 100644 --- a/packages/client/src/transactions/actions.ts +++ b/packages/client/src/transactions/actions.ts @@ -1,8 +1,9 @@ +import { PublicKey } from '@near-js/crypto'; + import type { ModifyAccessKeyParams, FunctionCallParams, TransferParams } from '../interfaces'; import { TransactionComposer } from './composer'; import { signAndSendFromComposer } from './sign_and_send'; -import { AddFunctionCallAccessKeyParams, SignAndSendNonceParams } from '../interfaces'; -import { PublicKey } from '@near-js/crypto'; +import { AddFunctionCallAccessKeyParams, SignAndSendParams } from '../interfaces'; /** * Helper method to compose, sign, and send a transaction from a TransactionComposer instance @@ -10,10 +11,9 @@ import { PublicKey } from '@near-js/crypto'; * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -async function getComposerResult({ composer, nonce, blockReference, deps }: { composer: TransactionComposer } & SignAndSendNonceParams) { +async function getComposerResult({ composer, blockReference, deps }: { composer: TransactionComposer } & SignAndSendParams) { const { result } = await signAndSendFromComposer({ composer, - nonce, blockReference, deps, }); @@ -32,11 +32,10 @@ async function getComposerResult({ composer, nonce, blockReference, deps }: { co * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function functionCall({ sender, receiver, method, args, gas, deposit, nonce, blockReference, deps }: FunctionCallParams) { +export async function functionCall({ sender, receiver, method, args, gas, deposit, blockReference, deps }: FunctionCallParams) { return getComposerResult({ composer: TransactionComposer.init({ sender, receiver }) .functionCall(method, args, gas, deposit), - nonce, blockReference, deps, }); @@ -50,11 +49,10 @@ export async function functionCall({ sender, receiver, method, args, gas, deposi * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function transfer({ sender, receiver, amount, nonce, blockReference, deps }: TransferParams) { +export async function transfer({ sender, receiver, amount, blockReference, deps }: TransferParams) { return getComposerResult({ composer: TransactionComposer.init({ sender, receiver }) .transfer(amount), - nonce, blockReference, deps, }); @@ -67,11 +65,10 @@ export async function transfer({ sender, receiver, amount, nonce, blockReference * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function addFullAccessKey({ account, publicKey, nonce, blockReference, deps }: ModifyAccessKeyParams) { +export async function addFullAccessKey({ account, publicKey, blockReference, deps }: ModifyAccessKeyParams) { return getComposerResult({ composer: TransactionComposer.init({ sender: account, receiver: account }) .addFullAccessKey(PublicKey.from(publicKey)), - nonce, blockReference, deps, }); @@ -87,11 +84,10 @@ export async function addFullAccessKey({ account, publicKey, nonce, blockReferen * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function addFunctionCallAccessKey({ account, publicKey, contract, methodNames, allowance, nonce, blockReference, deps }: AddFunctionCallAccessKeyParams) { +export async function addFunctionCallAccessKey({ account, publicKey, contract, methodNames, allowance, blockReference, deps }: AddFunctionCallAccessKeyParams) { return getComposerResult({ composer: TransactionComposer.init({ sender: account, receiver: account }) .addFunctionCallAccessKey(PublicKey.from(publicKey), contract, methodNames, allowance), - nonce, blockReference, deps, }); @@ -104,11 +100,10 @@ export async function addFunctionCallAccessKey({ account, publicKey, contract, m * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function deleteAccessKey({ account, publicKey, nonce, blockReference, deps }: ModifyAccessKeyParams) { +export async function deleteAccessKey({ account, publicKey, blockReference, deps }: ModifyAccessKeyParams) { return getComposerResult({ composer: TransactionComposer.init({ sender: account, receiver: account }) .deleteKey(PublicKey.from(publicKey)), - nonce, blockReference, deps, }); diff --git a/packages/client/src/transactions/sign_and_send.ts b/packages/client/src/transactions/sign_and_send.ts index cfc7f9f05d..95e278db97 100644 --- a/packages/client/src/transactions/sign_and_send.ts +++ b/packages/client/src/transactions/sign_and_send.ts @@ -42,18 +42,14 @@ export async function getSignerNonce({ account, blockReference, deps: { rpcProvi } // this might be more natural as a method on TransactionComposer but would be a major increase in scope -export async function signAndSendFromComposer({ composer, nonce, blockReference, deps }: SignAndSendComposerParams) { +export async function signAndSendFromComposer({ composer, blockReference, deps }: SignAndSendComposerParams) { const { rpcProvider, signer } = deps; const block = await getBlock({ blockReference, deps: { rpcProvider } }); - let signerNonce = nonce; - if (!signerNonce) { - signerNonce = await getSignerNonce({ account: composer.sender, blockReference, deps }); - signerNonce += 1n; - } + const signerNonce = await getSignerNonce({ account: composer.sender, blockReference, deps }); const transaction = composer.toTransaction({ - nonce: signerNonce, + nonce: signerNonce + 1n, publicKey: await signer.getPublicKey(), blockHeader: block?.header, }); From 0934ae7b7dcaec67b96bd03595b908e320f906f1 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Tue, 3 Sep 2024 16:34:42 -0700 Subject: [PATCH 09/55] feat: parse public key strings --- packages/client/src/transactions/actions.ts | 7 ++++--- packages/client/src/transactions/composer.ts | 16 ++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/client/src/transactions/actions.ts b/packages/client/src/transactions/actions.ts index ade92234f5..c245a6ae9c 100644 --- a/packages/client/src/transactions/actions.ts +++ b/packages/client/src/transactions/actions.ts @@ -68,7 +68,7 @@ export async function transfer({ sender, receiver, amount, blockReference, deps export async function addFullAccessKey({ account, publicKey, blockReference, deps }: ModifyAccessKeyParams) { return getComposerResult({ composer: TransactionComposer.init({ sender: account, receiver: account }) - .addFullAccessKey(PublicKey.from(publicKey)), + .addFullAccessKey(publicKey), blockReference, deps, }); @@ -87,7 +87,7 @@ export async function addFullAccessKey({ account, publicKey, blockReference, dep export async function addFunctionCallAccessKey({ account, publicKey, contract, methodNames, allowance, blockReference, deps }: AddFunctionCallAccessKeyParams) { return getComposerResult({ composer: TransactionComposer.init({ sender: account, receiver: account }) - .addFunctionCallAccessKey(PublicKey.from(publicKey), contract, methodNames, allowance), + .addFunctionCallAccessKey(publicKey, contract, methodNames, allowance), blockReference, deps, }); @@ -103,7 +103,8 @@ export async function addFunctionCallAccessKey({ account, publicKey, contract, m export async function deleteAccessKey({ account, publicKey, blockReference, deps }: ModifyAccessKeyParams) { return getComposerResult({ composer: TransactionComposer.init({ sender: account, receiver: account }) - .deleteKey(PublicKey.from(publicKey)), + .deleteKey(publicKey), + }); blockReference, deps, }); diff --git a/packages/client/src/transactions/composer.ts b/packages/client/src/transactions/composer.ts index 2bd3688218..78ff14f4b3 100644 --- a/packages/client/src/transactions/composer.ts +++ b/packages/client/src/transactions/composer.ts @@ -68,14 +68,14 @@ export class TransactionComposer { }); } - addFullAccessKey(publicKey: PublicKey) { - this.actions.push(actionCreators.addKey(publicKey, actionCreators.fullAccessKey())); + addFullAccessKey(publicKey: string) { + this.actions.push(actionCreators.addKey(PublicKey.from(publicKey), actionCreators.fullAccessKey())); return this; } - addFunctionCallAccessKey(publicKey: PublicKey, contractId: string, methodNames: string[], allowance?: bigint) { + addFunctionCallAccessKey(publicKey: string, contractId: string, methodNames: string[], allowance?: bigint) { const accessKey = actionCreators.functionCallAccessKey(contractId, methodNames, allowance) - this.actions.push(actionCreators.addKey(publicKey, accessKey)); + this.actions.push(actionCreators.addKey(PublicKey.from(publicKey), accessKey)); return this; } @@ -89,8 +89,8 @@ export class TransactionComposer { return this; } - deleteKey(publicKey: PublicKey) { - this.actions.push(actionCreators.deleteKey(publicKey)); + deleteKey(publicKey: string) { + this.actions.push(actionCreators.deleteKey(PublicKey.from(publicKey))); return this; } @@ -109,8 +109,8 @@ export class TransactionComposer { return this; } - stake(stake: bigint, publicKey: PublicKey) { - this.actions.push(actionCreators.stake(stake, publicKey)); + stake(stake: bigint, publicKey: string) { + this.actions.push(actionCreators.stake(stake, PublicKey.from(publicKey))); return this; } From 62e34855f8753e8a4bd0241d65c3f2fc112e080a Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Tue, 3 Sep 2024 16:36:41 -0700 Subject: [PATCH 10/55] feat: deployContract and deleteAccount methods --- .../client/src/interfaces/transactions.ts | 8 ++++ packages/client/src/transactions/actions.ts | 37 +++++++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/packages/client/src/interfaces/transactions.ts b/packages/client/src/interfaces/transactions.ts index a4bce47bb5..d2ddf9f8d4 100644 --- a/packages/client/src/interfaces/transactions.ts +++ b/packages/client/src/interfaces/transactions.ts @@ -36,6 +36,14 @@ export interface TransferParams extends ExternalActionTransaction { amount: bigint; } +export interface DeleteAccountParams extends ReflexiveActionTransaction { + beneficiaryId: string; +} + +export interface DeployContractParams extends ReflexiveActionTransaction { + code: Uint8Array +} + interface AddAccessKeyParams extends ReflexiveActionTransaction { publicKey: string; } diff --git a/packages/client/src/transactions/actions.ts b/packages/client/src/transactions/actions.ts index c245a6ae9c..11d0c5bcac 100644 --- a/packages/client/src/transactions/actions.ts +++ b/packages/client/src/transactions/actions.ts @@ -1,9 +1,7 @@ -import { PublicKey } from '@near-js/crypto'; - -import type { ModifyAccessKeyParams, FunctionCallParams, TransferParams } from '../interfaces'; +import type { ModifyAccessKeyParams, FunctionCallParams, TransferParams, DeleteAccountParams } from '../interfaces'; import { TransactionComposer } from './composer'; import { signAndSendFromComposer } from './sign_and_send'; -import { AddFunctionCallAccessKeyParams, SignAndSendParams } from '../interfaces'; +import { AddFunctionCallAccessKeyParams, DeployContractParams, SignAndSendParams } from '../interfaces'; /** * Helper method to compose, sign, and send a transaction from a TransactionComposer instance @@ -104,7 +102,38 @@ export async function deleteAccessKey({ account, publicKey, blockReference, deps return getComposerResult({ composer: TransactionComposer.init({ sender: account, receiver: account }) .deleteKey(publicKey), + blockReference, + deps, + }); +} + +/** + * Delete an account; account funds will be transferred to the designated beneficiary + * @param account account from which the access key will be removed + * @param publicKey public key string of the access key to be removed + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ +export async function deleteAccount({ account, beneficiaryId, blockReference, deps }: DeleteAccountParams) { + return getComposerResult({ + composer: TransactionComposer.init({ sender: account, receiver: account }) + .deleteAccount(beneficiaryId), + blockReference, + deps, }); +} + +/** + * Deploy contract code to an account + * @param account account to which the contract code will be deployed + * @param code WASM code as byte array + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ +export async function deployContract({ account, code, blockReference, deps }: DeployContractParams) { + return getComposerResult({ + composer: TransactionComposer.init({ sender: account, receiver: account }) + .deployContract(code), blockReference, deps, }); From c0fcd3478dabd9e668ea2273a7effa6ebe59d809 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 4 Sep 2024 16:08:33 -0700 Subject: [PATCH 11/55] chore: fix linting --- packages/near-api-js/package.json | 2 +- packages/near-api-js/test/.eslintrc.yml | 2 +- packages/near-api-js/test/test-utils.js | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/near-api-js/package.json b/packages/near-api-js/package.json index bccaac8b3e..a315641b87 100644 --- a/packages/near-api-js/package.json +++ b/packages/near-api-js/package.json @@ -51,7 +51,7 @@ "test": "jest test --passWithNoTests", "lint": "concurrently \"pnpm:lint:*(!fix) --no-error-on-unmatched-pattern\"", "lint:src": "eslint --ext .ts src", - "lint:fix": "concurrently \"pnpm:lint:*:fix\"", + "lint:fix": "pnpm lint:src:fix && pnpm lint:test:fix", "lint:src:fix": "eslint --ext .ts --fix src", "lint:test:fix": "eslint --ext .js --fix test", "prefuzz": "pnpm build", diff --git a/packages/near-api-js/test/.eslintrc.yml b/packages/near-api-js/test/.eslintrc.yml index 0fae1d994f..efc762f364 100644 --- a/packages/near-api-js/test/.eslintrc.yml +++ b/packages/near-api-js/test/.eslintrc.yml @@ -1,4 +1,4 @@ -extends: '../../../.eslintrc.js.yml' +extends: '../../../.eslintrc.base.yml' env: jest: true globals: diff --git a/packages/near-api-js/test/test-utils.js b/packages/near-api-js/test/test-utils.js index 275cf0b7e8..8348d5cf23 100644 --- a/packages/near-api-js/test/test-utils.js +++ b/packages/near-api-js/test/test-utils.js @@ -5,7 +5,7 @@ const nearApi = require('../src/index'); const networkId = 'unittest'; const HELLO_WASM_PATH = process.env.HELLO_WASM_PATH || 'node_modules/near-hello/dist/main.wasm'; -const HELLO_WASM_BALANCE = BigInt('10000000000000000000000000'); +const HELLO_WASM_BALANCE = 10000000000000000000000000n; const HELLO_WASM_METHODS = { viewMethods: ['getValue', 'getLastResult'], changeMethods: ['setValue', 'callPromise'] @@ -25,7 +25,7 @@ async function setUpTestConnection() { if (config.masterAccount) { // full accessKey on ci-testnet, dedicated rpc for tests. const secretKey = config.secretKey || 'ed25519:2wyRcSwSuHtRVmkMCGjPwnzZmQLeXLzLLyED1NDMt4BjnKgQL6tF85yBx6Jr26D2dUNeC716RBoTxntVHsegogYw'; - await keyStore.setKey(networkId, config.masterAccount, KeyPair.fromString(secretKey)); + await keyStore.setKey(networkId, config.masterAccount, nearApi.utils.KeyPair.fromString(secretKey)); } return nearApi.connect(config); } @@ -65,7 +65,7 @@ async function createAccountMultisig(near, options) { accountMultisig.getRecoveryMethods = () => ({ data: [] }); accountMultisig.postSignedJson = async (path) => { switch (path) { - case '/2fa/getAccessKey': return { publicKey }; + case '/2fa/getAccessKey': return { publicKey }; } }; await accountMultisig.deployMultisig(new Uint8Array([...(await fs.readFile(MULTISIG_WASM_PATH))])); From 43057837645d9230858fb91817a235b8c315b28e Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 5 Sep 2024 10:04:15 -0700 Subject: [PATCH 12/55] feat: staking --- .../client/src/interfaces/transactions.ts | 5 ++++ packages/client/src/transactions/actions.ts | 29 +++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/client/src/interfaces/transactions.ts b/packages/client/src/interfaces/transactions.ts index d2ddf9f8d4..309149a8a7 100644 --- a/packages/client/src/interfaces/transactions.ts +++ b/packages/client/src/interfaces/transactions.ts @@ -36,6 +36,11 @@ export interface TransferParams extends ExternalActionTransaction { amount: bigint; } +export interface StakeParams extends ReflexiveActionTransaction { + amount: bigint; + publicKey: string; +} + export interface DeleteAccountParams extends ReflexiveActionTransaction { beneficiaryId: string; } diff --git a/packages/client/src/transactions/actions.ts b/packages/client/src/transactions/actions.ts index 11d0c5bcac..45709e4434 100644 --- a/packages/client/src/transactions/actions.ts +++ b/packages/client/src/transactions/actions.ts @@ -1,7 +1,15 @@ -import type { ModifyAccessKeyParams, FunctionCallParams, TransferParams, DeleteAccountParams } from '../interfaces'; +import type { + AddFunctionCallAccessKeyParams, + DeleteAccountParams, + DeployContractParams, + FunctionCallParams, + ModifyAccessKeyParams, + SignAndSendParams, + StakeParams, + TransferParams, +} from '../interfaces'; import { TransactionComposer } from './composer'; import { signAndSendFromComposer } from './sign_and_send'; -import { AddFunctionCallAccessKeyParams, DeployContractParams, SignAndSendParams } from '../interfaces'; /** * Helper method to compose, sign, and send a transaction from a TransactionComposer instance @@ -56,6 +64,23 @@ export async function transfer({ sender, receiver, amount, blockReference, deps }); } +/** + * Stake Near with the specified validator + * @param account account staking Near + * @param amount Near to stake in yN + * @param publicKey public key for the target validator + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ +export async function stake({ account, amount, publicKey, blockReference, deps }: StakeParams) { + return getComposerResult({ + composer: TransactionComposer.init({ sender: account, receiver: account }) + .stake(amount, publicKey), + blockReference, + deps, + }); +} + /** * Add a full access key to an account * @param account account to which the FAK is added From 3b9e5210d22404b9e688aa1ac21f888816f4c48f Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 5 Sep 2024 10:09:15 -0700 Subject: [PATCH 13/55] feat: return entire response from single action calls --- packages/client/src/transactions/actions.ts | 33 +++++---------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/packages/client/src/transactions/actions.ts b/packages/client/src/transactions/actions.ts index 45709e4434..5958466f2b 100644 --- a/packages/client/src/transactions/actions.ts +++ b/packages/client/src/transactions/actions.ts @@ -4,29 +4,12 @@ import type { DeployContractParams, FunctionCallParams, ModifyAccessKeyParams, - SignAndSendParams, StakeParams, TransferParams, } from '../interfaces'; import { TransactionComposer } from './composer'; import { signAndSendFromComposer } from './sign_and_send'; -/** - * Helper method to compose, sign, and send a transaction from a TransactionComposer instance - * @param composer TransactionComposer instance with (at minimum) sender, receiver, and actions set - * @param blockReference block ID/finality - * @param deps sign-and-send dependencies - */ -async function getComposerResult({ composer, blockReference, deps }: { composer: TransactionComposer } & SignAndSendParams) { - const { result } = await signAndSendFromComposer({ - composer, - blockReference, - deps, - }); - - return result; -} - /** * Make a function call against a contract * @param sender transaction signer @@ -39,7 +22,7 @@ async function getComposerResult({ composer, blockReference, deps }: { composer: * @param deps sign-and-send dependencies */ export async function functionCall({ sender, receiver, method, args, gas, deposit, blockReference, deps }: FunctionCallParams) { - return getComposerResult({ + return signAndSendFromComposer({ composer: TransactionComposer.init({ sender, receiver }) .functionCall(method, args, gas, deposit), blockReference, @@ -56,7 +39,7 @@ export async function functionCall({ sender, receiver, method, args, gas, deposi * @param deps sign-and-send dependencies */ export async function transfer({ sender, receiver, amount, blockReference, deps }: TransferParams) { - return getComposerResult({ + return signAndSendFromComposer({ composer: TransactionComposer.init({ sender, receiver }) .transfer(amount), blockReference, @@ -73,7 +56,7 @@ export async function transfer({ sender, receiver, amount, blockReference, deps * @param deps sign-and-send dependencies */ export async function stake({ account, amount, publicKey, blockReference, deps }: StakeParams) { - return getComposerResult({ + return signAndSendFromComposer({ composer: TransactionComposer.init({ sender: account, receiver: account }) .stake(amount, publicKey), blockReference, @@ -89,7 +72,7 @@ export async function stake({ account, amount, publicKey, blockReference, deps } * @param deps sign-and-send dependencies */ export async function addFullAccessKey({ account, publicKey, blockReference, deps }: ModifyAccessKeyParams) { - return getComposerResult({ + return signAndSendFromComposer({ composer: TransactionComposer.init({ sender: account, receiver: account }) .addFullAccessKey(publicKey), blockReference, @@ -108,7 +91,7 @@ export async function addFullAccessKey({ account, publicKey, blockReference, dep * @param deps sign-and-send dependencies */ export async function addFunctionCallAccessKey({ account, publicKey, contract, methodNames, allowance, blockReference, deps }: AddFunctionCallAccessKeyParams) { - return getComposerResult({ + return signAndSendFromComposer({ composer: TransactionComposer.init({ sender: account, receiver: account }) .addFunctionCallAccessKey(publicKey, contract, methodNames, allowance), blockReference, @@ -124,7 +107,7 @@ export async function addFunctionCallAccessKey({ account, publicKey, contract, m * @param deps sign-and-send dependencies */ export async function deleteAccessKey({ account, publicKey, blockReference, deps }: ModifyAccessKeyParams) { - return getComposerResult({ + return signAndSendFromComposer({ composer: TransactionComposer.init({ sender: account, receiver: account }) .deleteKey(publicKey), blockReference, @@ -140,7 +123,7 @@ export async function deleteAccessKey({ account, publicKey, blockReference, deps * @param deps sign-and-send dependencies */ export async function deleteAccount({ account, beneficiaryId, blockReference, deps }: DeleteAccountParams) { - return getComposerResult({ + return signAndSendFromComposer({ composer: TransactionComposer.init({ sender: account, receiver: account }) .deleteAccount(beneficiaryId), blockReference, @@ -156,7 +139,7 @@ export async function deleteAccount({ account, beneficiaryId, blockReference, de * @param deps sign-and-send dependencies */ export async function deployContract({ account, code, blockReference, deps }: DeployContractParams) { - return getComposerResult({ + return signAndSendFromComposer({ composer: TransactionComposer.init({ sender: account, receiver: account }) .deployContract(code), blockReference, From 7b8a290dfdf4fe2697e6d5720d27f9be78f660be Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 5 Sep 2024 10:10:37 -0700 Subject: [PATCH 14/55] feat: public key utils --- packages/client/src/crypto.ts | 17 +++++++++++++++++ packages/client/src/index.ts | 1 + 2 files changed, 18 insertions(+) create mode 100644 packages/client/src/crypto.ts diff --git a/packages/client/src/crypto.ts b/packages/client/src/crypto.ts new file mode 100644 index 0000000000..be060a46f1 --- /dev/null +++ b/packages/client/src/crypto.ts @@ -0,0 +1,17 @@ +import { CurveType, KeyPair, KeyPairString } from '@near-js/crypto'; + +/** + * Generate a random key pair for the specified elliptic curve + * @param curve elliptic curve (e.g. `ed25519`) + */ +export function generateRandomKeyPair(curve: CurveType) { + return KeyPair.fromRandom(curve); +} + +/** + * Parse a signing key pair from a private key string + * @param privateKey private key string + */ +export function parseKeyPair(privateKey: KeyPairString) { + return KeyPair.fromString(privateKey); +} diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index a072414f4e..e281e923b7 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1,3 +1,4 @@ +export * from './crypto'; export * from './interfaces'; export * from './providers'; export * from './signers'; From 3646aa35f791b8ed38030a25da9c15dfaa828862 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Fri, 6 Sep 2024 15:26:44 -0700 Subject: [PATCH 15/55] feat: create account --- .../client/src/interfaces/transactions.ts | 11 ++++ packages/client/src/transactions/composer.ts | 2 +- .../client/src/transactions/create_account.ts | 57 +++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 packages/client/src/transactions/create_account.ts diff --git a/packages/client/src/interfaces/transactions.ts b/packages/client/src/interfaces/transactions.ts index 309149a8a7..407ea3a7c3 100644 --- a/packages/client/src/interfaces/transactions.ts +++ b/packages/client/src/interfaces/transactions.ts @@ -61,6 +61,17 @@ export interface AddFunctionCallAccessKeyParams extends AddAccessKeyParams { allowance?: bigint; } +export interface CreateAccountParams extends SignAndSendParams { + account: string; + initialBalance: bigint; + newAccount: string; + newPublicKey: string; +} + +export interface CreateTopLevelAccountParams extends CreateAccountParams { + contract: string +} + export interface SignTransactionParams extends Dependent { transaction: Transaction; } diff --git a/packages/client/src/transactions/composer.ts b/packages/client/src/transactions/composer.ts index 78ff14f4b3..8cf8d5c80e 100644 --- a/packages/client/src/transactions/composer.ts +++ b/packages/client/src/transactions/composer.ts @@ -74,7 +74,7 @@ export class TransactionComposer { } addFunctionCallAccessKey(publicKey: string, contractId: string, methodNames: string[], allowance?: bigint) { - const accessKey = actionCreators.functionCallAccessKey(contractId, methodNames, allowance) + const accessKey = actionCreators.functionCallAccessKey(contractId, methodNames, allowance); this.actions.push(actionCreators.addKey(PublicKey.from(publicKey), accessKey)); return this; } diff --git a/packages/client/src/transactions/create_account.ts b/packages/client/src/transactions/create_account.ts new file mode 100644 index 0000000000..9b019dbde5 --- /dev/null +++ b/packages/client/src/transactions/create_account.ts @@ -0,0 +1,57 @@ +import type { + CreateAccountParams, + CreateTopLevelAccountParams, +} from '../interfaces'; +import { TransactionComposer } from './composer'; +import { signAndSendFromComposer } from './sign_and_send'; +import { functionCall } from './actions'; + + +/** + * Create a new top-level account using an existing account + * (e.g. create `new.near` by signing with `creator.near`) + * @param account name of the account creating and funding the account + * @param contract root contract for the target network (e.g. `near`) + * @param newAccount name of the created account + * @param newPublicKey public key for the created account's initial full access key + * @param initialBalance initial account balance in yN + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ +export async function createTopLevelAccount({ account, contract, newAccount, newPublicKey, initialBalance, blockReference, deps }: CreateTopLevelAccountParams) { + return functionCall({ + sender: account, + receiver: contract, + method: 'create_account', + args: { + new_account_id: newAccount, + new_public_key: newPublicKey, + }, + deposit: initialBalance, + blockReference, + deps, + }); +} + +/** + * Create a new sub-account under an existing account + * (e.g. create `sub.new.near` by signing with `new.near`) + * @param account name of the existing account under which the new account is created + * @param contract root contract for the target network (e.g. `testnet`) + * @param newAccount name of the created account + * @param newPublicKey public key for the created account's initial full access key + * @param initialBalance initial account balance in yN + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ +export async function createSubAccount({ account, newAccount, newPublicKey, initialBalance, blockReference, deps }: CreateAccountParams) { + return signAndSendFromComposer({ + composer: TransactionComposer.init({ sender: account, receiver: newAccount }) + .createAccount() + .transfer(initialBalance) + .addFullAccessKey(newPublicKey), + blockReference, + deps, + }); +} + From 0edf382c52aa397e24b85d57b2804ebd450f5457 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Fri, 6 Sep 2024 16:25:59 -0700 Subject: [PATCH 16/55] feat: funded testnet account creator --- packages/client/package.json | 3 +- packages/client/src/constants.ts | 2 + packages/client/src/funded_account.ts | 38 ++++++++++++ packages/client/src/index.ts | 1 + .../client/src/interfaces/transactions.ts | 17 +++--- pnpm-lock.yaml | 61 +++++++++++++++++++ 6 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 packages/client/src/funded_account.ts diff --git a/packages/client/package.json b/packages/client/package.json index d11952e7f7..2468f1476e 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -20,7 +20,8 @@ "@near-js/transactions": "workspace:*", "@near-js/types": "workspace:*", "@near-js/utils": "workspace:*", - "@noble/hashes": "1.3.3" + "@noble/hashes": "1.3.3", + "isomorphic-unfetch": "4.0.2" }, "keywords": [], "author": "", diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts index 6dc8ea34db..7f07651cc2 100644 --- a/packages/client/src/constants.ts +++ b/packages/client/src/constants.ts @@ -13,3 +13,5 @@ export const PAGODA_RPC_ENDPOINTS_TESTNET = [ 'https://rpc.testnet.near.org', 'https://rpc.testnet.pagoda.co', ]; + +export const KITWALLET_FUNDED_TESTNET_ACCOUNT_ENDPOINT = 'https://testnet.kitwallet.api/create'; \ No newline at end of file diff --git a/packages/client/src/funded_account.ts b/packages/client/src/funded_account.ts new file mode 100644 index 0000000000..92bfb65405 --- /dev/null +++ b/packages/client/src/funded_account.ts @@ -0,0 +1,38 @@ +import type { FinalExecutionOutcome } from '@near-js/types'; +import unfetch from 'isomorphic-unfetch'; + +import { KITWALLET_FUNDED_TESTNET_ACCOUNT_ENDPOINT } from './constants'; +import { NewAccountParams } from './interfaces'; + +interface CreateFundedTestnetAccountParams extends NewAccountParams { + endpointUrl?: string; +} + +/** + * Create a new funded testnet account via faucet REST endpoint + * (e.g. create `new.testnet` with a preset amount of Near) + * @param endpointUrl REST endpoint for the funded testnet account creation (defaults to the current Near Contract Helper endpoint) + * @param newAccount name of the created account + * @param newPublicKey public key for the created account's initial full access key + */ +export async function createFundedTestnetAccount({ + newAccount, + newPublicKey, + endpointUrl = KITWALLET_FUNDED_TESTNET_ACCOUNT_ENDPOINT, +}: CreateFundedTestnetAccountParams) { + const res = await unfetch(endpointUrl, { + method: 'POST', + body: JSON.stringify({ + newAccountId: newAccount, + newAccountPublicKey: newPublicKey, + }), + headers: { 'Content-Type': 'application/json' }, + }); + + const { ok, status } = res; + if (!ok) { + throw new Error(`Failed to create account on ${endpointUrl}: ${status}`); + } + + return await res.json() as FinalExecutionOutcome; +} diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index e281e923b7..9d74ce4847 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1,4 +1,5 @@ export * from './crypto'; +export * from './funded_account'; export * from './interfaces'; export * from './providers'; export * from './signers'; diff --git a/packages/client/src/interfaces/transactions.ts b/packages/client/src/interfaces/transactions.ts index 407ea3a7c3..744938ab69 100644 --- a/packages/client/src/interfaces/transactions.ts +++ b/packages/client/src/interfaces/transactions.ts @@ -61,17 +61,20 @@ export interface AddFunctionCallAccessKeyParams extends AddAccessKeyParams { allowance?: bigint; } -export interface CreateAccountParams extends SignAndSendParams { - account: string; - initialBalance: bigint; +export interface SignTransactionParams extends Dependent { + transaction: Transaction; +} + +export interface NewAccountParams { newAccount: string; newPublicKey: string; } -export interface CreateTopLevelAccountParams extends CreateAccountParams { - contract: string +export interface CreateAccountParams extends SignAndSendParams, NewAccountParams { + account: string; + initialBalance: bigint; } -export interface SignTransactionParams extends Dependent { - transaction: Transaction; +export interface CreateTopLevelAccountParams extends CreateAccountParams { + contract: string } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ff2436428..7a98d039a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -217,6 +217,9 @@ importers: '@noble/hashes': specifier: 1.3.3 version: 1.3.3 + isomorphic-unfetch: + specifier: 4.0.2 + version: 4.0.2 devDependencies: '@types/node': specifier: 20.0.0 @@ -1861,6 +1864,10 @@ packages: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} @@ -2151,6 +2158,10 @@ packages: fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + fido2-lib@3.4.1: resolution: {integrity: sha512-efNrRbckp48AW7Q43o6gcp8/wnoBM7hwKikQntdiR2/NqVMPaCXFQs8kJ9wQqfv5V3PxZdg4kD9DpxdqYl6jxQ==} engines: {node: '>=10'} @@ -2192,6 +2203,10 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -2600,6 +2615,9 @@ packages: isomorphic-unfetch@3.1.0: resolution: {integrity: sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==} + isomorphic-unfetch@4.0.2: + resolution: {integrity: sha512-1Yd+CF/7al18/N2BDbsLBcp6RO3tucSW+jcLq24dqdX5MNbCNTw1z4BsGsp4zNmjr/Izm2cs/cEqZPp4kvWSCA==} + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -3068,6 +3086,10 @@ packages: node-addon-api@5.1.0: resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} engines: {node: 4.x || >=6.0.0} @@ -3077,6 +3099,10 @@ packages: encoding: optional: true + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-gyp-build-optional-packages@5.1.1: resolution: {integrity: sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==} hasBin: true @@ -3881,6 +3907,9 @@ packages: unfetch@4.2.0: resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} + unfetch@5.0.0: + resolution: {integrity: sha512-3xM2c89siXg0nHvlmYsQ2zkLASvVMBisZm5lF3gFDqfF2xonNStDJyMpvaOBe0a1Edxmqrf2E0HBdmy9QyZaeg==} + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -3931,6 +3960,10 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + webcrypto-core@1.8.0: resolution: {integrity: sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw==} @@ -5717,6 +5750,8 @@ snapshots: dargs@8.1.0: {} + data-uri-to-buffer@4.0.1: {} + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 @@ -6075,6 +6110,11 @@ snapshots: dependencies: bser: 2.1.1 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + fido2-lib@3.4.1: dependencies: '@hexagon/base64': 1.1.26 @@ -6131,6 +6171,10 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -6528,6 +6572,11 @@ snapshots: transitivePeerDependencies: - encoding + isomorphic-unfetch@4.0.2: + dependencies: + node-fetch: 3.3.2 + unfetch: 5.0.0 + istanbul-lib-coverage@3.2.2: {} istanbul-lib-instrument@5.2.1: @@ -7370,12 +7419,20 @@ snapshots: node-addon-api@5.1.0: {} + node-domexception@1.0.0: {} + node-fetch@2.6.7(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 optionalDependencies: encoding: 0.1.13 + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-gyp-build-optional-packages@5.1.1: dependencies: detect-libc: 2.0.3 @@ -8207,6 +8264,8 @@ snapshots: unfetch@4.2.0: {} + unfetch@5.0.0: {} + unicorn-magic@0.1.0: {} universalify@0.1.2: {} @@ -8254,6 +8313,8 @@ snapshots: dependencies: defaults: 1.0.4 + web-streams-polyfill@3.3.3: {} + webcrypto-core@1.8.0: dependencies: '@peculiar/asn1-schema': 2.3.8 From 36e8b5d45bb2ce9a2ba982446b6b8ecefc78cfa7 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 11 Sep 2024 13:14:34 -0700 Subject: [PATCH 17/55] fix: fetch dependency --- packages/client/package.json | 7 ++- packages/client/src/funded_account.ts | 4 +- pnpm-lock.yaml | 82 +++++++-------------------- 3 files changed, 27 insertions(+), 66 deletions(-) diff --git a/packages/client/package.json b/packages/client/package.json index 2468f1476e..4420a08219 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -8,8 +8,8 @@ "build": "pnpm compile:esm && pnpm compile:cjs", "compile:esm": "tsc -p tsconfig.json", "compile:cjs": "tsc -p tsconfig.cjs.json && cjsify ./lib/commonjs", - "lint": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc --no-error-on-unmatched-pattern", - "lint:fix": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc --fix --no-error-on-unmatched-pattern" + "lint": "eslint -c .eslintrc.yml src/**/*.ts --no-eslintrc --no-error-on-unmatched-pattern", + "lint:fix": "eslint -c .eslintrc.yml src/**/*.ts --no-eslintrc --no-error-on-unmatched-pattern --fix" }, "dependencies": { "@near-js/crypto": "workspace:*", @@ -21,7 +21,7 @@ "@near-js/types": "workspace:*", "@near-js/utils": "workspace:*", "@noble/hashes": "1.3.3", - "isomorphic-unfetch": "4.0.2" + "isomorphic-fetch": "3.0.0" }, "keywords": [], "author": "", @@ -29,6 +29,7 @@ "devDependencies": { "@types/node": "20.0.0", "build": "workspace:*", + "tsconfig": "workspace:*", "typescript": "5.4.5" }, "files": [ diff --git a/packages/client/src/funded_account.ts b/packages/client/src/funded_account.ts index 92bfb65405..25e3e2f02b 100644 --- a/packages/client/src/funded_account.ts +++ b/packages/client/src/funded_account.ts @@ -1,5 +1,5 @@ import type { FinalExecutionOutcome } from '@near-js/types'; -import unfetch from 'isomorphic-unfetch'; +import fetch from 'isomorphic-fetch'; import { KITWALLET_FUNDED_TESTNET_ACCOUNT_ENDPOINT } from './constants'; import { NewAccountParams } from './interfaces'; @@ -20,7 +20,7 @@ export async function createFundedTestnetAccount({ newPublicKey, endpointUrl = KITWALLET_FUNDED_TESTNET_ACCOUNT_ENDPOINT, }: CreateFundedTestnetAccountParams) { - const res = await unfetch(endpointUrl, { + const res = await fetch(endpointUrl, { method: 'POST', body: JSON.stringify({ newAccountId: newAccount, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a98d039a7..df5107009e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -217,9 +217,9 @@ importers: '@noble/hashes': specifier: 1.3.3 version: 1.3.3 - isomorphic-unfetch: - specifier: 4.0.2 - version: 4.0.2 + isomorphic-fetch: + specifier: 3.0.0 + version: 3.0.0(encoding@0.1.13) devDependencies: '@types/node': specifier: 20.0.0 @@ -227,6 +227,9 @@ importers: build: specifier: workspace:* version: link:../build + tsconfig: + specifier: workspace:* + version: link:../tsconfig typescript: specifier: 5.4.5 version: 5.4.5 @@ -1864,10 +1867,6 @@ packages: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} @@ -2158,10 +2157,6 @@ packages: fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - fido2-lib@3.4.1: resolution: {integrity: sha512-efNrRbckp48AW7Q43o6gcp8/wnoBM7hwKikQntdiR2/NqVMPaCXFQs8kJ9wQqfv5V3PxZdg4kD9DpxdqYl6jxQ==} engines: {node: '>=10'} @@ -2203,10 +2198,6 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -2612,12 +2603,12 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isomorphic-fetch@3.0.0: + resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} + isomorphic-unfetch@3.1.0: resolution: {integrity: sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==} - isomorphic-unfetch@4.0.2: - resolution: {integrity: sha512-1Yd+CF/7al18/N2BDbsLBcp6RO3tucSW+jcLq24dqdX5MNbCNTw1z4BsGsp4zNmjr/Izm2cs/cEqZPp4kvWSCA==} - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -3086,10 +3077,6 @@ packages: node-addon-api@5.1.0: resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} engines: {node: 4.x || >=6.0.0} @@ -3099,10 +3086,6 @@ packages: encoding: optional: true - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - node-gyp-build-optional-packages@5.1.1: resolution: {integrity: sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==} hasBin: true @@ -3907,9 +3890,6 @@ packages: unfetch@4.2.0: resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} - unfetch@5.0.0: - resolution: {integrity: sha512-3xM2c89siXg0nHvlmYsQ2zkLASvVMBisZm5lF3gFDqfF2xonNStDJyMpvaOBe0a1Edxmqrf2E0HBdmy9QyZaeg==} - unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -3960,16 +3940,15 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - webcrypto-core@1.8.0: resolution: {integrity: sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw==} webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + whatwg-fetch@3.6.20: + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -5750,8 +5729,6 @@ snapshots: dargs@8.1.0: {} - data-uri-to-buffer@4.0.1: {} - data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 @@ -6110,11 +6087,6 @@ snapshots: dependencies: bser: 2.1.1 - fetch-blob@3.2.0: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 - fido2-lib@3.4.1: dependencies: '@hexagon/base64': 1.1.26 @@ -6171,10 +6143,6 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 - formdata-polyfill@4.0.10: - dependencies: - fetch-blob: 3.2.0 - fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -6565,17 +6533,19 @@ snapshots: isexe@2.0.0: {} - isomorphic-unfetch@3.1.0(encoding@0.1.13): + isomorphic-fetch@3.0.0(encoding@0.1.13): dependencies: node-fetch: 2.6.7(encoding@0.1.13) - unfetch: 4.2.0 + whatwg-fetch: 3.6.20 transitivePeerDependencies: - encoding - isomorphic-unfetch@4.0.2: + isomorphic-unfetch@3.1.0(encoding@0.1.13): dependencies: - node-fetch: 3.3.2 - unfetch: 5.0.0 + node-fetch: 2.6.7(encoding@0.1.13) + unfetch: 4.2.0 + transitivePeerDependencies: + - encoding istanbul-lib-coverage@3.2.2: {} @@ -7419,20 +7389,12 @@ snapshots: node-addon-api@5.1.0: {} - node-domexception@1.0.0: {} - node-fetch@2.6.7(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 optionalDependencies: encoding: 0.1.13 - node-fetch@3.3.2: - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - node-gyp-build-optional-packages@5.1.1: dependencies: detect-libc: 2.0.3 @@ -8264,8 +8226,6 @@ snapshots: unfetch@4.2.0: {} - unfetch@5.0.0: {} - unicorn-magic@0.1.0: {} universalify@0.1.2: {} @@ -8313,8 +8273,6 @@ snapshots: dependencies: defaults: 1.0.4 - web-streams-polyfill@3.3.3: {} - webcrypto-core@1.8.0: dependencies: '@peculiar/asn1-schema': 2.3.8 @@ -8325,6 +8283,8 @@ snapshots: webidl-conversions@3.0.1: {} + whatwg-fetch@3.6.20: {} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 From 6402f7d6b34399f0c5440c7f44e9c4fe048944f0 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 11 Sep 2024 13:18:40 -0700 Subject: [PATCH 18/55] feat: group signing/keystore modules under dedicated path --- packages/client/src/index.ts | 2 +- packages/client/src/signers.ts | 34 ----------------- packages/client/src/signing/index.ts | 7 ++++ .../signing/keystores/plaintext_filesystem.ts | 15 ++++++++ packages/client/src/signing/signers.ts | 38 +++++++++++++++++++ pnpm-lock.yaml | 3 ++ 6 files changed, 64 insertions(+), 35 deletions(-) delete mode 100644 packages/client/src/signers.ts create mode 100644 packages/client/src/signing/index.ts create mode 100644 packages/client/src/signing/keystores/plaintext_filesystem.ts create mode 100644 packages/client/src/signing/signers.ts diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 9d74ce4847..caef674bd5 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -2,6 +2,6 @@ export * from './crypto'; export * from './funded_account'; export * from './interfaces'; export * from './providers'; -export * from './signers'; +export * from './signing'; export * from './transactions'; export * from './view'; diff --git a/packages/client/src/signers.ts b/packages/client/src/signers.ts deleted file mode 100644 index d5fd94888e..0000000000 --- a/packages/client/src/signers.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { KeyPair, KeyPairString, PublicKey } from '@near-js/crypto'; -import { KeyStore } from '@near-js/keystores'; -import { InMemorySigner } from '@near-js/signers'; - -import type { MessageSigner } from './interfaces'; - -export function getSignerFromKeyPair(keyPair: KeyPair): MessageSigner { - return { - getPublicKey(): Promise { - return Promise.resolve(keyPair.getPublicKey()); - }, - async signMessage(m: Uint8Array): Promise { - return keyPair.sign(m).signature; - } - } -} - -export function getSignerFromPrivateKey(privateKey: KeyPairString): MessageSigner { - return getSignerFromKeyPair(KeyPair.fromString(privateKey)) -} - -export async function getSignerFromKeyStore(accountId: string, network: string, keyStore: KeyStore): Promise { - const signer = new InMemorySigner(keyStore); - - return { - getPublicKey(): Promise { - return signer.getPublicKey(accountId, network); - }, - async signMessage(m: Uint8Array): Promise { - const { signature } = await signer.signMessage(m, accountId, network); - return signature; - } - } -} diff --git a/packages/client/src/signing/index.ts b/packages/client/src/signing/index.ts new file mode 100644 index 0000000000..97f0cbef49 --- /dev/null +++ b/packages/client/src/signing/index.ts @@ -0,0 +1,7 @@ +export { + getPlaintextFilesystemSigner, +} from './keystores/plaintext_filesystem'; +export { + getSignerFromKeyPair, + getSignerFromPrivateKey, +} from './signers'; diff --git a/packages/client/src/signing/keystores/plaintext_filesystem.ts b/packages/client/src/signing/keystores/plaintext_filesystem.ts new file mode 100644 index 0000000000..cfc492b38a --- /dev/null +++ b/packages/client/src/signing/keystores/plaintext_filesystem.ts @@ -0,0 +1,15 @@ +/* + THIS MODULE IS PROVIDED FOR BACKWARD COMPATIBILITY PURPOSES ONLY + PRIVATE KEYS ARE STORED IN PLAINTEXT - DO NOT USE FOR NON-TEST ACCOUNTS +*/ +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; + +import { getSignerFromKeystore } from '../signers'; + +export function getPlaintextFilesystemSigner(account: string, network: string, filepath: string) { + const keystore = new UnencryptedFileSystemKeyStore(filepath); + return { + keystore, + signer: getSignerFromKeystore(account, network, keystore), + }; +} diff --git a/packages/client/src/signing/signers.ts b/packages/client/src/signing/signers.ts new file mode 100644 index 0000000000..2fdbe806b0 --- /dev/null +++ b/packages/client/src/signing/signers.ts @@ -0,0 +1,38 @@ +import { + KeyPair, + type KeyPairString, + type PublicKey, +} from '@near-js/crypto'; +import { KeyStore } from '@near-js/keystores'; +import { InMemorySigner } from '@near-js/signers'; + +import type { MessageSigner } from '../interfaces'; + +export function getSignerFromKeyPair(keyPair: KeyPair): MessageSigner { + return { + async getPublicKey(): Promise { + return keyPair.getPublicKey(); + }, + async signMessage(m: Uint8Array): Promise { + return keyPair.sign(m).signature; + } + }; +} + +export function getSignerFromPrivateKey(privateKey: KeyPairString): MessageSigner { + return getSignerFromKeyPair(KeyPair.fromString(privateKey)); +} + +export function getSignerFromKeystore(account: string, network: string, keyStore: KeyStore): MessageSigner { + const signer = new InMemorySigner(keyStore); + + return { + async getPublicKey(): Promise { + return signer.getPublicKey(account, network); + }, + async signMessage(m: Uint8Array): Promise { + const { signature } = await signer.signMessage(m, account, network); + return signature; + } + }; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df5107009e..a47109098c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -196,6 +196,9 @@ importers: '@near-js/crypto': specifier: workspace:* version: link:../crypto + '@near-js/keystores': + specifier: workspace:* + version: link:../keystores '@near-js/keystores-node': specifier: workspace:* version: link:../keystores-node From 88f5c92e468ddbdbb624bb6c4c086aa4b1c4eef7 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 11 Sep 2024 13:25:37 -0700 Subject: [PATCH 19/55] refactor: consolidate imports --- packages/client/src/view.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index 39cc3a0c46..7b6bb83902 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -1,4 +1,12 @@ -import type { AccountView, CodeResult, QueryResponseKind } from '@near-js/types'; +import type { + AccessKeyList, + AccessKeyView, + AccountView, + CodeResult, + ContractCodeView, + QueryResponseKind, + ViewStateResult, +} from '@near-js/types'; import type { Dependent, @@ -9,7 +17,6 @@ import type { ViewContractStateParams, ViewParams, } from './interfaces'; -import { AccessKeyList, AccessKeyView, ContractCodeView, ViewStateResult } from '@near-js/types'; const DEFAULT_VIEW_BLOCK_REFERENCE = { finality: 'optimistic' }; @@ -164,6 +171,7 @@ export async function getContractCode({ account, blockReference, deps }: ViewAcc * @param prefix target prefix filter (empty string/buffer returns all keys) * @param blockReference block ID/finality * @param deps readonly RPC dependencies + * @returns object key-value pairs */ export async function getContractState({ account, prefix, blockReference, deps }: ViewContractStateParams) { const { values } = await query({ From dde6f86eba8c8a7a917aaa84461938f314b53cb5 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 11 Sep 2024 20:58:03 -0700 Subject: [PATCH 20/55] feat: fixes, interface updates --- packages/client/src/constants.ts | 4 +++- packages/client/src/index.ts | 3 +++ .../client/src/signing/keystores/plaintext_filesystem.ts | 8 +++++++- packages/client/src/transactions/index.ts | 1 + packages/client/src/transactions/sign_and_send.ts | 7 +++++-- packages/client/src/view.ts | 4 ++-- 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts index 7f07651cc2..2a6514d1e8 100644 --- a/packages/client/src/constants.ts +++ b/packages/client/src/constants.ts @@ -4,6 +4,8 @@ export const TESTNET_RPC_URL = 'https://rpc.testnet.near.org'; export const DEFAULT_FILESYSTEM_KEYSTORE_PATH = '.near-credentials'; export const DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL = BigInt(100); +export const MAX_GAS = 300000000000000n; + export const PAGODA_RPC_ENDPOINTS_MAINNET = [ 'https://rpc.near.org', 'https://rpc.mainnet.pagoda.co', @@ -14,4 +16,4 @@ export const PAGODA_RPC_ENDPOINTS_TESTNET = [ 'https://rpc.testnet.pagoda.co', ]; -export const KITWALLET_FUNDED_TESTNET_ACCOUNT_ENDPOINT = 'https://testnet.kitwallet.api/create'; \ No newline at end of file +export const KITWALLET_FUNDED_TESTNET_ACCOUNT_ENDPOINT = 'https://testnet-api.kitwallet.app/account'; diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index caef674bd5..eb2f5f5621 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1,3 +1,6 @@ +export { formatNearAmount } from '@near-js/utils'; + +export * from './constants'; export * from './crypto'; export * from './funded_account'; export * from './interfaces'; diff --git a/packages/client/src/signing/keystores/plaintext_filesystem.ts b/packages/client/src/signing/keystores/plaintext_filesystem.ts index cfc492b38a..c33b5033de 100644 --- a/packages/client/src/signing/keystores/plaintext_filesystem.ts +++ b/packages/client/src/signing/keystores/plaintext_filesystem.ts @@ -6,7 +6,13 @@ import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; import { getSignerFromKeystore } from '../signers'; -export function getPlaintextFilesystemSigner(account: string, network: string, filepath: string) { +interface PlaintextFilesystemSigner { + account: string; + network: string; + filepath: string; +} + +export function getPlaintextFilesystemSigner({ account, network, filepath }: PlaintextFilesystemSigner) { const keystore = new UnencryptedFileSystemKeyStore(filepath); return { keystore, diff --git a/packages/client/src/transactions/index.ts b/packages/client/src/transactions/index.ts index 72224e48a2..fde2f5d43d 100644 --- a/packages/client/src/transactions/index.ts +++ b/packages/client/src/transactions/index.ts @@ -1,3 +1,4 @@ export * from './actions'; export * from './composer'; +export * from './create_account'; export * from './sign_and_send'; diff --git a/packages/client/src/transactions/sign_and_send.ts b/packages/client/src/transactions/sign_and_send.ts index 95e278db97..7e8a835bd4 100644 --- a/packages/client/src/transactions/sign_and_send.ts +++ b/packages/client/src/transactions/sign_and_send.ts @@ -6,6 +6,9 @@ import type { SignTransactionParams, SignAndSendTransactionParams } from '../int import { getBlock } from '../providers'; import { getNonce } from '../view'; import { SignAndSendComposerParams } from '../interfaces'; +import { BlockReference } from '@near-js/types'; + +const DEFAULT_FINALITY: BlockReference = { finality: 'final' }; export async function signTransaction({ transaction, deps: { signer } }: SignTransactionParams) { const encodedTx = transaction.encode(); @@ -32,7 +35,7 @@ export async function signAndSendTransaction({ transaction, deps: { rpcProvider, }; } -export async function getSignerNonce({ account, blockReference, deps: { rpcProvider, signer } }) { +export async function getSignerNonce({ account, blockReference = DEFAULT_FINALITY, deps: { rpcProvider, signer } }) { return getNonce({ account, publicKey: (await signer.getPublicKey()).toString(), @@ -42,7 +45,7 @@ export async function getSignerNonce({ account, blockReference, deps: { rpcProvi } // this might be more natural as a method on TransactionComposer but would be a major increase in scope -export async function signAndSendFromComposer({ composer, blockReference, deps }: SignAndSendComposerParams) { +export async function signAndSendFromComposer({ composer, blockReference = DEFAULT_FINALITY, deps }: SignAndSendComposerParams) { const { rpcProvider, signer } = deps; const block = await getBlock({ blockReference, deps: { rpcProvider } }); diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index 7b6bb83902..165cbcf2b9 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -111,7 +111,7 @@ export function getAccessKey({ account, publicKey, blockReference, deps }: ViewA request: RequestType.ViewAccessKey, account, args: { - publicKey, + public_key: publicKey, }, blockReference, deps, @@ -205,5 +205,5 @@ export async function getNonce({ account, publicKey, blockReference, deps }: Vie deps, }); - return nonce; + return BigInt(nonce); } From 27271b711b0125daff538ccaad9b6d78f91ab4aa Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 11 Sep 2024 21:04:08 -0700 Subject: [PATCH 21/55] feat: update cookbook examples --- .../access-keys/create-full-access-key.js | 25 ------- .../access-keys/create-full-access-key.ts | 47 ++++++++++++++ .../access-keys/create-function-access-key.js | 30 --------- .../access-keys/create-function-access-key.ts | 48 ++++++++++++++ .../accounts/access-keys/delete-access-key.js | 25 ------- .../accounts/access-keys/delete-access-key.ts | 42 ++++++++++++ .../accounts/create-funded-testnet-account.ts | 35 ++++++++++ .../accounts/create-mainnet-account.js | 44 ------------- .../accounts/create-mainnet-account.ts | 48 ++++++++++++++ .../accounts/create-testnet-account.js | 44 ------------- .../accounts/create-testnet-account.ts | 48 ++++++++++++++ packages/cookbook/accounts/index.ts | 6 ++ packages/cookbook/package.json | 26 +++++++- packages/cookbook/src/index.ts | 2 + packages/cookbook/tsconfig.cjs.json | 10 --- packages/cookbook/tsconfig.json | 12 ++-- packages/cookbook/utils/calculate-gas.js | 65 ------------------- packages/cookbook/utils/calculate-gas.ts | 65 +++++++++++++++++++ packages/cookbook/utils/index.ts | 1 + packages/crypto/src/public_key.ts | 1 + packages/providers/src/fetch_json.ts | 2 +- packages/utils/src/provider.ts | 2 +- pnpm-lock.yaml | 54 +++++++-------- 23 files changed, 401 insertions(+), 281 deletions(-) delete mode 100644 packages/cookbook/accounts/access-keys/create-full-access-key.js create mode 100644 packages/cookbook/accounts/access-keys/create-full-access-key.ts delete mode 100644 packages/cookbook/accounts/access-keys/create-function-access-key.js create mode 100644 packages/cookbook/accounts/access-keys/create-function-access-key.ts delete mode 100644 packages/cookbook/accounts/access-keys/delete-access-key.js create mode 100644 packages/cookbook/accounts/access-keys/delete-access-key.ts create mode 100644 packages/cookbook/accounts/create-funded-testnet-account.ts delete mode 100644 packages/cookbook/accounts/create-mainnet-account.js create mode 100644 packages/cookbook/accounts/create-mainnet-account.ts delete mode 100644 packages/cookbook/accounts/create-testnet-account.js create mode 100644 packages/cookbook/accounts/create-testnet-account.ts create mode 100644 packages/cookbook/accounts/index.ts create mode 100644 packages/cookbook/src/index.ts delete mode 100644 packages/cookbook/tsconfig.cjs.json delete mode 100644 packages/cookbook/utils/calculate-gas.js create mode 100644 packages/cookbook/utils/calculate-gas.ts create mode 100644 packages/cookbook/utils/index.ts diff --git a/packages/cookbook/accounts/access-keys/create-full-access-key.js b/packages/cookbook/accounts/access-keys/create-full-access-key.js deleted file mode 100644 index 8f566a0fe6..0000000000 --- a/packages/cookbook/accounts/access-keys/create-full-access-key.js +++ /dev/null @@ -1,25 +0,0 @@ -const { KeyPair, keyStores, connect } = require("near-api-js"); -const path = require("path"); -const homedir = require("os").homedir(); - -const CREDENTIALS_DIR = ".near-credentials"; -const ACCOUNT_ID = "example.testnet"; -const credentialsPath = path.join(homedir, CREDENTIALS_DIR); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -const config = { - keyStore, - networkId: "testnet", - nodeUrl: "https://rpc.testnet.near.org", -}; - -createFullAccessKey(ACCOUNT_ID); - -async function createFullAccessKey(accountId) { - const keyPair = KeyPair.fromRandom("ed25519"); - const publicKey = keyPair.publicKey.toString(); - const near = await connect(config); - const account = await near.account(accountId); - await keyStore.setKey(config.networkId, publicKey, keyPair); - await account.addKey(publicKey); -} diff --git a/packages/cookbook/accounts/access-keys/create-full-access-key.ts b/packages/cookbook/accounts/access-keys/create-full-access-key.ts new file mode 100644 index 0000000000..f744008d9c --- /dev/null +++ b/packages/cookbook/accounts/access-keys/create-full-access-key.ts @@ -0,0 +1,47 @@ +import { + addFullAccessKey, + generateRandomKeyPair, + getPlaintextFilesystemSigner, + getTestnetRpcProvider, +} from '@near-js/client'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +export default async function createFullAccessKey(accountId: string) { + if (!accountId) { + console.log(chalk`{red pnpm createFullAccessKey -- ACCOUNT_ID}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const { keystore, signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + const previousKey = await signer.getPublicKey(); + + // create a new key from random data + const keyPair = generateRandomKeyPair('ed25519'); + + // add the generated key to the account as a Full Access Key (FAK) + await addFullAccessKey({ + account: accountId, + publicKey: keyPair.getPublicKey().toString(), + deps: { + rpcProvider, + signer, + }, + }); + + // overwrite the key used to sign the transaction + // above with the new FAK in the key store + await keystore.setKey('testnet', accountId, keyPair); + + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.green RESULTS} {white Added new full access key and set in keystore}` ); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.white Previous Key} {white |} {bold.yellow ${previousKey.toString()}}`); + console.log(chalk`{bold.white New Key} {white |} {bold.yellow ${keyPair.getPublicKey().toString()}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); +} diff --git a/packages/cookbook/accounts/access-keys/create-function-access-key.js b/packages/cookbook/accounts/access-keys/create-function-access-key.js deleted file mode 100644 index 0352bf5542..0000000000 --- a/packages/cookbook/accounts/access-keys/create-function-access-key.js +++ /dev/null @@ -1,30 +0,0 @@ -const { KeyPair, keyStores, connect } = require("near-api-js"); -const path = require("path"); -const homedir = require("os").homedir(); - -const CREDENTIALS_DIR = ".near-credentials"; -const ACCOUNT_ID = "example.testnet"; -const credentialsPath = path.join(homedir, CREDENTIALS_DIR); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -const config = { - keyStore, - networkId: "testnet", - nodeUrl: "https://rpc.testnet.near.org", -}; - -addFunctionAccessKey(ACCOUNT_ID); - -async function addFunctionAccessKey(accountId) { - const keyPair = KeyPair.fromRandom("ed25519"); - const publicKey = keyPair.publicKey.toString(); - const near = await connect(config); - const account = await near.account(accountId); - await keyStore.setKey(config.networkId, publicKey, keyPair); - await account.addKey( - publicKey, // public key for new account - "example-account.testnet", // contract this key is allowed to call (optional) - "example_method", // methods this key is allowed to call (optional) - "2500000000000" // allowance key can use to call methods (optional) - ); -} diff --git a/packages/cookbook/accounts/access-keys/create-function-access-key.ts b/packages/cookbook/accounts/access-keys/create-function-access-key.ts new file mode 100644 index 0000000000..db53523611 --- /dev/null +++ b/packages/cookbook/accounts/access-keys/create-function-access-key.ts @@ -0,0 +1,48 @@ +import { + addFunctionCallAccessKey, + generateRandomKeyPair, + getPlaintextFilesystemSigner, + getTestnetRpcProvider, +} from '@near-js/client'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +const CONTRACT_NAME = 'example-account.testnet'; +const METHOD_NAMES = ['example_method']; + +export default async function createFunctionCallAccessKey(accountId: string, contract = CONTRACT_NAME, methods = METHOD_NAMES) { + if (!accountId) { + console.log(chalk`{red pnpm createFunctionCallAccessKey -- ACCOUNT_ID}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for ACCOUNT_ID + const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + + // create a new key from random data + const keyPair = generateRandomKeyPair('ed25519'); + + // add the generated key to the account as a Function Call Access Key + await addFunctionCallAccessKey({ + account: accountId, + publicKey: keyPair.getPublicKey().toString(), + contract, + methodNames: methods, + allowance: 2500000000000n, + deps: { + rpcProvider, + signer, + }, + }); + + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.green RESULTS} {white Added new function call access key}` ); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.white New Key} {white |} {bold.yellow ${keyPair.getPublicKey().toString()}} {white |} {bold.yellow ${contract}} {white |} {bold.yellow ${methods}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); +} diff --git a/packages/cookbook/accounts/access-keys/delete-access-key.js b/packages/cookbook/accounts/access-keys/delete-access-key.js deleted file mode 100644 index 7cebf9d1c1..0000000000 --- a/packages/cookbook/accounts/access-keys/delete-access-key.js +++ /dev/null @@ -1,25 +0,0 @@ -const { keyStores, connect } = require("near-api-js"); -const path = require("path"); -const homedir = require("os").homedir(); - -const CREDENTIALS_DIR = ".near-credentials"; -// NOTE: replace "example.testnet" with your accountId -const ACCOUNT_ID = "example.testnet"; -// NOTE: replace this PK with the one that you are trying to delete -const PUBLIC_KEY = "ed25519:4yLYjwC3Rd8EkhKwVPoAdy6EUcCVSz2ZixFtsCeuBEZD"; -const credentialsPath = path.join(homedir, CREDENTIALS_DIR); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -const config = { - keyStore, - networkId: "testnet", - nodeUrl: "https://rpc.testnet.near.org", -}; - -deleteAccessKey(ACCOUNT_ID, PUBLIC_KEY); - -async function deleteAccessKey(accountId, publicKey) { - const near = await connect(config); - const account = await near.account(accountId); - await account.deleteKey(publicKey); -} diff --git a/packages/cookbook/accounts/access-keys/delete-access-key.ts b/packages/cookbook/accounts/access-keys/delete-access-key.ts new file mode 100644 index 0000000000..ba7b2e884e --- /dev/null +++ b/packages/cookbook/accounts/access-keys/delete-access-key.ts @@ -0,0 +1,42 @@ +import { + deleteAccessKey, + getPlaintextFilesystemSigner, + getTestnetRpcProvider, + parseKeyPair, +} from '@near-js/client'; +import { type KeyPairString } from '@near-js/crypto'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +export default async function deleteAccessKeyCookbook(accountId: string, publicKey: string) { + if (!accountId) { + console.log(chalk`{red pnpm deleteAccessKey -- ACCOUNT_ID [PUBLIC_KEY]}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for ACCOUNT_ID + const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + + // parse the target key from its string representation + const keyPair = parseKeyPair(publicKey as KeyPairString); + + // add the generated key to the account as a Function Call Access Key + await deleteAccessKey({ + account: accountId, + publicKey: keyPair.getPublicKey().toString(), + deps: { + rpcProvider, + signer, + }, + }); + + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.green RESULTS} {white Deleted access key}` ); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.white Deleted Key} {white |} {bold.yellow ${keyPair.getPublicKey().toString()}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); +} diff --git a/packages/cookbook/accounts/create-funded-testnet-account.ts b/packages/cookbook/accounts/create-funded-testnet-account.ts new file mode 100644 index 0000000000..7f5742ce69 --- /dev/null +++ b/packages/cookbook/accounts/create-funded-testnet-account.ts @@ -0,0 +1,35 @@ +import { + createFundedTestnetAccount, + generateRandomKeyPair, + getPlaintextFilesystemSigner, +} from '@near-js/client'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +export default async function createFundedTestnetAccountCookbook(accountId: string) { + if (!accountId) { + console.log(chalk`{red pnpm createFundedTestnetAccount -- ACCOUNT_ID}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + // initialize the transaction signer using a pre-existing key for ACCOUNT_ID + const { keystore } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + + // create new keypair and persist it to filesystem keystore + const keyPair = generateRandomKeyPair('ed25519'); + await keystore.setKey('testnet', accountId, keyPair); + + // call funded testnet creation endpoint + await createFundedTestnetAccount({ + newAccount: accountId, + newPublicKey: keyPair.getPublicKey().toString(), + }); + + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.green RESULTS} {white Created funded testnet account}` ); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.white New Account} {white |} {bold.yellow ${accountId}} {white |} {bold.yellow ${keyPair.getPublicKey().toString()}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); +} diff --git a/packages/cookbook/accounts/create-mainnet-account.js b/packages/cookbook/accounts/create-mainnet-account.js deleted file mode 100644 index ddbcfa586e..0000000000 --- a/packages/cookbook/accounts/create-mainnet-account.js +++ /dev/null @@ -1,44 +0,0 @@ -const HELP = `Please run this script in the following format: - - node create-mainnet-account.js CREATOR_ACCOUNT.near NEW_ACCOUNT.near AMOUNT" -`; - -const { connect, KeyPair, keyStores, utils } = require("near-api-js"); -const path = require("path"); -const homedir = require("os").homedir(); - -const CREDENTIALS_DIR = ".near-credentials"; -const credentialsPath = path.join(homedir, CREDENTIALS_DIR); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -const config = { - keyStore, - networkId: "mainnet", - nodeUrl: "https://rpc.mainnet.near.org", -}; - -if (process.argv.length !== 5) { - console.info(HELP); - process.exit(1); -} - -createAccount(process.argv[2], process.argv[3], process.argv[4]); - -async function createAccount(creatorAccountId, newAccountId, amount) { - const near = await connect(config); - const creatorAccount = await near.account(creatorAccountId); - const keyPair = KeyPair.fromRandom("ed25519"); - const publicKey = keyPair.publicKey.toString(); - await keyStore.setKey(config.networkId, newAccountId, keyPair); - - return await creatorAccount.functionCall({ - contractId: "near", - methodName: "create_account", - args: { - new_account_id: newAccountId, - new_public_key: publicKey, - }, - gas: "300000000000000", - attachedDeposit: utils.format.parseNearAmount(amount), - }); -} diff --git a/packages/cookbook/accounts/create-mainnet-account.ts b/packages/cookbook/accounts/create-mainnet-account.ts new file mode 100644 index 0000000000..6dabaa169e --- /dev/null +++ b/packages/cookbook/accounts/create-mainnet-account.ts @@ -0,0 +1,48 @@ +import { + createTopLevelAccount, + generateRandomKeyPair, + getMainnetRpcProvider, + getPlaintextFilesystemSigner, +} from '@near-js/client'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +export default async function createMainnetAccountCookbook(accountId: string, newAccountId?: string) { + if (!accountId) { + console.log(chalk`{red pnpm createMainnetAccount -- CREATOR_ACCOUNT_ID [NEW_ACCOUNT_ID]}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + // initialize testnet RPC provider + const rpcProvider = getMainnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const { keystore, signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + + // create a new key from random data + const keyPair = generateRandomKeyPair('ed25519'); + + const newAccount = newAccountId || `${new Date().valueOf()}-${Math.ceil(Math.random() * 10e12)}.near`; + + await createTopLevelAccount({ + account: accountId, + contract: 'mainnet', + initialBalance: 100n, + newAccount, + newPublicKey: keyPair.getPublicKey().toString(), + deps: { + rpcProvider, + signer, + }, + }); + + // save new account in plaintext filesystem keystore + await keystore.setKey('mainnet', accountId, keyPair); + + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.green RESULTS} {white Created mainnet account}` ); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.white New Account} {white |} {bold.yellow ${accountId}} {white |} {bold.yellow ${keyPair.getPublicKey().toString()}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); +} diff --git a/packages/cookbook/accounts/create-testnet-account.js b/packages/cookbook/accounts/create-testnet-account.js deleted file mode 100644 index 1a2c5f1b15..0000000000 --- a/packages/cookbook/accounts/create-testnet-account.js +++ /dev/null @@ -1,44 +0,0 @@ -const HELP = `Please run this script in the following format: - - node create-testnet-account.js CREATOR_ACCOUNT.testnet NEW_ACCOUNT.testnet AMOUNT -`; - -const { connect, KeyPair, keyStores, utils } = require("near-api-js"); -const path = require("path"); -const homedir = require("os").homedir(); - -const CREDENTIALS_DIR = ".near-credentials"; -const credentialsPath = path.join(homedir, CREDENTIALS_DIR); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -const config = { - keyStore, - networkId: "testnet", - nodeUrl: "https://rpc.testnet.near.org", -}; - -if (process.argv.length !== 5) { - console.info(HELP); - process.exit(1); -} - -createAccount(process.argv[2], process.argv[3], process.argv[4]); - -async function createAccount(creatorAccountId, newAccountId, amount) { - const near = await connect({ ...config, keyStore }); - const creatorAccount = await near.account(creatorAccountId); - const keyPair = KeyPair.fromRandom("ed25519"); - const publicKey = keyPair.publicKey.toString(); - await keyStore.setKey(config.networkId, newAccountId, keyPair); - - return await creatorAccount.functionCall({ - contractId: "testnet", - methodName: "create_account", - args: { - new_account_id: newAccountId, - new_public_key: publicKey, - }, - gas: "300000000000000", - attachedDeposit: utils.format.parseNearAmount(amount), - }); -} diff --git a/packages/cookbook/accounts/create-testnet-account.ts b/packages/cookbook/accounts/create-testnet-account.ts new file mode 100644 index 0000000000..833aa7579e --- /dev/null +++ b/packages/cookbook/accounts/create-testnet-account.ts @@ -0,0 +1,48 @@ +import { + createTopLevelAccount, + generateRandomKeyPair, + getPlaintextFilesystemSigner, + getTestnetRpcProvider, +} from '@near-js/client'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +export default async function createTestnetAccountCookbook(accountId: string, newAccountId?: string) { + if (!accountId) { + console.log(chalk`{red pnpm createTestnetAccount -- CREATOR_ACCOUNT_ID [NEW_ACCOUNT_ID]}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const { keystore, signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + + // create a new key from random data + const keyPair = generateRandomKeyPair('ed25519'); + + const newAccount = newAccountId || `${new Date().valueOf()}-${Math.ceil(Math.random() * 10e12)}.testnet`; + + await createTopLevelAccount({ + account: accountId, + contract: 'testnet', + initialBalance: 100n, + newAccount, + newPublicKey: keyPair.getPublicKey().toString(), + deps: { + rpcProvider, + signer, + }, + }); + + // save new account in plaintext filesystem keystore + await keystore.setKey('testnet', accountId, keyPair); + + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.green RESULTS} {white Created testnet account}` ); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.white New Account} {white |} {bold.yellow ${accountId}} {white |} {bold.yellow ${keyPair.getPublicKey().toString()}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); +} diff --git a/packages/cookbook/accounts/index.ts b/packages/cookbook/accounts/index.ts new file mode 100644 index 0000000000..5f40f3dcde --- /dev/null +++ b/packages/cookbook/accounts/index.ts @@ -0,0 +1,6 @@ +export * from './access-keys/create-full-access-key'; +export * from './access-keys/create-function-access-key'; +export * from './access-keys/delete-access-key'; +export * from './create-funded-testnet-account'; +export * from './create-mainnet-account'; +export * from './create-testnet-account'; \ No newline at end of file diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index cb8a6806de..627410e375 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -3,17 +3,37 @@ "private": true, "version": "1.0.22", "description": "", - "main": "main.js", + "main": "src/index.ts", + "type": "module", "author": "", "license": "ISC", - "dependencies": { + "scripts": { + "build": "pnpm compile:esm && pnpm compile:cjs", + "compile:esm": "tsc -p tsconfig.json", + "compile:cjs": "tsc -p tsconfig.cjs.json && cjsify ./lib/commonjs", + "lint": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc", + "lint:fix": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc --fix", + "addFullAccessKey": "tsx -e \"import f from './accounts/access-keys/create-full-access-key.ts'; f(...process.argv.slice(1));\"", + "addFunctionCallAccessKey": "tsx -e \"import f from './accounts/access-keys/create-function-access-key.ts'; f(...process.argv.slice(1));\"", + "calculateGas": "tsx -e \"import f from './utils/calculate-gas.ts'; f(...process.argv.slice(1));\"", + "createFundedTestnetAccount": "tsx -e \"import f from './accounts/create-funded-testnet-account.ts'; f(...process.argv.slice(1));\"", + "createMainnetAccount": "tsx -e \"import f from './accounts/create-mainnet-account.ts'; f(...process.argv.slice(1));\"", + "createTestnetAccount": "tsx -e \"import f from './accounts/create-testnet-account.ts'; f(...process.argv.slice(1));\"", + "deleteAccessKey": "tsx -e \"import f from './accounts/access-keys/delete-access-key.ts'; f(...process.argv.slice(1));\"" + }, + "devDependencies": { "@near-js/accounts": "workspace:*", + "@near-js/client": "workspace:*", + "@near-js/crypto": "workspace:*", "@near-js/keystores-node": "workspace:*", "@near-js/providers": "workspace:*", "@near-js/signers": "workspace:*", "@near-js/transactions": "workspace:*", + "build": "workspace:*", "chalk": "4.1.1", "homedir": "0.6.0", - "near-api-js": "workspace:*" + "ts-node": "^10.9.2", + "tsconfig": "workspace:*", + "typescript": "5.4.5" } } diff --git a/packages/cookbook/src/index.ts b/packages/cookbook/src/index.ts new file mode 100644 index 0000000000..b138a26530 --- /dev/null +++ b/packages/cookbook/src/index.ts @@ -0,0 +1,2 @@ +export * from '../accounts'; +export * from '../utils'; diff --git a/packages/cookbook/tsconfig.cjs.json b/packages/cookbook/tsconfig.cjs.json deleted file mode 100644 index 83abd57c4f..0000000000 --- a/packages/cookbook/tsconfig.cjs.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "tsconfig/cjs.json", - "compilerOptions": { - "outDir": "./lib/commonjs", - "lib": ["es2022", "dom"] - }, - "files": [ - "src/index.ts" - ] -} diff --git a/packages/cookbook/tsconfig.json b/packages/cookbook/tsconfig.json index cddbc6a829..75b9dc47ae 100644 --- a/packages/cookbook/tsconfig.json +++ b/packages/cookbook/tsconfig.json @@ -1,11 +1,9 @@ { + "extends": "tsconfig/esm.json", "compilerOptions": { - "module": "commonjs", - "allowJs": true, - "outDir": "./dist", - }, - "include": [ - "**/*.js", - "**/*.ts" + "outDir": "./lib/esm", + }, + "files": [ + "src/index.ts" ] } \ No newline at end of file diff --git a/packages/cookbook/utils/calculate-gas.js b/packages/cookbook/utils/calculate-gas.js deleted file mode 100644 index 438916f8a8..0000000000 --- a/packages/cookbook/utils/calculate-gas.js +++ /dev/null @@ -1,65 +0,0 @@ -const { connect, keyStores, utils } = require("near-api-js"); -const path = require("path"); -const homedir = require("os").homedir(); -const chalk = require("chalk"); - -const CREDENTIALS_DIR = ".near-credentials"; -const ACCOUNT_ID = "near-example.testnet"; -const CONTRACT_ID = "guest-book.testnet"; -const METHOD_NAME = "addMessage"; -const MAX_GAS = "300000000000000"; -const ATTACHED_DEPOSIT = "0"; - -const args = { - text: "Howdy!", -}; - -const credentialsPath = path.join(homedir, CREDENTIALS_DIR); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -const config = { - keyStore, - networkId: "testnet", - nodeUrl: "https://rpc.testnet.near.org", -}; - -calculateGas(CONTRACT_ID, METHOD_NAME, args, ATTACHED_DEPOSIT); - -async function calculateGas(contractId, methodName, args, depositAmount) { - const near = await connect(config); - const account = await near.account(ACCOUNT_ID); - const result = await account.functionCall({ - contractId, - methodName, - args, - gas: MAX_GAS, - attachedDeposit: utils.format.parseNearAmount(depositAmount), - }); - const { totalGasBurned, totalTokensBurned } = result.receipts_outcome.reduce( - (acc, receipt) => { - acc.totalGasBurned += receipt.outcome.gas_burnt; - acc.totalTokensBurned += utils.format.formatNearAmount( - receipt.outcome.tokens_burnt - ); - return acc; - }, - { - totalGasBurned: result.transaction_outcome.outcome.gas_burnt, - totalTokensBurned: utils.format.formatNearAmount( - result.transaction_outcome.outcome.tokens_burnt - ), - } - ); - - console.log(chalk`{white ------------------------------------------------------------------------ }`) - console.log(chalk`{bold.green RESULTS} {white for: [ {bold.blue ${METHOD_NAME}} ] called on contract: [ {bold.blue ${CONTRACT_ID}} ]}` ) - console.log(chalk`{white ------------------------------------------------------------------------ }`) - console.log(chalk`{bold.white Gas Burnt} {white |} {bold.yellow ${totalGasBurned}}`); - console.log(chalk`{bold.white Tokens Burnt} {white |} {bold.yellow ${totalTokensBurned}}`); - console.log(chalk`{white ------------------------------------------------------------------------ }`) - - return { - totalTokensBurned, - totalGasBurned, - }; -} diff --git a/packages/cookbook/utils/calculate-gas.ts b/packages/cookbook/utils/calculate-gas.ts new file mode 100644 index 0000000000..647e3d985e --- /dev/null +++ b/packages/cookbook/utils/calculate-gas.ts @@ -0,0 +1,65 @@ +import { + formatNearAmount, + functionCall, + getPlaintextFilesystemSigner, + getTestnetRpcProvider, + MAX_GAS, +} from '@near-js/client'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +const CONTRACT_ID = "guest-book.testnet"; +const METHOD_NAME = "addMessage"; + +export default async function calculateGas(accountId: string, method = METHOD_NAME, contract = CONTRACT_ID) { + if (!accountId) { + console.log(chalk`{red pnpm calculateGas -- ACCOUNT_ID}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + + const { + outcome: { + receipts_outcome: receiptsOutcome, + transaction_outcome: { outcome: transactionOutcome }, + }, + } = await functionCall({ + sender: accountId, + receiver: contract, + method, + args: { + text: 'Howdy!', + }, + gas: MAX_GAS, + deps: { + rpcProvider, + signer, + }, + }); + + const { totalGasBurned, totalTokensBurned } = receiptsOutcome.reduce( + (acc, receipt) => { + acc.totalGasBurned += receipt.outcome.gas_burnt; + acc.totalTokensBurned += BigInt(receipt.outcome.tokens_burnt); + return acc; + }, + { + totalGasBurned: transactionOutcome.gas_burnt, + totalTokensBurned: BigInt(transactionOutcome.tokens_burnt), + } + ); + + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.green RESULTS} {white for: [ {bold.blue ${method}} ] called on contract: [ {bold.blue ${contract}} ]}` ); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.white Gas Burnt} {white |} {bold.yellow ${formatNearAmount(totalGasBurned.toString())}}`); + console.log(chalk`{bold.white Tokens Burnt} {white |} {bold.yellow ${formatNearAmount(totalTokensBurned.toString())}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); +} diff --git a/packages/cookbook/utils/index.ts b/packages/cookbook/utils/index.ts new file mode 100644 index 0000000000..e28a85025e --- /dev/null +++ b/packages/cookbook/utils/index.ts @@ -0,0 +1 @@ +export * from './calculate-gas'; diff --git a/packages/crypto/src/public_key.ts b/packages/crypto/src/public_key.ts index fbb5c89c35..349175f26c 100644 --- a/packages/crypto/src/public_key.ts +++ b/packages/crypto/src/public_key.ts @@ -58,6 +58,7 @@ abstract class Enum { /** * PublicKey representation that has type and bytes of the key. */ + export class PublicKey extends Enum { enum: string; ed25519Key?: ED25519PublicKey; diff --git a/packages/providers/src/fetch_json.ts b/packages/providers/src/fetch_json.ts index fe83961208..9701aca52d 100644 --- a/packages/providers/src/fetch_json.ts +++ b/packages/providers/src/fetch_json.ts @@ -13,7 +13,7 @@ const retryConfig = { return true; } - if (['FetchError', 'Failed to fetch'].includes(e.toString())) { + if (e.toString().includes('FetchError') || e.toString().includes('Failed to fetch')) { return true; } diff --git a/packages/utils/src/provider.ts b/packages/utils/src/provider.ts index 24530eda52..56a270548c 100644 --- a/packages/utils/src/provider.ts +++ b/packages/utils/src/provider.ts @@ -1,7 +1,7 @@ import { FinalExecutionOutcome } from '@near-js/types'; /** @hidden */ -export function getTransactionLastResult(txResult: FinalExecutionOutcome): any { +export function getTransactionLastResult(txResult: FinalExecutionOutcome): Exclude { if (typeof txResult.status === 'object' && typeof txResult.status.SuccessValue === 'string') { const value = Buffer.from(txResult.status.SuccessValue, 'base64').toString(); try { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a47109098c..e7322dabe9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -238,10 +238,16 @@ importers: version: 5.4.5 packages/cookbook: - dependencies: + devDependencies: '@near-js/accounts': specifier: workspace:* version: link:../accounts + '@near-js/client': + specifier: workspace:* + version: link:../client + '@near-js/crypto': + specifier: workspace:* + version: link:../crypto '@near-js/keystores-node': specifier: workspace:* version: link:../keystores-node @@ -254,15 +260,24 @@ importers: '@near-js/transactions': specifier: workspace:* version: link:../transactions + build: + specifier: workspace:* + version: link:../build chalk: specifier: 4.1.1 version: 4.1.1 homedir: specifier: 0.6.0 version: 0.6.0 - near-api-js: - specifier: 4.0.0 - version: 4.0.0(encoding@0.1.13) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@20.5.1)(typescript@5.4.5) + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: 5.4.5 + version: 5.4.5 packages/crypto: dependencies: @@ -4572,7 +4587,6 @@ snapshots: '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - optional: true '@eslint-community/eslint-utils@4.4.0(eslint@8.20.0)': dependencies: @@ -4887,7 +4901,6 @@ snapshots: dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - optional: true '@manypkg/find-root@1.1.0': dependencies: @@ -5053,17 +5066,13 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@tsconfig/node10@1.0.11': - optional: true + '@tsconfig/node10@1.0.11': {} - '@tsconfig/node12@1.0.11': - optional: true + '@tsconfig/node12@1.0.11': {} - '@tsconfig/node14@1.0.3': - optional: true + '@tsconfig/node14@1.0.3': {} - '@tsconfig/node16@1.0.4': - optional: true + '@tsconfig/node16@1.0.4': {} '@types/babel__core@7.20.5': dependencies: @@ -5251,7 +5260,6 @@ snapshots: acorn-walk@8.3.3: dependencies: acorn: 8.12.0 - optional: true acorn@8.12.0: {} @@ -5298,8 +5306,7 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - arg@4.1.3: - optional: true + arg@4.1.3: {} argparse@1.0.10: dependencies: @@ -5702,8 +5709,7 @@ snapshots: - supports-color - ts-node - create-require@1.1.1: - optional: true + create-require@1.1.1: {} cross-spawn@5.1.0: dependencies: @@ -5808,8 +5814,7 @@ snapshots: diff-sequences@29.6.3: {} - diff@4.0.2: - optional: true + diff@4.0.2: {} dir-glob@3.0.1: dependencies: @@ -8089,7 +8094,6 @@ snapshots: typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - optional: true tslib@2.6.3: {} @@ -8248,8 +8252,7 @@ snapshots: uuid@8.3.2: optional: true - v8-compile-cache-lib@3.0.1: - optional: true + v8-compile-cache-lib@3.0.1: {} v8-compile-cache@2.4.0: {} @@ -8394,8 +8397,7 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yn@3.1.1: - optional: true + yn@3.1.1: {} yocto-queue@0.1.0: {} From 97bae8eb063823d5ca325ddda8e7f9cb6f3292a0 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 11 Sep 2024 21:12:24 -0700 Subject: [PATCH 22/55] chore: fix cookbook lint scripts --- packages/cookbook/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index 627410e375..7fd85b3853 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -11,8 +11,8 @@ "build": "pnpm compile:esm && pnpm compile:cjs", "compile:esm": "tsc -p tsconfig.json", "compile:cjs": "tsc -p tsconfig.cjs.json && cjsify ./lib/commonjs", - "lint": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc", - "lint:fix": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts test/**/*.ts --no-eslintrc --fix", + "lint": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts --no-eslintrc", + "lint:fix": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts --no-eslintrc --fix", "addFullAccessKey": "tsx -e \"import f from './accounts/access-keys/create-full-access-key.ts'; f(...process.argv.slice(1));\"", "addFunctionCallAccessKey": "tsx -e \"import f from './accounts/access-keys/create-function-access-key.ts'; f(...process.argv.slice(1));\"", "calculateGas": "tsx -e \"import f from './utils/calculate-gas.ts'; f(...process.argv.slice(1));\"", From 4c310a0fa49822d88f76f7dbd67ec89d963db47a Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 11 Sep 2024 21:22:37 -0700 Subject: [PATCH 23/55] fix: simplify scripts --- packages/cookbook/package.json | 4 +--- packages/cookbook/tsconfig.json | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index 7fd85b3853..40feca766b 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -8,9 +8,7 @@ "author": "", "license": "ISC", "scripts": { - "build": "pnpm compile:esm && pnpm compile:cjs", - "compile:esm": "tsc -p tsconfig.json", - "compile:cjs": "tsc -p tsconfig.cjs.json && cjsify ./lib/commonjs", + "build": "tsc -p tsconfig.json", "lint": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts --no-eslintrc", "lint:fix": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts --no-eslintrc --fix", "addFullAccessKey": "tsx -e \"import f from './accounts/access-keys/create-full-access-key.ts'; f(...process.argv.slice(1));\"", diff --git a/packages/cookbook/tsconfig.json b/packages/cookbook/tsconfig.json index 75b9dc47ae..d9964a7efc 100644 --- a/packages/cookbook/tsconfig.json +++ b/packages/cookbook/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "tsconfig/esm.json", "compilerOptions": { - "outDir": "./lib/esm", + "noEmit": true }, "files": [ "src/index.ts" From 843de04aad3412bdb7204311c7d22c9fc43861ec Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 12 Sep 2024 15:50:21 -0700 Subject: [PATCH 24/55] feat: more cookbook examples, docs --- .../client/src/transactions/sign_and_send.ts | 26 ++++++- packages/cookbook/package.json | 6 +- .../cookbook/utils/check-account-existence.js | 28 -------- .../cookbook/utils/check-account-existence.ts | 28 ++++++++ packages/cookbook/utils/deploy-contract.js | 25 ------- packages/cookbook/utils/deploy-contract.ts | 40 +++++++++++ packages/cookbook/utils/get-state.js | 23 ------- packages/cookbook/utils/get-state.ts | 15 +++++ packages/cookbook/utils/wrap-near.js | 67 ------------------- packages/cookbook/utils/wrap-near.ts | 63 +++++++++++++++++ 10 files changed, 176 insertions(+), 145 deletions(-) delete mode 100644 packages/cookbook/utils/check-account-existence.js create mode 100644 packages/cookbook/utils/check-account-existence.ts delete mode 100644 packages/cookbook/utils/deploy-contract.js create mode 100644 packages/cookbook/utils/deploy-contract.ts delete mode 100644 packages/cookbook/utils/get-state.js create mode 100644 packages/cookbook/utils/get-state.ts delete mode 100644 packages/cookbook/utils/wrap-near.js create mode 100644 packages/cookbook/utils/wrap-near.ts diff --git a/packages/client/src/transactions/sign_and_send.ts b/packages/client/src/transactions/sign_and_send.ts index 7e8a835bd4..e74c8eeab6 100644 --- a/packages/client/src/transactions/sign_and_send.ts +++ b/packages/client/src/transactions/sign_and_send.ts @@ -10,6 +10,11 @@ import { BlockReference } from '@near-js/types'; const DEFAULT_FINALITY: BlockReference = { finality: 'final' }; +/** + * Sign a transaction, returning the signed transaction and encoded hash + * @param transaction Transaction instance + * @param signer MessageSigner + */ export async function signTransaction({ transaction, deps: { signer } }: SignTransactionParams) { const encodedTx = transaction.encode(); const signedTransaction = new SignedTransaction({ @@ -26,6 +31,11 @@ export async function signTransaction({ transaction, deps: { signer } }: SignTra }; } +/** + * Sign a transaction and publish to RPC + * @param transaction Transaction instance to sign and publish + * @param deps sign-and-send dependencies + */ export async function signAndSendTransaction({ transaction, deps: { rpcProvider, signer } }: SignAndSendTransactionParams) { const { signedTransaction } = await signTransaction({ transaction, deps: { signer } }); const outcome = await rpcProvider.sendTransaction(signedTransaction); @@ -35,6 +45,13 @@ export async function signAndSendTransaction({ transaction, deps: { rpcProvider, }; } +/** + * Get the current nonce for an access key given an account and MessageSigner instance + * @param account owner of the access key + * @param blockReference block ID/finality + * @param rpcProvider + * @param deps sign-and-send dependencies + */ export async function getSignerNonce({ account, blockReference = DEFAULT_FINALITY, deps: { rpcProvider, signer } }) { return getNonce({ account, @@ -44,7 +61,14 @@ export async function getSignerNonce({ account, blockReference = DEFAULT_FINALIT }); } -// this might be more natural as a method on TransactionComposer but would be a major increase in scope +/** + * Sign and send a transaction given a TransactionComposer instance. + * Derive values for other transaction fields (public key, nonce, block header) from MessageSigner dependency. + * NB - this might be more natural as a method on TransactionComposer but would be a major increase in scope. + * @param composer Transaction Composer instance with values for sender, receiver, and actions + * @param blockReference block ID/finality + * @param deps sign-and-send dependencies + */ export async function signAndSendFromComposer({ composer, blockReference = DEFAULT_FINALITY, deps }: SignAndSendComposerParams) { const { rpcProvider, signer } = deps; const block = await getBlock({ blockReference, deps: { rpcProvider } }); diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index 40feca766b..d50853da97 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -14,10 +14,14 @@ "addFullAccessKey": "tsx -e \"import f from './accounts/access-keys/create-full-access-key.ts'; f(...process.argv.slice(1));\"", "addFunctionCallAccessKey": "tsx -e \"import f from './accounts/access-keys/create-function-access-key.ts'; f(...process.argv.slice(1));\"", "calculateGas": "tsx -e \"import f from './utils/calculate-gas.ts'; f(...process.argv.slice(1));\"", + "checkAccountExists": "tsx -e \"import f from './utils/check-account-existence.ts'; f(...process.argv.slice(1));\"", "createFundedTestnetAccount": "tsx -e \"import f from './accounts/create-funded-testnet-account.ts'; f(...process.argv.slice(1));\"", "createMainnetAccount": "tsx -e \"import f from './accounts/create-mainnet-account.ts'; f(...process.argv.slice(1));\"", "createTestnetAccount": "tsx -e \"import f from './accounts/create-testnet-account.ts'; f(...process.argv.slice(1));\"", - "deleteAccessKey": "tsx -e \"import f from './accounts/access-keys/delete-access-key.ts'; f(...process.argv.slice(1));\"" + "deleteAccessKey": "tsx -e \"import f from './accounts/access-keys/delete-access-key.ts'; f(...process.argv.slice(1));\"", + "deployContract": "tsx -e \"import f from './utils/deploy-contract.ts'; f(...process.argv.slice(1));\"", + "getState": "tsx -e \"import f from './utils/get-state.ts'; f(...process.argv.slice(1));\"", + "wrapNear": "tsx -e \"import f from './utils/wrap-near.ts'; f(...process.argv.slice(1));\"" }, "devDependencies": { "@near-js/accounts": "workspace:*", diff --git a/packages/cookbook/utils/check-account-existence.js b/packages/cookbook/utils/check-account-existence.js deleted file mode 100644 index ded71038b6..0000000000 --- a/packages/cookbook/utils/check-account-existence.js +++ /dev/null @@ -1,28 +0,0 @@ -// Demonstrates checking if an account exists -const { providers } = require("near-api-js"); - -const provider = new providers.JsonRpcProvider( - "https://archival-rpc.testnet.near.org" -); - -async function accountExists() { - let rawResult; - - for (const account_id of ["does-not-exist.mike.testnet", "mike.testnet"]) { - let succeeded = true; - try { - rawResult = await provider.query({ - request_type: "view_account", - account_id: account_id, - finality: "final", - }); - } catch (e) { - if (e.type === 'AccountDoesNotExist') { - succeeded = false; - } - } - console.log(succeeded ? `The account ${account_id} exists.` : `There is no account ${account_id}.`) - } -} - -accountExists(); diff --git a/packages/cookbook/utils/check-account-existence.ts b/packages/cookbook/utils/check-account-existence.ts new file mode 100644 index 0000000000..0cad0ab3ba --- /dev/null +++ b/packages/cookbook/utils/check-account-existence.ts @@ -0,0 +1,28 @@ +import { + getAccountState, + getTestnetRpcProvider, +} from '@near-js/client'; + +export default async function accountExists() { + const isRegisteredAccount = async (account: string) => { + try { + await getAccountState({ + account, + deps: { + rpcProvider: getTestnetRpcProvider(), + }, + }); + } catch (e) { + if (e.type === 'AccountDoesNotExist') { + return false; + } + } + + return true; + }; + + for (const account of ['does-not-exist.mike.testnet', 'mike.testnet']) { + const succeeded = await isRegisteredAccount(account); + console.log(succeeded ? `The account ${account} exists.` : `There is no account ${account}.`); + } +} diff --git a/packages/cookbook/utils/deploy-contract.js b/packages/cookbook/utils/deploy-contract.js deleted file mode 100644 index 9674b689de..0000000000 --- a/packages/cookbook/utils/deploy-contract.js +++ /dev/null @@ -1,25 +0,0 @@ -const { keyStores, connect } = require("near-api-js"); -const fs = require("fs"); -const path = require("path"); -const homedir = require("os").homedir(); - -const CREDENTIALS_DIR = ".near-credentials"; -const ACCOUNT_ID = "near-example.testnet"; -const WASM_PATH = path.join(__dirname, "/wasm-files/status_message.wasm"); -const credentialsPath = path.join(homedir, CREDENTIALS_DIR); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -const config = { - keyStore, - networkId: "testnet", - nodeUrl: "https://rpc.testnet.near.org", -}; - -deployContract(ACCOUNT_ID, WASM_PATH); - -async function deployContract(accountId, wasmPath) { - const near = await connect(config); - const account = await near.account(accountId); - const result = await account.deployContract(fs.readFileSync(wasmPath)); - console.log(result); -} diff --git a/packages/cookbook/utils/deploy-contract.ts b/packages/cookbook/utils/deploy-contract.ts new file mode 100644 index 0000000000..7030632073 --- /dev/null +++ b/packages/cookbook/utils/deploy-contract.ts @@ -0,0 +1,40 @@ +import { + deployContract, + getPlaintextFilesystemSigner, + getTestnetRpcProvider, +} from '@near-js/client'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; +import * as fs from 'node:fs/promises'; + +const WASM_PATH = join(__dirname, '/wasm-files/status_message.wasm'); + +export default async function deployContractCookbook(accountId: string, wasmPath: string = WASM_PATH) { + if (!accountId) { + console.log(chalk`{red pnpm deployContract -- ACCOUNT_ID [WASM_PATH]}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + + await deployContract({ + account: accountId, + code: await fs.readFile(wasmPath), + deps: { + rpcProvider, + signer, + }, + }); + + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.green RESULTS} {white Deployed contract}` ); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.white Contract Deployed} {white |} {bold.yellow WASM at ${wasmPath} deployed to ${accountId}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); +} diff --git a/packages/cookbook/utils/get-state.js b/packages/cookbook/utils/get-state.js deleted file mode 100644 index 557e0091a0..0000000000 --- a/packages/cookbook/utils/get-state.js +++ /dev/null @@ -1,23 +0,0 @@ -// demonstrates how to query the state without setting -// up an account. (View methods only) -const { providers } = require("near-api-js"); -//network config (replace testnet with mainnet or betanet) -const provider = new providers.JsonRpcProvider( - "https://rpc.testnet.near.org" -); - -getState(); - -async function getState() { - const rawResult = await provider.query({ - request_type: "call_function", - account_id: "guest-book.testnet", - method_name: "getMessages", - args_base64: "e30=", - finality: "optimistic", - }); - - // format result - const res = JSON.parse(Buffer.from(rawResult.result).toString()); - console.log(res); -} diff --git a/packages/cookbook/utils/get-state.ts b/packages/cookbook/utils/get-state.ts new file mode 100644 index 0000000000..28e9097a80 --- /dev/null +++ b/packages/cookbook/utils/get-state.ts @@ -0,0 +1,15 @@ +import { getTestnetRpcProvider, view } from '@near-js/client'; + +export default async function getState() { + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + + // call view method without parameters + const result = await view({ + account: 'guest-book.testnet', + method: 'getMessages', + deps: { rpcProvider }, + }); + + console.log(result); +} diff --git a/packages/cookbook/utils/wrap-near.js b/packages/cookbook/utils/wrap-near.js deleted file mode 100644 index 1dc5edbfea..0000000000 --- a/packages/cookbook/utils/wrap-near.js +++ /dev/null @@ -1,67 +0,0 @@ -const HELP = `To convert N $NEAR to wNEAR, run this script in the following format: - - node wrap-near.js YOU.near N - -`; - -const { connect, keyStores, transactions, utils } = require("near-api-js"); -const path = require("path"); -const homedir = require("os").homedir(); - -// On mainnet it's wrap.near, by the way -const WRAP_NEAR_CONTRACT_ID = "wrap.testnet"; - -const credentialsPath = path.join(homedir, ".near-credentials"); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -const config = { - keyStore, - networkId: "testnet", - nodeUrl: "https://rpc.testnet.near.org", -}; - -if (process.argv.length !== 4) { - console.info(HELP); - process.exit(1); -} - -wrapNear(process.argv[2], process.argv[3]); - -async function wrapNear(accountId, wrapAmount) { - const near = await connect(config); - const account = await near.account(accountId); - - const actions = [ - transactions.functionCall( - "near_deposit", // contract method to deposit NEAR for wNEAR - {}, - 30000000000000, // attached gas - utils.format.parseNearAmount(wrapAmount) // amount of NEAR to deposit and wrap - ), - ]; - - // check if storage has been paid (the account has a wNEAR account) - 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) { - actions.unshift( - transactions.functionCall( - "storage_deposit", // method to create an account - {}, - 30000000000000, // attached gas - utils.format.parseNearAmount('0.00125') // account creation costs 0.00125 NEAR for storage - ) - ); - } - - // send batched transaction - return account.signAndSendTransaction({ - receiverId: WRAP_NEAR_CONTRACT_ID, - actions, - }); -} diff --git a/packages/cookbook/utils/wrap-near.ts b/packages/cookbook/utils/wrap-near.ts new file mode 100644 index 0000000000..f39e00e1fa --- /dev/null +++ b/packages/cookbook/utils/wrap-near.ts @@ -0,0 +1,63 @@ +import { + formatNearAmount, + getPlaintextFilesystemSigner, + getTestnetRpcProvider, + signAndSendFromComposer, + TransactionComposer, + view, +} from '@near-js/client'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +// On mainnet it's wrap.near, by the way +const WRAP_NEAR_CONTRACT_ID = "wrap.testnet"; + +export default async function wrapNear(accountId: string, wrapAmount: bigint, wrapContract = WRAP_NEAR_CONTRACT_ID) { + if (!accountId || !wrapAmount) { + console.log(chalk`{red pnpm wrapNear -- ACCOUNT_ID WRAP_AMOUNT [WRAP_CONTRACT]}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + + const getStorageBalance = () => view({ + account: wrapContract, + method: 'storage_balance_of', + args: { account_id: accountId }, + deps: { rpcProvider }, + }) as Promise<{ available: string, total: string }>; + + const wrapTransaction = TransactionComposer.init({ + sender: accountId, + receiver: wrapContract, + }); + + const { total: preTotal, available: preAvailable } = await getStorageBalance(); + const _30tgas = BigInt(3e13); + if (!preTotal) { + wrapTransaction.functionCall('storage_deposit', {}, _30tgas, BigInt(1.25e21)); + } + wrapTransaction.functionCall('near_deposit', {}, _30tgas, BigInt(wrapAmount)); + + await signAndSendFromComposer({ + composer: wrapTransaction, + deps: { + rpcProvider, + signer, + }, + }); + + const { total: postTotal, available: postAvailable } = await getStorageBalance(); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.green RESULTS} {white Wrapped ${wrapAmount}yN with ${wrapContract}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.white Starting Balance} {white |} {bold.yellow ${formatNearAmount(preAvailable)} / ${formatNearAmount(preTotal)}}`); + console.log(chalk`{bold.white Ending Balance} {white |} {bold.yellow ${formatNearAmount(postAvailable)} / ${formatNearAmount(postTotal)}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); +} From 53c2d279c7803cfb2605a6d11690e04d36236ee1 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Fri, 13 Sep 2024 17:32:29 -0700 Subject: [PATCH 25/55] feat: provider options and missing types --- packages/client/src/constants.ts | 8 +++++ .../client/src/interfaces/dependencies.ts | 15 ++------ packages/client/src/interfaces/index.ts | 1 + packages/client/src/interfaces/providers.ts | 25 +++++++++++++ packages/client/src/providers.ts | 35 +++++++++++++++---- .../providers/src/failover-rpc-provider.ts | 3 +- packages/types/src/provider/response.ts | 22 ++++++++++++ 7 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 packages/client/src/interfaces/providers.ts diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts index 2a6514d1e8..81a68d6887 100644 --- a/packages/client/src/constants.ts +++ b/packages/client/src/constants.ts @@ -11,9 +11,17 @@ export const PAGODA_RPC_ENDPOINTS_MAINNET = [ 'https://rpc.mainnet.pagoda.co', ]; +export const PAGODA_RPC_ARCHIVAL_ENDPOINTS_MAINNET = [ + 'https://archival-rpc.near.org', +]; + export const PAGODA_RPC_ENDPOINTS_TESTNET = [ 'https://rpc.testnet.near.org', 'https://rpc.testnet.pagoda.co', ]; +export const PAGODA_RPC_ARCHIVAL_ENDPOINTS_TESTNET = [ + 'https://archival-rpc.testnet.near.org', +]; + export const KITWALLET_FUNDED_TESTNET_ACCOUNT_ENDPOINT = 'https://testnet-api.kitwallet.app/account'; diff --git a/packages/client/src/interfaces/dependencies.ts b/packages/client/src/interfaces/dependencies.ts index dd408dfbce..e8f82bdb87 100644 --- a/packages/client/src/interfaces/dependencies.ts +++ b/packages/client/src/interfaces/dependencies.ts @@ -1,22 +1,11 @@ -import type { - BlockReference, - BlockResult, - FinalExecutionOutcome, - QueryResponseKind, -} from '@near-js/types'; -import type { SignedTransaction } from '@near-js/transactions'; import type { PublicKey } from '@near-js/crypto'; +import type { RpcQueryProvider } from './providers'; + export interface Dependent { deps: T; } -export interface RpcQueryProvider { - block(block: BlockReference): Promise; - query(...args: any[]): Promise; - sendTransaction(transaction: SignedTransaction): Promise; -} - export interface MessageSigner { getPublicKey(): Promise; signMessage(m: Uint8Array): Promise; diff --git a/packages/client/src/interfaces/index.ts b/packages/client/src/interfaces/index.ts index 36d525630a..a3827e4c93 100644 --- a/packages/client/src/interfaces/index.ts +++ b/packages/client/src/interfaces/index.ts @@ -1,3 +1,4 @@ export * from './dependencies'; +export * from './providers'; export * from './transactions'; export * from './view'; diff --git a/packages/client/src/interfaces/providers.ts b/packages/client/src/interfaces/providers.ts new file mode 100644 index 0000000000..b418de9af0 --- /dev/null +++ b/packages/client/src/interfaces/providers.ts @@ -0,0 +1,25 @@ +import type { + BlockReference, + BlockResult, + ChunkId, + ChunkResult, + FinalExecutionOutcome, + QueryResponseKind, + TxExecutionStatus, +} from '@near-js/types'; +import type { SignedTransaction } from '@near-js/transactions'; + +interface GetTransactionParams { + transactionHash: string; + account: string; + includeReceipts?: boolean; + waitUntil?: TxExecutionStatus; +} + +export interface RpcQueryProvider { + block(block: BlockReference): Promise; + chunk(chunkId: ChunkId): Promise; + getTransaction(params: GetTransactionParams): Promise; + sendTransaction(transaction: SignedTransaction): Promise; + query(...args: any[]): Promise; +} diff --git a/packages/client/src/providers.ts b/packages/client/src/providers.ts index de389727de..67fd392b85 100644 --- a/packages/client/src/providers.ts +++ b/packages/client/src/providers.ts @@ -1,8 +1,12 @@ import { FailoverRpcProvider, JsonRpcProvider } from '@near-js/providers'; import type { Finality } from '@near-js/types'; -import type { ViewBaseParams } from './interfaces'; -import { PAGODA_RPC_ENDPOINTS_MAINNET, PAGODA_RPC_ENDPOINTS_TESTNET } from './constants'; +import type { RpcQueryProvider, ViewBaseParams } from './interfaces'; +import { + PAGODA_RPC_ARCHIVAL_ENDPOINTS_TESTNET, + PAGODA_RPC_ENDPOINTS_MAINNET, + PAGODA_RPC_ENDPOINTS_TESTNET, +} from './constants'; interface DefaultFinality { defaultFinality?: Finality; @@ -44,12 +48,24 @@ export function getEndpointsByNetwork(network: string) { * Initialize a failover RPC provider capable of retrying requests against a set of endpoints * @param urls RPC endpoint URLs */ -export function getFailoverRpcProvider(urls: string[]) { +export function createRpcClientWrapper(urls: string[]): RpcQueryProvider { if (!urls) { throw new Error('at least one RPC endpoint URL required'); } - return new FailoverRpcProvider(urls.map((url) => new JsonRpcProvider({ url }))); + const provider = new FailoverRpcProvider(urls.map((url) => new JsonRpcProvider({ url }))); + return { + block: (block) => provider.block(block), + chunk: (chunkId) => provider.chunk(chunkId), + getTransaction: ({ transactionHash, account, includeReceipts, waitUntil}) => { + if (includeReceipts) { + return provider.txStatusReceipts(transactionHash, account, waitUntil); + } + return provider.txStatus(transactionHash, account, waitUntil); + }, + sendTransaction: (transaction) => provider.sendTransaction(transaction), + query: (params) => provider.query(params), + }; } /** @@ -57,7 +73,7 @@ export function getFailoverRpcProvider(urls: string[]) { * @param network target blockchain network (e.g. `mainnet`) */ export function getProviderByNetwork(network: string) { - return getFailoverRpcProvider(getEndpointsByNetwork(network)); + return createRpcClientWrapper(getEndpointsByNetwork(network)); } /** @@ -65,7 +81,7 @@ export function getProviderByNetwork(network: string) { * @param urls RPC endpoint URLs */ export function getProviderByEndpoints(...urls: string[]) { - return getFailoverRpcProvider(urls); + return createRpcClientWrapper(urls); } /** @@ -75,6 +91,13 @@ export function getTestnetRpcProvider() { return getProviderByNetwork('testnet'); } +/** + * Initialize a testnet archival RPC provider + */ +export function getTestnetRpcArchivalProvider() { + return createRpcClientWrapper(PAGODA_RPC_ARCHIVAL_ENDPOINTS_TESTNET); +} + /** * Initialize a mainnet RPC provider */ diff --git a/packages/providers/src/failover-rpc-provider.ts b/packages/providers/src/failover-rpc-provider.ts index dbcf225b57..1d3d36eca1 100644 --- a/packages/providers/src/failover-rpc-provider.ts +++ b/packages/providers/src/failover-rpc-provider.ts @@ -88,7 +88,8 @@ export class FailoverRpcProvider extends Provider { const result = await getResult(this.currentProvider); if (result) return result; - } catch { + } catch (e) { + console.error(e); this.switchToNextProvider(); } } diff --git a/packages/types/src/provider/response.ts b/packages/types/src/provider/response.ts index 6c1ca85231..6ef3b8ca9b 100644 --- a/packages/types/src/provider/response.ts +++ b/packages/types/src/provider/response.ts @@ -46,6 +46,27 @@ export interface ExecutionOutcome { status: ExecutionStatus | ExecutionStatusBasic; } +export type ReceiptAction = + { Transfer: { deposit: string }}; + +export interface ExecutionOutcomeReceiptDetail { + predecessor_id: string; + receipt: { + Action: ExecutionOutcomeReceiptAction + }; + receipt_id: string; + receiver_id: string; +} + +export interface ExecutionOutcomeReceiptAction { + actions: ReceiptAction[]; + gas_price: string; + input_data_ids: string[]; + output_data_receivers: string[]; + signer_id: string; + signer_public_key: string; +} + export interface ExecutionOutcomeWithIdView { proof: MerklePath; block_hash: string; @@ -59,6 +80,7 @@ export interface FinalExecutionOutcome { transaction: any; transaction_outcome: ExecutionOutcomeWithId; receipts_outcome: ExecutionOutcomeWithId[]; + receipts?: ExecutionOutcomeReceiptDetail[]; } export interface QueryResponseKind { From c230e9b3a1cebc09e46a696418401d96e6419df8 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Fri, 13 Sep 2024 17:33:56 -0700 Subject: [PATCH 26/55] feat: cookbook updates --- packages/cookbook/package.json | 4 ++ .../{get-tx-detail.js => get-tx-detail.ts} | 50 ++++------------- packages/cookbook/utils/unwrap-near.js | 29 ---------- packages/cookbook/utils/unwrap-near.ts | 56 +++++++++++++++++++ packages/cookbook/utils/verify-signature.js | 30 ---------- packages/cookbook/utils/verify-signature.ts | 22 ++++++++ packages/cookbook/utils/wrap-near.ts | 2 +- pnpm-lock.yaml | 3 + 8 files changed, 98 insertions(+), 98 deletions(-) rename packages/cookbook/transactions/{get-tx-detail.js => get-tx-detail.ts} (51%) delete mode 100644 packages/cookbook/utils/unwrap-near.js create mode 100644 packages/cookbook/utils/unwrap-near.ts delete mode 100644 packages/cookbook/utils/verify-signature.js create mode 100644 packages/cookbook/utils/verify-signature.ts diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index d50853da97..23c0e6d115 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -21,6 +21,9 @@ "deleteAccessKey": "tsx -e \"import f from './accounts/access-keys/delete-access-key.ts'; f(...process.argv.slice(1));\"", "deployContract": "tsx -e \"import f from './utils/deploy-contract.ts'; f(...process.argv.slice(1));\"", "getState": "tsx -e \"import f from './utils/get-state.ts'; f(...process.argv.slice(1));\"", + "getTransactionDetail": "tsx -e \"import f from './transactions/get-tx-detail.ts'; f(...process.argv.slice(1));\"", + "unwrapNear": "tsx -e \"import f from './utils/unwrap-near.ts'; f(...process.argv.slice(1));\"", + "verifySignature": "tsx -e \"import f from './utils/verify-signature.ts'; f(...process.argv.slice(1));\"", "wrapNear": "tsx -e \"import f from './utils/wrap-near.ts'; f(...process.argv.slice(1));\"" }, "devDependencies": { @@ -31,6 +34,7 @@ "@near-js/providers": "workspace:*", "@near-js/signers": "workspace:*", "@near-js/transactions": "workspace:*", + "@near-js/utils": "workspace:*", "build": "workspace:*", "chalk": "4.1.1", "homedir": "0.6.0", diff --git a/packages/cookbook/transactions/get-tx-detail.js b/packages/cookbook/transactions/get-tx-detail.ts similarity index 51% rename from packages/cookbook/transactions/get-tx-detail.js rename to packages/cookbook/transactions/get-tx-detail.ts index 7c6bc39bd4..589dc52937 100644 --- a/packages/cookbook/transactions/get-tx-detail.js +++ b/packages/cookbook/transactions/get-tx-detail.ts @@ -1,8 +1,7 @@ -const { connect, keyStores } = require("near-api-js"); -const path = require("path"); -const homedir = require("os").homedir(); +import { + getTestnetRpcArchivalProvider, +} from '@near-js/client'; -const CREDENTIALS_DIR = ".near-credentials"; // block hash of query start (oldest block) const START_BLOCK_HASH = "GZ8vKdcgsavkEndkDWHCjuhyqSR2TGnp9VDZbTzd6ufG"; // block hash of query end (newest block) @@ -10,40 +9,23 @@ const END_BLOCK_HASH = "8aEcKhF7N1Jyw84e6vHW6Hzp3Ep7mSXJ6Rvnsy5qGJPF"; // contract ID or account ID you want to find transactions details for const CONTRACT_ID = "relayer.ropsten.testnet"; -const credentialsPath = path.join(homedir, CREDENTIALS_DIR); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -// NOTE: we're using the archival rpc to look back in time for a specific set -// of transactions. For a full list of what nodes are available, visit: -// https://docs.near.org/docs/develop/node/intro/types-of-node -const config = { - keyStore, - networkId: "testnet", - nodeUrl: "https://archival-rpc.testnet.near.org", -}; - -getTransactions(START_BLOCK_HASH, END_BLOCK_HASH, CONTRACT_ID); - -async function getTransactions(startBlock, endBlock, accountId) { - const near = await connect(config); +export default async function getTransactions(startBlockHash: string = START_BLOCK_HASH, endBlockHash: string = END_BLOCK_HASH, contractId: string = CONTRACT_ID) { + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcArchivalProvider(); // creates an array of block hashes for given range const blockArr = []; - let blockHash = endBlock; + let blockHash = endBlockHash; do { - const currentBlock = await getBlockByID(blockHash); + const currentBlock = await rpcProvider.block({ blockId: blockHash }); blockArr.push(currentBlock.header.hash); blockHash = currentBlock.header.prev_hash; console.log("working...", blockHash); - } while (blockHash !== startBlock); + } while (blockHash !== startBlockHash); // returns block details based on hashes in array const blockDetails = await Promise.all( - blockArr.map((blockId) => - near.connection.provider.block({ - blockId, - }) - ) + blockArr.map((blockId) => rpcProvider.block({ blockId })) ); // returns an array of chunk hashes from block details @@ -53,14 +35,14 @@ async function getTransactions(startBlock, endBlock, accountId) { //returns chunk details based from the array of hashes const chunkDetails = await Promise.all( - chunkHashArr.map(chunk => near.connection.provider.chunk(chunk)) + chunkHashArr.map(chunk => rpcProvider.chunk(chunk)) ); // checks chunk details for transactions // if there are transactions in the chunk we // find ones associated with passed accountId const transactions = chunkDetails.flatMap((chunk) => - (chunk.transactions || []).filter((tx) => tx.signer_id === accountId) + (chunk.transactions || []).filter((tx) => tx.signer_id === contractId) ); //creates transaction links from matchingTxs @@ -71,11 +53,3 @@ async function getTransactions(startBlock, endBlock, accountId) { console.log("MATCHING TRANSACTIONS: ", transactions); console.log("TRANSACTION LINKS: ", txsLinks); } - -async function getBlockByID(blockID) { - const near = await connect(config); - const blockInfoByHeight = await near.connection.provider.block({ - blockId: blockID, - }); - return blockInfoByHeight; -} diff --git a/packages/cookbook/utils/unwrap-near.js b/packages/cookbook/utils/unwrap-near.js deleted file mode 100644 index 947843aa73..0000000000 --- a/packages/cookbook/utils/unwrap-near.js +++ /dev/null @@ -1,29 +0,0 @@ -const { connect, keyStores, utils } = require("near-api-js"); -const path = require("path"); -const homedir = require("os").homedir(); - -const WRAP_NEAR_CONTRACT_ID = "wrap.near"; - -const credentialsPath = path.join(homedir, ".near-credentials"); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -const config = { - keyStore, - networkId: "mainnet", - nodeUrl: "https://rpc.mainnet.near.org", -}; - -// Unwrap 1 wNEAR to NEAR -unwrapNear("example.near", "1"); - -async function unwrapNear(accountId, unwrapAmount) { - const near = await connect(config); - const account = await near.account(accountId); - - return account.functionCall({ - contractId: WRAP_NEAR_CONTRACT_ID, - methodName: "near_withdraw", // method to withdraw wNEAR for NEAR - args: { amount: utils.format.parseNearAmount(unwrapAmount) }, - attachedDeposit: "1", // attach one yoctoNEAR - }); - } \ No newline at end of file diff --git a/packages/cookbook/utils/unwrap-near.ts b/packages/cookbook/utils/unwrap-near.ts new file mode 100644 index 0000000000..152f804ba1 --- /dev/null +++ b/packages/cookbook/utils/unwrap-near.ts @@ -0,0 +1,56 @@ +import { + functionCall, + getPlaintextFilesystemSigner, + getTestnetRpcProvider, + view, +} from '@near-js/client'; +import { formatNearAmount, parseNearAmount } from '@near-js/utils'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +// On mainnet it's wrap.near, by the way +const WRAP_NEAR_CONTRACT_ID = "wrap.testnet"; + +export default async function unwrapNear(accountId: string, unwrapAmount: bigint, wrapContract = WRAP_NEAR_CONTRACT_ID) { + if (!accountId || !unwrapAmount) { + console.log(chalk`{red pnpm unwrapNear -- ACCOUNT_ID UNWRAP_AMOUNT [WRAP_CONTRACT]}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + + const getStorageBalance = () => view({ + account: wrapContract, + method: 'storage_balance_of', + args: { account_id: accountId }, + deps: { rpcProvider }, + }) as Promise<{ available: string, total: string }>; + + const { total: preTotal, available: preAvailable } = (await getStorageBalance()) || {}; + + await functionCall({ + sender: accountId, + receiver: wrapContract, + method: 'near_withdraw', + args: { amount: parseNearAmount(unwrapAmount.toString()) }, + deposit: 1n, + deps: { + rpcProvider, + signer, + }, + }); + + const { total: postTotal, available: postAvailable } = await getStorageBalance(); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.green RESULTS} {white Unwrapped ${unwrapAmount}yN with ${wrapContract}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.white Starting Balance} {white |} {bold.yellow ${formatNearAmount(preAvailable)} / ${formatNearAmount(preTotal)}}`); + console.log(chalk`{bold.white Ending Balance} {white |} {bold.yellow ${formatNearAmount(postAvailable)} / ${formatNearAmount(postTotal)}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); +} diff --git a/packages/cookbook/utils/verify-signature.js b/packages/cookbook/utils/verify-signature.js deleted file mode 100644 index b42c5068e5..0000000000 --- a/packages/cookbook/utils/verify-signature.js +++ /dev/null @@ -1,30 +0,0 @@ -const { keyStores } = require("near-api-js"); -const path = require("path"); -const homedir = require("os").homedir(); - -const ACCOUNT_ID = "near-example.testnet"; -const CREDENTIALS_DIR = ".near-credentials"; - -const credentialsPath = path.join(homedir, CREDENTIALS_DIR); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -const config = { - keyStore, - networkId: "testnet", - nodeUrl: "https://rpc.testnet.near.org", -}; - -verifySignature(); - -async function verifySignature() { - const keyPair = await keyStore.getKey(config.networkId, ACCOUNT_ID); - const msg = Buffer.from("hi"); - - const { signature } = keyPair.sign(msg); - - const isValid = keyPair.verify(msg, signature); - - console.log("Signature Valid?:", isValid); - - return isValid; -} diff --git a/packages/cookbook/utils/verify-signature.ts b/packages/cookbook/utils/verify-signature.ts new file mode 100644 index 0000000000..11e56ec686 --- /dev/null +++ b/packages/cookbook/utils/verify-signature.ts @@ -0,0 +1,22 @@ +import { getTestnetRpcProvider } from '@near-js/client'; +import { KeyType, PublicKey } from '@near-js/crypto'; +import { baseDecode } from '@near-js/utils'; + +const ACCOUNT_ID = 'gornt.testnet'; +const TX_HASH = '4tMHzHU5p9dXc4WqopReNZ2TMJxZyu913zK4Fn9nMRoB'; + +export default async function verifySignature(accountId: string = ACCOUNT_ID, transactionHash: string = TX_HASH) { + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + + const { transaction: { public_key, signature } } = await rpcProvider.getTransaction({ transactionHash, account: accountId }); + + const hashBytes = baseDecode(transactionHash); + const publicKeyBytes = baseDecode(public_key.slice('ed25519:'.length)); + const signatureBytes = baseDecode(signature.slice('ed25519:'.length)); + const publicKey = new PublicKey({ keyType: KeyType.ED25519, data: publicKeyBytes }); + + const isVerified = publicKey.verify(hashBytes, signatureBytes); + + console.log(isVerified); +} diff --git a/packages/cookbook/utils/wrap-near.ts b/packages/cookbook/utils/wrap-near.ts index f39e00e1fa..e08ae8b4f5 100644 --- a/packages/cookbook/utils/wrap-near.ts +++ b/packages/cookbook/utils/wrap-near.ts @@ -38,7 +38,7 @@ export default async function wrapNear(accountId: string, wrapAmount: bigint, wr receiver: wrapContract, }); - const { total: preTotal, available: preAvailable } = await getStorageBalance(); + const { total: preTotal, available: preAvailable } = (await getStorageBalance()) || {}; const _30tgas = BigInt(3e13); if (!preTotal) { wrapTransaction.functionCall('storage_deposit', {}, _30tgas, BigInt(1.25e21)); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7322dabe9..a6908d7565 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -260,6 +260,9 @@ importers: '@near-js/transactions': specifier: workspace:* version: link:../transactions + '@near-js/utils': + specifier: workspace:* + version: link:../utils build: specifier: workspace:* version: link:../build From d326e5051fb560be3d58a352dd45be2fdd839063 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Tue, 17 Sep 2024 16:17:51 -0700 Subject: [PATCH 27/55] feat: add SignedTransactionComposer --- packages/client/src/transactions/actions.ts | 91 +++++++------------ packages/client/src/transactions/composer.ts | 72 +++++++++++---- .../client/src/transactions/create_account.ts | 17 ++-- .../client/src/transactions/sign_and_send.ts | 25 ----- packages/cookbook/utils/wrap-near.ts | 14 +-- 5 files changed, 96 insertions(+), 123 deletions(-) diff --git a/packages/client/src/transactions/actions.ts b/packages/client/src/transactions/actions.ts index 5958466f2b..9a36f2527d 100644 --- a/packages/client/src/transactions/actions.ts +++ b/packages/client/src/transactions/actions.ts @@ -7,8 +7,7 @@ import type { StakeParams, TransferParams, } from '../interfaces'; -import { TransactionComposer } from './composer'; -import { signAndSendFromComposer } from './sign_and_send'; +import { SignedTransactionComposer } from './composer'; /** * Make a function call against a contract @@ -21,13 +20,10 @@ import { signAndSendFromComposer } from './sign_and_send'; * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function functionCall({ sender, receiver, method, args, gas, deposit, blockReference, deps }: FunctionCallParams) { - return signAndSendFromComposer({ - composer: TransactionComposer.init({ sender, receiver }) - .functionCall(method, args, gas, deposit), - blockReference, - deps, - }); +export function functionCall({ sender, receiver, method, args, gas, deposit, blockReference, deps }: FunctionCallParams) { + return SignedTransactionComposer.init({ sender, receiver, deps }) + .functionCall(method, args, gas, deposit) + .signAndSend(blockReference); } /** @@ -38,13 +34,10 @@ export async function functionCall({ sender, receiver, method, args, gas, deposi * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function transfer({ sender, receiver, amount, blockReference, deps }: TransferParams) { - return signAndSendFromComposer({ - composer: TransactionComposer.init({ sender, receiver }) - .transfer(amount), - blockReference, - deps, - }); +export function transfer({ sender, receiver, amount, blockReference, deps }: TransferParams) { + return SignedTransactionComposer.init({ sender, receiver, deps }) + .transfer(amount) + .signAndSend(blockReference); } /** @@ -55,13 +48,10 @@ export async function transfer({ sender, receiver, amount, blockReference, deps * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function stake({ account, amount, publicKey, blockReference, deps }: StakeParams) { - return signAndSendFromComposer({ - composer: TransactionComposer.init({ sender: account, receiver: account }) - .stake(amount, publicKey), - blockReference, - deps, - }); +export function stake({ account, amount, publicKey, blockReference, deps }: StakeParams) { + return SignedTransactionComposer.init({ sender: account, receiver: account, deps }) + .stake(amount, publicKey) + .signAndSend(blockReference); } /** @@ -71,13 +61,10 @@ export async function stake({ account, amount, publicKey, blockReference, deps } * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function addFullAccessKey({ account, publicKey, blockReference, deps }: ModifyAccessKeyParams) { - return signAndSendFromComposer({ - composer: TransactionComposer.init({ sender: account, receiver: account }) - .addFullAccessKey(publicKey), - blockReference, - deps, - }); +export function addFullAccessKey({ account, publicKey, blockReference, deps }: ModifyAccessKeyParams) { + return SignedTransactionComposer.init({ sender: account, receiver: account, deps }) + .addFullAccessKey(publicKey) + .signAndSend(blockReference); } /** @@ -90,13 +77,10 @@ export async function addFullAccessKey({ account, publicKey, blockReference, dep * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function addFunctionCallAccessKey({ account, publicKey, contract, methodNames, allowance, blockReference, deps }: AddFunctionCallAccessKeyParams) { - return signAndSendFromComposer({ - composer: TransactionComposer.init({ sender: account, receiver: account }) - .addFunctionCallAccessKey(publicKey, contract, methodNames, allowance), - blockReference, - deps, - }); +export function addFunctionCallAccessKey({ account, publicKey, contract, methodNames, allowance, blockReference, deps }: AddFunctionCallAccessKeyParams) { + return SignedTransactionComposer.init({ sender: account, receiver: account, deps }) + .addFunctionCallAccessKey(publicKey, contract, methodNames, allowance) + .signAndSend(blockReference); } /** @@ -106,13 +90,10 @@ export async function addFunctionCallAccessKey({ account, publicKey, contract, m * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function deleteAccessKey({ account, publicKey, blockReference, deps }: ModifyAccessKeyParams) { - return signAndSendFromComposer({ - composer: TransactionComposer.init({ sender: account, receiver: account }) - .deleteKey(publicKey), - blockReference, - deps, - }); +export function deleteAccessKey({ account, publicKey, blockReference, deps }: ModifyAccessKeyParams) { + return SignedTransactionComposer.init({ sender: account, receiver: account, deps }) + .deleteKey(publicKey) + .signAndSend(blockReference); } /** @@ -122,13 +103,10 @@ export async function deleteAccessKey({ account, publicKey, blockReference, deps * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function deleteAccount({ account, beneficiaryId, blockReference, deps }: DeleteAccountParams) { - return signAndSendFromComposer({ - composer: TransactionComposer.init({ sender: account, receiver: account }) - .deleteAccount(beneficiaryId), - blockReference, - deps, - }); +export function deleteAccount({ account, beneficiaryId, blockReference, deps }: DeleteAccountParams) { + return SignedTransactionComposer.init({ sender: account, receiver: account, deps }) + .deleteAccount(beneficiaryId) + .signAndSend(blockReference); } /** @@ -138,11 +116,8 @@ export async function deleteAccount({ account, beneficiaryId, blockReference, de * @param blockReference block ID/finality * @param deps sign-and-send dependencies */ -export async function deployContract({ account, code, blockReference, deps }: DeployContractParams) { - return signAndSendFromComposer({ - composer: TransactionComposer.init({ sender: account, receiver: account }) - .deployContract(code), - blockReference, - deps, - }); +export function deployContract({ account, code, blockReference, deps }: DeployContractParams) { + return SignedTransactionComposer.init({ sender: account, receiver: account, deps }) + .deployContract(code) + .signAndSend(blockReference); } diff --git a/packages/client/src/transactions/composer.ts b/packages/client/src/transactions/composer.ts index 8cf8d5c80e..1144734c05 100644 --- a/packages/client/src/transactions/composer.ts +++ b/packages/client/src/transactions/composer.ts @@ -9,7 +9,9 @@ import { } from '@near-js/transactions'; import { baseDecode, DEFAULT_FUNCTION_CALL_GAS } from '@near-js/utils'; import { DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL } from '../constants'; -import { BlockHeader } from '@near-js/types'; +import { BlockHeader, BlockReference } from '@near-js/types'; +import { MessageSigner, RpcQueryProvider, SignAndSendTransactionDependency } from '../interfaces'; +import { getSignerNonce, signAndSendTransaction } from './sign_and_send'; interface TransactionOptions { blockHeader?: BlockHeader; @@ -19,6 +21,8 @@ interface TransactionOptions { sender?: string; } +interface SignedTransactionOptions extends TransactionOptions, SignAndSendTransactionDependency {} + export class TransactionComposer { private actions: Action[] = []; receiver: string | undefined; @@ -27,15 +31,16 @@ export class TransactionComposer { nonce: bigint | undefined; publicKey: PublicKey | undefined; - static init(transaction: TransactionOptions) { - const composer = new TransactionComposer(); - composer.receiver = transaction.receiver; - composer.sender = transaction.sender; - composer.blockHeader = transaction.blockHeader; - composer.nonce = transaction.nonce; - composer.publicKey = transaction.publicKey; + constructor(transaction: TransactionOptions) { + this.receiver = transaction.receiver; + this.sender = transaction.sender; + this.blockHeader = transaction.blockHeader; + this.nonce = transaction.nonce; + this.publicKey = transaction.publicKey; + } - return composer; + static init(transaction: TransactionOptions) { + return new TransactionComposer(transaction); } private buildTransactionObject(transaction?: TransactionOptions) { @@ -68,54 +73,83 @@ export class TransactionComposer { }); } - addFullAccessKey(publicKey: string) { + addFullAccessKey(publicKey: string): this { this.actions.push(actionCreators.addKey(PublicKey.from(publicKey), actionCreators.fullAccessKey())); return this; } - addFunctionCallAccessKey(publicKey: string, contractId: string, methodNames: string[], allowance?: bigint) { + addFunctionCallAccessKey(publicKey: string, contractId: string, methodNames: string[], allowance?: bigint): this { const accessKey = actionCreators.functionCallAccessKey(contractId, methodNames, allowance); this.actions.push(actionCreators.addKey(PublicKey.from(publicKey), accessKey)); return this; } - createAccount() { + createAccount(): this { this.actions.push(actionCreators.createAccount()); return this; } - deleteAccount(beneficiaryId: string) { + deleteAccount(beneficiaryId: string): this { this.actions.push(actionCreators.deleteAccount(beneficiaryId)); return this; } - deleteKey(publicKey: string) { + deleteKey(publicKey: string): this { this.actions.push(actionCreators.deleteKey(PublicKey.from(publicKey))); return this; } - deployContract(code: Uint8Array) { + deployContract(code: Uint8Array): this { this.actions.push(actionCreators.deployContract(code)); return this; } - functionCall(method: string, args: object, gas: bigint = DEFAULT_FUNCTION_CALL_GAS * BigInt(10), deposit = BigInt(0)) { + functionCall(method: string, args: object, gas: bigint = DEFAULT_FUNCTION_CALL_GAS * BigInt(10), deposit = BigInt(0)): this { this.actions.push(actionCreators.functionCall(method, args, gas, deposit)); return this; } - signedDelegate(delegateAction: DelegateAction, signature: Signature) { + signedDelegate(delegateAction: DelegateAction, signature: Signature): this { this.actions.push(actionCreators.signedDelegate({ delegateAction, signature })); return this; } - stake(stake: bigint, publicKey: string) { + stake(stake: bigint, publicKey: string): this { this.actions.push(actionCreators.stake(stake, PublicKey.from(publicKey))); return this; } - transfer(deposit: bigint) { + transfer(deposit: bigint): this { this.actions.push(actionCreators.transfer(deposit)); return this; } } + +export class SignedTransactionComposer extends TransactionComposer { + rpcProvider: RpcQueryProvider; + signer: MessageSigner; + + constructor({ deps, ...baseOptions }: SignedTransactionOptions) { + super(baseOptions); + this.rpcProvider = deps.rpcProvider; + this.signer = deps.signer; + } + + static init(options: SignedTransactionOptions) { + return new SignedTransactionComposer(options); + } + + async signAndSend(blockReference: BlockReference = { finality: 'final' }) { + const deps = { rpcProvider: this.rpcProvider, signer: this.signer }; + const blockHeader = this.blockHeader || (await this.rpcProvider.block(blockReference))?.header; + const signerNonce = this.nonce || (await getSignerNonce({ account: this.sender, blockReference, deps }) + 1n); + + const transaction = this.toTransaction({ + nonce: signerNonce, + publicKey: this.publicKey || await this.signer.getPublicKey(), + blockHeader, + }); + + return signAndSendTransaction({ transaction, deps }); + } +} diff --git a/packages/client/src/transactions/create_account.ts b/packages/client/src/transactions/create_account.ts index 9b019dbde5..2b78c5c1e2 100644 --- a/packages/client/src/transactions/create_account.ts +++ b/packages/client/src/transactions/create_account.ts @@ -2,11 +2,9 @@ import type { CreateAccountParams, CreateTopLevelAccountParams, } from '../interfaces'; -import { TransactionComposer } from './composer'; -import { signAndSendFromComposer } from './sign_and_send'; +import { SignedTransactionComposer } from './composer'; import { functionCall } from './actions'; - /** * Create a new top-level account using an existing account * (e.g. create `new.near` by signing with `creator.near`) @@ -45,13 +43,10 @@ export async function createTopLevelAccount({ account, contract, newAccount, new * @param deps sign-and-send dependencies */ export async function createSubAccount({ account, newAccount, newPublicKey, initialBalance, blockReference, deps }: CreateAccountParams) { - return signAndSendFromComposer({ - composer: TransactionComposer.init({ sender: account, receiver: newAccount }) - .createAccount() - .transfer(initialBalance) - .addFullAccessKey(newPublicKey), - blockReference, - deps, - }); + return SignedTransactionComposer.init({ sender: account, receiver: newAccount, deps }) + .createAccount() + .transfer(initialBalance) + .addFullAccessKey(newPublicKey) + .signAndSend(blockReference); } diff --git a/packages/client/src/transactions/sign_and_send.ts b/packages/client/src/transactions/sign_and_send.ts index e74c8eeab6..c9e0eb2dba 100644 --- a/packages/client/src/transactions/sign_and_send.ts +++ b/packages/client/src/transactions/sign_and_send.ts @@ -3,9 +3,7 @@ import { getTransactionLastResult } from '@near-js/utils'; import { sha256 } from '@noble/hashes/sha256'; import type { SignTransactionParams, SignAndSendTransactionParams } from '../interfaces'; -import { getBlock } from '../providers'; import { getNonce } from '../view'; -import { SignAndSendComposerParams } from '../interfaces'; import { BlockReference } from '@near-js/types'; const DEFAULT_FINALITY: BlockReference = { finality: 'final' }; @@ -60,26 +58,3 @@ export async function getSignerNonce({ account, blockReference = DEFAULT_FINALIT deps: { rpcProvider }, }); } - -/** - * Sign and send a transaction given a TransactionComposer instance. - * Derive values for other transaction fields (public key, nonce, block header) from MessageSigner dependency. - * NB - this might be more natural as a method on TransactionComposer but would be a major increase in scope. - * @param composer Transaction Composer instance with values for sender, receiver, and actions - * @param blockReference block ID/finality - * @param deps sign-and-send dependencies - */ -export async function signAndSendFromComposer({ composer, blockReference = DEFAULT_FINALITY, deps }: SignAndSendComposerParams) { - const { rpcProvider, signer } = deps; - const block = await getBlock({ blockReference, deps: { rpcProvider } }); - - const signerNonce = await getSignerNonce({ account: composer.sender, blockReference, deps }); - - const transaction = composer.toTransaction({ - nonce: signerNonce + 1n, - publicKey: await signer.getPublicKey(), - blockHeader: block?.header, - }); - - return signAndSendTransaction({ transaction, deps }); -} diff --git a/packages/cookbook/utils/wrap-near.ts b/packages/cookbook/utils/wrap-near.ts index e08ae8b4f5..a847de031f 100644 --- a/packages/cookbook/utils/wrap-near.ts +++ b/packages/cookbook/utils/wrap-near.ts @@ -2,8 +2,7 @@ import { formatNearAmount, getPlaintextFilesystemSigner, getTestnetRpcProvider, - signAndSendFromComposer, - TransactionComposer, + SignedTransactionComposer, view, } from '@near-js/client'; import chalk from 'chalk'; @@ -33,9 +32,10 @@ export default async function wrapNear(accountId: string, wrapAmount: bigint, wr deps: { rpcProvider }, }) as Promise<{ available: string, total: string }>; - const wrapTransaction = TransactionComposer.init({ + const wrapTransaction = SignedTransactionComposer.init({ sender: accountId, receiver: wrapContract, + deps: { rpcProvider, signer }, }); const { total: preTotal, available: preAvailable } = (await getStorageBalance()) || {}; @@ -45,13 +45,7 @@ export default async function wrapNear(accountId: string, wrapAmount: bigint, wr } wrapTransaction.functionCall('near_deposit', {}, _30tgas, BigInt(wrapAmount)); - await signAndSendFromComposer({ - composer: wrapTransaction, - deps: { - rpcProvider, - signer, - }, - }); + await wrapTransaction.signAndSend(); const { total: postTotal, available: postAvailable } = await getStorageBalance(); console.log(chalk`{white ------------------------------------------------------------------------ }`); From 503de05e7f629bb1589bde23368fe7d49e55f213 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Tue, 17 Sep 2024 16:18:28 -0700 Subject: [PATCH 28/55] feat: meta transaction support --- packages/client/src/transactions/index.ts | 1 + .../src/transactions/meta_transactions.ts | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 packages/client/src/transactions/meta_transactions.ts diff --git a/packages/client/src/transactions/index.ts b/packages/client/src/transactions/index.ts index fde2f5d43d..e39ab7f7d0 100644 --- a/packages/client/src/transactions/index.ts +++ b/packages/client/src/transactions/index.ts @@ -1,4 +1,5 @@ export * from './actions'; export * from './composer'; export * from './create_account'; +export * from './meta_transactions'; export * from './sign_and_send'; diff --git a/packages/client/src/transactions/meta_transactions.ts b/packages/client/src/transactions/meta_transactions.ts new file mode 100644 index 0000000000..cec591963d --- /dev/null +++ b/packages/client/src/transactions/meta_transactions.ts @@ -0,0 +1,47 @@ +import { Action, buildDelegateAction, signDelegateAction, SignedDelegate } from '@near-js/transactions'; + +import { SignAndSendTransactionDependency } from '../interfaces'; +import { getSignerNonce } from './sign_and_send'; + +interface SignedDelegateOptions extends SignAndSendTransactionDependency { + account: string; + actions: Action[]; + blockHeightTtl: number; + receiver: string; +} + +export async function buildSignedDelegate({ + account, + actions, + blockHeightTtl, + receiver, + deps: { + rpcProvider, + signer, + }, +}: SignedDelegateOptions): Promise { + const { header } = await rpcProvider.block({ finality: 'final' }); + const signerNonce = await getSignerNonce({ + account, + deps: { + rpcProvider, + signer, + }, + }); + + const delegateAction = buildDelegateAction({ + actions, + maxBlockHeight: BigInt(header.height) + BigInt(blockHeightTtl), + nonce: signerNonce + 1n, + publicKey: await signer.getPublicKey(), + receiverId: receiver, + senderId: account, + }); + + const { signedDelegateAction } = await signDelegateAction({ + delegateAction, + signer: { sign: (m) => signer.signMessage(m) }, + }); + + return signedDelegateAction; +} From 0d87a82ad97517bdddb69d8f2414284a18cc4eee Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Tue, 17 Sep 2024 16:18:44 -0700 Subject: [PATCH 29/55] chore: useless wrapper --- packages/client/src/providers.ts | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/packages/client/src/providers.ts b/packages/client/src/providers.ts index 67fd392b85..152f56c3bc 100644 --- a/packages/client/src/providers.ts +++ b/packages/client/src/providers.ts @@ -1,34 +1,12 @@ import { FailoverRpcProvider, JsonRpcProvider } from '@near-js/providers'; -import type { Finality } from '@near-js/types'; -import type { RpcQueryProvider, ViewBaseParams } from './interfaces'; +import type { RpcQueryProvider } from './interfaces'; import { PAGODA_RPC_ARCHIVAL_ENDPOINTS_TESTNET, PAGODA_RPC_ENDPOINTS_MAINNET, PAGODA_RPC_ENDPOINTS_TESTNET, } from './constants'; -interface DefaultFinality { - defaultFinality?: Finality; -} - -/** - * Query for a block - * @param account target account/contract being queried - * @param blockReference block ID/finality - * @param defaultFinality finality value to fall back on when blockReference is not specified - * @param rpcProvider RPC provider instance - */ -export function getBlock({ blockReference, defaultFinality, deps: { rpcProvider } }: ViewBaseParams & DefaultFinality) { - if (!blockReference && !defaultFinality) { - return Promise.resolve(null); - } - - return rpcProvider.block( - blockReference || { finality: defaultFinality! } - ); -} - /** * Get the set of public endpoints for the provided network * @param network target blockchain network (e.g. `mainnet`) From 9ee9835080aed3dbceb9987cf2019377901e8933 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Tue, 17 Sep 2024 16:25:47 -0700 Subject: [PATCH 30/55] feat: more cookbook updates --- packages/cookbook/package.json | 6 +- packages/cookbook/src/index.ts | 1 + .../transactions/batch-transactions.js | 41 ---------- .../transactions/batch-transactions.ts | 47 +++++++++++ .../cookbook/transactions/get-tx-status.js | 18 ----- .../cookbook/transactions/get-tx-status.ts | 20 +++++ packages/cookbook/transactions/index.ts | 5 ++ .../transactions/meta-transaction-relayer.js | 51 ------------ .../transactions/meta-transaction-relayer.ts | 38 +++++++++ .../cookbook/transactions/meta-transaction.js | 60 -------------- .../cookbook/transactions/meta-transaction.ts | 60 ++++++++++++++ .../{get-tx-detail.ts => traverse-blocks.ts} | 2 +- packages/cookbook/utils/index.ts | 6 ++ pnpm-lock.yaml | 79 ++++++++++--------- 14 files changed, 226 insertions(+), 208 deletions(-) delete mode 100644 packages/cookbook/transactions/batch-transactions.js create mode 100644 packages/cookbook/transactions/batch-transactions.ts delete mode 100644 packages/cookbook/transactions/get-tx-status.js create mode 100644 packages/cookbook/transactions/get-tx-status.ts create mode 100644 packages/cookbook/transactions/index.ts delete mode 100644 packages/cookbook/transactions/meta-transaction-relayer.js create mode 100644 packages/cookbook/transactions/meta-transaction-relayer.ts delete mode 100644 packages/cookbook/transactions/meta-transaction.js create mode 100644 packages/cookbook/transactions/meta-transaction.ts rename packages/cookbook/transactions/{get-tx-detail.ts => traverse-blocks.ts} (92%) diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index 23c0e6d115..2518d8289e 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -13,6 +13,7 @@ "lint:fix": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts --no-eslintrc --fix", "addFullAccessKey": "tsx -e \"import f from './accounts/access-keys/create-full-access-key.ts'; f(...process.argv.slice(1));\"", "addFunctionCallAccessKey": "tsx -e \"import f from './accounts/access-keys/create-function-access-key.ts'; f(...process.argv.slice(1));\"", + "batchTransactions": "tsx -e \"import f from './transactions/batch-transactions.ts'; f(...process.argv.slice(1));\"", "calculateGas": "tsx -e \"import f from './utils/calculate-gas.ts'; f(...process.argv.slice(1));\"", "checkAccountExists": "tsx -e \"import f from './utils/check-account-existence.ts'; f(...process.argv.slice(1));\"", "createFundedTestnetAccount": "tsx -e \"import f from './accounts/create-funded-testnet-account.ts'; f(...process.argv.slice(1));\"", @@ -21,7 +22,10 @@ "deleteAccessKey": "tsx -e \"import f from './accounts/access-keys/delete-access-key.ts'; f(...process.argv.slice(1));\"", "deployContract": "tsx -e \"import f from './utils/deploy-contract.ts'; f(...process.argv.slice(1));\"", "getState": "tsx -e \"import f from './utils/get-state.ts'; f(...process.argv.slice(1));\"", - "getTransactionDetail": "tsx -e \"import f from './transactions/get-tx-detail.ts'; f(...process.argv.slice(1));\"", + "getTransactionStatus": "tsx -e \"import f from './transactions/get-tx-status.ts'; f(...process.argv.slice(1));\"", + "metaTransaction": "tsx -e \"import f from './transactions/meta-transaction.ts'; f(...process.argv.slice(1));\"", + "metaTransactionRelayer": "tsx -e \"import f from './transactions/meta-transaction-relayer.ts'; f(...process.argv.slice(1));\"", + "traverseBlocks": "tsx -e \"import f from './transactions/traverse-blocks.ts'; f(...process.argv.slice(1));\"", "unwrapNear": "tsx -e \"import f from './utils/unwrap-near.ts'; f(...process.argv.slice(1));\"", "verifySignature": "tsx -e \"import f from './utils/verify-signature.ts'; f(...process.argv.slice(1));\"", "wrapNear": "tsx -e \"import f from './utils/wrap-near.ts'; f(...process.argv.slice(1));\"" diff --git a/packages/cookbook/src/index.ts b/packages/cookbook/src/index.ts index b138a26530..122bda714b 100644 --- a/packages/cookbook/src/index.ts +++ b/packages/cookbook/src/index.ts @@ -1,2 +1,3 @@ export * from '../accounts'; +export * from '../transactions'; export * from '../utils'; diff --git a/packages/cookbook/transactions/batch-transactions.js b/packages/cookbook/transactions/batch-transactions.js deleted file mode 100644 index 29bcd2326f..0000000000 --- a/packages/cookbook/transactions/batch-transactions.js +++ /dev/null @@ -1,41 +0,0 @@ -const { connect, transactions, keyStores } = require("near-api-js"); -const fs = require("fs"); -const path = require("path"); -const homedir = require("os").homedir(); - -const CREDENTIALS_DIR = ".near-credentials"; -// NOTE: replace "example" with your accountId -const CONTRACT_NAME = "contract.example.testnet"; -const WHITELIST_ACCOUNT_ID = "whitelisted-account.example.testnet"; -const WASM_PATH = path.join(__dirname, "../utils/wasm-files/staking_pool_factory.wasm"); - -const credentialsPath = path.join(homedir, CREDENTIALS_DIR); -const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); - -const config = { - keyStore, - networkId: "testnet", - nodeUrl: "https://rpc.testnet.near.org", -}; - -sendTransactions(); - -async function sendTransactions() { - const near = await connect({ ...config, keyStore }); - const account = await near.account(CONTRACT_NAME); - const newArgs = { staking_pool_whitelist_account_id: WHITELIST_ACCOUNT_ID }; - const result = await account.signAndSendTransaction({ - receiverId: CONTRACT_NAME, - actions: [ - transactions.deployContract(fs.readFileSync(WASM_PATH)), - transactions.functionCall( - "new", - Buffer.from(JSON.stringify(newArgs)), - 10000000000000, - "0" - ), - ], - }); - - console.log(result); -} diff --git a/packages/cookbook/transactions/batch-transactions.ts b/packages/cookbook/transactions/batch-transactions.ts new file mode 100644 index 0000000000..dc33dcc55e --- /dev/null +++ b/packages/cookbook/transactions/batch-transactions.ts @@ -0,0 +1,47 @@ +import { + getPlaintextFilesystemSigner, + getTestnetRpcProvider, + SignedTransactionComposer, +} from '@near-js/client'; +import chalk from 'chalk'; +import { readFile } from 'node:fs/promises'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +// NOTE: replace "example" with your accountId +const CONTRACT_NAME = 'contract.example.testnet'; +const WHITELIST_ACCOUNT_ID = 'whitelisted-account.example.testnet'; +const WASM_PATH = join(__dirname, '../utils/wasm-files/staking_pool_factory.wasm'); + +export default async function batchTransactions(accountId: string = CONTRACT_NAME, whitelistAccountId: string = WHITELIST_ACCOUNT_ID, wasmPath: string = WASM_PATH) { + if (!accountId || !whitelistAccountId || !wasmPath) { + console.log(chalk`{red pnpm batchTransactions -- [ACCOUNT_ID] [WHITELIST_ACCOUNT_ID] [WASM_PATH]}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + + const { result } = await SignedTransactionComposer.init({ + sender: accountId, + receiver: accountId, + deps: { rpcProvider, signer }, + }) + .deployContract(await readFile(wasmPath)) + .functionCall( + 'new', + Buffer.from(JSON.stringify({ staking_pool_whitelist_account_id: WHITELIST_ACCOUNT_ID })), + 10000000000000n, + ) + .signAndSend(); + + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.green RESULTS} {white Deployed contract at ${wasmPath} to ${accountId}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); + console.log(chalk`{bold.white Call result} {white |} {bold.yellow ${JSON.stringify(result, null, 2)}}`); + console.log(chalk`{white ------------------------------------------------------------------------ }`); +} diff --git a/packages/cookbook/transactions/get-tx-status.js b/packages/cookbook/transactions/get-tx-status.js deleted file mode 100644 index d6a20ffaeb..0000000000 --- a/packages/cookbook/transactions/get-tx-status.js +++ /dev/null @@ -1,18 +0,0 @@ -// demonstrates how to get a transaction status -const { providers } = require("near-api-js"); - -//network config (replace testnet with mainnet or betanet) -const provider = new providers.JsonRpcProvider( - "https://archival-rpc.testnet.near.org" -); - -const TX_HASH = "9av2U6cova7LZPA9NPij6CTUrpBbgPG6LKVkyhcCqtk3"; -// account ID associated with the transaction -const ACCOUNT_ID = "sender.testnet"; - -getState(TX_HASH, ACCOUNT_ID); - -async function getState(txHash, accountId) { - const result = await provider.txStatus(txHash, accountId); - console.log("Result: ", result); -} diff --git a/packages/cookbook/transactions/get-tx-status.ts b/packages/cookbook/transactions/get-tx-status.ts new file mode 100644 index 0000000000..bc4a609b0a --- /dev/null +++ b/packages/cookbook/transactions/get-tx-status.ts @@ -0,0 +1,20 @@ +import { + getTestnetRpcArchivalProvider, +} from '@near-js/client'; + +const TX_HASH = '9av2U6cova7LZPA9NPij6CTUrpBbgPG6LKVkyhcCqtk3'; +// account ID associated with the transaction +const ACCOUNT_ID = 'sender.testnet'; + +export default async function getTransactionStatus(accountId: string = ACCOUNT_ID, transactionHash: string = TX_HASH) { + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcArchivalProvider(); + + const result = await rpcProvider.getTransaction({ + account: accountId, + transactionHash, + includeReceipts: true, // toggle flag to include/exclude the `receipts` field + }); + + console.log(JSON.stringify(result, null, 2)); +} diff --git a/packages/cookbook/transactions/index.ts b/packages/cookbook/transactions/index.ts new file mode 100644 index 0000000000..6b78b23e7c --- /dev/null +++ b/packages/cookbook/transactions/index.ts @@ -0,0 +1,5 @@ +export * from './batch-transactions'; +export * from './get-tx-status'; +export * from './meta-transaction'; +export * from './meta-transaction-relayer'; +export * from './traverse-blocks'; diff --git a/packages/cookbook/transactions/meta-transaction-relayer.js b/packages/cookbook/transactions/meta-transaction-relayer.js deleted file mode 100644 index a0f5054fb1..0000000000 --- a/packages/cookbook/transactions/meta-transaction-relayer.js +++ /dev/null @@ -1,51 +0,0 @@ -const { Account } = require('@near-js/accounts'); -const { UnencryptedFileSystemKeyStore } = require('@near-js/keystores-node'); -const { JsonRpcProvider, fetchJson } = require('@near-js/providers'); -const { InMemorySigner } = require('@near-js/signers'); -const { actionCreators, encodeSignedDelegate } = require('@near-js/transactions'); -const os = require('os'); -const path = require('path'); - -const { transfer } = actionCreators; - -async function sendNearThroughRelayer({ amount, receiverId, senderAccount }) { - const signedDelegate = await senderAccount.signedDelegate({ - actions: [transfer(amount)], - blockHeightTtl: 60, - receiverId - }); - - return fetchJson( - 'https://relayer.org/relay', - JSON.stringify(Array.from(encodeSignedDelegate(signedDelegate))) - ); -} - -module.exports = { - sendNearThroughRelayer, -}; - -if (require.main === module) { - (async function () { - const networkId = 'testnet'; - const provider = new JsonRpcProvider({ url: 'https://rpc.testnet.near.org' }); - - const CREDENTIALS_DIR = '.near-credentials'; - const credentialsPath = path.join(os.homedir(), CREDENTIALS_DIR); - - const RECEIVER_ACCOUNT_ID = 'receiver.testnet'; // the ultimate recipient of the meta transaction execution - const SENDER_ACCOUNT_ID = 'sender.testnet'; // the account requesting the transaction be executed - - const senderAccount = new Account({ - networkId, - provider, - signer: new InMemorySigner(new UnencryptedFileSystemKeyStore(credentialsPath)) - }, SENDER_ACCOUNT_ID); - - console.log(await sendNearThroughRelayer({ - amount: BigInt('1000000000'), - receiverId: RECEIVER_ACCOUNT_ID, - senderAccount, - })); - }()); -} \ No newline at end of file diff --git a/packages/cookbook/transactions/meta-transaction-relayer.ts b/packages/cookbook/transactions/meta-transaction-relayer.ts new file mode 100644 index 0000000000..ccf6f8a057 --- /dev/null +++ b/packages/cookbook/transactions/meta-transaction-relayer.ts @@ -0,0 +1,38 @@ +import { + buildSignedDelegate, + getPlaintextFilesystemSigner, + getTestnetRpcProvider, +} from '@near-js/client'; +import { actionCreators, encodeSignedDelegate } from '@near-js/transactions'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +export default async function sendMetaTransactionViaRelayer(signerAccountId: string, receiverAccountId: string, relayerUrl: string) { + if (!signerAccountId || !receiverAccountId) { + console.log(chalk`{red pnpm metaTransaction -- SENDER_ACCOUNT_ID RECEIVER_ACCOUNT_ID RELAYER_URL}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const { signer } = getPlaintextFilesystemSigner({ account: signerAccountId, network: 'testnet', filepath: credentialsPath }); + + const signedDelegate = await buildSignedDelegate({ + account: signerAccountId, + receiver: receiverAccountId, + actions: [actionCreators.transfer(1000n)], + blockHeightTtl: 60, + deps: { rpcProvider, signer }, + }); + + // @ts-ignore global + const res = await fetch(relayerUrl, { + method: 'POST', + body: JSON.stringify(Array.from(encodeSignedDelegate(signedDelegate))), + }); + console.log(await res.json()); +} diff --git a/packages/cookbook/transactions/meta-transaction.js b/packages/cookbook/transactions/meta-transaction.js deleted file mode 100644 index a37a8c3562..0000000000 --- a/packages/cookbook/transactions/meta-transaction.js +++ /dev/null @@ -1,60 +0,0 @@ -const { Account } = require('@near-js/accounts'); -const { UnencryptedFileSystemKeyStore } = require('@near-js/keystores-node'); -const { JsonRpcProvider } = require('@near-js/providers'); -const { InMemorySigner } = require('@near-js/signers'); -const { actionCreators } = require('@near-js/transactions'); -const os = require('os'); -const path = require('path'); - -const { signedDelegate, transfer } = actionCreators; - -async function sendNearViaMetaTransaction({ amount, receiverId, senderAccount, signingAccount }) { - const delegate = await senderAccount.signedDelegate({ - actions: [transfer(amount)], - blockHeightTtl: 60, - receiverId, - }); - - return signingAccount.signAndSendTransaction({ - actions: [signedDelegate(delegate)], - receiverId: delegate.delegateAction.senderId, - }); -} - -module.exports = { - sendNearViaMetaTransaction, -}; - -if (require.main === module) { - (async function () { - const networkId = 'testnet'; - const provider = new JsonRpcProvider({ url: 'https://rpc.testnet.near.org' }); - - const CREDENTIALS_DIR = '.near-credentials'; - const credentialsPath = path.join(os.homedir(), CREDENTIALS_DIR); - - // access keys are required for the sender and signer - const RECEIVER_ACCOUNT_ID = 'receiver.testnet'; // the ultimate recipient of the meta transaction execution - const SENDER_ACCOUNT_ID = 'sender.testnet'; // the account requesting the transaction be executed - const SIGNER_ACCOUNT_ID = 'signer.testnet'; // the account executing the meta transaction on behalf (e.g. as a relayer) of the sender - - const senderAccount = new Account({ - networkId, - provider, - signer: new InMemorySigner(new UnencryptedFileSystemKeyStore(credentialsPath)) - }, SENDER_ACCOUNT_ID); - - const signingAccount = new Account({ - networkId, - provider, - signer: new InMemorySigner(new UnencryptedFileSystemKeyStore(credentialsPath)) - }, SIGNER_ACCOUNT_ID); - - console.log(await sendNearViaMetaTransaction({ - amount: BigInt('1000000000'), - receiverId: RECEIVER_ACCOUNT_ID, - senderAccount, - signingAccount, - })); - }()); -} \ No newline at end of file diff --git a/packages/cookbook/transactions/meta-transaction.ts b/packages/cookbook/transactions/meta-transaction.ts new file mode 100644 index 0000000000..ae6512cc9d --- /dev/null +++ b/packages/cookbook/transactions/meta-transaction.ts @@ -0,0 +1,60 @@ +import { + buildSignedDelegate, + getPlaintextFilesystemSigner, + getTestnetRpcProvider, + SignedTransactionComposer, +} from '@near-js/client'; +import { actionCreators } from '@near-js/transactions'; +import chalk from 'chalk'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + +// access keys are required for the sender and signer +const RECEIVER_ACCOUNT_ID = 'receiver.testnet'; // the ultimate recipient of the meta transaction execution +const SENDER_ACCOUNT_ID = 'sender.testnet'; // the account requesting the transaction be executed +const SIGNER_ACCOUNT_ID = 'signer.testnet'; // the account executing the meta transaction on behalf (e.g. as a relayer) of the sender + +export default async function metaTransaction(signerAccountId: string = SIGNER_ACCOUNT_ID, receiverAccountId: string = RECEIVER_ACCOUNT_ID, senderAccountId: string = SENDER_ACCOUNT_ID): Promise { + if (!signerAccountId || !receiverAccountId || !senderAccountId) { + console.log(chalk`{red pnpm metaTransaction -- [SIGNER_ACCOUNT_ID] [RECEIVER_ACCOUNT_ID] [SENDER_ACCOUNT_ID]}`); + return; + } + + const credentialsPath = join(homedir(), '.near-credentials'); + + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const { signer } = getPlaintextFilesystemSigner({ account: signerAccountId, network: 'testnet', filepath: credentialsPath }); + + // define the set of actions for the relayer to execute on behalf of the signer + const innerActions = [actionCreators.transfer(100n)]; + + // create the signed delegate action encapsulating the inner transaction + const { delegateAction, signature } = await buildSignedDelegate({ + account: signerAccountId, + receiver: receiverAccountId, + actions: innerActions, + blockHeightTtl: 100, + deps: { + rpcProvider, + signer, + }, + }); + + // initialize the relayer's signer + const { signer: relayerSigner } = getPlaintextFilesystemSigner({ + account: senderAccountId, + network: 'testnet', + filepath: credentialsPath, + }); + + // sign the outer transaction using the relayer's key + return SignedTransactionComposer.init({ + sender: senderAccountId, + receiver: receiverAccountId, + deps: { rpcProvider, signer: relayerSigner }, + }) + .signedDelegate(delegateAction, signature) + .signAndSend(); +} diff --git a/packages/cookbook/transactions/get-tx-detail.ts b/packages/cookbook/transactions/traverse-blocks.ts similarity index 92% rename from packages/cookbook/transactions/get-tx-detail.ts rename to packages/cookbook/transactions/traverse-blocks.ts index 589dc52937..9a37bf1d65 100644 --- a/packages/cookbook/transactions/get-tx-detail.ts +++ b/packages/cookbook/transactions/traverse-blocks.ts @@ -9,7 +9,7 @@ const END_BLOCK_HASH = "8aEcKhF7N1Jyw84e6vHW6Hzp3Ep7mSXJ6Rvnsy5qGJPF"; // contract ID or account ID you want to find transactions details for const CONTRACT_ID = "relayer.ropsten.testnet"; -export default async function getTransactions(startBlockHash: string = START_BLOCK_HASH, endBlockHash: string = END_BLOCK_HASH, contractId: string = CONTRACT_ID) { +export default async function traverseBlocks(startBlockHash: string = START_BLOCK_HASH, endBlockHash: string = END_BLOCK_HASH, contractId: string = CONTRACT_ID) { // initialize testnet RPC provider const rpcProvider = getTestnetRpcArchivalProvider(); diff --git a/packages/cookbook/utils/index.ts b/packages/cookbook/utils/index.ts index e28a85025e..0ca463d46b 100644 --- a/packages/cookbook/utils/index.ts +++ b/packages/cookbook/utils/index.ts @@ -1 +1,7 @@ export * from './calculate-gas'; +export * from './check-account-existence'; +export * from './deploy-contract'; +export * from './get-state'; +export * from './unwrap-near'; +export * from './verify-signature'; +export * from './wrap-near'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6908d7565..95f1482a87 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,7 +20,7 @@ importers: version: 2.24.4 '@commitlint/cli': specifier: 19.3.0 - version: 19.3.0(@types/node@20.5.1)(typescript@5.4.5) + version: 19.3.0(@types/node@22.5.5)(typescript@5.4.5) '@commitlint/config-conventional': specifier: 19.2.2 version: 19.2.2 @@ -32,7 +32,7 @@ importers: version: 6.21.0(eslint@8.20.0)(typescript@5.4.5) commitlint: specifier: 19.3.0 - version: 19.3.0(@types/node@20.5.1)(typescript@5.4.5) + version: 19.3.0(@types/node@22.5.5)(typescript@5.4.5) eslint: specifier: 8.20.0 version: 8.20.0 @@ -274,7 +274,7 @@ importers: version: 0.6.0 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.5.1)(typescript@5.4.5) + version: 10.9.2(@types/node@22.5.5)(typescript@5.4.5) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -389,13 +389,13 @@ importers: version: link:../build jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.5.1)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)) + version: 29.7.0(@types/node@22.5.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)) localstorage-memory: specifier: 1.0.3 version: 1.0.3 ts-jest: specifier: 29.1.5 - version: 29.1.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@20.5.1)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)))(typescript@5.4.5) + version: 29.1.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@22.5.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)))(typescript@5.4.5) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -1403,8 +1403,8 @@ packages: '@types/node@20.0.0': resolution: {integrity: sha512-cD2uPTDnQQCVpmRefonO98/PPijuOnnEy5oytWJFPY1N9aJCz2wJ5kSGWO+zJoed2cY2JxQh6yBuUq4vIn61hw==} - '@types/node@20.5.1': - resolution: {integrity: sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==} + '@types/node@22.5.5': + resolution: {integrity: sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -3908,6 +3908,9 @@ packages: unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + unfetch@4.2.0: resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} @@ -4476,11 +4479,11 @@ snapshots: human-id: 1.0.2 prettier: 2.8.8 - '@commitlint/cli@19.3.0(@types/node@20.5.1)(typescript@5.4.5)': + '@commitlint/cli@19.3.0(@types/node@22.5.5)(typescript@5.4.5)': dependencies: '@commitlint/format': 19.3.0 '@commitlint/lint': 19.2.2 - '@commitlint/load': 19.2.0(@types/node@20.5.1)(typescript@5.4.5) + '@commitlint/load': 19.2.0(@types/node@22.5.5)(typescript@5.4.5) '@commitlint/read': 19.2.1 '@commitlint/types': 19.0.3 execa: 8.0.1 @@ -4527,7 +4530,7 @@ snapshots: '@commitlint/rules': 19.0.3 '@commitlint/types': 19.0.3 - '@commitlint/load@19.2.0(@types/node@20.5.1)(typescript@5.4.5)': + '@commitlint/load@19.2.0(@types/node@22.5.5)(typescript@5.4.5)': dependencies: '@commitlint/config-validator': 19.0.3 '@commitlint/execute-rule': 19.0.0 @@ -4535,7 +4538,7 @@ snapshots: '@commitlint/types': 19.0.3 chalk: 5.3.0 cosmiconfig: 9.0.0(typescript@5.4.5) - cosmiconfig-typescript-loader: 5.0.0(@types/node@20.5.1)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5) + cosmiconfig-typescript-loader: 5.0.0(@types/node@22.5.5)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -4726,7 +4729,7 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5))': + '@jest/core@29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0(node-notifier@8.0.2) @@ -4740,7 +4743,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.0.0)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@20.0.0)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -5145,7 +5148,9 @@ snapshots: '@types/node@20.0.0': {} - '@types/node@20.5.1': {} + '@types/node@22.5.5': + dependencies: + undici-types: 6.19.8 '@types/normalize-package-data@2.4.4': {} @@ -5607,9 +5612,9 @@ snapshots: color-name@1.1.4: {} - commitlint@19.3.0(@types/node@20.5.1)(typescript@5.4.5): + commitlint@19.3.0(@types/node@22.5.5)(typescript@5.4.5): dependencies: - '@commitlint/cli': 19.3.0(@types/node@20.5.1)(typescript@5.4.5) + '@commitlint/cli': 19.3.0(@types/node@22.5.5)(typescript@5.4.5) '@commitlint/types': 19.0.3 transitivePeerDependencies: - '@types/node' @@ -5651,9 +5656,9 @@ snapshots: convert-source-map@2.0.0: {} - cosmiconfig-typescript-loader@5.0.0(@types/node@20.5.1)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5): + cosmiconfig-typescript-loader@5.0.0(@types/node@22.5.5)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5): dependencies: - '@types/node': 20.5.1 + '@types/node': 22.5.5 cosmiconfig: 9.0.0(typescript@5.4.5) jiti: 1.21.6 typescript: 5.4.5 @@ -5697,13 +5702,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.5.1)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)): + create-jest@29.7.0(@types/node@22.5.5)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.1 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.5.1)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@22.5.5)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -6679,16 +6684,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.5.1)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)): + jest-cli@29.7.0(@types/node@22.5.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)): dependencies: - '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)) + '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.1 - create-jest: 29.7.0(@types/node@20.5.1)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)) + create-jest: 29.7.0(@types/node@22.5.5)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.5.1)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@22.5.5)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -6793,7 +6798,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.0.0)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)): + jest-config@29.7.0(@types/node@20.0.0)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)): dependencies: '@babel/core': 7.24.7 '@jest/test-sequencer': 29.7.0 @@ -6819,12 +6824,12 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.0.0 - ts-node: 10.9.2(@types/node@20.5.1)(typescript@5.4.5) + ts-node: 10.9.2(@types/node@22.5.5)(typescript@5.4.5) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.5.1)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)): + jest-config@29.7.0(@types/node@22.5.5)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)): dependencies: '@babel/core': 7.24.7 '@jest/test-sequencer': 29.7.0 @@ -6849,8 +6854,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.5.1 - ts-node: 10.9.2(@types/node@20.5.1)(typescript@5.4.5) + '@types/node': 22.5.5 + ts-node: 10.9.2(@types/node@22.5.5)(typescript@5.4.5) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -7098,12 +7103,12 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@20.5.1)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)): + jest@29.7.0(@types/node@22.5.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)): dependencies: - '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)) + '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.5.1)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)) + jest-cli: 29.7.0(@types/node@22.5.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)) optionalDependencies: node-notifier: 8.0.2 transitivePeerDependencies: @@ -8024,11 +8029,11 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.24.7) - ts-jest@29.1.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@20.5.1)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)))(typescript@5.4.5): + ts-jest@29.1.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@22.5.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)))(typescript@5.4.5): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.5.1)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5)) + jest: 29.7.0(@types/node@22.5.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -8080,14 +8085,14 @@ snapshots: yn: 3.1.1 optional: true - ts-node@10.9.2(@types/node@20.5.1)(typescript@5.4.5): + ts-node@10.9.2(@types/node@22.5.5)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.5.1 + '@types/node': 22.5.5 acorn: 8.12.0 acorn-walk: 8.3.3 arg: 4.1.3 @@ -8234,6 +8239,8 @@ snapshots: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + undici-types@6.19.8: {} + unfetch@4.2.0: {} unicorn-magic@0.1.0: {} From 295df17617c5061155c73ae72dcc2b13e4b2450a Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 18 Sep 2024 15:20:14 -0700 Subject: [PATCH 31/55] feat: ledger --- packages/client/package.json | 9 +- packages/client/src/constants.ts | 2 + packages/client/src/signing/index.ts | 4 + packages/client/src/signing/ledger.ts | 34 ++ packages/cookbook/package.json | 1 + packages/cookbook/utils/index.ts | 1 + packages/cookbook/utils/ledger-signing.ts | 24 ++ pnpm-lock.yaml | 404 +++++++++++++++++++++- 8 files changed, 476 insertions(+), 3 deletions(-) create mode 100644 packages/client/src/signing/ledger.ts create mode 100644 packages/cookbook/utils/ledger-signing.ts diff --git a/packages/client/package.json b/packages/client/package.json index 4420a08219..4550fe292f 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -12,6 +12,7 @@ "lint:fix": "eslint -c .eslintrc.yml src/**/*.ts --no-eslintrc --no-error-on-unmatched-pattern --fix" }, "dependencies": { + "@ledgerhq/hw-transport-node-hid": "6.29.4", "@near-js/crypto": "workspace:*", "@near-js/keystores": "workspace:*", "@near-js/keystores-node": "workspace:*", @@ -21,13 +22,19 @@ "@near-js/types": "workspace:*", "@near-js/utils": "workspace:*", "@noble/hashes": "1.3.3", - "isomorphic-fetch": "3.0.0" + "isomorphic-fetch": "3.0.0", + "near-ledger-js": "0.2.1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { + "@ledgerhq/devices": "8.4.3", + "@ledgerhq/hw-transport-node-hid-noevents": "6.30.4", + "@ledgerhq/logs": "6.12.0", + "@types/ledgerhq__hw-transport": "4.21.8", "@types/node": "20.0.0", + "@types/node-hid": "1.3.4", "build": "workspace:*", "tsconfig": "workspace:*", "typescript": "5.4.5" diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts index 81a68d6887..94b7221d19 100644 --- a/packages/client/src/constants.ts +++ b/packages/client/src/constants.ts @@ -4,6 +4,8 @@ export const TESTNET_RPC_URL = 'https://rpc.testnet.near.org'; export const DEFAULT_FILESYSTEM_KEYSTORE_PATH = '.near-credentials'; export const DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL = BigInt(100); +export const LEDGER_HD_PATH = `44'/397'/0'/0'/1`; + export const MAX_GAS = 300000000000000n; export const PAGODA_RPC_ENDPOINTS_MAINNET = [ diff --git a/packages/client/src/signing/index.ts b/packages/client/src/signing/index.ts index 97f0cbef49..1f9285affb 100644 --- a/packages/client/src/signing/index.ts +++ b/packages/client/src/signing/index.ts @@ -1,6 +1,10 @@ export { getPlaintextFilesystemSigner, } from './keystores/plaintext_filesystem'; +export { + getUsbLedgerSigner, + getBrowserLedgerSigner, +} from './ledger'; export { getSignerFromKeyPair, getSignerFromPrivateKey, diff --git a/packages/client/src/signing/ledger.ts b/packages/client/src/signing/ledger.ts new file mode 100644 index 0000000000..1a362d1a44 --- /dev/null +++ b/packages/client/src/signing/ledger.ts @@ -0,0 +1,34 @@ +import TransportNodeHidModule from '@ledgerhq/hw-transport-node-hid'; +import { KeyType, PublicKey } from '@near-js/crypto'; +import { createClient, getSupportedTransport } from 'near-ledger-js'; + +import { LEDGER_HD_PATH } from '../constants'; +import type { MessageSigner } from '../interfaces'; + +export async function getNearLedgerSigner(transport: any, hdPath: string) { + if (!transport) { + throw new Error('Invalid transport for ledger signer'); + } + + transport.setScrambleKey('NEAR'); + const client = await createClient(transport); + return { + async getPublicKey(): Promise { + return new PublicKey({ + keyType: KeyType.ED25519, + data: await client.getPublicKey(), + }); + }, + async signMessage(m: Uint8Array): Promise { + return client.sign(m, hdPath); + }, + }; +} + +export async function getBrowserLedgerSigner(hdPath: string = LEDGER_HD_PATH): Promise { + return getNearLedgerSigner(await getSupportedTransport(), hdPath); +} + +export async function getUsbLedgerSigner(hdPath: string = LEDGER_HD_PATH): Promise { + return getNearLedgerSigner(await TransportNodeHidModule.create(), hdPath); +} diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index 2518d8289e..930548bb5c 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -25,6 +25,7 @@ "getTransactionStatus": "tsx -e \"import f from './transactions/get-tx-status.ts'; f(...process.argv.slice(1));\"", "metaTransaction": "tsx -e \"import f from './transactions/meta-transaction.ts'; f(...process.argv.slice(1));\"", "metaTransactionRelayer": "tsx -e \"import f from './transactions/meta-transaction-relayer.ts'; f(...process.argv.slice(1));\"", + "signWithLedger": "tsx -e \"import f from './utils/ledger-signing.ts'; f(...process.argv.slice(1));\"", "traverseBlocks": "tsx -e \"import f from './transactions/traverse-blocks.ts'; f(...process.argv.slice(1));\"", "unwrapNear": "tsx -e \"import f from './utils/unwrap-near.ts'; f(...process.argv.slice(1));\"", "verifySignature": "tsx -e \"import f from './utils/verify-signature.ts'; f(...process.argv.slice(1));\"", diff --git a/packages/cookbook/utils/index.ts b/packages/cookbook/utils/index.ts index 0ca463d46b..fb1096d1ff 100644 --- a/packages/cookbook/utils/index.ts +++ b/packages/cookbook/utils/index.ts @@ -2,6 +2,7 @@ export * from './calculate-gas'; export * from './check-account-existence'; export * from './deploy-contract'; export * from './get-state'; +export * from './ledger-signing'; export * from './unwrap-near'; export * from './verify-signature'; export * from './wrap-near'; diff --git a/packages/cookbook/utils/ledger-signing.ts b/packages/cookbook/utils/ledger-signing.ts new file mode 100644 index 0000000000..da820b6614 --- /dev/null +++ b/packages/cookbook/utils/ledger-signing.ts @@ -0,0 +1,24 @@ +import { getTestnetRpcProvider, getUsbLedgerSigner, SignedTransactionComposer } from '@near-js/client'; + +export default async function signWithLedger() { + const ledgerSigner = await getUsbLedgerSigner(); + const publicKey = await ledgerSigner.getPublicKey(); + + const rpcProvider = getTestnetRpcProvider(); + + const { signedTransaction: { signature: { data } } } = await SignedTransactionComposer.init({ + sender: 'a.testnet', + receiver: 'b.testnet', + nonce: 100n, + blockHeader: (await rpcProvider.block({ finality: 'optimistic' })).header, + publicKey, + deps: { + rpcProvider, + signer: ledgerSigner, + } + }) + .transfer(100n) + .toSignedTransaction(); + + console.log(`Signed message with ledger key ${publicKey}: ${Buffer.from(data).toString('base64')}`); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95f1482a87..8dd62ea179 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -193,6 +193,9 @@ importers: packages/client: dependencies: + '@ledgerhq/hw-transport-node-hid': + specifier: 6.29.4 + version: 6.29.4 '@near-js/crypto': specifier: workspace:* version: link:../crypto @@ -223,10 +226,28 @@ importers: isomorphic-fetch: specifier: 3.0.0 version: 3.0.0(encoding@0.1.13) + near-ledger-js: + specifier: 0.2.1 + version: 0.2.1 devDependencies: + '@ledgerhq/devices': + specifier: 8.4.3 + version: 8.4.3 + '@ledgerhq/hw-transport-node-hid-noevents': + specifier: 6.30.4 + version: 6.30.4 + '@ledgerhq/logs': + specifier: 6.12.0 + version: 6.12.0 + '@types/ledgerhq__hw-transport': + specifier: 4.21.8 + version: 4.21.8 '@types/node': specifier: 20.0.0 version: 20.0.0 + '@types/node-hid': + specifier: 1.3.4 + version: 1.3.4 build: specifier: workspace:* version: link:../build @@ -1236,6 +1257,46 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@ledgerhq/devices@5.51.1': + resolution: {integrity: sha512-4w+P0VkbjzEXC7kv8T1GJ/9AVaP9I6uasMZ/JcdwZBS3qwvKo5A5z9uGhP5c7TvItzcmPb44b5Mw2kT+WjUuAA==} + + '@ledgerhq/devices@8.4.3': + resolution: {integrity: sha512-+ih+M27E6cm6DHrmw3GbS3mEaznCyFc0e62VdQux40XK2psgYhL2yBPftM4KCrBYm1UbHqXzqLN+Jb7rNIzsHg==} + + '@ledgerhq/errors@5.50.0': + resolution: {integrity: sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow==} + + '@ledgerhq/errors@6.19.0': + resolution: {integrity: sha512-c3Jid7euMSnpHFp8H7iPtsmKDjwbTjlG46YKdw+RpCclsqtBx1uQDlYmcbP1Yv9201kVlUFUhhP4H623k8xzlQ==} + + '@ledgerhq/hw-transport-node-hid-noevents@6.30.4': + resolution: {integrity: sha512-01cPvdIe8xwfrcOoR7vr2kN/4HHYVvAydU7hAHvLMC76irbhkbuTZiWzhr54FmEOdqz+fZnC+MUDIQHLQsjXQw==} + + '@ledgerhq/hw-transport-node-hid@6.29.4': + resolution: {integrity: sha512-Spwb7L9YnKrhvRVj5hJ1DzmvQJHVeNSmN+yTVSFQ4QRcjqDKC+N+zVO/7HDhLl5TWo1KmgNx4/BhAZ648gpp5w==} + + '@ledgerhq/hw-transport-u2f@5.36.0-deprecated': + resolution: {integrity: sha512-T/+mGHIiUK/ZQATad6DMDmobCMZ1mVST952009jKzhaE1Et2Uy2secU+QhRkx3BfEAkvwa0zSRSYCL9d20Iqjg==} + deprecated: '@ledgerhq/hw-transport-u2f is deprecated. Please use @ledgerhq/hw-transport-webusb or @ledgerhq/hw-transport-webhid. https://github.com/LedgerHQ/ledgerjs/blob/master/docs/migrate_webusb.md' + + '@ledgerhq/hw-transport-webhid@5.51.1': + resolution: {integrity: sha512-w/2qSU0vwFY+D/4ucuYRViO7iS3Uuxmj9sI/Iiqkoiax9Xppb0/6H5m3ffKv6iPMmRYbgwCgXorqx4SLLSD8Kg==} + + '@ledgerhq/hw-transport-webusb@5.53.1': + resolution: {integrity: sha512-A/f+xcrkIAZiJrvPpDvsrjxQX4cI2kbdiunQkwsYmOG3Bp4z89ZnsBiC7YBst4n2/g+QgTg0/KPVtODU5djooQ==} + + '@ledgerhq/hw-transport@5.51.1': + resolution: {integrity: sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw==} + + '@ledgerhq/hw-transport@6.31.3': + resolution: {integrity: sha512-rFplkHWF5NXtlYwAusqLlMu298NHtRD+2q/jrTYc//uu/xJO9LkDIgKid6IVF2+e1Wj7yX6YQVrU6L0Yu1ntEw==} + + '@ledgerhq/logs@5.50.0': + resolution: {integrity: sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA==} + + '@ledgerhq/logs@6.12.0': + resolution: {integrity: sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA==} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -1391,9 +1452,15 @@ packages: '@types/keyv@3.1.4': resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + '@types/ledgerhq__hw-transport@4.21.8': + resolution: {integrity: sha512-uO2AJYZUVCwgyqgyy2/KW+JsQaO0hcwDdubRaHgF2ehO0ngGAY41PbE8qnPnmUw1uerMXONvL68QFioA7Y6C5g==} + '@types/minimist@1.2.5': resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} + '@types/node-hid@1.3.4': + resolution: {integrity: sha512-0ootpsYetN9vjqkDSwm/cA4fk/9yGM/PO0X8SLPE/BzXlUaBelImMWMymtF9QEoEzxY0pnhcROIJM0CNSUqO8w==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -1421,6 +1488,9 @@ packages: '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + '@types/w3c-web-usb@1.0.10': + resolution: {integrity: sha512-CHgUI5kTc/QLMP8hODUHhge0D4vx+9UiAwIGiT0sTy/B2XpdX1U5rJt6JSISgr6ikRT7vxV9EVAFeYZqUnl1gQ==} + '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -1638,6 +1708,12 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bn.js@4.12.0: resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} @@ -1687,6 +1763,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -1762,6 +1841,9 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -1936,6 +2018,10 @@ packages: babel-plugin-macros: optional: true + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -2145,6 +2231,10 @@ packages: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2186,6 +2276,9 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -2219,6 +2312,9 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -2299,6 +2395,9 @@ packages: engines: {node: '>=16'} hasBin: true + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2480,6 +2579,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + ini@4.1.1: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3058,6 +3160,9 @@ packages: resolution: {integrity: sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q==} engines: {node: '>= 8.0.0'} + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} @@ -3071,6 +3176,9 @@ packages: engines: {npm: '>=1.4.0'} hasBin: true + napi-build-utils@1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -3083,6 +3191,9 @@ packages: near-hello@0.5.1: resolution: {integrity: sha512-k7S8VFyESWgkKYDso99B4XbxAdo0VX9b3+GAaO5PvMgQjNr/6o09PHRywg/NkBQpf+ZYj7nNpJcyrNJGQsvA3w==} + near-ledger-js@0.2.1: + resolution: {integrity: sha512-8anZb6e96gJNBOKUR/HReLN/x8BmBhCpyPq+XxFbx8jxmsRz+M1Hxq085+ROYaMI2EDJqatrjjLAdArk13BOhA==} + near-sandbox@0.0.18: resolution: {integrity: sha512-6/AjLOzGLQsSzTHavrRe7CVMU5+CUnTg/7g66a7+jkP22ff5CupnwXrUE14iJ2QyyhRZr7VuQQj/G+glUmaNQg==} hasBin: true @@ -3095,9 +3206,19 @@ packages: resolution: {integrity: sha512-lkRCP2os8v7W+q1maQPSEI98f+YiPb86ibDyIy8bxr6fqnJPOsIRR3xE33rOwBsiph3WuUH46qtpYK0a+Co+EQ==} engines: {node: '>= 14.0.0', npm: '>= 6.0.0'} + node-abi@3.67.0: + resolution: {integrity: sha512-bLn/fU/ALVBE9wj+p4Y21ZJWYFjUXLXPi/IewyLZkx3ApxKDNBWCKdReeKOtD8dWpOdDCeMyLh6ZewzcLsG2Nw==} + engines: {node: '>=10'} + + node-addon-api@3.2.1: + resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} + node-addon-api@5.1.0: resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + node-addon-api@6.1.0: + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} engines: {node: 4.x || >=6.0.0} @@ -3115,6 +3236,11 @@ packages: resolution: {integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==} hasBin: true + node-hid@2.1.2: + resolution: {integrity: sha512-qhCyQqrPpP93F/6Wc/xUR7L8mAJW0Z6R7HMQV8jCHHksAxNDe/4z4Un/H9CpLOT+5K39OPyt9tIQlavxWES3lg==} + engines: {node: '>=10'} + hasBin: true + node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -3284,10 +3410,18 @@ packages: resolution: {integrity: sha512-iDUm90wfgtfd1PDV1oEnQj/4jBIU9hCSJeV0kQKThwDpbseFxC4TdpoMYlwE9maol5u0wMGZX9cNG2h1/0Lhww==} engines: {node: '>=12.0.0'} + platform@1.3.6: + resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} + prebuild-install@7.1.2: + resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} + engines: {node: '>=10'} + hasBin: true + preferred-pm@3.1.3: resolution: {integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w==} engines: {node: '>=10'} @@ -3354,6 +3488,10 @@ packages: randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -3369,6 +3507,10 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -3442,6 +3584,10 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rxjs@6.6.7: + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} + rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} @@ -3531,6 +3677,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -3609,6 +3761,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3637,6 +3792,10 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -3657,6 +3816,13 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -3767,6 +3933,9 @@ packages: '@swc/wasm': optional: true + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} @@ -3775,6 +3944,9 @@ packages: engines: {node: '>=8.0.0'} hasBin: true + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + turbo-android-arm64@1.4.5: resolution: {integrity: sha512-cKPJVyS1A2BBVbcH8XVeBArtEjHxioEm9zQa3Hv68usQOOFW+KOjH+0fGvjqMrWztLVFhE+npeVsnyu/6AmRew==} cpu: [arm64] @@ -3905,6 +4077,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + u2f-api@0.2.7: + resolution: {integrity: sha512-fqLNg8vpvLOD5J/z4B6wpPg4Lvowz1nJ9xdHcCzdUPKcFE/qNCceV2gNZxSJd5vhAZemHr/K/hbzVA0zxB5mkg==} + unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -3935,6 +4110,13 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + usb@2.9.0: + resolution: {integrity: sha512-G0I/fPgfHUzWH8xo2KkDxTTFruUWfppgSFJ+bQxz/kVY2x15EQ/XDB7dqD1G432G4gBG4jYQuF3U7j/orSs5nw==} + engines: {node: '>=10.20.0 <11.x || >=12.17.0 <13.0 || >=14.0.0'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -4908,6 +5090,81 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@ledgerhq/devices@5.51.1': + dependencies: + '@ledgerhq/errors': 5.50.0 + '@ledgerhq/logs': 5.50.0 + rxjs: 6.6.7 + semver: 7.6.2 + + '@ledgerhq/devices@8.4.3': + dependencies: + '@ledgerhq/errors': 6.19.0 + '@ledgerhq/logs': 6.12.0 + rxjs: 7.8.1 + semver: 7.6.2 + + '@ledgerhq/errors@5.50.0': {} + + '@ledgerhq/errors@6.19.0': {} + + '@ledgerhq/hw-transport-node-hid-noevents@6.30.4': + dependencies: + '@ledgerhq/devices': 8.4.3 + '@ledgerhq/errors': 6.19.0 + '@ledgerhq/hw-transport': 6.31.3 + '@ledgerhq/logs': 6.12.0 + node-hid: 2.1.2 + + '@ledgerhq/hw-transport-node-hid@6.29.4': + dependencies: + '@ledgerhq/devices': 8.4.3 + '@ledgerhq/errors': 6.19.0 + '@ledgerhq/hw-transport': 6.31.3 + '@ledgerhq/hw-transport-node-hid-noevents': 6.30.4 + '@ledgerhq/logs': 6.12.0 + lodash: 4.17.21 + node-hid: 2.1.2 + usb: 2.9.0 + + '@ledgerhq/hw-transport-u2f@5.36.0-deprecated': + dependencies: + '@ledgerhq/errors': 5.50.0 + '@ledgerhq/hw-transport': 5.51.1 + '@ledgerhq/logs': 5.50.0 + u2f-api: 0.2.7 + + '@ledgerhq/hw-transport-webhid@5.51.1': + dependencies: + '@ledgerhq/devices': 5.51.1 + '@ledgerhq/errors': 5.50.0 + '@ledgerhq/hw-transport': 5.51.1 + '@ledgerhq/logs': 5.50.0 + + '@ledgerhq/hw-transport-webusb@5.53.1': + dependencies: + '@ledgerhq/devices': 5.51.1 + '@ledgerhq/errors': 5.50.0 + '@ledgerhq/hw-transport': 5.51.1 + '@ledgerhq/logs': 5.50.0 + + '@ledgerhq/hw-transport@5.51.1': + dependencies: + '@ledgerhq/devices': 5.51.1 + '@ledgerhq/errors': 5.50.0 + events: 3.3.0 + + '@ledgerhq/hw-transport@6.31.3': + dependencies: + '@ledgerhq/devices': 8.4.3 + '@ledgerhq/errors': 6.19.0 + '@ledgerhq/logs': 6.12.0 + events: 3.3.0 + + '@ledgerhq/logs@5.50.0': {} + + '@ledgerhq/logs@6.12.0': {} + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.24.7 @@ -5140,8 +5397,16 @@ snapshots: dependencies: '@types/node': 20.0.0 + '@types/ledgerhq__hw-transport@4.21.8': + dependencies: + '@types/node': 20.0.0 + '@types/minimist@1.2.5': {} + '@types/node-hid@1.3.4': + dependencies: + '@types/node': 20.0.0 + '@types/node@12.20.55': {} '@types/node@18.11.18': {} @@ -5164,6 +5429,8 @@ snapshots: '@types/stack-utils@2.0.3': {} + '@types/w3c-web-usb@1.0.10': {} + '@types/yargs-parser@21.0.3': {} '@types/yargs@17.0.32': @@ -5433,6 +5700,16 @@ snapshots: dependencies: is-windows: 1.0.2 + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + bn.js@4.12.0: {} bn.js@5.2.1: {} @@ -5489,6 +5766,11 @@ snapshots: buffer-from@1.1.2: {} + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -5572,6 +5854,8 @@ snapshots: chardet@0.7.0: {} + chownr@1.1.4: {} + chownr@2.0.0: {} ci-info@3.9.0: {} @@ -5787,6 +6071,8 @@ snapshots: dedent@1.5.3: {} + deep-extend@0.6.0: {} + deep-is@0.1.4: {} deepmerge@4.3.1: {} @@ -5815,8 +6101,7 @@ snapshots: detect-indent@6.1.0: {} - detect-libc@2.0.3: - optional: true + detect-libc@2.0.3: {} detect-newline@3.1.0: {} @@ -6063,6 +6348,8 @@ snapshots: exit@0.1.2: {} + expand-template@2.0.3: {} + expect@29.7.0: dependencies: '@jest/expect-utils': 29.7.0 @@ -6117,6 +6404,8 @@ snapshots: dependencies: flat-cache: 3.2.0 + file-uri-to-path@1.0.0: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -6159,6 +6448,8 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 + fs-constants@1.0.0: {} + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -6241,6 +6532,8 @@ snapshots: meow: 12.1.1 split2: 4.2.0 + github-from-package@0.0.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -6422,6 +6715,8 @@ snapshots: inherits@2.0.4: {} + ini@1.3.8: {} + ini@4.1.1: {} internal-slot@1.0.7: @@ -7337,12 +7632,16 @@ snapshots: mixme@0.5.10: {} + mkdirp-classic@0.5.3: {} + mkdirp@1.0.4: {} ms@2.1.2: {} mustache@4.0.0: {} + napi-build-utils@1.0.2: {} + natural-compare@1.4.0: {} near-abi@0.1.1: @@ -7373,6 +7672,14 @@ snapshots: near-hello@0.5.1: {} + near-ledger-js@0.2.1: + dependencies: + '@ledgerhq/hw-transport-u2f': 5.36.0-deprecated + '@ledgerhq/hw-transport-webhid': 5.51.1 + '@ledgerhq/hw-transport-webusb': 5.53.1 + bs58: 4.0.1 + platform: 1.3.6 + near-sandbox@0.0.18: dependencies: got: 11.8.6 @@ -7403,8 +7710,16 @@ snapshots: transitivePeerDependencies: - encoding + node-abi@3.67.0: + dependencies: + semver: 7.6.2 + + node-addon-api@3.2.1: {} + node-addon-api@5.1.0: {} + node-addon-api@6.1.0: {} + node-fetch@2.6.7(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 @@ -7418,6 +7733,12 @@ snapshots: node-gyp-build@4.8.1: {} + node-hid@2.1.2: + dependencies: + bindings: 1.5.0 + node-addon-api: 3.2.1 + prebuild-install: 7.1.2 + node-int64@0.4.0: {} node-notifier@8.0.2: @@ -7575,8 +7896,25 @@ snapshots: pvutils: 1.1.3 tslib: 2.6.3 + platform@1.3.6: {} + possible-typed-array-names@1.0.0: {} + prebuild-install@7.1.2: + dependencies: + detect-libc: 2.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.67.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + preferred-pm@3.1.3: dependencies: find-up: 5.0.0 @@ -7636,6 +7974,13 @@ snapshots: dependencies: safe-buffer: 5.2.1 + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + react-is@18.3.1: {} read-pkg-up@7.0.1: @@ -7658,6 +8003,12 @@ snapshots: pify: 4.0.1 strip-bom: 3.0.0 + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -7719,6 +8070,10 @@ snapshots: dependencies: queue-microtask: 1.2.3 + rxjs@6.6.7: + dependencies: + tslib: 1.14.1 + rxjs@7.8.1: dependencies: tslib: 2.6.3 @@ -7809,6 +8164,14 @@ snapshots: signal-exit@4.1.0: {} + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + sisteransi@1.0.5: {} slash@3.0.0: {} @@ -7900,6 +8263,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -7920,6 +8287,8 @@ snapshots: dependencies: min-indent: 1.0.1 + strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} supports-color@5.5.0: @@ -7936,6 +8305,21 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tar-fs@2.1.1: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + tar@6.2.1: dependencies: chownr: 2.0.0 @@ -8103,6 +8487,8 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + tslib@1.14.1: {} + tslib@2.6.3: {} tty-table@4.2.3: @@ -8115,6 +8501,10 @@ snapshots: wcwidth: 1.0.1 yargs: 17.7.2 + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + turbo-android-arm64@1.4.5: optional: true @@ -8232,6 +8622,8 @@ snapshots: typescript@5.4.5: {} + u2f-api@0.2.7: {} + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 @@ -8259,6 +8651,14 @@ snapshots: dependencies: punycode: 2.3.1 + usb@2.9.0: + dependencies: + '@types/w3c-web-usb': 1.0.10 + node-addon-api: 6.1.0 + node-gyp-build: 4.8.1 + + util-deprecate@1.0.2: {} + uuid@8.3.2: optional: true From 951b46a557ebedc31b95316e23c524beb8e03990 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 18 Sep 2024 15:23:09 -0700 Subject: [PATCH 32/55] feat: toSignedTransaction method --- packages/client/src/transactions/composer.ts | 38 +++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/packages/client/src/transactions/composer.ts b/packages/client/src/transactions/composer.ts index 1144734c05..0b0777ea8b 100644 --- a/packages/client/src/transactions/composer.ts +++ b/packages/client/src/transactions/composer.ts @@ -7,11 +7,11 @@ import { Signature, Transaction, } from '@near-js/transactions'; -import { baseDecode, DEFAULT_FUNCTION_CALL_GAS } from '@near-js/utils'; +import { baseDecode, DEFAULT_FUNCTION_CALL_GAS, getTransactionLastResult } from '@near-js/utils'; import { DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL } from '../constants'; import { BlockHeader, BlockReference } from '@near-js/types'; import { MessageSigner, RpcQueryProvider, SignAndSendTransactionDependency } from '../interfaces'; -import { getSignerNonce, signAndSendTransaction } from './sign_and_send'; +import { getSignerNonce, signTransaction } from './sign_and_send'; interface TransactionOptions { blockHeader?: BlockHeader; @@ -21,7 +21,8 @@ interface TransactionOptions { sender?: string; } -interface SignedTransactionOptions extends TransactionOptions, SignAndSendTransactionDependency {} +interface SignedTransactionOptions extends TransactionOptions, SignAndSendTransactionDependency { +} export class TransactionComposer { private actions: Action[] = []; @@ -83,42 +84,42 @@ export class TransactionComposer { this.actions.push(actionCreators.addKey(PublicKey.from(publicKey), accessKey)); return this; } - + createAccount(): this { this.actions.push(actionCreators.createAccount()); return this; } - + deleteAccount(beneficiaryId: string): this { this.actions.push(actionCreators.deleteAccount(beneficiaryId)); return this; } - + deleteKey(publicKey: string): this { this.actions.push(actionCreators.deleteKey(PublicKey.from(publicKey))); return this; } - + deployContract(code: Uint8Array): this { this.actions.push(actionCreators.deployContract(code)); return this; } - + functionCall(method: string, args: object, gas: bigint = DEFAULT_FUNCTION_CALL_GAS * BigInt(10), deposit = BigInt(0)): this { this.actions.push(actionCreators.functionCall(method, args, gas, deposit)); return this; } - + signedDelegate(delegateAction: DelegateAction, signature: Signature): this { this.actions.push(actionCreators.signedDelegate({ delegateAction, signature })); return this; } - + stake(stake: bigint, publicKey: string): this { this.actions.push(actionCreators.stake(stake, PublicKey.from(publicKey))); return this; } - + transfer(deposit: bigint): this { this.actions.push(actionCreators.transfer(deposit)); return this; @@ -139,17 +140,28 @@ export class SignedTransactionComposer extends TransactionComposer { return new SignedTransactionComposer(options); } + async toSignedTransaction(transaction?: TransactionOptions) { + return signTransaction({ + transaction: this.toTransaction(transaction), + deps: { signer: this.signer }, + }); + } + async signAndSend(blockReference: BlockReference = { finality: 'final' }) { const deps = { rpcProvider: this.rpcProvider, signer: this.signer }; const blockHeader = this.blockHeader || (await this.rpcProvider.block(blockReference))?.header; const signerNonce = this.nonce || (await getSignerNonce({ account: this.sender, blockReference, deps }) + 1n); - const transaction = this.toTransaction({ + const { signedTransaction } = await this.toSignedTransaction({ nonce: signerNonce, publicKey: this.publicKey || await this.signer.getPublicKey(), blockHeader, }); - return signAndSendTransaction({ transaction, deps }); + const outcome = await this.rpcProvider.sendTransaction(signedTransaction); + return { + outcome, + result: getTransactionLastResult(outcome), + }; } } From 2fa73c13abe299920dd6165563ec16513a9e637a Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 18 Sep 2024 15:33:38 -0700 Subject: [PATCH 33/55] chore: update cookbook dependencies --- packages/cookbook/package.json | 5 +---- pnpm-lock.yaml | 15 +++------------ 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index 930548bb5c..96b5e91fb0 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -32,17 +32,14 @@ "wrapNear": "tsx -e \"import f from './utils/wrap-near.ts'; f(...process.argv.slice(1));\"" }, "devDependencies": { - "@near-js/accounts": "workspace:*", "@near-js/client": "workspace:*", "@near-js/crypto": "workspace:*", - "@near-js/keystores-node": "workspace:*", - "@near-js/providers": "workspace:*", - "@near-js/signers": "workspace:*", "@near-js/transactions": "workspace:*", "@near-js/utils": "workspace:*", "build": "workspace:*", "chalk": "4.1.1", "homedir": "0.6.0", + "near-api-js": "workspace:*", "ts-node": "^10.9.2", "tsconfig": "workspace:*", "typescript": "5.4.5" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8dd62ea179..cd7180a918 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -260,24 +260,12 @@ importers: packages/cookbook: devDependencies: - '@near-js/accounts': - specifier: workspace:* - version: link:../accounts '@near-js/client': specifier: workspace:* version: link:../client '@near-js/crypto': specifier: workspace:* version: link:../crypto - '@near-js/keystores-node': - specifier: workspace:* - version: link:../keystores-node - '@near-js/providers': - specifier: workspace:* - version: link:../providers - '@near-js/signers': - specifier: workspace:* - version: link:../signers '@near-js/transactions': specifier: workspace:* version: link:../transactions @@ -293,6 +281,9 @@ importers: homedir: specifier: 0.6.0 version: 0.6.0 + near-api-js: + specifier: 4.0.0 + version: 4.0.0(encoding@0.1.13) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.5.5)(typescript@5.4.5) From 32d17ae6a5d3ecba8d0303e51b0de7fdc87cfef4 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 19 Sep 2024 09:54:41 -0700 Subject: [PATCH 34/55] feat: optional public key cache for ledger --- packages/client/src/signing/ledger.ts | 30 +++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/client/src/signing/ledger.ts b/packages/client/src/signing/ledger.ts index 1a362d1a44..e06d666726 100644 --- a/packages/client/src/signing/ledger.ts +++ b/packages/client/src/signing/ledger.ts @@ -4,19 +4,37 @@ import { createClient, getSupportedTransport } from 'near-ledger-js'; import { LEDGER_HD_PATH } from '../constants'; import type { MessageSigner } from '../interfaces'; +import type Transport from '@ledgerhq/hw-transport'; -export async function getNearLedgerSigner(transport: any, hdPath: string) { +interface LedgerSignerParams { + transport: Transport; + hdPath: string; + cachePublicKey: boolean; +} + +export async function getNearLedgerSigner({ transport, hdPath, cachePublicKey }: LedgerSignerParams) { if (!transport) { throw new Error('Invalid transport for ledger signer'); } transport.setScrambleKey('NEAR'); const client = await createClient(transport); + let cachedKeyData: Uint8Array = null; + return { async getPublicKey(): Promise { + let publicKeyData = cachePublicKey ? cachedKeyData : null; + if (!publicKeyData) { + publicKeyData = await client.getPublicKey(); + + if (cachePublicKey) { + cachedKeyData = publicKeyData; + } + } + return new PublicKey({ keyType: KeyType.ED25519, - data: await client.getPublicKey(), + data: publicKeyData, }); }, async signMessage(m: Uint8Array): Promise { @@ -25,10 +43,10 @@ export async function getNearLedgerSigner(transport: any, hdPath: string) { }; } -export async function getBrowserLedgerSigner(hdPath: string = LEDGER_HD_PATH): Promise { - return getNearLedgerSigner(await getSupportedTransport(), hdPath); +export async function getBrowserLedgerSigner(hdPath: string = LEDGER_HD_PATH, cachePublicKey: boolean = false): Promise { + return getNearLedgerSigner({ transport: await getSupportedTransport(), hdPath, cachePublicKey }); } -export async function getUsbLedgerSigner(hdPath: string = LEDGER_HD_PATH): Promise { - return getNearLedgerSigner(await TransportNodeHidModule.create(), hdPath); +export async function getUsbLedgerSigner(hdPath: string = LEDGER_HD_PATH, cachePublicKey: boolean = false): Promise { + return getNearLedgerSigner({ transport: await TransportNodeHidModule.create(), hdPath, cachePublicKey }); } From 85cb910a8d7690fc7237ba145778f486fafbb4c0 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 19 Sep 2024 10:13:17 -0700 Subject: [PATCH 35/55] feat: transaction validation --- packages/client/src/transactions/composer.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/client/src/transactions/composer.ts b/packages/client/src/transactions/composer.ts index 0b0777ea8b..c34afaed5e 100644 --- a/packages/client/src/transactions/composer.ts +++ b/packages/client/src/transactions/composer.ts @@ -45,15 +45,20 @@ export class TransactionComposer { } private buildTransactionObject(transaction?: TransactionOptions) { - const hash = (transaction?.blockHeader?.hash || this.blockHeader?.hash)!; - return { + const tx = { actions: this.actions, - blockHash: baseDecode(hash), - nonce: (transaction?.nonce || this.nonce)!, - publicKey: (transaction?.publicKey || this.publicKey)!, - receiverId: (transaction?.receiver || this.receiver)!, - signerId: (transaction?.sender || this.sender)!, + blockHash: baseDecode(transaction?.blockHeader?.hash || this.blockHeader?.hash), + nonce: transaction?.nonce || this.nonce, + publicKey: transaction?.publicKey || this.publicKey, + receiverId: transaction?.receiver || this.receiver, + signerId: transaction?.sender || this.sender, }; + + if (!tx.actions.length || !tx.blockHash || !tx.nonce || !tx.publicKey || !tx.receiverId || !tx.signerId) { + throw new Error(`invalid transaction: ${JSON.stringify(tx)}`); + } + + return tx; } toTransaction(transaction?: TransactionOptions): Transaction { From 3dc94de0b62d3a5fbf7541bf204669226a442a81 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 19 Sep 2024 11:27:35 -0700 Subject: [PATCH 36/55] feat: composer refactor, better meta transaction support --- .../client/src/interfaces/transactions.ts | 19 +++ packages/client/src/transactions/actions.ts | 2 +- .../src/transactions/composers/index.ts | 2 + .../composers/signed_transaction_composer.ts | 109 +++++++++++++ .../transaction_composer.ts} | 143 +++++++++--------- .../client/src/transactions/create_account.ts | 2 +- packages/client/src/transactions/index.ts | 3 +- .../src/transactions/meta_transactions.ts | 47 ------ .../transactions/meta-transaction-relayer.ts | 25 ++- .../cookbook/transactions/meta-transaction.ts | 22 +-- packages/cookbook/utils/ledger-signing.ts | 2 +- 11 files changed, 230 insertions(+), 146 deletions(-) create mode 100644 packages/client/src/transactions/composers/index.ts create mode 100644 packages/client/src/transactions/composers/signed_transaction_composer.ts rename packages/client/src/transactions/{composer.ts => composers/transaction_composer.ts} (52%) delete mode 100644 packages/client/src/transactions/meta_transactions.ts diff --git a/packages/client/src/interfaces/transactions.ts b/packages/client/src/interfaces/transactions.ts index 744938ab69..b062ec7f41 100644 --- a/packages/client/src/interfaces/transactions.ts +++ b/packages/client/src/interfaces/transactions.ts @@ -3,6 +3,8 @@ import type { Transaction } from '@near-js/transactions'; import type { TransactionComposer } from '../transactions'; import type { Dependent, RpcProviderDependency, SignerDependency } from './dependencies'; import type { RpcProviderQueryParams } from './view'; +import { BlockHash } from '@near-js/types'; +import { PublicKey } from '@near-js/crypto'; export interface SignAndSendTransactionDependency extends Dependent {} @@ -78,3 +80,20 @@ export interface CreateAccountParams extends SignAndSendParams, NewAccountParams export interface CreateTopLevelAccountParams extends CreateAccountParams { contract: string } + +export interface TransactionOptions { + blockHash?: BlockHash; + nonce?: bigint; + publicKey?: PublicKey; + receiver?: string; + sender?: string; +} + +export interface MetaTransactionOptions extends TransactionOptions { + blockHeightTtl?: bigint; + maxBlockHeight?: bigint; +} + +export interface SignedTransactionOptions extends TransactionOptions, SignAndSendTransactionDependency { +} + diff --git a/packages/client/src/transactions/actions.ts b/packages/client/src/transactions/actions.ts index 9a36f2527d..04357a44ad 100644 --- a/packages/client/src/transactions/actions.ts +++ b/packages/client/src/transactions/actions.ts @@ -7,7 +7,7 @@ import type { StakeParams, TransferParams, } from '../interfaces'; -import { SignedTransactionComposer } from './composer'; +import { SignedTransactionComposer } from './composers'; /** * Make a function call against a contract diff --git a/packages/client/src/transactions/composers/index.ts b/packages/client/src/transactions/composers/index.ts new file mode 100644 index 0000000000..26ea6b7efa --- /dev/null +++ b/packages/client/src/transactions/composers/index.ts @@ -0,0 +1,2 @@ +export * from './signed_transaction_composer'; +export * from './transaction_composer'; \ No newline at end of file diff --git a/packages/client/src/transactions/composers/signed_transaction_composer.ts b/packages/client/src/transactions/composers/signed_transaction_composer.ts new file mode 100644 index 0000000000..f8c01ea8b7 --- /dev/null +++ b/packages/client/src/transactions/composers/signed_transaction_composer.ts @@ -0,0 +1,109 @@ +import { + buildDelegateAction, + signDelegateAction, +} from '@near-js/transactions'; +import { getTransactionLastResult } from '@near-js/utils'; +import { BlockReference } from '@near-js/types'; + +import { DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL } from '../../constants'; +import { + MessageSigner, + MetaTransactionOptions, + RpcQueryProvider, + SignedTransactionOptions, + TransactionOptions, +} from '../../interfaces'; +import { getSignerNonce, signTransaction } from '../sign_and_send'; +import { TransactionComposer } from './transaction_composer'; + +export class SignedTransactionComposer extends TransactionComposer { + rpcProvider: RpcQueryProvider; + signer: MessageSigner; + + constructor({ deps, ...baseOptions }: SignedTransactionOptions) { + super(baseOptions); + this.rpcProvider = deps.rpcProvider; + this.signer = deps.signer; + } + + /** + * Initialize the composer + * @param options signed composer configuration + */ + static init(options: SignedTransactionOptions) { + return new SignedTransactionComposer(options); + } + + /** + * Return a signed delegate action encapsulating the composed transaction for inclusion in a meta transaction + * @param transaction meta transaction configuration + */ + async toSignedDelegateAction(transaction?: MetaTransactionOptions) { + let maxBlockHeight = transaction?.maxBlockHeight; + if (!maxBlockHeight) { + const { header } = await this.rpcProvider.block({ finality: 'final' }); + const ttl = transaction?.blockHeightTtl || DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL; + maxBlockHeight = BigInt(header.height) + ttl; + } + + let nonce = transaction?.nonce || this.nonce; + if (!nonce) { + nonce = await getSignerNonce({ + account: this.sender, + deps: { + rpcProvider: this.rpcProvider, + signer: this.signer, + }, + }) + 1n; + } + + const delegateAction = buildDelegateAction({ + actions: this.actions, + maxBlockHeight, + nonce, + publicKey: transaction?.publicKey || await this.signer.getPublicKey(), + receiverId: transaction?.receiver || this.receiver, + senderId: transaction?.sender || this.sender, + }); + + const { signedDelegateAction } = await signDelegateAction({ + delegateAction, + signer: { sign: (m) => this.signer.signMessage(m) }, + }); + + return signedDelegateAction; + } + + /** + * Return a signed transaction from the composed transaction + * @param transaction transaction configuration to override values set at composer initialization + */ + async toSignedTransaction(transaction?: TransactionOptions) { + return signTransaction({ + transaction: this.toTransaction(transaction), + deps: { signer: this.signer }, + }); + } + + /** + * Sign and send the composed transaction + * @param blockReference block to use for determining hash + */ + async signAndSend(blockReference: BlockReference = { finality: 'final' }) { + const deps = { rpcProvider: this.rpcProvider, signer: this.signer }; + const blockHash = this.blockHash || (await this.rpcProvider.block(blockReference))?.header?.hash; + const signerNonce = this.nonce || (await getSignerNonce({ account: this.sender, blockReference, deps }) + 1n); + + const { signedTransaction } = await this.toSignedTransaction({ + nonce: signerNonce, + publicKey: this.publicKey || await this.signer.getPublicKey(), + blockHash, + }); + + const outcome = await this.rpcProvider.sendTransaction(signedTransaction); + return { + outcome, + result: getTransactionLastResult(outcome), + }; + } +} diff --git a/packages/client/src/transactions/composer.ts b/packages/client/src/transactions/composers/transaction_composer.ts similarity index 52% rename from packages/client/src/transactions/composer.ts rename to packages/client/src/transactions/composers/transaction_composer.ts index c34afaed5e..ed58242481 100644 --- a/packages/client/src/transactions/composer.ts +++ b/packages/client/src/transactions/composers/transaction_composer.ts @@ -2,52 +2,47 @@ import { PublicKey } from '@near-js/crypto'; import { Action, actionCreators, - buildDelegateAction, DelegateAction, Signature, Transaction, } from '@near-js/transactions'; -import { baseDecode, DEFAULT_FUNCTION_CALL_GAS, getTransactionLastResult } from '@near-js/utils'; -import { DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL } from '../constants'; -import { BlockHeader, BlockReference } from '@near-js/types'; -import { MessageSigner, RpcQueryProvider, SignAndSendTransactionDependency } from '../interfaces'; -import { getSignerNonce, signTransaction } from './sign_and_send'; - -interface TransactionOptions { - blockHeader?: BlockHeader; - nonce?: bigint; - publicKey?: PublicKey; - receiver?: string; - sender?: string; -} - -interface SignedTransactionOptions extends TransactionOptions, SignAndSendTransactionDependency { -} +import { baseDecode, DEFAULT_FUNCTION_CALL_GAS } from '@near-js/utils'; +import { BlockHash } from '@near-js/types'; +import { TransactionOptions } from '../../interfaces'; export class TransactionComposer { - private actions: Action[] = []; + protected actions: Action[] = []; receiver: string | undefined; sender: string | undefined; - blockHeader: BlockHeader | undefined; + blockHash: BlockHash | undefined; nonce: bigint | undefined; publicKey: PublicKey | undefined; constructor(transaction: TransactionOptions) { this.receiver = transaction.receiver; this.sender = transaction.sender; - this.blockHeader = transaction.blockHeader; + this.blockHash = transaction.blockHash; this.nonce = transaction.nonce; this.publicKey = transaction.publicKey; } + /** + * Initialize the composer + * @param transaction composer configuration + */ static init(transaction: TransactionOptions) { return new TransactionComposer(transaction); } + /** + * Validate and return the object used for Transaction instantiation + * @param transaction transaction values to override composed transaction fields + * @private + */ private buildTransactionObject(transaction?: TransactionOptions) { const tx = { actions: this.actions, - blockHash: baseDecode(transaction?.blockHeader?.hash || this.blockHeader?.hash), + blockHash: baseDecode(transaction?.blockHash || this.blockHash), nonce: transaction?.nonce || this.nonce, publicKey: transaction?.publicKey || this.publicKey, receiverId: transaction?.receiver || this.receiver, @@ -61,112 +56,110 @@ export class TransactionComposer { return tx; } + + /** + * Return a Transaction instance from the composed transaction + * @param transaction transaction configuration to override values set at composer initialization + */ toTransaction(transaction?: TransactionOptions): Transaction { return new Transaction(this.buildTransactionObject(transaction)); } - toDelegateAction(transaction?: TransactionOptions): DelegateAction { - const { actions, nonce, publicKey, receiverId, signerId } = this.buildTransactionObject(transaction); - const blockHeader = transaction?.blockHeader || this.blockHeader; - - return buildDelegateAction({ - actions, - maxBlockHeight: BigInt(blockHeader!.height) + DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL, - nonce: BigInt(nonce) + BigInt(1), - publicKey, - receiverId, - senderId: signerId, - }); - } - + /** + * Add an action to add a full access key + * @param publicKey string representation of the public key on the new access key + */ addFullAccessKey(publicKey: string): this { this.actions.push(actionCreators.addKey(PublicKey.from(publicKey), actionCreators.fullAccessKey())); return this; } + /** + * Add an action to create a function call access key + * @param publicKey string representation of the public key on the new access key + * @param contractId permitted contract + * @param methodNames set of permitted methods + * @param allowance max allowable balance attached to transactions signed with this key + */ addFunctionCallAccessKey(publicKey: string, contractId: string, methodNames: string[], allowance?: bigint): this { const accessKey = actionCreators.functionCallAccessKey(contractId, methodNames, allowance); this.actions.push(actionCreators.addKey(PublicKey.from(publicKey), accessKey)); return this; } + /** + * Add an action to create a sub-account for the transaction recipient + */ createAccount(): this { this.actions.push(actionCreators.createAccount()); return this; } + /** + * Add an action to delete the account signing the composed transaction + * @param beneficiaryId designated recipient account for any remaining balance on the deleted account + */ deleteAccount(beneficiaryId: string): this { this.actions.push(actionCreators.deleteAccount(beneficiaryId)); return this; } + /** + * Add an action to delete the specified access key + * @param publicKey string representation of the public key on the access key to be deleted + */ deleteKey(publicKey: string): this { this.actions.push(actionCreators.deleteKey(PublicKey.from(publicKey))); return this; } + /** + * Add an action to deploy code to a contract + * @param code compiled smart contract binary + */ deployContract(code: Uint8Array): this { this.actions.push(actionCreators.deployContract(code)); return this; } + /** + * Add an action to invoke a smart contract method + * @param method name of the method to be executed + * @param args named arguments to the invocation + * @param gas amount of gas (in yN) included to cover execution cost + * @param deposit amount of Near (in yN) to attach to the invocation + */ functionCall(method: string, args: object, gas: bigint = DEFAULT_FUNCTION_CALL_GAS * BigInt(10), deposit = BigInt(0)): this { this.actions.push(actionCreators.functionCall(method, args, gas, deposit)); return this; } + /** + * Add an action wrapping a delegate action for inclusion in meta transaction + * @param delegateAction delegate action encapsulating the set of actions to be executed on the requesting account's behalf + * @param signature signature of the delegate action signed by the requesting account + */ signedDelegate(delegateAction: DelegateAction, signature: Signature): this { this.actions.push(actionCreators.signedDelegate({ delegateAction, signature })); return this; } + /** + * Add an action to stake Near with a validator + * @param stake amount of Near (in yN) to stake + * @param publicKey string representation of the validator's key + */ stake(stake: bigint, publicKey: string): this { this.actions.push(actionCreators.stake(stake, PublicKey.from(publicKey))); return this; } + /** + * Add an action to transfer Near to another account + * @param deposit amount of Near (in yN) to transfer + */ transfer(deposit: bigint): this { this.actions.push(actionCreators.transfer(deposit)); return this; } } - -export class SignedTransactionComposer extends TransactionComposer { - rpcProvider: RpcQueryProvider; - signer: MessageSigner; - - constructor({ deps, ...baseOptions }: SignedTransactionOptions) { - super(baseOptions); - this.rpcProvider = deps.rpcProvider; - this.signer = deps.signer; - } - - static init(options: SignedTransactionOptions) { - return new SignedTransactionComposer(options); - } - - async toSignedTransaction(transaction?: TransactionOptions) { - return signTransaction({ - transaction: this.toTransaction(transaction), - deps: { signer: this.signer }, - }); - } - - async signAndSend(blockReference: BlockReference = { finality: 'final' }) { - const deps = { rpcProvider: this.rpcProvider, signer: this.signer }; - const blockHeader = this.blockHeader || (await this.rpcProvider.block(blockReference))?.header; - const signerNonce = this.nonce || (await getSignerNonce({ account: this.sender, blockReference, deps }) + 1n); - - const { signedTransaction } = await this.toSignedTransaction({ - nonce: signerNonce, - publicKey: this.publicKey || await this.signer.getPublicKey(), - blockHeader, - }); - - const outcome = await this.rpcProvider.sendTransaction(signedTransaction); - return { - outcome, - result: getTransactionLastResult(outcome), - }; - } -} diff --git a/packages/client/src/transactions/create_account.ts b/packages/client/src/transactions/create_account.ts index 2b78c5c1e2..04d5c5506c 100644 --- a/packages/client/src/transactions/create_account.ts +++ b/packages/client/src/transactions/create_account.ts @@ -2,7 +2,7 @@ import type { CreateAccountParams, CreateTopLevelAccountParams, } from '../interfaces'; -import { SignedTransactionComposer } from './composer'; +import { SignedTransactionComposer } from './composers'; import { functionCall } from './actions'; /** diff --git a/packages/client/src/transactions/index.ts b/packages/client/src/transactions/index.ts index e39ab7f7d0..15ef0dbfa3 100644 --- a/packages/client/src/transactions/index.ts +++ b/packages/client/src/transactions/index.ts @@ -1,5 +1,4 @@ export * from './actions'; -export * from './composer'; +export * from './composers'; export * from './create_account'; -export * from './meta_transactions'; export * from './sign_and_send'; diff --git a/packages/client/src/transactions/meta_transactions.ts b/packages/client/src/transactions/meta_transactions.ts deleted file mode 100644 index cec591963d..0000000000 --- a/packages/client/src/transactions/meta_transactions.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Action, buildDelegateAction, signDelegateAction, SignedDelegate } from '@near-js/transactions'; - -import { SignAndSendTransactionDependency } from '../interfaces'; -import { getSignerNonce } from './sign_and_send'; - -interface SignedDelegateOptions extends SignAndSendTransactionDependency { - account: string; - actions: Action[]; - blockHeightTtl: number; - receiver: string; -} - -export async function buildSignedDelegate({ - account, - actions, - blockHeightTtl, - receiver, - deps: { - rpcProvider, - signer, - }, -}: SignedDelegateOptions): Promise { - const { header } = await rpcProvider.block({ finality: 'final' }); - const signerNonce = await getSignerNonce({ - account, - deps: { - rpcProvider, - signer, - }, - }); - - const delegateAction = buildDelegateAction({ - actions, - maxBlockHeight: BigInt(header.height) + BigInt(blockHeightTtl), - nonce: signerNonce + 1n, - publicKey: await signer.getPublicKey(), - receiverId: receiver, - senderId: account, - }); - - const { signedDelegateAction } = await signDelegateAction({ - delegateAction, - signer: { sign: (m) => signer.signMessage(m) }, - }); - - return signedDelegateAction; -} diff --git a/packages/cookbook/transactions/meta-transaction-relayer.ts b/packages/cookbook/transactions/meta-transaction-relayer.ts index ccf6f8a057..c0d7973186 100644 --- a/packages/cookbook/transactions/meta-transaction-relayer.ts +++ b/packages/cookbook/transactions/meta-transaction-relayer.ts @@ -1,13 +1,19 @@ import { - buildSignedDelegate, getPlaintextFilesystemSigner, getTestnetRpcProvider, + SignedTransactionComposer, } from '@near-js/client'; -import { actionCreators, encodeSignedDelegate } from '@near-js/transactions'; +import { encodeSignedDelegate } from '@near-js/transactions'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; +/** + * Submit a transaction to a relayer + * @param signerAccountId account requesting the transaction's execution + * @param receiverAccountId recipient of the transaction + * @param relayerUrl URL processing relayer requests + */ export default async function sendMetaTransactionViaRelayer(signerAccountId: string, receiverAccountId: string, relayerUrl: string) { if (!signerAccountId || !receiverAccountId) { console.log(chalk`{red pnpm metaTransaction -- SENDER_ACCOUNT_ID RECEIVER_ACCOUNT_ID RELAYER_URL}`); @@ -21,13 +27,16 @@ export default async function sendMetaTransactionViaRelayer(signerAccountId: str // initialize the transaction signer using a pre-existing key for `accountId` const { signer } = getPlaintextFilesystemSigner({ account: signerAccountId, network: 'testnet', filepath: credentialsPath }); - const signedDelegate = await buildSignedDelegate({ - account: signerAccountId, + const signedDelegate = await SignedTransactionComposer.init({ + sender: signerAccountId, receiver: receiverAccountId, - actions: [actionCreators.transfer(1000n)], - blockHeightTtl: 60, - deps: { rpcProvider, signer }, - }); + deps: { + rpcProvider, + signer, + }, + }) + .transfer(100n) + .toSignedDelegateAction({ blockHeightTtl: 60n }); // @ts-ignore global const res = await fetch(relayerUrl, { diff --git a/packages/cookbook/transactions/meta-transaction.ts b/packages/cookbook/transactions/meta-transaction.ts index ae6512cc9d..3d199e2c3a 100644 --- a/packages/cookbook/transactions/meta-transaction.ts +++ b/packages/cookbook/transactions/meta-transaction.ts @@ -1,10 +1,8 @@ import { - buildSignedDelegate, getPlaintextFilesystemSigner, getTestnetRpcProvider, SignedTransactionComposer, } from '@near-js/client'; -import { actionCreators } from '@near-js/transactions'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; @@ -14,6 +12,12 @@ const RECEIVER_ACCOUNT_ID = 'receiver.testnet'; // the ultimate recipient of the const SENDER_ACCOUNT_ID = 'sender.testnet'; // the account requesting the transaction be executed const SIGNER_ACCOUNT_ID = 'signer.testnet'; // the account executing the meta transaction on behalf (e.g. as a relayer) of the sender +/** + * Sign and send a meta transaction + * @param signerAccountId the account that wants actions executed on their behalf + * @param receiverAccountId ultimate recipient of the transaction + * @param senderAccountId the account (i.e. relayer) executing the transaction on behalf of the signer + */ export default async function metaTransaction(signerAccountId: string = SIGNER_ACCOUNT_ID, receiverAccountId: string = RECEIVER_ACCOUNT_ID, senderAccountId: string = SENDER_ACCOUNT_ID): Promise { if (!signerAccountId || !receiverAccountId || !senderAccountId) { console.log(chalk`{red pnpm metaTransaction -- [SIGNER_ACCOUNT_ID] [RECEIVER_ACCOUNT_ID] [SENDER_ACCOUNT_ID]}`); @@ -27,20 +31,16 @@ export default async function metaTransaction(signerAccountId: string = SIGNER_A // initialize the transaction signer using a pre-existing key for `accountId` const { signer } = getPlaintextFilesystemSigner({ account: signerAccountId, network: 'testnet', filepath: credentialsPath }); - // define the set of actions for the relayer to execute on behalf of the signer - const innerActions = [actionCreators.transfer(100n)]; - - // create the signed delegate action encapsulating the inner transaction - const { delegateAction, signature } = await buildSignedDelegate({ - account: signerAccountId, + const { delegateAction, signature } = await SignedTransactionComposer.init({ + sender: signerAccountId, receiver: receiverAccountId, - actions: innerActions, - blockHeightTtl: 100, deps: { rpcProvider, signer, }, - }); + }) + .transfer(100n) + .toSignedDelegateAction(); // initialize the relayer's signer const { signer: relayerSigner } = getPlaintextFilesystemSigner({ diff --git a/packages/cookbook/utils/ledger-signing.ts b/packages/cookbook/utils/ledger-signing.ts index da820b6614..de1338510c 100644 --- a/packages/cookbook/utils/ledger-signing.ts +++ b/packages/cookbook/utils/ledger-signing.ts @@ -10,7 +10,7 @@ export default async function signWithLedger() { sender: 'a.testnet', receiver: 'b.testnet', nonce: 100n, - blockHeader: (await rpcProvider.block({ finality: 'optimistic' })).header, + blockHash: (await rpcProvider.block({ finality: 'optimistic' })).header.hash, publicKey, deps: { rpcProvider, From 503bf19d209a6e07b7856c9f9913f563d7764cf4 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 19 Sep 2024 14:12:48 -0700 Subject: [PATCH 37/55] chore: typed responses --- .../transactions/composers/signed_transaction_composer.ts | 5 +++-- packages/client/src/transactions/sign_and_send.ts | 5 +++-- packages/client/src/view.ts | 5 +++-- packages/types/src/provider/index.ts | 1 + packages/types/src/provider/response.ts | 2 ++ 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/client/src/transactions/composers/signed_transaction_composer.ts b/packages/client/src/transactions/composers/signed_transaction_composer.ts index f8c01ea8b7..7fd4b03f84 100644 --- a/packages/client/src/transactions/composers/signed_transaction_composer.ts +++ b/packages/client/src/transactions/composers/signed_transaction_composer.ts @@ -15,6 +15,7 @@ import { } from '../../interfaces'; import { getSignerNonce, signTransaction } from '../sign_and_send'; import { TransactionComposer } from './transaction_composer'; +import { SerializedReturnValue } from '@near-js/types/lib/esm/provider/response'; export class SignedTransactionComposer extends TransactionComposer { rpcProvider: RpcQueryProvider; @@ -89,7 +90,7 @@ export class SignedTransactionComposer extends TransactionComposer { * Sign and send the composed transaction * @param blockReference block to use for determining hash */ - async signAndSend(blockReference: BlockReference = { finality: 'final' }) { + async signAndSend(blockReference: BlockReference = { finality: 'final' }) { const deps = { rpcProvider: this.rpcProvider, signer: this.signer }; const blockHash = this.blockHash || (await this.rpcProvider.block(blockReference))?.header?.hash; const signerNonce = this.nonce || (await getSignerNonce({ account: this.sender, blockReference, deps }) + 1n); @@ -103,7 +104,7 @@ export class SignedTransactionComposer extends TransactionComposer { const outcome = await this.rpcProvider.sendTransaction(signedTransaction); return { outcome, - result: getTransactionLastResult(outcome), + result: getTransactionLastResult(outcome) as T, }; } } diff --git a/packages/client/src/transactions/sign_and_send.ts b/packages/client/src/transactions/sign_and_send.ts index c9e0eb2dba..c0a5e23242 100644 --- a/packages/client/src/transactions/sign_and_send.ts +++ b/packages/client/src/transactions/sign_and_send.ts @@ -5,6 +5,7 @@ import { sha256 } from '@noble/hashes/sha256'; import type { SignTransactionParams, SignAndSendTransactionParams } from '../interfaces'; import { getNonce } from '../view'; import { BlockReference } from '@near-js/types'; +import { SerializedReturnValue } from '@near-js/types/lib/esm/provider/response'; const DEFAULT_FINALITY: BlockReference = { finality: 'final' }; @@ -34,12 +35,12 @@ export async function signTransaction({ transaction, deps: { signer } }: SignTra * @param transaction Transaction instance to sign and publish * @param deps sign-and-send dependencies */ -export async function signAndSendTransaction({ transaction, deps: { rpcProvider, signer } }: SignAndSendTransactionParams) { +export async function signAndSendTransaction({ transaction, deps: { rpcProvider, signer } }: SignAndSendTransactionParams) { const { signedTransaction } = await signTransaction({ transaction, deps: { signer } }); const outcome = await rpcProvider.sendTransaction(signedTransaction); return { outcome, - result: getTransactionLastResult(outcome), + result: getTransactionLastResult(outcome) as T, }; } diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index 165cbcf2b9..7b614798e6 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -5,6 +5,7 @@ import type { CodeResult, ContractCodeView, QueryResponseKind, + SerializedReturnValue, ViewStateResult, } from '@near-js/types'; @@ -89,13 +90,13 @@ export function callViewMethod({ account, method, args = {}, blockReference, dep * @param blockReference block ID/finality * @param deps readonly RPC dependencies */ -export async function view({ account, method, args = {}, blockReference, deps }: ViewParams): Promise { +export async function view({ account, method, args = {}, blockReference, deps }: ViewParams): Promise { const { result } = await callViewMethod({ account, method, args, blockReference, deps }); const stringResult = Buffer.from(result).toString(); try { return JSON.parse(stringResult); } catch { - return isNaN(+stringResult) ? stringResult : +stringResult; + return (isNaN(+stringResult) ? stringResult : +stringResult) as T; } } diff --git a/packages/types/src/provider/index.ts b/packages/types/src/provider/index.ts index d22b4125a6..4b78aafea3 100644 --- a/packages/types/src/provider/index.ts +++ b/packages/types/src/provider/index.ts @@ -66,6 +66,7 @@ export { FinalExecutionStatusBasic, FunctionCallPermissionView, QueryResponseKind, + SerializedReturnValue, ViewStateResult, } from './response'; export { diff --git a/packages/types/src/provider/response.ts b/packages/types/src/provider/response.ts index 6ef3b8ca9b..a9ccc155d1 100644 --- a/packages/types/src/provider/response.ts +++ b/packages/types/src/provider/response.ts @@ -4,6 +4,8 @@ */ import { BlockHash, BlockHeight, MerklePath, TxExecutionStatus } from './protocol'; +export type SerializedReturnValue = string | number | boolean | object; + export enum ExecutionStatusBasic { Unknown = 'Unknown', Pending = 'Pending', From 93e6a3152e9471dafdbf0510df509afa761d00ab Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Thu, 19 Sep 2024 14:13:37 -0700 Subject: [PATCH 38/55] chore: type refactor --- packages/client/src/interfaces/dependencies.ts | 12 +++++++++--- packages/client/src/interfaces/transactions.ts | 6 ++---- packages/client/src/interfaces/view.ts | 4 ++-- packages/client/src/view.ts | 3 +-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/client/src/interfaces/dependencies.ts b/packages/client/src/interfaces/dependencies.ts index e8f82bdb87..950deb61db 100644 --- a/packages/client/src/interfaces/dependencies.ts +++ b/packages/client/src/interfaces/dependencies.ts @@ -2,7 +2,7 @@ import type { PublicKey } from '@near-js/crypto'; import type { RpcQueryProvider } from './providers'; -export interface Dependent { +interface Dependent { deps: T; } @@ -11,10 +11,16 @@ export interface MessageSigner { signMessage(m: Uint8Array): Promise; } -export interface RpcProviderDependency { +interface RpcProviderDependent { rpcProvider: RpcQueryProvider; } -export interface SignerDependency { +interface SignerDependent { signer: MessageSigner; } + +export interface RpcProviderDependency extends Dependent {} + +export interface SignerDependency extends Dependent {} + +export interface SignAndSendTransactionDependency extends Dependent {} diff --git a/packages/client/src/interfaces/transactions.ts b/packages/client/src/interfaces/transactions.ts index b062ec7f41..0cb4a50dad 100644 --- a/packages/client/src/interfaces/transactions.ts +++ b/packages/client/src/interfaces/transactions.ts @@ -1,13 +1,11 @@ import type { Transaction } from '@near-js/transactions'; import type { TransactionComposer } from '../transactions'; -import type { Dependent, RpcProviderDependency, SignerDependency } from './dependencies'; +import type { SignAndSendTransactionDependency, SignerDependency } from './dependencies'; import type { RpcProviderQueryParams } from './view'; import { BlockHash } from '@near-js/types'; import { PublicKey } from '@near-js/crypto'; -export interface SignAndSendTransactionDependency extends Dependent {} - export interface SignAndSendParams extends SignAndSendTransactionDependency, RpcProviderQueryParams {} export interface ExternalActionTransaction extends SignAndSendParams { @@ -63,7 +61,7 @@ export interface AddFunctionCallAccessKeyParams extends AddAccessKeyParams { allowance?: bigint; } -export interface SignTransactionParams extends Dependent { +export interface SignTransactionParams extends SignerDependency { transaction: Transaction; } diff --git a/packages/client/src/interfaces/view.ts b/packages/client/src/interfaces/view.ts index 3c147ce308..834ca11f3f 100644 --- a/packages/client/src/interfaces/view.ts +++ b/packages/client/src/interfaces/view.ts @@ -1,12 +1,12 @@ import type { BlockReference } from '@near-js/types'; -import { Dependent, RpcProviderDependency } from './dependencies'; +import { RpcProviderDependency } from './dependencies'; export interface RpcProviderQueryParams { blockReference?: BlockReference; } -export interface ViewBaseParams extends Dependent, RpcProviderQueryParams { +export interface ViewBaseParams extends RpcProviderDependency, RpcProviderQueryParams { } export interface ViewAccountParams extends ViewBaseParams { diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index 7b614798e6..27ae8ea3c0 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -10,7 +10,6 @@ import type { } from '@near-js/types'; import type { - Dependent, RpcProviderDependency, RpcProviderQueryParams, ViewAccessKeyParams, @@ -30,7 +29,7 @@ enum RequestType { ViewState = 'view_state', } -interface QueryParams extends Dependent, RpcProviderQueryParams { +interface QueryParams extends RpcProviderDependency, RpcProviderQueryParams { account: string; request: string; args?: object; From 5db9b636cb426da7787a6dafaea30c18e3c9008e Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Mon, 23 Sep 2024 13:47:28 -0700 Subject: [PATCH 39/55] fix: default nonce lookup finality --- .../transactions/composers/signed_transaction_composer.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/client/src/transactions/composers/signed_transaction_composer.ts b/packages/client/src/transactions/composers/signed_transaction_composer.ts index 7fd4b03f84..1d7b067cda 100644 --- a/packages/client/src/transactions/composers/signed_transaction_composer.ts +++ b/packages/client/src/transactions/composers/signed_transaction_composer.ts @@ -93,7 +93,11 @@ export class SignedTransactionComposer extends TransactionComposer { async signAndSend(blockReference: BlockReference = { finality: 'final' }) { const deps = { rpcProvider: this.rpcProvider, signer: this.signer }; const blockHash = this.blockHash || (await this.rpcProvider.block(blockReference))?.header?.hash; - const signerNonce = this.nonce || (await getSignerNonce({ account: this.sender, blockReference, deps }) + 1n); + const signerNonce = this.nonce || (await getSignerNonce({ + account: this.sender, + blockReference: { finality: 'optimistic' }, + deps, + }) + 1n); const { signedTransaction } = await this.toSignedTransaction({ nonce: signerNonce, From 7a701da9c72af5391a0581a7ea6031293305b8d2 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Mon, 23 Sep 2024 15:45:29 -0700 Subject: [PATCH 40/55] feat: cached access key lookup --- .../client/src/interfaces/dependencies.ts | 6 ++ packages/client/src/signing/signers.ts | 74 +++++++++++++++++-- .../composers/signed_transaction_composer.ts | 39 +++------- .../client/src/transactions/sign_and_send.ts | 2 +- 4 files changed, 85 insertions(+), 36 deletions(-) diff --git a/packages/client/src/interfaces/dependencies.ts b/packages/client/src/interfaces/dependencies.ts index 950deb61db..29e8bb2c22 100644 --- a/packages/client/src/interfaces/dependencies.ts +++ b/packages/client/src/interfaces/dependencies.ts @@ -1,4 +1,5 @@ import type { PublicKey } from '@near-js/crypto'; +import type { AccessKeyView } from '@near-js/types'; import type { RpcQueryProvider } from './providers'; @@ -11,6 +12,11 @@ export interface MessageSigner { signMessage(m: Uint8Array): Promise; } +export interface AccessKeySigner extends MessageSigner { + getAccessKey(ignoreCache?: boolean): Promise; + getNonce(ignoreCache?: boolean): Promise; +} + interface RpcProviderDependent { rpcProvider: RpcQueryProvider; } diff --git a/packages/client/src/signing/signers.ts b/packages/client/src/signing/signers.ts index 2fdbe806b0..e140874578 100644 --- a/packages/client/src/signing/signers.ts +++ b/packages/client/src/signing/signers.ts @@ -1,38 +1,100 @@ import { KeyPair, type KeyPairString, - type PublicKey, } from '@near-js/crypto'; import { KeyStore } from '@near-js/keystores'; import { InMemorySigner } from '@near-js/signers'; -import type { MessageSigner } from '../interfaces'; +import type { + AccessKeySigner, + MessageSigner, + SignerDependency, + ViewAccountParams, +} from '../interfaces'; +import { getAccessKey } from '../view'; +import { AccessKeyView } from '@near-js/types'; +/** + * Initialize a message signer from a KeyPair + * @param keyPair used to sign transactions + */ export function getSignerFromKeyPair(keyPair: KeyPair): MessageSigner { return { - async getPublicKey(): Promise { + async getPublicKey() { return keyPair.getPublicKey(); }, - async signMessage(m: Uint8Array): Promise { + async signMessage(m) { return keyPair.sign(m).signature; } }; } +/** + * Initialize a message singer from a private key string + * @param privateKey string representation of the private key used to sign transactions + */ export function getSignerFromPrivateKey(privateKey: KeyPairString): MessageSigner { return getSignerFromKeyPair(KeyPair.fromString(privateKey)); } +/** + * Initialize a message signer from a keystore instance + * @param account used to sign transactions + * @param network to sign transactions on + * @param keyStore used to store the signing key + */ export function getSignerFromKeystore(account: string, network: string, keyStore: KeyStore): MessageSigner { const signer = new InMemorySigner(keyStore); return { - async getPublicKey(): Promise { + async getPublicKey() { return signer.getPublicKey(account, network); }, - async signMessage(m: Uint8Array): Promise { + async signMessage(m) { const { signature } = await signer.signMessage(m, account, network); return signature; } }; } + +/** + * Initialize a signer that caches the access key and increments the nonce + * @param account access key owner + * @param rpcProvider RPC provider instance + * @param deps sign-and-send dependencies + */ +export function getAccessKeySigner({ account, blockReference, deps: { rpcProvider, signer } }: ViewAccountParams & SignerDependency): AccessKeySigner { + let accessKey: AccessKeyView; + let nonce: bigint; + + return { + async getAccessKey(ignoreCache = false) { + if (!accessKey || ignoreCache) { + accessKey = await getAccessKey({ + account, + blockReference: blockReference || { finality: 'optimistic' }, + publicKey: (await signer.getPublicKey()).toString(), + deps: { rpcProvider }, + }); + + nonce = accessKey.nonce + 1n; + } + + return accessKey; + }, + async getNonce(ignoreCache = false) { + if (!nonce || ignoreCache) { + await this.getAccessKey(true); + } + + return nonce; + }, + getPublicKey() { + return signer.getPublicKey(); + }, + signMessage(m: Uint8Array) { + nonce += 1n; + return signer.signMessage(m); + } + }; +} diff --git a/packages/client/src/transactions/composers/signed_transaction_composer.ts b/packages/client/src/transactions/composers/signed_transaction_composer.ts index 1d7b067cda..0e46ccd1ff 100644 --- a/packages/client/src/transactions/composers/signed_transaction_composer.ts +++ b/packages/client/src/transactions/composers/signed_transaction_composer.ts @@ -2,29 +2,29 @@ import { buildDelegateAction, signDelegateAction, } from '@near-js/transactions'; +import type { BlockReference, SerializedReturnValue } from '@near-js/types'; import { getTransactionLastResult } from '@near-js/utils'; -import { BlockReference } from '@near-js/types'; import { DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL } from '../../constants'; import { - MessageSigner, + AccessKeySigner, MetaTransactionOptions, RpcQueryProvider, SignedTransactionOptions, TransactionOptions, } from '../../interfaces'; -import { getSignerNonce, signTransaction } from '../sign_and_send'; +import { signTransaction } from '../sign_and_send'; import { TransactionComposer } from './transaction_composer'; -import { SerializedReturnValue } from '@near-js/types/lib/esm/provider/response'; +import { getAccessKeySigner } from '../../signing/signers'; export class SignedTransactionComposer extends TransactionComposer { rpcProvider: RpcQueryProvider; - signer: MessageSigner; + signer: AccessKeySigner; constructor({ deps, ...baseOptions }: SignedTransactionOptions) { super(baseOptions); this.rpcProvider = deps.rpcProvider; - this.signer = deps.signer; + this.signer = getAccessKeySigner({ account: this.sender, deps }); } /** @@ -47,22 +47,11 @@ export class SignedTransactionComposer extends TransactionComposer { maxBlockHeight = BigInt(header.height) + ttl; } - let nonce = transaction?.nonce || this.nonce; - if (!nonce) { - nonce = await getSignerNonce({ - account: this.sender, - deps: { - rpcProvider: this.rpcProvider, - signer: this.signer, - }, - }) + 1n; - } - const delegateAction = buildDelegateAction({ actions: this.actions, maxBlockHeight, - nonce, - publicKey: transaction?.publicKey || await this.signer.getPublicKey(), + nonce: transaction?.nonce || this.nonce || await this.signer.getNonce(), + publicKey: transaction?.publicKey || this.publicKey || await this.signer.getPublicKey(), receiverId: transaction?.receiver || this.receiver, senderId: transaction?.sender || this.sender, }); @@ -91,18 +80,10 @@ export class SignedTransactionComposer extends TransactionComposer { * @param blockReference block to use for determining hash */ async signAndSend(blockReference: BlockReference = { finality: 'final' }) { - const deps = { rpcProvider: this.rpcProvider, signer: this.signer }; - const blockHash = this.blockHash || (await this.rpcProvider.block(blockReference))?.header?.hash; - const signerNonce = this.nonce || (await getSignerNonce({ - account: this.sender, - blockReference: { finality: 'optimistic' }, - deps, - }) + 1n); - const { signedTransaction } = await this.toSignedTransaction({ - nonce: signerNonce, + nonce: this.nonce || await this.signer.getNonce(), publicKey: this.publicKey || await this.signer.getPublicKey(), - blockHash, + blockHash: this.blockHash || (await this.rpcProvider.block(blockReference))?.header?.hash, }); const outcome = await this.rpcProvider.sendTransaction(signedTransaction); diff --git a/packages/client/src/transactions/sign_and_send.ts b/packages/client/src/transactions/sign_and_send.ts index c0a5e23242..c555d3f6f5 100644 --- a/packages/client/src/transactions/sign_and_send.ts +++ b/packages/client/src/transactions/sign_and_send.ts @@ -48,7 +48,7 @@ export async function signAndSendTransaction({ * Get the current nonce for an access key given an account and MessageSigner instance * @param account owner of the access key * @param blockReference block ID/finality - * @param rpcProvider + * @param rpcProvider RPC provider instance * @param deps sign-and-send dependencies */ export async function getSignerNonce({ account, blockReference = DEFAULT_FINALITY, deps: { rpcProvider, signer } }) { From 3abe1107dbe678c8c6f3f0b6ce4cb869d6eb6e06 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Mon, 23 Sep 2024 16:09:26 -0700 Subject: [PATCH 41/55] feat: lazy signer initialization --- .../client/src/interfaces/dependencies.ts | 1 + packages/client/src/signing/signers.ts | 3 ++ .../composers/signed_transaction_composer.ts | 37 +++++++++++++++++-- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/client/src/interfaces/dependencies.ts b/packages/client/src/interfaces/dependencies.ts index 29e8bb2c22..ccda4ce342 100644 --- a/packages/client/src/interfaces/dependencies.ts +++ b/packages/client/src/interfaces/dependencies.ts @@ -15,6 +15,7 @@ export interface MessageSigner { export interface AccessKeySigner extends MessageSigner { getAccessKey(ignoreCache?: boolean): Promise; getNonce(ignoreCache?: boolean): Promise; + getSigningAccount(): string; } interface RpcProviderDependent { diff --git a/packages/client/src/signing/signers.ts b/packages/client/src/signing/signers.ts index e140874578..11f831c47f 100644 --- a/packages/client/src/signing/signers.ts +++ b/packages/client/src/signing/signers.ts @@ -92,6 +92,9 @@ export function getAccessKeySigner({ account, blockReference, deps: { rpcProvide getPublicKey() { return signer.getPublicKey(); }, + getSigningAccount() { + return account; + }, signMessage(m: Uint8Array) { nonce += 1n; return signer.signMessage(m); diff --git a/packages/client/src/transactions/composers/signed_transaction_composer.ts b/packages/client/src/transactions/composers/signed_transaction_composer.ts index 0e46ccd1ff..a768d5e68d 100644 --- a/packages/client/src/transactions/composers/signed_transaction_composer.ts +++ b/packages/client/src/transactions/composers/signed_transaction_composer.ts @@ -8,6 +8,7 @@ import { getTransactionLastResult } from '@near-js/utils'; import { DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL } from '../../constants'; import { AccessKeySigner, + MessageSigner, MetaTransactionOptions, RpcQueryProvider, SignedTransactionOptions, @@ -18,13 +19,17 @@ import { TransactionComposer } from './transaction_composer'; import { getAccessKeySigner } from '../../signing/signers'; export class SignedTransactionComposer extends TransactionComposer { + messageSigner: MessageSigner; rpcProvider: RpcQueryProvider; signer: AccessKeySigner; constructor({ deps, ...baseOptions }: SignedTransactionOptions) { super(baseOptions); + this.messageSigner = deps.signer; this.rpcProvider = deps.rpcProvider; - this.signer = getAccessKeySigner({ account: this.sender, deps }); + if (this.sender) { + this.signer = getAccessKeySigner({ account: this.sender, deps }); + } } /** @@ -64,13 +69,36 @@ export class SignedTransactionComposer extends TransactionComposer { return signedDelegateAction; } + /** + * Verify the transaction's signer matches the account mapped to the AccessKeySigner. + * Initialize the signer if not already done (i.e. for lazy setting of the transaction signer). + * Throw an error if there is a mismatch between the current AccessKeySigner and the transaction's specified signer. + * @param signingAccount + * @private + */ + private verifySigner(signingAccount: string) { + if (!this.signer) { + this.signer = getAccessKeySigner({ + account: signingAccount, + deps: { rpcProvider: this.rpcProvider, signer: this.messageSigner }, + }); + } + + const signerAccount = this.signer.getSigningAccount(); + if (signingAccount !== signerAccount) { + throw new Error(`Cannot sign transaction as ${signingAccount} with AccessKeySigner for ${signerAccount}`); + } + } + /** * Return a signed transaction from the composed transaction - * @param transaction transaction configuration to override values set at composer initialization + * @param transactionOptions transaction configuration to override values set at composer initialization */ - async toSignedTransaction(transaction?: TransactionOptions) { + async toSignedTransaction(transactionOptions?: TransactionOptions) { + const transaction = this.toTransaction(transactionOptions); + this.verifySigner(transaction.signerId); return signTransaction({ - transaction: this.toTransaction(transaction), + transaction, deps: { signer: this.signer }, }); } @@ -80,6 +108,7 @@ export class SignedTransactionComposer extends TransactionComposer { * @param blockReference block to use for determining hash */ async signAndSend(blockReference: BlockReference = { finality: 'final' }) { + this.verifySigner(this.sender); const { signedTransaction } = await this.toSignedTransaction({ nonce: this.nonce || await this.signer.getNonce(), publicKey: this.publicKey || await this.signer.getPublicKey(), From 84e1cb93f865efaf6eb6986e06940ce932b160e7 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 25 Sep 2024 16:04:51 -0700 Subject: [PATCH 42/55] feat: remove node keystores dependency --- packages/client/package.json | 1 - packages/client/src/signing/index.ts | 4 +--- .../signing/keystores/plaintext_filesystem.ts | 21 ------------------- packages/cookbook/package.json | 1 + pnpm-lock.yaml | 6 +++--- 5 files changed, 5 insertions(+), 28 deletions(-) delete mode 100644 packages/client/src/signing/keystores/plaintext_filesystem.ts diff --git a/packages/client/package.json b/packages/client/package.json index 4550fe292f..7f87d4e16a 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -15,7 +15,6 @@ "@ledgerhq/hw-transport-node-hid": "6.29.4", "@near-js/crypto": "workspace:*", "@near-js/keystores": "workspace:*", - "@near-js/keystores-node": "workspace:*", "@near-js/providers": "workspace:*", "@near-js/signers": "workspace:*", "@near-js/transactions": "workspace:*", diff --git a/packages/client/src/signing/index.ts b/packages/client/src/signing/index.ts index 1f9285affb..0a6d666b6d 100644 --- a/packages/client/src/signing/index.ts +++ b/packages/client/src/signing/index.ts @@ -1,11 +1,9 @@ -export { - getPlaintextFilesystemSigner, -} from './keystores/plaintext_filesystem'; export { getUsbLedgerSigner, getBrowserLedgerSigner, } from './ledger'; export { getSignerFromKeyPair, + getSignerFromKeystore, getSignerFromPrivateKey, } from './signers'; diff --git a/packages/client/src/signing/keystores/plaintext_filesystem.ts b/packages/client/src/signing/keystores/plaintext_filesystem.ts deleted file mode 100644 index c33b5033de..0000000000 --- a/packages/client/src/signing/keystores/plaintext_filesystem.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - THIS MODULE IS PROVIDED FOR BACKWARD COMPATIBILITY PURPOSES ONLY - PRIVATE KEYS ARE STORED IN PLAINTEXT - DO NOT USE FOR NON-TEST ACCOUNTS -*/ -import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; - -import { getSignerFromKeystore } from '../signers'; - -interface PlaintextFilesystemSigner { - account: string; - network: string; - filepath: string; -} - -export function getPlaintextFilesystemSigner({ account, network, filepath }: PlaintextFilesystemSigner) { - const keystore = new UnencryptedFileSystemKeyStore(filepath); - return { - keystore, - signer: getSignerFromKeystore(account, network, keystore), - }; -} diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index 96b5e91fb0..de062e0a0e 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -34,6 +34,7 @@ "devDependencies": { "@near-js/client": "workspace:*", "@near-js/crypto": "workspace:*", + "@near-js/keystores-node": "workspace:*", "@near-js/transactions": "workspace:*", "@near-js/utils": "workspace:*", "build": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd7180a918..31048e04ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -202,9 +202,6 @@ importers: '@near-js/keystores': specifier: workspace:* version: link:../keystores - '@near-js/keystores-node': - specifier: workspace:* - version: link:../keystores-node '@near-js/providers': specifier: workspace:* version: link:../providers @@ -266,6 +263,9 @@ importers: '@near-js/crypto': specifier: workspace:* version: link:../crypto + '@near-js/keystores-node': + specifier: workspace:* + version: link:../keystores-node '@near-js/transactions': specifier: workspace:* version: link:../transactions From 7933fbe834c7f57dce4e1825c077091caeceef26 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 25 Sep 2024 16:05:33 -0700 Subject: [PATCH 43/55] feat: move keystore functionality to cookbook --- .../access-keys/create-full-access-key.ts | 46 +++++++++--------- .../access-keys/create-function-access-key.ts | 7 ++- .../accounts/access-keys/delete-access-key.ts | 6 +-- .../accounts/create-funded-testnet-account.ts | 5 +- .../accounts/create-mainnet-account.ts | 47 ++++++++++--------- .../accounts/create-testnet-account.ts | 47 ++++++++++--------- packages/cookbook/index.ts | 3 ++ packages/cookbook/package.json | 2 +- packages/cookbook/src/index.ts | 3 -- .../transactions/batch-transactions.ts | 7 ++- .../transactions/meta-transaction-relayer.ts | 7 ++- .../cookbook/transactions/meta-transaction.ts | 25 +++++----- packages/cookbook/tsconfig.json | 2 +- packages/cookbook/utils/calculate-gas.ts | 7 ++- packages/cookbook/utils/deploy-contract.ts | 7 ++- packages/cookbook/utils/index.ts | 2 +- packages/cookbook/utils/unwrap-near.ts | 7 ++- packages/cookbook/utils/wrap-near.ts | 7 ++- 18 files changed, 118 insertions(+), 119 deletions(-) create mode 100644 packages/cookbook/index.ts delete mode 100644 packages/cookbook/src/index.ts diff --git a/packages/cookbook/accounts/access-keys/create-full-access-key.ts b/packages/cookbook/accounts/access-keys/create-full-access-key.ts index f744008d9c..f86c7224cc 100644 --- a/packages/cookbook/accounts/access-keys/create-full-access-key.ts +++ b/packages/cookbook/accounts/access-keys/create-full-access-key.ts @@ -1,45 +1,47 @@ import { addFullAccessKey, generateRandomKeyPair, - getPlaintextFilesystemSigner, + getSignerFromKeystore, getTestnetRpcProvider, } from '@near-js/client'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; + export default async function createFullAccessKey(accountId: string) { if (!accountId) { console.log(chalk`{red pnpm createFullAccessKey -- ACCOUNT_ID}`); return; } - const credentialsPath = join(homedir(), '.near-credentials'); - // initialize testnet RPC provider - const rpcProvider = getTestnetRpcProvider(); - // initialize the transaction signer using a pre-existing key for `accountId` - const { keystore, signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); - const previousKey = await signer.getPublicKey(); + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const keystore = new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials')); + const signer = getSignerFromKeystore(accountId, 'testnet', keystore); + const previousKey = await signer.getPublicKey(); - // create a new key from random data - const keyPair = generateRandomKeyPair('ed25519'); + // create a new key from random data + const keyPair = generateRandomKeyPair('ed25519'); - // add the generated key to the account as a Full Access Key (FAK) - await addFullAccessKey({ - account: accountId, - publicKey: keyPair.getPublicKey().toString(), - deps: { - rpcProvider, - signer, - }, - }); + // add the generated key to the account as a Full Access Key (FAK) + await addFullAccessKey({ + account: accountId, + publicKey: keyPair.getPublicKey().toString(), + deps: { + rpcProvider, + signer, + }, + }); - // overwrite the key used to sign the transaction - // above with the new FAK in the key store - await keystore.setKey('testnet', accountId, keyPair); + // overwrite the key used to sign the transaction + // above with the new FAK in the key store + await keystore.setKey('testnet', accountId, keyPair); console.log(chalk`{white ------------------------------------------------------------------------ }`); - console.log(chalk`{bold.green RESULTS} {white Added new full access key and set in keystore}` ); + console.log(chalk`{bold.green RESULTS} {white Added new full access key and set in keystore}`); console.log(chalk`{white ------------------------------------------------------------------------ }`); console.log(chalk`{bold.white Previous Key} {white |} {bold.yellow ${previousKey.toString()}}`); console.log(chalk`{bold.white New Key} {white |} {bold.yellow ${keyPair.getPublicKey().toString()}}`); diff --git a/packages/cookbook/accounts/access-keys/create-function-access-key.ts b/packages/cookbook/accounts/access-keys/create-function-access-key.ts index db53523611..41085275f4 100644 --- a/packages/cookbook/accounts/access-keys/create-function-access-key.ts +++ b/packages/cookbook/accounts/access-keys/create-function-access-key.ts @@ -1,9 +1,10 @@ import { addFunctionCallAccessKey, generateRandomKeyPair, - getPlaintextFilesystemSigner, + getSignerFromKeystore, getTestnetRpcProvider, } from '@near-js/client'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; @@ -17,12 +18,10 @@ export default async function createFunctionCallAccessKey(accountId: string, con return; } - const credentialsPath = join(homedir(), '.near-credentials'); - // initialize testnet RPC provider const rpcProvider = getTestnetRpcProvider(); // initialize the transaction signer using a pre-existing key for ACCOUNT_ID - const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + const signer = getSignerFromKeystore(accountId, 'testnet', new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials'))); // create a new key from random data const keyPair = generateRandomKeyPair('ed25519'); diff --git a/packages/cookbook/accounts/access-keys/delete-access-key.ts b/packages/cookbook/accounts/access-keys/delete-access-key.ts index ba7b2e884e..bfc32f67aa 100644 --- a/packages/cookbook/accounts/access-keys/delete-access-key.ts +++ b/packages/cookbook/accounts/access-keys/delete-access-key.ts @@ -1,9 +1,10 @@ import { deleteAccessKey, - getPlaintextFilesystemSigner, + getSignerFromKeystore, getTestnetRpcProvider, parseKeyPair, } from '@near-js/client'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; import { type KeyPairString } from '@near-js/crypto'; import chalk from 'chalk'; import { join } from 'node:path'; @@ -15,11 +16,10 @@ export default async function deleteAccessKeyCookbook(accountId: string, publicK return; } - const credentialsPath = join(homedir(), '.near-credentials'); // initialize testnet RPC provider const rpcProvider = getTestnetRpcProvider(); // initialize the transaction signer using a pre-existing key for ACCOUNT_ID - const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + const signer = getSignerFromKeystore(accountId, 'testnet', new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials'))); // parse the target key from its string representation const keyPair = parseKeyPair(publicKey as KeyPairString); diff --git a/packages/cookbook/accounts/create-funded-testnet-account.ts b/packages/cookbook/accounts/create-funded-testnet-account.ts index 7f5742ce69..428d14e925 100644 --- a/packages/cookbook/accounts/create-funded-testnet-account.ts +++ b/packages/cookbook/accounts/create-funded-testnet-account.ts @@ -1,11 +1,11 @@ import { createFundedTestnetAccount, generateRandomKeyPair, - getPlaintextFilesystemSigner, } from '@near-js/client'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; export default async function createFundedTestnetAccountCookbook(accountId: string) { if (!accountId) { @@ -13,9 +13,8 @@ export default async function createFundedTestnetAccountCookbook(accountId: stri return; } - const credentialsPath = join(homedir(), '.near-credentials'); // initialize the transaction signer using a pre-existing key for ACCOUNT_ID - const { keystore } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + const keystore = new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials')); // create new keypair and persist it to filesystem keystore const keyPair = generateRandomKeyPair('ed25519'); diff --git a/packages/cookbook/accounts/create-mainnet-account.ts b/packages/cookbook/accounts/create-mainnet-account.ts index 6dabaa169e..14c07b5f29 100644 --- a/packages/cookbook/accounts/create-mainnet-account.ts +++ b/packages/cookbook/accounts/create-mainnet-account.ts @@ -2,8 +2,9 @@ import { createTopLevelAccount, generateRandomKeyPair, getMainnetRpcProvider, - getPlaintextFilesystemSigner, + getSignerFromKeystore, } from '@near-js/client'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; @@ -14,34 +15,34 @@ export default async function createMainnetAccountCookbook(accountId: string, ne return; } - const credentialsPath = join(homedir(), '.near-credentials'); - // initialize testnet RPC provider - const rpcProvider = getMainnetRpcProvider(); - // initialize the transaction signer using a pre-existing key for `accountId` - const { keystore, signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + // initialize testnet RPC provider + const rpcProvider = getMainnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const keystore = new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials')); + const signer = getSignerFromKeystore(accountId, 'mainnet', keystore); - // create a new key from random data - const keyPair = generateRandomKeyPair('ed25519'); + // create a new key from random data + const keyPair = generateRandomKeyPair('ed25519'); - const newAccount = newAccountId || `${new Date().valueOf()}-${Math.ceil(Math.random() * 10e12)}.near`; + const newAccount = newAccountId || `${new Date().valueOf()}-${Math.ceil(Math.random() * 10e12)}.near`; - await createTopLevelAccount({ - account: accountId, - contract: 'mainnet', - initialBalance: 100n, - newAccount, - newPublicKey: keyPair.getPublicKey().toString(), - deps: { - rpcProvider, - signer, - }, - }); + await createTopLevelAccount({ + account: accountId, + contract: 'mainnet', + initialBalance: 100n, + newAccount, + newPublicKey: keyPair.getPublicKey().toString(), + deps: { + rpcProvider, + signer, + }, + }); - // save new account in plaintext filesystem keystore - await keystore.setKey('mainnet', accountId, keyPair); + // save new account in plaintext filesystem keystore + await keystore.setKey('mainnet', accountId, keyPair); console.log(chalk`{white ------------------------------------------------------------------------ }`); - console.log(chalk`{bold.green RESULTS} {white Created mainnet account}` ); + console.log(chalk`{bold.green RESULTS} {white Created mainnet account}`); console.log(chalk`{white ------------------------------------------------------------------------ }`); console.log(chalk`{bold.white New Account} {white |} {bold.yellow ${accountId}} {white |} {bold.yellow ${keyPair.getPublicKey().toString()}}`); console.log(chalk`{white ------------------------------------------------------------------------ }`); diff --git a/packages/cookbook/accounts/create-testnet-account.ts b/packages/cookbook/accounts/create-testnet-account.ts index 833aa7579e..cd3e41d454 100644 --- a/packages/cookbook/accounts/create-testnet-account.ts +++ b/packages/cookbook/accounts/create-testnet-account.ts @@ -1,9 +1,10 @@ import { createTopLevelAccount, generateRandomKeyPair, - getPlaintextFilesystemSigner, + getSignerFromKeystore, getTestnetRpcProvider, } from '@near-js/client'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; @@ -14,34 +15,34 @@ export default async function createTestnetAccountCookbook(accountId: string, ne return; } - const credentialsPath = join(homedir(), '.near-credentials'); - // initialize testnet RPC provider - const rpcProvider = getTestnetRpcProvider(); - // initialize the transaction signer using a pre-existing key for `accountId` - const { keystore, signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + // initialize testnet RPC provider + const rpcProvider = getTestnetRpcProvider(); + // initialize the transaction signer using a pre-existing key for `accountId` + const keystore = new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials')); + const signer = getSignerFromKeystore(accountId, 'testnet', keystore); - // create a new key from random data - const keyPair = generateRandomKeyPair('ed25519'); + // create a new key from random data + const keyPair = generateRandomKeyPair('ed25519'); - const newAccount = newAccountId || `${new Date().valueOf()}-${Math.ceil(Math.random() * 10e12)}.testnet`; + const newAccount = newAccountId || `${new Date().valueOf()}-${Math.ceil(Math.random() * 10e12)}.testnet`; - await createTopLevelAccount({ - account: accountId, - contract: 'testnet', - initialBalance: 100n, - newAccount, - newPublicKey: keyPair.getPublicKey().toString(), - deps: { - rpcProvider, - signer, - }, - }); + await createTopLevelAccount({ + account: accountId, + contract: 'testnet', + initialBalance: 100n, + newAccount, + newPublicKey: keyPair.getPublicKey().toString(), + deps: { + rpcProvider, + signer, + }, + }); - // save new account in plaintext filesystem keystore - await keystore.setKey('testnet', accountId, keyPair); + // save new account in plaintext filesystem keystore + await keystore.setKey('testnet', accountId, keyPair); console.log(chalk`{white ------------------------------------------------------------------------ }`); - console.log(chalk`{bold.green RESULTS} {white Created testnet account}` ); + console.log(chalk`{bold.green RESULTS} {white Created testnet account}`); console.log(chalk`{white ------------------------------------------------------------------------ }`); console.log(chalk`{bold.white New Account} {white |} {bold.yellow ${accountId}} {white |} {bold.yellow ${keyPair.getPublicKey().toString()}}`); console.log(chalk`{white ------------------------------------------------------------------------ }`); diff --git a/packages/cookbook/index.ts b/packages/cookbook/index.ts new file mode 100644 index 0000000000..1bef8a034d --- /dev/null +++ b/packages/cookbook/index.ts @@ -0,0 +1,3 @@ +export * from './accounts'; +export * from './transactions'; +export * from './utils'; diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index de062e0a0e..d38f0bbf2b 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -3,7 +3,7 @@ "private": true, "version": "1.0.22", "description": "", - "main": "src/index.ts", + "main": "index.ts", "type": "module", "author": "", "license": "ISC", diff --git a/packages/cookbook/src/index.ts b/packages/cookbook/src/index.ts deleted file mode 100644 index 122bda714b..0000000000 --- a/packages/cookbook/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from '../accounts'; -export * from '../transactions'; -export * from '../utils'; diff --git a/packages/cookbook/transactions/batch-transactions.ts b/packages/cookbook/transactions/batch-transactions.ts index dc33dcc55e..612363faa9 100644 --- a/packages/cookbook/transactions/batch-transactions.ts +++ b/packages/cookbook/transactions/batch-transactions.ts @@ -1,8 +1,9 @@ import { - getPlaintextFilesystemSigner, + getSignerFromKeystore, getTestnetRpcProvider, SignedTransactionComposer, } from '@near-js/client'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; import chalk from 'chalk'; import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; @@ -19,12 +20,10 @@ export default async function batchTransactions(accountId: string = CONTRACT_NAM return; } - const credentialsPath = join(homedir(), '.near-credentials'); - // initialize testnet RPC provider const rpcProvider = getTestnetRpcProvider(); // initialize the transaction signer using a pre-existing key for `accountId` - const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + const signer = getSignerFromKeystore(accountId, 'testnet', new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials'))); const { result } = await SignedTransactionComposer.init({ sender: accountId, diff --git a/packages/cookbook/transactions/meta-transaction-relayer.ts b/packages/cookbook/transactions/meta-transaction-relayer.ts index c0d7973186..14e8820b56 100644 --- a/packages/cookbook/transactions/meta-transaction-relayer.ts +++ b/packages/cookbook/transactions/meta-transaction-relayer.ts @@ -1,5 +1,5 @@ import { - getPlaintextFilesystemSigner, + getSignerFromKeystore, getTestnetRpcProvider, SignedTransactionComposer, } from '@near-js/client'; @@ -7,6 +7,7 @@ import { encodeSignedDelegate } from '@near-js/transactions'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; /** * Submit a transaction to a relayer @@ -20,12 +21,10 @@ export default async function sendMetaTransactionViaRelayer(signerAccountId: str return; } - const credentialsPath = join(homedir(), '.near-credentials'); - // initialize testnet RPC provider const rpcProvider = getTestnetRpcProvider(); // initialize the transaction signer using a pre-existing key for `accountId` - const { signer } = getPlaintextFilesystemSigner({ account: signerAccountId, network: 'testnet', filepath: credentialsPath }); + const signer = getSignerFromKeystore(signerAccountId, 'testnet', new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials'))); const signedDelegate = await SignedTransactionComposer.init({ sender: signerAccountId, diff --git a/packages/cookbook/transactions/meta-transaction.ts b/packages/cookbook/transactions/meta-transaction.ts index 3d199e2c3a..71593d5a36 100644 --- a/packages/cookbook/transactions/meta-transaction.ts +++ b/packages/cookbook/transactions/meta-transaction.ts @@ -1,11 +1,12 @@ import { - getPlaintextFilesystemSigner, - getTestnetRpcProvider, - SignedTransactionComposer, + getSignerFromKeystore, + getTestnetRpcProvider, + SignedTransactionComposer, } from '@near-js/client'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; // access keys are required for the sender and signer const RECEIVER_ACCOUNT_ID = 'receiver.testnet'; // the ultimate recipient of the meta transaction execution @@ -24,12 +25,14 @@ export default async function metaTransaction(signerAccountId: string = SIGNER_A return; } - const credentialsPath = join(homedir(), '.near-credentials'); - // initialize testnet RPC provider const rpcProvider = getTestnetRpcProvider(); // initialize the transaction signer using a pre-existing key for `accountId` - const { signer } = getPlaintextFilesystemSigner({ account: signerAccountId, network: 'testnet', filepath: credentialsPath }); + const signer = getSignerFromKeystore( + signerAccountId, + 'testnet', + new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials')) + ); const { delegateAction, signature } = await SignedTransactionComposer.init({ sender: signerAccountId, @@ -43,11 +46,11 @@ export default async function metaTransaction(signerAccountId: string = SIGNER_A .toSignedDelegateAction(); // initialize the relayer's signer - const { signer: relayerSigner } = getPlaintextFilesystemSigner({ - account: senderAccountId, - network: 'testnet', - filepath: credentialsPath, - }); + const relayerSigner = getSignerFromKeystore( + senderAccountId, + 'testnet', + new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials')) + ); // sign the outer transaction using the relayer's key return SignedTransactionComposer.init({ diff --git a/packages/cookbook/tsconfig.json b/packages/cookbook/tsconfig.json index d9964a7efc..56d9083e21 100644 --- a/packages/cookbook/tsconfig.json +++ b/packages/cookbook/tsconfig.json @@ -4,6 +4,6 @@ "noEmit": true }, "files": [ - "src/index.ts" + "index.ts" ] } \ No newline at end of file diff --git a/packages/cookbook/utils/calculate-gas.ts b/packages/cookbook/utils/calculate-gas.ts index 647e3d985e..e35866fdd8 100644 --- a/packages/cookbook/utils/calculate-gas.ts +++ b/packages/cookbook/utils/calculate-gas.ts @@ -1,10 +1,11 @@ import { formatNearAmount, functionCall, - getPlaintextFilesystemSigner, + getSignerFromKeystore, getTestnetRpcProvider, MAX_GAS, } from '@near-js/client'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; @@ -18,12 +19,10 @@ export default async function calculateGas(accountId: string, method = METHOD_NA return; } - const credentialsPath = join(homedir(), '.near-credentials'); - // initialize testnet RPC provider const rpcProvider = getTestnetRpcProvider(); // initialize the transaction signer using a pre-existing key for `accountId` - const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + const signer = getSignerFromKeystore(accountId, 'testnet', new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials'))); const { outcome: { diff --git a/packages/cookbook/utils/deploy-contract.ts b/packages/cookbook/utils/deploy-contract.ts index 7030632073..654d280767 100644 --- a/packages/cookbook/utils/deploy-contract.ts +++ b/packages/cookbook/utils/deploy-contract.ts @@ -1,8 +1,9 @@ import { deployContract, - getPlaintextFilesystemSigner, + getSignerFromKeystore, getTestnetRpcProvider, } from '@near-js/client'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; @@ -16,12 +17,10 @@ export default async function deployContractCookbook(accountId: string, wasmPath return; } - const credentialsPath = join(homedir(), '.near-credentials'); - // initialize testnet RPC provider const rpcProvider = getTestnetRpcProvider(); // initialize the transaction signer using a pre-existing key for `accountId` - const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + const signer = getSignerFromKeystore(accountId, 'testnet', new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials'))); await deployContract({ account: accountId, diff --git a/packages/cookbook/utils/index.ts b/packages/cookbook/utils/index.ts index fb1096d1ff..db69201589 100644 --- a/packages/cookbook/utils/index.ts +++ b/packages/cookbook/utils/index.ts @@ -2,7 +2,7 @@ export * from './calculate-gas'; export * from './check-account-existence'; export * from './deploy-contract'; export * from './get-state'; -export * from './ledger-signing'; +// export * from './ledger-signing'; export * from './unwrap-near'; export * from './verify-signature'; export * from './wrap-near'; diff --git a/packages/cookbook/utils/unwrap-near.ts b/packages/cookbook/utils/unwrap-near.ts index 152f804ba1..b88926e6fc 100644 --- a/packages/cookbook/utils/unwrap-near.ts +++ b/packages/cookbook/utils/unwrap-near.ts @@ -1,9 +1,10 @@ import { functionCall, - getPlaintextFilesystemSigner, + getSignerFromKeystore, getTestnetRpcProvider, view, } from '@near-js/client'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; import { formatNearAmount, parseNearAmount } from '@near-js/utils'; import chalk from 'chalk'; import { join } from 'node:path'; @@ -18,12 +19,10 @@ export default async function unwrapNear(accountId: string, unwrapAmount: bigint return; } - const credentialsPath = join(homedir(), '.near-credentials'); - // initialize testnet RPC provider const rpcProvider = getTestnetRpcProvider(); // initialize the transaction signer using a pre-existing key for `accountId` - const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + const signer = getSignerFromKeystore(accountId, 'testnet', new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials'))); const getStorageBalance = () => view({ account: wrapContract, diff --git a/packages/cookbook/utils/wrap-near.ts b/packages/cookbook/utils/wrap-near.ts index a847de031f..8e8087e2c6 100644 --- a/packages/cookbook/utils/wrap-near.ts +++ b/packages/cookbook/utils/wrap-near.ts @@ -1,10 +1,11 @@ import { formatNearAmount, - getPlaintextFilesystemSigner, + getSignerFromKeystore, getTestnetRpcProvider, SignedTransactionComposer, view, } from '@near-js/client'; +import { UnencryptedFileSystemKeyStore } from '@near-js/keystores-node'; import chalk from 'chalk'; import { join } from 'node:path'; import { homedir } from 'node:os'; @@ -18,12 +19,10 @@ export default async function wrapNear(accountId: string, wrapAmount: bigint, wr return; } - const credentialsPath = join(homedir(), '.near-credentials'); - // initialize testnet RPC provider const rpcProvider = getTestnetRpcProvider(); // initialize the transaction signer using a pre-existing key for `accountId` - const { signer } = getPlaintextFilesystemSigner({ account: accountId, network: 'testnet', filepath: credentialsPath }); + const signer = getSignerFromKeystore(accountId, 'testnet', new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials'))); const getStorageBalance = () => view({ account: wrapContract, From aae2265fcc6fa3eb3d8a9c1b1854635fd296e80f Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 25 Sep 2024 16:30:59 -0700 Subject: [PATCH 44/55] chore: lint config --- packages/cookbook/package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index d38f0bbf2b..5f4ca17128 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -9,8 +9,6 @@ "license": "ISC", "scripts": { "build": "tsc -p tsconfig.json", - "lint": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts --no-eslintrc", - "lint:fix": "eslint -c ../../.eslintrc.ts.yml src/**/*.ts --no-eslintrc --fix", "addFullAccessKey": "tsx -e \"import f from './accounts/access-keys/create-full-access-key.ts'; f(...process.argv.slice(1));\"", "addFunctionCallAccessKey": "tsx -e \"import f from './accounts/access-keys/create-function-access-key.ts'; f(...process.argv.slice(1));\"", "batchTransactions": "tsx -e \"import f from './transactions/batch-transactions.ts'; f(...process.argv.slice(1));\"", From e0751ae31e059dabe7d44f21e059c5e8d058aa50 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 25 Sep 2024 16:34:27 -0700 Subject: [PATCH 45/55] feat: simpler types for access key --- .../client/src/interfaces/dependencies.ts | 4 +- packages/client/src/interfaces/view.ts | 17 +++++++ packages/client/src/signing/signers.ts | 5 ++- packages/client/src/view.ts | 45 +++++++++++++++++-- 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/packages/client/src/interfaces/dependencies.ts b/packages/client/src/interfaces/dependencies.ts index ccda4ce342..f8124e5e63 100644 --- a/packages/client/src/interfaces/dependencies.ts +++ b/packages/client/src/interfaces/dependencies.ts @@ -1,7 +1,7 @@ import type { PublicKey } from '@near-js/crypto'; -import type { AccessKeyView } from '@near-js/types'; import type { RpcQueryProvider } from './providers'; +import { FullAccessKey, FunctionCallAccessKey } from './view'; interface Dependent { deps: T; @@ -13,7 +13,7 @@ export interface MessageSigner { } export interface AccessKeySigner extends MessageSigner { - getAccessKey(ignoreCache?: boolean): Promise; + getAccessKey(ignoreCache?: boolean): Promise; getNonce(ignoreCache?: boolean): Promise; getSigningAccount(): string; } diff --git a/packages/client/src/interfaces/view.ts b/packages/client/src/interfaces/view.ts index 834ca11f3f..2d0ae43aaa 100644 --- a/packages/client/src/interfaces/view.ts +++ b/packages/client/src/interfaces/view.ts @@ -25,3 +25,20 @@ export interface ViewContractStateParams extends ViewAccountParams { export interface ViewAccessKeyParams extends ViewAccountParams { publicKey: string; } + +interface AccessKey { + nonce: bigint; + publicKey: string; +} + +export interface FullAccessKey extends AccessKey {} +export interface FunctionCallAccessKey extends AccessKey { + contract: string; + methods: string[]; + allowance: bigint; +} + +export interface AccessKeys { + fullAccessKeys: FullAccessKey[]; + functionCallAccessKeys: FunctionCallAccessKey[]; +} diff --git a/packages/client/src/signing/signers.ts b/packages/client/src/signing/signers.ts index 11f831c47f..723fe5d036 100644 --- a/packages/client/src/signing/signers.ts +++ b/packages/client/src/signing/signers.ts @@ -7,12 +7,13 @@ import { InMemorySigner } from '@near-js/signers'; import type { AccessKeySigner, + FullAccessKey, + FunctionCallAccessKey, MessageSigner, SignerDependency, ViewAccountParams, } from '../interfaces'; import { getAccessKey } from '../view'; -import { AccessKeyView } from '@near-js/types'; /** * Initialize a message signer from a KeyPair @@ -64,7 +65,7 @@ export function getSignerFromKeystore(account: string, network: string, keyStore * @param deps sign-and-send dependencies */ export function getAccessKeySigner({ account, blockReference, deps: { rpcProvider, signer } }: ViewAccountParams & SignerDependency): AccessKeySigner { - let accessKey: AccessKeyView; + let accessKey: FullAccessKey | FunctionCallAccessKey; let nonce: bigint; return { diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index 27ae8ea3c0..1945d95c5f 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -10,6 +10,8 @@ import type { } from '@near-js/types'; import type { + FunctionCallAccessKey, + FullAccessKey, RpcProviderDependency, RpcProviderQueryParams, ViewAccessKeyParams, @@ -106,8 +108,8 @@ export async function view({ account, method, a * @param blockReference block ID/finality * @param deps readonly RPC dependencies */ -export function getAccessKey({ account, publicKey, blockReference, deps }: ViewAccessKeyParams) { - return query({ +export async function getAccessKey({ account, publicKey, blockReference, deps }: ViewAccessKeyParams) { + const { nonce, permission } = await query({ request: RequestType.ViewAccessKey, account, args: { @@ -116,6 +118,21 @@ export function getAccessKey({ account, publicKey, blockReference, deps }: ViewA blockReference, deps, }); + + if (permission === 'FullAccess') { + return { + nonce, + publicKey, + } as FullAccessKey; + } + const { FunctionCall: { allowance, receiver_id, method_names } } = permission; + return { + allowance: BigInt(allowance), + contract: receiver_id, + methods: method_names, + nonce, + publicKey, + } as FunctionCallAccessKey; } /** @@ -139,13 +156,33 @@ export function getAccountState({ account, blockReference, deps }: ViewAccountPa * @param blockReference block ID/finality * @param deps readonly RPC dependencies */ -export function getAccessKeys({ account, blockReference, deps }: ViewAccountParams) { - return query({ +export async function getAccessKeys({ account, blockReference, deps }: ViewAccountParams) { + const { keys } = await query({ request: RequestType.ViewAccessKeyList, account, blockReference, deps, }); + + return keys.reduce((accessKeys, { access_key: { nonce, permission }, public_key: publicKey }) => { + if (permission === 'FullAccess') { + accessKeys.fullAccessKeys.push({ + nonce, + publicKey, + }); + } else { + const { FunctionCall: { allowance, receiver_id, method_names } } = permission; + accessKeys.functionCallAccessKeys.push({ + allowance: BigInt(allowance), + contract: receiver_id, + methods: method_names, + nonce, + publicKey, + }); + } + + return accessKeys; + }, { fullAccessKeys: [] as FullAccessKey[], functionCallAccessKeys: [] as FunctionCallAccessKey[] }); } /** From fc2cbd799c073d46af8da7288c74d5adca219801 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Wed, 25 Sep 2024 16:53:05 -0700 Subject: [PATCH 46/55] chore: remove ledger functionality --- packages/client/package.json | 9 +- packages/client/src/signing/index.ts | 4 - packages/client/src/signing/ledger.ts | 52 --- packages/cookbook/package.json | 1 - packages/cookbook/utils/index.ts | 1 - packages/cookbook/utils/ledger-signing.ts | 24 -- pnpm-lock.yaml | 404 +--------------------- 7 files changed, 3 insertions(+), 492 deletions(-) delete mode 100644 packages/client/src/signing/ledger.ts delete mode 100644 packages/cookbook/utils/ledger-signing.ts diff --git a/packages/client/package.json b/packages/client/package.json index 7f87d4e16a..70fe6ef10c 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -12,7 +12,6 @@ "lint:fix": "eslint -c .eslintrc.yml src/**/*.ts --no-eslintrc --no-error-on-unmatched-pattern --fix" }, "dependencies": { - "@ledgerhq/hw-transport-node-hid": "6.29.4", "@near-js/crypto": "workspace:*", "@near-js/keystores": "workspace:*", "@near-js/providers": "workspace:*", @@ -21,19 +20,13 @@ "@near-js/types": "workspace:*", "@near-js/utils": "workspace:*", "@noble/hashes": "1.3.3", - "isomorphic-fetch": "3.0.0", - "near-ledger-js": "0.2.1" + "isomorphic-fetch": "3.0.0" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { - "@ledgerhq/devices": "8.4.3", - "@ledgerhq/hw-transport-node-hid-noevents": "6.30.4", - "@ledgerhq/logs": "6.12.0", - "@types/ledgerhq__hw-transport": "4.21.8", "@types/node": "20.0.0", - "@types/node-hid": "1.3.4", "build": "workspace:*", "tsconfig": "workspace:*", "typescript": "5.4.5" diff --git a/packages/client/src/signing/index.ts b/packages/client/src/signing/index.ts index 0a6d666b6d..01e03a91e1 100644 --- a/packages/client/src/signing/index.ts +++ b/packages/client/src/signing/index.ts @@ -1,7 +1,3 @@ -export { - getUsbLedgerSigner, - getBrowserLedgerSigner, -} from './ledger'; export { getSignerFromKeyPair, getSignerFromKeystore, diff --git a/packages/client/src/signing/ledger.ts b/packages/client/src/signing/ledger.ts deleted file mode 100644 index e06d666726..0000000000 --- a/packages/client/src/signing/ledger.ts +++ /dev/null @@ -1,52 +0,0 @@ -import TransportNodeHidModule from '@ledgerhq/hw-transport-node-hid'; -import { KeyType, PublicKey } from '@near-js/crypto'; -import { createClient, getSupportedTransport } from 'near-ledger-js'; - -import { LEDGER_HD_PATH } from '../constants'; -import type { MessageSigner } from '../interfaces'; -import type Transport from '@ledgerhq/hw-transport'; - -interface LedgerSignerParams { - transport: Transport; - hdPath: string; - cachePublicKey: boolean; -} - -export async function getNearLedgerSigner({ transport, hdPath, cachePublicKey }: LedgerSignerParams) { - if (!transport) { - throw new Error('Invalid transport for ledger signer'); - } - - transport.setScrambleKey('NEAR'); - const client = await createClient(transport); - let cachedKeyData: Uint8Array = null; - - return { - async getPublicKey(): Promise { - let publicKeyData = cachePublicKey ? cachedKeyData : null; - if (!publicKeyData) { - publicKeyData = await client.getPublicKey(); - - if (cachePublicKey) { - cachedKeyData = publicKeyData; - } - } - - return new PublicKey({ - keyType: KeyType.ED25519, - data: publicKeyData, - }); - }, - async signMessage(m: Uint8Array): Promise { - return client.sign(m, hdPath); - }, - }; -} - -export async function getBrowserLedgerSigner(hdPath: string = LEDGER_HD_PATH, cachePublicKey: boolean = false): Promise { - return getNearLedgerSigner({ transport: await getSupportedTransport(), hdPath, cachePublicKey }); -} - -export async function getUsbLedgerSigner(hdPath: string = LEDGER_HD_PATH, cachePublicKey: boolean = false): Promise { - return getNearLedgerSigner({ transport: await TransportNodeHidModule.create(), hdPath, cachePublicKey }); -} diff --git a/packages/cookbook/package.json b/packages/cookbook/package.json index 5f4ca17128..271d469e28 100644 --- a/packages/cookbook/package.json +++ b/packages/cookbook/package.json @@ -23,7 +23,6 @@ "getTransactionStatus": "tsx -e \"import f from './transactions/get-tx-status.ts'; f(...process.argv.slice(1));\"", "metaTransaction": "tsx -e \"import f from './transactions/meta-transaction.ts'; f(...process.argv.slice(1));\"", "metaTransactionRelayer": "tsx -e \"import f from './transactions/meta-transaction-relayer.ts'; f(...process.argv.slice(1));\"", - "signWithLedger": "tsx -e \"import f from './utils/ledger-signing.ts'; f(...process.argv.slice(1));\"", "traverseBlocks": "tsx -e \"import f from './transactions/traverse-blocks.ts'; f(...process.argv.slice(1));\"", "unwrapNear": "tsx -e \"import f from './utils/unwrap-near.ts'; f(...process.argv.slice(1));\"", "verifySignature": "tsx -e \"import f from './utils/verify-signature.ts'; f(...process.argv.slice(1));\"", diff --git a/packages/cookbook/utils/index.ts b/packages/cookbook/utils/index.ts index db69201589..0ca463d46b 100644 --- a/packages/cookbook/utils/index.ts +++ b/packages/cookbook/utils/index.ts @@ -2,7 +2,6 @@ export * from './calculate-gas'; export * from './check-account-existence'; export * from './deploy-contract'; export * from './get-state'; -// export * from './ledger-signing'; export * from './unwrap-near'; export * from './verify-signature'; export * from './wrap-near'; diff --git a/packages/cookbook/utils/ledger-signing.ts b/packages/cookbook/utils/ledger-signing.ts deleted file mode 100644 index de1338510c..0000000000 --- a/packages/cookbook/utils/ledger-signing.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { getTestnetRpcProvider, getUsbLedgerSigner, SignedTransactionComposer } from '@near-js/client'; - -export default async function signWithLedger() { - const ledgerSigner = await getUsbLedgerSigner(); - const publicKey = await ledgerSigner.getPublicKey(); - - const rpcProvider = getTestnetRpcProvider(); - - const { signedTransaction: { signature: { data } } } = await SignedTransactionComposer.init({ - sender: 'a.testnet', - receiver: 'b.testnet', - nonce: 100n, - blockHash: (await rpcProvider.block({ finality: 'optimistic' })).header.hash, - publicKey, - deps: { - rpcProvider, - signer: ledgerSigner, - } - }) - .transfer(100n) - .toSignedTransaction(); - - console.log(`Signed message with ledger key ${publicKey}: ${Buffer.from(data).toString('base64')}`); -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31048e04ee..fafe83ccdb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -193,9 +193,6 @@ importers: packages/client: dependencies: - '@ledgerhq/hw-transport-node-hid': - specifier: 6.29.4 - version: 6.29.4 '@near-js/crypto': specifier: workspace:* version: link:../crypto @@ -223,28 +220,10 @@ importers: isomorphic-fetch: specifier: 3.0.0 version: 3.0.0(encoding@0.1.13) - near-ledger-js: - specifier: 0.2.1 - version: 0.2.1 devDependencies: - '@ledgerhq/devices': - specifier: 8.4.3 - version: 8.4.3 - '@ledgerhq/hw-transport-node-hid-noevents': - specifier: 6.30.4 - version: 6.30.4 - '@ledgerhq/logs': - specifier: 6.12.0 - version: 6.12.0 - '@types/ledgerhq__hw-transport': - specifier: 4.21.8 - version: 4.21.8 '@types/node': specifier: 20.0.0 version: 20.0.0 - '@types/node-hid': - specifier: 1.3.4 - version: 1.3.4 build: specifier: workspace:* version: link:../build @@ -1248,46 +1227,6 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@ledgerhq/devices@5.51.1': - resolution: {integrity: sha512-4w+P0VkbjzEXC7kv8T1GJ/9AVaP9I6uasMZ/JcdwZBS3qwvKo5A5z9uGhP5c7TvItzcmPb44b5Mw2kT+WjUuAA==} - - '@ledgerhq/devices@8.4.3': - resolution: {integrity: sha512-+ih+M27E6cm6DHrmw3GbS3mEaznCyFc0e62VdQux40XK2psgYhL2yBPftM4KCrBYm1UbHqXzqLN+Jb7rNIzsHg==} - - '@ledgerhq/errors@5.50.0': - resolution: {integrity: sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow==} - - '@ledgerhq/errors@6.19.0': - resolution: {integrity: sha512-c3Jid7euMSnpHFp8H7iPtsmKDjwbTjlG46YKdw+RpCclsqtBx1uQDlYmcbP1Yv9201kVlUFUhhP4H623k8xzlQ==} - - '@ledgerhq/hw-transport-node-hid-noevents@6.30.4': - resolution: {integrity: sha512-01cPvdIe8xwfrcOoR7vr2kN/4HHYVvAydU7hAHvLMC76irbhkbuTZiWzhr54FmEOdqz+fZnC+MUDIQHLQsjXQw==} - - '@ledgerhq/hw-transport-node-hid@6.29.4': - resolution: {integrity: sha512-Spwb7L9YnKrhvRVj5hJ1DzmvQJHVeNSmN+yTVSFQ4QRcjqDKC+N+zVO/7HDhLl5TWo1KmgNx4/BhAZ648gpp5w==} - - '@ledgerhq/hw-transport-u2f@5.36.0-deprecated': - resolution: {integrity: sha512-T/+mGHIiUK/ZQATad6DMDmobCMZ1mVST952009jKzhaE1Et2Uy2secU+QhRkx3BfEAkvwa0zSRSYCL9d20Iqjg==} - deprecated: '@ledgerhq/hw-transport-u2f is deprecated. Please use @ledgerhq/hw-transport-webusb or @ledgerhq/hw-transport-webhid. https://github.com/LedgerHQ/ledgerjs/blob/master/docs/migrate_webusb.md' - - '@ledgerhq/hw-transport-webhid@5.51.1': - resolution: {integrity: sha512-w/2qSU0vwFY+D/4ucuYRViO7iS3Uuxmj9sI/Iiqkoiax9Xppb0/6H5m3ffKv6iPMmRYbgwCgXorqx4SLLSD8Kg==} - - '@ledgerhq/hw-transport-webusb@5.53.1': - resolution: {integrity: sha512-A/f+xcrkIAZiJrvPpDvsrjxQX4cI2kbdiunQkwsYmOG3Bp4z89ZnsBiC7YBst4n2/g+QgTg0/KPVtODU5djooQ==} - - '@ledgerhq/hw-transport@5.51.1': - resolution: {integrity: sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw==} - - '@ledgerhq/hw-transport@6.31.3': - resolution: {integrity: sha512-rFplkHWF5NXtlYwAusqLlMu298NHtRD+2q/jrTYc//uu/xJO9LkDIgKid6IVF2+e1Wj7yX6YQVrU6L0Yu1ntEw==} - - '@ledgerhq/logs@5.50.0': - resolution: {integrity: sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA==} - - '@ledgerhq/logs@6.12.0': - resolution: {integrity: sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA==} - '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -1443,15 +1382,9 @@ packages: '@types/keyv@3.1.4': resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} - '@types/ledgerhq__hw-transport@4.21.8': - resolution: {integrity: sha512-uO2AJYZUVCwgyqgyy2/KW+JsQaO0hcwDdubRaHgF2ehO0ngGAY41PbE8qnPnmUw1uerMXONvL68QFioA7Y6C5g==} - '@types/minimist@1.2.5': resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} - '@types/node-hid@1.3.4': - resolution: {integrity: sha512-0ootpsYetN9vjqkDSwm/cA4fk/9yGM/PO0X8SLPE/BzXlUaBelImMWMymtF9QEoEzxY0pnhcROIJM0CNSUqO8w==} - '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -1479,9 +1412,6 @@ packages: '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - '@types/w3c-web-usb@1.0.10': - resolution: {integrity: sha512-CHgUI5kTc/QLMP8hODUHhge0D4vx+9UiAwIGiT0sTy/B2XpdX1U5rJt6JSISgr6ikRT7vxV9EVAFeYZqUnl1gQ==} - '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -1699,12 +1629,6 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} - bindings@1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - bn.js@4.12.0: resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} @@ -1754,9 +1678,6 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -1832,9 +1753,6 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -2009,10 +1927,6 @@ packages: babel-plugin-macros: optional: true - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -2222,10 +2136,6 @@ packages: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} - expand-template@2.0.3: - resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} - engines: {node: '>=6'} - expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2267,9 +2177,6 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} - file-uri-to-path@1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -2303,9 +2210,6 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} - fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -2386,9 +2290,6 @@ packages: engines: {node: '>=16'} hasBin: true - github-from-package@0.0.0: - resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2570,9 +2471,6 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - ini@4.1.1: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3151,9 +3049,6 @@ packages: resolution: {integrity: sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q==} engines: {node: '>= 8.0.0'} - mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} @@ -3167,9 +3062,6 @@ packages: engines: {npm: '>=1.4.0'} hasBin: true - napi-build-utils@1.0.2: - resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -3182,9 +3074,6 @@ packages: near-hello@0.5.1: resolution: {integrity: sha512-k7S8VFyESWgkKYDso99B4XbxAdo0VX9b3+GAaO5PvMgQjNr/6o09PHRywg/NkBQpf+ZYj7nNpJcyrNJGQsvA3w==} - near-ledger-js@0.2.1: - resolution: {integrity: sha512-8anZb6e96gJNBOKUR/HReLN/x8BmBhCpyPq+XxFbx8jxmsRz+M1Hxq085+ROYaMI2EDJqatrjjLAdArk13BOhA==} - near-sandbox@0.0.18: resolution: {integrity: sha512-6/AjLOzGLQsSzTHavrRe7CVMU5+CUnTg/7g66a7+jkP22ff5CupnwXrUE14iJ2QyyhRZr7VuQQj/G+glUmaNQg==} hasBin: true @@ -3197,19 +3086,9 @@ packages: resolution: {integrity: sha512-lkRCP2os8v7W+q1maQPSEI98f+YiPb86ibDyIy8bxr6fqnJPOsIRR3xE33rOwBsiph3WuUH46qtpYK0a+Co+EQ==} engines: {node: '>= 14.0.0', npm: '>= 6.0.0'} - node-abi@3.67.0: - resolution: {integrity: sha512-bLn/fU/ALVBE9wj+p4Y21ZJWYFjUXLXPi/IewyLZkx3ApxKDNBWCKdReeKOtD8dWpOdDCeMyLh6ZewzcLsG2Nw==} - engines: {node: '>=10'} - - node-addon-api@3.2.1: - resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} - node-addon-api@5.1.0: resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} - node-addon-api@6.1.0: - resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} - node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} engines: {node: 4.x || >=6.0.0} @@ -3227,11 +3106,6 @@ packages: resolution: {integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==} hasBin: true - node-hid@2.1.2: - resolution: {integrity: sha512-qhCyQqrPpP93F/6Wc/xUR7L8mAJW0Z6R7HMQV8jCHHksAxNDe/4z4Un/H9CpLOT+5K39OPyt9tIQlavxWES3lg==} - engines: {node: '>=10'} - hasBin: true - node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -3401,18 +3275,10 @@ packages: resolution: {integrity: sha512-iDUm90wfgtfd1PDV1oEnQj/4jBIU9hCSJeV0kQKThwDpbseFxC4TdpoMYlwE9maol5u0wMGZX9cNG2h1/0Lhww==} engines: {node: '>=12.0.0'} - platform@1.3.6: - resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} - possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} - prebuild-install@7.1.2: - resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} - engines: {node: '>=10'} - hasBin: true - preferred-pm@3.1.3: resolution: {integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w==} engines: {node: '>=10'} @@ -3479,10 +3345,6 @@ packages: randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -3498,10 +3360,6 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -3575,10 +3433,6 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@6.6.7: - resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} - engines: {npm: '>=2.0.0'} - rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} @@ -3668,12 +3522,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - - simple-get@4.0.1: - resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} - sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -3752,9 +3600,6 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3783,10 +3628,6 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} - strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -3807,13 +3648,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - tar-fs@2.1.1: - resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} - - tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} - tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -3924,9 +3758,6 @@ packages: '@swc/wasm': optional: true - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} @@ -3935,9 +3766,6 @@ packages: engines: {node: '>=8.0.0'} hasBin: true - tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - turbo-android-arm64@1.4.5: resolution: {integrity: sha512-cKPJVyS1A2BBVbcH8XVeBArtEjHxioEm9zQa3Hv68usQOOFW+KOjH+0fGvjqMrWztLVFhE+npeVsnyu/6AmRew==} cpu: [arm64] @@ -4068,9 +3896,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - u2f-api@0.2.7: - resolution: {integrity: sha512-fqLNg8vpvLOD5J/z4B6wpPg4Lvowz1nJ9xdHcCzdUPKcFE/qNCceV2gNZxSJd5vhAZemHr/K/hbzVA0zxB5mkg==} - unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -4101,13 +3926,6 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - usb@2.9.0: - resolution: {integrity: sha512-G0I/fPgfHUzWH8xo2KkDxTTFruUWfppgSFJ+bQxz/kVY2x15EQ/XDB7dqD1G432G4gBG4jYQuF3U7j/orSs5nw==} - engines: {node: '>=10.20.0 <11.x || >=12.17.0 <13.0 || >=14.0.0'} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -5081,81 +4899,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@ledgerhq/devices@5.51.1': - dependencies: - '@ledgerhq/errors': 5.50.0 - '@ledgerhq/logs': 5.50.0 - rxjs: 6.6.7 - semver: 7.6.2 - - '@ledgerhq/devices@8.4.3': - dependencies: - '@ledgerhq/errors': 6.19.0 - '@ledgerhq/logs': 6.12.0 - rxjs: 7.8.1 - semver: 7.6.2 - - '@ledgerhq/errors@5.50.0': {} - - '@ledgerhq/errors@6.19.0': {} - - '@ledgerhq/hw-transport-node-hid-noevents@6.30.4': - dependencies: - '@ledgerhq/devices': 8.4.3 - '@ledgerhq/errors': 6.19.0 - '@ledgerhq/hw-transport': 6.31.3 - '@ledgerhq/logs': 6.12.0 - node-hid: 2.1.2 - - '@ledgerhq/hw-transport-node-hid@6.29.4': - dependencies: - '@ledgerhq/devices': 8.4.3 - '@ledgerhq/errors': 6.19.0 - '@ledgerhq/hw-transport': 6.31.3 - '@ledgerhq/hw-transport-node-hid-noevents': 6.30.4 - '@ledgerhq/logs': 6.12.0 - lodash: 4.17.21 - node-hid: 2.1.2 - usb: 2.9.0 - - '@ledgerhq/hw-transport-u2f@5.36.0-deprecated': - dependencies: - '@ledgerhq/errors': 5.50.0 - '@ledgerhq/hw-transport': 5.51.1 - '@ledgerhq/logs': 5.50.0 - u2f-api: 0.2.7 - - '@ledgerhq/hw-transport-webhid@5.51.1': - dependencies: - '@ledgerhq/devices': 5.51.1 - '@ledgerhq/errors': 5.50.0 - '@ledgerhq/hw-transport': 5.51.1 - '@ledgerhq/logs': 5.50.0 - - '@ledgerhq/hw-transport-webusb@5.53.1': - dependencies: - '@ledgerhq/devices': 5.51.1 - '@ledgerhq/errors': 5.50.0 - '@ledgerhq/hw-transport': 5.51.1 - '@ledgerhq/logs': 5.50.0 - - '@ledgerhq/hw-transport@5.51.1': - dependencies: - '@ledgerhq/devices': 5.51.1 - '@ledgerhq/errors': 5.50.0 - events: 3.3.0 - - '@ledgerhq/hw-transport@6.31.3': - dependencies: - '@ledgerhq/devices': 8.4.3 - '@ledgerhq/errors': 6.19.0 - '@ledgerhq/logs': 6.12.0 - events: 3.3.0 - - '@ledgerhq/logs@5.50.0': {} - - '@ledgerhq/logs@6.12.0': {} - '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.24.7 @@ -5388,16 +5131,8 @@ snapshots: dependencies: '@types/node': 20.0.0 - '@types/ledgerhq__hw-transport@4.21.8': - dependencies: - '@types/node': 20.0.0 - '@types/minimist@1.2.5': {} - '@types/node-hid@1.3.4': - dependencies: - '@types/node': 20.0.0 - '@types/node@12.20.55': {} '@types/node@18.11.18': {} @@ -5420,8 +5155,6 @@ snapshots: '@types/stack-utils@2.0.3': {} - '@types/w3c-web-usb@1.0.10': {} - '@types/yargs-parser@21.0.3': {} '@types/yargs@17.0.32': @@ -5691,16 +5424,6 @@ snapshots: dependencies: is-windows: 1.0.2 - bindings@1.5.0: - dependencies: - file-uri-to-path: 1.0.0 - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - bn.js@4.12.0: {} bn.js@5.2.1: {} @@ -5757,11 +5480,6 @@ snapshots: buffer-from@1.1.2: {} - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -5845,8 +5563,6 @@ snapshots: chardet@0.7.0: {} - chownr@1.1.4: {} - chownr@2.0.0: {} ci-info@3.9.0: {} @@ -6062,8 +5778,6 @@ snapshots: dedent@1.5.3: {} - deep-extend@0.6.0: {} - deep-is@0.1.4: {} deepmerge@4.3.1: {} @@ -6092,7 +5806,8 @@ snapshots: detect-indent@6.1.0: {} - detect-libc@2.0.3: {} + detect-libc@2.0.3: + optional: true detect-newline@3.1.0: {} @@ -6339,8 +6054,6 @@ snapshots: exit@0.1.2: {} - expand-template@2.0.3: {} - expect@29.7.0: dependencies: '@jest/expect-utils': 29.7.0 @@ -6395,8 +6108,6 @@ snapshots: dependencies: flat-cache: 3.2.0 - file-uri-to-path@1.0.0: {} - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -6439,8 +6150,6 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 - fs-constants@1.0.0: {} - fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -6523,8 +6232,6 @@ snapshots: meow: 12.1.1 split2: 4.2.0 - github-from-package@0.0.0: {} - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -6706,8 +6413,6 @@ snapshots: inherits@2.0.4: {} - ini@1.3.8: {} - ini@4.1.1: {} internal-slot@1.0.7: @@ -7623,16 +7328,12 @@ snapshots: mixme@0.5.10: {} - mkdirp-classic@0.5.3: {} - mkdirp@1.0.4: {} ms@2.1.2: {} mustache@4.0.0: {} - napi-build-utils@1.0.2: {} - natural-compare@1.4.0: {} near-abi@0.1.1: @@ -7663,14 +7364,6 @@ snapshots: near-hello@0.5.1: {} - near-ledger-js@0.2.1: - dependencies: - '@ledgerhq/hw-transport-u2f': 5.36.0-deprecated - '@ledgerhq/hw-transport-webhid': 5.51.1 - '@ledgerhq/hw-transport-webusb': 5.53.1 - bs58: 4.0.1 - platform: 1.3.6 - near-sandbox@0.0.18: dependencies: got: 11.8.6 @@ -7701,16 +7394,8 @@ snapshots: transitivePeerDependencies: - encoding - node-abi@3.67.0: - dependencies: - semver: 7.6.2 - - node-addon-api@3.2.1: {} - node-addon-api@5.1.0: {} - node-addon-api@6.1.0: {} - node-fetch@2.6.7(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 @@ -7724,12 +7409,6 @@ snapshots: node-gyp-build@4.8.1: {} - node-hid@2.1.2: - dependencies: - bindings: 1.5.0 - node-addon-api: 3.2.1 - prebuild-install: 7.1.2 - node-int64@0.4.0: {} node-notifier@8.0.2: @@ -7887,25 +7566,8 @@ snapshots: pvutils: 1.1.3 tslib: 2.6.3 - platform@1.3.6: {} - possible-typed-array-names@1.0.0: {} - prebuild-install@7.1.2: - dependencies: - detect-libc: 2.0.3 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 1.0.2 - node-abi: 3.67.0 - pump: 3.0.0 - rc: 1.2.8 - simple-get: 4.0.1 - tar-fs: 2.1.1 - tunnel-agent: 0.6.0 - preferred-pm@3.1.3: dependencies: find-up: 5.0.0 @@ -7965,13 +7627,6 @@ snapshots: dependencies: safe-buffer: 5.2.1 - rc@1.2.8: - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 - react-is@18.3.1: {} read-pkg-up@7.0.1: @@ -7994,12 +7649,6 @@ snapshots: pify: 4.0.1 strip-bom: 3.0.0 - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -8061,10 +7710,6 @@ snapshots: dependencies: queue-microtask: 1.2.3 - rxjs@6.6.7: - dependencies: - tslib: 1.14.1 - rxjs@7.8.1: dependencies: tslib: 2.6.3 @@ -8155,14 +7800,6 @@ snapshots: signal-exit@4.1.0: {} - simple-concat@1.0.1: {} - - simple-get@4.0.1: - dependencies: - decompress-response: 6.0.0 - once: 1.4.0 - simple-concat: 1.0.1 - sisteransi@1.0.5: {} slash@3.0.0: {} @@ -8254,10 +7891,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -8278,8 +7911,6 @@ snapshots: dependencies: min-indent: 1.0.1 - strip-json-comments@2.0.1: {} - strip-json-comments@3.1.1: {} supports-color@5.5.0: @@ -8296,21 +7927,6 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - tar-fs@2.1.1: - dependencies: - chownr: 1.1.4 - mkdirp-classic: 0.5.3 - pump: 3.0.0 - tar-stream: 2.2.0 - - tar-stream@2.2.0: - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.4 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - tar@6.2.1: dependencies: chownr: 2.0.0 @@ -8478,8 +8094,6 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - tslib@1.14.1: {} - tslib@2.6.3: {} tty-table@4.2.3: @@ -8492,10 +8106,6 @@ snapshots: wcwidth: 1.0.1 yargs: 17.7.2 - tunnel-agent@0.6.0: - dependencies: - safe-buffer: 5.2.1 - turbo-android-arm64@1.4.5: optional: true @@ -8613,8 +8223,6 @@ snapshots: typescript@5.4.5: {} - u2f-api@0.2.7: {} - unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 @@ -8642,14 +8250,6 @@ snapshots: dependencies: punycode: 2.3.1 - usb@2.9.0: - dependencies: - '@types/w3c-web-usb': 1.0.10 - node-addon-api: 6.1.0 - node-gyp-build: 4.8.1 - - util-deprecate@1.0.2: {} - uuid@8.3.2: optional: true From ff1ec958fe342c8b58fcadb50dc8912eb7e0145c Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Fri, 27 Sep 2024 18:06:25 -0700 Subject: [PATCH 47/55] feat: view stake function --- packages/client/src/interfaces/view.ts | 11 +++++++++++ packages/client/src/view.ts | 24 ++++++++++++++++++++++++ packages/types/src/provider/validator.ts | 7 +++++++ 3 files changed, 42 insertions(+) diff --git a/packages/client/src/interfaces/view.ts b/packages/client/src/interfaces/view.ts index 2d0ae43aaa..59890bbbef 100644 --- a/packages/client/src/interfaces/view.ts +++ b/packages/client/src/interfaces/view.ts @@ -13,6 +13,10 @@ export interface ViewAccountParams extends ViewBaseParams { account: string; } +export interface ViewValidatorStakeParams extends ViewAccountParams { + validator: string; +} + export interface ViewParams extends ViewAccountParams { method: string; args?: T; @@ -42,3 +46,10 @@ export interface AccessKeys { fullAccessKeys: FullAccessKey[]; functionCallAccessKeys: FunctionCallAccessKey[]; } + +export interface AccountState { + availableBalance: bigint; + codeHash: string; + locked: bigint; + storageUsed: bigint; +} diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index 1945d95c5f..5f9d09a0d4 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -6,6 +6,7 @@ import type { ContractCodeView, QueryResponseKind, SerializedReturnValue, + StakedAccount, ViewStateResult, } from '@near-js/types'; @@ -244,3 +245,26 @@ export async function getNonce({ account, publicKey, blockReference, deps }: Vie return BigInt(nonce); } + +/** + * Get the balance staked with a validator + * @param account the account staking Near with the validator + * @param validator contract with which Near is staked + * @param blockReference block ID/finality + * @param deps readonly RPC dependencies + */ +export async function getStakedBalance({ account, validator, blockReference, deps }: ViewValidatorStakeParams) { + const staked = await view({ + account: validator, + blockReference, + method: 'get_account', + args: { account_id: account }, + deps, + }); + + return { + canWithdraw: staked.can_withdraw, + stakedBalance: BigInt(staked.staked_balance), + unstakedBalance: BigInt(staked.unstaked_balance), + }; +} diff --git a/packages/types/src/provider/validator.ts b/packages/types/src/provider/validator.ts index abd2f1d55f..ec5606305b 100644 --- a/packages/types/src/provider/validator.ts +++ b/packages/types/src/provider/validator.ts @@ -43,3 +43,10 @@ export interface EpochValidatorInfo { // Epoch start height. epoch_start_height: number; } + +export interface StakedAccount { + account_id: string; + unstaked_balance: string; + staked_balance: string; + can_withdraw: boolean; +} From 71321d48ea172bdaa5e482c4070f11a3ed7ffa5e Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Fri, 27 Sep 2024 18:06:42 -0700 Subject: [PATCH 48/55] chore: cleanup unused constants --- packages/client/src/constants.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts index 94b7221d19..4c07f22cb1 100644 --- a/packages/client/src/constants.ts +++ b/packages/client/src/constants.ts @@ -1,11 +1,4 @@ -export const ACCOUNT_CREATION_DEPOSIT = BigInt(100); -export const MAINNET_RPC_URL = 'https://rpc.mainnet.near.org'; -export const TESTNET_RPC_URL = 'https://rpc.testnet.near.org'; -export const DEFAULT_FILESYSTEM_KEYSTORE_PATH = '.near-credentials'; -export const DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL = BigInt(100); - -export const LEDGER_HD_PATH = `44'/397'/0'/0'/1`; - +export const DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL = 100n; export const MAX_GAS = 300000000000000n; export const PAGODA_RPC_ENDPOINTS_MAINNET = [ From 3c655039398b83a9a939e2db4cb62b33657cfe41 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Fri, 27 Sep 2024 18:07:16 -0700 Subject: [PATCH 49/55] feat: deserialize bigint primitive return values --- packages/client/src/view.ts | 7 ++++++- packages/types/src/provider/response.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index 5f9d09a0d4..7fac886593 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -98,7 +98,12 @@ export async function view({ account, method, a try { return JSON.parse(stringResult); } catch { - return (isNaN(+stringResult) ? stringResult : +stringResult) as T; + const numeric = +stringResult; + if (isNaN(numeric)) { + return stringResult as T; + } + + return (Number.isSafeInteger(numeric) ? numeric : BigInt(numeric)) as T; } } diff --git a/packages/types/src/provider/response.ts b/packages/types/src/provider/response.ts index a9ccc155d1..7a4155cd42 100644 --- a/packages/types/src/provider/response.ts +++ b/packages/types/src/provider/response.ts @@ -4,7 +4,7 @@ */ import { BlockHash, BlockHeight, MerklePath, TxExecutionStatus } from './protocol'; -export type SerializedReturnValue = string | number | boolean | object; +export type SerializedReturnValue = string | number | bigint | boolean | object; export enum ExecutionStatusBasic { Unknown = 'Unknown', From 188e38be83fc9180604886682fc58e749c425ab2 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Fri, 27 Sep 2024 18:07:54 -0700 Subject: [PATCH 50/55] feat: clean up account state return value --- packages/client/src/view.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index 7fac886593..0d675e0d97 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -11,6 +11,7 @@ import type { } from '@near-js/types'; import type { + AccountState, FunctionCallAccessKey, FullAccessKey, RpcProviderDependency, @@ -19,6 +20,7 @@ import type { ViewAccountParams, ViewContractStateParams, ViewParams, + ViewValidatorStakeParams, } from './interfaces'; const DEFAULT_VIEW_BLOCK_REFERENCE = { finality: 'optimistic' }; @@ -147,13 +149,20 @@ export async function getAccessKey({ account, publicKey, blockReference, deps }: * @param blockReference block ID/finality * @param deps readonly RPC dependencies */ -export function getAccountState({ account, blockReference, deps }: ViewAccountParams) { - return query({ +export async function getAccountState({ account, blockReference, deps }: ViewAccountParams) { + const accountState = await query({ request: RequestType.ViewAccount, account, blockReference, deps, }); + + return { + availableBalance: BigInt(accountState.amount), + codeHash: accountState.code_hash, + locked: BigInt(accountState.locked), + storageUsed: BigInt(accountState.storage_usage), + } as AccountState; } /** From 026b72b39756526c3fff71d658c4018ec7a96e16 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Fri, 27 Sep 2024 18:09:39 -0700 Subject: [PATCH 51/55] docs: readme --- packages/client/README.md | 186 +++++++++++++++++++++++++++++++++++++- 1 file changed, 185 insertions(+), 1 deletion(-) diff --git a/packages/client/README.md b/packages/client/README.md index 6c52045f90..d8ced7dcf5 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -1,6 +1,190 @@ # @near-js/client -TODO +This package provides a simple interface for interacting with the Near blockchain. As a modern, tree-shakeable package, +it is intended to replace usage of `near-api-js`. + +### Installation +```shell +# npm +npm i -s @near-js/client + +# pnpm +pnpm add @near-js/client +``` + +### Usage + + +### Dependency Initialization +This package uses a common interface for specifying dependencies for RPC providers and cryptographic signing, allowing +a more flexible approach to composing functionality. + +#### RPC provider +RPC providers are required for any blockchain interaction, e.g. block queries, transaction publishing. + +This interface makes use of the `FallbackRpcProvider`, making it configurable with multiple RPC endpoint URLs +which can be cycled through when the current URL returns an error. + +Specifying by URL endpoints: +```ts +import { getProviderByEndpoints } from '@near-js/client'; + +const rpcProvider = getProviderByEndpoints('https://rpc.tld', 'https://fallback-rpc.tld'); +``` + +Specifying by network (uses default RPC endpoints specified in code): +```ts +import { getProviderByNetwork } from '@near-js/client'; + +const rpcProvider = getProviderByNetwork('testnet'); +``` + +Shortcut methods: +```ts +import { getTestnetRpcProvider } from '@near-js/client'; + +// equivalent to getProviderByNetwork('testnet'); +const rpcProvider = getTestnetRpcProvider(); +``` + +#### Message Signer +Implementers of the `MessageSigner` interface can be initialized from the existing `KeyStore` classes or using private keys. + +Using an existing keystore: +```ts +import { getSignerFromKeystore } from '@near-js/client'; +import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser'; + +const keystore = new BrowserLocalStorageKeyStore(); +const signer = getSignerFromKeystore('account.near', 'mainnet', keystore); +``` + + +Using a private key string: +```ts +import { getSignerFromKeystore } from '@near-js/client'; +import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser'; + +const signer = getSignerFromPrivateKey('ed25519:...'); +``` + +An access key-based signer is also available. Initialized using a `MessageSigner` implementer, this signer caches the +access key record and maintains a local, auto-incremented value for its nonce: +```ts +import { getAccessKeySigner, getMainnetRpcProvider, getSignerFromPrivateKey } from '@near-js/client'; +import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser'; + +const keystore = new BrowserLocalStorageKeyStore(); +const signer = getSignerFromKeystore('account.near', 'mainnet', keystore); +const accessKeySigner = getAccessKeySigner({ + accout: 'account.near', + deps: { + signer, + rpcProvider: getMainnetRpcProvider(), + }, +}); +``` + +### View Methods +Several functions are provided for working with builtin account methods and smart contract view methods. + +Executing a view method on a smart contract and return the entire response: +```ts +import { callViewMethod, getTestnetRpcProvider } from '@near-js/client'; + +const response = await callViewMethod({ + account: 'guest-book.testnet', + method: 'getMessages', + deps: { rpcProvider: getTestnetRpcProvider() }, +}); +``` + +Shorthand function to call the method and return only the parsed value: +```ts +import { getTestnetRpcProvider, view } from '@near-js/client'; + +interface GuestBookMessage { + premium: boolean; + sender: string; + text: string; +} + +// parse the returned buffer and parse as JSON +const data = await view({ + account: 'guest-book.testnet', + method: 'getMessages', + deps: { rpcProvider: getTestnetRpcProvider() }, +}); +``` + +Client method for requesting access keys: +```ts +import { getAccessKeys, getTestnetRpcProvider } from '@near-js/client'; + +const { fullAccessKeys, functionCallAccessKeys } = await getAccessKeys({ + account: 'account.testnet', + deps: { rpcProvider: getTestnetRpcProvider() }, +}); +``` + +### Transactions +New `*Composer` classes facilitate transaction building and signing: +```ts +import { + getSignerFromKeystore, + getTestnetRpcProvider, + SignedTransactionComposer, +} from '@near-js/client'; +import { KeyPairEd25519 } from '@near-js/keystores-browser'; +import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser'; + +const keystore = new BrowserLocalStorageKeyStore(); +const signer = getSignerFromKeystore('account.testnet', 'testnet', keystore); + +const oldPublicKey = await signer.getPublicKey(); +const newKeyPair = KeyPairEd25519.fromRandom(); + +const composer = SignedTransactionComposer.init({ + sender: 'account.testnet', + receiver: 'receiver.testnet', + deps: { + signer, + rpcProvider: getMainnetRpcProvider(), + } +}); + +// add new key and remove old key in single transaction +await composer + .addFullAccessKey(newKeyPair.publicKey) + .deleteKey(oldPublicKey.toString()) + .signAndSend(); + +keystore.setKey('testnet', 'account.testnet', newKeyPair); +``` + +For convenience, there are also functions wrapping individual actions: +```ts +import { + getSignerFromKeystore, + getTestnetRpcProvider, + transfer, +} from '@near-js/client'; +import { KeyPairEd25519 } from '@near-js/keystores-browser'; +import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser'; + +const keystore = new BrowserLocalStorageKeyStore(); +const signer = getSignerFromKeystore('account.testnet', 'testnet', keystore); + +await transfer({ + sender: 'account.testnet', + receiver: 'receiver.testnet', + amount: 1000n, // in yoctoNear + deps: { + rpcProvider: getTestnetRpcProvider(), + signer, + } +}); +``` # License From f9eb78989297874c858ab49ec1813509664a1641 Mon Sep 17 00:00:00 2001 From: Michael Peter Date: Mon, 7 Oct 2024 11:39:34 -0400 Subject: [PATCH 52/55] fix: add missing StakedAccount export to @near-js/types --- packages/types/src/provider/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/types/src/provider/index.ts b/packages/types/src/provider/index.ts index 4b78aafea3..940352a350 100644 --- a/packages/types/src/provider/index.ts +++ b/packages/types/src/provider/index.ts @@ -74,4 +74,5 @@ export { EpochValidatorInfo, NextEpochValidatorInfo, ValidatorStakeView, + StakedAccount } from './validator'; From 6be05ccc50898b6cf09e9d58e3aaf0aa350cb248 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Mon, 7 Oct 2024 09:39:15 -0700 Subject: [PATCH 53/55] fix: missing cast --- packages/client/src/view.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/view.ts b/packages/client/src/view.ts index 0d675e0d97..9f7ad6cf15 100644 --- a/packages/client/src/view.ts +++ b/packages/client/src/view.ts @@ -129,7 +129,7 @@ export async function getAccessKey({ account, publicKey, blockReference, deps }: if (permission === 'FullAccess') { return { - nonce, + nonce: BigInt(nonce), publicKey, } as FullAccessKey; } @@ -138,7 +138,7 @@ export async function getAccessKey({ account, publicKey, blockReference, deps }: allowance: BigInt(allowance), contract: receiver_id, methods: method_names, - nonce, + nonce: BigInt(nonce), publicKey, } as FunctionCallAccessKey; } From 0f35ac6a8dd6b7dff2c6a08a26c7c061b66a0e13 Mon Sep 17 00:00:00 2001 From: Andy Haynes Date: Mon, 7 Oct 2024 09:40:31 -0700 Subject: [PATCH 54/55] fix: lil fixes --- packages/client/README.md | 6 +++--- packages/types/src/provider/index.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/client/README.md b/packages/client/README.md index d8ced7dcf5..d5bf76d821 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -135,7 +135,7 @@ import { getTestnetRpcProvider, SignedTransactionComposer, } from '@near-js/client'; -import { KeyPairEd25519 } from '@near-js/keystores-browser'; +import { KeyPairEd25519 } from '@near-js/crypto'; import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser'; const keystore = new BrowserLocalStorageKeyStore(); @@ -162,14 +162,14 @@ await composer keystore.setKey('testnet', 'account.testnet', newKeyPair); ``` -For convenience, there are also functions wrapping individual actions: +For convenience, there are also functions wrapping individual actions, e.g. `transfer`: ```ts import { getSignerFromKeystore, getTestnetRpcProvider, transfer, } from '@near-js/client'; -import { KeyPairEd25519 } from '@near-js/keystores-browser'; +import { KeyPairEd25519 } from '@near-js/crypto'; import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser'; const keystore = new BrowserLocalStorageKeyStore(); diff --git a/packages/types/src/provider/index.ts b/packages/types/src/provider/index.ts index 940352a350..3a34e8cad0 100644 --- a/packages/types/src/provider/index.ts +++ b/packages/types/src/provider/index.ts @@ -73,6 +73,7 @@ export { CurrentEpochValidatorInfo, EpochValidatorInfo, NextEpochValidatorInfo, + StakedAccount, ValidatorStakeView, StakedAccount } from './validator'; From 773f50d49f2e4dba467c00ba150ef87e33b3a967 Mon Sep 17 00:00:00 2001 From: Michael Peter Date: Mon, 7 Oct 2024 13:32:53 -0400 Subject: [PATCH 55/55] fix: remove duplicated StakedAccount export --- packages/types/src/provider/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/types/src/provider/index.ts b/packages/types/src/provider/index.ts index 3a34e8cad0..eac273dea6 100644 --- a/packages/types/src/provider/index.ts +++ b/packages/types/src/provider/index.ts @@ -74,6 +74,5 @@ export { EpochValidatorInfo, NextEpochValidatorInfo, StakedAccount, - ValidatorStakeView, - StakedAccount + ValidatorStakeView } from './validator';