suidouble 2.5.0 → 2.17.0-1
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/.claude/settings.local.json +7 -0
- package/README.md +222 -131
- package/index.js +0 -2
- package/lib/SuiCliCommands.js +18 -25
- package/lib/SuiCoin.js +79 -137
- package/lib/SuiCoins.js +41 -29
- package/lib/SuiCommonMethods.js +40 -3
- package/lib/SuiEvent.js +54 -6
- package/lib/SuiInBrowser.js +161 -16
- package/lib/SuiInBrowserAdapter.js +192 -40
- package/lib/SuiLocalTestValidator.js +76 -14
- package/lib/SuiMaster.js +335 -139
- package/lib/SuiMemoryObjectStorage.js +66 -73
- package/lib/SuiObject.js +128 -153
- package/lib/SuiPackage.js +292 -187
- package/lib/SuiPackageModule.js +176 -221
- package/lib/SuiPaginatedResponse.js +288 -25
- package/lib/SuiPseudoRandomAddress.js +29 -2
- package/lib/SuiTransaction.js +115 -70
- package/lib/SuiUtils.js +179 -127
- package/package.json +29 -13
- package/test/build_modules.test.js +41 -0
- package/test/coins.test.js +17 -16
- package/test/custom_transaction.test.js +167 -0
- package/test/event_listeners.test.js +171 -0
- package/test/failed_transaction.test.js +184 -0
- package/test/name_service.test.js +28 -0
- package/test/owned_objects.test.js +148 -0
- package/test/rpc.test.js +3 -6
- package/test/sui_in_browser.test.js +2 -2
- package/test/sui_master_basic.test.js +4 -5
- package/test/sui_master_onlocal.test.js +84 -22
- package/test/sui_object_properties.test.js +85 -0
- package/test/test_move_contracts/different_types/Move.lock +18 -0
- package/test/test_move_contracts/suidouble_chat/Move.lock +18 -0
- package/tsconfig.json +15 -0
- package/types/index.d.ts +15 -0
- package/types/lib/SuiCliCommands.d.ts +6 -0
- package/types/lib/SuiCoin.d.ts +183 -0
- package/types/lib/SuiCoins.d.ts +93 -0
- package/types/lib/SuiCommonMethods.d.ts +37 -0
- package/types/lib/SuiEvent.d.ts +95 -0
- package/types/lib/SuiInBrowser.d.ts +195 -0
- package/types/lib/SuiInBrowserAdapter.d.ts +173 -0
- package/types/lib/SuiLocalTestValidator.d.ts +92 -0
- package/types/lib/SuiMaster.d.ts +333 -0
- package/types/lib/SuiMemoryObjectStorage.d.ts +96 -0
- package/types/lib/SuiObject.d.ts +135 -0
- package/types/lib/SuiPackage.d.ts +233 -0
- package/types/lib/SuiPackageModule.d.ts +139 -0
- package/types/lib/SuiPaginatedResponse.d.ts +148 -0
- package/types/lib/SuiPseudoRandomAddress.d.ts +33 -0
- package/types/lib/SuiTransaction.d.ts +92 -0
- package/types/lib/SuiUtils.d.ts +152 -0
- package/types/lib/data/icons.d.ts +12 -0
- package/lib/SuiTestScenario.js +0 -169
- package/test/sui_test_scenario.test.js +0 -61
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import SuiCommonMethods from './SuiCommonMethods.js';
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import("@mysten/sui/cryptography").SignatureWithBytes} SignatureWithBytes
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Wallet Standard feature identifiers — keys map to the string feature names used by the
|
|
10
|
+
* `@wallet-standard/core` registry.
|
|
11
|
+
* @enum {string}
|
|
12
|
+
*/
|
|
3
13
|
const Feature = {
|
|
4
14
|
DISCONNECT: 'standard:disconnect',
|
|
5
15
|
CONNECT: 'standard:connect',
|
|
@@ -12,25 +22,55 @@ const Feature = {
|
|
|
12
22
|
SUI_SIGN_PERSONAL_MESSAGE: 'sui:signPersonalMessage',
|
|
13
23
|
};
|
|
14
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Wrapper over a single Wallet Standard adapter (e.g. Sui Wallet, Martian, Suiet).
|
|
27
|
+
* Abstracts away legacy `signAndExecuteTransactionBlock` vs the current
|
|
28
|
+
* `signAndExecuteTransaction` API, and normalises connection state changes into
|
|
29
|
+
* `'connected'` / `'disconnected'` events on this instance.
|
|
30
|
+
*/
|
|
15
31
|
export default class SuiInBrowserAdapter extends SuiCommonMethods {
|
|
32
|
+
/**
|
|
33
|
+
* @param {Object} [params]
|
|
34
|
+
* @param {Object} [params.standardAdapter] - raw Wallet Standard adapter from `@wallet-standard/core`
|
|
35
|
+
* @param {?string} [params.name] - display name used when no adapter is attached (e.g. "not installed" placeholder)
|
|
36
|
+
* @param {?string} [params.icon] - data-URI icon used when no adapter is attached
|
|
37
|
+
* @param {Object} [params.downloadUrls] - map of browser name → extension store URL (e.g. `{ chrome: 'https://…' }`)
|
|
38
|
+
* @param {boolean} [params.debug]
|
|
39
|
+
*/
|
|
16
40
|
constructor(params = {}) {
|
|
17
41
|
super(params);
|
|
18
42
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (params.
|
|
22
|
-
this.setStandartAdapter(params.
|
|
43
|
+
/** @type {?Object} raw Wallet Standard adapter */
|
|
44
|
+
this._standardAdapter = null;
|
|
45
|
+
if (params.standardAdapter) {
|
|
46
|
+
this.setStandartAdapter(params.standardAdapter);
|
|
23
47
|
}
|
|
24
|
-
|
|
48
|
+
|
|
49
|
+
/** @type {?string} */
|
|
25
50
|
this._name = params.name || null;
|
|
51
|
+
/** @type {?string} */
|
|
26
52
|
this._icon = params.icon || null;
|
|
53
|
+
/** @type {Object.<string, string>} */
|
|
27
54
|
this._downloadUrls = params.downloadUrls || {};
|
|
28
55
|
|
|
56
|
+
/** @type {?string} */
|
|
29
57
|
this._connectedAddress = null;
|
|
58
|
+
/** @type {?string} */
|
|
30
59
|
this._connectedChain = null;
|
|
60
|
+
/** @type {boolean} */
|
|
31
61
|
this._isConnected = false;
|
|
62
|
+
/** @type {boolean} */
|
|
63
|
+
this._userDisconnected = false;
|
|
64
|
+
/** @type {?string} */
|
|
65
|
+
this._preferredChain = null;
|
|
32
66
|
}
|
|
33
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Sign and execute a transaction. Prefers the current `sui:signAndExecuteTransaction`
|
|
70
|
+
* feature; falls back to the legacy `sui:signAndExecuteTransactionBlock` for older wallets.
|
|
71
|
+
* @param {Object} params
|
|
72
|
+
* @returns {Promise<*>}
|
|
73
|
+
*/
|
|
34
74
|
async signAndExecuteTransaction(params) {
|
|
35
75
|
if (this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX)) {
|
|
36
76
|
return await this.getFeature(Feature.SUI_SIGN_AND_EXECUTE_TX).signAndExecuteTransaction(params);
|
|
@@ -41,10 +81,32 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
|
|
|
41
81
|
}
|
|
42
82
|
}
|
|
43
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Sign and execute a transaction using the legacy `sui:signAndExecuteTransactionBlock` feature.
|
|
86
|
+
* @param {Object} params
|
|
87
|
+
* @returns {Promise<*>}
|
|
88
|
+
*/
|
|
44
89
|
async signAndExecuteTransactionBlock(params) {
|
|
45
90
|
return await this.getFeature(Feature.SUI_SIGN_AND_EXECUTE_TX_BLOCK).signAndExecuteTransactionBlock(params);
|
|
46
91
|
}
|
|
47
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Sign a transaction using the current `sui:signTransaction` feature.
|
|
95
|
+
* @param {Object} params
|
|
96
|
+
* @returns {Promise<SignatureWithBytes>}
|
|
97
|
+
*/
|
|
98
|
+
async signTransaction(params) {
|
|
99
|
+
/** @type {SignatureWithBytes} */
|
|
100
|
+
const resp = await this.getFeature(Feature.SUI_SIGN_TX).signTransaction(params);
|
|
101
|
+
return resp;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Sign a transaction. Prefers the current `sui:signTransaction` feature;
|
|
106
|
+
* falls back to the legacy `sui:signTransactionBlock` for older wallets.
|
|
107
|
+
* @param {Object} params
|
|
108
|
+
* @returns {Promise<*>}
|
|
109
|
+
*/
|
|
48
110
|
async signTransactionBlock(params) {
|
|
49
111
|
if (this.hasFeature(Feature.SUI_SIGN_TX)) {
|
|
50
112
|
return await this.getFeature(Feature.SUI_SIGN_TX).signTransaction(params);
|
|
@@ -55,10 +117,12 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
|
|
|
55
117
|
}
|
|
56
118
|
}
|
|
57
119
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Sign a personal/arbitrary message. Prefers `sui:signPersonalMessage`;
|
|
122
|
+
* falls back to `sui:signMessage` for older wallets.
|
|
123
|
+
* @param {Object} params
|
|
124
|
+
* @returns {Promise<*>}
|
|
125
|
+
*/
|
|
62
126
|
async signPersonalMessage(params) {
|
|
63
127
|
if (this.hasFeature(Feature.SUI_SIGN_PERSONAL_MESSAGE)) {
|
|
64
128
|
return await this.getFeature(Feature.SUI_SIGN_PERSONAL_MESSAGE).signPersonalMessage(params);
|
|
@@ -67,16 +131,43 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
|
|
|
67
131
|
}
|
|
68
132
|
}
|
|
69
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Alias for `signPersonalMessage`.
|
|
136
|
+
* @param {Object} params
|
|
137
|
+
* @returns {Promise<*>}
|
|
138
|
+
*/
|
|
70
139
|
async signMessage(params) {
|
|
71
140
|
return await this.signPersonalMessage(params);
|
|
72
141
|
}
|
|
73
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Disconnect from the wallet and refresh connection state.
|
|
145
|
+
* Force-clears local connection state even if the wallet doesn't support
|
|
146
|
+
* standard:disconnect or doesn't revoke site authorization (e.g. Phantom).
|
|
147
|
+
* @param {Object} [params]
|
|
148
|
+
* @returns {Promise<void>}
|
|
149
|
+
*/
|
|
74
150
|
async disconnect(params) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
151
|
+
try {
|
|
152
|
+
const feature = this.getFeature(Feature.DISCONNECT);
|
|
153
|
+
if (feature) {
|
|
154
|
+
await feature.disconnect(params);
|
|
155
|
+
}
|
|
156
|
+
} catch (e) {
|
|
157
|
+
console.warn('[SuiInBrowserAdapter] standard:disconnect failed:', e);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this._connectedAddress = null;
|
|
161
|
+
this._connectedChain = null;
|
|
162
|
+
this._isConnected = false;
|
|
163
|
+
this._userDisconnected = true;
|
|
164
|
+
this.emit('disconnected', this);
|
|
78
165
|
}
|
|
79
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Return the Chrome Web Store download URL, or null if not configured.
|
|
169
|
+
* @returns {?string}
|
|
170
|
+
*/
|
|
80
171
|
getDownloadURL() {
|
|
81
172
|
if (this._downloadUrls && this._downloadUrls.chrome) {
|
|
82
173
|
return this._downloadUrls.chrome;
|
|
@@ -84,26 +175,36 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
|
|
|
84
175
|
return null;
|
|
85
176
|
}
|
|
86
177
|
|
|
178
|
+
/**
|
|
179
|
+
* True when no underlying adapter has been attached (i.e. this is a "not installed" placeholder).
|
|
180
|
+
* @returns {boolean}
|
|
181
|
+
*/
|
|
87
182
|
get isDefault() {
|
|
88
|
-
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
return false;
|
|
183
|
+
return !this._standardAdapter;
|
|
92
184
|
}
|
|
93
185
|
|
|
186
|
+
/** @returns {?string} currently connected Sui address, or null */
|
|
94
187
|
get connectedAddress() {
|
|
95
188
|
return this._connectedAddress;
|
|
96
189
|
}
|
|
97
190
|
|
|
191
|
+
/** @returns {?string} currently connected chain identifier (e.g. `'sui:mainnet'`), or null */
|
|
98
192
|
get connectedChain() {
|
|
99
193
|
return this._connectedChain;
|
|
100
194
|
}
|
|
101
195
|
|
|
196
|
+
/** @returns {boolean} true if the wallet is connected and an address is available */
|
|
102
197
|
get isConnected() {
|
|
103
198
|
return this._isConnected;
|
|
104
199
|
}
|
|
105
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Request connection to the wallet. Errors are swallowed; connection state is refreshed
|
|
203
|
+
* via `connectionUpdated` regardless.
|
|
204
|
+
* @returns {Promise<void>}
|
|
205
|
+
*/
|
|
106
206
|
async connect() {
|
|
207
|
+
this._userDisconnected = false;
|
|
107
208
|
try {
|
|
108
209
|
await this.getFeature(Feature.CONNECT).connect();
|
|
109
210
|
} catch (e) {
|
|
@@ -113,14 +214,26 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
|
|
|
113
214
|
this.connectionUpdated();
|
|
114
215
|
}
|
|
115
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Read the current connection state from the underlying adapter and update
|
|
219
|
+
* `connectedAddress`, `connectedChain`, and `isConnected`. Emits `'connected'` or
|
|
220
|
+
* `'disconnected'` if the state changed.
|
|
221
|
+
*/
|
|
116
222
|
connectionUpdated() {
|
|
223
|
+
if (this._userDisconnected) return;
|
|
224
|
+
|
|
117
225
|
const wasConnectedAddress = ''+this._connectedAddress;
|
|
118
226
|
const wasConnectedChain = ''+this._connectedChain;
|
|
119
227
|
|
|
120
228
|
try {
|
|
121
|
-
if (this.
|
|
122
|
-
this._connectedAddress = this.
|
|
123
|
-
|
|
229
|
+
if (this._standardAdapter && this._standardAdapter.accounts && this._standardAdapter.accounts.length) {
|
|
230
|
+
this._connectedAddress = this._standardAdapter.accounts[0].address;
|
|
231
|
+
const accountChains = this._standardAdapter.accounts[0].chains;
|
|
232
|
+
if (this._preferredChain && accountChains.includes(this._preferredChain)) {
|
|
233
|
+
this._connectedChain = this._preferredChain;
|
|
234
|
+
} else {
|
|
235
|
+
this._connectedChain = accountChains[0];
|
|
236
|
+
}
|
|
124
237
|
} else {
|
|
125
238
|
this._connectedAddress = null;
|
|
126
239
|
this._connectedChain = null;
|
|
@@ -141,16 +254,23 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
|
|
|
141
254
|
}
|
|
142
255
|
}
|
|
143
256
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Attach a Wallet Standard adapter to this instance. No-op if already attached.
|
|
259
|
+
* Returns false and does nothing if the adapter has no `sui:` features.
|
|
260
|
+
* Subscribes to adapter `'change'` events to keep connection state up to date.
|
|
261
|
+
*
|
|
262
|
+
* @param {Object} standardAdapter - raw adapter from `@wallet-standard/core`
|
|
263
|
+
* @returns {boolean} false if the adapter lacks Sui features, undefined on success
|
|
264
|
+
*/
|
|
265
|
+
setStandartAdapter(standardAdapter) {
|
|
266
|
+
if (this._standardAdapter) {
|
|
147
267
|
// no need to re-attach
|
|
148
268
|
return true;
|
|
149
269
|
}
|
|
150
270
|
|
|
151
271
|
// check if it has sui: features
|
|
152
272
|
let hasSuiFeatures = false;
|
|
153
|
-
for (const key in
|
|
273
|
+
for (const key in standardAdapter.features) {
|
|
154
274
|
if ((''+key).indexOf('sui:') === 0) {
|
|
155
275
|
hasSuiFeatures = true;
|
|
156
276
|
}
|
|
@@ -160,65 +280,97 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
|
|
|
160
280
|
return false;
|
|
161
281
|
}
|
|
162
282
|
|
|
163
|
-
this.
|
|
164
|
-
if (!this.
|
|
165
|
-
this.
|
|
283
|
+
this._standardAdapter = standardAdapter;
|
|
284
|
+
if (!this.__standardAdapterChangeListener) {
|
|
285
|
+
this.__standardAdapterChangeListener = () => {
|
|
166
286
|
this.connectionUpdated();
|
|
167
287
|
};
|
|
168
288
|
}
|
|
169
|
-
this.getFeature(Feature.EVENTS).on('change', this.
|
|
289
|
+
this.getFeature(Feature.EVENTS).on('change', this.__standardAdapterChangeListener);
|
|
170
290
|
|
|
171
291
|
this.connectionUpdated();
|
|
172
292
|
}
|
|
173
293
|
|
|
294
|
+
/**
|
|
295
|
+
* True if the adapter is installed and supports the minimum required Sui features
|
|
296
|
+
* (`sui:signAndExecuteTransaction` or `sui:signAndExecuteTransactionBlock`, plus `standard:events`).
|
|
297
|
+
* @returns {boolean}
|
|
298
|
+
*/
|
|
174
299
|
get okForSui() {
|
|
175
300
|
if (!this.isInstalled) {
|
|
176
301
|
return false;
|
|
177
302
|
}
|
|
178
303
|
|
|
179
|
-
return this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX_BLOCK) && this.hasFeature(Feature.EVENTS);
|
|
304
|
+
return (this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX_BLOCK) || this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX)) && this.hasFeature(Feature.EVENTS);
|
|
180
305
|
}
|
|
181
306
|
|
|
307
|
+
/** @returns {boolean} true if a Wallet Standard adapter has been attached */
|
|
182
308
|
get isInstalled() {
|
|
183
|
-
|
|
184
|
-
return true;
|
|
185
|
-
}
|
|
186
|
-
return false;
|
|
309
|
+
return !!this._standardAdapter;
|
|
187
310
|
}
|
|
188
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Feature map from the underlying adapter, or an empty object if not installed.
|
|
314
|
+
* Keys are Wallet Standard feature strings (e.g. `'sui:signAndExecuteTransaction'`).
|
|
315
|
+
* @returns {Object}
|
|
316
|
+
*/
|
|
189
317
|
get features() {
|
|
190
|
-
if (this.
|
|
191
|
-
return this.
|
|
318
|
+
if (this._standardAdapter) {
|
|
319
|
+
return this._standardAdapter.features;
|
|
192
320
|
}
|
|
193
321
|
return {};
|
|
194
322
|
}
|
|
195
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Display name — from the adapter if installed, otherwise from the constructor param.
|
|
326
|
+
* @returns {?string}
|
|
327
|
+
*/
|
|
196
328
|
get name() {
|
|
197
|
-
if (this.
|
|
198
|
-
return this.
|
|
329
|
+
if (this._standardAdapter) {
|
|
330
|
+
return this._standardAdapter.name;
|
|
199
331
|
} else {
|
|
200
332
|
return this._name;
|
|
201
333
|
}
|
|
202
334
|
}
|
|
203
335
|
|
|
336
|
+
/**
|
|
337
|
+
* Data-URI icon — from the adapter if installed, otherwise from the constructor param.
|
|
338
|
+
* @returns {?string}
|
|
339
|
+
*/
|
|
204
340
|
get icon() {
|
|
205
|
-
if (this.
|
|
206
|
-
return this.
|
|
341
|
+
if (this._standardAdapter) {
|
|
342
|
+
return this._standardAdapter.icon;
|
|
207
343
|
} else {
|
|
208
344
|
return this._icon;
|
|
209
345
|
}
|
|
210
346
|
}
|
|
211
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Wallet version string from the adapter, or undefined if not installed.
|
|
350
|
+
* @returns {?string}
|
|
351
|
+
*/
|
|
212
352
|
get version() {
|
|
213
|
-
if (this.
|
|
214
|
-
return this.
|
|
353
|
+
if (this._standardAdapter) {
|
|
354
|
+
return this._standardAdapter.version;
|
|
215
355
|
}
|
|
216
356
|
}
|
|
217
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Returns true if this adapter supports the given feature.
|
|
360
|
+
* Accepts either a `Feature` enum key (e.g. `'SUI_SIGN_TX'`) or a raw feature string.
|
|
361
|
+
* @param {string} featureName
|
|
362
|
+
* @returns {boolean}
|
|
363
|
+
*/
|
|
218
364
|
hasFeature(featureName) {
|
|
219
365
|
return (!!this.getFeature(featureName));
|
|
220
366
|
}
|
|
221
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Return the feature object for `featureName`, or null if not available.
|
|
370
|
+
* Accepts either a `Feature` enum key (e.g. `'SUI_SIGN_TX'`) or a raw feature string.
|
|
371
|
+
* @param {string} featureName
|
|
372
|
+
* @returns {?Object}
|
|
373
|
+
*/
|
|
222
374
|
getFeature(featureName) {
|
|
223
375
|
const features = this.features;
|
|
224
376
|
|
|
@@ -2,29 +2,55 @@ import SuiCliCommands from "./SuiCliCommands.js";
|
|
|
2
2
|
import SuiCommonMethods from "./SuiCommonMethods.js";
|
|
3
3
|
import SuiUtils from "./SuiUtils.js";
|
|
4
4
|
|
|
5
|
+
/** @typedef {import("@mysten/sui/grpc").SuiGrpcClient} SuiGrpcClient */
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Manages a local `sui start` test-validator process. The singleton pattern (`launch` / `stop`)
|
|
9
|
+
* means only one validator runs per test suite even when called from multiple test files.
|
|
10
|
+
*
|
|
11
|
+
* When `testFallbackEnabled` is true and `sui start` fails (e.g. Sui CLI not installed),
|
|
12
|
+
* the instance transparently switches to `sui:devnet` so tests can still run against a live node.
|
|
13
|
+
*/
|
|
5
14
|
export default class SuiLocalTestValidator extends SuiCommonMethods {
|
|
15
|
+
/**
|
|
16
|
+
* @param {Object} [params]
|
|
17
|
+
* @param {boolean} [params.testFallbackEnabled] - fall back to devnet if the local node can't start
|
|
18
|
+
* @param {?number} [params.epochDuration] - custom epoch duration in ms (only valid with `--force-regenesis`)
|
|
19
|
+
* @param {boolean} [params.debug]
|
|
20
|
+
*/
|
|
6
21
|
constructor(params = {}) {
|
|
7
22
|
super(params);
|
|
8
23
|
|
|
24
|
+
/** @type {?Object} spawned child process */
|
|
9
25
|
this._child = null;
|
|
26
|
+
/** @type {boolean} */
|
|
10
27
|
this._active = false;
|
|
11
28
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
29
|
+
/**
|
|
30
|
+
* When true, falls back to sui:devnet if the local node can't be started.
|
|
31
|
+
* @type {boolean}
|
|
32
|
+
*/
|
|
33
|
+
this._testFallbackEnabled = !!params.testFallbackEnabled;
|
|
18
34
|
|
|
35
|
+
/** @type {?number} */
|
|
19
36
|
this._epochDuration = params.epochDuration || null;
|
|
20
37
|
|
|
38
|
+
/** @type {string} */
|
|
21
39
|
this._providerName = 'sui:localnet';
|
|
22
40
|
}
|
|
23
41
|
|
|
42
|
+
/** @returns {string} active chain identifier, e.g. `'sui:localnet'` or `'sui:devnet'` after fallback */
|
|
24
43
|
get providerName() {
|
|
25
44
|
return this._providerName;
|
|
26
45
|
}
|
|
27
46
|
|
|
47
|
+
/** @type {?SuiLocalTestValidator} */
|
|
48
|
+
static __instance = null;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* gRPC client for the currently active chain.
|
|
52
|
+
* @returns {SuiGrpcClient}
|
|
53
|
+
*/
|
|
28
54
|
get client() {
|
|
29
55
|
if (this._providerName === 'sui:localnet') {
|
|
30
56
|
return SuiUtils.suiClientFor('localnet');
|
|
@@ -34,15 +60,19 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
|
|
|
34
60
|
}
|
|
35
61
|
}
|
|
36
62
|
|
|
63
|
+
/** @returns {boolean} true once the node is up and accepting transactions */
|
|
37
64
|
get active() {
|
|
38
65
|
return this._active;
|
|
39
66
|
}
|
|
40
67
|
|
|
41
68
|
/**
|
|
42
|
-
* Launch
|
|
43
|
-
* Don't forget to
|
|
44
|
-
*
|
|
45
|
-
* @
|
|
69
|
+
* Launch the local sui node (singleton). If already running, returns the existing instance.
|
|
70
|
+
* Don't forget to call `.stop()` after usage.
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} [params]
|
|
73
|
+
* @param {boolean} [params.testFallbackEnabled]
|
|
74
|
+
* @param {?number} [params.epochDuration]
|
|
75
|
+
* @returns {Promise<SuiLocalTestValidator>}
|
|
46
76
|
*/
|
|
47
77
|
static async launch(params = {}) {
|
|
48
78
|
if (SuiLocalTestValidator.__instance) {
|
|
@@ -54,7 +84,8 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
|
|
|
54
84
|
}
|
|
55
85
|
|
|
56
86
|
/**
|
|
57
|
-
* Stop the local sui node
|
|
87
|
+
* Stop the singleton local sui node if running.
|
|
88
|
+
* @returns {Promise<void>}
|
|
58
89
|
*/
|
|
59
90
|
static async stop() {
|
|
60
91
|
if (SuiLocalTestValidator.__instance) {
|
|
@@ -62,10 +93,20 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
|
|
|
62
93
|
}
|
|
63
94
|
}
|
|
64
95
|
|
|
96
|
+
/**
|
|
97
|
+
* @param {number} port
|
|
98
|
+
* @returns {Promise<boolean>} true if the port accepts a TCP connection within 3 seconds
|
|
99
|
+
*/
|
|
65
100
|
async isPortThere(port) {
|
|
66
101
|
return await SuiCliCommands.isPortThere(port);
|
|
67
102
|
}
|
|
68
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Poll `isPortThere` every 500 ms until the port is open or the timeout elapses.
|
|
106
|
+
* @param {number} port
|
|
107
|
+
* @param {number} timeout - total wait time in ms
|
|
108
|
+
* @returns {Promise<boolean>}
|
|
109
|
+
*/
|
|
69
110
|
async waitForPort(port, timeout) {
|
|
70
111
|
this.log('waiting for port', port);
|
|
71
112
|
const startedCheckingAt = (new Date()).getTime();
|
|
@@ -82,9 +123,13 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
|
|
|
82
123
|
}
|
|
83
124
|
|
|
84
125
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
126
|
+
* Start `sui start --with-faucet --with-indexer --with-graphql --force-regenesis`,
|
|
127
|
+
* wait for the faucet (port 9123) and GraphQL (port 9125) to come up,
|
|
128
|
+
* then return this instance with `active === true`.
|
|
129
|
+
*
|
|
130
|
+
* If `testFallbackEnabled` is set and the spawn fails, silently switches to devnet.
|
|
131
|
+
*
|
|
132
|
+
* @returns {Promise<SuiLocalTestValidator>}
|
|
88
133
|
*/
|
|
89
134
|
async launch() {
|
|
90
135
|
if (this._active) {
|
|
@@ -98,6 +143,8 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
|
|
|
98
143
|
const params = [];
|
|
99
144
|
params.push('start');
|
|
100
145
|
params.push('--with-faucet');
|
|
146
|
+
params.push('--with-indexer');
|
|
147
|
+
params.push('--with-graphql');
|
|
101
148
|
params.push('--force-regenesis');
|
|
102
149
|
this._child = await SuiCliCommands.spawn('sui', params, { RUST_LOG: 'off,sui_node=info' });
|
|
103
150
|
} catch (e) {
|
|
@@ -120,22 +167,37 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
|
|
|
120
167
|
this.log(`child process exited with code ${code}`);
|
|
121
168
|
});
|
|
122
169
|
|
|
170
|
+
// @ts-ignore — process is a Node.js global; not available in TypeScript without @types/node
|
|
123
171
|
process.on('exit', ()=>{
|
|
124
172
|
if (this._child) {
|
|
125
173
|
this._child.kill();
|
|
126
174
|
}
|
|
127
175
|
});
|
|
176
|
+
// @ts-ignore
|
|
128
177
|
const cleanExit = function() { process.exit() };
|
|
178
|
+
// @ts-ignore
|
|
129
179
|
process.on('SIGINT', cleanExit); // catch ctrl-c
|
|
180
|
+
// @ts-ignore
|
|
130
181
|
process.on('SIGTERM', cleanExit); // catch kill
|
|
131
182
|
|
|
132
183
|
this._active = await this.waitForPort(9123, 30000);
|
|
133
184
|
|
|
185
|
+
if (this._active) {
|
|
186
|
+
const graphqlUp = await this.waitForPort(9125, 30000);
|
|
187
|
+
if (!graphqlUp) {
|
|
188
|
+
this.log('warning: GraphQL port 9125 did not come up in time');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
134
192
|
// await this.__readyLaunchedPromise;
|
|
135
193
|
|
|
136
194
|
return this;
|
|
137
195
|
}
|
|
138
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Kill the child `sui start` process if it is running.
|
|
199
|
+
* @returns {Promise<void>}
|
|
200
|
+
*/
|
|
139
201
|
async stop() {
|
|
140
202
|
if (this._child) {
|
|
141
203
|
await this._child.kill();
|