near-kit 0.0.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +473 -2
- package/dist/contracts/contract.d.ts +63 -0
- package/dist/contracts/contract.d.ts.map +1 -0
- package/dist/contracts/contract.js +42 -0
- package/dist/contracts/contract.js.map +1 -0
- package/dist/contracts/index.d.ts +5 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +5 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/core/actions.d.ts +193 -0
- package/dist/core/actions.d.ts.map +1 -0
- package/dist/core/actions.js +195 -0
- package/dist/core/actions.js.map +1 -0
- package/dist/core/config-schemas.d.ts +179 -0
- package/dist/core/config-schemas.d.ts.map +1 -0
- package/dist/core/config-schemas.js +169 -0
- package/dist/core/config-schemas.js.map +1 -0
- package/dist/core/constants.d.ts +43 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/constants.js +49 -0
- package/dist/core/constants.js.map +1 -0
- package/dist/core/near.d.ts +301 -0
- package/dist/core/near.d.ts.map +1 -0
- package/dist/core/near.js +504 -0
- package/dist/core/near.js.map +1 -0
- package/dist/core/nonce-manager.d.ts +39 -0
- package/dist/core/nonce-manager.d.ts.map +1 -0
- package/dist/core/nonce-manager.js +73 -0
- package/dist/core/nonce-manager.js.map +1 -0
- package/dist/core/rpc/rpc-error-handler.d.ts +60 -0
- package/dist/core/rpc/rpc-error-handler.d.ts.map +1 -0
- package/dist/core/rpc/rpc-error-handler.js +324 -0
- package/dist/core/rpc/rpc-error-handler.js.map +1 -0
- package/dist/core/rpc/rpc-schemas.d.ts +1812 -0
- package/dist/core/rpc/rpc-schemas.d.ts.map +1 -0
- package/dist/core/rpc/rpc-schemas.js +424 -0
- package/dist/core/rpc/rpc-schemas.js.map +1 -0
- package/dist/core/rpc/rpc.d.ts +117 -0
- package/dist/core/rpc/rpc.d.ts.map +1 -0
- package/dist/core/rpc/rpc.js +325 -0
- package/dist/core/rpc/rpc.js.map +1 -0
- package/dist/core/schema.d.ts +1188 -0
- package/dist/core/schema.d.ts.map +1 -0
- package/dist/core/schema.js +396 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/transaction.d.ts +390 -0
- package/dist/core/transaction.d.ts.map +1 -0
- package/dist/core/transaction.js +653 -0
- package/dist/core/transaction.js.map +1 -0
- package/dist/core/types.d.ts +271 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +9 -0
- package/dist/core/types.js.map +1 -0
- package/dist/errors/index.d.ts +226 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +366 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/keys/credential-schemas.d.ts +98 -0
- package/dist/keys/credential-schemas.d.ts.map +1 -0
- package/dist/keys/credential-schemas.js +128 -0
- package/dist/keys/credential-schemas.js.map +1 -0
- package/dist/keys/file-keystore.d.ts +130 -0
- package/dist/keys/file-keystore.d.ts.map +1 -0
- package/dist/keys/file-keystore.js +266 -0
- package/dist/keys/file-keystore.js.map +1 -0
- package/dist/keys/in-memory-keystore.d.ts +71 -0
- package/dist/keys/in-memory-keystore.d.ts.map +1 -0
- package/dist/keys/in-memory-keystore.js +85 -0
- package/dist/keys/in-memory-keystore.js.map +1 -0
- package/dist/keys/index.d.ts +14 -0
- package/dist/keys/index.d.ts.map +1 -0
- package/dist/keys/index.js +20 -0
- package/dist/keys/index.js.map +1 -0
- package/dist/keys/native-keystore.d.ts +111 -0
- package/dist/keys/native-keystore.d.ts.map +1 -0
- package/dist/keys/native-keystore.js +167 -0
- package/dist/keys/native-keystore.js.map +1 -0
- package/dist/keys/rotating-keystore.d.ts +207 -0
- package/dist/keys/rotating-keystore.d.ts.map +1 -0
- package/dist/keys/rotating-keystore.js +240 -0
- package/dist/keys/rotating-keystore.js.map +1 -0
- package/dist/sandbox/index.d.ts +6 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/sandbox/index.js +5 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sandbox/sandbox.d.ts +55 -0
- package/dist/sandbox/sandbox.d.ts.map +1 -0
- package/dist/sandbox/sandbox.js +341 -0
- package/dist/sandbox/sandbox.js.map +1 -0
- package/dist/utils/amount.d.ts +76 -0
- package/dist/utils/amount.d.ts.map +1 -0
- package/dist/utils/amount.js +137 -0
- package/dist/utils/amount.js.map +1 -0
- package/dist/utils/gas.d.ts +69 -0
- package/dist/utils/gas.d.ts.map +1 -0
- package/dist/utils/gas.js +92 -0
- package/dist/utils/gas.js.map +1 -0
- package/dist/utils/index.d.ts +14 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +14 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/key.d.ts +117 -0
- package/dist/utils/key.d.ts.map +1 -0
- package/dist/utils/key.js +270 -0
- package/dist/utils/key.js.map +1 -0
- package/dist/utils/nep413.d.ts +97 -0
- package/dist/utils/nep413.d.ts.map +1 -0
- package/dist/utils/nep413.js +154 -0
- package/dist/utils/nep413.js.map +1 -0
- package/dist/utils/validation.d.ts +114 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +150 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/wallets/adapters.d.ts +119 -0
- package/dist/wallets/adapters.d.ts.map +1 -0
- package/dist/wallets/adapters.js +267 -0
- package/dist/wallets/adapters.js.map +1 -0
- package/dist/wallets/index.d.ts +11 -0
- package/dist/wallets/index.d.ts.map +1 -0
- package/dist/wallets/index.js +2 -0
- package/dist/wallets/index.js.map +1 -0
- package/dist/wallets/types.d.ts +99 -0
- package/dist/wallets/types.d.ts.map +1 -0
- package/dist/wallets/types.js +10 -0
- package/dist/wallets/types.js.map +1 -0
- package/package.json +78 -7
- package/index.js +0 -1
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { parseKey } from "../utils/key.js";
|
|
2
|
+
/**
|
|
3
|
+
* Rotating key store that cycles through multiple keys per account.
|
|
4
|
+
*
|
|
5
|
+
* This keystore enables high-throughput concurrent transactions by rotating
|
|
6
|
+
* through multiple access keys for a single account. Each transaction uses
|
|
7
|
+
* a different key in round-robin fashion, eliminating nonce collisions.
|
|
8
|
+
*
|
|
9
|
+
* ## Use Cases
|
|
10
|
+
* - **High-throughput applications**: Send many concurrent transactions without nonce collisions
|
|
11
|
+
* - **Load balancing**: Distribute transaction load across multiple access keys
|
|
12
|
+
* - **Key rotation**: Seamlessly rotate keys without downtime
|
|
13
|
+
*
|
|
14
|
+
* ## How It Works
|
|
15
|
+
* - Each account can have multiple keys registered
|
|
16
|
+
* - `get()` returns the next key in round-robin order
|
|
17
|
+
* - Each key has independent nonce tracking via NonceManager
|
|
18
|
+
* - No nonce collisions between concurrent transactions
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* // Create keystore with multiple keys for one account
|
|
23
|
+
* const keyStore = new RotatingKeyStore()
|
|
24
|
+
* await keyStore.add("alice.near", parseKey("ed25519:key1..."))
|
|
25
|
+
* await keyStore.add("alice.near", parseKey("ed25519:key2..."))
|
|
26
|
+
* await keyStore.add("alice.near", parseKey("ed25519:key3..."))
|
|
27
|
+
*
|
|
28
|
+
* const near = new Near({ network: "testnet", keyStore })
|
|
29
|
+
*
|
|
30
|
+
* // Send 100 concurrent transactions - no nonce collisions!
|
|
31
|
+
* await Promise.all(
|
|
32
|
+
* Array(100).fill(0).map(() =>
|
|
33
|
+
* near.transaction("alice.near")
|
|
34
|
+
* .transfer("bob.near", "0.1")
|
|
35
|
+
* .send()
|
|
36
|
+
* )
|
|
37
|
+
* )
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // Initialize with keys
|
|
43
|
+
* const keyStore = new RotatingKeyStore({
|
|
44
|
+
* "alice.near": [
|
|
45
|
+
* "ed25519:key1...",
|
|
46
|
+
* "ed25519:key2...",
|
|
47
|
+
* "ed25519:key3..."
|
|
48
|
+
* ]
|
|
49
|
+
* })
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* // Query rotation state
|
|
55
|
+
* const keys = await keyStore.getAll("alice.near")
|
|
56
|
+
* console.log(`Account has ${keys.length} keys`)
|
|
57
|
+
*
|
|
58
|
+
* const index = keyStore.getCurrentIndex("alice.near")
|
|
59
|
+
* console.log(`Currently at key index ${index}`)
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export class RotatingKeyStore {
|
|
63
|
+
/**
|
|
64
|
+
* Create a new rotating keystore.
|
|
65
|
+
*
|
|
66
|
+
* @param initialKeys - Optional initial keys to populate the store.
|
|
67
|
+
* Maps account IDs to arrays of private key strings.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const keyStore = new RotatingKeyStore({
|
|
72
|
+
* "alice.near": ["ed25519:key1...", "ed25519:key2..."],
|
|
73
|
+
* "bob.near": ["ed25519:key3..."]
|
|
74
|
+
* })
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
constructor(initialKeys) {
|
|
78
|
+
this.keys = new Map();
|
|
79
|
+
this.counters = new Map();
|
|
80
|
+
if (initialKeys) {
|
|
81
|
+
for (const [accountId, keyStrings] of Object.entries(initialKeys)) {
|
|
82
|
+
for (const keyString of keyStrings) {
|
|
83
|
+
const keyPair = parseKey(keyString);
|
|
84
|
+
const existing = this.keys.get(accountId) ?? [];
|
|
85
|
+
existing.push(keyPair);
|
|
86
|
+
this.keys.set(accountId, existing);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get the next key for an account using round-robin rotation.
|
|
93
|
+
*
|
|
94
|
+
* Each call to `get()` advances to the next key in the rotation.
|
|
95
|
+
* This is the core mechanism that enables concurrent transactions
|
|
96
|
+
* without nonce collisions.
|
|
97
|
+
*
|
|
98
|
+
* @param accountId - NEAR account ID
|
|
99
|
+
* @returns Next key in rotation, or null if no keys exist for account
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // First call returns key1, second returns key2, third returns key3, fourth returns key1...
|
|
104
|
+
* const key1 = await keyStore.get("alice.near")
|
|
105
|
+
* const key2 = await keyStore.get("alice.near")
|
|
106
|
+
* const key3 = await keyStore.get("alice.near")
|
|
107
|
+
* const key4 = await keyStore.get("alice.near") // Back to key1
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
async get(accountId) {
|
|
111
|
+
const accountKeys = this.keys.get(accountId);
|
|
112
|
+
if (!accountKeys || accountKeys.length === 0) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
// Get current counter and increment for next call
|
|
116
|
+
const counter = this.counters.get(accountId) ?? 0;
|
|
117
|
+
const key = accountKeys[counter % accountKeys.length];
|
|
118
|
+
this.counters.set(accountId, counter + 1);
|
|
119
|
+
// We know key exists because we checked accountKeys.length > 0 above
|
|
120
|
+
return key ?? null;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Add a key to an account's rotation pool.
|
|
124
|
+
*
|
|
125
|
+
* If the account already has keys, the new key is appended to the rotation.
|
|
126
|
+
* If this is the first key for the account, it becomes the starting key.
|
|
127
|
+
*
|
|
128
|
+
* @param accountId - NEAR account ID
|
|
129
|
+
* @param key - Key pair to add to rotation
|
|
130
|
+
* @param options - Optional metadata (preserved but not used for rotation)
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* await keyStore.add("alice.near", keyPair1)
|
|
135
|
+
* await keyStore.add("alice.near", keyPair2) // Now rotates between both
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
async add(accountId, key, _options) {
|
|
139
|
+
const existing = this.keys.get(accountId) ?? [];
|
|
140
|
+
existing.push(key);
|
|
141
|
+
this.keys.set(accountId, existing);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Remove all keys for an account from the rotation pool.
|
|
145
|
+
*
|
|
146
|
+
* This also resets the rotation counter for the account.
|
|
147
|
+
*
|
|
148
|
+
* @param accountId - NEAR account ID
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* await keyStore.remove("alice.near")
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
async remove(accountId) {
|
|
156
|
+
this.keys.delete(accountId);
|
|
157
|
+
this.counters.delete(accountId);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* List all account IDs in the keystore.
|
|
161
|
+
*
|
|
162
|
+
* @returns Array of account IDs that have at least one key
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* const accounts = await keyStore.list()
|
|
167
|
+
* console.log(`Managing keys for: ${accounts.join(", ")}`)
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
async list() {
|
|
171
|
+
return Array.from(this.keys.keys());
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get all keys for an account (non-rotating).
|
|
175
|
+
*
|
|
176
|
+
* Returns all keys in the rotation pool without advancing the counter.
|
|
177
|
+
* Useful for inspecting or managing the key pool.
|
|
178
|
+
*
|
|
179
|
+
* @param accountId - NEAR account ID
|
|
180
|
+
* @returns Array of all key pairs for the account, or empty array if none exist
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* const keys = await keyStore.getAll("alice.near")
|
|
185
|
+
* console.log(`Account has ${keys.length} keys in rotation`)
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
async getAll(accountId) {
|
|
189
|
+
return this.keys.get(accountId) ?? [];
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Get the current rotation index for an account.
|
|
193
|
+
*
|
|
194
|
+
* The index indicates which key will be returned on the next `get()` call.
|
|
195
|
+
*
|
|
196
|
+
* @param accountId - NEAR account ID
|
|
197
|
+
* @returns Current counter value (0-based index into key array)
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* const index = keyStore.getCurrentIndex("alice.near")
|
|
202
|
+
* const totalKeys = (await keyStore.getAll("alice.near")).length
|
|
203
|
+
* console.log(`Next key: ${index % totalKeys}`)
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
getCurrentIndex(accountId) {
|
|
207
|
+
return this.counters.get(accountId) ?? 0;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Reset the rotation counter for an account.
|
|
211
|
+
*
|
|
212
|
+
* The next `get()` call will return the first key in the rotation.
|
|
213
|
+
*
|
|
214
|
+
* @param accountId - NEAR account ID
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* keyStore.resetCounter("alice.near")
|
|
219
|
+
* const key = await keyStore.get("alice.near") // Returns first key
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
resetCounter(accountId) {
|
|
223
|
+
this.counters.set(accountId, 0);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Clear all keys and counters from the keystore.
|
|
227
|
+
*
|
|
228
|
+
* Useful for testing cleanup or resetting state.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```typescript
|
|
232
|
+
* keyStore.clear()
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
clear() {
|
|
236
|
+
this.keys.clear();
|
|
237
|
+
this.counters.clear();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=rotating-keystore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rotating-keystore.js","sourceRoot":"","sources":["../../src/keys/rotating-keystore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NEAR Sandbox - Local testing environment
|
|
3
|
+
*
|
|
4
|
+
* Simple, explicit API for running a local NEAR node for testing.
|
|
5
|
+
*/
|
|
6
|
+
export interface SandboxOptions {
|
|
7
|
+
version?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Whether to spawn the sandbox process as detached.
|
|
10
|
+
* Default: true
|
|
11
|
+
* Set to false in test environments to prevent the process from being killed by test runners.
|
|
12
|
+
*/
|
|
13
|
+
detached?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* NEAR Sandbox instance
|
|
17
|
+
*
|
|
18
|
+
* Manages a local NEAR node for testing. Automatically cleans up on stop().
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const sandbox = await Sandbox.start();
|
|
23
|
+
* const near = new Near({ network: sandbox });
|
|
24
|
+
* // ... run tests
|
|
25
|
+
* await sandbox.stop();
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare class Sandbox {
|
|
29
|
+
readonly rpcUrl: string;
|
|
30
|
+
readonly networkId: string;
|
|
31
|
+
readonly rootAccount: {
|
|
32
|
+
id: string;
|
|
33
|
+
secretKey: string;
|
|
34
|
+
};
|
|
35
|
+
private process;
|
|
36
|
+
private homeDir;
|
|
37
|
+
private constructor();
|
|
38
|
+
/**
|
|
39
|
+
* Start a new sandbox instance
|
|
40
|
+
*
|
|
41
|
+
* Downloads the sandbox binary if needed, initializes a temporary directory,
|
|
42
|
+
* and starts the sandbox process.
|
|
43
|
+
*
|
|
44
|
+
* @param options - Optional configuration
|
|
45
|
+
* @returns Promise resolving to a running Sandbox instance
|
|
46
|
+
*/
|
|
47
|
+
static start(options?: SandboxOptions): Promise<Sandbox>;
|
|
48
|
+
/**
|
|
49
|
+
* Stop the sandbox and clean up
|
|
50
|
+
*
|
|
51
|
+
* Kills the sandbox process and removes temporary files.
|
|
52
|
+
*/
|
|
53
|
+
stop(): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/sandbox/sandbox.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkCH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,OAAO;IAClB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAEvD,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,OAAO,CAAQ;IAEvB,OAAO;IAcP;;;;;;;;OAQG;WACU,KAAK,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;IAuDlE;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAiC5B"}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NEAR Sandbox - Local testing environment
|
|
3
|
+
*
|
|
4
|
+
* Simple, explicit API for running a local NEAR node for testing.
|
|
5
|
+
*/
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import { mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
|
|
9
|
+
import { createServer } from "node:net";
|
|
10
|
+
import os from "node:os";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { Readable } from "node:stream";
|
|
13
|
+
import { pipeline } from "node:stream/promises";
|
|
14
|
+
import * as tar from "tar";
|
|
15
|
+
const DEFAULT_VERSION = "2.9.0";
|
|
16
|
+
const BINARY_NAME = "near-sandbox";
|
|
17
|
+
const ARCHIVE_NAME = "near-sandbox.tar.gz";
|
|
18
|
+
const DOWNLOAD_BASE = "https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/nearcore";
|
|
19
|
+
const STARTUP_TIMEOUT = 60000;
|
|
20
|
+
const DOWNLOAD_TIMEOUT = 120000;
|
|
21
|
+
/**
|
|
22
|
+
* NEAR Sandbox instance
|
|
23
|
+
*
|
|
24
|
+
* Manages a local NEAR node for testing. Automatically cleans up on stop().
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const sandbox = await Sandbox.start();
|
|
29
|
+
* const near = new Near({ network: sandbox });
|
|
30
|
+
* // ... run tests
|
|
31
|
+
* await sandbox.stop();
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class Sandbox {
|
|
35
|
+
constructor(rpcUrl, networkId, rootAccount, homeDir, childProcess) {
|
|
36
|
+
this.rpcUrl = rpcUrl;
|
|
37
|
+
this.networkId = networkId;
|
|
38
|
+
this.rootAccount = rootAccount;
|
|
39
|
+
this.homeDir = homeDir;
|
|
40
|
+
this.process = childProcess;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Start a new sandbox instance
|
|
44
|
+
*
|
|
45
|
+
* Downloads the sandbox binary if needed, initializes a temporary directory,
|
|
46
|
+
* and starts the sandbox process.
|
|
47
|
+
*
|
|
48
|
+
* @param options - Optional configuration
|
|
49
|
+
* @returns Promise resolving to a running Sandbox instance
|
|
50
|
+
*/
|
|
51
|
+
static async start(options = {}) {
|
|
52
|
+
const version = options.version ?? DEFAULT_VERSION;
|
|
53
|
+
const detached = options.detached ?? true;
|
|
54
|
+
// 1. Ensure binary is available
|
|
55
|
+
const binaryPath = await ensureBinary(version);
|
|
56
|
+
// 2. Create temporary home directory
|
|
57
|
+
const homeDir = await mkdtemp(path.join(os.tmpdir(), "near-sandbox-"));
|
|
58
|
+
// 3. Initialize sandbox
|
|
59
|
+
await runInit(binaryPath, homeDir);
|
|
60
|
+
// 4. Read validator key
|
|
61
|
+
const validatorKey = await loadValidatorKey(homeDir);
|
|
62
|
+
const rootAccount = {
|
|
63
|
+
id: validatorKey.account_id,
|
|
64
|
+
secretKey: validatorKey.secret_key ?? validatorKey.private_key ?? "",
|
|
65
|
+
};
|
|
66
|
+
// 5. Start sandbox process
|
|
67
|
+
const port = await findAvailablePort();
|
|
68
|
+
const networkPort = port + 1;
|
|
69
|
+
const childProcess = spawn(binaryPath, [
|
|
70
|
+
"--home",
|
|
71
|
+
homeDir,
|
|
72
|
+
"run",
|
|
73
|
+
"--rpc-addr",
|
|
74
|
+
`0.0.0.0:${port}`,
|
|
75
|
+
"--network-addr",
|
|
76
|
+
`0.0.0.0:${networkPort}`,
|
|
77
|
+
], {
|
|
78
|
+
detached,
|
|
79
|
+
stdio: detached ? "ignore" : "pipe",
|
|
80
|
+
});
|
|
81
|
+
if (!childProcess.pid) {
|
|
82
|
+
throw new Error("Failed to start sandbox: no PID");
|
|
83
|
+
}
|
|
84
|
+
if (detached) {
|
|
85
|
+
childProcess.unref();
|
|
86
|
+
}
|
|
87
|
+
const rpcUrl = `http://127.0.0.1:${port}`;
|
|
88
|
+
// 6. Wait for RPC to be ready
|
|
89
|
+
await waitForReady(rpcUrl);
|
|
90
|
+
return new Sandbox(rpcUrl, "localnet", rootAccount, homeDir, childProcess);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Stop the sandbox and clean up
|
|
94
|
+
*
|
|
95
|
+
* Kills the sandbox process and removes temporary files.
|
|
96
|
+
*/
|
|
97
|
+
async stop() {
|
|
98
|
+
if (this.process?.pid) {
|
|
99
|
+
const pid = this.process.pid;
|
|
100
|
+
try {
|
|
101
|
+
// Try to kill process group first (for detached processes)
|
|
102
|
+
process.kill(-pid, "SIGTERM");
|
|
103
|
+
await sleep(100);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Try direct kill
|
|
107
|
+
try {
|
|
108
|
+
process.kill(pid, "SIGTERM");
|
|
109
|
+
await sleep(100);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Try SIGKILL as last resort
|
|
113
|
+
try {
|
|
114
|
+
process.kill(pid, "SIGKILL");
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Process already dead
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
this.process = undefined;
|
|
122
|
+
}
|
|
123
|
+
// Clean up temporary directory
|
|
124
|
+
try {
|
|
125
|
+
await rm(this.homeDir, { recursive: true, force: true });
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Ignore cleanup errors
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// ==================== Helper Functions ====================
|
|
133
|
+
/**
|
|
134
|
+
* Get platform identifier for downloading correct binary.
|
|
135
|
+
* @internal
|
|
136
|
+
*/
|
|
137
|
+
function getPlatformId() {
|
|
138
|
+
const system = os.platform();
|
|
139
|
+
const arch = os.arch();
|
|
140
|
+
const platform = system === "darwin" ? "Darwin" : "Linux";
|
|
141
|
+
const normalizedArch = arch === "x64" ? "x86_64" : arch;
|
|
142
|
+
if (!["x86_64", "arm64"].includes(normalizedArch)) {
|
|
143
|
+
throw new Error(`Unsupported architecture: ${arch}`);
|
|
144
|
+
}
|
|
145
|
+
if (system !== "darwin" && system !== "linux") {
|
|
146
|
+
throw new Error(`Unsupported platform: ${system}`);
|
|
147
|
+
}
|
|
148
|
+
return { system: platform, arch: normalizedArch };
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get directory for storing sandbox binaries.
|
|
152
|
+
* @internal
|
|
153
|
+
*/
|
|
154
|
+
function getBinaryDir() {
|
|
155
|
+
const dir = path.join(os.homedir(), ".near-kit", "sandbox", "bin");
|
|
156
|
+
return dir;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Download and extract sandbox binary.
|
|
160
|
+
* @internal
|
|
161
|
+
*/
|
|
162
|
+
async function downloadBinary(version) {
|
|
163
|
+
const { system, arch } = getPlatformId();
|
|
164
|
+
const destDir = getBinaryDir();
|
|
165
|
+
const filename = `${BINARY_NAME}-${version}`;
|
|
166
|
+
const dest = path.join(destDir, filename);
|
|
167
|
+
// Return if already exists
|
|
168
|
+
if (fs.existsSync(dest)) {
|
|
169
|
+
return dest;
|
|
170
|
+
}
|
|
171
|
+
const url = `${DOWNLOAD_BASE}/${system}-${arch}/${version}/${ARCHIVE_NAME}`;
|
|
172
|
+
const tmpDir = await mkdtemp(path.join(os.tmpdir(), "near-sandbox-download-"));
|
|
173
|
+
try {
|
|
174
|
+
const archivePath = path.join(tmpDir, ARCHIVE_NAME);
|
|
175
|
+
// Download with timeout
|
|
176
|
+
const controller = new AbortController();
|
|
177
|
+
const timeout = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT);
|
|
178
|
+
try {
|
|
179
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
180
|
+
if (!response.ok) {
|
|
181
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
182
|
+
}
|
|
183
|
+
if (!response.body) {
|
|
184
|
+
throw new Error("Response body is null");
|
|
185
|
+
}
|
|
186
|
+
const stream = fs.createWriteStream(archivePath);
|
|
187
|
+
// Convert DOM ReadableStream to Node.js ReadableStream
|
|
188
|
+
await pipeline(Readable.fromWeb(response.body), stream);
|
|
189
|
+
}
|
|
190
|
+
finally {
|
|
191
|
+
clearTimeout(timeout);
|
|
192
|
+
}
|
|
193
|
+
// Extract archive (strip=1 removes top-level directory)
|
|
194
|
+
await tar.x({ file: archivePath, cwd: tmpDir, strip: 1 });
|
|
195
|
+
const extracted = path.join(tmpDir, BINARY_NAME);
|
|
196
|
+
if (!fs.existsSync(extracted)) {
|
|
197
|
+
throw new Error(`Binary ${BINARY_NAME} not found in archive`);
|
|
198
|
+
}
|
|
199
|
+
// Move to final location and make executable
|
|
200
|
+
await mkdir(path.dirname(dest), { recursive: true });
|
|
201
|
+
await fs.promises.rename(extracted, dest);
|
|
202
|
+
await fs.promises.chmod(dest, 0o755);
|
|
203
|
+
return dest;
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
throw new Error(`Failed to download binary from ${url}: ${error}`);
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Ensure binary is available and return its path.
|
|
214
|
+
* @internal
|
|
215
|
+
*/
|
|
216
|
+
async function ensureBinary(version) {
|
|
217
|
+
return await downloadBinary(version);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Run sandbox init command.
|
|
221
|
+
* @internal
|
|
222
|
+
*/
|
|
223
|
+
async function runInit(binaryPath, homeDir) {
|
|
224
|
+
const args = ["--home", homeDir, "init", "--chain-id", "localnet"];
|
|
225
|
+
return new Promise((resolve, reject) => {
|
|
226
|
+
const child = spawn(binaryPath, args, { stdio: "pipe" });
|
|
227
|
+
let stderr = "";
|
|
228
|
+
child.stderr?.on("data", (data) => {
|
|
229
|
+
stderr += data.toString();
|
|
230
|
+
});
|
|
231
|
+
child.on("exit", (code) => {
|
|
232
|
+
if (code === 0) {
|
|
233
|
+
resolve();
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
// Provide helpful error message for common issues
|
|
237
|
+
let errorMsg = `Sandbox init failed with code ${code}: ${stderr}`;
|
|
238
|
+
if (stderr.includes("file descriptor limit")) {
|
|
239
|
+
errorMsg +=
|
|
240
|
+
"\n\n" +
|
|
241
|
+
"The sandbox requires at least 65,535 file descriptors.\n" +
|
|
242
|
+
"Current limit can be checked with: ulimit -n\n\n" +
|
|
243
|
+
"To fix on Linux, add to /etc/security/limits.conf:\n" +
|
|
244
|
+
" * soft nofile 65535\n" +
|
|
245
|
+
" * hard nofile 65535\n\n" +
|
|
246
|
+
"To fix on macOS:\n" +
|
|
247
|
+
" sudo launchctl limit maxfiles 65536 200000\n\n" +
|
|
248
|
+
"For Docker, add: --ulimit nofile=65535:65535\n\n" +
|
|
249
|
+
"See: https://github.com/r-near/near-kit/blob/main/src/sandbox/README.md";
|
|
250
|
+
}
|
|
251
|
+
reject(new Error(errorMsg));
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
child.on("error", reject);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Load validator key from sandbox home directory.
|
|
259
|
+
* @internal
|
|
260
|
+
*/
|
|
261
|
+
async function loadValidatorKey(homeDir) {
|
|
262
|
+
const keyPath = path.join(homeDir, "validator_key.json");
|
|
263
|
+
try {
|
|
264
|
+
const data = await readFile(keyPath, "utf8");
|
|
265
|
+
return JSON.parse(data);
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
throw new Error(`Failed to read validator key from ${keyPath}: ${error}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Find an available port by letting the OS choose.
|
|
273
|
+
* @internal
|
|
274
|
+
*/
|
|
275
|
+
async function findAvailablePort() {
|
|
276
|
+
return new Promise((resolve, reject) => {
|
|
277
|
+
const server = createServer();
|
|
278
|
+
server.on("error", reject);
|
|
279
|
+
server.listen(0, "127.0.0.1", () => {
|
|
280
|
+
const address = server.address();
|
|
281
|
+
if (!address || typeof address === "string") {
|
|
282
|
+
reject(new Error("Failed to get port"));
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const port = address.port;
|
|
286
|
+
server.close(() => {
|
|
287
|
+
resolve(port);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Ping sandbox RPC endpoint to check if it's ready.
|
|
294
|
+
* @internal
|
|
295
|
+
*/
|
|
296
|
+
async function pingRpc(url, timeoutMs = 1000) {
|
|
297
|
+
const controller = new AbortController();
|
|
298
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
299
|
+
try {
|
|
300
|
+
const response = await fetch(url, {
|
|
301
|
+
method: "POST",
|
|
302
|
+
headers: { "Content-Type": "application/json" },
|
|
303
|
+
body: JSON.stringify({
|
|
304
|
+
jsonrpc: "2.0",
|
|
305
|
+
id: "status",
|
|
306
|
+
method: "status",
|
|
307
|
+
params: [],
|
|
308
|
+
}),
|
|
309
|
+
signal: controller.signal,
|
|
310
|
+
});
|
|
311
|
+
return response.ok;
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
finally {
|
|
317
|
+
clearTimeout(timeout);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Wait for sandbox to be ready.
|
|
322
|
+
* @internal
|
|
323
|
+
*/
|
|
324
|
+
async function waitForReady(rpcUrl, timeout = STARTUP_TIMEOUT) {
|
|
325
|
+
const start = Date.now();
|
|
326
|
+
while (Date.now() - start < timeout) {
|
|
327
|
+
if (await pingRpc(rpcUrl)) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
await sleep(500);
|
|
331
|
+
}
|
|
332
|
+
throw new Error(`Sandbox failed to start within ${timeout}ms`);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Sleep helper.
|
|
336
|
+
* @internal
|
|
337
|
+
*/
|
|
338
|
+
function sleep(ms) {
|
|
339
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
340
|
+
}
|
|
341
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/sandbox/sandbox.ts"],"names":[],"mappings":"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"}
|