@7kprotocol/sdk-ts 3.4.0 → 3.4.2-beta.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.
Files changed (65) hide show
  1. package/lib/cjs/features/limitDca/placeLimitOrder.js +8 -3
  2. package/lib/cjs/features/swap/buildTx.js +111 -65
  3. package/lib/cjs/features/swap/buildTxV2.js +227 -0
  4. package/lib/cjs/features/swap/getQuote.js +13 -2
  5. package/lib/cjs/features/swap/index.js +1 -0
  6. package/lib/cjs/index.js +6 -2
  7. package/lib/cjs/types/features/limitDca/placeLimitOrder.d.ts +2 -1
  8. package/lib/cjs/types/features/limitDca/placeLimitOrder.d.ts.map +1 -1
  9. package/lib/cjs/types/features/swap/buildTx.d.ts +14 -1
  10. package/lib/cjs/types/features/swap/buildTx.d.ts.map +1 -1
  11. package/lib/cjs/types/features/swap/buildTxV2.d.ts +20 -0
  12. package/lib/cjs/types/features/swap/buildTxV2.d.ts.map +1 -0
  13. package/lib/cjs/types/features/swap/getQuote.d.ts +4 -1
  14. package/lib/cjs/types/features/swap/getQuote.d.ts.map +1 -1
  15. package/lib/cjs/types/features/swap/index.d.ts +1 -0
  16. package/lib/cjs/types/features/swap/index.d.ts.map +1 -1
  17. package/lib/cjs/types/index.d.ts +4 -2
  18. package/lib/cjs/types/index.d.ts.map +1 -1
  19. package/lib/cjs/types/libs/protocols/base.d.ts +4 -5
  20. package/lib/cjs/types/libs/protocols/base.d.ts.map +1 -1
  21. package/lib/cjs/types/libs/protocols/steamm/index.d.ts +3 -12
  22. package/lib/cjs/types/libs/protocols/steamm/index.d.ts.map +1 -1
  23. package/lib/cjs/types/libs/swapWithRoute.d.ts +3 -4
  24. package/lib/cjs/types/libs/swapWithRoute.d.ts.map +1 -1
  25. package/lib/cjs/types/types/tx.d.ts +13 -0
  26. package/lib/cjs/types/types/tx.d.ts.map +1 -1
  27. package/lib/esm/features/limitDca/placeLimitOrder.js +8 -3
  28. package/lib/esm/features/swap/buildTx.js +105 -65
  29. package/lib/esm/features/swap/buildTxV2.js +221 -0
  30. package/lib/esm/features/swap/getQuote.js +12 -1
  31. package/lib/esm/features/swap/index.js +1 -0
  32. package/lib/esm/index.mjs +12 -10
  33. package/lib/esm/types/features/limitDca/placeLimitOrder.d.ts +2 -1
  34. package/lib/esm/types/features/limitDca/placeLimitOrder.d.ts.map +1 -1
  35. package/lib/esm/types/features/swap/buildTx.d.ts +14 -1
  36. package/lib/esm/types/features/swap/buildTx.d.ts.map +1 -1
  37. package/lib/esm/types/features/swap/buildTxV2.d.ts +20 -0
  38. package/lib/esm/types/features/swap/buildTxV2.d.ts.map +1 -0
  39. package/lib/esm/types/features/swap/getQuote.d.ts +4 -1
  40. package/lib/esm/types/features/swap/getQuote.d.ts.map +1 -1
  41. package/lib/esm/types/features/swap/index.d.ts +1 -0
  42. package/lib/esm/types/features/swap/index.d.ts.map +1 -1
  43. package/lib/esm/types/index.d.ts +4 -2
  44. package/lib/esm/types/index.d.ts.map +1 -1
  45. package/lib/esm/types/libs/protocols/base.d.ts +4 -5
  46. package/lib/esm/types/libs/protocols/base.d.ts.map +1 -1
  47. package/lib/esm/types/libs/protocols/steamm/index.d.ts +3 -12
  48. package/lib/esm/types/libs/protocols/steamm/index.d.ts.map +1 -1
  49. package/lib/esm/types/libs/swapWithRoute.d.ts +3 -4
  50. package/lib/esm/types/libs/swapWithRoute.d.ts.map +1 -1
  51. package/lib/esm/types/types/tx.d.ts +13 -0
  52. package/lib/esm/types/types/tx.d.ts.map +1 -1
  53. package/package.json +3 -3
  54. package/lib/cjs/libs/getCoinOjectIdsByAmount.js +0 -65
  55. package/lib/cjs/libs/getSplitCoinForTx.js +0 -33
  56. package/lib/cjs/types/libs/getCoinOjectIdsByAmount.d.ts +0 -7
  57. package/lib/cjs/types/libs/getCoinOjectIdsByAmount.d.ts.map +0 -1
  58. package/lib/cjs/types/libs/getSplitCoinForTx.d.ts +0 -6
  59. package/lib/cjs/types/libs/getSplitCoinForTx.d.ts.map +0 -1
  60. package/lib/esm/libs/getCoinOjectIdsByAmount.js +0 -61
  61. package/lib/esm/libs/getSplitCoinForTx.js +0 -29
  62. package/lib/esm/types/libs/getCoinOjectIdsByAmount.d.ts +0 -7
  63. package/lib/esm/types/libs/getCoinOjectIdsByAmount.d.ts.map +0 -1
  64. package/lib/esm/types/libs/getSplitCoinForTx.d.ts +0 -6
  65. package/lib/esm/types/libs/getSplitCoinForTx.d.ts.map +0 -1
@@ -1,11 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.placeLimitOrder = placeLimitOrder;
4
- const getSplitCoinForTx_1 = require("../../libs/getSplitCoinForTx");
5
- const token_1 = require("../../utils/token");
4
+ const transactions_1 = require("@mysten/sui/transactions");
6
5
  const constants_1 = require("./constants");
7
6
  async function placeLimitOrder({ accountAddress, payCoinType, targetCoinType, payCoinAmount, rate, slippage, expireTs, devInspect, }) {
8
- const { tx, coinData: payCoin } = await (0, getSplitCoinForTx_1.getSplitCoinForTx)(accountAddress, payCoinAmount.toString(), [payCoinAmount.toString()], (0, token_1.denormalizeTokenType)(payCoinType), undefined, devInspect);
7
+ const tx = new transactions_1.Transaction();
8
+ const payCoin = tx.add((0, transactions_1.coinWithBalance)({
9
+ type: payCoinType,
10
+ balance: payCoinAmount,
11
+ useGasCoin: !devInspect,
12
+ }));
9
13
  tx.moveCall({
10
14
  target: `${constants_1.LIMIT_ORDER_MODULE_ID}::place_limit_order`,
11
15
  arguments: [
@@ -18,5 +22,6 @@ async function placeLimitOrder({ accountAddress, payCoinType, targetCoinType, pa
18
22
  ],
19
23
  typeArguments: [payCoinType, targetCoinType],
20
24
  });
25
+ tx.setSenderIfNotSet(accountAddress);
21
26
  return tx;
22
27
  }
@@ -1,19 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildTx = void 0;
3
+ exports.buildBluefinXTx = exports.settle = exports.getExpectedReturn = exports.validateRoutes = exports.updatePythPriceFeedsIfAny = exports.getPythPriceFeeds = exports.buildTx = void 0;
4
4
  const transactions_1 = require("@mysten/sui/transactions");
5
5
  const utils_1 = require("@mysten/sui/utils");
6
6
  const config_1 = require("../../config");
7
7
  const _7k_1 = require("../../constants/_7k");
8
- const getSplitCoinForTx_1 = require("../../libs/getSplitCoinForTx");
9
8
  const groupSwapRoutes_1 = require("../../libs/groupSwapRoutes");
10
9
  const client_1 = require("../../libs/protocols/bluefinx/client");
11
10
  const types_1 = require("../../libs/protocols/bluefinx/types");
12
11
  const swapWithRoute_1 = require("../../libs/swapWithRoute");
13
12
  const aggregator_1 = require("../../types/aggregator");
14
13
  const sui_1 = require("../../utils/sui");
15
- const token_1 = require("../../utils/token");
16
14
  const config_2 = require("./config");
15
+ const getQuote_1 = require("./getQuote");
17
16
  const buildTx = async ({ quoteResponse, accountAddress, slippage, commission: __commission, devInspect, extendTx, isSponsored, }) => {
18
17
  const isBluefinX = (0, aggregator_1.isBluefinXRouting)(quoteResponse);
19
18
  const _commission = {
@@ -21,7 +20,7 @@ const buildTx = async ({ quoteResponse, accountAddress, slippage, commission: __
21
20
  // commission is ignored for bluefinx
22
21
  commissionBps: isBluefinX ? 0 : __commission.commissionBps,
23
22
  };
24
- const { tx: _tx, coinIn } = extendTx || {};
23
+ const { tx: _tx, coinIn: _coinIn } = extendTx || {};
25
24
  let coinOut;
26
25
  if (isBluefinX && devInspect) {
27
26
  throw new Error("BluefinX tx is sponsored, skip devInspect");
@@ -37,17 +36,17 @@ const buildTx = async ({ quoteResponse, accountAddress, slippage, commission: __
37
36
  }
38
37
  const tx = _tx || new transactions_1.Transaction();
39
38
  const routes = (0, groupSwapRoutes_1.groupSwapRoutes)(quoteResponse);
39
+ (0, exports.validateRoutes)(routes, isSponsored);
40
40
  const splits = routes.map((group) => group[0]?.amount ?? "0");
41
- let coinData;
42
- if (coinIn) {
43
- coinData = tx.splitCoins(coinIn, splits);
44
- sui_1.SuiUtils.transferOrDestroyZeroCoin(tx, quoteResponse.tokenIn, coinIn, accountAddress);
45
- }
46
- else {
47
- const { coinData: _data } = await (0, getSplitCoinForTx_1.getSplitCoinForTx)(accountAddress, quoteResponse.swapAmountWithDecimal, splits, (0, token_1.denormalizeTokenType)(quoteResponse.tokenIn), tx, devInspect, isSponsored || isBluefinX);
48
- coinData = _data;
49
- }
50
- const pythMap = await updatePythPriceFeedsIfAny(tx, quoteResponse);
41
+ const coinIn = _coinIn ||
42
+ tx.add((0, transactions_1.coinWithBalance)({
43
+ type: quoteResponse.tokenIn,
44
+ balance: BigInt(quoteResponse.swapAmountWithDecimal),
45
+ useGasCoin: !isSponsored && !isBluefinX,
46
+ }));
47
+ const coinData = tx.splitCoins(coinIn, splits);
48
+ sui_1.SuiUtils.transferOrDestroyZeroCoin(tx, quoteResponse.tokenIn, coinIn, accountAddress);
49
+ const pythMap = await (0, exports.updatePythPriceFeedsIfAny)(tx, [quoteResponse]);
51
50
  const coinObjects = [];
52
51
  const config = await (0, config_2.getConfig)();
53
52
  await Promise.all(routes.map(async (route, index) => {
@@ -65,79 +64,43 @@ const buildTx = async ({ quoteResponse, accountAddress, slippage, commission: __
65
64
  }
66
65
  }));
67
66
  if (coinObjects.length > 0) {
68
- const mergeCoin = coinObjects.length > 1
69
- ? sui_1.SuiUtils.mergeCoins(coinObjects, tx)
70
- : coinObjects[0];
71
- const returnAmountAfterCommission = (BigInt(10000 - _commission.commissionBps) *
72
- BigInt(quoteResponse.returnAmountWithDecimal)) /
73
- BigInt(10000);
74
- const minReceived = (BigInt(1e9 - +slippage * 1e9) * BigInt(returnAmountAfterCommission)) /
75
- BigInt(1e9);
76
- tx.moveCall({
77
- target: `${_7k_1._7K_PACKAGE_ID}::settle::settle`,
78
- typeArguments: [quoteResponse.tokenIn, quoteResponse.tokenOut],
79
- arguments: [
80
- tx.object(_7k_1._7K_CONFIG),
81
- tx.object(_7k_1._7K_VAULT),
82
- tx.pure.u64(quoteResponse.swapAmountWithDecimal),
83
- mergeCoin,
84
- tx.pure.u64(minReceived), // minimum received
85
- tx.pure.u64(returnAmountAfterCommission), // expected amount out
86
- tx.pure.option("address", (0, utils_1.isValidSuiAddress)(_commission.partner) ? _commission.partner : null),
87
- tx.pure.u64(_commission.commissionBps),
88
- tx.pure.u64(0),
89
- ],
90
- });
67
+ const mergedCoin = tx.add((0, exports.settle)(coinObjects, quoteResponse, Math.floor(+slippage * 10000), _commission));
91
68
  if (!extendTx) {
92
- tx.transferObjects([mergeCoin], tx.pure.address(accountAddress));
69
+ tx.transferObjects([mergedCoin], tx.pure.address(accountAddress));
93
70
  }
94
71
  else {
95
- coinOut = mergeCoin;
72
+ coinOut = mergedCoin;
96
73
  }
97
74
  }
98
75
  if (isBluefinX) {
99
- const extra = quoteResponse.swaps[0].extra;
100
- if (extra.quoteExpiresAtUtcMillis < Date.now()) {
101
- throw new Error("Quote expired");
102
- }
103
- tx.setSenderIfNotSet(accountAddress);
104
- const bytes = await tx.build({
105
- client: config_1.Config.getSuiClient(),
106
- onlyTransactionKind: true,
107
- });
108
- const res = await (0, client_1.sponsorBluefinX)({
109
- quoteId: extra.quoteId,
110
- txBytes: (0, utils_1.toBase64)(bytes),
111
- sender: accountAddress,
112
- });
113
- if (!res.success) {
114
- throw new Error("Sponsor failed");
115
- }
116
76
  return {
117
- tx: new types_1.BluefinXTx(res.quoteId, res.data.txBytes),
77
+ tx: await (0, exports.buildBluefinXTx)(tx, accountAddress, quoteResponse),
118
78
  coinOut,
119
79
  };
120
80
  }
81
+ tx.setSenderIfNotSet(accountAddress);
121
82
  return { tx, coinOut };
122
83
  };
123
84
  exports.buildTx = buildTx;
124
- const getPythPriceFeeds = (res) => {
85
+ const getPythPriceFeeds = (responses) => {
125
86
  const ids = new Set();
126
- for (const s of res.swaps) {
127
- for (const o of (s.extra?.oracles || [])) {
128
- // FIXME: deprecation price_identifier in the next version
129
- const bytes = o.Pyth?.bytes || o.Pyth?.price_identifier?.bytes;
130
- if (bytes) {
131
- ids.add("0x" + (0, utils_1.toHex)(Uint8Array.from(bytes)));
87
+ for (const res of responses) {
88
+ for (const s of res.swaps) {
89
+ for (const o of s.extra?.oracles || []) {
90
+ const bytes = o.Pyth?.price_identifier?.bytes || o.Pyth?.bytes;
91
+ if (bytes) {
92
+ ids.add("0x" + (0, utils_1.toHex)(Uint8Array.from(bytes)));
93
+ }
132
94
  }
133
95
  }
134
96
  }
135
97
  return Array.from(ids);
136
98
  };
99
+ exports.getPythPriceFeeds = getPythPriceFeeds;
137
100
  const updatePythPriceFeedsIfAny = async (tx, quoteResponse) => {
138
101
  // update oracles price if any
139
102
  const pythMap = {};
140
- const pythIds = getPythPriceFeeds(quoteResponse);
103
+ const pythIds = (0, exports.getPythPriceFeeds)(quoteResponse);
141
104
  if (pythIds.length > 0) {
142
105
  const prices = await config_1.Config.getPythConnection().getPriceFeedsUpdateData(pythIds);
143
106
  const ids = await config_1.Config.getPythClient().updatePriceFeeds(tx, prices, pythIds);
@@ -147,3 +110,86 @@ const updatePythPriceFeedsIfAny = async (tx, quoteResponse) => {
147
110
  }
148
111
  return pythMap;
149
112
  };
113
+ exports.updatePythPriceFeedsIfAny = updatePythPriceFeedsIfAny;
114
+ const validateRoutes = (routes, isSponsored) => {
115
+ if (!isSponsored) {
116
+ return;
117
+ }
118
+ const hasOracleBasedSource = routes.some((g) => g.some((s) => getQuote_1.ORACLE_BASED_SOURCES.has(s.pool.type)));
119
+ if (hasOracleBasedSource) {
120
+ throw new Error("Oracle based sources are not supported for sponsored tx");
121
+ }
122
+ };
123
+ exports.validateRoutes = validateRoutes;
124
+ const getExpectedReturn = (returnAmount, slippageBps, commissionBps, tipBps = 0) => {
125
+ if (slippageBps > 10000) {
126
+ throw new Error("Slippage must be less than 100%");
127
+ }
128
+ if (commissionBps > 10000) {
129
+ throw new Error("Commission must be less than 100%");
130
+ }
131
+ if (tipBps > 10000) {
132
+ throw new Error("Tip must be less than 100%");
133
+ }
134
+ const returnAmountWithDecimal = BigInt(returnAmount);
135
+ const tipAmountWithDecimal = (returnAmountWithDecimal * BigInt(tipBps || 0)) / 10000n;
136
+ const commissionAmountWithDecimal = ((returnAmountWithDecimal - tipAmountWithDecimal) * BigInt(commissionBps)) /
137
+ 10000n;
138
+ const expectedReturnWithDecimal = returnAmountWithDecimal -
139
+ tipAmountWithDecimal -
140
+ commissionAmountWithDecimal;
141
+ const minAmountWithDecimal = (expectedReturnWithDecimal * BigInt(1e4 - slippageBps)) / 10000n;
142
+ return {
143
+ tipAmount: tipAmountWithDecimal,
144
+ minAmount: minAmountWithDecimal,
145
+ commissionAmount: commissionAmountWithDecimal,
146
+ expectedAmount: expectedReturnWithDecimal.toString(10),
147
+ };
148
+ };
149
+ exports.getExpectedReturn = getExpectedReturn;
150
+ const settle = (coinObjects, quoteResponse, slippageBps, _commission) => {
151
+ return (tx) => {
152
+ const mergeCoin = coinObjects.length > 1
153
+ ? (tx.mergeCoins(coinObjects[0], coinObjects.slice(1)), coinObjects[0])
154
+ : coinObjects[0];
155
+ const { minAmount, expectedAmount } = (0, exports.getExpectedReturn)(quoteResponse.returnAmountWithDecimal, slippageBps, _commission.commissionBps);
156
+ tx.moveCall({
157
+ target: `${_7k_1._7K_PACKAGE_ID}::settle::settle`,
158
+ typeArguments: [quoteResponse.tokenIn, quoteResponse.tokenOut],
159
+ arguments: [
160
+ tx.object(_7k_1._7K_CONFIG),
161
+ tx.object(_7k_1._7K_VAULT),
162
+ tx.pure.u64(quoteResponse.swapAmountWithDecimal),
163
+ mergeCoin,
164
+ tx.pure.u64(minAmount), // minimum received
165
+ tx.pure.u64(expectedAmount), // expected amount out
166
+ tx.pure.option("address", (0, utils_1.isValidSuiAddress)(_commission.partner) ? _commission.partner : null),
167
+ tx.pure.u64(_commission.commissionBps),
168
+ tx.pure.u64(0),
169
+ ],
170
+ });
171
+ return mergeCoin;
172
+ };
173
+ };
174
+ exports.settle = settle;
175
+ const buildBluefinXTx = async (tx, accountAddress, quoteResponse) => {
176
+ const extra = quoteResponse.swaps[0].extra;
177
+ if (extra.quoteExpiresAtUtcMillis < Date.now()) {
178
+ throw new Error("Quote expired");
179
+ }
180
+ tx.setSenderIfNotSet(accountAddress);
181
+ const bytes = await tx.build({
182
+ client: config_1.Config.getSuiClient(),
183
+ onlyTransactionKind: true,
184
+ });
185
+ const res = await (0, client_1.sponsorBluefinX)({
186
+ quoteId: extra.quoteId,
187
+ txBytes: (0, utils_1.toBase64)(bytes),
188
+ sender: accountAddress,
189
+ });
190
+ if (!res.success) {
191
+ throw new Error("Sponsor failed");
192
+ }
193
+ return new types_1.BluefinXTx(res.quoteId, res.data.txBytes);
194
+ };
195
+ exports.buildBluefinXTx = buildBluefinXTx;
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.multiSwap = exports.optimize = exports.buildTxV2 = void 0;
4
+ const transactions_1 = require("@mysten/sui/transactions");
5
+ const utils_1 = require("@mysten/sui/utils");
6
+ const groupSwapRoutes_1 = require("../../libs/groupSwapRoutes");
7
+ const swapWithRoute_1 = require("../../libs/swapWithRoute");
8
+ const aggregator_1 = require("../../types/aggregator");
9
+ const sui_1 = require("../../utils/sui");
10
+ const buildTx_1 = require("./buildTx");
11
+ const config_1 = require("./config");
12
+ /**
13
+ * Wave-based transaction builder that optimizes swap execution by:
14
+ * 1. Grouping swaps into execution waves based on readiness
15
+ * 2. Merging redundant swaps to the same pool within each wave
16
+ * 3. Processing waves sequentially, passing intermediate tokens between waves
17
+ */
18
+ const buildTxV2 = async ({ quoteResponse, accountAddress, slippage, commission: __commission, devInspect, extendTx, isSponsored, }) => {
19
+ const isBluefinX = (0, aggregator_1.isBluefinXRouting)(quoteResponse);
20
+ const _commission = {
21
+ ...__commission,
22
+ commissionBps: isBluefinX ? 0 : __commission.commissionBps,
23
+ };
24
+ const { tx: _tx, coinIn: _coinIn } = extendTx || {};
25
+ let coinOut;
26
+ if (isBluefinX && devInspect) {
27
+ throw new Error("BluefinX tx is sponsored, skip devInspect");
28
+ }
29
+ if (!accountAddress) {
30
+ throw new Error("Sender address is required");
31
+ }
32
+ if (!quoteResponse.routes) {
33
+ throw new Error("Invalid quote response: 'routes' are required");
34
+ }
35
+ if (!(0, utils_1.isValidSuiAddress)(_commission.partner)) {
36
+ throw new Error("Invalid commission partner address");
37
+ }
38
+ const tx = _tx || new transactions_1.Transaction();
39
+ const routes = (0, groupSwapRoutes_1.groupSwapRoutes)(quoteResponse);
40
+ (0, buildTx_1.validateRoutes)(routes, isSponsored);
41
+ const splits = routes.map((group) => group[0]?.amount ?? "0");
42
+ const coinIn = _coinIn ||
43
+ tx.add((0, transactions_1.coinWithBalance)({
44
+ type: quoteResponse.tokenIn,
45
+ balance: BigInt(quoteResponse.swapAmountWithDecimal),
46
+ useGasCoin: !isSponsored && !isBluefinX,
47
+ }));
48
+ const coinData = tx.splitCoins(coinIn, splits);
49
+ sui_1.SuiUtils.transferOrDestroyZeroCoin(tx, quoteResponse.tokenIn, coinIn, accountAddress);
50
+ const pythMap = await (0, buildTx_1.updatePythPriceFeedsIfAny)(tx, [quoteResponse]);
51
+ const config = await (0, config_1.getConfig)();
52
+ const finalCoins = await (0, exports.optimize)(pythMap, config, routes, coinData, tx, accountAddress);
53
+ // Merge all final coins
54
+ if (finalCoins.length > 0) {
55
+ let mergeCoin = tx.add((0, buildTx_1.settle)(finalCoins, quoteResponse, Math.floor(+slippage * 10000), _commission));
56
+ if (!extendTx) {
57
+ tx.transferObjects([mergeCoin], tx.pure.address(accountAddress));
58
+ }
59
+ else {
60
+ coinOut = mergeCoin;
61
+ }
62
+ }
63
+ if (isBluefinX) {
64
+ return {
65
+ tx: await (0, buildTx_1.buildBluefinXTx)(tx, accountAddress, quoteResponse),
66
+ coinOut,
67
+ };
68
+ }
69
+ tx.setSenderIfNotSet(accountAddress);
70
+ return { tx, coinOut };
71
+ };
72
+ exports.buildTxV2 = buildTxV2;
73
+ const optimize = async (pythMap, config, routes, coinData, tx, accountAddress) => {
74
+ // Initialize route states with split coins
75
+ const routeStates = routes.map((route, index) => ({
76
+ routeIndex: index,
77
+ currentHopIndex: 0,
78
+ currentCoin: coinData[index],
79
+ swaps: route,
80
+ completed: false,
81
+ }));
82
+ const finalCoins = [];
83
+ const coinTypeOut = routes[0][routes[0].length - 1].assetOut;
84
+ // Execute swaps in waves
85
+ let waveNumber = 0;
86
+ while (true) {
87
+ const readyHops = [];
88
+ routeStates.forEach((state) => {
89
+ if (state.completed)
90
+ return;
91
+ if (state.currentHopIndex >= state.swaps.length) {
92
+ state.completed = true;
93
+ finalCoins.push(state.currentCoin);
94
+ return;
95
+ }
96
+ const swap = state.swaps[state.currentHopIndex];
97
+ readyHops.push({
98
+ routeIndex: state.routeIndex,
99
+ hopIndex: state.currentHopIndex,
100
+ swap,
101
+ inputCoin: state.currentCoin,
102
+ });
103
+ });
104
+ if (readyHops.length === 0)
105
+ break;
106
+ // Group hops by pool for merging opportunities
107
+ const poolGroups = new Map();
108
+ readyHops.forEach((hop) => {
109
+ const poolKey = `${hop.swap.poolId}|${hop.swap.assetIn}|${hop.swap.assetOut}`;
110
+ if (!poolGroups.has(poolKey)) {
111
+ poolGroups.set(poolKey, {
112
+ swap: hop.swap,
113
+ hops: [],
114
+ });
115
+ }
116
+ poolGroups.get(poolKey).hops.push(hop);
117
+ });
118
+ // Execute each pool group in this wave
119
+ const wavePromises = [];
120
+ for (const group of poolGroups.values()) {
121
+ const { swap, hops } = group;
122
+ // For merged swaps, we need to combine input coins first
123
+ const wavePromise = (async () => {
124
+ let combinedCoin;
125
+ if (hops.length > 1) {
126
+ // Merge all input coins for this pool
127
+ const inputCoins = hops.map((h) => h.inputCoin);
128
+ tx.mergeCoins(inputCoins[0], inputCoins.slice(1));
129
+ combinedCoin = inputCoins[0];
130
+ }
131
+ else {
132
+ combinedCoin = hops[0].inputCoin;
133
+ }
134
+ // Execute the swap with the combined coin
135
+ const resultCoin = await (0, swapWithRoute_1.swapWithRoute)({
136
+ route: [swap],
137
+ inputCoinObject: combinedCoin,
138
+ currentAccount: accountAddress,
139
+ tx,
140
+ config,
141
+ pythMap,
142
+ });
143
+ if (!resultCoin) {
144
+ throw new Error(`Swap failed for pool ${swap.poolId}`);
145
+ }
146
+ if (hops[0].swap.assetOut === coinTypeOut) {
147
+ finalCoins.push(resultCoin);
148
+ hops.forEach((hop) => {
149
+ const state = routeStates[hop.routeIndex];
150
+ state.completed = true;
151
+ state.currentHopIndex++;
152
+ });
153
+ return;
154
+ }
155
+ // For merged swaps, we need to split the output proportionally
156
+ if (hops.length > 1) {
157
+ // Split output proportionally (except the last one which gets the remainder)
158
+ const splitAmounts = hops
159
+ .slice(1)
160
+ .map((hop) => hop.swap.returnAmount);
161
+ const splitCoins = splitAmounts.length > 0
162
+ ? [resultCoin, ...tx.splitCoins(resultCoin, splitAmounts)]
163
+ : [resultCoin];
164
+ // Assign split coins back to routes
165
+ hops.forEach((hop, index) => {
166
+ const state = routeStates[hop.routeIndex];
167
+ state.currentCoin = splitCoins[index];
168
+ state.currentHopIndex++;
169
+ });
170
+ }
171
+ else {
172
+ // Single swap - simply update the route state
173
+ const state = routeStates[hops[0].routeIndex];
174
+ state.currentCoin = resultCoin;
175
+ state.currentHopIndex++;
176
+ }
177
+ })();
178
+ wavePromises.push(wavePromise);
179
+ }
180
+ // Wait for all swaps in this wave to complete
181
+ await Promise.all(wavePromises);
182
+ waveNumber++;
183
+ }
184
+ return finalCoins;
185
+ };
186
+ exports.optimize = optimize;
187
+ /**
188
+ * execute multiple swap in single transaction
189
+ *
190
+ * User must handle the coins from return
191
+ * @param param - MultiSwapParams
192
+ * @returns a map of coinType to coinObject
193
+ */
194
+ const multiSwap = async ({ sender, slippageBps, swaps, tx, commission, }) => {
195
+ if (swaps.some((s) => (0, aggregator_1.isBluefinXRouting)(s.quote))) {
196
+ throw Error("BluefinX routing not supported yet");
197
+ }
198
+ // update oracles price if any
199
+ const pythMap = await (0, buildTx_1.updatePythPriceFeedsIfAny)(tx, swaps.map((s) => s.quote));
200
+ const map = {};
201
+ const config = await (0, config_1.getConfig)();
202
+ for (const { quote: sorResponse, coinIn } of swaps) {
203
+ const routes = (0, groupSwapRoutes_1.groupSwapRoutes)(sorResponse);
204
+ const splits = routes.map((group) => group[0]?.amount ?? "0");
205
+ const coinData = splits.length === 1
206
+ ? [coinIn]
207
+ : [coinIn, ...tx.splitCoins(coinIn, splits.slice(1))];
208
+ const coinObjects = await (0, exports.optimize)(pythMap, config, routes, coinData, tx, sender);
209
+ if (coinObjects.length > 0) {
210
+ const mergeCoin = tx.add((0, buildTx_1.settle)(coinObjects, sorResponse, slippageBps, commission));
211
+ if (!map[(0, utils_1.normalizeStructTag)(sorResponse.tokenOut)]) {
212
+ map[(0, utils_1.normalizeStructTag)(sorResponse.tokenOut)] = [];
213
+ }
214
+ map[(0, utils_1.normalizeStructTag)(sorResponse.tokenOut)].push(mergeCoin);
215
+ }
216
+ }
217
+ const result = {};
218
+ for (const [tokenOut, coins] of Object.entries(map)) {
219
+ if (coins.length > 1) {
220
+ tx.mergeCoins(coins[0], coins.slice(1));
221
+ }
222
+ result[tokenOut] = coins[0];
223
+ }
224
+ tx.setSenderIfNotSet(sender);
225
+ return result;
226
+ };
227
+ exports.multiSwap = multiSwap;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_SOURCES = void 0;
3
+ exports.ORACLE_BASED_SOURCES = exports.DEFAULT_SOURCES = void 0;
4
4
  exports.getQuote = getQuote;
5
5
  const utils_1 = require("@mysten/sui/utils");
6
6
  const fetchClient_1 = require("../../config/fetchClient");
@@ -30,7 +30,18 @@ exports.DEFAULT_SOURCES = [
30
30
  "sevenk_v1",
31
31
  "fullsail",
32
32
  ];
33
- async function getQuote({ tokenIn, tokenOut, amountIn, sources = exports.DEFAULT_SOURCES, commissionBps, targetPools, excludedPools, taker, }) {
33
+ exports.ORACLE_BASED_SOURCES = new Set([
34
+ "obric",
35
+ "haedal_pmm",
36
+ "sevenk_v1",
37
+ "steamm_oracle_quoter",
38
+ "steamm_oracle_quoter_v2",
39
+ ]);
40
+ async function getQuote({ tokenIn, tokenOut, amountIn, sources: _sources = exports.DEFAULT_SOURCES, commissionBps, targetPools, excludedPools, taker, isSponsored, }) {
41
+ let sources = _sources;
42
+ if (isSponsored) {
43
+ sources = _sources.filter((s) => !exports.ORACLE_BASED_SOURCES.has(s));
44
+ }
34
45
  const params = new URLSearchParams({
35
46
  amount: amountIn,
36
47
  from: (0, utils_1.normalizeStructTag)(tokenIn),
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./buildTx"), exports);
18
+ __exportStar(require("./buildTxV2"), exports);
18
19
  __exportStar(require("./estimateGasFee"), exports);
19
20
  __exportStar(require("./executeTx"), exports);
20
21
  __exportStar(require("./getQuote"), exports);
package/lib/cjs/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.getDcaOrderExecutions = exports.getClosedDcaOrders = exports.cancelDcaOrder = exports.getOpenDcaOrders = exports.placeDcaOrder = exports.getClosedLimitOrders = exports.claimExpiredLimitOrder = exports.cancelLimitOrder = exports.getOpenLimitOrders = exports.placeLimitOrder = exports.DEFAULT_SOURCES = exports.executeBluefinTx = exports.executeTx = exports.getSwapHistory = exports.buildTx = exports.estimateGasFee = exports.getQuote = exports.getSuiPrice = exports.getTokenPrices = exports.getTokenPrice = exports.setSuiClient = exports.getSuiClient = exports.Config = void 0;
17
+ exports.setSuiClient = exports.placeLimitOrder = exports.placeDcaOrder = exports.multiSwap = exports.getTokenPrices = exports.getTokenPrice = exports.getSwapHistory = exports.getSuiPrice = exports.getSuiClient = exports.getQuote = exports.getOpenLimitOrders = exports.getOpenDcaOrders = exports.getDcaOrderExecutions = exports.getClosedLimitOrders = exports.getClosedDcaOrders = exports.executeTx = exports.executeBluefinTx = exports.estimateGasFee = exports.DEFAULT_SOURCES = exports.Config = exports.claimExpiredLimitOrder = exports.cancelLimitOrder = exports.cancelDcaOrder = exports.buildTxV2 = exports.buildTx = void 0;
18
18
  __exportStar(require("./types/aggregator"), exports);
19
19
  const config_1 = require("./config");
20
20
  Object.defineProperty(exports, "Config", { enumerable: true, get: function () { return config_1.Config; } });
@@ -26,11 +26,13 @@ const client_1 = require("./libs/protocols/bluefinx/client");
26
26
  Object.defineProperty(exports, "executeBluefinTx", { enumerable: true, get: function () { return client_1.executeBluefinTx; } });
27
27
  const swap_1 = require("./features/swap");
28
28
  Object.defineProperty(exports, "buildTx", { enumerable: true, get: function () { return swap_1.buildTx; } });
29
+ Object.defineProperty(exports, "buildTxV2", { enumerable: true, get: function () { return swap_1.buildTxV2; } });
30
+ Object.defineProperty(exports, "DEFAULT_SOURCES", { enumerable: true, get: function () { return swap_1.DEFAULT_SOURCES; } });
29
31
  Object.defineProperty(exports, "estimateGasFee", { enumerable: true, get: function () { return swap_1.estimateGasFee; } });
30
32
  Object.defineProperty(exports, "executeTx", { enumerable: true, get: function () { return swap_1.executeTx; } });
31
33
  Object.defineProperty(exports, "getQuote", { enumerable: true, get: function () { return swap_1.getQuote; } });
32
34
  Object.defineProperty(exports, "getSwapHistory", { enumerable: true, get: function () { return swap_1.getSwapHistory; } });
33
- Object.defineProperty(exports, "DEFAULT_SOURCES", { enumerable: true, get: function () { return swap_1.DEFAULT_SOURCES; } });
35
+ Object.defineProperty(exports, "multiSwap", { enumerable: true, get: function () { return swap_1.multiSwap; } });
34
36
  const limitDca_1 = require("./features/limitDca");
35
37
  Object.defineProperty(exports, "cancelDcaOrder", { enumerable: true, get: function () { return limitDca_1.cancelDcaOrder; } });
36
38
  Object.defineProperty(exports, "cancelLimitOrder", { enumerable: true, get: function () { return limitDca_1.cancelLimitOrder; } });
@@ -61,6 +63,8 @@ exports.default = {
61
63
  getQuote: swap_1.getQuote,
62
64
  estimateGasFee: swap_1.estimateGasFee,
63
65
  buildTx: swap_1.buildTx,
66
+ buildTxV2: swap_1.buildTxV2,
67
+ multiSwap: swap_1.multiSwap,
64
68
  getSwapHistory: swap_1.getSwapHistory,
65
69
  executeTx: swap_1.executeTx,
66
70
  executeBluefinTx: client_1.executeBluefinTx,
@@ -1,3 +1,4 @@
1
+ import { Transaction } from "@mysten/sui/transactions";
1
2
  export interface PlaceLimitOrderParams {
2
3
  accountAddress: string;
3
4
  payCoinType: string;
@@ -8,5 +9,5 @@ export interface PlaceLimitOrderParams {
8
9
  expireTs: bigint;
9
10
  devInspect?: boolean;
10
11
  }
11
- export declare function placeLimitOrder({ accountAddress, payCoinType, targetCoinType, payCoinAmount, rate, slippage, expireTs, devInspect, }: PlaceLimitOrderParams): Promise<import("@mysten/sui/dist/cjs/transactions").Transaction>;
12
+ export declare function placeLimitOrder({ accountAddress, payCoinType, targetCoinType, payCoinAmount, rate, slippage, expireTs, devInspect, }: PlaceLimitOrderParams): Promise<Transaction>;
12
13
  //# sourceMappingURL=placeLimitOrder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"placeLimitOrder.d.ts","sourceRoot":"","sources":["../../../../../src/features/limitDca/placeLimitOrder.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAsB,eAAe,CAAC,EACpC,cAAc,EACd,WAAW,EACX,cAAc,EACd,aAAa,EACb,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,UAAU,GACX,EAAE,qBAAqB,oEAwBvB"}
1
+ {"version":3,"file":"placeLimitOrder.d.ts","sourceRoot":"","sources":["../../../../../src/features/limitDca/placeLimitOrder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGxE,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAsB,eAAe,CAAC,EACpC,cAAc,EACd,WAAW,EACX,cAAc,EACd,aAAa,EACb,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,UAAU,GACX,EAAE,qBAAqB,wBAyBvB"}
@@ -1,4 +1,17 @@
1
- import { BuildTxResult } from "../../types/aggregator";
1
+ import { Transaction, TransactionObjectArgument } from "@mysten/sui/transactions";
2
+ import { BluefinXTx } from "../../libs/protocols/bluefinx/types";
3
+ import { BuildTxResult, Commission, QuoteResponse, TxSorSwap } from "../../types/aggregator";
2
4
  import { BuildTxParams } from "../../types/tx";
3
5
  export declare const buildTx: ({ quoteResponse, accountAddress, slippage, commission: __commission, devInspect, extendTx, isSponsored, }: BuildTxParams) => Promise<BuildTxResult>;
6
+ export declare const getPythPriceFeeds: (responses: QuoteResponse[]) => string[];
7
+ export declare const updatePythPriceFeedsIfAny: (tx: Transaction, quoteResponse: QuoteResponse[]) => Promise<Record<string, string>>;
8
+ export declare const validateRoutes: (routes: TxSorSwap[][], isSponsored?: boolean) => void;
9
+ export declare const getExpectedReturn: (returnAmount: string, slippageBps: number, commissionBps: number, tipBps?: number) => {
10
+ tipAmount: bigint;
11
+ minAmount: bigint;
12
+ commissionAmount: bigint;
13
+ expectedAmount: string;
14
+ };
15
+ export declare const settle: (coinObjects: TransactionObjectArgument[], quoteResponse: QuoteResponse, slippageBps: number, _commission: Commission) => (tx: Transaction) => TransactionObjectArgument;
16
+ export declare const buildBluefinXTx: (tx: Transaction, accountAddress: string, quoteResponse: QuoteResponse) => Promise<BluefinXTx>;
4
17
  //# sourceMappingURL=buildTx.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildTx.d.ts","sourceRoot":"","sources":["../../../../../src/features/swap/buildTx.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,aAAa,EAId,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAK/C,eAAO,MAAM,OAAO,GAAU,2GAQ3B,aAAa,KAAG,OAAO,CAAC,aAAa,CA4IvC,CAAC"}
1
+ {"version":3,"file":"buildTx.d.ts","sourceRoot":"","sources":["../../../../../src/features/swap/buildTx.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,yBAAyB,EAC1B,MAAM,0BAA0B,CAAC;AAOlC,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAEjE,OAAO,EACL,aAAa,EACb,UAAU,EAEV,aAAa,EACb,SAAS,EACV,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAK/C,eAAO,MAAM,OAAO,GAAU,2GAQ3B,aAAa,KAAG,OAAO,CAAC,aAAa,CA0FvC,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,WAAW,aAAa,EAAE,aAa3D,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,IAAI,WAAW,EACf,eAAe,aAAa,EAAE,oCAmB/B,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,QAAQ,SAAS,EAAE,EAAE,EACrB,cAAc,OAAO,SAWtB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,cAAc,MAAM,EACpB,aAAa,MAAM,EACnB,eAAe,MAAM,EACrB,SAAQ,MAAU;;;;;CA8BnB,CAAC;AAEF,eAAO,MAAM,MAAM,GACjB,aAAa,yBAAyB,EAAE,EACxC,eAAe,aAAa,EAC5B,aAAa,MAAM,EACnB,aAAa,UAAU,MAEf,IAAI,WAAW,8BAiCxB,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,IAAI,WAAW,EACf,gBAAgB,MAAM,EACtB,eAAe,aAAa,wBAsB7B,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { Transaction, TransactionObjectArgument } from "@mysten/sui/transactions";
2
+ import { BuildTxResult, Config, TxSorSwap } from "../../types/aggregator";
3
+ import { BuildTxParams, MultiSwapParams } from "../../types/tx";
4
+ /**
5
+ * Wave-based transaction builder that optimizes swap execution by:
6
+ * 1. Grouping swaps into execution waves based on readiness
7
+ * 2. Merging redundant swaps to the same pool within each wave
8
+ * 3. Processing waves sequentially, passing intermediate tokens between waves
9
+ */
10
+ export declare const buildTxV2: ({ quoteResponse, accountAddress, slippage, commission: __commission, devInspect, extendTx, isSponsored, }: BuildTxParams) => Promise<BuildTxResult>;
11
+ export declare const optimize: (pythMap: Record<string, string>, config: Config, routes: TxSorSwap[][], coinData: TransactionObjectArgument[], tx: Transaction, accountAddress: string) => Promise<TransactionObjectArgument[]>;
12
+ /**
13
+ * execute multiple swap in single transaction
14
+ *
15
+ * User must handle the coins from return
16
+ * @param param - MultiSwapParams
17
+ * @returns a map of coinType to coinObject
18
+ */
19
+ export declare const multiSwap: ({ sender, slippageBps, swaps, tx, commission, }: MultiSwapParams) => Promise<Record<string, TransactionObjectArgument>>;
20
+ //# sourceMappingURL=buildTxV2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildTxV2.d.ts","sourceRoot":"","sources":["../../../../../src/features/swap/buildTxV2.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,yBAAyB,EAC1B,MAAM,0BAA0B,CAAC;AAIlC,OAAO,EACL,aAAa,EACb,MAAM,EAEN,SAAS,EACV,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAUhE;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAU,2GAQ7B,aAAa,KAAG,OAAO,CAAC,aAAa,CAuFvC,CAAC;AAEF,eAAO,MAAM,QAAQ,GACnB,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,QAAQ,MAAM,EACd,QAAQ,SAAS,EAAE,EAAE,EACrB,UAAU,yBAAyB,EAAE,EACrC,IAAI,WAAW,EACf,gBAAgB,MAAM,yCA0JvB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GAAU,iDAM7B,eAAe,uDAiDjB,CAAC"}
@@ -15,8 +15,11 @@ interface Params {
15
15
  excludedPools?: string[];
16
16
  /** The taker address, required for bluefinx */
17
17
  taker?: string;
18
+ /** If true, excludes all liquidity sources that depend on pyth price feeds - pyth client use tx.gas to pay the fee*/
19
+ isSponsored?: boolean;
18
20
  }
19
21
  export declare const DEFAULT_SOURCES: SourceDex[];
20
- export declare function getQuote({ tokenIn, tokenOut, amountIn, sources, commissionBps, targetPools, excludedPools, taker, }: Params): Promise<QuoteResponse>;
22
+ export declare const ORACLE_BASED_SOURCES: Set<SourceDex>;
23
+ export declare function getQuote({ tokenIn, tokenOut, amountIn, sources: _sources, commissionBps, targetPools, excludedPools, taker, isSponsored, }: Params): Promise<QuoteResponse>;
21
24
  export {};
22
25
  //# sourceMappingURL=getQuote.d.ts.map