suidouble 2.5.0 → 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 +0 -2
- package/lib/SuiCliCommands.js +18 -25
- package/lib/SuiCoin.js +79 -137
- package/lib/SuiCoins.js +41 -29
- package/lib/SuiCommonMethods.js +40 -3
- package/lib/SuiEvent.js +54 -6
- package/lib/SuiInBrowser.js +143 -15
- package/lib/SuiInBrowserAdapter.js +164 -37
- package/lib/SuiLocalTestValidator.js +76 -14
- package/lib/SuiMaster.js +335 -139
- package/lib/SuiMemoryObjectStorage.js +66 -73
- package/lib/SuiObject.js +128 -153
- package/lib/SuiPackage.js +292 -187
- package/lib/SuiPackageModule.js +176 -221
- package/lib/SuiPaginatedResponse.js +288 -25
- package/lib/SuiPseudoRandomAddress.js +29 -2
- package/lib/SuiTransaction.js +115 -70
- package/lib/SuiUtils.js +179 -127
- package/package.json +29 -13
- package/test/build_modules.test.js +41 -0
- package/test/coins.test.js +17 -16
- package/test/custom_transaction.test.js +167 -0
- package/test/event_listeners.test.js +171 -0
- package/test/failed_transaction.test.js +184 -0
- package/test/name_service.test.js +28 -0
- package/test/owned_objects.test.js +148 -0
- package/test/rpc.test.js +3 -6
- package/test/sui_in_browser.test.js +2 -2
- package/test/sui_master_basic.test.js +4 -5
- package/test/sui_master_onlocal.test.js +84 -22
- package/test/sui_object_properties.test.js +85 -0
- package/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/SuiTransaction.js
CHANGED
|
@@ -1,37 +1,59 @@
|
|
|
1
1
|
import SuiCommonMethods from './SuiCommonMethods.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @import { SuiClientTypes } from "@mysten/sui/client"
|
|
5
|
+
* @typedef {import("./SuiMaster.js").default} SuiMaster
|
|
6
|
+
* @typedef {import("./SuiObject.js").default} SuiObject
|
|
7
|
+
* @typedef {import("./SuiEvent.js").default} SuiEvent
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Wrapper over a Sui transaction result. Lazily parses `effects.changedObjects` into
|
|
12
|
+
* typed `created`, `mutated`, `deleted` lists and wraps events in `SuiEvent` instances.
|
|
13
|
+
*/
|
|
3
14
|
export default class SuiTransaction extends SuiCommonMethods {
|
|
4
|
-
|
|
15
|
+
/**
|
|
16
|
+
* @param {Object} params
|
|
17
|
+
* @param {SuiMaster} params.suiMaster
|
|
18
|
+
* @param {SuiClientTypes.Transaction} params.data - raw transaction data
|
|
19
|
+
* @param {boolean} [params.debug]
|
|
20
|
+
*/
|
|
21
|
+
constructor(params) {
|
|
5
22
|
super(params);
|
|
6
23
|
|
|
7
24
|
this._debug = !!params.debug;
|
|
25
|
+
/** @type {SuiMaster} */
|
|
8
26
|
this._suiMaster = params.suiMaster;
|
|
9
27
|
if (!this._suiMaster) {
|
|
10
28
|
throw new Error('suiMaster is requried');
|
|
11
29
|
}
|
|
12
30
|
|
|
13
|
-
|
|
31
|
+
/** @type {?SuiClientTypes.Transaction} */
|
|
32
|
+
this._data = params.data;
|
|
14
33
|
|
|
34
|
+
/** @type {?{created: SuiObject[], mutated: SuiObject[], deleted: SuiObject[], objects: SuiObject[]}} */
|
|
15
35
|
this._results = null;
|
|
36
|
+
/** @type {?SuiEvent[]} */
|
|
16
37
|
this._events = null;
|
|
17
38
|
}
|
|
18
39
|
|
|
40
|
+
/** @returns {?number} epoch the transaction executed in, or null if unavailable */
|
|
19
41
|
get executedEpoch() {
|
|
20
|
-
if (this._data && this._data.
|
|
21
|
-
return
|
|
42
|
+
if (this._data && this._data.epoch) {
|
|
43
|
+
return Number(this._data.epoch);
|
|
22
44
|
}
|
|
23
|
-
|
|
24
45
|
return null;
|
|
25
46
|
}
|
|
26
47
|
|
|
48
|
+
/** @returns {?string} Base58-encoded transaction digest, or null if data isn't set */
|
|
27
49
|
get digest() {
|
|
28
50
|
if (this._data) {
|
|
29
51
|
return this._data.digest;
|
|
30
52
|
}
|
|
31
|
-
|
|
32
53
|
return null;
|
|
33
54
|
}
|
|
34
55
|
|
|
56
|
+
/** @returns {?bigint} net gas cost in MIST (computationCost + storageCost − storageRebate), or null */
|
|
35
57
|
get gasUsed() {
|
|
36
58
|
try {
|
|
37
59
|
if (this._data && this._data.effects && this._data.effects.gasUsed) {
|
|
@@ -44,43 +66,41 @@ export default class SuiTransaction extends SuiCommonMethods {
|
|
|
44
66
|
return null;
|
|
45
67
|
}
|
|
46
68
|
|
|
69
|
+
/** @returns {SuiObject[]} objects deleted by this transaction */
|
|
47
70
|
get deleted() {
|
|
48
71
|
const results = this.results;
|
|
49
72
|
return results.deleted;
|
|
50
73
|
}
|
|
51
74
|
|
|
75
|
+
/** @returns {SuiObject[]} objects mutated (version changed) by this transaction */
|
|
52
76
|
get mutated() {
|
|
53
77
|
const results = this.results;
|
|
54
78
|
return results.mutated;
|
|
55
79
|
}
|
|
56
80
|
|
|
81
|
+
/** @returns {SuiObject[]} objects created by this transaction */
|
|
57
82
|
get created() {
|
|
58
83
|
const results = this.results;
|
|
59
84
|
return results.created;
|
|
60
85
|
}
|
|
61
86
|
|
|
87
|
+
/** @returns {?SuiClientTypes.Transaction} raw transaction data as received from the client */
|
|
62
88
|
get data() {
|
|
63
89
|
return this._data;
|
|
64
90
|
}
|
|
65
91
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
return status;
|
|
72
|
-
}
|
|
73
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Returns true if the transaction's status indicates success.
|
|
94
|
+
* @returns {boolean}
|
|
95
|
+
*/
|
|
74
96
|
isSuccessful() {
|
|
75
|
-
if (this.data && this.data.
|
|
76
|
-
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
97
|
+
if (this.data && this.data.status && this.data.status.success) {
|
|
98
|
+
return true;
|
|
79
99
|
}
|
|
80
|
-
|
|
81
100
|
return false;
|
|
82
101
|
}
|
|
83
102
|
|
|
103
|
+
/** @returns {SuiEvent[]} events emitted by this transaction, lazily built and cached */
|
|
84
104
|
get events() {
|
|
85
105
|
if (this._events) {
|
|
86
106
|
return this._events;
|
|
@@ -93,7 +113,13 @@ export default class SuiTransaction extends SuiCommonMethods {
|
|
|
93
113
|
const suiEvent = new this._suiMaster.SuiEvent({
|
|
94
114
|
suiMaster: this._suiMaster,
|
|
95
115
|
debug: this._debug,
|
|
96
|
-
data:
|
|
116
|
+
data: {
|
|
117
|
+
// map gRPC SuiClientTypes.Event shape → SuiEventData shape
|
|
118
|
+
type: event.eventType,
|
|
119
|
+
parsedJson: event.json,
|
|
120
|
+
sender: event.sender,
|
|
121
|
+
bcs: event.bcs,
|
|
122
|
+
},
|
|
97
123
|
});
|
|
98
124
|
|
|
99
125
|
events.push(suiEvent);
|
|
@@ -104,70 +130,52 @@ export default class SuiTransaction extends SuiCommonMethods {
|
|
|
104
130
|
return this._events;
|
|
105
131
|
}
|
|
106
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Parsed object-change results, lazily built from `effects.changedObjects` and cached.
|
|
135
|
+
* @returns {{created: SuiObject[], mutated: SuiObject[], deleted: SuiObject[], objects: SuiObject[]}}
|
|
136
|
+
*/
|
|
107
137
|
get results() {
|
|
108
138
|
if (this._results) {
|
|
109
139
|
return this._results;
|
|
110
140
|
}
|
|
111
141
|
|
|
142
|
+
/** @type {Object.<string, SuiObject>} */
|
|
112
143
|
const objects = {};
|
|
113
144
|
|
|
114
145
|
const listCreated = [];
|
|
115
146
|
const listMutated = [];
|
|
116
147
|
const listDeleted = [];
|
|
117
148
|
|
|
118
|
-
if (this.data
|
|
119
|
-
for (const objectChange of this.data.
|
|
149
|
+
if (this.data?.effects?.changedObjects) {
|
|
150
|
+
for (const objectChange of this.data.effects.changedObjects) {
|
|
120
151
|
if (objectChange.objectId) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
152
|
+
const objectId = objectChange.objectId;
|
|
153
|
+
const idOperation = objectChange.idOperation;
|
|
154
|
+
const typeGuess =
|
|
155
|
+
(this.data.objectTypes && this.data.objectTypes[objectChange.objectId]) ?
|
|
156
|
+
this.data.objectTypes[objectChange.objectId] : undefined;
|
|
157
|
+
|
|
158
|
+
if (!objects[objectId]) {
|
|
124
159
|
const obj = new this._suiMaster.SuiObject({
|
|
125
160
|
suiMaster: this._suiMaster,
|
|
126
161
|
debug: this._debug,
|
|
127
|
-
|
|
162
|
+
id: objectChange.objectId,
|
|
163
|
+
version: objectChange.outputVersion,
|
|
164
|
+
type: typeGuess,
|
|
128
165
|
});
|
|
129
|
-
|
|
130
|
-
objects[obj.address] = obj;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (this.data.effects) {
|
|
138
|
-
const events = ['created', 'mutated']; // events names are the same as properties in result.effects
|
|
139
|
-
|
|
140
|
-
for (const eventName of events) {
|
|
141
|
-
if ( this.data.effects[eventName] && this.data.effects[eventName].length) {
|
|
142
|
-
for (const effect of this.data.effects[eventName]) {
|
|
143
|
-
if (effect.reference && effect.reference.objectId) {
|
|
144
|
-
if (objects[effect.reference.objectId]) {
|
|
145
|
-
if (eventName === 'created') {
|
|
146
|
-
listCreated.push(objects[effect.reference.objectId]);
|
|
147
|
-
} else if (eventName === 'mutated') {
|
|
148
|
-
listMutated.push(objects[effect.reference.objectId]);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
166
|
+
objects[objectId] = obj;
|
|
152
167
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
debug: this._debug,
|
|
165
|
-
objectChange: effect,
|
|
166
|
-
});
|
|
167
|
-
objects[effect.objectId] = obj;
|
|
168
|
-
}
|
|
169
|
-
objects[effect.objectId].markAsDeleted();
|
|
170
|
-
listDeleted.push(objects[effect.objectId]);
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if (idOperation == 'Created') {
|
|
171
|
+
listCreated.push(objects[objectId]);
|
|
172
|
+
} else if (idOperation == 'Deleted') {
|
|
173
|
+
listDeleted.push(objects[objectId]);
|
|
174
|
+
objects[objectId].markAsDeleted();
|
|
175
|
+
} else if (objectChange.inputVersion && objectChange.outputVersion
|
|
176
|
+
&& objectChange.inputVersion != objectChange.outputVersion
|
|
177
|
+
) {
|
|
178
|
+
listMutated.push(objects[objectId]);
|
|
171
179
|
}
|
|
172
180
|
}
|
|
173
181
|
}
|
|
@@ -178,15 +186,52 @@ export default class SuiTransaction extends SuiCommonMethods {
|
|
|
178
186
|
mutated: listMutated,
|
|
179
187
|
deleted: listDeleted,
|
|
180
188
|
objects: Object.values(objects),
|
|
181
|
-
status: this.status,
|
|
182
189
|
};
|
|
183
190
|
|
|
184
191
|
return this._results;
|
|
185
192
|
}
|
|
186
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Wait for this transaction to be finalised on chain, then refresh `_data` with the full result.
|
|
196
|
+
* Clears the cached `_results` and `_events` so getters recompute from the fresh data.
|
|
197
|
+
*
|
|
198
|
+
* @param {SuiClientTypes.WaitForTransactionOptions} [input] - extra options forwarded to `client.waitForTransaction`
|
|
199
|
+
* @returns {Promise<this>}
|
|
200
|
+
* @throws if `digest` is not set
|
|
201
|
+
*/
|
|
202
|
+
async waitForTransaction(input = /** @type {SuiClientTypes.WaitForTransactionOptions<{}>} */ ({})) {
|
|
203
|
+
if (!this.digest) {
|
|
204
|
+
throw new Error('can not waitForTransaction: digest is not set');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const result = await this._suiMaster.client.waitForTransaction(
|
|
208
|
+
/** @type {SuiClientTypes.WaitForTransactionOptions<{}>} */ ({
|
|
209
|
+
...input,
|
|
210
|
+
digest: this.digest,
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
this._data = result.Transaction;
|
|
215
|
+
this._results = null;
|
|
216
|
+
this._events = null;
|
|
217
|
+
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Checkpoint timestamp in milliseconds since epoch, or null if unavailable.
|
|
223
|
+
*
|
|
224
|
+
* Only populated when the transaction was fetched via the GraphQL path
|
|
225
|
+
* (`SuiMaster.fetchTransactions`). Transactions returned from `signAndExecuteTransaction`
|
|
226
|
+
* or `waitForTransaction` (gRPC) do not carry a timestamp in their data shape and will
|
|
227
|
+
* always return null here.
|
|
228
|
+
*
|
|
229
|
+
* @returns {?number}
|
|
230
|
+
*/
|
|
187
231
|
get timestampMs() {
|
|
188
|
-
|
|
189
|
-
|
|
232
|
+
const ts = /** @type {any} */ (this.data)?.timestampMs;
|
|
233
|
+
if (ts) {
|
|
234
|
+
return parseInt(''+ts, 10);
|
|
190
235
|
} else {
|
|
191
236
|
return null;
|
|
192
237
|
}
|
package/lib/SuiUtils.js
CHANGED
|
@@ -2,14 +2,17 @@ import SuiCommonMethods from './SuiCommonMethods.js';
|
|
|
2
2
|
import { Inputs, Transaction } from '@mysten/sui/transactions';
|
|
3
3
|
import { bcs } from '@mysten/sui/bcs';
|
|
4
4
|
import { fromBase64 } from '@mysten/bcs';
|
|
5
|
-
import {
|
|
5
|
+
import { SuiGrpcClient, GrpcWebFetchTransport } from '@mysten/sui/grpc';
|
|
6
|
+
import { SuiGraphQLClient } from '@mysten/sui/graphql';
|
|
6
7
|
import { normalizeSuiAddress } from '@mysten/sui/utils';
|
|
7
|
-
import WebSocket from 'websocket';
|
|
8
|
-
|
|
9
|
-
const WebSocketClient = WebSocket.w3cwebsocket;
|
|
10
8
|
|
|
11
9
|
/**
|
|
10
|
+
* @import { SuiClientTypes } from "@mysten/sui/client"
|
|
12
11
|
* @typedef {import("@mysten/sui/transactions").Argument} Argument
|
|
12
|
+
* @typedef {import("@mysten/sui/transactions").CallArg} CallArg
|
|
13
|
+
* @typedef {{ Pure: { bytes: string } }} PureInput
|
|
14
|
+
* @typedef {import("@mysten/sui/grpc").SuiGrpcClientOptions} SuiGrpcClientOptions
|
|
15
|
+
* @typedef {import("@mysten/sui/grpc").GrpcWebOptions} GrpcWebOptions
|
|
13
16
|
*/
|
|
14
17
|
|
|
15
18
|
export { normalizeSuiAddress };
|
|
@@ -21,16 +24,16 @@ export default class SuiUtils extends SuiCommonMethods {
|
|
|
21
24
|
* Attach the parameter input into transaction, to be used for moveCall
|
|
22
25
|
* accepts an Inputs.Pure (result of .pureInput) or type + value directly
|
|
23
26
|
*
|
|
24
|
-
* @param {Transaction} tx
|
|
25
|
-
* @param {string |
|
|
26
|
-
* @param {
|
|
27
|
-
* @returns Argument
|
|
27
|
+
* @param {Transaction} tx
|
|
28
|
+
* @param {string | PureInput} typeOrInput - string type name, or already-serialised Pure input `{ Pure: { bytes } }`
|
|
29
|
+
* @param {*} [value]
|
|
30
|
+
* @returns {Argument}
|
|
28
31
|
*/
|
|
29
32
|
static txInput(tx, typeOrInput, value = null) {
|
|
30
|
-
if (typeOrInput && typeOrInput.Pure && typeOrInput.Pure.bytes) {
|
|
31
|
-
return tx.pure(SuiUtils.pureInputToBytes(typeOrInput));
|
|
33
|
+
if (typeOrInput && /** @type {any} */ (typeOrInput).Pure && /** @type {any} */ (typeOrInput).Pure.bytes) {
|
|
34
|
+
return tx.pure(SuiUtils.pureInputToBytes(/** @type {PureInput} */ (typeOrInput)));
|
|
32
35
|
} else {
|
|
33
|
-
return tx.pure(SuiUtils.pureInputToBytes(SuiUtils.pureInput(typeOrInput, value)));
|
|
36
|
+
return tx.pure(SuiUtils.pureInputToBytes(/** @type {any} */ (SuiUtils.pureInput(/** @type {string} */ (typeOrInput), value))));
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
|
|
@@ -47,9 +50,9 @@ export default class SuiUtils extends SuiCommonMethods {
|
|
|
47
50
|
*
|
|
48
51
|
* if you are going to construct tx yourself, you'd better use SuiUtils.txInput static method
|
|
49
52
|
*
|
|
50
|
-
* @param {string} type
|
|
51
|
-
* @param {
|
|
52
|
-
* @returns
|
|
53
|
+
* @param {string} type
|
|
54
|
+
* @param {*} value
|
|
55
|
+
* @returns {CallArg}
|
|
53
56
|
*/
|
|
54
57
|
static pureInput(type, value) {
|
|
55
58
|
let typeNormalized = type;
|
|
@@ -76,16 +79,43 @@ export default class SuiUtils extends SuiCommonMethods {
|
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
}
|
|
82
|
+
|
|
83
|
+
throw new Error(`pureInput: unsupported type "${typeNormalized}"`);
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
/**
|
|
82
87
|
* Convert sui's PureInput into bcs serialized bytes
|
|
83
|
-
* @param {
|
|
88
|
+
* @param {PureInput} pureInput
|
|
84
89
|
*/
|
|
85
90
|
static pureInputToBytes(pureInput) {
|
|
86
91
|
return fromBase64(pureInput.Pure.bytes);
|
|
87
92
|
}
|
|
88
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Decode a Move `vector<u8>` field into a UTF-8 string.
|
|
96
|
+
*
|
|
97
|
+
* Accepts the shapes SUI returns across representations:
|
|
98
|
+
* - base64 string (v2 gRPC/GraphQL JSON representation for byte vectors)
|
|
99
|
+
* - Uint8Array
|
|
100
|
+
* - array of numbers (legacy JSON-RPC shape)
|
|
101
|
+
*
|
|
102
|
+
* @param {string | Uint8Array | number[]} value
|
|
103
|
+
* @returns {string}
|
|
104
|
+
*/
|
|
105
|
+
static bytesFieldToString(value) {
|
|
106
|
+
if (value == null) return '';
|
|
107
|
+
if (typeof value === 'string') {
|
|
108
|
+
return new TextDecoder().decode(fromBase64(value));
|
|
109
|
+
}
|
|
110
|
+
if (value instanceof Uint8Array) {
|
|
111
|
+
return new TextDecoder().decode(value);
|
|
112
|
+
}
|
|
113
|
+
if (Array.isArray(value)) {
|
|
114
|
+
return new TextDecoder().decode(new Uint8Array(value));
|
|
115
|
+
}
|
|
116
|
+
throw new Error('bytesFieldToString: unsupported value type');
|
|
117
|
+
}
|
|
118
|
+
|
|
89
119
|
/**
|
|
90
120
|
* Wrapper for sui's utils normalizeSuiAddress
|
|
91
121
|
* Perform the following operations:
|
|
@@ -103,139 +133,161 @@ export default class SuiUtils extends SuiCommonMethods {
|
|
|
103
133
|
}
|
|
104
134
|
|
|
105
135
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
136
|
+
* Makes an instance of SuiGrpcClient pointed at a custom gRPC-web endpoint.
|
|
137
|
+
*
|
|
138
|
+
* `transportOptions` is passed through to GrpcWebFetchTransport — the same shape
|
|
139
|
+
* as `@protobuf-ts/grpcweb-transport`'s GrpcWebOptions:
|
|
140
|
+
* - baseUrl : string — required, the gRPC-web endpoint
|
|
141
|
+
* - format : 'text' | 'binary' — wire format, defaults to 'text'
|
|
142
|
+
* - fetchInit : RequestInit-ish — extra fetch options (credentials, cache, ...); `body`/`headers`/`method`/`signal` are reserved
|
|
143
|
+
* - fetch : typeof fetch — custom fetch implementation (useful for proxies, auth, logging)
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* const client = SuiUtils.suiClientForRPC('mainnet', {
|
|
147
|
+
* baseUrl: 'https://fullnode.mainnet.sui.io:443',
|
|
148
|
+
* format: 'binary',
|
|
149
|
+
* fetchInit: { credentials: 'include' },
|
|
150
|
+
* fetch: (url, init) => fetch(url, { ...init, headers: { ...init.headers, 'x-api-key': 'xxx' } }),
|
|
151
|
+
* });
|
|
152
|
+
*
|
|
153
|
+
* @param {SuiClientTypes.Network} network
|
|
154
|
+
* @param {GrpcWebOptions} transportOptions
|
|
155
|
+
* @returns {SuiGrpcClient}
|
|
109
156
|
*/
|
|
110
|
-
static
|
|
111
|
-
|
|
157
|
+
static suiClientForRPC(network, transportOptions) {
|
|
158
|
+
const transport = new GrpcWebFetchTransport(transportOptions);
|
|
159
|
+
return new SuiGrpcClient({ network, transport });
|
|
112
160
|
}
|
|
113
161
|
|
|
114
162
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* @param {
|
|
121
|
-
* @
|
|
163
|
+
* Build a SuiGraphQLClient reusing the network of an existing SuiGrpcClient.
|
|
164
|
+
*
|
|
165
|
+
* Picks the default Sui GraphQL endpoint for the network unless `overrides.url` is set.
|
|
166
|
+
* `overrides` is forwarded to SuiGraphQLClient (fetch, headers, queries, mvr, url).
|
|
167
|
+
*
|
|
168
|
+
* @param {SuiGrpcClient} grpcClient
|
|
169
|
+
* @param {Object} [overrides]
|
|
170
|
+
* @param {?string} [overrides.url]
|
|
171
|
+
* @param {?typeof fetch} [overrides.fetch]
|
|
172
|
+
* @param {?Object.<string, string>} [overrides.headers]
|
|
173
|
+
* @returns {SuiGraphQLClient}
|
|
122
174
|
*/
|
|
123
|
-
static
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
delete params.chain;
|
|
128
|
-
params.WebSocketConstructor = SuiUtils.WebSocketConstructor();
|
|
175
|
+
static suiGraphQLClientFromGrpc(grpcClient, overrides = {}) {
|
|
176
|
+
if (!grpcClient || !grpcClient.network) {
|
|
177
|
+
throw new Error('grpcClient with a network is required');
|
|
178
|
+
}
|
|
129
179
|
|
|
130
|
-
const
|
|
131
|
-
|
|
180
|
+
const defaultUrls = {
|
|
181
|
+
mainnet: 'https://sui-mainnet.mystenlabs.com/graphql',
|
|
182
|
+
testnet: 'https://graphql.testnet.sui.io/graphql',
|
|
183
|
+
devnet: 'https://graphql.devnet.sui.io/graphql',
|
|
184
|
+
localnet: 'http://127.0.0.1:9125/graphql',
|
|
185
|
+
};
|
|
132
186
|
|
|
133
|
-
|
|
187
|
+
const url = overrides.url || defaultUrls[grpcClient.network];
|
|
188
|
+
if (!url) {
|
|
189
|
+
throw new Error(`no default GraphQL url for network "${grpcClient.network}" — pass overrides.url`);
|
|
190
|
+
}
|
|
134
191
|
|
|
135
|
-
return
|
|
192
|
+
return new SuiGraphQLClient({
|
|
193
|
+
...overrides,
|
|
194
|
+
url,
|
|
195
|
+
network: grpcClient.network,
|
|
196
|
+
});
|
|
136
197
|
}
|
|
137
198
|
|
|
138
199
|
/**
|
|
139
|
-
* Makes an instance for
|
|
140
|
-
* @param {
|
|
141
|
-
* @returns {
|
|
200
|
+
* Makes an instance for SuiGrpcClient for a specific chain, eg: 'mainnet' | 'testnet' | 'devnet' | 'localnet'
|
|
201
|
+
* @param {SuiClientTypes.Network} network
|
|
202
|
+
* @returns {SuiGrpcClient}
|
|
142
203
|
*/
|
|
143
|
-
static suiClientFor(
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return
|
|
204
|
+
static suiClientFor(network) {
|
|
205
|
+
// strip optional 'sui:' prefix so both 'mainnet' and 'sui:mainnet' work
|
|
206
|
+
const normalized = SuiUtils.normalizeNetworkString(network) ?? network;
|
|
207
|
+
const baseUrls = {
|
|
208
|
+
mainnet: 'https://fullnode.mainnet.sui.io:443',
|
|
209
|
+
testnet: 'https://fullnode.testnet.sui.io:443',
|
|
210
|
+
devnet: 'https://fullnode.devnet.sui.io:443',
|
|
211
|
+
localnet: 'http://127.0.0.1:9000',
|
|
212
|
+
};
|
|
213
|
+
return SuiUtils.suiClientForRPC(normalized, { baseUrl: baseUrls[normalized] });
|
|
153
214
|
}
|
|
154
215
|
|
|
155
216
|
/**
|
|
156
|
-
* Normalize
|
|
157
|
-
* -
|
|
158
|
-
* -
|
|
159
|
-
* -
|
|
160
|
-
* -
|
|
161
|
-
*
|
|
162
|
-
* @
|
|
217
|
+
* Normalize client parameter into a SuiGrpcClient, accepting:
|
|
218
|
+
* - an existing SuiGrpcClient instance
|
|
219
|
+
* - a SuiLocalTestValidator instance
|
|
220
|
+
* - a string chain name: 'local' | 'localnet' | 'dev' | 'devnet' | 'test' | 'testnet' | 'main' | 'mainnet'
|
|
221
|
+
* - a legacy client object (sniffs network from endpoint / connection.fullnode / providerName)
|
|
222
|
+
*
|
|
223
|
+
* @param {SuiGrpcClient | string | Object} clientParam
|
|
224
|
+
* @returns {SuiGrpcClient | null}
|
|
163
225
|
*/
|
|
164
226
|
static normalizeClient(clientParam) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
client = SuiUtils.suiClientFor('localnet');
|
|
172
|
-
providerName = 'sui:localnet';
|
|
173
|
-
} else {
|
|
174
|
-
// SuiLocalTestValidator
|
|
175
|
-
providerName = clientParam.providerName;
|
|
176
|
-
client = clientParam.client;
|
|
177
|
-
}
|
|
178
|
-
} else if (clientParam == 'test' || clientParam == 'testnet') {
|
|
179
|
-
client = SuiUtils.suiClientFor('testnet');
|
|
180
|
-
providerName = 'sui:testnet';
|
|
181
|
-
} else if (clientParam == 'dev' || clientParam == 'devnet') {
|
|
182
|
-
client = SuiUtils.suiClientFor('devnet');
|
|
183
|
-
providerName = 'sui:devnet';
|
|
184
|
-
} else if (clientParam == 'main' || clientParam == 'mainnet') {
|
|
185
|
-
client = SuiUtils.suiClientFor('mainnet');
|
|
186
|
-
providerName = 'sui:mainnet';
|
|
187
|
-
} else {
|
|
188
|
-
if (clientParam && clientParam.constructor && (clientParam.endpoint || clientParam.transport)) {
|
|
189
|
-
client = clientParam;
|
|
190
|
-
let url = '';
|
|
191
|
-
if (clientParam.endpoint) {
|
|
192
|
-
// workaround set in SuiInBrowserAdapter
|
|
193
|
-
url = clientParam.endpoint;
|
|
194
|
-
} else if (clientParam.transport && clientParam.transport.websocketClient && clientParam.transport.websocketClient.endpoint) {
|
|
195
|
-
url = clientParam.transport.websocketClient.endpoint;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (clientParam.providerName) {
|
|
199
|
-
providerName = clientParam.providerName;
|
|
200
|
-
if (['devnet', 'mainnet', 'testnet', 'localnet'].indexOf(clientParam.providerName) !== -1) {
|
|
201
|
-
providerName = 'sui:'+clientParam.providerName; // no prefix - add prefix
|
|
202
|
-
}
|
|
203
|
-
} else if (url.indexOf('devnet') !== -1) {
|
|
204
|
-
providerName = 'sui:devnet';
|
|
205
|
-
} else if (url.indexOf('testnet') !== -1) {
|
|
206
|
-
providerName = 'sui:testnet';
|
|
207
|
-
} else if (url.indexOf('mainnet') !== -1) {
|
|
208
|
-
providerName = 'sui:mainnet';
|
|
209
|
-
} else if (url.indexOf('127.0.0.1') !== -1) {
|
|
210
|
-
providerName = 'sui:localnet';
|
|
211
|
-
} else {
|
|
212
|
-
// just keep provider name as unique to fullnode URL to keep separate ObjectStorage instances
|
|
213
|
-
providerName = url.split('//')[1];
|
|
214
|
-
}
|
|
215
|
-
} else if (clientParam && clientParam.connection && clientParam.connection.fullnode) {
|
|
216
|
-
client = clientParam;
|
|
217
|
-
|
|
218
|
-
if (clientParam.connection.fullnode.indexOf('devnet') !== -1) {
|
|
219
|
-
providerName = 'sui:devnet';
|
|
220
|
-
} else if (clientParam.connection.fullnode.indexOf('testnet') !== -1) {
|
|
221
|
-
providerName = 'sui:testnet';
|
|
222
|
-
} else if (clientParam.connection.fullnode.indexOf('mainnet') !== -1) {
|
|
223
|
-
providerName = 'sui:mainnet';
|
|
224
|
-
} else if (clientParam.connection.fullnode.indexOf('127.0.0.1') !== -1) {
|
|
225
|
-
providerName = 'sui:localnet';
|
|
226
|
-
} else {
|
|
227
|
-
// just keep provider name as unique to fullnode URL to keep separate ObjectStorage instances
|
|
228
|
-
providerName = clientParam.connection.fullnode;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
227
|
+
if (!clientParam) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (clientParam instanceof SuiGrpcClient) {
|
|
232
|
+
return clientParam;
|
|
232
233
|
}
|
|
233
234
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return
|
|
235
|
+
const network = SuiUtils._sniffNetwork(clientParam);
|
|
236
|
+
if (!network) {
|
|
237
|
+
return null;
|
|
237
238
|
}
|
|
238
239
|
|
|
240
|
+
return SuiUtils.suiClientFor(network);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Strip an optional "sui:" prefix and map short aliases ('local', 'dev', 'test', 'main')
|
|
245
|
+
* to full network names. Returns null if the string doesn't match a known network.
|
|
246
|
+
*
|
|
247
|
+
* @param {string} s
|
|
248
|
+
* @returns {SuiClientTypes.Network | null}
|
|
249
|
+
*/
|
|
250
|
+
static normalizeNetworkString(s) {
|
|
251
|
+
if (typeof s !== 'string') return null;
|
|
252
|
+
const normalized = s.toLowerCase().replace(/^sui:/, '');
|
|
253
|
+
if (normalized === 'local' || normalized === 'localnet') return 'localnet';
|
|
254
|
+
if (normalized === 'dev' || normalized === 'devnet') return 'devnet';
|
|
255
|
+
if (normalized === 'test' || normalized === 'testnet') return 'testnet';
|
|
256
|
+
if (normalized === 'main' || normalized === 'mainnet') return 'mainnet';
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Extracts a SuiClientTypes.Network string from various inputs used historically by suidouble.
|
|
262
|
+
*
|
|
263
|
+
* @param {string | Object} clientParam
|
|
264
|
+
* @returns {SuiClientTypes.Network | null}
|
|
265
|
+
*/
|
|
266
|
+
static _sniffNetwork(clientParam) {
|
|
267
|
+
if (typeof clientParam === 'string') {
|
|
268
|
+
return SuiUtils.normalizeNetworkString(clientParam);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (clientParam.constructor && clientParam.constructor.name === 'SuiLocalTestValidator') {
|
|
272
|
+
return SuiUtils.normalizeNetworkString(clientParam.providerName) || 'localnet';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const fromNetwork = SuiUtils.normalizeNetworkString(clientParam.network);
|
|
276
|
+
if (fromNetwork) return fromNetwork;
|
|
277
|
+
|
|
278
|
+
const fromProvider = SuiUtils.normalizeNetworkString(clientParam.providerName);
|
|
279
|
+
if (fromProvider) return fromProvider;
|
|
280
|
+
|
|
281
|
+
const url = clientParam.endpoint
|
|
282
|
+
|| (clientParam.transport && clientParam.transport.websocketClient && clientParam.transport.websocketClient.endpoint)
|
|
283
|
+
|| (clientParam.connection && clientParam.connection.fullnode)
|
|
284
|
+
|| '';
|
|
285
|
+
|
|
286
|
+
if (url.indexOf('devnet') !== -1) return 'devnet';
|
|
287
|
+
if (url.indexOf('testnet') !== -1) return 'testnet';
|
|
288
|
+
if (url.indexOf('mainnet') !== -1) return 'mainnet';
|
|
289
|
+
if (url.indexOf('127.0.0.1') !== -1 || url.indexOf('localhost') !== -1) return 'localnet';
|
|
290
|
+
|
|
239
291
|
return null;
|
|
240
292
|
}
|
|
241
293
|
|