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/SuiPackage.js CHANGED
@@ -3,10 +3,14 @@ import SuiObject from './SuiObject.js';
3
3
  import SuiPackageModule from './SuiPackageModule.js';
4
4
  import SuiPaginatedResponse from './SuiPaginatedResponse.js';
5
5
  import { Transaction } from '@mysten/sui/transactions';
6
- import { normalizeSuiAddress } from './SuiUtils.js';
6
+
7
7
 
8
8
  /**
9
9
  * @typedef {import("./SuiMaster.js").default} SuiMaster
10
+ * @typedef {import("./SuiTransaction.js").default} SuiTransaction
11
+ * @typedef {import("./SuiEvent.js").default} SuiEvent
12
+ * @typedef {import("./SuiMemoryObjectStorage.js").default} SuiMemoryObjectStorage
13
+ * @typedef {import("@mysten/sui/transactions").Inputs} Inputs
10
14
  */
11
15
 
12
16
  export default class SuiPackage extends SuiObject {
@@ -20,14 +24,9 @@ export default class SuiPackage extends SuiObject {
20
24
  * @param {?Array.<string>|string} [params.modules] - List of modules in the package to look on chain in the UpgradeCap owned by current address
21
25
  * @param {boolean} [params.debug] - Enable debug mode
22
26
  */
23
- constructor(params = {}) {
27
+ constructor(params) {
24
28
  super(params);
25
29
 
26
- // set in super() :
27
- // this._id
28
- // this._suiMaster
29
-
30
-
31
30
  /** @type {?string} */
32
31
  this._path = params.path;
33
32
  /** @type {?string} */
@@ -46,33 +45,58 @@ export default class SuiPackage extends SuiObject {
46
45
  this._builtDependencies = null;
47
46
  this._builtDigest = null;
48
47
 
48
+ /**
49
+ * Cached original package id (first published version). Stable across upgrades.
50
+ * Lazy — do not read directly, use `getOriginalPackageId()`.
51
+ * @type {?string}
52
+ */
53
+ this._originalPackageId = null;
54
+
49
55
  /** @type {Object.<string, SuiPackageModule>} */
50
56
  this._modules = {
51
57
 
52
58
  };
53
59
  }
54
60
 
61
+ /** @returns {SuiMemoryObjectStorage} shared object storage for this suiMaster */
55
62
  get objectStorage() {
56
63
  return this._suiMaster.objectStorage;
57
64
  }
58
65
 
66
+ /** @returns {Object.<string, SuiPackageModule>} map of module name → SuiPackageModule */
59
67
  get modules() {
60
68
  return this._modules;
61
69
  }
62
70
 
71
+ /**
72
+ * Ensure the package is on chain, then return the named module.
73
+ * @param {string} moduleName
74
+ * @returns {Promise<SuiPackageModule|undefined>}
75
+ */
63
76
  async getModule(moduleName) {
64
77
  await this.checkOnChainIfNeeded();
65
78
  return this._modules[moduleName];
66
79
  }
67
80
 
81
+ /** @returns {boolean} true if the local Move project has been built via `build()` */
68
82
  get isBuilt() {
69
83
  return this._isBuilt;
70
84
  }
71
85
 
86
+ /**
87
+ * Published version of this package as a plain `number`.
88
+ * Returns `0` if the package has not been published yet (`_publishedVersion` is null).
89
+ * @returns {number}
90
+ */
72
91
  get version() {
73
92
  return Number(this._publishedVersion); // return as Number in getter
74
93
  }
75
94
 
95
+ /**
96
+ * Returns true if this package is confirmed published on chain.
97
+ * Swallows `checkOnChainIfNeeded` errors so it always resolves.
98
+ * @returns {Promise<boolean>}
99
+ */
76
100
  async isOnChain() {
77
101
  try {
78
102
  await this.checkOnChainIfNeeded();
@@ -87,18 +111,93 @@ export default class SuiPackage extends SuiObject {
87
111
  return false;
88
112
  }
89
113
 
114
+ /**
115
+ * Shortcut for `suiMaster.utils.pureInput(type, value)` — build a BCS-serialised Pure input
116
+ * to use as a moveCall parameter.
117
+ * @param {string} type - BCS type name, e.g. `'u64'`, `'address'`, `'string'`
118
+ * @param {*} value
119
+ * @returns {ReturnType<Inputs['Pure']>}
120
+ */
90
121
  arg(type, value) {
91
122
  return this._suiMaster.utils.pureInput(type, value);
92
123
  }
93
124
 
125
+ /**
126
+ * Convenience wrapper — delegates to `this.modules[moduleName].moveCall(...)`.
127
+ * @param {string} moduleName
128
+ * @param {string} methodName
129
+ * @param {Array} params - see `SuiPackageModule.moveCall` for element shapes
130
+ * @param {string[]} [typeArguments]
131
+ * @returns {Promise<SuiTransaction>}
132
+ */
94
133
  async moveCall(moduleName, methodName, params, typeArguments) {
95
134
  await this.checkOnChainIfNeeded();
96
135
  return await this.modules[moduleName].moveCall(methodName, params, typeArguments);
97
136
  }
98
137
 
99
- async fetchEvents(moduleName, params = {}) {
138
+ /**
139
+ * Fetch all events emitted by any module of this package, via the GraphQL `events` query.
140
+ * Uses the original (first-version) package id so the filter stays stable across upgrades.
141
+ * For module-scoped or event-type-scoped queries use `this.modules[name].fetchEvents(...)`.
142
+ *
143
+ * @param {Object} [params]
144
+ * @param {number} [params.limit=50]
145
+ * @param {string} [params.order] - Passed through to the underlying paginated response
146
+ * @returns {Promise<SuiPaginatedResponse<SuiEvent>>}
147
+ */
148
+ async fetchEvents(params = {}) {
149
+ await this.checkOnChainIfNeeded();
150
+
151
+ const packageId = await this.getOriginalPackageId();
152
+
153
+ return await this._suiMaster.fetchEvents({
154
+ filter: { module: packageId },
155
+ limit: params.limit,
156
+ order: params.order,
157
+ });
158
+ }
159
+
160
+ /**
161
+ * List objects of any type belonging to this package owned by the current SuiMaster address.
162
+ * Uses the original (first-version) package id and GraphQL (gRPC can't filter by package alone).
163
+ *
164
+ * For module-scoped or full-struct-type filtering use `this.modules[name].getOwnedObjects(...)`.
165
+ *
166
+ * @param {Object} [params]
167
+ * @param {number} [params.limit=50]
168
+ * @param {string} [params.order]
169
+ * @returns {Promise<SuiPaginatedResponse<SuiObject>>}
170
+ */
171
+ async getOwnedObjects(params = {}) {
100
172
  await this.checkOnChainIfNeeded();
101
- return await this.modules[moduleName].fetchEvents(params);
173
+
174
+ const packageId = await this.getOriginalPackageId();
175
+
176
+ return await this._suiMaster.getOwnedObjects({
177
+ owner: this._suiMaster.address,
178
+ type: packageId,
179
+ limit: params.limit || 50,
180
+ }, { order: params.order });
181
+ }
182
+
183
+ /**
184
+ * Get the original (first-version) package id, stable across upgrades.
185
+ * Caches the value on first fetch.
186
+ *
187
+ * @returns {Promise<string>}
188
+ */
189
+ async getOriginalPackageId() {
190
+ if (this._originalPackageId) {
191
+ return this._originalPackageId;
192
+ }
193
+
194
+ const { response } = await this._suiMaster._client.movePackageService.getPackage({
195
+ packageId: this.address,
196
+ });
197
+ const pkg = response?.package;
198
+ this._originalPackageId = pkg?.originalId || pkg?.storageId || this.address;
199
+
200
+ return this._originalPackageId;
102
201
  }
103
202
 
104
203
  async checkOnChainIfNeeded() {
@@ -132,7 +231,8 @@ export default class SuiPackage extends SuiObject {
132
231
  * Try to find package on chain using its modules names.
133
232
  * Search for packages you own, in last versions of it
134
233
  * List all UpgradeCap -> List packages -> Filter ( max version, all modules )
135
- * @returns id of package
234
+ *
235
+ * @returns {Promise<?string>} id of the highest-version package that contains all expected modules, or null if none matched
136
236
  */
137
237
  async tryToFindByExpectedModules() {
138
238
  this.log('trying to find Package by expected modules in its content...');
@@ -140,11 +240,9 @@ export default class SuiPackage extends SuiObject {
140
240
  // normalize expected modules. May be an array or comma separated string
141
241
  const expectModules = [];
142
242
 
143
- let arr = this._expectedModules;
144
- if (!Array.isArray(this._expectedModules)) {
145
- //
146
- arr = (''+this._expectedModules).split(',');
147
- }
243
+ let arr = Array.isArray(this._expectedModules)
244
+ ? this._expectedModules
245
+ : (''+this._expectedModules).split(',');
148
246
  arr.forEach((item)=>{
149
247
  if (item.trim()) {
150
248
  if (expectModules.indexOf(item.trim()) === -1) {
@@ -159,23 +257,14 @@ export default class SuiPackage extends SuiObject {
159
257
 
160
258
  // UpgradeCap references to most recent version of packages. But there're no modules fields in it
161
259
  // So what we do is getting list of UpgradeCap first
162
- const queryParams = {
260
+ const paginatedResponse = await this._suiMaster.getOwnedObjects({
163
261
  owner: this._suiMaster.address,
164
- filter: { StructType: '0x2::package::UpgradeCap', },
165
- limit: 50, // max limit is 50
166
- options: {
167
- showType: true,
168
- showContent: true,
169
- showOwner: true,
170
- showDisplay: true,
262
+ type: '0x2::package::UpgradeCap',
263
+ limit: 50,
264
+ include: {
265
+ content: true,
266
+ json: true,
171
267
  },
172
- };
173
-
174
- const paginatedResponse = new SuiPaginatedResponse({
175
- debug: this._debug,
176
- suiMaster: this._suiMaster,
177
- params: queryParams,
178
- method: 'getOwnedObjects',
179
268
  });
180
269
 
181
270
  await paginatedResponse.forEach((suiObject)=>{
@@ -186,36 +275,38 @@ export default class SuiPackage extends SuiObject {
186
275
  }); // go through all available UpgradeCap
187
276
  // paginatedResponse.forEach also accepts async callbacks
188
277
 
189
- // queriing packages out of the loop, as not sure if pagination cursor works ok with mixed calls, @todo: check
190
- // @todo: what is the max count of ids here?
191
- const packagesResult = await this._suiMaster._client.multiGetObjects({
192
- ids: packagesOnChainIds,
193
- // only fetch the object type
194
- options: { showType: true, showContent: true, },
195
- });
278
+ // fetch each package via movePackageService to check its modules list
279
+ const packagesResult = await Promise.all(
280
+ packagesOnChainIds.map(async (packageId) => {
281
+ try {
282
+ const { response } = await this._suiMaster._client.movePackageService.getPackage({ packageId });
283
+ return response?.package ? { packageId, pkg: response.package } : null;
284
+ } catch (e) {
285
+ return null;
286
+ }
287
+ })
288
+ );
196
289
 
197
- let maxVersion = BigInt(0);
290
+ let maxVersion = 0;
198
291
  let packageIdWithMaxVersion = null;
199
292
  let packagesWithOkModulesCount = 0; // just to log
200
293
 
201
294
  // find package with highest version which has all needed modules
202
- for (const packagesResultItem of packagesResult) {
203
- let allNeededModules = true;
204
- expectModules.forEach((expectModuleName)=>{
205
- if (!packagesResultItem?.data?.content?.disassembled[expectModuleName]) {
206
- allNeededModules = false;
207
- }
208
- });
295
+ for (const item of packagesResult) {
296
+ if (!item) continue;
297
+ const { packageId, pkg } = item;
298
+
299
+ const moduleNames = (pkg.modules || []).map((m) => m.name);
300
+ const allNeededModules = expectModules.every((m) => moduleNames.includes(m));
209
301
 
210
- const version = BigInt(packagesResultItem.data.version);
302
+ const version = Number(pkg.version ?? 0);
211
303
 
212
304
  if (allNeededModules) {
213
305
  packagesWithOkModulesCount++;
214
- }
215
-
216
- if (version > maxVersion) {
217
- maxVersion = version;
218
- packageIdWithMaxVersion = packagesResultItem.data.objectId;
306
+ if (version > maxVersion) {
307
+ maxVersion = version;
308
+ packageIdWithMaxVersion = pkg.storageId || packageId;
309
+ }
219
310
  }
220
311
  }
221
312
 
@@ -231,54 +322,69 @@ export default class SuiPackage extends SuiObject {
231
322
 
232
323
  /**
233
324
  * Get published package version
234
- * @returns Number
325
+ *
326
+ * @param {Object} [options]
327
+ * @param {number} [options.timeout=10000] - total time in ms to keep polling if package is not found
328
+ *
329
+ * @returns {Promise<?number>} on-chain package version, or null if it could not be read before the timeout
235
330
  */
236
- async getVersionOnChain() {
331
+ async getVersionOnChain(options = {}) {
237
332
  this.log('geting package version previously published on chain...');
238
333
 
334
+ const timeout = options.timeout !== undefined ? options.timeout : 10000;
335
+ const pollInterval = 1000;
336
+ const deadline = Date.now() + timeout;
337
+
239
338
  const client = await this._suiMaster.getClient();
240
339
 
241
- const result = await client.getObject({
242
- id: this.address, // normalized id
243
- options: {
244
- showType: true,
245
- showContent: true,
246
- showOwner: true,
247
- showDisplay: true,
248
- "showPreviousTransaction": true,
249
- "showBcs": false,
250
- "showStorageRebate": true
251
- },
252
- });
340
+ let pkg = null;
341
+ while (true) {
342
+ try {
343
+ const { response } = await client.movePackageService.getPackage({
344
+ packageId: this.address,
345
+ });
346
+ pkg = response?.package;
347
+ } catch (e) {
348
+ const willRetry = Date.now() + pollInterval <= deadline;
349
+ this.log('getPackage failed', e && e.message ? e.message : e, willRetry ? `will retry in ${pollInterval}ms` : 'giving up, timeout reached');
350
+ pkg = null;
351
+ }
352
+
353
+ if (pkg) {
354
+ break;
355
+ }
356
+
357
+ if (Date.now() + pollInterval > deadline) {
358
+ this.log('package not found on chain', this.address);
359
+ return this._publishedVersion;
360
+ }
253
361
 
254
- if (result?.data?.version) {
255
- this._publishedVersion = BigInt(result?.data?.version); // not sure, but it's string in response, so let's convert it to bigint, who knows
362
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
363
+ }
364
+
365
+ if (pkg.version !== undefined) {
366
+ this._publishedVersion = Number(pkg.version);
256
367
  this._isPublished = true;
257
368
  }
258
369
 
259
- if (result?.data?.content?.disassembled) {
260
- for (const key in result?.data?.content?.disassembled) {
261
- // console.log(result?.data?.content?.disassembled[key]);
262
- this.attachModule(key);
263
- // if (!this._modules[key]) {
264
- // this._modules[key] = new SuiPackageModule({
265
- // suiMaster: this._suiMaster,
266
- // debug: this._debug,
267
- // moduleName: key,
268
- // package: this,
269
- // });
270
- // }
370
+ if (pkg.modules) {
371
+ for (const moduleDef of pkg.modules) {
372
+ if (moduleDef.name) {
373
+ this.attachModule(moduleDef.name);
374
+ }
271
375
  }
272
376
  }
273
377
 
274
378
  this.log('on chain version', this._publishedVersion, 'with modules', Object.keys(this._modules));
275
-
379
+
276
380
  return this._publishedVersion;
277
381
  }
278
382
 
279
383
  /**
280
384
  * Attach module to this package and add event listeners over it
281
- * @param {String} moduleName
385
+ *
386
+ * @param {string} moduleName
387
+ * @returns {boolean} true if the module was newly attached, false if it was already attached
282
388
  */
283
389
  attachModule(moduleName) {
284
390
  if (this._modules[moduleName]) {
@@ -292,7 +398,7 @@ export default class SuiPackage extends SuiObject {
292
398
  package: this,
293
399
  });
294
400
  this._modules[moduleName].addEventListener('added', (data)=>{
295
- const object = data.detail;
401
+ const object = /** @type {any} */ (data).detail;
296
402
  this.emit('added', object);
297
403
  });
298
404
 
@@ -304,7 +410,8 @@ export default class SuiPackage extends SuiObject {
304
410
  /**
305
411
  * UpgradeCap is capability object required to publish updates for a package.
306
412
  * We are trying to find it in owned objects with this function
307
- * @returns address of UpgradeCap for this package
413
+ *
414
+ * @returns {Promise<?string>} id of UpgradeCap for this package, or null if none is owned by the current address
308
415
  */
309
416
  async getUpgradeCapId() {
310
417
  if (this._upgradeCap) {
@@ -313,103 +420,77 @@ export default class SuiPackage extends SuiObject {
313
420
 
314
421
  this.log('trying to find UpgradeCap for this package in owned objects...');
315
422
 
316
- let hasNextPage = false;
317
- let nextCursor = null;
318
-
319
- do {
320
- const queryParams = {
423
+ const ownedUpgradeCapsPaginated = await this._suiMaster.getOwnedObjects({
321
424
  owner: this._suiMaster.address,
322
- filter: { StructType: '0x2::package::UpgradeCap', },
323
- limit: 50, // max limit is 50
324
- options: {
325
- showType: true,
326
- showContent: true,
327
- showOwner: true,
328
- showDisplay: true,
425
+ type: '0x2::package::UpgradeCap',
426
+ include: {
427
+ content: true,
428
+ objectBcs: true,
429
+ json: true,
329
430
  },
330
- };
331
-
332
- if (nextCursor) {
333
- queryParams.cursor = nextCursor;
334
- }
335
-
336
- const result = await this._suiMaster._client.getOwnedObjects(queryParams);
337
-
338
- if (result.hasNextPage && result.nextCursor) {
339
- hasNextPage = true;
340
- nextCursor = result.nextCursor;
341
- } else {
342
- hasNextPage = false;
431
+ });
432
+ ownedUpgradeCapsPaginated.forEach((suiObject)=>{
433
+ if (suiObject?.fields?.package == this._id) {
434
+ this._upgradeCap = suiObject;
343
435
  }
436
+ });
344
437
 
345
- for (const object of result.data) {
346
- if (object?.data?.content?.fields?.package == this._id) {
347
- this._upgradeCap = new SuiObject({
348
- id: object.data.objectId,
349
- suiMaster: this._suiMaster,
350
- debug: this._debug,
351
- });
352
-
353
- this.log('found UpgradeCap', this._upgradeCap.address);
354
-
355
- return this._upgradeCap.address;
356
- }
357
- }
358
- } while(hasNextPage && !this._upgradeCap);
438
+ if (this._upgradeCap) {
439
+ this.log('found UpgradeCap', this._upgradeCap.id);
440
+ return this._upgradeCap.id;
441
+ }
359
442
 
360
443
  this.log('no UpgradeCap for this package found. Are you sure you work with most recent version of the package?');
361
-
362
444
  return null;
363
445
  }
364
446
 
447
+ /**
448
+ * Read the result of a publish/upgrade transaction and update local package state:
449
+ * sets `_id`, `_publishedVersion`, `_isPublished` from the created `package` object,
450
+ * records `_upgradeCapId` from the created `UpgradeCap`, and refreshes on-chain version.
451
+ *
452
+ * @param {SuiTransaction} result - transaction result returned from publish()/upgrade()
453
+ * @returns {Promise<boolean>} true if the package is now marked as published
454
+ */
365
455
  async storeInfoFromPublishResult(result) {
366
-
367
- if (result && result.objectChanges && result.objectChanges.length) {
368
- for (const objectChange of result.objectChanges) {
369
- if (objectChange.type === 'published' && objectChange.packageId) {
370
- this._id = normalizeSuiAddress(objectChange.packageId);
456
+ if (result && result.created) {
457
+ for (const createdObject of result.created) {
458
+ if (createdObject.type == 'package') {
459
+ this._id = createdObject.id;
371
460
  this._isPublished = true;
372
-
373
- if (objectChange.version) {
374
- this._publishedVersion = BigInt(objectChange.version);
375
- }
376
-
377
- if (objectChange.modules) {
378
- for (const module of objectChange.modules) {
379
- this.attachModule(module);
380
- }
381
- }
382
- }
383
-
384
- if (objectChange.type === 'created' && objectChange.objectType.indexOf('::package::UpgradeCap') !== -1) {
385
- this._upgradeCapId = objectChange.objectId;
461
+ this._publishedVersion = createdObject.version;
462
+ } else if (createdObject.typeName == 'UpgradeCap') {
463
+ this._upgradeCapId = createdObject.id;
386
464
  this.log('UpgradeCap', this._upgradeCapId);
465
+ } else {
466
+ // objects created by the Move `init` function (e.g. shared objects)
467
+ this.objectStorage.push(createdObject);
387
468
  }
388
469
  }
389
470
 
390
- // now as we have modules stored, we can try to push objects to them
391
- for (const objectChange of result.objectChanges) {
392
- if (objectChange.objectId && objectChange.objectType && objectChange.type && (objectChange.type == 'created' || objectChange.type == 'mutated')) {
393
- // : not sure if it's good decision, but lets add objects to all modules we published
394
- for (const moduleName in this._modules) {
395
- const object = this._modules[moduleName].pushObject(objectChange.objectId);
396
- if (object) {
397
- object.tryToFillDataFromObjectChange(objectChange);
398
- }
399
- }
400
- }
471
+ if (this._id) {
472
+ await this.getVersionOnChain();
401
473
  }
474
+ }
402
475
 
403
- this.log('got results:', this.address, 'version', this._publishedVersion, 'with modules', Object.keys(this._modules));
404
-
405
- return true;
406
- } else {
407
- this.log('nothing is found in publish result. storing old values');
408
476
 
409
- return false;
410
- }
477
+ return this._isPublished;
411
478
  }
412
479
 
480
+ /**
481
+ * Publish the Move package to the chain for the first time.
482
+ *
483
+ * Flow:
484
+ * - build the local Move project if it has not been built yet
485
+ * - throw if the package already has an on-chain address (use upgrade() instead)
486
+ * - submit a publish transaction and transfer the returned UpgradeCap to the current address
487
+ * - refresh local publish info from the result
488
+ *
489
+ * @param {Object} [params] - Configuration parameters
490
+ * @param {?string} [params.env] - Sui CLI env to switch to before building (forwarded to build())
491
+ * @returns {Promise<?string>} the on-chain package id, or null if publish info could not be read
492
+ * @throws if the package is already published
493
+ */
413
494
  async publish(params = {}) {
414
495
  if (!this._isBuilt) {
415
496
  await this.build(params);
@@ -427,33 +508,43 @@ export default class SuiPackage extends SuiObject {
427
508
  });
428
509
 
429
510
  tx.transferObjects([upgradeCap], this._suiMaster.address);
511
+ tx.setSenderIfNotSet(this._suiMaster.address);
430
512
 
431
513
  const result = await this._suiMaster.signAndExecuteTransaction({
432
514
  transaction: tx,
433
- requestType: 'WaitForLocalExecution',
434
- options: {
435
- "showEffects": true, // @todo: remove?
436
- "showEvents": true, // @todo: remove?
437
- "showObjectChanges": true,
515
+ include: {
516
+ effects: true,
517
+ objectTypes: true,
438
518
  },
439
519
  });
440
520
 
441
- // const suiTransaction = new this._suiMaster.SuiTransaction({
442
- // suiMaster: this._suiMaster,
443
- // debug: this._debug,
444
- // data: result,
445
- // });
446
- // await suiTransaction.waitForTransaction();
447
-
448
521
  const success = await this.storeInfoFromPublishResult(result);
449
522
 
450
523
  if (success) {
451
524
  this.log('published');
452
525
  }
453
526
 
527
+ this.log('published', this.address);
528
+
454
529
  return this.address;
455
530
  }
456
531
 
532
+ /**
533
+ * Upgrade an on-chain package to a newer version from local Move sources.
534
+ *
535
+ * Flow:
536
+ * - ensure the package is known on chain (checkOnChainIfNeeded)
537
+ * - build the local Move project if it has not been built yet
538
+ * - authorize the upgrade using the owned UpgradeCap (COMPATIBLE policy)
539
+ * - execute the upgrade transaction and commit it
540
+ * - refresh local publish info from the result
541
+ *
542
+ * Requires an UpgradeCap owned by the current address.
543
+ *
544
+ * @param {Object} [params] - Configuration parameters
545
+ * @param {?string} [params.env] - Sui CLI env to switch to before building (forwarded to build())
546
+ * @returns {Promise<boolean>} true on successful upgrade (version incremented), false otherwise
547
+ */
457
548
  async upgrade(params = {}) {
458
549
  await this.checkOnChainIfNeeded();
459
550
 
@@ -487,7 +578,7 @@ export default class SuiPackage extends SuiObject {
487
578
  const receipt = tx.upgrade({
488
579
  modules: this._builtModules,
489
580
  dependencies: this._builtDependencies,
490
- package: this.address, // packageId -> package in sdk v1.0
581
+ package: this.address,
491
582
  ticket,
492
583
  });
493
584
 
@@ -496,28 +587,39 @@ export default class SuiPackage extends SuiObject {
496
587
  arguments: [cap, receipt],
497
588
  });
498
589
 
499
- // console.log(tx);
590
+ const wasAtVersion = this._publishedVersion;
500
591
 
501
- const result = await this._suiMaster.signAndExecuteTransaction({
592
+ const signAndExecuteTransactionResult = await this._suiMaster.signAndExecuteTransaction({
502
593
  transaction: tx,
503
- options: {
504
- showEffects: true,
505
- showObjectChanges: true,
594
+ include: {
595
+ effects: true,
596
+ objectTypes: true,
506
597
  },
598
+ requestType: 'WaitForLocalExecution',
507
599
  });
600
+ const success = await this.storeInfoFromPublishResult(signAndExecuteTransactionResult);
508
601
 
509
- const success = await this.storeInfoFromPublishResult(result);
602
+ if (success && this._publishedVersion > wasAtVersion) {
603
+ this.log('upgraded. New version: '+this._publishedVersion+' at packageId: '+this._id);
510
604
 
511
- if (success) {
512
- this.log('upgraded');
605
+ return true;
513
606
  }
514
607
 
515
- return this.address;
608
+ return false;
516
609
  }
517
610
 
518
611
  /**
519
- * Build a Move project using `sui move build`
520
- * @returns Boolean true on success
612
+ * Build the local Move project by invoking `sui move build --dump-bytecode-as-base64`
613
+ * and cache the resulting modules, dependencies and digest on the instance
614
+ * (`_builtModules`, `_builtDependencies`, `_builtDigest`). Sets `_isBuilt = true`.
615
+ *
616
+ * Must have a local source `path` set on the package — throws otherwise.
617
+ *
618
+ * @param {Object} [params] - Configuration parameters
619
+ * @param {?string} [params.env] - If set, runs `sui client switch --env <env>` before building
620
+ * @param {?boolean} [params.withUnpublishedDependencies] - Pass `--with-unpublished-dependencies` to the build command
621
+ * @returns {Promise<boolean>} true on success
622
+ * @throws if no local path is set
521
623
  */
522
624
  async build(params = {}) {
523
625
  this.log('building a package...');
@@ -554,11 +656,14 @@ export default class SuiPackage extends SuiObject {
554
656
  }
555
657
 
556
658
  /**
557
- * Get list of expected modules from local package path
558
- * @returns array of module names
659
+ * Read the local `build/<pkg>/bytecode_modules` directory and return the list of compiled module names.
660
+ * Requires the package to have been built already (via `sui move build` or this instance's `build()`).
661
+ *
662
+ * @returns {Promise<string[]>} list of module names (without the `.mv` suffix)
663
+ * @throws if the local path is not set or the build directory can not be read
559
664
  */
560
665
  async getModulesNamesFromBuild() {
561
- this.log('tring to get modules names from local package path...');
666
+ this.log('trying to get modules names from local package path...');
562
667
 
563
668
  try {
564
669
  return SuiCliCommands.getModulesNamesFromPackagePath(this._path);