@0xobelisk/sui-client 0.4.9

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 (47) hide show
  1. package/LICENSE +92 -0
  2. package/README.md +284 -0
  3. package/dist/index.d.ts +9 -0
  4. package/dist/index.js +1311 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/index.mjs +1292 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/dist/libs/suiAccountManager/crypto.d.ts +1 -0
  9. package/dist/libs/suiAccountManager/index.d.ts +35 -0
  10. package/dist/libs/suiAccountManager/keypair.d.ts +21 -0
  11. package/dist/libs/suiAccountManager/util.d.ts +29 -0
  12. package/dist/libs/suiContractFactory/index.d.ts +20 -0
  13. package/dist/libs/suiContractFactory/types.d.ts +49 -0
  14. package/dist/libs/suiInteractor/defaultConfig.d.ts +10 -0
  15. package/dist/libs/suiInteractor/index.d.ts +2 -0
  16. package/dist/libs/suiInteractor/suiInteractor.d.ts +204 -0
  17. package/dist/libs/suiInteractor/util.d.ts +1 -0
  18. package/dist/libs/suiModel/index.d.ts +2 -0
  19. package/dist/libs/suiModel/suiOwnedObject.d.ts +24 -0
  20. package/dist/libs/suiModel/suiSharedObject.d.ts +12 -0
  21. package/dist/libs/suiTxBuilder/index.d.ts +544 -0
  22. package/dist/libs/suiTxBuilder/util.d.ts +76 -0
  23. package/dist/metadata/index.d.ts +34 -0
  24. package/dist/obelisk.d.ts +2568 -0
  25. package/dist/types/index.d.ts +141 -0
  26. package/dist/utils/index.d.ts +3 -0
  27. package/package.json +149 -0
  28. package/src/index.ts +9 -0
  29. package/src/libs/suiAccountManager/crypto.ts +7 -0
  30. package/src/libs/suiAccountManager/index.ts +72 -0
  31. package/src/libs/suiAccountManager/keypair.ts +38 -0
  32. package/src/libs/suiAccountManager/util.ts +70 -0
  33. package/src/libs/suiContractFactory/index.ts +120 -0
  34. package/src/libs/suiContractFactory/types.ts +61 -0
  35. package/src/libs/suiInteractor/defaultConfig.ts +32 -0
  36. package/src/libs/suiInteractor/index.ts +2 -0
  37. package/src/libs/suiInteractor/suiInteractor.ts +302 -0
  38. package/src/libs/suiInteractor/util.ts +2 -0
  39. package/src/libs/suiModel/index.ts +2 -0
  40. package/src/libs/suiModel/suiOwnedObject.ts +62 -0
  41. package/src/libs/suiModel/suiSharedObject.ts +33 -0
  42. package/src/libs/suiTxBuilder/index.ts +245 -0
  43. package/src/libs/suiTxBuilder/util.ts +84 -0
  44. package/src/metadata/index.ts +22 -0
  45. package/src/obelisk.ts +636 -0
  46. package/src/types/index.ts +211 -0
  47. package/src/utils/index.ts +23 -0
@@ -0,0 +1,32 @@
1
+ import {
2
+ localnetConnection,
3
+ devnetConnection,
4
+ testnetConnection,
5
+ mainnetConnection,
6
+ } from '@mysten/sui.js';
7
+ import type { Connection } from '@mysten/sui.js';
8
+ import type { NetworkType } from '../../types';
9
+ export const defaultGasBudget = 10 ** 8; // 0.1 SUI, should be enough for most of the transactions
10
+ export const defaultGasPrice = 1000; // 1000 MIST
11
+
12
+ /**
13
+ * @description Get the default fullnode url and faucet url for the given network type
14
+ * @param networkType, 'testnet' | 'mainnet' | 'devnet' | 'localnet', default is 'devnet'
15
+ * @returns { fullNode: string, websocket: string, faucet?: string }
16
+ */
17
+ export const getDefaultConnection = (
18
+ networkType: NetworkType = 'devnet'
19
+ ): Connection => {
20
+ switch (networkType) {
21
+ case 'localnet':
22
+ return localnetConnection;
23
+ case 'devnet':
24
+ return devnetConnection;
25
+ case 'testnet':
26
+ return testnetConnection;
27
+ case 'mainnet':
28
+ return mainnetConnection;
29
+ default:
30
+ return devnetConnection;
31
+ }
32
+ };
@@ -0,0 +1,2 @@
1
+ export { SuiInteractor } from './suiInteractor';
2
+ export { getDefaultConnection } from './defaultConfig';
@@ -0,0 +1,302 @@
1
+ import {
2
+ SuiTransactionBlockResponse,
3
+ SuiTransactionBlockResponseOptions,
4
+ JsonRpcProvider,
5
+ Connection,
6
+ getObjectDisplay,
7
+ getObjectFields,
8
+ getObjectId,
9
+ getObjectType,
10
+ getObjectVersion,
11
+ getSharedObjectInitialVersion,
12
+ DynamicFieldName,
13
+ SuiAddress,
14
+ } from '@mysten/sui.js';
15
+ import { requestSuiFromFaucetV0, getFaucetHost } from '@mysten/sui.js/faucet';
16
+ import { FaucetNetworkType, NetworkType, ObjectData } from '../../types';
17
+ import { SuiOwnedObject, SuiSharedObject } from '../suiModel';
18
+ import { delay } from './util';
19
+
20
+ /**
21
+ * `SuiTransactionSender` is used to send transaction with a given gas coin.
22
+ * It always uses the gas coin to pay for the gas,
23
+ * and update the gas coin after the transaction.
24
+ */
25
+ export class SuiInteractor {
26
+ public readonly providers: JsonRpcProvider[];
27
+ public currentProvider: JsonRpcProvider;
28
+ public network?: NetworkType;
29
+
30
+ constructor(fullNodeUrls: string[], network?: NetworkType) {
31
+ if (fullNodeUrls.length === 0)
32
+ throw new Error('fullNodeUrls must not be empty');
33
+ this.providers = fullNodeUrls.map(
34
+ (url) => new JsonRpcProvider(new Connection({ fullnode: url }))
35
+ );
36
+ this.currentProvider = this.providers[0];
37
+ this.network = network;
38
+ }
39
+
40
+ switchToNextProvider() {
41
+ const currentProviderIdx = this.providers.indexOf(this.currentProvider);
42
+ this.currentProvider =
43
+ this.providers[(currentProviderIdx + 1) % this.providers.length];
44
+ }
45
+
46
+ async sendTx(
47
+ transactionBlock: Uint8Array | string,
48
+ signature: string | string[]
49
+ ): Promise<SuiTransactionBlockResponse> {
50
+ const txResOptions: SuiTransactionBlockResponseOptions = {
51
+ showEvents: true,
52
+ showEffects: true,
53
+ showObjectChanges: true,
54
+ showBalanceChanges: true,
55
+ };
56
+
57
+ // const currentProviderIdx = this.providers.indexOf(this.currentProvider);
58
+ // const providers = [
59
+ // ...this.providers.slice(currentProviderIdx, this.providers.length),
60
+ // ...this.providers.slice(0, currentProviderIdx),
61
+ // ]
62
+
63
+ for (const provider of this.providers) {
64
+ try {
65
+ const res = await provider.executeTransactionBlock({
66
+ transactionBlock,
67
+ signature,
68
+ options: txResOptions,
69
+ });
70
+ return res;
71
+ } catch (err) {
72
+ console.warn(
73
+ `Failed to send transaction with fullnode ${provider.connection.fullnode}: ${err}`
74
+ );
75
+ await delay(2000);
76
+ }
77
+ }
78
+ throw new Error('Failed to send transaction with all fullnodes');
79
+ }
80
+
81
+ async getObjects(ids: string[]) {
82
+ const options = {
83
+ showContent: true,
84
+ showDisplay: true,
85
+ showType: true,
86
+ showOwner: true,
87
+ };
88
+
89
+ // const currentProviderIdx = this.providers.indexOf(this.currentProvider);
90
+ // const providers = [
91
+ // ...this.providers.slice(currentProviderIdx, this.providers.length),
92
+ // ...this.providers.slice(0, currentProviderIdx),
93
+ // ]
94
+
95
+ for (const provider of this.providers) {
96
+ try {
97
+ const objects = await provider.multiGetObjects({ ids, options });
98
+ const parsedObjects = objects.map((object) => {
99
+ const objectId = getObjectId(object);
100
+ const objectType = getObjectType(object);
101
+ const objectVersion = getObjectVersion(object);
102
+ const objectDigest = object.data ? object.data.digest : undefined;
103
+ const initialSharedVersion = getSharedObjectInitialVersion(object);
104
+ const objectFields = getObjectFields(object);
105
+ const objectDisplay = getObjectDisplay(object);
106
+ return {
107
+ objectId,
108
+ objectType,
109
+ objectVersion,
110
+ objectDigest,
111
+ objectFields,
112
+ objectDisplay,
113
+ initialSharedVersion,
114
+ };
115
+ });
116
+ return parsedObjects as ObjectData[];
117
+ } catch (err) {
118
+ await delay(2000);
119
+ console.warn(
120
+ `Failed to get objects with fullnode ${provider.connection.fullnode}: ${err}`
121
+ );
122
+ }
123
+ }
124
+ throw new Error('Failed to get objects with all fullnodes');
125
+ }
126
+
127
+ async getObject(id: string) {
128
+ const objects = await this.getObjects([id]);
129
+ return objects[0];
130
+ }
131
+
132
+ // async getEntitiesObjects(ids: string[]) {
133
+ // const options = {
134
+ // showContent: true,
135
+ // showType: true,
136
+ // };
137
+
138
+ // for (const provider of this.providers) {
139
+ // try {
140
+ // const objects = await provider.multiGetObjects({ ids, options });
141
+ // const parsedObjects = objects.map((object) => {
142
+ // const objectId = getObjectId(object);
143
+ // const objectFields = getObjectFields(object) as ObjectFieldType;
144
+ // const index = objectFields.name;
145
+ // const key = objectFields.value;
146
+ // return {
147
+ // objectId,
148
+ // index,
149
+ // key,
150
+ // };
151
+ // });
152
+ // return parsedObjects as EntityData[];
153
+ // } catch (err) {
154
+ // await delay(2000);
155
+ // console.warn(
156
+ // `Failed to get EntitiesObjects with fullnode ${provider.connection.fullnode}: ${err}`
157
+ // );
158
+ // }
159
+ // }
160
+ // throw new Error('Failed to get EntitiesObjects with all fullnodes');
161
+ // }
162
+
163
+ async getDynamicFieldObject(
164
+ parentId: string,
165
+ name: string | DynamicFieldName
166
+ ) {
167
+ for (const provider of this.providers) {
168
+ try {
169
+ return provider.getDynamicFieldObject({ parentId, name });
170
+ } catch (err) {
171
+ await delay(2000);
172
+ console.warn(
173
+ `Failed to get DynamicFieldObject with fullnode ${provider.connection.fullnode}: ${err}`
174
+ );
175
+ }
176
+ }
177
+ throw new Error('Failed to get DynamicFieldObject with all fullnodes');
178
+ }
179
+
180
+ async getDynamicFields(parentId: string, cursor?: string, limit?: number) {
181
+ for (const provider of this.providers) {
182
+ try {
183
+ return provider.getDynamicFields({ parentId, cursor, limit });
184
+ } catch (err) {
185
+ await delay(2000);
186
+ console.warn(
187
+ `Failed to get DynamicFields with fullnode ${provider.connection.fullnode}: ${err}`
188
+ );
189
+ }
190
+ }
191
+ throw new Error('Failed to get DynamicFields with all fullnodes');
192
+ }
193
+
194
+ async getOwnedObjects(owner: SuiAddress, cursor?: string, limit?: number) {
195
+ for (const provider of this.providers) {
196
+ try {
197
+ return await provider.getOwnedObjects({ owner, cursor, limit });
198
+ } catch (err) {
199
+ await delay(2000);
200
+ console.warn(
201
+ `Failed to get OwnedObjects with fullnode ${provider.connection.fullnode}: ${err}`
202
+ );
203
+ }
204
+ }
205
+ throw new Error('Failed to get OwnedObjects with all fullnodes');
206
+ }
207
+
208
+ async getNormalizedMoveModulesByPackage(packageId: string) {
209
+ for (const provider of this.providers) {
210
+ try {
211
+ return provider.getNormalizedMoveModulesByPackage({
212
+ package: packageId,
213
+ });
214
+ } catch (err) {
215
+ await delay(2000);
216
+ console.warn(
217
+ `Failed to get NormalizedMoveModules with fullnode ${provider.connection.fullnode}: ${err}`
218
+ );
219
+ }
220
+ }
221
+ throw new Error('Failed to get NormalizedMoveModules with all fullnodes');
222
+ }
223
+
224
+ /**
225
+ * @description Update objects in a batch
226
+ * @param suiObjects
227
+ */
228
+ async updateObjects(suiObjects: (SuiOwnedObject | SuiSharedObject)[]) {
229
+ const objectIds = suiObjects.map((obj) => obj.objectId);
230
+ const objects = await this.getObjects(objectIds);
231
+ for (const object of objects) {
232
+ const suiObject = suiObjects.find(
233
+ (obj) => obj.objectId === object.objectId
234
+ );
235
+ if (suiObject instanceof SuiSharedObject) {
236
+ suiObject.initialSharedVersion = object.initialSharedVersion;
237
+ } else if (suiObject instanceof SuiOwnedObject) {
238
+ suiObject.version = object.objectVersion;
239
+ suiObject.digest = object.objectDigest;
240
+ }
241
+ }
242
+ }
243
+
244
+ /**
245
+ * @description Select coins that add up to the given amount.
246
+ * @param addr the address of the owner
247
+ * @param amount the amount that is needed for the coin
248
+ * @param coinType the coin type, default is '0x2::SUI::SUI'
249
+ */
250
+ async selectCoins(
251
+ addr: string,
252
+ amount: number,
253
+ coinType: string = '0x2::SUI::SUI'
254
+ ) {
255
+ const selectedCoins: {
256
+ objectId: string;
257
+ digest: string;
258
+ version: string;
259
+ }[] = [];
260
+ let totalAmount = 0;
261
+ let hasNext = true,
262
+ nextCursor: string | null = null;
263
+ while (hasNext && totalAmount < amount) {
264
+ const coins = await this.currentProvider.getCoins({
265
+ owner: addr,
266
+ coinType: coinType,
267
+ cursor: nextCursor,
268
+ });
269
+ // Sort the coins by balance in descending order
270
+ coins.data.sort((a, b) => parseInt(b.balance) - parseInt(a.balance));
271
+ for (const coinData of coins.data) {
272
+ selectedCoins.push({
273
+ objectId: coinData.coinObjectId,
274
+ digest: coinData.digest,
275
+ version: coinData.version,
276
+ });
277
+ totalAmount = totalAmount + parseInt(coinData.balance);
278
+ if (totalAmount >= amount) {
279
+ break;
280
+ }
281
+ }
282
+
283
+ nextCursor = coins.nextCursor;
284
+ hasNext = coins.hasNextPage;
285
+ }
286
+
287
+ if (!selectedCoins.length) {
288
+ throw new Error('No valid coins found for the transaction.');
289
+ }
290
+ return selectedCoins;
291
+ }
292
+
293
+ async requestFaucet(address: SuiAddress, network: FaucetNetworkType) {
294
+ await requestSuiFromFaucetV0({
295
+ host: getFaucetHost(network),
296
+ recipient: address,
297
+ });
298
+ // let params = {
299
+ // owner: address
300
+ // } as GetBalanceParams;
301
+ }
302
+ }
@@ -0,0 +1,2 @@
1
+ export const delay = (ms: number) =>
2
+ new Promise((resolve) => setTimeout(resolve, ms));
@@ -0,0 +1,2 @@
1
+ export { SuiOwnedObject } from './suiOwnedObject';
2
+ export { SuiSharedObject } from './suiSharedObject';
@@ -0,0 +1,62 @@
1
+ import { Infer } from 'superstruct';
2
+ import {
3
+ getObjectChanges,
4
+ SuiTransactionBlockResponse,
5
+ ObjectCallArg,
6
+ ObjectId,
7
+ } from '@mysten/sui.js';
8
+
9
+ export class SuiOwnedObject {
10
+ public readonly objectId: string;
11
+ public version?: number | string;
12
+ public digest?: string;
13
+
14
+ constructor(param: { objectId: string; version?: string; digest?: string }) {
15
+ this.objectId = param.objectId;
16
+ this.version = param.version;
17
+ this.digest = param.digest;
18
+ }
19
+
20
+ /**
21
+ * Check if the object is fully initialized.
22
+ * So that when it's used as an input, it won't be necessary to fetch from fullnode again.
23
+ * Which can save time when sending transactions.
24
+ */
25
+ isFullObject(): boolean {
26
+ return !!this.version && !!this.digest;
27
+ }
28
+
29
+ asCallArg(): Infer<typeof ObjectCallArg> | Infer<typeof ObjectId> {
30
+ if (!this.version || !this.digest) {
31
+ return this.objectId;
32
+ }
33
+ return {
34
+ Object: {
35
+ ImmOrOwned: {
36
+ objectId: this.objectId,
37
+ version: this.version,
38
+ digest: this.digest,
39
+ },
40
+ },
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Update object version & digest based on the transaction response.
46
+ * @param txResponse
47
+ */
48
+ updateFromTxResponse(txResponse: SuiTransactionBlockResponse) {
49
+ const changes = getObjectChanges(txResponse);
50
+ if (!changes) {
51
+ throw new Error('Bad transaction response!');
52
+ }
53
+ for (const change of changes) {
54
+ if (change.type === 'mutated' && change.objectId === this.objectId) {
55
+ this.digest = change.digest;
56
+ this.version = change.version;
57
+ return;
58
+ }
59
+ }
60
+ throw new Error('Could not find object in transaction response!');
61
+ }
62
+ }
@@ -0,0 +1,33 @@
1
+ import { Infer } from 'superstruct';
2
+ import { ObjectCallArg, ObjectId } from '@mysten/sui.js';
3
+
4
+ export class SuiSharedObject {
5
+ public readonly objectId: string;
6
+ public initialSharedVersion?: number | string;
7
+
8
+ constructor(param: {
9
+ objectId: string;
10
+ initialSharedVersion?: number;
11
+ mutable?: boolean;
12
+ }) {
13
+ this.objectId = param.objectId;
14
+ this.initialSharedVersion = param.initialSharedVersion;
15
+ }
16
+
17
+ asCallArg(
18
+ mutable: boolean = false
19
+ ): Infer<typeof ObjectCallArg> | Infer<typeof ObjectId> {
20
+ if (!this.initialSharedVersion) {
21
+ return this.objectId;
22
+ }
23
+ return {
24
+ Object: {
25
+ Shared: {
26
+ objectId: this.objectId,
27
+ initialSharedVersion: this.initialSharedVersion,
28
+ mutable,
29
+ },
30
+ },
31
+ };
32
+ }
33
+ }
@@ -0,0 +1,245 @@
1
+ import {
2
+ TransactionBlock,
3
+ SUI_SYSTEM_STATE_OBJECT_ID,
4
+ TransactionExpiration,
5
+ SuiObjectRef,
6
+ SharedObjectRef,
7
+ JsonRpcProvider,
8
+ TransactionType,
9
+ Transactions,
10
+ ObjectCallArg,
11
+ } from '@mysten/sui.js';
12
+ import { convertArgs } from './util';
13
+ import type { SuiTxArg, SuiObjectArg, SuiVecTxArg } from '../../types';
14
+
15
+ export class SuiTxBlock {
16
+ public txBlock: TransactionBlock;
17
+ constructor(transaction?: TransactionBlock) {
18
+ this.txBlock = new TransactionBlock(transaction);
19
+ }
20
+
21
+ //======== override methods of TransactionBlock ============
22
+
23
+ address(value: string) {
24
+ return this.txBlock.pure(value, 'address');
25
+ }
26
+ pure(value: unknown, type?: string) {
27
+ return this.txBlock.pure(value, type);
28
+ }
29
+ object(value: string | ObjectCallArg) {
30
+ return this.txBlock.object(value);
31
+ }
32
+ objectRef(ref: SuiObjectRef) {
33
+ return this.txBlock.objectRef(ref);
34
+ }
35
+ sharedObjectRef(ref: SharedObjectRef) {
36
+ return this.txBlock.sharedObjectRef(ref);
37
+ }
38
+ setSender(sender: string) {
39
+ return this.txBlock.setSender(sender);
40
+ }
41
+ setSenderIfNotSet(sender: string) {
42
+ return this.txBlock.setSenderIfNotSet(sender);
43
+ }
44
+ setExpiration(expiration?: TransactionExpiration) {
45
+ return this.txBlock.setExpiration(expiration);
46
+ }
47
+ setGasPrice(price: number | bigint) {
48
+ return this.txBlock.setGasPrice(price);
49
+ }
50
+ setGasBudget(budget: number | bigint) {
51
+ return this.txBlock.setGasBudget(budget);
52
+ }
53
+ setGasOwner(owner: string) {
54
+ return this.txBlock.setGasOwner(owner);
55
+ }
56
+ setGasPayment(payments: SuiObjectRef[]) {
57
+ return this.txBlock.setGasPayment(payments);
58
+ }
59
+
60
+ add(transaction: TransactionType) {
61
+ return this.txBlock.add(transaction);
62
+ }
63
+ serialize() {
64
+ return this.txBlock.serialize();
65
+ }
66
+ build(
67
+ params: {
68
+ provider?: JsonRpcProvider;
69
+ onlyTransactionKind?: boolean;
70
+ } = {}
71
+ ) {
72
+ return this.txBlock.build(params);
73
+ }
74
+ getDigest({ provider }: { provider?: JsonRpcProvider } = {}) {
75
+ return this.txBlock.getDigest({ provider });
76
+ }
77
+
78
+ get gas() {
79
+ return this.txBlock.gas;
80
+ }
81
+ get blockData() {
82
+ return this.txBlock.blockData;
83
+ }
84
+
85
+ transferObjects(objects: SuiObjectArg[], recipient: string) {
86
+ const tx = this.txBlock;
87
+ tx.transferObjects(convertArgs(this.txBlock, objects), tx.pure(recipient));
88
+ return this;
89
+ }
90
+ splitCoins(coin: SuiObjectArg, amounts: number[]) {
91
+ const tx = this.txBlock;
92
+ const coinObject = convertArgs(this.txBlock, [coin])[0];
93
+ const res = tx.splitCoins(
94
+ coinObject,
95
+ amounts.map((m) => tx.pure(m))
96
+ );
97
+ return amounts.map((_, i) => res[i]);
98
+ }
99
+ mergeCoins(destination: SuiObjectArg, sources: SuiObjectArg[]) {
100
+ const destinationObject = convertArgs(this.txBlock, [destination])[0];
101
+ const sourceObjects = convertArgs(this.txBlock, sources);
102
+ return this.txBlock.mergeCoins(destinationObject, sourceObjects);
103
+ }
104
+ publish(...args: Parameters<(typeof Transactions)['Publish']>) {
105
+ return this.txBlock.publish(...args);
106
+ }
107
+ upgrade(...args: Parameters<(typeof Transactions)['Upgrade']>) {
108
+ return this.txBlock.upgrade(...args);
109
+ }
110
+ makeMoveVec(...args: Parameters<(typeof Transactions)['MakeMoveVec']>) {
111
+ return this.txBlock.makeMoveVec(...args);
112
+ }
113
+
114
+ /**
115
+ * @description Move call
116
+ * @param target `${string}::${string}::${string}`, e.g. `0x3::sui_system::request_add_stake`
117
+ * @param args the arguments of the move call, such as `['0x1', '0x2']`
118
+ * @param typeArguments the type arguments of the move call, such as `['0x2::sui::SUI']`
119
+ */
120
+ moveCall(
121
+ target: string,
122
+ args: (SuiTxArg | SuiVecTxArg)[] = [],
123
+ typeArguments: string[] = []
124
+ ) {
125
+ // a regex for pattern `${string}::${string}::${string}`
126
+ const regex =
127
+ /(?<package>[a-zA-Z0-9]+)::(?<module>[a-zA-Z0-9_]+)::(?<function>[a-zA-Z0-9_]+)/;
128
+ const match = target.match(regex);
129
+ if (match === null)
130
+ throw new Error(
131
+ 'Invalid target format. Expected `${string}::${string}::${string}`'
132
+ );
133
+ const convertedArgs = convertArgs(this.txBlock, args);
134
+ const tx = this.txBlock;
135
+ return tx.moveCall({
136
+ target: target as `${string}::${string}::${string}`,
137
+ arguments: convertedArgs,
138
+ typeArguments,
139
+ });
140
+ }
141
+
142
+ //======== enhance methods ============
143
+ transferSuiToMany(recipients: string[], amounts: number[]) {
144
+ // require recipients.length === amounts.length
145
+ if (recipients.length !== amounts.length) {
146
+ throw new Error(
147
+ 'transferSuiToMany: recipients.length !== amounts.length'
148
+ );
149
+ }
150
+
151
+ const tx = this.txBlock;
152
+ const coins = tx.splitCoins(
153
+ tx.gas,
154
+ amounts.map((amount) => tx.pure(amount))
155
+ );
156
+ recipients.forEach((recipient, index) => {
157
+ tx.transferObjects([coins[index]], tx.pure(recipient));
158
+ });
159
+ return this;
160
+ }
161
+
162
+ transferSui(recipient: string, amount: number) {
163
+ return this.transferSuiToMany([recipient], [amount]);
164
+ }
165
+
166
+ takeAmountFromCoins(coins: SuiObjectArg[], amount: number) {
167
+ const tx = this.txBlock;
168
+ const coinObjects = convertArgs(this.txBlock, coins);
169
+ const mergedCoin = coinObjects[0];
170
+ if (coins.length > 1) {
171
+ tx.mergeCoins(mergedCoin, coinObjects.slice(1));
172
+ }
173
+ const [sendCoin] = tx.splitCoins(mergedCoin, [tx.pure(amount)]);
174
+ return [sendCoin, mergedCoin];
175
+ }
176
+
177
+ splitSUIFromGas(amounts: number[]) {
178
+ const tx = this.txBlock;
179
+ return tx.splitCoins(
180
+ tx.gas,
181
+ amounts.map((m) => tx.pure(m))
182
+ );
183
+ }
184
+
185
+ splitMultiCoins(coins: SuiObjectArg[], amounts: number[]) {
186
+ const tx = this.txBlock;
187
+ const coinObjects = convertArgs(this.txBlock, coins);
188
+ const mergedCoin = coinObjects[0];
189
+ if (coins.length > 1) {
190
+ tx.mergeCoins(mergedCoin, coinObjects.slice(1));
191
+ }
192
+ const splitedCoins = tx.splitCoins(
193
+ mergedCoin,
194
+ amounts.map((m) => tx.pure(m))
195
+ );
196
+ return { splitedCoins, mergedCoin };
197
+ }
198
+
199
+ transferCoinToMany(
200
+ inputCoins: SuiObjectArg[],
201
+ sender: string,
202
+ recipients: string[],
203
+ amounts: number[]
204
+ ) {
205
+ // require recipients.length === amounts.length
206
+ if (recipients.length !== amounts.length) {
207
+ throw new Error(
208
+ 'transferSuiToMany: recipients.length !== amounts.length'
209
+ );
210
+ }
211
+ const tx = this.txBlock;
212
+ const { splitedCoins, mergedCoin } = this.splitMultiCoins(
213
+ inputCoins,
214
+ amounts
215
+ );
216
+ recipients.forEach((recipient, index) => {
217
+ tx.transferObjects([splitedCoins[index]], tx.pure(recipient));
218
+ });
219
+ tx.transferObjects([mergedCoin], tx.pure(sender));
220
+ return this;
221
+ }
222
+
223
+ transferCoin(
224
+ inputCoins: SuiObjectArg[],
225
+ sender: string,
226
+ recipient: string,
227
+ amount: number
228
+ ) {
229
+ return this.transferCoinToMany(inputCoins, sender, [recipient], [amount]);
230
+ }
231
+
232
+ stakeSui(amount: number, validatorAddr: string) {
233
+ const tx = this.txBlock;
234
+ const [stakeCoin] = tx.splitCoins(tx.gas, [tx.pure(amount)]);
235
+ tx.moveCall({
236
+ target: '0x3::sui_system::request_add_stake',
237
+ arguments: [
238
+ tx.object(SUI_SYSTEM_STATE_OBJECT_ID),
239
+ stakeCoin,
240
+ tx.pure(validatorAddr),
241
+ ],
242
+ });
243
+ return tx;
244
+ }
245
+ }