suidouble 2.5.0 → 2.17.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/.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 +143 -15
- package/lib/SuiInBrowserAdapter.js +185 -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 +189 -0
- package/types/lib/SuiInBrowserAdapter.d.ts +171 -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
package/lib/SuiMaster.js
CHANGED
|
@@ -20,25 +20,28 @@ import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* @
|
|
23
|
+
* @import { SuiClientTypes } from "@mysten/sui/client"
|
|
24
|
+
*
|
|
25
|
+
* @typedef {import("@mysten/sui/grpc").SuiGrpcClient} SuiGrpcClient
|
|
26
|
+
* @typedef {import("@mysten/sui/graphql").SuiGraphQLClient} SuiGraphQLClient
|
|
24
27
|
* @typedef {import("@mysten/sui/cryptography").Signer} SuiSigner
|
|
25
28
|
* @typedef {import("@mysten/sui/cryptography").Keypair} SuiKeypair
|
|
26
|
-
* @typedef {import("
|
|
29
|
+
* @typedef {import("./SuiInBrowser.js").default} SuiInBrowser
|
|
27
30
|
*/
|
|
28
31
|
|
|
29
32
|
export default class SuiMaster extends SuiCommonMethods {
|
|
30
33
|
/**
|
|
31
34
|
* SuiMaster constructor
|
|
32
35
|
* @param {Object} params - Initialization parameters
|
|
33
|
-
* @param {
|
|
34
|
-
* @param {
|
|
35
|
-
* @param {
|
|
36
|
-
* @param {
|
|
37
|
-
* @param {
|
|
38
|
-
* @param {
|
|
39
|
-
* @param {
|
|
40
|
-
* @param {
|
|
41
|
-
* @param {
|
|
36
|
+
* @param {SuiSigner|SuiInBrowser} [params.signer] - instance of Sui SDK Signer (like Keypair) or SuiInBrowser for browser wallet signing
|
|
37
|
+
* @param {SuiKeypair} [params.keypair] - instance of Sui SDK Keypair
|
|
38
|
+
* @param {string} [params.privateKey] - private key in Sui format, starting with "suiprivkey1"
|
|
39
|
+
* @param {boolean} [params.debug] - enable debug mode
|
|
40
|
+
* @param {string} [params.phrase] - mnemonic phrase to derive keypair from
|
|
41
|
+
* @param {string} [params.keypairAlgo] - keypair algorithm to use with mnemonic phrase. One of 'ed25519' (default), 'secp256k1', 'secp256r1'
|
|
42
|
+
* @param {number} [params.accountIndex] - index of account to derive from mnemonic phrase. Default is 0
|
|
43
|
+
* @param {string} [params.as] - pseudo-random address generation seed string
|
|
44
|
+
* @param {SuiGrpcClient|string} [params.client] - instance of SuiGrpcClient or chain name to make SuiGrpcClient connected to it (like 'local', 'devnet', 'testnet', 'mainnet')
|
|
42
45
|
*/
|
|
43
46
|
constructor(params) {
|
|
44
47
|
super(params);
|
|
@@ -47,7 +50,7 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
47
50
|
SuiMaster.instancesCount++;
|
|
48
51
|
this._instanceN = SuiMaster.instancesCount;
|
|
49
52
|
|
|
50
|
-
/** @type {SuiSigner} */
|
|
53
|
+
/** @type {SuiSigner|SuiInBrowser|null} */
|
|
51
54
|
this._signer = null;
|
|
52
55
|
/** @type {SuiKeypair} */
|
|
53
56
|
this._keypair = null;
|
|
@@ -57,8 +60,8 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
57
60
|
|
|
58
61
|
if (params.signer) {
|
|
59
62
|
this._signer = params.signer;
|
|
60
|
-
if (this._signer && this._signer.connectedAddress) {
|
|
61
|
-
this._address = this._signer.connectedAddress;
|
|
63
|
+
if (this._signer && /** @type {any} */ (this._signer).connectedAddress) {
|
|
64
|
+
this._address = /** @type {any} */ (this._signer).connectedAddress;
|
|
62
65
|
}
|
|
63
66
|
} else if (params.keypair) {
|
|
64
67
|
this._keypair = params.keypair;
|
|
@@ -102,19 +105,24 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
this.log('
|
|
108
|
+
this.log('going to use keypair of', this._keypair.getPublicKey().toSuiAddress());
|
|
106
109
|
} else if (params.as) {
|
|
107
110
|
// generate pseudo-random keypair
|
|
108
111
|
this._keypair = SuiPseudoRandomAddress.stringToKeyPair(params.as);
|
|
109
112
|
|
|
110
|
-
this.log('
|
|
113
|
+
this.log('going to use keypair of', this._keypair.getPublicKey().toSuiAddress());
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
|
|
114
|
-
/** @type {
|
|
117
|
+
/** @type {SuiGrpcClient} */
|
|
115
118
|
this._client = SuiUtils.normalizeClient(params.client);
|
|
119
|
+
/**
|
|
120
|
+
* Lazy — do not read directly, use the `graphqlClient` getter so it's built on first access.
|
|
121
|
+
* @type {?SuiGraphQLClient}
|
|
122
|
+
*/
|
|
123
|
+
this._graphqlClient = null;
|
|
116
124
|
/** @type {string} */
|
|
117
|
-
this._providerName = this._client
|
|
125
|
+
this._providerName = this._client.network;
|
|
118
126
|
|
|
119
127
|
if (!this._client) {
|
|
120
128
|
throw new Error('Can not do anything without Sui client. Set params.client at least to `local`, so we can make default one for the chain');
|
|
@@ -140,6 +148,7 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
140
148
|
});
|
|
141
149
|
}
|
|
142
150
|
|
|
151
|
+
/** @returns {typeof SuiUtils} static SuiUtils helper class */
|
|
143
152
|
get utils() {
|
|
144
153
|
return SuiUtils;
|
|
145
154
|
}
|
|
@@ -153,14 +162,17 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
153
162
|
return this._suiCoins;
|
|
154
163
|
}
|
|
155
164
|
|
|
165
|
+
/** @returns {bigint} `1_000_000_000n` — number of MIST per SUI */
|
|
156
166
|
get MIST_PER_SUI() {
|
|
157
167
|
return BigInt(MIST_PER_SUI);
|
|
158
168
|
}
|
|
159
169
|
|
|
170
|
+
/** @returns {typeof Transaction} SDK `Transaction` class, exposed to avoid extra imports at call sites */
|
|
160
171
|
get Transaction() {
|
|
161
172
|
return Transaction;
|
|
162
173
|
}
|
|
163
174
|
|
|
175
|
+
/** @returns {typeof Commands} SDK `TransactionCommands` class, exposed to avoid extra imports at call sites */
|
|
164
176
|
get Commands() {
|
|
165
177
|
return Commands;
|
|
166
178
|
}
|
|
@@ -189,27 +201,39 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
189
201
|
get SuiPaginatedResponse() {
|
|
190
202
|
return SuiPaginatedResponse;
|
|
191
203
|
}
|
|
192
|
-
/**
|
|
193
|
-
|
|
194
204
|
/**
|
|
195
205
|
* Storage storing all objects interacted by this suiMaster
|
|
196
|
-
*
|
|
206
|
+
*
|
|
197
207
|
* @type {SuiMemoryObjectStorage}
|
|
198
208
|
*/
|
|
199
209
|
get objectStorage() {
|
|
200
210
|
return this._objectStorage;
|
|
201
211
|
}
|
|
202
212
|
|
|
213
|
+
/** @returns {number} monotonically increasing instance counter, useful for differentiating logs */
|
|
203
214
|
get instanceN() {
|
|
204
215
|
return this._instanceN;
|
|
205
216
|
}
|
|
206
217
|
|
|
207
218
|
static instancesCount = 0;
|
|
208
219
|
|
|
220
|
+
/** @returns {SuiGrpcClient} gRPC client for the connected chain */
|
|
209
221
|
get client() {
|
|
210
222
|
return this._client;
|
|
211
223
|
}
|
|
212
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Lazily-built SuiGraphQLClient that reuses the network of this suiMaster's gRPC client.
|
|
227
|
+
* @returns {SuiGraphQLClient}
|
|
228
|
+
*/
|
|
229
|
+
get graphqlClient() {
|
|
230
|
+
if (!this._graphqlClient) {
|
|
231
|
+
this._graphqlClient = SuiUtils.suiGraphQLClientFromGrpc(this._client);
|
|
232
|
+
}
|
|
233
|
+
return this._graphqlClient;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** @returns {string} connected chain identifier, e.g. `'mainnet'` or `'localnet'` */
|
|
213
237
|
get connectedChain() {
|
|
214
238
|
return this._providerName;
|
|
215
239
|
}
|
|
@@ -219,6 +243,7 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
219
243
|
* @returns {boolean} is on mainnet
|
|
220
244
|
*/
|
|
221
245
|
get onMainnet() {
|
|
246
|
+
// @todo:
|
|
222
247
|
const provider = this._providerName.split('sui:').join('').toLowerCase();
|
|
223
248
|
if (provider === 'mainnet') {
|
|
224
249
|
return true;
|
|
@@ -227,10 +252,12 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
227
252
|
return false;
|
|
228
253
|
}
|
|
229
254
|
|
|
255
|
+
/** @returns {?string} connected Sui address, or null in read-only mode before `initialize()` */
|
|
230
256
|
get address() {
|
|
231
257
|
return this._address;
|
|
232
258
|
}
|
|
233
259
|
|
|
260
|
+
/** @returns {SuiSigner|SuiInBrowser|null} active signer/keypair, or null in read-only mode */
|
|
234
261
|
get signer() {
|
|
235
262
|
return this._signer;
|
|
236
263
|
}
|
|
@@ -276,11 +303,20 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
276
303
|
return suiPackage;
|
|
277
304
|
}
|
|
278
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Ensure `initialize()` has run, then return the gRPC client.
|
|
308
|
+
* @returns {Promise<SuiGrpcClient>}
|
|
309
|
+
*/
|
|
279
310
|
async getClient() {
|
|
280
311
|
await this.initialize();
|
|
281
312
|
return this._client;
|
|
282
313
|
}
|
|
283
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Resolve the connected address from the signer/keypair (if any) and mark the instance as
|
|
317
|
+
* ready. Idempotent — subsequent calls return immediately.
|
|
318
|
+
* @returns {Promise<boolean>}
|
|
319
|
+
*/
|
|
284
320
|
async initialize() {
|
|
285
321
|
if (this._initialized) {
|
|
286
322
|
return true;
|
|
@@ -295,12 +331,13 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
295
331
|
}
|
|
296
332
|
|
|
297
333
|
if (this._signer) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
334
|
+
const s = /** @type {any} */ (this._signer);
|
|
335
|
+
if (s.toSuiAddress) {
|
|
336
|
+
this._address = s.toSuiAddress(); // after Sui's refactor Keypair's method
|
|
337
|
+
} else if (s.connectedAddress) {
|
|
338
|
+
this._address = s.connectedAddress;
|
|
302
339
|
} else {
|
|
303
|
-
this._address = await
|
|
340
|
+
this._address = await s.getAddress(); // old method
|
|
304
341
|
}
|
|
305
342
|
|
|
306
343
|
this.log('initialized. connected as', this._address);
|
|
@@ -313,118 +350,251 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
313
350
|
}
|
|
314
351
|
|
|
315
352
|
/**
|
|
316
|
-
* Return the
|
|
317
|
-
*
|
|
318
|
-
* @
|
|
319
|
-
* @param {'at' | 'dot' | undefined} params.format
|
|
320
|
-
*
|
|
321
|
-
* @returns {string | null}
|
|
353
|
+
* Return the primary Name Service name for the connected wallet address, or null if none.
|
|
354
|
+
* Uses `client.core.defaultNameServiceName` (v2 gRPC).
|
|
355
|
+
* @returns {Promise<string | null>}
|
|
322
356
|
*/
|
|
323
|
-
async
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
357
|
+
async defaultNameServiceName() {
|
|
358
|
+
if (!this._address) {
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
const resp = await this._client.core.defaultNameServiceName({
|
|
364
|
+
address: this._address,
|
|
365
|
+
});
|
|
366
|
+
return resp?.data?.name ?? null;
|
|
367
|
+
} catch (e) {
|
|
368
|
+
return null;
|
|
327
369
|
}
|
|
328
|
-
return null;
|
|
329
370
|
}
|
|
330
371
|
|
|
331
372
|
/**
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
373
|
+
* Fetch many on-chain objects. The v2 client batches internally (50 per request).
|
|
374
|
+
*
|
|
375
|
+
* Per-item behavior:
|
|
376
|
+
* - if a `SuiObject` is passed in, it is updated in place via `replaceWithSuiObjectIfNeeded`
|
|
377
|
+
* and returned at its original index in the output array (same instance reference).
|
|
378
|
+
* - if a string id is passed in, a freshly-constructed `SuiObject` is returned at that index.
|
|
379
|
+
* - if an item couldn't be matched in the response, the original (or a new bare) SuiObject is
|
|
380
|
+
* still placed at that index — no nulls, no skipped slots.
|
|
381
|
+
*
|
|
382
|
+
* Errors-in-results entries from the client are skipped silently. Returns `[]` for empty input.
|
|
383
|
+
*
|
|
384
|
+
* @param {Array<string | SuiObject>} idsOrSuiObjects - mix of object ids and/or SuiObject instances
|
|
385
|
+
* @param {SuiClientTypes.ObjectInclude} [include] - fields to include; defaults to content/json/display
|
|
386
|
+
* @returns {Promise<SuiObject[]>} array aligned with input order
|
|
338
387
|
*/
|
|
339
|
-
async
|
|
340
|
-
if (!
|
|
388
|
+
async getObjects(idsOrSuiObjects, include) {
|
|
389
|
+
if (!Array.isArray(idsOrSuiObjects) || idsOrSuiObjects.length === 0) {
|
|
341
390
|
return [];
|
|
342
391
|
}
|
|
343
392
|
|
|
344
|
-
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
});
|
|
349
|
-
if (resp && resp.data) {
|
|
350
|
-
return resp.data;
|
|
393
|
+
const slots = idsOrSuiObjects.map((item) => {
|
|
394
|
+
const a = /** @type {any} */ (item);
|
|
395
|
+
if (a && a.address) {
|
|
396
|
+
return { id: a.address, obj: /** @type {SuiObject} */ (a) };
|
|
351
397
|
}
|
|
398
|
+
const id = '' + a;
|
|
399
|
+
return {
|
|
400
|
+
id,
|
|
401
|
+
obj: new SuiObject({ suiMaster: this, debug: this._debug, id }),
|
|
402
|
+
};
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
const uniqueIds = [];
|
|
406
|
+
for (const s of slots) {
|
|
407
|
+
if (s.id && uniqueIds.indexOf(s.id) === -1) uniqueIds.push(s.id);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
let raws = [];
|
|
411
|
+
try {
|
|
412
|
+
const { objects } = await this._client.getObjects({
|
|
413
|
+
objectIds: uniqueIds,
|
|
414
|
+
include: include || { content: true, json: true, display: true },
|
|
415
|
+
});
|
|
416
|
+
raws = (objects || []).filter((entry) => entry && !(entry instanceof Error) && entry.objectId);
|
|
352
417
|
} catch (e) {
|
|
353
|
-
|
|
418
|
+
console.error(e);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
for (const slot of slots) {
|
|
422
|
+
const raw = raws.find((r) => slot.obj.idEquals(r.objectId));
|
|
423
|
+
if (!raw) continue;
|
|
424
|
+
const fresh = new SuiObject({ suiMaster: this, debug: this._debug, raw });
|
|
425
|
+
slot.obj.replaceWithSuiObjectIfNeeded(fresh);
|
|
354
426
|
}
|
|
427
|
+
|
|
428
|
+
return slots.map((s) => s.obj);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Fetch a single on-chain object and return it wrapped in a SuiObject.
|
|
433
|
+
*
|
|
434
|
+
* @param {string | SuiObject} idOrSuiObject - object id, or a SuiObject instance whose `.address` will be used
|
|
435
|
+
* @param {SuiClientTypes.ObjectInclude} [include] - fields to include; defaults to content/json/display
|
|
436
|
+
* @returns {Promise<?SuiObject>} SuiObject populated from the api, or null if not found
|
|
437
|
+
*/
|
|
438
|
+
async getObject(idOrSuiObject, include) {
|
|
439
|
+
const a = /** @type {any} */ (idOrSuiObject);
|
|
440
|
+
const objectId = (a && a.address) ? a.address : a;
|
|
441
|
+
|
|
442
|
+
const { object } = await this._client.getObject({
|
|
443
|
+
objectId,
|
|
444
|
+
include: include || { content: true, json: true, display: true },
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
if (!object) return null;
|
|
448
|
+
|
|
449
|
+
return new SuiObject({
|
|
450
|
+
suiMaster: this,
|
|
451
|
+
debug: this._debug,
|
|
452
|
+
raw: object,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* List objects owned by an address. Picks the right backend based on `input.type`:
|
|
458
|
+
*
|
|
459
|
+
* - no type / full struct type (`pkg::mod::Struct[<...>]`) → gRPC `listOwnedObjects` (fast path)
|
|
460
|
+
* - package (`pkg`) or module (`pkg::mod`) filter → GraphQL `address.objects` (gRPC can't parse those)
|
|
461
|
+
*
|
|
462
|
+
* @param {SuiClientTypes.ListOwnedObjectsOptions<SuiClientTypes.ObjectInclude>} input
|
|
463
|
+
* @param {Object} [extra]
|
|
464
|
+
* @param {string} [extra.order]
|
|
465
|
+
* @returns {Promise<SuiPaginatedResponse<SuiObject>>}
|
|
466
|
+
*/
|
|
467
|
+
async getOwnedObjects(input, extra = {}) {
|
|
468
|
+
const useGraphql = SuiMaster._typeNeedsGraphqlOwned(input && input.type);
|
|
469
|
+
|
|
470
|
+
/** @type {SuiPaginatedResponse<SuiObject>} */
|
|
471
|
+
const paginatedResponse = new SuiPaginatedResponse({
|
|
472
|
+
suiMaster: this,
|
|
473
|
+
method: useGraphql ? 'graphqlOwnedObjects' : 'listOwnedObjects',
|
|
474
|
+
params: input,
|
|
475
|
+
order: extra.order,
|
|
476
|
+
});
|
|
477
|
+
await paginatedResponse.fetch();
|
|
478
|
+
return paginatedResponse;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* gRPC `ListOwnedObjects.objectType` only accepts a full struct type. A bare package address or
|
|
483
|
+
* `package::module` string needs the GraphQL path.
|
|
484
|
+
* @param {?string} type
|
|
485
|
+
* @returns {boolean}
|
|
486
|
+
*/
|
|
487
|
+
static _typeNeedsGraphqlOwned(type) {
|
|
488
|
+
if (!type) return false;
|
|
489
|
+
// Strip generic type params (content inside the outermost `<…>`) before counting `::` segments.
|
|
490
|
+
const bare = type.split('<')[0];
|
|
491
|
+
const segments = bare.split('::');
|
|
492
|
+
return segments.length < 3;
|
|
355
493
|
}
|
|
356
494
|
|
|
357
495
|
/**
|
|
358
496
|
* Sign and execute a transaction on the Sui blockchain.
|
|
359
497
|
* Uses the connected keypair or signer or browser extension wallet adapter to sign the transaction before submitting it.
|
|
360
498
|
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
* @param {boolean} [params.options.showObjectChanges] - Whether to include object changes
|
|
367
|
-
* @param {boolean} [params.options.showBalanceChanges] - Whether to include balance changes
|
|
368
|
-
* @param {boolean} [params.options.showInput] - Whether to include transaction input
|
|
369
|
-
* @param {boolean} [params.options.showType] - Whether to include object types
|
|
370
|
-
* @param {boolean} [params.options.showContent] - Whether to include object content
|
|
371
|
-
* @param {boolean} [params.options.showOwner] - Whether to include object ownership info
|
|
372
|
-
* @param {boolean} [params.options.showDisplay] - Whether to include display data
|
|
373
|
-
* @param {string} [params.requestType] - Request type, use 'WaitForLocalExecution' to wait for transaction finality
|
|
374
|
-
* @param {string} [params.chain] - Chain identifier (defaults to connected chain, e.g., 'sui:mainnet')
|
|
375
|
-
* @param {Object} [params.account] - Account info (defaults to connected address)
|
|
376
|
-
* @param {string} [params.account.address] - The account address
|
|
499
|
+
* Throws on two failure paths, both with a normalised `executionError` + `digest` shape:
|
|
500
|
+
*
|
|
501
|
+
* 1. **`SimulationError`** (SDK, pre-submission) — thrown during `tx.build()` / gas selection
|
|
502
|
+
* when the SDK simulates the transaction and detects an abort before even submitting.
|
|
503
|
+
* `executionError` is set by the SDK; `digest` is `null` (no submission happened).
|
|
377
504
|
*
|
|
378
|
-
*
|
|
505
|
+
* 2. **`FailedTransaction`** (on-chain) — the transaction was submitted and executed but aborted.
|
|
506
|
+
* Both `executionError` and `digest` are set.
|
|
379
507
|
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
508
|
+
* In both cases the error has:
|
|
509
|
+
* - `message` — human-readable failure reason
|
|
510
|
+
* - `executionError` — `SuiClientTypes.ExecutionError`:
|
|
511
|
+
* `{ message, command?, MoveAbort?, SizeError?, CommandArgumentError?, TypeArgumentError?, ... }`
|
|
512
|
+
* - `digest` — transaction digest, or `null` for pre-submission failures
|
|
383
513
|
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
* options: {
|
|
388
|
-
* showEffects: true,
|
|
389
|
-
* showEvents: true,
|
|
390
|
-
* showObjectChanges: true,
|
|
391
|
-
* },
|
|
392
|
-
* });
|
|
514
|
+
* @param {{ transaction: Transaction, include?: SuiClientTypes.TransactionInclude, requestType?: string }} params
|
|
515
|
+
* @returns {Promise<SuiTransaction>}
|
|
516
|
+
* @throws {Error & { executionError: SuiClientTypes.ExecutionError, digest: ?string }} if the transaction fails on-chain
|
|
393
517
|
*/
|
|
394
518
|
async signAndExecuteTransaction(params) {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
if (!params.account) {
|
|
400
|
-
params.account = { address: this._address };
|
|
401
|
-
}
|
|
519
|
+
let signAndExecuteTransactionResponse;
|
|
520
|
+
try {
|
|
521
|
+
const tx = /** @type {Transaction} */ (params.transaction);
|
|
402
522
|
|
|
403
|
-
|
|
404
|
-
let txResults = null;
|
|
405
|
-
if (this._keypair) {
|
|
406
|
-
params.signer = this._keypair;
|
|
407
|
-
txResults = await this._client.signAndExecuteTransaction(params);
|
|
408
|
-
} else if (this._signer) {
|
|
409
|
-
txResults = await this._signer.signAndExecuteTransaction(params);
|
|
410
|
-
}
|
|
523
|
+
tx.setSenderIfNotSet(this.address);
|
|
411
524
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
525
|
+
const signerAny = /** @type {any} */ (this._signer);
|
|
526
|
+
|
|
527
|
+
/** @type {?Uint8Array} */
|
|
528
|
+
let transactionBytes = null;
|
|
529
|
+
/** @type {?string} */
|
|
530
|
+
let signature = null;
|
|
531
|
+
|
|
532
|
+
if (signerAny.activeAdapter) {
|
|
533
|
+
// We are using browser adapter — build first to resolve intents,
|
|
534
|
+
// then sign
|
|
535
|
+
const signer = /** @type {SuiInBrowser} */ (signerAny);
|
|
536
|
+
|
|
537
|
+
transactionBytes = await tx.build({
|
|
538
|
+
client: this._client,
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
// Sign TX with browser extension
|
|
542
|
+
({ signature } = await signer.signTransaction({
|
|
543
|
+
...params,
|
|
544
|
+
}));
|
|
545
|
+
} else {
|
|
546
|
+
const signer = /** @type {SuiSigner} */ (signerAny);
|
|
547
|
+
|
|
548
|
+
transactionBytes = await tx.build({
|
|
549
|
+
client: this._client,
|
|
417
550
|
});
|
|
418
|
-
|
|
419
|
-
|
|
551
|
+
|
|
552
|
+
({ signature } = await signer.signTransaction(transactionBytes));
|
|
420
553
|
}
|
|
554
|
+
|
|
555
|
+
signAndExecuteTransactionResponse = await this._client.executeTransaction({
|
|
556
|
+
transaction: transactionBytes,
|
|
557
|
+
signatures: [signature],
|
|
558
|
+
include: params.include ? params.include : {},
|
|
559
|
+
});
|
|
421
560
|
} catch (e) {
|
|
422
|
-
|
|
561
|
+
// SimulationError — SDK aborted during pre-submission simulation (tx.build / gas selection).
|
|
562
|
+
// Already has executionError; add digest = null for a consistent error shape.
|
|
563
|
+
if (e && e.executionError !== undefined) {
|
|
564
|
+
e.digest = e.digest ?? null;
|
|
565
|
+
}
|
|
566
|
+
throw e;
|
|
423
567
|
}
|
|
424
568
|
|
|
425
|
-
|
|
569
|
+
if (signAndExecuteTransactionResponse.$kind === 'FailedTransaction') {
|
|
570
|
+
const executionError = signAndExecuteTransactionResponse.FailedTransaction?.status?.error;
|
|
571
|
+
const err = /** @type {Error & { executionError: any, digest: any }} */ (new Error(executionError?.message ?? 'Transaction failed'));
|
|
572
|
+
err.executionError = executionError ?? null; // full SuiClientTypes.ExecutionError: { message, command?, MoveAbort?, ... }
|
|
573
|
+
err.digest = signAndExecuteTransactionResponse.FailedTransaction?.digest ?? null;
|
|
574
|
+
throw err;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const suiTransaction = new SuiTransaction({
|
|
578
|
+
suiMaster: this,
|
|
579
|
+
data: signAndExecuteTransactionResponse.Transaction,
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
if (params.requestType && params.requestType == 'WaitForLocalExecution') {
|
|
583
|
+
await suiTransaction.waitForTransaction(
|
|
584
|
+
/** @type {SuiClientTypes.WaitForTransactionOptions<{}>} */ (
|
|
585
|
+
params.include ? { include: params.include } : {}
|
|
586
|
+
)
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return suiTransaction;
|
|
426
591
|
}
|
|
427
592
|
|
|
593
|
+
/**
|
|
594
|
+
* Request SUI tokens from the faucet for the connected address.
|
|
595
|
+
* Only works on localnet, devnet, and testnet.
|
|
596
|
+
* @returns {Promise<bigint>} total MIST received across all coin objects
|
|
597
|
+
*/
|
|
428
598
|
async requestSuiFromFaucet() {
|
|
429
599
|
await this.initialize();
|
|
430
600
|
let amount = BigInt(0);
|
|
@@ -433,7 +603,7 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
433
603
|
if (provider === "mainnet") {
|
|
434
604
|
this.log(`no faucet on ${provider}`);
|
|
435
605
|
} else {
|
|
436
|
-
const faucetHost = getFaucetHost(provider);
|
|
606
|
+
const faucetHost = getFaucetHost(/** @type {any} */ (provider));
|
|
437
607
|
this.log(`requesting sui from faucet... ${faucetHost}`);
|
|
438
608
|
const requested = await requestSuiFromFaucetV2({
|
|
439
609
|
host: faucetHost,
|
|
@@ -458,9 +628,9 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
458
628
|
/**
|
|
459
629
|
* Query the balance of specific coinType for an owner. If owner == null, returns balance of connected address owner
|
|
460
630
|
*
|
|
461
|
-
* @param {
|
|
462
|
-
* @param {
|
|
463
|
-
* @returns
|
|
631
|
+
* @param {string} [coinType='0x2::sui::SUI']
|
|
632
|
+
* @param {?string} [owner] - defaults to the connected address
|
|
633
|
+
* @returns {Promise<bigint>}
|
|
464
634
|
*/
|
|
465
635
|
async getBalance(coinType = '0x2::sui::SUI', owner = null) {
|
|
466
636
|
await this.initialize();
|
|
@@ -474,20 +644,34 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
474
644
|
return await (suiCoin.getBalance(queryingBalanceOf));
|
|
475
645
|
}
|
|
476
646
|
|
|
647
|
+
/**
|
|
648
|
+
* Fetch events via the GraphQL `events` query. Lowest-level entry point —
|
|
649
|
+
* `SuiPackage.fetchEvents` and `SuiPackageModule.fetchEvents` both delegate here.
|
|
650
|
+
*
|
|
651
|
+
* The filter is a GraphQL `EventFilter`:
|
|
652
|
+
* - `module` : "<package>" or "<package>::<module>"
|
|
653
|
+
* - `type` : "<package>::<module>::<EventName>" (or a prefix thereof)
|
|
654
|
+
* - `sender` : SuiAddress
|
|
655
|
+
* - `atCheckpoint` / `afterCheckpoint` / `beforeCheckpoint` : UInt53
|
|
656
|
+
*
|
|
657
|
+
* @param {Object} [params]
|
|
658
|
+
* @param {Object} [params.filter] - GraphQL EventFilter
|
|
659
|
+
* @param {number} [params.limit=50]
|
|
660
|
+
* @param {string} [params.order] - Passed through to the underlying paginated response
|
|
661
|
+
* @returns {Promise<SuiPaginatedResponse<SuiEvent>>}
|
|
662
|
+
*/
|
|
477
663
|
async fetchEvents(params = {}) {
|
|
478
|
-
let query = params.query;
|
|
479
|
-
|
|
480
664
|
const queryParams = {
|
|
481
|
-
|
|
482
|
-
query: query,
|
|
665
|
+
filter: params.filter || {},
|
|
483
666
|
limit: params.limit || 50,
|
|
484
667
|
};
|
|
485
668
|
|
|
669
|
+
/** @type {SuiPaginatedResponse<SuiEvent>} */
|
|
486
670
|
const paginatedResponse = new SuiPaginatedResponse({
|
|
487
671
|
debug: this._debug,
|
|
488
672
|
suiMaster: this,
|
|
489
673
|
params: queryParams,
|
|
490
|
-
method: '
|
|
674
|
+
method: 'graphqlEvents',
|
|
491
675
|
order: params.order,
|
|
492
676
|
});
|
|
493
677
|
|
|
@@ -496,36 +680,48 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
496
680
|
return paginatedResponse;
|
|
497
681
|
}
|
|
498
682
|
|
|
683
|
+
/**
|
|
684
|
+
* Fetch transactions via the GraphQL root `transactions(filter: TransactionFilter)` query.
|
|
685
|
+
*
|
|
686
|
+
* Accepts either a direct `params.filter` (GraphQL `TransactionFilter` shape) or the legacy
|
|
687
|
+
* convenience fields (`fromAddress`, `affectedAddress`, `affectedObject`, `function`, `atCheckpoint`,
|
|
688
|
+
* `afterCheckpoint`, `beforeCheckpoint`, `kind`) which get translated to the corresponding
|
|
689
|
+
* `TransactionFilter` fields.
|
|
690
|
+
*
|
|
691
|
+
* @param {Object} [params]
|
|
692
|
+
* @param {Object} [params.filter] - raw TransactionFilter; overrides the convenience fields if set
|
|
693
|
+
* @param {?string} [params.fromAddress]
|
|
694
|
+
* @param {?string} [params.affectedAddress]
|
|
695
|
+
* @param {?string} [params.affectedObject]
|
|
696
|
+
* @param {?string} [params.function]
|
|
697
|
+
* @param {?number} [params.atCheckpoint]
|
|
698
|
+
* @param {?number} [params.afterCheckpoint]
|
|
699
|
+
* @param {?number} [params.beforeCheckpoint]
|
|
700
|
+
* @param {?string} [params.kind]
|
|
701
|
+
* @param {number} [params.limit=50]
|
|
702
|
+
* @param {string} [params.order]
|
|
703
|
+
* @returns {Promise<SuiPaginatedResponse<SuiTransaction>>}
|
|
704
|
+
*/
|
|
499
705
|
async fetchTransactions(params = {}) {
|
|
500
|
-
let filter =
|
|
501
|
-
if (
|
|
502
|
-
filter
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
filter = params.
|
|
706
|
+
let filter = params.filter;
|
|
707
|
+
if (!filter) {
|
|
708
|
+
filter = {};
|
|
709
|
+
if (params.fromAddress) filter.sentAddress = params.fromAddress;
|
|
710
|
+
if (params.affectedAddress) filter.affectedAddress = params.affectedAddress;
|
|
711
|
+
if (params.affectedObject) filter.affectedObject = params.affectedObject;
|
|
712
|
+
if (params.function) filter.function = params.function;
|
|
713
|
+
if (params.atCheckpoint !== undefined) filter.atCheckpoint = params.atCheckpoint;
|
|
714
|
+
if (params.afterCheckpoint !== undefined) filter.afterCheckpoint = params.afterCheckpoint;
|
|
715
|
+
if (params.beforeCheckpoint !== undefined) filter.beforeCheckpoint = params.beforeCheckpoint;
|
|
716
|
+
if (params.kind) filter.kind = params.kind;
|
|
506
717
|
}
|
|
507
718
|
|
|
508
|
-
|
|
509
|
-
descending_order: false,
|
|
510
|
-
filter: filter,
|
|
511
|
-
options: {
|
|
512
|
-
// showInput: true,
|
|
513
|
-
showEffects: true,
|
|
514
|
-
// showEvents: true,
|
|
515
|
-
// showObjectChanges: true,
|
|
516
|
-
// showBalanceChanges: true,
|
|
517
|
-
// showType: true,
|
|
518
|
-
// showContent: true,
|
|
519
|
-
// showOwner: true,
|
|
520
|
-
// showDisplay: true,
|
|
521
|
-
},
|
|
522
|
-
limit: params.limit || 50,
|
|
523
|
-
};
|
|
719
|
+
/** @type {SuiPaginatedResponse<SuiTransaction>} */
|
|
524
720
|
const paginatedResponse = new SuiPaginatedResponse({
|
|
525
721
|
debug: this._debug,
|
|
526
722
|
suiMaster: this,
|
|
527
|
-
params:
|
|
528
|
-
method: '
|
|
723
|
+
params: { filter, limit: params.limit || 50 },
|
|
724
|
+
method: 'graphqlTransactions',
|
|
529
725
|
order: params.order,
|
|
530
726
|
});
|
|
531
727
|
|