Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: client #1372

Merged
merged 55 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
9ff900c
feat: client lib init
Aug 9, 2024
f83ea5d
refactor: organize
Aug 9, 2024
22fcef7
test: test/lint commands
Aug 28, 2024
9c74649
feat: add curve type, reorganize types
Aug 29, 2024
d6b6955
fix: contract methods
Aug 29, 2024
6a91707
docs: jsdocs
Aug 29, 2024
524d6dd
feat: delete access key method
Aug 29, 2024
26d7a17
feat: remove nonce as parameter
Aug 29, 2024
0934ae7
feat: parse public key strings
Sep 3, 2024
62e3485
feat: deployContract and deleteAccount methods
Sep 3, 2024
c0fcd34
chore: fix linting
Sep 4, 2024
4305783
feat: staking
Sep 5, 2024
3b9e521
feat: return entire response from single action calls
Sep 5, 2024
7b8a290
feat: public key utils
Sep 5, 2024
3646aa3
feat: create account
Sep 6, 2024
0edf382
feat: funded testnet account creator
Sep 6, 2024
36e8b5d
fix: fetch dependency
Sep 11, 2024
6402f7d
feat: group signing/keystore modules under dedicated path
Sep 11, 2024
88f5c92
refactor: consolidate imports
Sep 11, 2024
dde6f86
feat: fixes, interface updates
Sep 12, 2024
27271b7
feat: update cookbook examples
Sep 12, 2024
97bae8e
chore: fix cookbook lint scripts
Sep 12, 2024
4c310a0
fix: simplify scripts
Sep 12, 2024
843de04
feat: more cookbook examples, docs
Sep 12, 2024
53c2d27
feat: provider options and missing types
Sep 14, 2024
c230e9b
feat: cookbook updates
Sep 14, 2024
d326e50
feat: add SignedTransactionComposer
Sep 17, 2024
503de05
feat: meta transaction support
Sep 17, 2024
0d87a82
chore: useless wrapper
Sep 17, 2024
9ee9835
feat: more cookbook updates
Sep 17, 2024
295df17
feat: ledger
Sep 18, 2024
951b46a
feat: toSignedTransaction method
Sep 18, 2024
2fa73c1
chore: update cookbook dependencies
Sep 18, 2024
32d17ae
feat: optional public key cache for ledger
Sep 19, 2024
85cb910
feat: transaction validation
Sep 19, 2024
3dc94de
feat: composer refactor, better meta transaction support
Sep 19, 2024
503bf19
chore: typed responses
Sep 19, 2024
93e6a31
chore: type refactor
Sep 19, 2024
5db9b63
fix: default nonce lookup finality
Sep 23, 2024
7a701da
feat: cached access key lookup
Sep 23, 2024
3abe110
feat: lazy signer initialization
Sep 23, 2024
84e1cb9
feat: remove node keystores dependency
Sep 25, 2024
7933fbe
feat: move keystore functionality to cookbook
Sep 25, 2024
aae2265
chore: lint config
Sep 25, 2024
e0751ae
feat: simpler types for access key
Sep 25, 2024
fc2cbd7
chore: remove ledger functionality
Sep 25, 2024
ff1ec95
feat: view stake function
Sep 28, 2024
71321d4
chore: cleanup unused constants
Sep 28, 2024
3c65503
feat: deserialize bigint primitive return values
Sep 28, 2024
188e38b
feat: clean up account state return value
Sep 28, 2024
026b72b
docs: readme
Sep 28, 2024
f9eb789
fix: add missing StakedAccount export to @near-js/types
mpeterdev Oct 7, 2024
6be05cc
fix: missing cast
Oct 7, 2024
0f35ac6
fix: lil fixes
Oct 7, 2024
773f50d
fix: remove duplicated StakedAccount export
mpeterdev Oct 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/client/.eslintrc.yml
Original file line number Diff line number Diff line change
@@ -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
192 changes: 192 additions & 0 deletions packages/client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# @near-js/client

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<GuestBookMessage[]>({
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/crypto';
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, e.g. `transfer`:
```ts
import {
getSignerFromKeystore,
getTestnetRpcProvider,
transfer,
} from '@near-js/client';
import { KeyPairEd25519 } from '@near-js/crypto';
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

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.
13 changes: 13 additions & 0 deletions packages/client/jest.config.ts
Original file line number Diff line number Diff line change
@@ -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,
},
}],
},
};
41 changes: 41 additions & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"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.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:*",
"@near-js/keystores": "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",
"isomorphic-fetch": "3.0.0"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/node": "20.0.0",
"build": "workspace:*",
"tsconfig": "workspace:*",
"typescript": "5.4.5"
},
"files": [
"lib"
],
"exports": {
"require": "./lib/commonjs/index.cjs",
"import": "./lib/esm/index.js"
}
}
22 changes: 22 additions & 0 deletions packages/client/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL = 100n;
export const MAX_GAS = 300000000000000n;

export const PAGODA_RPC_ENDPOINTS_MAINNET = [
'https://rpc.near.org',
'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';
17 changes: 17 additions & 0 deletions packages/client/src/crypto.ts
Original file line number Diff line number Diff line change
@@ -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);
}
38 changes: 38 additions & 0 deletions packages/client/src/funded_account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { FinalExecutionOutcome } from '@near-js/types';
import fetch from 'isomorphic-fetch';

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 fetch(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;
}
10 changes: 10 additions & 0 deletions packages/client/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export { formatNearAmount } from '@near-js/utils';

export * from './constants';
export * from './crypto';
export * from './funded_account';
export * from './interfaces';
export * from './providers';
export * from './signing';
export * from './transactions';
export * from './view';
33 changes: 33 additions & 0 deletions packages/client/src/interfaces/dependencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { PublicKey } from '@near-js/crypto';

import type { RpcQueryProvider } from './providers';
import { FullAccessKey, FunctionCallAccessKey } from './view';

interface Dependent<T> {
deps: T;
}

export interface MessageSigner {
getPublicKey(): Promise<PublicKey>;
signMessage(m: Uint8Array): Promise<Uint8Array>;
}

export interface AccessKeySigner extends MessageSigner {
getAccessKey(ignoreCache?: boolean): Promise<FullAccessKey | FunctionCallAccessKey>;
getNonce(ignoreCache?: boolean): Promise<bigint>;
getSigningAccount(): string;
}

interface RpcProviderDependent {
rpcProvider: RpcQueryProvider;
}

interface SignerDependent {
signer: MessageSigner;
}

export interface RpcProviderDependency extends Dependent<RpcProviderDependent> {}

export interface SignerDependency extends Dependent<SignerDependent> {}

export interface SignAndSendTransactionDependency extends Dependent<RpcProviderDependent & SignerDependent> {}
4 changes: 4 additions & 0 deletions packages/client/src/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './dependencies';
export * from './providers';
export * from './transactions';
export * from './view';
Loading
Loading