suidouble 1.45.2 → 2.16.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.
Files changed (59) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/README.md +222 -131
  3. package/index.js +1 -3
  4. package/lib/SuiCliCommands.js +18 -25
  5. package/lib/SuiCoin.js +86 -138
  6. package/lib/SuiCoins.js +70 -31
  7. package/lib/SuiCommonMethods.js +40 -3
  8. package/lib/SuiEvent.js +54 -6
  9. package/lib/SuiInBrowser.js +145 -46
  10. package/lib/SuiInBrowserAdapter.js +164 -37
  11. package/lib/SuiLocalTestValidator.js +78 -25
  12. package/lib/SuiMaster.js +351 -126
  13. package/lib/SuiMemoryObjectStorage.js +66 -73
  14. package/lib/SuiObject.js +128 -153
  15. package/lib/SuiPackage.js +292 -187
  16. package/lib/SuiPackageModule.js +176 -221
  17. package/lib/SuiPaginatedResponse.js +288 -25
  18. package/lib/SuiPseudoRandomAddress.js +29 -2
  19. package/lib/SuiTransaction.js +115 -70
  20. package/lib/SuiUtils.js +179 -124
  21. package/package.json +30 -14
  22. package/test/build_modules.test.js +41 -0
  23. package/test/coins.test.js +17 -16
  24. package/test/custom_transaction.test.js +167 -0
  25. package/test/event_listeners.test.js +171 -0
  26. package/test/failed_transaction.test.js +184 -0
  27. package/test/name_service.test.js +28 -0
  28. package/test/owned_objects.test.js +148 -0
  29. package/test/rpc.test.js +3 -6
  30. package/test/sui_in_browser.test.js +2 -2
  31. package/test/sui_master_basic.test.js +4 -5
  32. package/test/sui_master_onlocal.test.js +84 -22
  33. package/test/sui_object_properties.test.js +85 -0
  34. package/test/test_move_contracts/different_types/Move.lock +18 -21
  35. package/test/test_move_contracts/different_types/sources/different_types.move +12 -12
  36. package/test/test_move_contracts/suidouble_chat/Move.lock +18 -22
  37. package/test/test_move_contracts/suidouble_chat/sources/suidouble_chat.move +9 -8
  38. package/tsconfig.json +15 -0
  39. package/types/index.d.ts +15 -0
  40. package/types/lib/SuiCliCommands.d.ts +6 -0
  41. package/types/lib/SuiCoin.d.ts +183 -0
  42. package/types/lib/SuiCoins.d.ts +93 -0
  43. package/types/lib/SuiCommonMethods.d.ts +37 -0
  44. package/types/lib/SuiEvent.d.ts +95 -0
  45. package/types/lib/SuiInBrowser.d.ts +189 -0
  46. package/types/lib/SuiInBrowserAdapter.d.ts +167 -0
  47. package/types/lib/SuiLocalTestValidator.d.ts +92 -0
  48. package/types/lib/SuiMaster.d.ts +333 -0
  49. package/types/lib/SuiMemoryObjectStorage.d.ts +96 -0
  50. package/types/lib/SuiObject.d.ts +135 -0
  51. package/types/lib/SuiPackage.d.ts +233 -0
  52. package/types/lib/SuiPackageModule.d.ts +139 -0
  53. package/types/lib/SuiPaginatedResponse.d.ts +148 -0
  54. package/types/lib/SuiPseudoRandomAddress.d.ts +33 -0
  55. package/types/lib/SuiTransaction.d.ts +92 -0
  56. package/types/lib/SuiUtils.d.ts +152 -0
  57. package/types/lib/data/icons.d.ts +12 -0
  58. package/lib/SuiTestScenario.js +0 -169
  59. package/test/sui_test_scenario.test.js +0 -61
package/lib/SuiMaster.js CHANGED
@@ -13,32 +13,35 @@ import { MIST_PER_SUI } from '@mysten/sui/utils';
13
13
  import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
14
14
  import { Secp256k1Keypair } from '@mysten/sui/keypairs/secp256k1';
15
15
  import { Secp256r1Keypair } from '@mysten/sui/keypairs/secp256r1';
16
- import { requestSuiFromFaucetV0, getFaucetHost } from '@mysten/sui/faucet';
17
- import { Transaction, Commands } from '@mysten/sui/transactions';
16
+ import { requestSuiFromFaucetV2, getFaucetHost } from '@mysten/sui/faucet';
17
+ import { Transaction, TransactionCommands as Commands } from '@mysten/sui/transactions';
18
18
  import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';
19
19
 
20
20
 
21
21
 
22
22
  /**
23
- * @typedef {import("@mysten/sui/client").SuiClient} SuiClient
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("@mysten/sui/client").SuiTransactionBlockResponse} SuiTransactionBlockResponse
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 {?SuiSigner} params.signer - instance of Sui SDK Signer (like Keypair)
34
- * @param {?SuiKeypair} params.keypair - instance of Sui SDK Keypair
35
- * @param {?string} param.privateKey - private key in Sui format, starting with "suiprivkey1"
36
- * @param {?boolean} param.debug - enable debug mode
37
- * @param {?string} param.phrase - mnemonic phrase to derive keypair from
38
- * @param {?string} param.keypairAlgo - keypair algorithm to use with mnemonic phrase. One of 'ed25519' (default), 'secp256k1', 'secp256r1'
39
- * @param {?number} param.accountIndex - index of account to derive from mnemonic phrase. Default is 0
40
- * @param {?string} param.as - pseudo-random address generation seed string
41
- * @param {?SuiClient|string} params.client - instance of SuiClient or chain name to make SuiClient connected to it (like 'local', 'devnet', 'testnet', 'mainnet')
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,22 +60,14 @@ 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;
65
68
  } else if (params.privateKey) {
66
69
  const parsed = decodeSuiPrivateKey(params.privateKey);
67
- if (parsed && parsed.schema) {
68
- if (parsed.schema === 'ED25519') {
69
- this._keypair = Ed25519Keypair.fromSecretKey(parsed.secretKey);
70
- } else if (parsed.schema == 'Secp256k1') {
71
- this._keypair = Secp256k1Keypair.fromSecretKey(parsed.secretKey);
72
- } else if (parsed.schema == 'Secp256r1') {
73
- this._keypair = Secp256r1Keypair.fromSecretKey(parsed.secretKey);
74
- }
75
- } else if (parsed && parsed.scheme) {
70
+ if (parsed && parsed.scheme) {
76
71
  if (parsed.scheme === 'ED25519') {
77
72
  this._keypair = Ed25519Keypair.fromSecretKey(parsed.secretKey);
78
73
  } else if (parsed.scheme == 'Secp256k1') {
@@ -110,22 +105,27 @@ export default class SuiMaster extends SuiCommonMethods {
110
105
  }
111
106
  }
112
107
 
113
- this.log('goint to use keypair of', this._keypair.getPublicKey().toSuiAddress());
108
+ this.log('going to use keypair of', this._keypair.getPublicKey().toSuiAddress());
114
109
  } else if (params.as) {
115
110
  // generate pseudo-random keypair
116
111
  this._keypair = SuiPseudoRandomAddress.stringToKeyPair(params.as);
117
112
 
118
- this.log('goint to use keypair of', this._keypair.getPublicKey().toSuiAddress());
113
+ this.log('going to use keypair of', this._keypair.getPublicKey().toSuiAddress());
119
114
  }
120
115
 
121
116
 
122
- /** @type {SuiClient} */
117
+ /** @type {SuiGrpcClient} */
123
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;
124
124
  /** @type {string} */
125
- this._providerName = this._client ? this._client.providerName : null;
125
+ this._providerName = this._client.network;
126
126
 
127
127
  if (!this._client) {
128
- throw new Error('Can not do anything without SuiClient. Set params.client at least to `local`');
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');
129
129
  }
130
130
 
131
131
  // we are differient single instances of object storage by provider name (so we can separate like devnet-testnet entities if needed)
@@ -148,6 +148,7 @@ export default class SuiMaster extends SuiCommonMethods {
148
148
  });
149
149
  }
150
150
 
151
+ /** @returns {typeof SuiUtils} static SuiUtils helper class */
151
152
  get utils() {
152
153
  return SuiUtils;
153
154
  }
@@ -161,14 +162,17 @@ export default class SuiMaster extends SuiCommonMethods {
161
162
  return this._suiCoins;
162
163
  }
163
164
 
165
+ /** @returns {bigint} `1_000_000_000n` — number of MIST per SUI */
164
166
  get MIST_PER_SUI() {
165
167
  return BigInt(MIST_PER_SUI);
166
168
  }
167
169
 
170
+ /** @returns {typeof Transaction} SDK `Transaction` class, exposed to avoid extra imports at call sites */
168
171
  get Transaction() {
169
172
  return Transaction;
170
173
  }
171
174
 
175
+ /** @returns {typeof Commands} SDK `TransactionCommands` class, exposed to avoid extra imports at call sites */
172
176
  get Commands() {
173
177
  return Commands;
174
178
  }
@@ -197,27 +201,39 @@ export default class SuiMaster extends SuiCommonMethods {
197
201
  get SuiPaginatedResponse() {
198
202
  return SuiPaginatedResponse;
199
203
  }
200
- /**
201
-
202
204
  /**
203
205
  * Storage storing all objects interacted by this suiMaster
204
- *
206
+ *
205
207
  * @type {SuiMemoryObjectStorage}
206
208
  */
207
209
  get objectStorage() {
208
210
  return this._objectStorage;
209
211
  }
210
212
 
213
+ /** @returns {number} monotonically increasing instance counter, useful for differentiating logs */
211
214
  get instanceN() {
212
215
  return this._instanceN;
213
216
  }
214
217
 
215
218
  static instancesCount = 0;
216
219
 
220
+ /** @returns {SuiGrpcClient} gRPC client for the connected chain */
217
221
  get client() {
218
222
  return this._client;
219
223
  }
220
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'` */
221
237
  get connectedChain() {
222
238
  return this._providerName;
223
239
  }
@@ -227,6 +243,7 @@ export default class SuiMaster extends SuiCommonMethods {
227
243
  * @returns {boolean} is on mainnet
228
244
  */
229
245
  get onMainnet() {
246
+ // @todo:
230
247
  const provider = this._providerName.split('sui:').join('').toLowerCase();
231
248
  if (provider === 'mainnet') {
232
249
  return true;
@@ -235,10 +252,12 @@ export default class SuiMaster extends SuiCommonMethods {
235
252
  return false;
236
253
  }
237
254
 
255
+ /** @returns {?string} connected Sui address, or null in read-only mode before `initialize()` */
238
256
  get address() {
239
257
  return this._address;
240
258
  }
241
259
 
260
+ /** @returns {SuiSigner|SuiInBrowser|null} active signer/keypair, or null in read-only mode */
242
261
  get signer() {
243
262
  return this._signer;
244
263
  }
@@ -284,11 +303,20 @@ export default class SuiMaster extends SuiCommonMethods {
284
303
  return suiPackage;
285
304
  }
286
305
 
306
+ /**
307
+ * Ensure `initialize()` has run, then return the gRPC client.
308
+ * @returns {Promise<SuiGrpcClient>}
309
+ */
287
310
  async getClient() {
288
311
  await this.initialize();
289
312
  return this._client;
290
313
  }
291
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
+ */
292
320
  async initialize() {
293
321
  if (this._initialized) {
294
322
  return true;
@@ -303,12 +331,13 @@ export default class SuiMaster extends SuiCommonMethods {
303
331
  }
304
332
 
305
333
  if (this._signer) {
306
- if (this._signer.toSuiAddress) {
307
- this._address = this._signer.toSuiAddress(); // after Sui's refactor Keypair's method
308
- } else if (this._signer.connectedAddress) {
309
- this._address = this._signer.connectedAddress;
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;
310
339
  } else {
311
- this._address = await this._signer.getAddress(); // old method
340
+ this._address = await s.getAddress(); // old method
312
341
  }
313
342
 
314
343
  this.log('initialized. connected as', this._address);
@@ -321,81 +350,251 @@ export default class SuiMaster extends SuiCommonMethods {
321
350
  }
322
351
 
323
352
  /**
324
- * Return the first resolved name for the connected wallet if there's any, null otherwise
325
- *
326
- * @param {Object} params parameters
327
- * @param {'at' | 'dot' | undefined} params.format
328
- *
329
- * @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>}
330
356
  */
331
- async resolveNameServiceName(params = {}) {
332
- const resolvedNames = await this.resolveNameServiceNames(params);
333
- if (resolvedNames && resolvedNames.length) {
334
- return resolvedNames[0];
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;
335
369
  }
336
- return null;
337
370
  }
338
371
 
339
372
  /**
340
- * Return the resolved names for the connected wallet, if multiple names are resolved, the first one is the primary name.
341
- * Currently returns array with only the first name
342
- * @param {Object} params parameters
343
- * @param {'at' | 'dot' | undefined} params.format
344
- *
345
- * @returns {Array.<string>}
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
346
387
  */
347
- async resolveNameServiceNames(params = {}) {
348
- if (!this._address) {
388
+ async getObjects(idsOrSuiObjects, include) {
389
+ if (!Array.isArray(idsOrSuiObjects) || idsOrSuiObjects.length === 0) {
349
390
  return [];
350
391
  }
351
392
 
352
- try {
353
- const resp = await this._client.resolveNameServiceNames({
354
- address: this.address,
355
- format: params.format,
356
- });
357
- if (resp && resp.data) {
358
- 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) };
359
397
  }
360
- } catch (e) {
361
- return [];
362
- }
363
- }
398
+ const id = '' + a;
399
+ return {
400
+ id,
401
+ obj: new SuiObject({ suiMaster: this, debug: this._debug, id }),
402
+ };
403
+ });
364
404
 
365
- async signAndExecuteTransaction(params) {
366
- if (!params.chain) {
367
- const chainId = 'sui:'+(this._providerName.split('sui:').join('').toLowerCase());
368
- params.chain = chainId;
405
+ const uniqueIds = [];
406
+ for (const s of slots) {
407
+ if (s.id && uniqueIds.indexOf(s.id) === -1) uniqueIds.push(s.id);
369
408
  }
370
- if (!params.account) {
371
- params.account = { address: this._address };
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);
417
+ } catch (e) {
418
+ console.error(e);
372
419
  }
373
420
 
374
- /** @type {SuiTransactionBlockResponse} */
375
- let txResults = null;
376
- if (this._keypair) {
377
- params.signer = this._keypair;
378
- txResults = await this._client.signAndExecuteTransaction(params);
379
- } else if (this._signer) {
380
- txResults = await this._signer.signAndExecuteTransaction(params);
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);
381
426
  }
382
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;
493
+ }
494
+
495
+ /**
496
+ * Sign and execute a transaction on the Sui blockchain.
497
+ * Uses the connected keypair or signer or browser extension wallet adapter to sign the transaction before submitting it.
498
+ *
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).
504
+ *
505
+ * 2. **`FailedTransaction`** (on-chain) — the transaction was submitted and executed but aborted.
506
+ * Both `executionError` and `digest` are set.
507
+ *
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
513
+ *
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
517
+ */
518
+ async signAndExecuteTransaction(params) {
519
+ let signAndExecuteTransactionResponse;
383
520
  try {
384
- if (params && params.requestType && params.requestType == 'WaitForLocalExecution') {
385
- const detailedResults = await this.client.waitForTransaction({
386
- digest: txResults.digest,
387
- options: (params.options || {}),
521
+ const tx = /** @type {Transaction} */ (params.transaction);
522
+
523
+ tx.setSenderIfNotSet(this.address);
524
+
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,
388
550
  });
389
-
390
- return detailedResults;
551
+
552
+ ({ signature } = await signer.signTransaction(transactionBytes));
391
553
  }
554
+
555
+ signAndExecuteTransactionResponse = await this._client.executeTransaction({
556
+ transaction: transactionBytes,
557
+ signatures: [signature],
558
+ include: params.include ? params.include : {},
559
+ });
392
560
  } catch (e) {
393
- this.log(e);
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;
567
+ }
568
+
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
+ );
394
588
  }
395
589
 
396
- return txResults;
590
+ return suiTransaction;
397
591
  }
398
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
+ */
399
598
  async requestSuiFromFaucet() {
400
599
  await this.initialize();
401
600
  let amount = BigInt(0);
@@ -404,18 +603,18 @@ export default class SuiMaster extends SuiCommonMethods {
404
603
  if (provider === "mainnet") {
405
604
  this.log(`no faucet on ${provider}`);
406
605
  } else {
407
- const faucetHost = getFaucetHost(provider);
606
+ const faucetHost = getFaucetHost(/** @type {any} */ (provider));
408
607
  this.log(`requesting sui from faucet... ${faucetHost}`);
409
- const requested = await requestSuiFromFaucetV0({
608
+ const requested = await requestSuiFromFaucetV2({
410
609
  host: faucetHost,
411
610
  recipient: this._address,
412
611
  });
413
612
 
414
613
  let objectsCount = 0;
415
614
 
416
- if (requested && requested.transferredGasObjects) {
417
- for (let transferredGasObject of requested.transferredGasObjects) {
418
- amount += BigInt(transferredGasObject.amount);
615
+ if (requested && requested.coins_sent) {
616
+ for (const transferred of requested.coins_sent) {
617
+ amount += BigInt(transferred.amount);
419
618
  objectsCount++;
420
619
  }
421
620
  }
@@ -429,9 +628,9 @@ export default class SuiMaster extends SuiCommonMethods {
429
628
  /**
430
629
  * Query the balance of specific coinType for an owner. If owner == null, returns balance of connected address owner
431
630
  *
432
- * @param {String} coinType
433
- * @param {String|null} owner
434
- * @returns BigInt
631
+ * @param {string} [coinType='0x2::sui::SUI']
632
+ * @param {?string} [owner] - defaults to the connected address
633
+ * @returns {Promise<bigint>}
435
634
  */
436
635
  async getBalance(coinType = '0x2::sui::SUI', owner = null) {
437
636
  await this.initialize();
@@ -445,20 +644,34 @@ export default class SuiMaster extends SuiCommonMethods {
445
644
  return await (suiCoin.getBalance(queryingBalanceOf));
446
645
  }
447
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
+ */
448
663
  async fetchEvents(params = {}) {
449
- let query = params.query;
450
-
451
664
  const queryParams = {
452
- descending_order: params.descending_order || false,
453
- query: query,
665
+ filter: params.filter || {},
454
666
  limit: params.limit || 50,
455
667
  };
456
668
 
669
+ /** @type {SuiPaginatedResponse<SuiEvent>} */
457
670
  const paginatedResponse = new SuiPaginatedResponse({
458
671
  debug: this._debug,
459
672
  suiMaster: this,
460
673
  params: queryParams,
461
- method: 'queryEvents',
674
+ method: 'graphqlEvents',
462
675
  order: params.order,
463
676
  });
464
677
 
@@ -467,36 +680,48 @@ export default class SuiMaster extends SuiCommonMethods {
467
680
  return paginatedResponse;
468
681
  }
469
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
+ */
470
705
  async fetchTransactions(params = {}) {
471
- let filter = {};
472
- if (params.fromAddress) {
473
- filter.FromAddress = params.fromAddress;
474
- }
475
- if (params.filter) {
476
- filter = params.filter;
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;
477
717
  }
478
718
 
479
- const queryParams = {
480
- descending_order: false,
481
- filter: filter,
482
- options: {
483
- // showInput: true,
484
- showEffects: true,
485
- // showEvents: true,
486
- // showObjectChanges: true,
487
- // showBalanceChanges: true,
488
- // showType: true,
489
- // showContent: true,
490
- // showOwner: true,
491
- // showDisplay: true,
492
- },
493
- limit: params.limit || 50,
494
- };
719
+ /** @type {SuiPaginatedResponse<SuiTransaction>} */
495
720
  const paginatedResponse = new SuiPaginatedResponse({
496
721
  debug: this._debug,
497
722
  suiMaster: this,
498
- params: queryParams,
499
- method: 'queryTransactionBlocks',
723
+ params: { filter, limit: params.limit || 50 },
724
+ method: 'graphqlTransactions',
500
725
  order: params.order,
501
726
  });
502
727