@scallop-io/sui-kit 2.1.0 → 2.2.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/README.md +6 -6
- package/dist/index.cjs +11 -11
- package/dist/index.d.cts +69 -69
- package/dist/index.d.ts +69 -69
- package/dist/index.js +7 -7
- package/package.json +163 -164
- package/src/index.ts +11 -11
- package/src/libs/multiSig/client.ts +35 -35
- package/src/libs/multiSig/index.ts +1 -1
- package/src/libs/multiSig/publickey.ts +8 -8
- package/src/libs/suiAccountManager/crypto.ts +4 -4
- package/src/libs/suiAccountManager/index.ts +82 -82
- package/src/libs/suiAccountManager/keypair.ts +13 -13
- package/src/libs/suiAccountManager/util.ts +20 -20
- package/src/libs/suiInteractor/index.ts +6 -6
- package/src/libs/suiInteractor/suiInteractor.ts +279 -272
- package/src/libs/suiInteractor/util.ts +6 -6
- package/src/libs/suiModel/index.ts +2 -2
- package/src/libs/suiModel/suiOwnedObject.ts +57 -57
- package/src/libs/suiModel/suiSharedObject.ts +27 -27
- package/src/libs/suiTxBuilder/index.ts +303 -302
- package/src/libs/suiTxBuilder/util.ts +185 -183
- package/src/suiKit.ts +422 -410
- package/src/types/index.ts +71 -76
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
1
|
+
import type { ClientWithCoreApi, SuiClientTypes } from "@mysten/sui/client";
|
|
2
|
+
import { SuiGrpcClient, type SuiGrpcClientOptions } from "@mysten/sui/grpc";
|
|
3
|
+
import type { NetworkType, SuiInteractorParams } from "../../types/index.js";
|
|
4
|
+
import { SuiOwnedObject, SuiSharedObject } from "../suiModel/index.js";
|
|
5
|
+
import { batch, delay } from "./util.js";
|
|
6
6
|
|
|
7
7
|
const MAX_OBJECTS_PER_REQUEST = 50;
|
|
8
8
|
|
|
9
9
|
// Helper to create gRPC client options with baseUrl
|
|
10
10
|
function createGrpcClientOptions(
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
url: string,
|
|
12
|
+
network: NetworkType,
|
|
13
13
|
): SuiGrpcClientOptions {
|
|
14
|
-
|
|
14
|
+
return { baseUrl: url, network } satisfies SuiGrpcClientOptions;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// Helper to get fullnode URLs for each network
|
|
18
18
|
function getFullnodeUrl(network: NetworkType): string {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
switch (network) {
|
|
20
|
+
case "mainnet":
|
|
21
|
+
return "https://fullnode.mainnet.sui.io:443";
|
|
22
|
+
case "testnet":
|
|
23
|
+
return "https://fullnode.testnet.sui.io:443";
|
|
24
|
+
case "devnet":
|
|
25
|
+
return "https://fullnode.devnet.sui.io:443";
|
|
26
|
+
case "localnet":
|
|
27
|
+
return "http://127.0.0.1:9000";
|
|
28
|
+
default:
|
|
29
|
+
throw new Error(`Unknown network: ${network}`);
|
|
30
|
+
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// Object data type from SDK v2
|
|
34
34
|
export type SuiObjectData = SuiClientTypes.Object<{
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
content: true;
|
|
36
|
+
json: true;
|
|
37
37
|
}>;
|
|
38
38
|
|
|
39
39
|
// Options for getObjects (SDK v2 naming)
|
|
@@ -41,283 +41,290 @@ export type SuiObjectDataOptions = SuiClientTypes.ObjectInclude;
|
|
|
41
41
|
|
|
42
42
|
// Simulate transaction response type
|
|
43
43
|
export type SimulateTransactionResponse =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
SuiClientTypes.SimulateTransactionResult<{
|
|
45
|
+
effects: true;
|
|
46
|
+
events: true;
|
|
47
|
+
balanceChanges: true;
|
|
48
|
+
commandResults: true;
|
|
49
|
+
}>;
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Encapsulates all functions that interact with the sui sdk
|
|
53
53
|
*/
|
|
54
54
|
export class SuiInteractor {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
private clients: ClientWithCoreApi[] = [];
|
|
56
|
+
public currentClient: ClientWithCoreApi;
|
|
57
|
+
private fullNodes: string[] = [];
|
|
58
|
+
private network: NetworkType;
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
constructor(params: Partial<SuiInteractorParams>) {
|
|
61
|
+
// Default network
|
|
62
|
+
this.network = "mainnet";
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
64
|
+
if ("fullnodeUrls" in params && params.fullnodeUrls) {
|
|
65
|
+
this.network = params.network ?? "mainnet";
|
|
66
|
+
this.fullNodes = params.fullnodeUrls;
|
|
67
|
+
this.clients = this.fullNodes.map(
|
|
68
|
+
(url) => new SuiGrpcClient(createGrpcClientOptions(url, this.network)),
|
|
69
|
+
);
|
|
70
|
+
} else if ("suiClients" in params && params.suiClients) {
|
|
71
|
+
this.clients = params.suiClients;
|
|
72
|
+
} else {
|
|
73
|
+
this.fullNodes = [getFullnodeUrl(this.network)];
|
|
74
|
+
this.clients = [
|
|
75
|
+
new SuiGrpcClient(
|
|
76
|
+
createGrpcClientOptions(this.fullNodes[0], this.network),
|
|
77
|
+
),
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
this.currentClient = this.clients[0];
|
|
81
|
+
}
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
getClient(idx: number) {
|
|
84
|
+
if (idx < 0 || idx >= this.clients.length) {
|
|
85
|
+
throw new Error("Client index out of bounds");
|
|
86
|
+
}
|
|
87
|
+
return this.clients[idx];
|
|
88
|
+
}
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (network) {
|
|
95
|
-
this.network = network;
|
|
96
|
-
}
|
|
97
|
-
this.clients = fullNodes.map(
|
|
98
|
-
(url) => new SuiGrpcClient(createGrpcClientOptions(url, this.network))
|
|
99
|
-
);
|
|
100
|
-
this.currentClient = this.clients[0];
|
|
101
|
-
}
|
|
90
|
+
switchToNextClient() {
|
|
91
|
+
const currentClientIdx = this.clients.indexOf(this.currentClient);
|
|
92
|
+
this.currentClient =
|
|
93
|
+
this.clients[(currentClientIdx + 1) % this.clients.length];
|
|
94
|
+
}
|
|
102
95
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
switchFullNodes(fullNodes: string[], network?: NetworkType) {
|
|
97
|
+
if (fullNodes.length === 0) {
|
|
98
|
+
throw new Error("fullNodes cannot be empty");
|
|
99
|
+
}
|
|
100
|
+
this.fullNodes = fullNodes;
|
|
101
|
+
if (network) {
|
|
102
|
+
this.network = network;
|
|
103
|
+
}
|
|
104
|
+
this.clients = fullNodes.map(
|
|
105
|
+
(url) => new SuiGrpcClient(createGrpcClientOptions(url, this.network)),
|
|
106
|
+
);
|
|
107
|
+
this.currentClient = this.clients[0];
|
|
108
|
+
}
|
|
107
109
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
get currentFullNode() {
|
|
111
|
+
if (this.fullNodes.length === 0) {
|
|
112
|
+
throw new Error("No full nodes available");
|
|
113
|
+
}
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
const clientIdx = this.clients.indexOf(this.currentClient);
|
|
116
|
+
if (clientIdx === -1) {
|
|
117
|
+
throw new Error("Current client not found");
|
|
118
|
+
}
|
|
115
119
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
signature: string | string[]
|
|
119
|
-
): Promise<
|
|
120
|
-
SuiClientTypes.TransactionResult<{
|
|
121
|
-
balanceChanges: true;
|
|
122
|
-
effects: true;
|
|
123
|
-
events: true;
|
|
124
|
-
objectTypes: true;
|
|
125
|
-
}>
|
|
126
|
-
> {
|
|
127
|
-
const txBytes =
|
|
128
|
-
typeof transactionBlock === 'string'
|
|
129
|
-
? Uint8Array.from(Buffer.from(transactionBlock, 'base64'))
|
|
130
|
-
: transactionBlock;
|
|
120
|
+
return this.fullNodes[clientIdx];
|
|
121
|
+
}
|
|
131
122
|
|
|
132
|
-
|
|
123
|
+
async sendTx(
|
|
124
|
+
transactionBlock: Uint8Array | string,
|
|
125
|
+
signature: string | string[],
|
|
126
|
+
): Promise<
|
|
127
|
+
SuiClientTypes.TransactionResult<{
|
|
128
|
+
balanceChanges: true;
|
|
129
|
+
effects: true;
|
|
130
|
+
events: true;
|
|
131
|
+
objectTypes: true;
|
|
132
|
+
}>
|
|
133
|
+
> {
|
|
134
|
+
const txBytes =
|
|
135
|
+
typeof transactionBlock === "string"
|
|
136
|
+
? Uint8Array.from(Buffer.from(transactionBlock, "base64"))
|
|
137
|
+
: transactionBlock;
|
|
133
138
|
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
return await this.clients[clientIdx].core.executeTransaction({
|
|
137
|
-
transaction: txBytes,
|
|
138
|
-
signatures,
|
|
139
|
-
include: {
|
|
140
|
-
balanceChanges: true,
|
|
141
|
-
effects: true,
|
|
142
|
-
events: true,
|
|
143
|
-
objectTypes: true,
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
} catch (err) {
|
|
147
|
-
console.warn(
|
|
148
|
-
`Failed to send transaction with fullnode ${this.fullNodes[clientIdx]}: ${err}`
|
|
149
|
-
);
|
|
150
|
-
await delay(2000);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
throw new Error('Failed to send transaction with all fullnodes');
|
|
154
|
-
}
|
|
139
|
+
const signatures = Array.isArray(signature) ? signature : [signature];
|
|
155
140
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
throw new Error('Failed to dry run transaction with all fullnodes');
|
|
178
|
-
}
|
|
141
|
+
for (const clientIdx in this.clients) {
|
|
142
|
+
try {
|
|
143
|
+
return await this.clients[clientIdx].core.executeTransaction({
|
|
144
|
+
transaction: txBytes,
|
|
145
|
+
signatures,
|
|
146
|
+
include: {
|
|
147
|
+
balanceChanges: true,
|
|
148
|
+
effects: true,
|
|
149
|
+
events: true,
|
|
150
|
+
objectTypes: true,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
} catch (err) {
|
|
154
|
+
console.warn(
|
|
155
|
+
`Failed to send transaction with fullnode ${this.fullNodes[clientIdx]}: ${err}`,
|
|
156
|
+
);
|
|
157
|
+
await delay(2000);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
throw new Error("Failed to send transaction with all fullnodes");
|
|
161
|
+
}
|
|
179
162
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
163
|
+
async dryRunTx(
|
|
164
|
+
transactionBlock: Uint8Array,
|
|
165
|
+
): Promise<SimulateTransactionResponse> {
|
|
166
|
+
for (const clientIdx in this.clients) {
|
|
167
|
+
try {
|
|
168
|
+
return await this.clients[clientIdx].core.simulateTransaction({
|
|
169
|
+
transaction: transactionBlock,
|
|
170
|
+
include: {
|
|
171
|
+
effects: true,
|
|
172
|
+
events: true,
|
|
173
|
+
balanceChanges: true,
|
|
174
|
+
commandResults: true,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.warn(
|
|
179
|
+
`Failed to dry run transaction with fullnode ${this.fullNodes[clientIdx]}: ${err}`,
|
|
180
|
+
);
|
|
181
|
+
await delay(2000);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
throw new Error("Failed to dry run transaction with all fullnodes");
|
|
185
|
+
}
|
|
189
186
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
187
|
+
async getObjects(
|
|
188
|
+
ids: string[],
|
|
189
|
+
options?: {
|
|
190
|
+
include?: SuiObjectDataOptions;
|
|
191
|
+
batchSize?: number;
|
|
192
|
+
switchClientDelay?: number;
|
|
193
|
+
},
|
|
194
|
+
): Promise<SuiObjectData[]> {
|
|
195
|
+
const include = options?.include ?? { content: true, json: true };
|
|
199
196
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
197
|
+
const batchIds = batch(
|
|
198
|
+
ids,
|
|
199
|
+
Math.max(
|
|
200
|
+
options?.batchSize ?? MAX_OBJECTS_PER_REQUEST,
|
|
201
|
+
MAX_OBJECTS_PER_REQUEST,
|
|
202
|
+
),
|
|
203
|
+
);
|
|
204
|
+
const results: SuiObjectData[] = [];
|
|
205
|
+
let lastError = null;
|
|
207
206
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
.filter((object): object is SuiObjectData => object !== null);
|
|
207
|
+
for (const batchChunk of batchIds) {
|
|
208
|
+
for (const clientIdx in this.clients) {
|
|
209
|
+
try {
|
|
210
|
+
const response = await this.clients[clientIdx].core.getObjects({
|
|
211
|
+
objectIds: batchChunk,
|
|
212
|
+
include,
|
|
213
|
+
});
|
|
216
214
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
if (lastError) {
|
|
229
|
-
throw new Error(
|
|
230
|
-
`Failed to get objects with all fullnodes: ${lastError}`
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
215
|
+
const parsedObjects = response.objects
|
|
216
|
+
.map((obj) => {
|
|
217
|
+
if (obj instanceof Error) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
return obj as SuiObjectData;
|
|
221
|
+
})
|
|
222
|
+
.filter((object): object is SuiObjectData => object !== null);
|
|
234
223
|
|
|
235
|
-
|
|
236
|
-
|
|
224
|
+
results.push(...parsedObjects);
|
|
225
|
+
lastError = null;
|
|
226
|
+
break; // Exit the client loop if successful
|
|
227
|
+
} catch (err) {
|
|
228
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
229
|
+
await delay(options?.switchClientDelay ?? 2000);
|
|
230
|
+
console.warn(
|
|
231
|
+
`Failed to get objects with fullnode ${this.fullNodes[clientIdx]}: ${err}`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (lastError) {
|
|
236
|
+
throw new Error(
|
|
237
|
+
`Failed to get objects with all fullnodes: ${lastError}`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
237
241
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return objects[0];
|
|
241
|
-
}
|
|
242
|
+
return results;
|
|
243
|
+
}
|
|
242
244
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
async updateObjects(suiObjects: (SuiOwnedObject | SuiSharedObject)[]) {
|
|
248
|
-
const objectIds = suiObjects.map((obj) => obj.objectId);
|
|
249
|
-
const objects = await this.getObjects(objectIds);
|
|
250
|
-
for (const object of objects) {
|
|
251
|
-
const suiObject = suiObjects.find(
|
|
252
|
-
(obj) => obj.objectId === object?.objectId
|
|
253
|
-
);
|
|
254
|
-
if (suiObject instanceof SuiSharedObject) {
|
|
255
|
-
const owner = object.owner;
|
|
256
|
-
if (owner && typeof owner === 'object' && 'Shared' in owner) {
|
|
257
|
-
suiObject.initialSharedVersion = (
|
|
258
|
-
owner as { Shared: { initialSharedVersion: string } }
|
|
259
|
-
).Shared.initialSharedVersion;
|
|
260
|
-
} else {
|
|
261
|
-
suiObject.initialSharedVersion = undefined;
|
|
262
|
-
}
|
|
263
|
-
} else if (suiObject instanceof SuiOwnedObject) {
|
|
264
|
-
suiObject.version = object?.version;
|
|
265
|
-
suiObject.digest = object?.digest;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
245
|
+
async getObject(id: string, options?: { include?: SuiObjectDataOptions }) {
|
|
246
|
+
const objects = await this.getObjects([id], options);
|
|
247
|
+
return objects[0];
|
|
248
|
+
}
|
|
269
249
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
});
|
|
297
|
-
// Sort the coins by balance in descending order
|
|
298
|
-
objects.sort((a, b) => parseInt(b.balance) - parseInt(a.balance));
|
|
299
|
-
for (const coinData of objects) {
|
|
300
|
-
selectedCoins.push({
|
|
301
|
-
objectId: coinData.objectId,
|
|
302
|
-
digest: coinData.digest,
|
|
303
|
-
version: coinData.version,
|
|
304
|
-
balance: coinData.balance,
|
|
305
|
-
});
|
|
306
|
-
totalAmount = totalAmount + parseInt(coinData.balance);
|
|
307
|
-
if (totalAmount >= amount) {
|
|
308
|
-
break;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
250
|
+
/**
|
|
251
|
+
* @description Update objects in a batch
|
|
252
|
+
* @param suiObjects
|
|
253
|
+
*/
|
|
254
|
+
async updateObjects(suiObjects: (SuiOwnedObject | SuiSharedObject)[]) {
|
|
255
|
+
const objectIds = suiObjects.map((obj) => obj.objectId);
|
|
256
|
+
const objects = await this.getObjects(objectIds);
|
|
257
|
+
for (const object of objects) {
|
|
258
|
+
const suiObject = suiObjects.find(
|
|
259
|
+
(obj) => obj.objectId === object?.objectId,
|
|
260
|
+
);
|
|
261
|
+
if (suiObject instanceof SuiSharedObject) {
|
|
262
|
+
const owner = object.owner;
|
|
263
|
+
if (owner && typeof owner === "object" && "Shared" in owner) {
|
|
264
|
+
suiObject.initialSharedVersion = (
|
|
265
|
+
owner as { Shared: { initialSharedVersion: string } }
|
|
266
|
+
).Shared.initialSharedVersion;
|
|
267
|
+
} else {
|
|
268
|
+
suiObject.initialSharedVersion = undefined;
|
|
269
|
+
}
|
|
270
|
+
} else if (suiObject instanceof SuiOwnedObject) {
|
|
271
|
+
suiObject.version = object?.version;
|
|
272
|
+
suiObject.digest = object?.digest;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
311
276
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
277
|
+
/**
|
|
278
|
+
* @description Select coins that add up to the given amount.
|
|
279
|
+
* @param addr the address of the owner
|
|
280
|
+
* @param amount the amount that is needed for the coin
|
|
281
|
+
* @param coinType the coin type, default is '0x2::SUI::SUI'
|
|
282
|
+
*/
|
|
283
|
+
async selectCoins(
|
|
284
|
+
addr: string,
|
|
285
|
+
amount: number,
|
|
286
|
+
coinType: string = "0x2::SUI::SUI",
|
|
287
|
+
) {
|
|
288
|
+
const selectedCoins: {
|
|
289
|
+
objectId: string;
|
|
290
|
+
digest: string;
|
|
291
|
+
version: string;
|
|
292
|
+
balance: string;
|
|
293
|
+
}[] = [];
|
|
294
|
+
let totalAmount = 0;
|
|
295
|
+
let hasNext = true,
|
|
296
|
+
nextCursor: string | null | undefined = null;
|
|
297
|
+
while (hasNext && totalAmount < amount) {
|
|
298
|
+
const { objects, hasNextPage, cursor } =
|
|
299
|
+
await this.currentClient.core.listCoins({
|
|
300
|
+
owner: addr,
|
|
301
|
+
coinType: coinType,
|
|
302
|
+
cursor: nextCursor,
|
|
303
|
+
});
|
|
304
|
+
// Sort the coins by balance in descending order
|
|
305
|
+
objects.sort((a, b) => parseInt(b.balance, 10) - parseInt(a.balance, 10));
|
|
306
|
+
for (const coinData of objects) {
|
|
307
|
+
selectedCoins.push({
|
|
308
|
+
objectId: coinData.objectId,
|
|
309
|
+
digest: coinData.digest,
|
|
310
|
+
version: coinData.version,
|
|
311
|
+
balance: coinData.balance,
|
|
312
|
+
});
|
|
313
|
+
totalAmount = totalAmount + parseInt(coinData.balance, 10);
|
|
314
|
+
if (totalAmount >= amount) {
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
315
318
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
319
|
+
nextCursor = cursor;
|
|
320
|
+
hasNext = hasNextPage;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (!selectedCoins.length) {
|
|
324
|
+
throw new Error("No valid coins found for the transaction.");
|
|
325
|
+
}
|
|
326
|
+
return selectedCoins;
|
|
327
|
+
}
|
|
321
328
|
}
|
|
322
329
|
|
|
323
330
|
export { getFullnodeUrl };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export const delay = (ms: number) =>
|
|
2
|
-
|
|
2
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
3
3
|
|
|
4
4
|
export const batch = <T>(arr: T[], size: number): T[][] => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const batches = [];
|
|
6
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
7
|
+
batches.push(arr.slice(i, i + size));
|
|
8
|
+
}
|
|
9
|
+
return batches;
|
|
10
10
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { SuiOwnedObject } from
|
|
2
|
-
export { SuiSharedObject } from
|
|
1
|
+
export { SuiOwnedObject } from "./suiOwnedObject.js";
|
|
2
|
+
export { SuiSharedObject } from "./suiSharedObject.js";
|