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.
- package/.claude/settings.local.json +7 -0
- package/README.md +222 -131
- package/index.js +1 -3
- package/lib/SuiCliCommands.js +18 -25
- package/lib/SuiCoin.js +86 -138
- package/lib/SuiCoins.js +70 -31
- package/lib/SuiCommonMethods.js +40 -3
- package/lib/SuiEvent.js +54 -6
- package/lib/SuiInBrowser.js +145 -46
- package/lib/SuiInBrowserAdapter.js +164 -37
- package/lib/SuiLocalTestValidator.js +78 -25
- package/lib/SuiMaster.js +351 -126
- package/lib/SuiMemoryObjectStorage.js +66 -73
- package/lib/SuiObject.js +128 -153
- package/lib/SuiPackage.js +292 -187
- package/lib/SuiPackageModule.js +176 -221
- package/lib/SuiPaginatedResponse.js +288 -25
- package/lib/SuiPseudoRandomAddress.js +29 -2
- package/lib/SuiTransaction.js +115 -70
- package/lib/SuiUtils.js +179 -124
- package/package.json +30 -14
- package/test/build_modules.test.js +41 -0
- package/test/coins.test.js +17 -16
- package/test/custom_transaction.test.js +167 -0
- package/test/event_listeners.test.js +171 -0
- package/test/failed_transaction.test.js +184 -0
- package/test/name_service.test.js +28 -0
- package/test/owned_objects.test.js +148 -0
- package/test/rpc.test.js +3 -6
- package/test/sui_in_browser.test.js +2 -2
- package/test/sui_master_basic.test.js +4 -5
- package/test/sui_master_onlocal.test.js +84 -22
- package/test/sui_object_properties.test.js +85 -0
- package/test/test_move_contracts/different_types/Move.lock +18 -21
- package/test/test_move_contracts/different_types/sources/different_types.move +12 -12
- package/test/test_move_contracts/suidouble_chat/Move.lock +18 -22
- package/test/test_move_contracts/suidouble_chat/sources/suidouble_chat.move +9 -8
- package/tsconfig.json +15 -0
- package/types/index.d.ts +15 -0
- package/types/lib/SuiCliCommands.d.ts +6 -0
- package/types/lib/SuiCoin.d.ts +183 -0
- package/types/lib/SuiCoins.d.ts +93 -0
- package/types/lib/SuiCommonMethods.d.ts +37 -0
- package/types/lib/SuiEvent.d.ts +95 -0
- package/types/lib/SuiInBrowser.d.ts +189 -0
- package/types/lib/SuiInBrowserAdapter.d.ts +167 -0
- package/types/lib/SuiLocalTestValidator.d.ts +92 -0
- package/types/lib/SuiMaster.d.ts +333 -0
- package/types/lib/SuiMemoryObjectStorage.d.ts +96 -0
- package/types/lib/SuiObject.d.ts +135 -0
- package/types/lib/SuiPackage.d.ts +233 -0
- package/types/lib/SuiPackageModule.d.ts +139 -0
- package/types/lib/SuiPaginatedResponse.d.ts +148 -0
- package/types/lib/SuiPseudoRandomAddress.d.ts +33 -0
- package/types/lib/SuiTransaction.d.ts +92 -0
- package/types/lib/SuiUtils.d.ts +152 -0
- package/types/lib/data/icons.d.ts +12 -0
- package/lib/SuiTestScenario.js +0 -169
- package/test/sui_test_scenario.test.js +0 -61
package/lib/SuiMaster.js
CHANGED
|
@@ -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 {
|
|
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
|
-
* @
|
|
23
|
+
* @import { SuiClientTypes } from "@mysten/sui/client"
|
|
24
|
+
*
|
|
25
|
+
* @typedef {import("@mysten/sui/grpc").SuiGrpcClient} SuiGrpcClient
|
|
26
|
+
* @typedef {import("@mysten/sui/graphql").SuiGraphQLClient} SuiGraphQLClient
|
|
24
27
|
* @typedef {import("@mysten/sui/cryptography").Signer} SuiSigner
|
|
25
28
|
* @typedef {import("@mysten/sui/cryptography").Keypair} SuiKeypair
|
|
26
|
-
* @typedef {import("
|
|
29
|
+
* @typedef {import("./SuiInBrowser.js").default} SuiInBrowser
|
|
27
30
|
*/
|
|
28
31
|
|
|
29
32
|
export default class SuiMaster extends SuiCommonMethods {
|
|
30
33
|
/**
|
|
31
34
|
* SuiMaster constructor
|
|
32
35
|
* @param {Object} params - Initialization parameters
|
|
33
|
-
* @param {
|
|
34
|
-
* @param {
|
|
35
|
-
* @param {
|
|
36
|
-
* @param {
|
|
37
|
-
* @param {
|
|
38
|
-
* @param {
|
|
39
|
-
* @param {
|
|
40
|
-
* @param {
|
|
41
|
-
* @param {
|
|
36
|
+
* @param {SuiSigner|SuiInBrowser} [params.signer] - instance of Sui SDK Signer (like Keypair) or SuiInBrowser for browser wallet signing
|
|
37
|
+
* @param {SuiKeypair} [params.keypair] - instance of Sui SDK Keypair
|
|
38
|
+
* @param {string} [params.privateKey] - private key in Sui format, starting with "suiprivkey1"
|
|
39
|
+
* @param {boolean} [params.debug] - enable debug mode
|
|
40
|
+
* @param {string} [params.phrase] - mnemonic phrase to derive keypair from
|
|
41
|
+
* @param {string} [params.keypairAlgo] - keypair algorithm to use with mnemonic phrase. One of 'ed25519' (default), 'secp256k1', 'secp256r1'
|
|
42
|
+
* @param {number} [params.accountIndex] - index of account to derive from mnemonic phrase. Default is 0
|
|
43
|
+
* @param {string} [params.as] - pseudo-random address generation seed string
|
|
44
|
+
* @param {SuiGrpcClient|string} [params.client] - instance of SuiGrpcClient or chain name to make SuiGrpcClient connected to it (like 'local', 'devnet', 'testnet', 'mainnet')
|
|
42
45
|
*/
|
|
43
46
|
constructor(params) {
|
|
44
47
|
super(params);
|
|
@@ -47,7 +50,7 @@ export default class SuiMaster extends SuiCommonMethods {
|
|
|
47
50
|
SuiMaster.instancesCount++;
|
|
48
51
|
this._instanceN = SuiMaster.instancesCount;
|
|
49
52
|
|
|
50
|
-
/** @type {SuiSigner} */
|
|
53
|
+
/** @type {SuiSigner|SuiInBrowser|null} */
|
|
51
54
|
this._signer = null;
|
|
52
55
|
/** @type {SuiKeypair} */
|
|
53
56
|
this._keypair = null;
|
|
@@ -57,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.
|
|
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('
|
|
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('
|
|
113
|
+
this.log('going to use keypair of', this._keypair.getPublicKey().toSuiAddress());
|
|
119
114
|
}
|
|
120
115
|
|
|
121
116
|
|
|
122
|
-
/** @type {
|
|
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
|
|
125
|
+
this._providerName = this._client.network;
|
|
126
126
|
|
|
127
127
|
if (!this._client) {
|
|
128
|
-
throw new Error('Can not do anything without
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
|
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
|
|
325
|
-
*
|
|
326
|
-
* @
|
|
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
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
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
|
|
348
|
-
if (!
|
|
388
|
+
async getObjects(idsOrSuiObjects, include) {
|
|
389
|
+
if (!Array.isArray(idsOrSuiObjects) || idsOrSuiObjects.length === 0) {
|
|
349
390
|
return [];
|
|
350
391
|
}
|
|
351
392
|
|
|
352
|
-
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
|
|
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
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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
|
-
|
|
371
|
-
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
417
|
-
for (
|
|
418
|
-
amount += BigInt(
|
|
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 {
|
|
433
|
-
* @param {
|
|
434
|
-
* @returns
|
|
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
|
-
|
|
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: '
|
|
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 (
|
|
473
|
-
filter
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
filter = params.
|
|
706
|
+
let filter = params.filter;
|
|
707
|
+
if (!filter) {
|
|
708
|
+
filter = {};
|
|
709
|
+
if (params.fromAddress) filter.sentAddress = params.fromAddress;
|
|
710
|
+
if (params.affectedAddress) filter.affectedAddress = params.affectedAddress;
|
|
711
|
+
if (params.affectedObject) filter.affectedObject = params.affectedObject;
|
|
712
|
+
if (params.function) filter.function = params.function;
|
|
713
|
+
if (params.atCheckpoint !== undefined) filter.atCheckpoint = params.atCheckpoint;
|
|
714
|
+
if (params.afterCheckpoint !== undefined) filter.afterCheckpoint = params.afterCheckpoint;
|
|
715
|
+
if (params.beforeCheckpoint !== undefined) filter.beforeCheckpoint = params.beforeCheckpoint;
|
|
716
|
+
if (params.kind) filter.kind = params.kind;
|
|
477
717
|
}
|
|
478
718
|
|
|
479
|
-
|
|
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:
|
|
499
|
-
method: '
|
|
723
|
+
params: { filter, limit: params.limit || 50 },
|
|
724
|
+
method: 'graphqlTransactions',
|
|
500
725
|
order: params.order,
|
|
501
726
|
});
|
|
502
727
|
|