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.
Files changed (57) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/README.md +222 -131
  3. package/index.js +0 -2
  4. package/lib/SuiCliCommands.js +18 -25
  5. package/lib/SuiCoin.js +79 -137
  6. package/lib/SuiCoins.js +41 -29
  7. package/lib/SuiCommonMethods.js +40 -3
  8. package/lib/SuiEvent.js +54 -6
  9. package/lib/SuiInBrowser.js +161 -16
  10. package/lib/SuiInBrowserAdapter.js +192 -40
  11. package/lib/SuiLocalTestValidator.js +76 -14
  12. package/lib/SuiMaster.js +335 -139
  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 -127
  21. package/package.json +29 -13
  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 -0
  35. package/test/test_move_contracts/suidouble_chat/Move.lock +18 -0
  36. package/tsconfig.json +15 -0
  37. package/types/index.d.ts +15 -0
  38. package/types/lib/SuiCliCommands.d.ts +6 -0
  39. package/types/lib/SuiCoin.d.ts +183 -0
  40. package/types/lib/SuiCoins.d.ts +93 -0
  41. package/types/lib/SuiCommonMethods.d.ts +37 -0
  42. package/types/lib/SuiEvent.d.ts +95 -0
  43. package/types/lib/SuiInBrowser.d.ts +195 -0
  44. package/types/lib/SuiInBrowserAdapter.d.ts +173 -0
  45. package/types/lib/SuiLocalTestValidator.d.ts +92 -0
  46. package/types/lib/SuiMaster.d.ts +333 -0
  47. package/types/lib/SuiMemoryObjectStorage.d.ts +96 -0
  48. package/types/lib/SuiObject.d.ts +135 -0
  49. package/types/lib/SuiPackage.d.ts +233 -0
  50. package/types/lib/SuiPackageModule.d.ts +139 -0
  51. package/types/lib/SuiPaginatedResponse.d.ts +148 -0
  52. package/types/lib/SuiPseudoRandomAddress.d.ts +33 -0
  53. package/types/lib/SuiTransaction.d.ts +92 -0
  54. package/types/lib/SuiUtils.d.ts +152 -0
  55. package/types/lib/data/icons.d.ts +12 -0
  56. package/lib/SuiTestScenario.js +0 -169
  57. 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
- * @typedef {import("@mysten/sui/jsonRpc").SuiJsonRpcClient} SuiJsonRpcClient
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/jsonRpc").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 {?SuiJsonRpcClient|string} params.client - instance of SuiJsonRpcClient or chain name to make SuiJsonRpcClient 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,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('goint to use keypair of', this._keypair.getPublicKey().toSuiAddress());
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('goint to use keypair of', this._keypair.getPublicKey().toSuiAddress());
113
+ this.log('going to use keypair of', this._keypair.getPublicKey().toSuiAddress());
111
114
  }
112
115
 
113
116
 
114
- /** @type {SuiClient} */
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 ? this._client.providerName : null;
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
- if (this._signer.toSuiAddress) {
299
- this._address = this._signer.toSuiAddress(); // after Sui's refactor Keypair's method
300
- } else if (this._signer.connectedAddress) {
301
- 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;
302
339
  } else {
303
- this._address = await this._signer.getAddress(); // old method
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 first resolved name for the connected wallet if there's any, null otherwise
317
- *
318
- * @param {Object} params parameters
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 resolveNameServiceName(params = {}) {
324
- const resolvedNames = await this.resolveNameServiceNames(params);
325
- if (resolvedNames && resolvedNames.length) {
326
- 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;
327
369
  }
328
- return null;
329
370
  }
330
371
 
331
372
  /**
332
- * Return the resolved names for the connected wallet, if multiple names are resolved, the first one is the primary name.
333
- * Currently returns array with only the first name
334
- * @param {Object} params parameters
335
- * @param {'at' | 'dot' | undefined} params.format
336
- *
337
- * @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
338
387
  */
339
- async resolveNameServiceNames(params = {}) {
340
- if (!this._address) {
388
+ async getObjects(idsOrSuiObjects, include) {
389
+ if (!Array.isArray(idsOrSuiObjects) || idsOrSuiObjects.length === 0) {
341
390
  return [];
342
391
  }
343
392
 
344
- try {
345
- const resp = await this._client.resolveNameServiceNames({
346
- address: this.address,
347
- format: params.format,
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
- return [];
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
- * @param {Object} params - Transaction parameters
362
- * @param {Transaction} params.transaction - The Transaction object to sign and execute
363
- * @param {Object} [params.options] - Options specifying what data to return in the response
364
- * @param {boolean} [params.options.showEffects] - Whether to include transaction effects
365
- * @param {boolean} [params.options.showEvents] - Whether to include emitted events
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
- * @returns {Promise<SuiTransactionBlockResponse>} The transaction execution response
505
+ * 2. **`FailedTransaction`** (on-chain) the transaction was submitted and executed but aborted.
506
+ * Both `executionError` and `digest` are set.
379
507
  *
380
- * @example
381
- * const tx = new Transaction();
382
- * tx.moveCall({ target: '0x2::coin::transfer', arguments: [...] });
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
- * const result = await suiMaster.signAndExecuteTransaction({
385
- * transaction: tx,
386
- * requestType: 'WaitForLocalExecution',
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
- if (!params.chain) {
396
- const chainId = 'sui:'+(this._providerName.split('sui:').join('').toLowerCase());
397
- params.chain = chainId;
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
- /** @type {SuiTransactionBlockResponse} */
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
- try {
413
- if (params && params.requestType && params.requestType == 'WaitForLocalExecution') {
414
- const detailedResults = await this.client.waitForTransaction({
415
- digest: txResults.digest,
416
- options: (params.options || {}),
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
- return detailedResults;
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
- 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;
423
567
  }
424
568
 
425
- return txResults;
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 {String} coinType
462
- * @param {String|null} owner
463
- * @returns BigInt
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
- descending_order: params.descending_order || false,
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: 'queryEvents',
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 (params.fromAddress) {
502
- filter.FromAddress = params.fromAddress;
503
- }
504
- if (params.filter) {
505
- 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;
506
717
  }
507
718
 
508
- const queryParams = {
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: queryParams,
528
- method: 'queryTransactionBlocks',
723
+ params: { filter, limit: params.limit || 50 },
724
+ method: 'graphqlTransactions',
529
725
  order: params.order,
530
726
  });
531
727