@bolt-liquidity-hq/sui-client 0.1.0-beta.10 → 0.1.0-beta.11
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/dist/index.cjs +714 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +104 -12
- package/dist/index.d.ts +104 -12
- package/dist/index.js +709 -72
- package/dist/index.js.map +1 -1
- package/package.json +14 -13
package/dist/index.cjs
CHANGED
|
@@ -25,6 +25,7 @@ __export(index_exports, {
|
|
|
25
25
|
AssetPairStruct: () => AssetPairStruct,
|
|
26
26
|
AssetPairsResponsePaginatedStruct: () => AssetPairsResponsePaginatedStruct,
|
|
27
27
|
AssetPairsResponseStruct: () => AssetPairsResponseStruct,
|
|
28
|
+
BaseLiquidityInfoStruct: () => BaseLiquidityInfoStruct,
|
|
28
29
|
BaseLiquidityResponseStruct: () => BaseLiquidityResponseStruct,
|
|
29
30
|
BcsAddressType: () => BcsAddressType,
|
|
30
31
|
BoltSuiClient: () => BoltSuiClient,
|
|
@@ -44,10 +45,11 @@ __export(index_exports, {
|
|
|
44
45
|
module.exports = __toCommonJS(index_exports);
|
|
45
46
|
|
|
46
47
|
// src/lib/client.ts
|
|
47
|
-
var
|
|
48
|
+
var import_core8 = require("@bolt-liquidity-hq/core");
|
|
48
49
|
var import_client = require("@mysten/sui/client");
|
|
49
50
|
|
|
50
51
|
// src/config/mainnet.ts
|
|
52
|
+
var import_utils = require("@mysten/sui/utils");
|
|
51
53
|
var MainnetChainConfig = {
|
|
52
54
|
name: "Sui",
|
|
53
55
|
id: "101",
|
|
@@ -58,13 +60,15 @@ var MainnetContracts = {
|
|
|
58
60
|
router: "0x..."
|
|
59
61
|
};
|
|
60
62
|
var MainnetPackageId = "0x...";
|
|
63
|
+
var MainnetPoolGlobalConfigId = "0x...";
|
|
64
|
+
var MainnetNativeTokenDenom = import_utils.SUI_TYPE_ARG;
|
|
61
65
|
var MainnetAssets = {
|
|
62
|
-
|
|
66
|
+
[import_utils.SUI_TYPE_ARG]: {
|
|
63
67
|
symbol: "SUI",
|
|
64
68
|
name: "Sui",
|
|
65
69
|
chainId: "101",
|
|
66
|
-
denom:
|
|
67
|
-
decimals:
|
|
70
|
+
denom: import_utils.SUI_TYPE_ARG,
|
|
71
|
+
decimals: import_utils.SUI_DECIMALS,
|
|
68
72
|
logo: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/sui/info/logo.png",
|
|
69
73
|
coingeckoId: "sui"
|
|
70
74
|
},
|
|
@@ -80,6 +84,7 @@ var MainnetAssets = {
|
|
|
80
84
|
};
|
|
81
85
|
|
|
82
86
|
// src/config/testnet.ts
|
|
87
|
+
var import_utils2 = require("@mysten/sui/utils");
|
|
83
88
|
var TestnetChainConfig = {
|
|
84
89
|
name: "Sui Testnet",
|
|
85
90
|
id: "103",
|
|
@@ -90,13 +95,15 @@ var TestnetContracts = {
|
|
|
90
95
|
router: "0x3881fdcb4a7fbcda8edc230e6f92eb57b24c0be6b44af0b5e1d0b45564f3ed00"
|
|
91
96
|
};
|
|
92
97
|
var TestnetPackageId = "0x22384b1841229e2be878bb7e88833c03e23ff5dc39accd050fb932120602f85e";
|
|
98
|
+
var TestnetPoolGlobalConfigId = "0xa9491e59fa63e666cbfdea2e880b2d8ba0085eeb244220cefa3fb817559cd79c";
|
|
99
|
+
var TestnetNativeTokenDenom = import_utils2.SUI_TYPE_ARG;
|
|
93
100
|
var TestnetAssets = {
|
|
94
|
-
|
|
101
|
+
[import_utils2.SUI_TYPE_ARG]: {
|
|
95
102
|
symbol: "SUI",
|
|
96
103
|
name: "Sui",
|
|
97
104
|
chainId: "103",
|
|
98
|
-
denom:
|
|
99
|
-
decimals:
|
|
105
|
+
denom: import_utils2.SUI_TYPE_ARG,
|
|
106
|
+
decimals: import_utils2.SUI_DECIMALS,
|
|
100
107
|
logo: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/sui/info/logo.png",
|
|
101
108
|
coingeckoId: "sui"
|
|
102
109
|
},
|
|
@@ -159,8 +166,407 @@ var parseMultipleResults = (info, schema) => {
|
|
|
159
166
|
return schema.map((bcsType, index) => parseDevInspectResult(info, bcsType, 0, index));
|
|
160
167
|
};
|
|
161
168
|
|
|
162
|
-
// src/lib/helpers/
|
|
169
|
+
// src/lib/helpers/coin-manager.ts
|
|
163
170
|
var import_core2 = require("@bolt-liquidity-hq/core");
|
|
171
|
+
var import_utils3 = require("@mysten/sui/utils");
|
|
172
|
+
var CoinManager = class {
|
|
173
|
+
/**
|
|
174
|
+
* Creates a new CoinManager instance.
|
|
175
|
+
*
|
|
176
|
+
* @param client - The SuiClient instance for blockchain interactions
|
|
177
|
+
* @param defaultOwner - Optional default owner address for coin operations.
|
|
178
|
+
* If not provided, owner must be specified in each method call.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* // With default owner
|
|
183
|
+
* const coinManager = new CoinManager(suiClient, "0x123...");
|
|
184
|
+
*
|
|
185
|
+
* // Without default owner (must specify in each call)
|
|
186
|
+
* const coinManager = new CoinManager(suiClient);
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
constructor(client, defaultOwner) {
|
|
190
|
+
this.client = client;
|
|
191
|
+
this.defaultOwner = defaultOwner;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Fetches all coins of a specific type owned by an address.
|
|
195
|
+
*
|
|
196
|
+
* This method handles pagination automatically to retrieve all coins,
|
|
197
|
+
* regardless of how many exist. Coins are returned in the order
|
|
198
|
+
* provided by the blockchain.
|
|
199
|
+
*
|
|
200
|
+
* @param coinType - The fully qualified type of the coin (e.g., "0x2::sui::SUI")
|
|
201
|
+
* @param owner - Optional owner address. Uses defaultOwner if not provided.
|
|
202
|
+
*
|
|
203
|
+
* @returns A promise that resolves to an array of all coins of the specified type
|
|
204
|
+
*
|
|
205
|
+
* @throws {MissingParameterError} When owner is not provided and no defaultOwner is set
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* // Get all SUI coins
|
|
210
|
+
* const suiCoins = await coinManager.getAllCoins("0x2::sui::SUI");
|
|
211
|
+
* console.log(`Found ${suiCoins.length} SUI coins`);
|
|
212
|
+
*
|
|
213
|
+
* // Get all USDC coins for a specific owner
|
|
214
|
+
* const usdcCoins = await coinManager.getAllCoins(
|
|
215
|
+
* "0x5d4b...::coin::COIN",
|
|
216
|
+
* "0xowner..."
|
|
217
|
+
* );
|
|
218
|
+
*
|
|
219
|
+
* // Display coin details
|
|
220
|
+
* suiCoins.forEach(coin => {
|
|
221
|
+
* console.log(`Coin ID: ${coin.coinObjectId}`);
|
|
222
|
+
* console.log(`Balance: ${coin.balance}`);
|
|
223
|
+
* });
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
async getAllCoins(coinType, owner) {
|
|
227
|
+
const ownerAddress = this.getOwnerAddress(owner);
|
|
228
|
+
const allCoins = [];
|
|
229
|
+
let cursor;
|
|
230
|
+
do {
|
|
231
|
+
const response = await this.client.getCoins({
|
|
232
|
+
owner: ownerAddress,
|
|
233
|
+
coinType,
|
|
234
|
+
cursor
|
|
235
|
+
});
|
|
236
|
+
allCoins.push(...response.data);
|
|
237
|
+
cursor = response.hasNextPage ? response.nextCursor : void 0;
|
|
238
|
+
} while (cursor);
|
|
239
|
+
return allCoins;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Resolves the owner address from the provided parameter or default.
|
|
243
|
+
*
|
|
244
|
+
* This is a helper method used internally to ensure an owner address
|
|
245
|
+
* is always available for coin operations.
|
|
246
|
+
*
|
|
247
|
+
* @param owner - Optional owner address to use
|
|
248
|
+
*
|
|
249
|
+
* @returns The resolved owner address
|
|
250
|
+
*
|
|
251
|
+
* @throws {MissingParameterError} When owner is not provided and no defaultOwner is set
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* // With default owner set
|
|
256
|
+
* const address = coinManager.getOwnerAddress(); // Returns defaultOwner
|
|
257
|
+
*
|
|
258
|
+
* // With explicit owner
|
|
259
|
+
* const address = coinManager.getOwnerAddress("0x123..."); // Returns "0x123..."
|
|
260
|
+
*
|
|
261
|
+
* // Without default owner or parameter
|
|
262
|
+
* const address = coinManager.getOwnerAddress(); // Throws MissingParameterError
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
getOwnerAddress(owner) {
|
|
266
|
+
const address = owner || this.defaultOwner;
|
|
267
|
+
if (!address) throw new import_core2.MissingParameterError("owner");
|
|
268
|
+
return address;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Gets the total balance of a specific coin type for an owner.
|
|
272
|
+
*
|
|
273
|
+
* This method returns the sum of all coins of the specified type,
|
|
274
|
+
* providing a quick way to check if sufficient funds are available
|
|
275
|
+
* without fetching all coin objects.
|
|
276
|
+
*
|
|
277
|
+
* @param coinType - The fully qualified type of the coin (e.g., "0x2::sui::SUI")
|
|
278
|
+
* @param owner - Optional owner address. Uses defaultOwner if not provided.
|
|
279
|
+
*
|
|
280
|
+
* @returns A promise that resolves to the total balance as a string
|
|
281
|
+
*
|
|
282
|
+
* @throws {MissingParameterError} When owner is not provided and no defaultOwner is set
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```typescript
|
|
286
|
+
* // Get SUI balance
|
|
287
|
+
* const suiBalance = await coinManager.getTotalBalance("0x2::sui::SUI");
|
|
288
|
+
* console.log(`Total SUI: ${suiBalance}`);
|
|
289
|
+
*
|
|
290
|
+
* // Get USDC balance for specific owner
|
|
291
|
+
* const usdcBalance = await coinManager.getTotalBalance(
|
|
292
|
+
* "0x5d4b...::coin::COIN",
|
|
293
|
+
* "0xowner..."
|
|
294
|
+
* );
|
|
295
|
+
*
|
|
296
|
+
* // Convert to decimal for display (assuming 9 decimals for SUI)
|
|
297
|
+
* const suiDecimal = Number(suiBalance) / 1e9;
|
|
298
|
+
* console.log(`${suiDecimal} SUI`);
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
async getTotalBalance(coinType, owner) {
|
|
302
|
+
const ownerAddress = this.getOwnerAddress(owner);
|
|
303
|
+
const balance = await this.client.getBalance({
|
|
304
|
+
owner: ownerAddress,
|
|
305
|
+
coinType
|
|
306
|
+
});
|
|
307
|
+
return balance.totalBalance;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Asserts that the owner has sufficient balance for a transaction.
|
|
311
|
+
*
|
|
312
|
+
* This is a convenience method that checks the balance and throws
|
|
313
|
+
* a descriptive error if insufficient. Useful for fail-fast validation
|
|
314
|
+
* before attempting complex transaction preparation.
|
|
315
|
+
*
|
|
316
|
+
* @param coinType - The fully qualified type of the coin (e.g., "0x2::sui::SUI")
|
|
317
|
+
* @param amount - The required amount as a string
|
|
318
|
+
* @param owner - Optional owner address. Uses defaultOwner if not provided.
|
|
319
|
+
*
|
|
320
|
+
* @throws {MissingParameterError} When owner is not provided and no defaultOwner is set
|
|
321
|
+
* @throws {InsufficientFundsError} When the balance is less than the required amount
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```typescript
|
|
325
|
+
* try {
|
|
326
|
+
* // Check if user has 1 SUI
|
|
327
|
+
* await coinManager.assertEnoughBalance(
|
|
328
|
+
* "0x2::sui::SUI",
|
|
329
|
+
* "1000000000" // 1 SUI
|
|
330
|
+
* );
|
|
331
|
+
*
|
|
332
|
+
* // Proceed with transaction
|
|
333
|
+
* console.log("Sufficient balance confirmed");
|
|
334
|
+
* } catch (error) {
|
|
335
|
+
* if (error instanceof InsufficientFundsError) {
|
|
336
|
+
* console.error(`Need ${error.data.amount}, but only have ${error.data.available}`);
|
|
337
|
+
* }
|
|
338
|
+
* }
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
async assertEnoughBalance(coinType, amount, owner) {
|
|
342
|
+
const ownerAddress = this.getOwnerAddress(owner);
|
|
343
|
+
const totalBalance = await this.getTotalBalance(coinType, owner);
|
|
344
|
+
if (BigInt(totalBalance) < BigInt(amount)) {
|
|
345
|
+
throw new import_core2.InsufficientFundsError(amount, totalBalance, { coinType, amount, ownerAddress });
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Selects optimal coins to cover a specific amount.
|
|
350
|
+
*
|
|
351
|
+
* This method implements intelligent coin selection algorithms that consider
|
|
352
|
+
* various factors like minimizing inputs, preserving large coins, and
|
|
353
|
+
* avoiding fragmentation. It determines whether merging or splitting is needed.
|
|
354
|
+
*
|
|
355
|
+
* @param coinType - The fully qualified type of the coin (e.g., "0x2::sui::SUI")
|
|
356
|
+
* @param amount - The target amount to select for, as a string
|
|
357
|
+
* @param options - Configuration options for the selection algorithm
|
|
358
|
+
* @param owner - Optional owner address. Uses defaultOwner if not provided.
|
|
359
|
+
*
|
|
360
|
+
* @returns A promise that resolves to a CoinSelectionResult with selected coins and metadata
|
|
361
|
+
*
|
|
362
|
+
* @throws {MissingParameterError} When owner is not provided and no defaultOwner is set
|
|
363
|
+
* @throws {InsufficientFundsError} When available coins cannot cover the amount (unless allowPartial is true)
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* ```typescript
|
|
367
|
+
* // Basic selection
|
|
368
|
+
* const selection = await coinManager.selectCoins(
|
|
369
|
+
* "0x2::sui::SUI",
|
|
370
|
+
* "1000000000" // 1 SUI
|
|
371
|
+
* );
|
|
372
|
+
*
|
|
373
|
+
* if (selection.requiresMerge) {
|
|
374
|
+
* console.log(`Need to merge ${selection.coins.length} coins`);
|
|
375
|
+
* }
|
|
376
|
+
* if (selection.requiresSplit) {
|
|
377
|
+
* console.log(`Need to split coin for exact amount`);
|
|
378
|
+
* }
|
|
379
|
+
*
|
|
380
|
+
* // Minimize number of inputs
|
|
381
|
+
* const minimalSelection = await coinManager.selectCoins(
|
|
382
|
+
* "0x2::sui::SUI",
|
|
383
|
+
* "5000000000",
|
|
384
|
+
* { minimizeInputs: true }
|
|
385
|
+
* );
|
|
386
|
+
*
|
|
387
|
+
* // Exclude specific coins and allow partial
|
|
388
|
+
* const partialSelection = await coinManager.selectCoins(
|
|
389
|
+
* "0x2::sui::SUI",
|
|
390
|
+
* "10000000000",
|
|
391
|
+
* {
|
|
392
|
+
* excludeIds: ["0x123..."],
|
|
393
|
+
* allowPartial: true
|
|
394
|
+
* }
|
|
395
|
+
* );
|
|
396
|
+
* ```
|
|
397
|
+
*
|
|
398
|
+
* @remarks
|
|
399
|
+
* The selection algorithm follows these priorities:
|
|
400
|
+
* 1. Exact match - single coin with exact amount
|
|
401
|
+
* 2. Single coin that covers amount (requires split)
|
|
402
|
+
* 3. Multiple coins (requires merge, possibly split)
|
|
403
|
+
*
|
|
404
|
+
* When minimizeInputs is false, smaller coins are used first to preserve larger ones.
|
|
405
|
+
*/
|
|
406
|
+
async selectCoins(coinType, amount, options = {}, owner) {
|
|
407
|
+
const ownerAddress = this.getOwnerAddress(owner);
|
|
408
|
+
const coins = await this.getAllCoins(coinType, owner);
|
|
409
|
+
const availableCoins = options.excludeIds ? coins.filter((c) => !options.excludeIds?.includes(c.coinObjectId)) : coins;
|
|
410
|
+
if (availableCoins.length === 0) {
|
|
411
|
+
throw new import_core2.InsufficientFundsError(amount, "0", {
|
|
412
|
+
availableCoins,
|
|
413
|
+
coinType,
|
|
414
|
+
ownerAddress
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
const targetAmount = BigInt(amount);
|
|
418
|
+
availableCoins.sort((a, b) => {
|
|
419
|
+
const diff = BigInt(b.balance) - BigInt(a.balance);
|
|
420
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
421
|
+
});
|
|
422
|
+
const exactMatch = availableCoins.find((c) => BigInt(c.balance) === targetAmount);
|
|
423
|
+
if (exactMatch) {
|
|
424
|
+
return {
|
|
425
|
+
coins: [exactMatch],
|
|
426
|
+
totalBalance: amount,
|
|
427
|
+
requiresMerge: false,
|
|
428
|
+
requiresSplit: false
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
const singleCoin = availableCoins.find((c) => BigInt(c.balance) > targetAmount);
|
|
432
|
+
if (singleCoin && !options.preferMerge) {
|
|
433
|
+
return {
|
|
434
|
+
coins: [singleCoin],
|
|
435
|
+
totalBalance: singleCoin.balance,
|
|
436
|
+
requiresMerge: false,
|
|
437
|
+
requiresSplit: true
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
const selectedCoins = [];
|
|
441
|
+
let totalBalance = 0n;
|
|
442
|
+
if (options.minimizeInputs) {
|
|
443
|
+
for (const coin of availableCoins) {
|
|
444
|
+
selectedCoins.push(coin);
|
|
445
|
+
totalBalance += BigInt(coin.balance);
|
|
446
|
+
if (totalBalance >= targetAmount) break;
|
|
447
|
+
}
|
|
448
|
+
} else {
|
|
449
|
+
const sortedAsc = [...availableCoins].sort((a, b) => {
|
|
450
|
+
const diff = BigInt(a.balance) - BigInt(b.balance);
|
|
451
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
452
|
+
});
|
|
453
|
+
for (const coin of sortedAsc) {
|
|
454
|
+
if (totalBalance >= targetAmount) break;
|
|
455
|
+
selectedCoins.push(coin);
|
|
456
|
+
totalBalance += BigInt(coin.balance);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (totalBalance < targetAmount) {
|
|
460
|
+
if (options.allowPartial) {
|
|
461
|
+
return {
|
|
462
|
+
coins: selectedCoins,
|
|
463
|
+
totalBalance: totalBalance.toString(),
|
|
464
|
+
requiresMerge: selectedCoins.length > 1,
|
|
465
|
+
requiresSplit: false
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
throw new import_core2.InsufficientFundsError(amount, totalBalance.toString(), {
|
|
469
|
+
amount,
|
|
470
|
+
coinType,
|
|
471
|
+
totalBalance: totalBalance.toString(),
|
|
472
|
+
ownerAddress
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
return {
|
|
476
|
+
coins: selectedCoins,
|
|
477
|
+
totalBalance: totalBalance.toString(),
|
|
478
|
+
requiresMerge: selectedCoins.length > 1,
|
|
479
|
+
requiresSplit: totalBalance > targetAmount
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Prepares a coin input for a transaction with automatic merging and splitting.
|
|
484
|
+
*
|
|
485
|
+
* This is the main method for transaction preparation. It handles all the complexity
|
|
486
|
+
* of coin selection, merging multiple coins if needed, and splitting for exact amounts.
|
|
487
|
+
* Special handling is provided for SUI gas coins.
|
|
488
|
+
*
|
|
489
|
+
* @param tx - The Transaction object to add coin operations to
|
|
490
|
+
* @param coinType - The fully qualified type of the coin (e.g., "0x2::sui::SUI")
|
|
491
|
+
* @param amount - The exact amount needed for the transaction, as a string
|
|
492
|
+
* @param options - Configuration options for coin selection
|
|
493
|
+
* @param owner - Optional owner address. Uses defaultOwner if not provided.
|
|
494
|
+
*
|
|
495
|
+
* @returns A promise that resolves to an Argument that can be used in Move calls
|
|
496
|
+
*
|
|
497
|
+
* @throws {MissingParameterError} When owner is not provided and no defaultOwner is set
|
|
498
|
+
* @throws {InsufficientFundsError} When available coins cannot cover the amount
|
|
499
|
+
*
|
|
500
|
+
* @example
|
|
501
|
+
* ```typescript
|
|
502
|
+
* const tx = new Transaction();
|
|
503
|
+
*
|
|
504
|
+
* // Prepare exact SUI amount (uses gas coin)
|
|
505
|
+
* const suiCoin = await coinManager.prepareCoinInput(
|
|
506
|
+
* tx,
|
|
507
|
+
* "0x2::sui::SUI",
|
|
508
|
+
* "1000000000" // 1 SUI
|
|
509
|
+
* );
|
|
510
|
+
*
|
|
511
|
+
* // Prepare custom coin with options
|
|
512
|
+
* const customCoin = await coinManager.prepareCoinInput(
|
|
513
|
+
* tx,
|
|
514
|
+
* "0x5d4b...::coin::COIN",
|
|
515
|
+
* "1000000", // 1 USDC
|
|
516
|
+
* {
|
|
517
|
+
* minimizeInputs: true,
|
|
518
|
+
* excludeIds: ["0x123..."]
|
|
519
|
+
* }
|
|
520
|
+
* );
|
|
521
|
+
*
|
|
522
|
+
* // Use in a Move call
|
|
523
|
+
* tx.moveCall({
|
|
524
|
+
* target: `${packageId}::module::swap`,
|
|
525
|
+
* arguments: [suiCoin, customCoin],
|
|
526
|
+
* });
|
|
527
|
+
* ```
|
|
528
|
+
*
|
|
529
|
+
* @remarks
|
|
530
|
+
* - SUI coins are handled specially by splitting from the gas coin
|
|
531
|
+
* - Single coins matching the exact amount are used directly
|
|
532
|
+
* - Multiple coins are automatically merged
|
|
533
|
+
* - Coins with excess balance are automatically split
|
|
534
|
+
* - The resulting Argument can be used directly in Move calls
|
|
535
|
+
*/
|
|
536
|
+
async prepareCoinInput(tx, coinType, amount, options = {}, owner) {
|
|
537
|
+
const ownerAddress = this.getOwnerAddress(owner);
|
|
538
|
+
if (coinType === import_utils3.SUI_TYPE_ARG) {
|
|
539
|
+
const [coin] = tx.splitCoins(tx.gas, [amount]);
|
|
540
|
+
return coin;
|
|
541
|
+
}
|
|
542
|
+
const selection = await this.selectCoins(coinType, amount, options, owner);
|
|
543
|
+
if (!selection.coins[0]) {
|
|
544
|
+
throw new import_core2.InsufficientFundsError(amount, "0", {
|
|
545
|
+
coinType,
|
|
546
|
+
amount,
|
|
547
|
+
ownerAddress
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
if (selection.coins.length === 1 && !selection.requiresSplit) {
|
|
551
|
+
return tx.object(selection.coins[0].coinObjectId);
|
|
552
|
+
}
|
|
553
|
+
if (selection.coins.length === 1 && selection.requiresSplit) {
|
|
554
|
+
const [split] = tx.splitCoins(tx.object(selection.coins[0].coinObjectId), [amount]);
|
|
555
|
+
return split;
|
|
556
|
+
}
|
|
557
|
+
const primaryCoin = tx.object(selection.coins[0].coinObjectId);
|
|
558
|
+
const mergeCoins = selection.coins.slice(1).map((c) => tx.object(c.coinObjectId));
|
|
559
|
+
tx.mergeCoins(primaryCoin, mergeCoins);
|
|
560
|
+
if (selection.requiresSplit) {
|
|
561
|
+
const [split] = tx.splitCoins(primaryCoin, [amount]);
|
|
562
|
+
return split;
|
|
563
|
+
}
|
|
564
|
+
return primaryCoin;
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
// src/lib/helpers/queries.ts
|
|
569
|
+
var import_core3 = require("@bolt-liquidity-hq/core");
|
|
164
570
|
var import_transactions = require("@mysten/sui/transactions");
|
|
165
571
|
var queryDevInspect = async (suiClient, target, args, typeArguments, senderAddress) => {
|
|
166
572
|
const tx = new import_transactions.Transaction();
|
|
@@ -181,10 +587,10 @@ var queryDevInspect = async (suiClient, target, args, typeArguments, senderAddre
|
|
|
181
587
|
if (result.effects.status.status === "success") {
|
|
182
588
|
return result;
|
|
183
589
|
} else {
|
|
184
|
-
throw new
|
|
590
|
+
throw new import_core3.TransactionFailedError("N/A", "Failed to query smart contract", { result });
|
|
185
591
|
}
|
|
186
592
|
} catch (error) {
|
|
187
|
-
throw
|
|
593
|
+
throw import_core3.UnexpectedError.from(error, "Failed to query smart contract", {
|
|
188
594
|
target: targetString,
|
|
189
595
|
args,
|
|
190
596
|
typeArguments
|
|
@@ -192,9 +598,84 @@ var queryDevInspect = async (suiClient, target, args, typeArguments, senderAddre
|
|
|
192
598
|
}
|
|
193
599
|
};
|
|
194
600
|
|
|
195
|
-
// src/lib/
|
|
196
|
-
var
|
|
601
|
+
// src/lib/helpers/txs.ts
|
|
602
|
+
var import_core4 = require("@bolt-liquidity-hq/core");
|
|
603
|
+
var import_transactions2 = require("@mysten/sui/transactions");
|
|
604
|
+
var import_utils4 = require("@mysten/sui/utils");
|
|
197
605
|
var import_bignumber = require("bignumber.js");
|
|
606
|
+
var signAndExecuteTx = async (suiClient, signer, target, args, typeArguments, transaction, options) => {
|
|
607
|
+
const tx = transaction ?? new import_transactions2.Transaction();
|
|
608
|
+
const targetString = Array.isArray(target) ? `${target[0]}::${target[1]}::${target[2]}` : target;
|
|
609
|
+
const txArgs = args?.map(
|
|
610
|
+
(item) => typeof item === "string" ? tx.object(item) : "toBytes" in item ? tx.pure(item.toBytes()) : item
|
|
611
|
+
);
|
|
612
|
+
tx.moveCall({
|
|
613
|
+
target: targetString,
|
|
614
|
+
arguments: txArgs,
|
|
615
|
+
typeArguments
|
|
616
|
+
});
|
|
617
|
+
try {
|
|
618
|
+
const result = await suiClient.signAndExecuteTransaction({
|
|
619
|
+
signer,
|
|
620
|
+
transaction: tx,
|
|
621
|
+
options
|
|
622
|
+
});
|
|
623
|
+
if (result.effects?.status.status === "success") {
|
|
624
|
+
return result;
|
|
625
|
+
} else {
|
|
626
|
+
throw new import_core4.TransactionFailedError(result.digest, result.effects?.status.error, { result });
|
|
627
|
+
}
|
|
628
|
+
} catch (error) {
|
|
629
|
+
throw import_core4.UnexpectedError.from(error, "Failed to execute transaction", {
|
|
630
|
+
target: targetString,
|
|
631
|
+
args,
|
|
632
|
+
typeArguments
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
var estimateTxGasPrice = async (suiClient, target, args, typeArguments, transaction, gasAdjustment = import_core4.DEFAULT_GAS_ADJUSTMENT) => {
|
|
637
|
+
const tx = transaction ?? new import_transactions2.Transaction();
|
|
638
|
+
const targetString = Array.isArray(target) ? `${target[0]}::${target[1]}::${target[2]}` : target;
|
|
639
|
+
const txArgs = args?.map(
|
|
640
|
+
(item) => typeof item === "string" ? tx.object(item) : "toBytes" in item ? tx.pure(item.toBytes()) : item
|
|
641
|
+
);
|
|
642
|
+
tx.moveCall({
|
|
643
|
+
target: targetString,
|
|
644
|
+
arguments: txArgs,
|
|
645
|
+
typeArguments
|
|
646
|
+
});
|
|
647
|
+
try {
|
|
648
|
+
const transactionBytes = await tx.build({ client: suiClient });
|
|
649
|
+
const dryRunResult = await suiClient.dryRunTransactionBlock({
|
|
650
|
+
transactionBlock: transactionBytes
|
|
651
|
+
});
|
|
652
|
+
if (dryRunResult.effects.status.status !== "success") {
|
|
653
|
+
throw new import_core4.UnexpectedError(
|
|
654
|
+
`Dry run failed: ${dryRunResult.effects.status.error}`,
|
|
655
|
+
"Failed to estimate gas",
|
|
656
|
+
{
|
|
657
|
+
target: targetString,
|
|
658
|
+
args,
|
|
659
|
+
typeArguments,
|
|
660
|
+
error: dryRunResult.effects.status.error
|
|
661
|
+
}
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
const gasUsed = dryRunResult.effects.gasUsed;
|
|
665
|
+
const totalGasCost = (0, import_bignumber.BigNumber)(gasUsed.computationCost).plus(gasUsed.storageCost).minus(gasUsed.storageRebate);
|
|
666
|
+
const adjustedGasCost = totalGasCost.times(gasAdjustment);
|
|
667
|
+
return {
|
|
668
|
+
denom: import_utils4.SUI_TYPE_ARG,
|
|
669
|
+
amount: adjustedGasCost.toFixed(0, import_bignumber.BigNumber.ROUND_CEIL)
|
|
670
|
+
};
|
|
671
|
+
} catch {
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
// src/lib/oracle/parsers.ts
|
|
677
|
+
var import_core5 = require("@bolt-liquidity-hq/core");
|
|
678
|
+
var import_bignumber2 = require("bignumber.js");
|
|
198
679
|
var parseOracleConfigStructOutput = (output) => {
|
|
199
680
|
return {
|
|
200
681
|
admin: output.admin,
|
|
@@ -219,25 +700,26 @@ var parseAssetPairStructOutput = (output) => {
|
|
|
219
700
|
}
|
|
220
701
|
};
|
|
221
702
|
};
|
|
222
|
-
var parsePriceDataStructOutput = (
|
|
703
|
+
var parsePriceDataStructOutput = (output) => {
|
|
223
704
|
return {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
705
|
+
baseDenom: output.base_symbol,
|
|
706
|
+
quoteDenom: output.quote_symbol,
|
|
707
|
+
price: output.price,
|
|
708
|
+
expiryTime: (0, import_bignumber2.BigNumber)(output.expiry_time_ms).times(1e6).toFixed(),
|
|
227
709
|
isInverse: false
|
|
228
710
|
};
|
|
229
711
|
};
|
|
230
|
-
var parsePriceResponseStructOutput = (
|
|
231
|
-
if (!
|
|
232
|
-
throw new
|
|
712
|
+
var parsePriceResponseStructOutput = (output) => {
|
|
713
|
+
if (!output.pair_data) {
|
|
714
|
+
throw new import_core5.InvalidObjectError("Can't find pair data price");
|
|
233
715
|
}
|
|
234
|
-
return parsePriceDataStructOutput(
|
|
716
|
+
return parsePriceDataStructOutput(output.pair_data);
|
|
235
717
|
};
|
|
236
718
|
var parseAssetPairsResponsePaginatedStructOutput = (output) => {
|
|
237
719
|
return output.asset_pairs.map((item) => parseAssetPairStructOutput(item));
|
|
238
720
|
};
|
|
239
721
|
var parsePricesResponsePaginatedStructOutput = (output) => {
|
|
240
|
-
return output.prices.map((item) => parsePriceDataStructOutput(item
|
|
722
|
+
return output.prices.map((item) => parsePriceDataStructOutput(item));
|
|
241
723
|
};
|
|
242
724
|
|
|
243
725
|
// src/types/bcs.ts
|
|
@@ -287,6 +769,8 @@ var AssetPairsResponsePaginatedStruct = import_bcs3.bcs.struct("AssetPairsRespon
|
|
|
287
769
|
...PaginationStruct
|
|
288
770
|
});
|
|
289
771
|
var PriceDataStruct = import_bcs3.bcs.struct("PriceData", {
|
|
772
|
+
base_symbol: import_bcs3.bcs.string(),
|
|
773
|
+
quote_symbol: import_bcs3.bcs.string(),
|
|
290
774
|
price: import_bcs3.bcs.u128(),
|
|
291
775
|
expiry_time_ms: import_bcs3.bcs.u64(),
|
|
292
776
|
last_updated_ms: import_bcs3.bcs.u64(),
|
|
@@ -326,8 +810,12 @@ var MarketsResponsePaginatedStruct = import_bcs5.bcs.struct("MarketsResponsePagi
|
|
|
326
810
|
markets: import_bcs5.bcs.vector(MarketStruct),
|
|
327
811
|
...PaginationStruct
|
|
328
812
|
});
|
|
813
|
+
var BaseLiquidityInfoStruct = import_bcs5.bcs.struct("BaseLiquidityInfo", {
|
|
814
|
+
denom: import_bcs5.bcs.string(),
|
|
815
|
+
amount: import_bcs5.bcs.u64()
|
|
816
|
+
});
|
|
329
817
|
var BaseLiquidityResponseStruct = import_bcs5.bcs.struct("BaseLiquidityResponse", {
|
|
330
|
-
base_assets: import_bcs5.bcs.vector(
|
|
818
|
+
base_assets: import_bcs5.bcs.vector(BaseLiquidityInfoStruct),
|
|
331
819
|
...PaginationStruct
|
|
332
820
|
});
|
|
333
821
|
|
|
@@ -402,7 +890,7 @@ var getOracleConfig = async (client) => {
|
|
|
402
890
|
|
|
403
891
|
// src/lib/oracle/get-price.ts
|
|
404
892
|
var import_bcs10 = require("@mysten/bcs");
|
|
405
|
-
var
|
|
893
|
+
var import_utils5 = require("@mysten/sui/utils");
|
|
406
894
|
var getPrice = async (client, baseDenom, quoteDenom) => {
|
|
407
895
|
const GET_PRICE_FUNCTION = "get_price";
|
|
408
896
|
const response = await queryDevInspect(
|
|
@@ -412,7 +900,7 @@ var getPrice = async (client, baseDenom, quoteDenom) => {
|
|
|
412
900
|
client.contracts.oracle,
|
|
413
901
|
import_bcs10.bcs.string().serialize(baseDenom),
|
|
414
902
|
import_bcs10.bcs.string().serialize(quoteDenom),
|
|
415
|
-
|
|
903
|
+
import_utils5.SUI_CLOCK_OBJECT_ID
|
|
416
904
|
]
|
|
417
905
|
);
|
|
418
906
|
const output = parseDevInspectResult(response, PriceResponseStruct);
|
|
@@ -445,11 +933,42 @@ var getPrices = async (client) => {
|
|
|
445
933
|
return result;
|
|
446
934
|
};
|
|
447
935
|
|
|
448
|
-
// src/lib/router/
|
|
936
|
+
// src/lib/router/estimate-swap-exact-in-gas-fees.ts
|
|
937
|
+
var import_core6 = require("@bolt-liquidity-hq/core");
|
|
449
938
|
var import_bcs12 = require("@mysten/bcs");
|
|
939
|
+
var import_transactions3 = require("@mysten/sui/transactions");
|
|
940
|
+
var import_utils6 = require("@mysten/sui/utils");
|
|
941
|
+
var estimateSwapExactInGasFees = async (client, { assetIn, amountIn, assetOut, minimumAmountOut, receiver }, signer, gasAdjustment = import_core6.DEFAULT_GAS_ADJUSTMENT) => {
|
|
942
|
+
const SWAP_EXACT_IN_FUNCTION = "swap_exact_in";
|
|
943
|
+
const finalSigner = client.getSigner(signer);
|
|
944
|
+
const tx = new import_transactions3.Transaction();
|
|
945
|
+
const coinManager = new CoinManager(client.suiClient, finalSigner.toSuiAddress());
|
|
946
|
+
const coinInput = await coinManager.prepareCoinInput(tx, assetIn, amountIn);
|
|
947
|
+
return await estimateTxGasPrice(
|
|
948
|
+
client.suiClient,
|
|
949
|
+
[client.packageId, ROUTER_MODULE, SWAP_EXACT_IN_FUNCTION],
|
|
950
|
+
[
|
|
951
|
+
client.contracts.router,
|
|
952
|
+
import_bcs12.bcs.string().serialize(assetOut),
|
|
953
|
+
"",
|
|
954
|
+
// TODO: pass correct pool when router can actually route to the pools depending on other arguments
|
|
955
|
+
import_bcs12.bcs.string().serialize(assetIn),
|
|
956
|
+
import_bcs12.bcs.option(import_bcs12.bcs.string()).serialize(minimumAmountOut),
|
|
957
|
+
import_bcs12.bcs.option(import_bcs12.bcs.string()).serialize(receiver),
|
|
958
|
+
coinInput,
|
|
959
|
+
import_utils6.SUI_CLOCK_OBJECT_ID
|
|
960
|
+
],
|
|
961
|
+
[assetIn, assetOut],
|
|
962
|
+
tx,
|
|
963
|
+
gasAdjustment
|
|
964
|
+
);
|
|
965
|
+
};
|
|
966
|
+
|
|
967
|
+
// src/lib/router/get-all-base-liquidity.ts
|
|
968
|
+
var import_bcs13 = require("@mysten/bcs");
|
|
450
969
|
|
|
451
970
|
// src/lib/router/parsers.ts
|
|
452
|
-
var
|
|
971
|
+
var import_core7 = require("@bolt-liquidity-hq/core");
|
|
453
972
|
var parseMarketStructOutput = (output) => {
|
|
454
973
|
return {
|
|
455
974
|
poolAddress: output.market_address,
|
|
@@ -457,14 +976,14 @@ var parseMarketStructOutput = (output) => {
|
|
|
457
976
|
quoteDenoms: output.quote_assets_symbols
|
|
458
977
|
};
|
|
459
978
|
};
|
|
460
|
-
var parseMarketResponseStructOutput = (
|
|
461
|
-
if (!
|
|
462
|
-
throw new
|
|
979
|
+
var parseMarketResponseStructOutput = (output) => {
|
|
980
|
+
if (!output.market) {
|
|
981
|
+
throw new import_core7.NotFoundError("Market", void 0, { output });
|
|
463
982
|
}
|
|
464
|
-
return parseMarketStructOutput(
|
|
983
|
+
return parseMarketStructOutput(output.market);
|
|
465
984
|
};
|
|
466
|
-
var parseMarketsResponsePaginatedStructOutput = (
|
|
467
|
-
return
|
|
985
|
+
var parseMarketsResponsePaginatedStructOutput = (output) => {
|
|
986
|
+
return output.markets.map((item) => parseMarketStructOutput(item));
|
|
468
987
|
};
|
|
469
988
|
var parseRouterConfigStructOutput = (output) => {
|
|
470
989
|
return {
|
|
@@ -478,10 +997,11 @@ var parseRouterConfigStructOutput = (output) => {
|
|
|
478
997
|
var parseBaseLiquidityResponseStructOutput = (output) => {
|
|
479
998
|
return output.base_assets.map((item) => ({
|
|
480
999
|
baseLiquidity: {
|
|
481
|
-
denom: item,
|
|
482
|
-
amount:
|
|
1000
|
+
denom: item.denom,
|
|
1001
|
+
amount: item.amount
|
|
483
1002
|
},
|
|
484
|
-
totalShares: "
|
|
1003
|
+
totalShares: ""
|
|
1004
|
+
// TODO: add real total shares if it is returned by the smart contract later
|
|
485
1005
|
}));
|
|
486
1006
|
};
|
|
487
1007
|
|
|
@@ -497,8 +1017,8 @@ var getAllBaseLiquidity = async (client) => {
|
|
|
497
1017
|
[client.packageId, ROUTER_MODULE, BASE_LIQUIDITY_ALL_PAGINATED_FUNCTION],
|
|
498
1018
|
[
|
|
499
1019
|
client.contracts.router,
|
|
500
|
-
|
|
501
|
-
|
|
1020
|
+
import_bcs13.bcs.option(import_bcs13.bcs.u64()).serialize(DEFAULT_PAGINATION_LIMIT),
|
|
1021
|
+
import_bcs13.bcs.option(import_bcs13.bcs.string()).serialize(currentCursor)
|
|
502
1022
|
]
|
|
503
1023
|
);
|
|
504
1024
|
const output = parseDevInspectResult(response, BaseLiquidityResponseStruct);
|
|
@@ -513,33 +1033,33 @@ var getAllBaseLiquidity = async (client) => {
|
|
|
513
1033
|
};
|
|
514
1034
|
|
|
515
1035
|
// src/lib/router/get-all-quotes-for-user.ts
|
|
516
|
-
var
|
|
1036
|
+
var import_bcs14 = require("@mysten/bcs");
|
|
517
1037
|
var getAllQuotesForUser = async (client, lpAddress) => {
|
|
518
1038
|
const QUOTES_FOR_USER_ALL_FUNCTION = "quotes_for_user_all";
|
|
519
1039
|
const response = await queryDevInspect(
|
|
520
1040
|
client.suiClient,
|
|
521
1041
|
[client.packageId, ROUTER_MODULE, QUOTES_FOR_USER_ALL_FUNCTION],
|
|
522
|
-
[client.contracts.router,
|
|
1042
|
+
[client.contracts.router, import_bcs14.bcs.string().serialize(lpAddress)]
|
|
523
1043
|
);
|
|
524
1044
|
console.log(response);
|
|
525
1045
|
return {};
|
|
526
1046
|
};
|
|
527
1047
|
|
|
528
1048
|
// src/lib/router/get-pool-for-base.ts
|
|
529
|
-
var
|
|
1049
|
+
var import_bcs15 = require("@mysten/bcs");
|
|
530
1050
|
var getPoolForBase = async (client, baseDenom) => {
|
|
531
1051
|
const MARKET_FOR_BASE_FUNCTION = "market_for_base";
|
|
532
1052
|
const response = await queryDevInspect(
|
|
533
1053
|
client.suiClient,
|
|
534
1054
|
[client.packageId, ROUTER_MODULE, MARKET_FOR_BASE_FUNCTION],
|
|
535
|
-
[client.contracts.router,
|
|
1055
|
+
[client.contracts.router, import_bcs15.bcs.string().serialize(baseDenom)]
|
|
536
1056
|
);
|
|
537
1057
|
const output = parseDevInspectResult(response, MarketResponseStruct);
|
|
538
1058
|
return parseMarketResponseStructOutput(output);
|
|
539
1059
|
};
|
|
540
1060
|
|
|
541
1061
|
// src/lib/router/get-pools.ts
|
|
542
|
-
var
|
|
1062
|
+
var import_bcs16 = require("@mysten/bcs");
|
|
543
1063
|
var getPools = async (client) => {
|
|
544
1064
|
const MARKETS_PAGINATED_FUNCTION = "markets_paginated";
|
|
545
1065
|
const result = [];
|
|
@@ -551,8 +1071,8 @@ var getPools = async (client) => {
|
|
|
551
1071
|
[client.packageId, ROUTER_MODULE, MARKETS_PAGINATED_FUNCTION],
|
|
552
1072
|
[
|
|
553
1073
|
client.contracts.router,
|
|
554
|
-
|
|
555
|
-
|
|
1074
|
+
import_bcs16.bcs.option(import_bcs16.bcs.u64()).serialize(DEFAULT_PAGINATION_LIMIT),
|
|
1075
|
+
import_bcs16.bcs.option(import_bcs16.bcs.string()).serialize(currentCursor)
|
|
556
1076
|
]
|
|
557
1077
|
);
|
|
558
1078
|
const output = parseDevInspectResult(response, MarketsResponsePaginatedStruct);
|
|
@@ -577,60 +1097,89 @@ var getRouterConfig = async (client) => {
|
|
|
577
1097
|
};
|
|
578
1098
|
|
|
579
1099
|
// src/lib/router/swap-exact-in.ts
|
|
580
|
-
var
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
1100
|
+
var import_bcs17 = require("@mysten/bcs");
|
|
1101
|
+
var import_transactions4 = require("@mysten/sui/transactions");
|
|
1102
|
+
var import_utils7 = require("@mysten/sui/utils");
|
|
1103
|
+
var swapExactIn = async (client, { assetIn, amountIn, assetOut, minimumAmountOut, receiver }, signer) => {
|
|
1104
|
+
const SWAP_EXACT_IN_FUNCTION = "swap_exact_in";
|
|
1105
|
+
const finalSigner = client.getSigner(signer);
|
|
1106
|
+
const tx = new import_transactions4.Transaction();
|
|
1107
|
+
const coinManager = new CoinManager(client.suiClient, finalSigner.toSuiAddress());
|
|
1108
|
+
const coinInput = await coinManager.prepareCoinInput(tx, assetIn, amountIn);
|
|
1109
|
+
const txOutput = await signAndExecuteTx(
|
|
1110
|
+
client.suiClient,
|
|
1111
|
+
finalSigner,
|
|
1112
|
+
[client.packageId, ROUTER_MODULE, SWAP_EXACT_IN_FUNCTION],
|
|
1113
|
+
[
|
|
1114
|
+
client.contracts.router,
|
|
1115
|
+
import_bcs17.bcs.string().serialize(assetOut),
|
|
1116
|
+
"",
|
|
1117
|
+
// TODO: pass correct pool when router can actually route to the pools depending on other arguments
|
|
1118
|
+
import_bcs17.bcs.string().serialize(assetIn),
|
|
1119
|
+
import_bcs17.bcs.option(import_bcs17.bcs.string()).serialize(minimumAmountOut),
|
|
1120
|
+
import_bcs17.bcs.option(import_bcs17.bcs.string()).serialize(receiver),
|
|
1121
|
+
coinInput,
|
|
1122
|
+
import_utils7.SUI_CLOCK_OBJECT_ID
|
|
1123
|
+
],
|
|
1124
|
+
[assetIn, assetOut],
|
|
1125
|
+
tx,
|
|
1126
|
+
{ showEffects: true, showEvents: true, showBalanceChanges: true }
|
|
1127
|
+
);
|
|
1128
|
+
return {
|
|
1129
|
+
txOutput,
|
|
1130
|
+
txHash: txOutput.digest,
|
|
1131
|
+
amountOut: "",
|
|
1132
|
+
// TODO: implement when contract emits event
|
|
1133
|
+
assetOut
|
|
1134
|
+
};
|
|
584
1135
|
};
|
|
585
1136
|
|
|
586
1137
|
// src/lib/settlement/get-pool-info.ts
|
|
587
|
-
var
|
|
1138
|
+
var import_utils8 = require("@mysten/sui/utils");
|
|
588
1139
|
|
|
589
1140
|
// src/lib/settlement/parsers.ts
|
|
590
|
-
var parseSettlementConfigStructOutput = (
|
|
1141
|
+
var parseSettlementConfigStructOutput = (globalConfig, feesOutput) => {
|
|
591
1142
|
return {
|
|
592
1143
|
priceOracleContract: "0x",
|
|
593
|
-
// Should come from
|
|
594
|
-
|
|
595
|
-
|
|
1144
|
+
// Should come from pool config
|
|
1145
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
|
1146
|
+
protocolFeeRecipient: globalConfig.data?.content?.fields.protocol_fee_recipient || "",
|
|
596
1147
|
protocolFee: feesOutput[0],
|
|
597
1148
|
// Protocol fee percentage
|
|
598
1149
|
lpFee: feesOutput[2],
|
|
599
1150
|
// LP fee percentage
|
|
600
1151
|
allowanceMode: "allow",
|
|
601
|
-
// Should come from
|
|
1152
|
+
// Should come from pool config
|
|
602
1153
|
lps: ["0x"],
|
|
603
|
-
// Should come from
|
|
1154
|
+
// Should come from pool config
|
|
604
1155
|
minBaseOut: "1"
|
|
605
|
-
// Should come from
|
|
1156
|
+
// Should come from pool config
|
|
606
1157
|
};
|
|
607
1158
|
};
|
|
608
1159
|
|
|
609
1160
|
// src/lib/settlement/get-pool-info.ts
|
|
610
1161
|
var getPoolInfo = async (client, contractAddress) => {
|
|
611
|
-
const GET_POOL_INFO = "get_pool_info";
|
|
612
1162
|
const GET_FEES_FUNCTION = "get_fees";
|
|
613
|
-
const [
|
|
614
|
-
//
|
|
615
|
-
|
|
616
|
-
client.
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
),
|
|
622
|
-
// Query fee structure
|
|
1163
|
+
const [globalConfig, fees] = await Promise.all([
|
|
1164
|
+
// Fetch the global pool configuration object
|
|
1165
|
+
client.suiClient.getObject({
|
|
1166
|
+
id: client.poolGlobalConfigId,
|
|
1167
|
+
options: {
|
|
1168
|
+
showContent: true,
|
|
1169
|
+
showType: true
|
|
1170
|
+
}
|
|
1171
|
+
}),
|
|
1172
|
+
// Query pool-specific fee structure
|
|
623
1173
|
queryDevInspect(
|
|
624
1174
|
client.suiClient,
|
|
625
1175
|
[client.packageId, POOL_MODULE, GET_FEES_FUNCTION],
|
|
626
1176
|
[contractAddress],
|
|
627
1177
|
// TODO: get the base token of the pool to pass it here instead of hardcoded SUI token
|
|
628
|
-
[
|
|
1178
|
+
[import_utils8.SUI_TYPE_ARG]
|
|
629
1179
|
)
|
|
630
1180
|
]);
|
|
631
|
-
const infoOutput = parseMultipleResults(info, GetPoolInfoResponseStruct);
|
|
632
1181
|
const feesOutput = parseMultipleResults(fees, GetFeesResponseStruct);
|
|
633
|
-
return parseSettlementConfigStructOutput(
|
|
1182
|
+
return parseSettlementConfigStructOutput(globalConfig, feesOutput);
|
|
634
1183
|
};
|
|
635
1184
|
|
|
636
1185
|
// src/tests/constants/sui-objects.ts
|
|
@@ -643,25 +1192,29 @@ var getPoolInfoForBase = async (client, baseDenom) => {
|
|
|
643
1192
|
};
|
|
644
1193
|
|
|
645
1194
|
// src/lib/client.ts
|
|
646
|
-
var BoltSuiClient = class extends
|
|
1195
|
+
var BoltSuiClient = class extends import_core8.BaseClient {
|
|
647
1196
|
/**
|
|
648
1197
|
* Creates a new instance of the BoltSuiClient.
|
|
649
1198
|
*
|
|
650
|
-
* The client automatically configures itself based on the specified
|
|
651
|
-
* loading the appropriate contract addresses, chain configuration,
|
|
1199
|
+
* The client automatically configures itself based on the specified environment,
|
|
1200
|
+
* loading the appropriate contract addresses, chain configuration, native token denomination,
|
|
1201
|
+
* and assets from configuration files.
|
|
652
1202
|
*
|
|
653
1203
|
* @param config - (Optional) Configuration for the client
|
|
654
1204
|
* @param config.environment - (Optional) The deployment environment ('mainnet' or 'testnet'). Defaults to 'mainnet'
|
|
655
|
-
* @param config.customOverride - (Optional) Custom overrides for chain configuration, contracts, and assets
|
|
1205
|
+
* @param config.customOverride - (Optional) Custom overrides for chain configuration, contracts, native token, and assets
|
|
656
1206
|
* @param config.customOverride.chainConfig - (Optional) Override chain configuration
|
|
657
1207
|
* @param config.customOverride.chainConfig.id - (Optional) Custom chain ID
|
|
658
1208
|
* @param config.customOverride.chainConfig.name - (Optional) Custom chain name
|
|
659
1209
|
* @param config.customOverride.chainConfig.rpcEndpoint - (Optional) Custom RPC endpoint URL
|
|
660
1210
|
* @param config.customOverride.packageId - (Optional) Custom package ID for Bolt contracts
|
|
1211
|
+
* @param config.customOverride.poolGlobalConfigId - (Optional) Custom Object ID for the Pool contracts' Global Config
|
|
661
1212
|
* @param config.customOverride.contracts - (Optional) Override contract addresses
|
|
662
1213
|
* @param config.customOverride.contracts.oracle - (Optional) Custom oracle contract address
|
|
663
1214
|
* @param config.customOverride.contracts.router - (Optional) Custom router contract address
|
|
1215
|
+
* @param config.customOverride.nativeTokenDenom - (Optional) Custom native token denomination (e.g., "SUI")
|
|
664
1216
|
* @param config.customOverride.assetsConfig - (Optional) Custom asset configurations indexed by denom
|
|
1217
|
+
* @param config.signer - (Optional) Pre-existing Signer for transaction signing
|
|
665
1218
|
* @param config.suiClient - (Optional) Pre-existing SuiClient to use for blockchain queries
|
|
666
1219
|
*
|
|
667
1220
|
* @throws {InvalidObjectError} Thrown when required configuration fields are missing
|
|
@@ -685,10 +1238,12 @@ var BoltSuiClient = class extends import_core5.BaseClient {
|
|
|
685
1238
|
* rpcEndpoint: 'https://custom-rpc.example.com'
|
|
686
1239
|
* },
|
|
687
1240
|
* packageId: '0xcustom_package_id...',
|
|
1241
|
+
* poolGlobalConfigId: '0xcustom_global_config_id...',
|
|
688
1242
|
* contracts: {
|
|
689
1243
|
* oracle: '0xcustom_oracle...',
|
|
690
1244
|
* router: '0xcustom_router...'
|
|
691
1245
|
* },
|
|
1246
|
+
* nativeTokenDenom: 'SUI',
|
|
692
1247
|
* assetsConfig: {
|
|
693
1248
|
* '0x2::sui::SUI': {
|
|
694
1249
|
* symbol: 'SUI',
|
|
@@ -703,17 +1258,20 @@ var BoltSuiClient = class extends import_core5.BaseClient {
|
|
|
703
1258
|
* }
|
|
704
1259
|
* });
|
|
705
1260
|
*
|
|
706
|
-
* // Use pre-existing Sui client
|
|
1261
|
+
* // Use pre-existing Sui client and signer
|
|
707
1262
|
* const clientWithCustomClient = new BoltSuiClient({
|
|
708
|
-
* suiClient: mySuiClient
|
|
1263
|
+
* suiClient: mySuiClient,
|
|
1264
|
+
* signer: mySigner
|
|
709
1265
|
* });
|
|
710
1266
|
* ```
|
|
711
1267
|
*/
|
|
712
1268
|
constructor(config) {
|
|
713
|
-
const { environment = "mainnet", customOverride, suiClient } = config ?? {};
|
|
1269
|
+
const { environment = "mainnet", customOverride, signer, suiClient } = config ?? {};
|
|
714
1270
|
const defaultChainConfig = environment === "mainnet" ? MainnetChainConfig : TestnetChainConfig;
|
|
715
1271
|
const defaultContracts = environment === "mainnet" ? MainnetContracts : TestnetContracts;
|
|
716
1272
|
const defaultPackageId = environment === "mainnet" ? MainnetPackageId : TestnetPackageId;
|
|
1273
|
+
const defaultPoolGlobalConfigId = environment === "mainnet" ? MainnetPoolGlobalConfigId : TestnetPoolGlobalConfigId;
|
|
1274
|
+
const defaultNativeTokenDenom = environment === "mainnet" ? MainnetNativeTokenDenom : TestnetNativeTokenDenom;
|
|
717
1275
|
const assetsConfig = environment === "mainnet" ? MainnetAssets : TestnetAssets;
|
|
718
1276
|
const chainConfig = {
|
|
719
1277
|
id: customOverride?.chainConfig?.id ?? defaultChainConfig.id,
|
|
@@ -721,10 +1279,12 @@ var BoltSuiClient = class extends import_core5.BaseClient {
|
|
|
721
1279
|
rpcEndpoint: customOverride?.chainConfig?.rpcEndpoint ?? defaultChainConfig.rpcEndpoint
|
|
722
1280
|
};
|
|
723
1281
|
const packageId = customOverride?.packageId ?? defaultPackageId;
|
|
1282
|
+
const poolGlobalConfigId = customOverride?.poolGlobalConfigId ?? defaultPoolGlobalConfigId;
|
|
724
1283
|
const contracts = {
|
|
725
1284
|
oracle: customOverride?.contracts?.oracle ?? defaultContracts.oracle,
|
|
726
1285
|
router: customOverride?.contracts?.router ?? defaultContracts.router
|
|
727
1286
|
};
|
|
1287
|
+
const nativeTokenDenom = customOverride?.nativeTokenDenom ?? defaultNativeTokenDenom;
|
|
728
1288
|
for (const item of Object.values(customOverride?.assetsConfig ?? {})) {
|
|
729
1289
|
assetsConfig[item.denom] = item;
|
|
730
1290
|
}
|
|
@@ -732,6 +1292,7 @@ var BoltSuiClient = class extends import_core5.BaseClient {
|
|
|
732
1292
|
customOverride: {
|
|
733
1293
|
chainConfig,
|
|
734
1294
|
contracts,
|
|
1295
|
+
nativeTokenDenom,
|
|
735
1296
|
assetsConfig
|
|
736
1297
|
}
|
|
737
1298
|
});
|
|
@@ -743,14 +1304,55 @@ var BoltSuiClient = class extends import_core5.BaseClient {
|
|
|
743
1304
|
* Package Id for the deployed Bolt contracts on Sui
|
|
744
1305
|
*/
|
|
745
1306
|
__publicField(this, "packageId");
|
|
1307
|
+
/**
|
|
1308
|
+
* Object Id for the GlobalConfig of the deployed Pool contracts
|
|
1309
|
+
*/
|
|
1310
|
+
__publicField(this, "poolGlobalConfigId");
|
|
1311
|
+
/**
|
|
1312
|
+
* Cached instance of the Signer for transaction execution
|
|
1313
|
+
* @private
|
|
1314
|
+
*/
|
|
1315
|
+
__publicField(this, "signer");
|
|
746
1316
|
/**
|
|
747
1317
|
* Instance of the Sui client to interact with the blockchain
|
|
748
1318
|
*/
|
|
749
1319
|
__publicField(this, "suiClient");
|
|
750
1320
|
this.chainConfig = chainConfig;
|
|
751
1321
|
this.packageId = packageId;
|
|
1322
|
+
this.poolGlobalConfigId = poolGlobalConfigId;
|
|
1323
|
+
this.signer = signer;
|
|
752
1324
|
this.suiClient = suiClient ?? new import_client.SuiClient({ url: chainConfig.rpcEndpoint });
|
|
753
1325
|
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Gets or sets the signer for transaction execution.
|
|
1328
|
+
*
|
|
1329
|
+
* This method manages the signer instance used for signing transactions.
|
|
1330
|
+
* A signer must be provided either when creating the client, on first call,
|
|
1331
|
+
* or to replace the existing signer.
|
|
1332
|
+
*
|
|
1333
|
+
* @param newSigner - Optional new signer to replace the existing one
|
|
1334
|
+
*
|
|
1335
|
+
* @returns The current signer instance
|
|
1336
|
+
*
|
|
1337
|
+
* @throws {MissingParameterError} Thrown when no signer is available and none is provided
|
|
1338
|
+
*
|
|
1339
|
+
* @example
|
|
1340
|
+
* ```typescript
|
|
1341
|
+
* // Get the current signer (throws if none exists)
|
|
1342
|
+
* const currentSigner = client.getSigner();
|
|
1343
|
+
*
|
|
1344
|
+
* // Set a new signer
|
|
1345
|
+
* const newSigner = // ... obtain signer from wallet
|
|
1346
|
+
* const signer = client.getSigner(newSigner);
|
|
1347
|
+
* ```
|
|
1348
|
+
*/
|
|
1349
|
+
getSigner(newSigner) {
|
|
1350
|
+
this.signer = newSigner ?? this.signer;
|
|
1351
|
+
if (!this.signer) {
|
|
1352
|
+
throw new import_core8.MissingParameterError("signer");
|
|
1353
|
+
}
|
|
1354
|
+
return this.signer;
|
|
1355
|
+
}
|
|
754
1356
|
// The following methods inherit their documentation from BaseClient
|
|
755
1357
|
// Only add documentation here if you need to override or add implementation-specific details
|
|
756
1358
|
/** @inheritdoc */
|
|
@@ -810,13 +1412,13 @@ var BoltSuiClient = class extends import_core5.BaseClient {
|
|
|
810
1412
|
* const signer = // ... obtain signer from wallet
|
|
811
1413
|
*
|
|
812
1414
|
* // Execute swap: 1 SUI for USDC
|
|
813
|
-
* const result = await client.swap(
|
|
1415
|
+
* const result = await client.swap({
|
|
814
1416
|
* assetIn: "0x2::sui::SUI",
|
|
815
1417
|
* amountIn: "1000000000", // 1 SUI (9 decimals)
|
|
816
1418
|
* assetOut: "0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN", // USDC address
|
|
817
1419
|
* minimumAmountOut: "1950000", // Minimum 1.95 USDC expected (6 decimals)
|
|
818
1420
|
* receiver: "0x..." // Optional custom receiver address
|
|
819
|
-
* });
|
|
1421
|
+
* }, signer);
|
|
820
1422
|
*
|
|
821
1423
|
* console.log(`Swap successful!`);
|
|
822
1424
|
* console.log(`Transaction digest: ${result.txHash}`);
|
|
@@ -829,8 +1431,39 @@ var BoltSuiClient = class extends import_core5.BaseClient {
|
|
|
829
1431
|
* This implementation returns a SuiTransactionBlockResponse as the transaction output,
|
|
830
1432
|
* which includes details like gas costs, transaction effects, object changes, and events.
|
|
831
1433
|
*/
|
|
832
|
-
async swap(
|
|
833
|
-
return await swapExactIn(this,
|
|
1434
|
+
async swap(params, signer) {
|
|
1435
|
+
return await swapExactIn(this, params, signer);
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* @inheritdoc
|
|
1439
|
+
*
|
|
1440
|
+
* @example
|
|
1441
|
+
* ```typescript
|
|
1442
|
+
* // Get signer from wallet (e.g., Sui Wallet, Suiet, etc.)
|
|
1443
|
+
* const signer = // ... obtain signer from wallet
|
|
1444
|
+
*
|
|
1445
|
+
* // Estimate gas for swapping 1 SUI to USDC
|
|
1446
|
+
* const gasEstimate = await client.estimateSwapGasFees({
|
|
1447
|
+
* assetIn: "0x2::sui::SUI",
|
|
1448
|
+
* amountIn: "1000000000", // 1 SUI (9 decimals)
|
|
1449
|
+
* assetOut: "0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN", // USDC
|
|
1450
|
+
* minimumAmountOut: "1950000" // Min 1.95 USDC (6 decimals)
|
|
1451
|
+
* }, signer, 1.3); // 30% safety margin
|
|
1452
|
+
*
|
|
1453
|
+
* if (gasEstimate) {
|
|
1454
|
+
* console.log(`Estimated gas: ${gasEstimate.amount} ${gasEstimate.denom}`);
|
|
1455
|
+
* }
|
|
1456
|
+
* ```
|
|
1457
|
+
*
|
|
1458
|
+
* @remarks
|
|
1459
|
+
* - For Sui, gas is always paid in SUI tokens
|
|
1460
|
+
* - The returned amount is in MIST (smallest unit, 9 decimals)
|
|
1461
|
+
* - Gas estimation uses Sui's dry run transaction capability
|
|
1462
|
+
* - The gasAdjustment parameter helps account for variations in actual execution
|
|
1463
|
+
* - Sui's gas model includes computation, storage, and storage rebates
|
|
1464
|
+
*/
|
|
1465
|
+
async estimateSwapGasFees(params, signer, gasAdjustment) {
|
|
1466
|
+
return await estimateSwapExactInGasFees(this, params, signer, gasAdjustment);
|
|
834
1467
|
}
|
|
835
1468
|
};
|
|
836
1469
|
//# sourceMappingURL=index.cjs.map
|