Skip to content

Commit 4661484

Browse files
authored
Merge pull request #1372 from near/feat/client
feat: client
2 parents bc78d21 + 773f50d commit 4661484

File tree

84 files changed

+2574
-740
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+2574
-740
lines changed

packages/client/.eslintrc.yml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
env:
2+
es6: true
3+
node: true
4+
extends:
5+
- 'eslint:recommended'
6+
- 'plugin:@typescript-eslint/eslint-recommended'
7+
- 'plugin:@typescript-eslint/recommended'
8+
parser: '@typescript-eslint/parser'
9+
rules:
10+
no-inner-declarations: off
11+
indent:
12+
- error
13+
- 2
14+
- SwitchCase: 1
15+
'@typescript-eslint/no-explicit-any': off
16+
17+
parserOptions:
18+
ecmaVersion: 2018
19+
sourceType: module

packages/client/README.md

+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# @near-js/client
2+
3+
This package provides a simple interface for interacting with the Near blockchain. As a modern, tree-shakeable package,
4+
it is intended to replace usage of `near-api-js`.
5+
6+
### Installation
7+
```shell
8+
# npm
9+
npm i -s @near-js/client
10+
11+
# pnpm
12+
pnpm add @near-js/client
13+
```
14+
15+
### Usage
16+
17+
18+
### Dependency Initialization
19+
This package uses a common interface for specifying dependencies for RPC providers and cryptographic signing, allowing
20+
a more flexible approach to composing functionality.
21+
22+
#### RPC provider
23+
RPC providers are required for any blockchain interaction, e.g. block queries, transaction publishing.
24+
25+
This interface makes use of the `FallbackRpcProvider`, making it configurable with multiple RPC endpoint URLs
26+
which can be cycled through when the current URL returns an error.
27+
28+
Specifying by URL endpoints:
29+
```ts
30+
import { getProviderByEndpoints } from '@near-js/client';
31+
32+
const rpcProvider = getProviderByEndpoints('https://rpc.tld', 'https://fallback-rpc.tld');
33+
```
34+
35+
Specifying by network (uses default RPC endpoints specified in code):
36+
```ts
37+
import { getProviderByNetwork } from '@near-js/client';
38+
39+
const rpcProvider = getProviderByNetwork('testnet');
40+
```
41+
42+
Shortcut methods:
43+
```ts
44+
import { getTestnetRpcProvider } from '@near-js/client';
45+
46+
// equivalent to getProviderByNetwork('testnet');
47+
const rpcProvider = getTestnetRpcProvider();
48+
```
49+
50+
#### Message Signer
51+
Implementers of the `MessageSigner` interface can be initialized from the existing `KeyStore` classes or using private keys.
52+
53+
Using an existing keystore:
54+
```ts
55+
import { getSignerFromKeystore } from '@near-js/client';
56+
import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser';
57+
58+
const keystore = new BrowserLocalStorageKeyStore();
59+
const signer = getSignerFromKeystore('account.near', 'mainnet', keystore);
60+
```
61+
62+
63+
Using a private key string:
64+
```ts
65+
import { getSignerFromKeystore } from '@near-js/client';
66+
import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser';
67+
68+
const signer = getSignerFromPrivateKey('ed25519:...');
69+
```
70+
71+
An access key-based signer is also available. Initialized using a `MessageSigner` implementer, this signer caches the
72+
access key record and maintains a local, auto-incremented value for its nonce:
73+
```ts
74+
import { getAccessKeySigner, getMainnetRpcProvider, getSignerFromPrivateKey } from '@near-js/client';
75+
import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser';
76+
77+
const keystore = new BrowserLocalStorageKeyStore();
78+
const signer = getSignerFromKeystore('account.near', 'mainnet', keystore);
79+
const accessKeySigner = getAccessKeySigner({
80+
accout: 'account.near',
81+
deps: {
82+
signer,
83+
rpcProvider: getMainnetRpcProvider(),
84+
},
85+
});
86+
```
87+
88+
### View Methods
89+
Several functions are provided for working with builtin account methods and smart contract view methods.
90+
91+
Executing a view method on a smart contract and return the entire response:
92+
```ts
93+
import { callViewMethod, getTestnetRpcProvider } from '@near-js/client';
94+
95+
const response = await callViewMethod({
96+
account: 'guest-book.testnet',
97+
method: 'getMessages',
98+
deps: { rpcProvider: getTestnetRpcProvider() },
99+
});
100+
```
101+
102+
Shorthand function to call the method and return only the parsed value:
103+
```ts
104+
import { getTestnetRpcProvider, view } from '@near-js/client';
105+
106+
interface GuestBookMessage {
107+
premium: boolean;
108+
sender: string;
109+
text: string;
110+
}
111+
112+
// parse the returned buffer and parse as JSON
113+
const data = await view<GuestBookMessage[]>({
114+
account: 'guest-book.testnet',
115+
method: 'getMessages',
116+
deps: { rpcProvider: getTestnetRpcProvider() },
117+
});
118+
```
119+
120+
Client method for requesting access keys:
121+
```ts
122+
import { getAccessKeys, getTestnetRpcProvider } from '@near-js/client';
123+
124+
const { fullAccessKeys, functionCallAccessKeys } = await getAccessKeys({
125+
account: 'account.testnet',
126+
deps: { rpcProvider: getTestnetRpcProvider() },
127+
});
128+
```
129+
130+
### Transactions
131+
New `*Composer` classes facilitate transaction building and signing:
132+
```ts
133+
import {
134+
getSignerFromKeystore,
135+
getTestnetRpcProvider,
136+
SignedTransactionComposer,
137+
} from '@near-js/client';
138+
import { KeyPairEd25519 } from '@near-js/crypto';
139+
import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser';
140+
141+
const keystore = new BrowserLocalStorageKeyStore();
142+
const signer = getSignerFromKeystore('account.testnet', 'testnet', keystore);
143+
144+
const oldPublicKey = await signer.getPublicKey();
145+
const newKeyPair = KeyPairEd25519.fromRandom();
146+
147+
const composer = SignedTransactionComposer.init({
148+
sender: 'account.testnet',
149+
receiver: 'receiver.testnet',
150+
deps: {
151+
signer,
152+
rpcProvider: getMainnetRpcProvider(),
153+
}
154+
});
155+
156+
// add new key and remove old key in single transaction
157+
await composer
158+
.addFullAccessKey(newKeyPair.publicKey)
159+
.deleteKey(oldPublicKey.toString())
160+
.signAndSend();
161+
162+
keystore.setKey('testnet', 'account.testnet', newKeyPair);
163+
```
164+
165+
For convenience, there are also functions wrapping individual actions, e.g. `transfer`:
166+
```ts
167+
import {
168+
getSignerFromKeystore,
169+
getTestnetRpcProvider,
170+
transfer,
171+
} from '@near-js/client';
172+
import { KeyPairEd25519 } from '@near-js/crypto';
173+
import { BrowserLocalStorageKeyStore } from '@near-js/keystores-browser';
174+
175+
const keystore = new BrowserLocalStorageKeyStore();
176+
const signer = getSignerFromKeystore('account.testnet', 'testnet', keystore);
177+
178+
await transfer({
179+
sender: 'account.testnet',
180+
receiver: 'receiver.testnet',
181+
amount: 1000n, // in yoctoNear
182+
deps: {
183+
rpcProvider: getTestnetRpcProvider(),
184+
signer,
185+
}
186+
});
187+
```
188+
189+
# License
190+
191+
This repository is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
192+
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.

packages/client/jest.config.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export default {
2+
preset: 'ts-jest',
3+
collectCoverage: true,
4+
testEnvironment: 'node',
5+
testRegex: "(/tests/.*|(\\.|/)(test|spec))\\.[jt]sx?$",
6+
transform: {
7+
'^.+\\.[tj]s$': ['ts-jest', {
8+
tsconfig: {
9+
allowJs: true,
10+
},
11+
}],
12+
},
13+
};

packages/client/package.json

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "@near-js/client",
3+
"version": "0.0.1",
4+
"description": "",
5+
"main": "lib/esm/index.js",
6+
"type": "module",
7+
"scripts": {
8+
"build": "pnpm compile:esm && pnpm compile:cjs",
9+
"compile:esm": "tsc -p tsconfig.json",
10+
"compile:cjs": "tsc -p tsconfig.cjs.json && cjsify ./lib/commonjs",
11+
"lint": "eslint -c .eslintrc.yml src/**/*.ts --no-eslintrc --no-error-on-unmatched-pattern",
12+
"lint:fix": "eslint -c .eslintrc.yml src/**/*.ts --no-eslintrc --no-error-on-unmatched-pattern --fix"
13+
},
14+
"dependencies": {
15+
"@near-js/crypto": "workspace:*",
16+
"@near-js/keystores": "workspace:*",
17+
"@near-js/providers": "workspace:*",
18+
"@near-js/signers": "workspace:*",
19+
"@near-js/transactions": "workspace:*",
20+
"@near-js/types": "workspace:*",
21+
"@near-js/utils": "workspace:*",
22+
"@noble/hashes": "1.3.3",
23+
"isomorphic-fetch": "3.0.0"
24+
},
25+
"keywords": [],
26+
"author": "",
27+
"license": "ISC",
28+
"devDependencies": {
29+
"@types/node": "20.0.0",
30+
"build": "workspace:*",
31+
"tsconfig": "workspace:*",
32+
"typescript": "5.4.5"
33+
},
34+
"files": [
35+
"lib"
36+
],
37+
"exports": {
38+
"require": "./lib/commonjs/index.cjs",
39+
"import": "./lib/esm/index.js"
40+
}
41+
}

packages/client/src/constants.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export const DEFAULT_META_TRANSACTION_BLOCK_HEIGHT_TTL = 100n;
2+
export const MAX_GAS = 300000000000000n;
3+
4+
export const PAGODA_RPC_ENDPOINTS_MAINNET = [
5+
'https://rpc.near.org',
6+
'https://rpc.mainnet.pagoda.co',
7+
];
8+
9+
export const PAGODA_RPC_ARCHIVAL_ENDPOINTS_MAINNET = [
10+
'https://archival-rpc.near.org',
11+
];
12+
13+
export const PAGODA_RPC_ENDPOINTS_TESTNET = [
14+
'https://rpc.testnet.near.org',
15+
'https://rpc.testnet.pagoda.co',
16+
];
17+
18+
export const PAGODA_RPC_ARCHIVAL_ENDPOINTS_TESTNET = [
19+
'https://archival-rpc.testnet.near.org',
20+
];
21+
22+
export const KITWALLET_FUNDED_TESTNET_ACCOUNT_ENDPOINT = 'https://testnet-api.kitwallet.app/account';

packages/client/src/crypto.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { CurveType, KeyPair, KeyPairString } from '@near-js/crypto';
2+
3+
/**
4+
* Generate a random key pair for the specified elliptic curve
5+
* @param curve elliptic curve (e.g. `ed25519`)
6+
*/
7+
export function generateRandomKeyPair(curve: CurveType) {
8+
return KeyPair.fromRandom(curve);
9+
}
10+
11+
/**
12+
* Parse a signing key pair from a private key string
13+
* @param privateKey private key string
14+
*/
15+
export function parseKeyPair(privateKey: KeyPairString) {
16+
return KeyPair.fromString(privateKey);
17+
}

packages/client/src/funded_account.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type { FinalExecutionOutcome } from '@near-js/types';
2+
import fetch from 'isomorphic-fetch';
3+
4+
import { KITWALLET_FUNDED_TESTNET_ACCOUNT_ENDPOINT } from './constants';
5+
import { NewAccountParams } from './interfaces';
6+
7+
interface CreateFundedTestnetAccountParams extends NewAccountParams {
8+
endpointUrl?: string;
9+
}
10+
11+
/**
12+
* Create a new funded testnet account via faucet REST endpoint
13+
* (e.g. create `new.testnet` with a preset amount of Near)
14+
* @param endpointUrl REST endpoint for the funded testnet account creation (defaults to the current Near Contract Helper endpoint)
15+
* @param newAccount name of the created account
16+
* @param newPublicKey public key for the created account's initial full access key
17+
*/
18+
export async function createFundedTestnetAccount({
19+
newAccount,
20+
newPublicKey,
21+
endpointUrl = KITWALLET_FUNDED_TESTNET_ACCOUNT_ENDPOINT,
22+
}: CreateFundedTestnetAccountParams) {
23+
const res = await fetch(endpointUrl, {
24+
method: 'POST',
25+
body: JSON.stringify({
26+
newAccountId: newAccount,
27+
newAccountPublicKey: newPublicKey,
28+
}),
29+
headers: { 'Content-Type': 'application/json' },
30+
});
31+
32+
const { ok, status } = res;
33+
if (!ok) {
34+
throw new Error(`Failed to create account on ${endpointUrl}: ${status}`);
35+
}
36+
37+
return await res.json() as FinalExecutionOutcome;
38+
}

packages/client/src/index.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export { formatNearAmount } from '@near-js/utils';
2+
3+
export * from './constants';
4+
export * from './crypto';
5+
export * from './funded_account';
6+
export * from './interfaces';
7+
export * from './providers';
8+
export * from './signing';
9+
export * from './transactions';
10+
export * from './view';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { PublicKey } from '@near-js/crypto';
2+
3+
import type { RpcQueryProvider } from './providers';
4+
import { FullAccessKey, FunctionCallAccessKey } from './view';
5+
6+
interface Dependent<T> {
7+
deps: T;
8+
}
9+
10+
export interface MessageSigner {
11+
getPublicKey(): Promise<PublicKey>;
12+
signMessage(m: Uint8Array): Promise<Uint8Array>;
13+
}
14+
15+
export interface AccessKeySigner extends MessageSigner {
16+
getAccessKey(ignoreCache?: boolean): Promise<FullAccessKey | FunctionCallAccessKey>;
17+
getNonce(ignoreCache?: boolean): Promise<bigint>;
18+
getSigningAccount(): string;
19+
}
20+
21+
interface RpcProviderDependent {
22+
rpcProvider: RpcQueryProvider;
23+
}
24+
25+
interface SignerDependent {
26+
signer: MessageSigner;
27+
}
28+
29+
export interface RpcProviderDependency extends Dependent<RpcProviderDependent> {}
30+
31+
export interface SignerDependency extends Dependent<SignerDependent> {}
32+
33+
export interface SignAndSendTransactionDependency extends Dependent<RpcProviderDependent & SignerDependent> {}
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './dependencies';
2+
export * from './providers';
3+
export * from './transactions';
4+
export * from './view';

0 commit comments

Comments
 (0)