@tradeport/sui-trading-sdk 0.4.62 → 0.4.64

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.
@@ -1,58 +1,30 @@
1
- import type { Transaction } from '@mysten/sui/transactions';
2
- import { USDC_COIN_TYPE } from '../../constants';
3
- import type { SuiClient } from '@mysten/sui/client';
4
- import { TRADEPORT_LISTINGS_PACKAGE, TRADEPORT_LISTINGS_STORE } from '../../constants';
5
- import { type SponsorNftListingOptions } from './sponsorNftListing';
1
+ import { coinWithBalance, type Transaction } from '@mysten/sui/transactions';
6
2
  import BigNumber from '../../bigNumberConfig';
7
- import { calculateSwapInputAmountWithSlippage, swapCoins } from '../swapCoins/swapCoins';
3
+ import {
4
+ TRADEPORT_LISTINGS_PACKAGE,
5
+ TRADEPORT_LISTINGS_STORE,
6
+ USDC_COIN_TYPE,
7
+ } from '../../constants';
8
+ import { type SponsorNftListingOptions } from './sponsorNftListing';
8
9
 
9
10
  export const addSponsorListingTx = async ({
10
11
  tx,
11
12
  nftTokenId,
12
13
  nftType,
13
- suiClient,
14
- walletAddress,
15
14
  sponsorOptions,
16
- defiRouterUrl,
17
15
  }: {
18
16
  tx: Transaction;
19
17
  nftTokenId: string;
20
18
  nftType: string;
21
- suiClient: SuiClient;
22
- walletAddress: string;
23
19
  sponsorOptions: SponsorNftListingOptions;
24
- defiRouterUrl: string;
25
20
  }) => {
26
- const coinInAmount =
27
- sponsorOptions?.coinInAmount ??
28
- (await calculateSwapInputAmountWithSlippage({
29
- coinInType: sponsorOptions?.coinInType,
30
- coinOutType: USDC_COIN_TYPE,
31
- coinOutAmount: new BigNumber(sponsorOptions?.usdcFeeAmountPerPeriod)
32
- ?.times(sponsorOptions?.numOfPeriods)
33
- .toString(),
34
- slippage: sponsorOptions?.slippage,
35
- }));
21
+ const totalFee = new BigNumber(sponsorOptions?.usdcFeeAmountPerPeriod)
22
+ ?.times(sponsorOptions?.numOfPeriods)
23
+ .toString();
36
24
 
37
- const { coinOut } = await swapCoins(
38
- {
39
- walletAddress,
40
- coinInType: sponsorOptions?.coinInType,
41
- coinInAmount: coinInAmount.toString(),
42
- coinOutType: USDC_COIN_TYPE,
43
- slippage: sponsorOptions?.slippage,
44
- tx,
45
- },
46
- { suiClient, defiRouterUrl },
47
- );
48
-
49
- const [sponsorFeeCoin] = tx.splitCoins(coinOut, [
50
- tx.pure.u64(
51
- new BigNumber(sponsorOptions?.usdcFeeAmountPerPeriod)
52
- ?.times(sponsorOptions?.numOfPeriods)
53
- .toString(),
54
- ),
55
- ]);
25
+ const sponsorCoin = sponsorOptions.coinToSplit
26
+ ? tx.splitCoins(sponsorOptions.coinToSplit, [tx.pure.u64(totalFee)])[0]
27
+ : coinWithBalance({ type: USDC_COIN_TYPE, balance: BigInt(totalFee) });
56
28
 
57
29
  tx.moveCall({
58
30
  target: `${TRADEPORT_LISTINGS_PACKAGE}::tradeport_listings::add_sponsored_listing`,
@@ -61,10 +33,8 @@ export const addSponsorListingTx = async ({
61
33
  tx.object.clock(),
62
34
  tx.pure.id(nftTokenId),
63
35
  tx.pure.u64(sponsorOptions?.numOfPeriods),
64
- tx.object(sponsorFeeCoin),
36
+ tx.object(sponsorCoin),
65
37
  ],
66
38
  typeArguments: [nftType],
67
39
  });
68
-
69
- tx.transferObjects([coinOut], walletAddress);
70
40
  };
@@ -1,30 +1,28 @@
1
1
  import { Transaction } from '@mysten/sui/transactions';
2
2
  import { gqlChainRequest } from '../../graphql/gqlChainRequest';
3
3
  import { fetchNftCollectionChainState } from '../../graphql/queries/fetchNftCollectionChainState';
4
+ import { getNftType } from '../../helpers/getNftType';
4
5
  import { isOriginByteCollection } from '../../helpers/originByte/isOriginByteCollection';
5
- import type { RequestContext } from '../../SuiTradingClient';
6
6
  import { addSponsorListingTx } from './addSponsorNftListingTx';
7
- import { getNftType } from '../../helpers/getNftType';
8
7
 
9
8
  export type SponsorNftListingOptions = {
10
9
  shouldSponsor?: boolean;
11
10
  numOfPeriods?: number;
12
- slippage?: number;
13
- coinInType?: string;
14
- coinInAmount?: string;
15
11
  usdcFeeAmountPerPeriod?: number;
12
+ coinToSplit?: any;
16
13
  };
17
14
 
18
15
  export type SponsorNftListing = {
16
+ tx?: Transaction;
19
17
  nftTokenId: string;
20
- walletAddress: string;
21
18
  options: SponsorNftListingOptions;
22
19
  };
23
20
 
24
- export const sponsorNftListing = async (
25
- { nftTokenId, walletAddress, options }: SponsorNftListing,
26
- context: RequestContext,
27
- ): Promise<Transaction> => {
21
+ export const sponsorNftListing = async ({
22
+ tx: existingTx,
23
+ nftTokenId,
24
+ options,
25
+ }: SponsorNftListing): Promise<Transaction> => {
28
26
  const res = await gqlChainRequest({
29
27
  chain: 'sui',
30
28
  query: fetchNftCollectionChainState,
@@ -49,17 +47,14 @@ export const sponsorNftListing = async (
49
47
  throw new Error(`You cannot sponsor an Origin Byte NFT. Nft Token Id: ${nftTokenId}`);
50
48
  }
51
49
 
52
- const tx = new Transaction();
50
+ const tx = existingTx ?? new Transaction();
53
51
 
54
52
  await addSponsorListingTx({
55
53
  tx,
56
- suiClient: context.suiClient,
57
54
  nftTokenId,
58
55
  nftType,
59
- walletAddress,
60
56
  sponsorOptions: options,
61
- defiRouterUrl: context.defiRouterUrl,
62
57
  });
63
58
 
64
- return tx instanceof Transaction ? tx : Transaction.from(tx);
59
+ return tx;
65
60
  };
@@ -1,9 +1,8 @@
1
- import { type Transaction } from '@mysten/sui/transactions';
1
+ import { Transaction } from '@mysten/sui/transactions';
2
2
  import { TRADEPORT_MULTI_BID_PACKAGE, TRADEPORT_MULTI_BID_STORE } from '../../constants';
3
3
  import { gqlChainRequest } from '../../graphql/gqlChainRequest';
4
4
  import { fetchMultibidChainIdById } from '../../graphql/queries/fetchMultibidById';
5
- import { deserializeOrCreateTxBlock } from '../../helpers/deserializeOrCreateTxBlock';
6
- import { extractSwapResultCoinFromTxBlock } from '../../helpers/extractSwapResultCoin';
5
+ import { type RequestContext } from '../../SuiTradingClient';
7
6
 
8
7
  export interface UpdateMultiBid {
9
8
  walletAddress: string;
@@ -11,20 +10,25 @@ export interface UpdateMultiBid {
11
10
  name?: string;
12
11
  amount?: bigint;
13
12
  amountToWithdraw?: bigint;
14
- tx?: Transaction | string;
13
+ tx?: Transaction;
15
14
  multiBidChainId?: any;
15
+ coinToSplit?: any;
16
16
  }
17
17
 
18
- export async function updateMultiBid({
19
- multiBidId,
20
- name,
21
- amount,
22
- amountToWithdraw,
23
- tx: existingTx,
24
- multiBidChainId,
25
- }: UpdateMultiBid): Promise<Transaction> {
26
- const tx = deserializeOrCreateTxBlock({ existingTx });
27
- const swapResultCoin = extractSwapResultCoinFromTxBlock(tx);
18
+ export async function updateMultiBid(
19
+ {
20
+ walletAddress,
21
+ multiBidId,
22
+ name,
23
+ amount,
24
+ amountToWithdraw,
25
+ tx: existingTx,
26
+ multiBidChainId,
27
+ coinToSplit,
28
+ }: UpdateMultiBid,
29
+ context?: Pick<RequestContext, 'suiClient' | 'tradeportRouterUrl'>,
30
+ ): Promise<Transaction> {
31
+ const tx = existingTx ?? new Transaction();
28
32
 
29
33
  if (!multiBidChainId) {
30
34
  const { chain_id: chainId, cancelled_at } =
@@ -46,7 +50,7 @@ export async function updateMultiBid({
46
50
  multiBidChainId = chainId;
47
51
  }
48
52
 
49
- const [coin] = tx.splitCoins(swapResultCoin ? swapResultCoin : tx.gas, [amount ?? 0n]);
53
+ const [coin] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [amount ?? 0n]);
50
54
  tx.moveCall({
51
55
  target: `${TRADEPORT_MULTI_BID_PACKAGE}::tradeport_biddings::update_multi_bid`,
52
56
  arguments: [
@@ -1,2 +1,9 @@
1
1
  export const addLeadingZerosAfter0x = (str: string): string =>
2
2
  `0x${str?.replace('0x', '').padStart(64, '0')}`;
3
+
4
+ export const addLeadingZerosToPackage = (str: string, skip0x?: boolean) => {
5
+ const address = str?.split('::')[0];
6
+ const suffix = `::${str?.split('::').slice(1).join('::')}`;
7
+
8
+ return `${skip0x ? '' : '0x'}${address?.replace('0x', '').padStart(64, '0')}${suffix}`;
9
+ };
@@ -1,5 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": ["Bash(xxd:*)", "Bash(pnpm tsc:*)", "Bash(npx tsc:*)"]
4
- }
5
- }
@@ -1,13 +0,0 @@
1
- import { Transaction } from '@mysten/sui/transactions';
2
-
3
- export const deserializeOrCreateTxBlock = ({
4
- existingTx,
5
- }: {
6
- existingTx: Transaction | string;
7
- }) => {
8
- if (typeof existingTx === 'string') {
9
- return Transaction.from(existingTx);
10
- }
11
-
12
- return existingTx ?? new Transaction();
13
- };
@@ -1,17 +0,0 @@
1
- import { type Transaction } from '@mysten/sui/transactions';
2
-
3
- export const extractSwapResultCoinFromTxBlock = (txBlock: Transaction | string) => {
4
- if (!txBlock) return undefined;
5
-
6
- const tx = typeof txBlock === 'string' ? JSON.parse(txBlock) : txBlock?.getData();
7
- const commands = tx.commands ?? [];
8
-
9
- const index = commands.findIndex(
10
- (cmd: any) =>
11
- cmd?.MoveCall?.module === 'universal_router' && cmd?.MoveCall?.function === 'settle',
12
- );
13
-
14
- if (index === -1) return undefined;
15
-
16
- return { $kind: 'Result', Result: index };
17
- };
@@ -1,400 +0,0 @@
1
- import {
2
- AggregatorQuoter,
3
- type Coin,
4
- CoinProvider,
5
- type Commission,
6
- type GetRoutesResult,
7
- Protocol,
8
- type Route,
9
- TradeBuilder,
10
- } from '@flowx-finance/sdk';
11
- import BigNumber from '../../bigNumberConfig';
12
- import { coinWithBalance, Transaction, type TransactionResult } from '@mysten/sui/transactions';
13
- import { normalizeStructTag, normalizeSuiAddress, SUI_TYPE_ARG } from '@mysten/sui/utils';
14
- import ms, { type StringValue } from 'ms';
15
- import { type RequestContext } from '../../SuiTradingClient';
16
- import {
17
- DexConstants,
18
- TRADEPORT_NFT_STRATEGY_MANAGER_ID,
19
- TRADEPORT_NFT_STRATEGY_PACKAGE_ID,
20
- } from '../../constants';
21
- import { getCollectionChainStateByFtType } from '../../helpers/getCollectionChainState';
22
- import { toDecimalValue, toPureValue } from '../../utils/pureValues';
23
-
24
- export type SwapCoins = {
25
- walletAddress: string;
26
- coinInType: string;
27
- coinInAmount: string;
28
- coinOutType: string;
29
- slippage?: number;
30
- ttl?: StringValue;
31
- excludeSources?: Protocol[];
32
- routes?: Array<Route<Coin, Coin>>;
33
- commission?: Commission;
34
- tx?: Transaction;
35
- };
36
-
37
- export type SwapRoutesArgs = {
38
- walletAddress: string;
39
- coinInType: string;
40
- coinOutType: string;
41
- amountToSwap: bigint;
42
- excludeSources?: Protocol[];
43
- commission?: Commission;
44
- };
45
-
46
- export async function swapCoins(
47
- {
48
- walletAddress,
49
- coinInType,
50
- coinInAmount,
51
- coinOutType,
52
- slippage = 1000, // 0.1%
53
- ttl = '5m',
54
- excludeSources = [Protocol.STEAMM, Protocol.BOLT],
55
- routes,
56
- commission,
57
- tx: existingTx,
58
- }: SwapCoins,
59
- { suiClient, defiRouterUrl }: Pick<RequestContext, 'suiClient' | 'defiRouterUrl'>,
60
- ): Promise<{
61
- tx: Transaction;
62
- coinOut: TransactionResult;
63
- }> {
64
- const inAmount = BigInt(coinInAmount);
65
- const tx = existingTx ?? new Transaction();
66
- coinInType = normalizeStructTag(coinInType);
67
- coinOutType = normalizeStructTag(coinOutType);
68
- walletAddress = normalizeSuiAddress(walletAddress);
69
- tx.setSenderIfNotSet(normalizeSuiAddress(walletAddress));
70
-
71
- // Zero input amount, return zero output amount
72
- if (inAmount === 0n) {
73
- const zeroCoin = tx.moveCall({
74
- target: '0x2::coin::zero',
75
- typeArguments: [coinOutType],
76
- });
77
- return {
78
- tx,
79
- coinOut: zeroCoin,
80
- };
81
- }
82
-
83
- // Input Coin and Out Coin are the same, no need to swap
84
- if (coinInType === coinOutType) {
85
- const getCoin = coinWithBalance({
86
- type: coinInType,
87
- balance: inAmount,
88
- });
89
- return {
90
- tx,
91
- coinOut: getCoin(tx),
92
- };
93
- }
94
-
95
- const types = await getAffectedNftAndFtTypes(coinInType, coinOutType);
96
- const { feeAndReward, amountToSwap } = calculateSwapAmounts(types.length, inAmount);
97
-
98
- if (!routes) {
99
- routes = (
100
- await buildSwapRoutes(
101
- {
102
- walletAddress,
103
- coinInType,
104
- coinOutType,
105
-
106
- amountToSwap,
107
- excludeSources,
108
- commission,
109
- },
110
- { defiRouterUrl },
111
- )
112
- ).routes;
113
- }
114
-
115
- const tradeBuilder = new TradeBuilder('mainnet', routes);
116
- if (commission) {
117
- tradeBuilder.commission(commission);
118
- }
119
-
120
- const trade = tradeBuilder
121
- .sender(walletAddress)
122
- .slippage(slippage)
123
- .deadline(Date.now() + ms(ttl))
124
- .build();
125
-
126
- const coinOut = (await trade.swap({ client: suiClient, tx })) as TransactionResult;
127
-
128
- let suiFeeAndRewardCoin: TransactionResult;
129
- if (coinInType === normalizeStructTag(SUI_TYPE_ARG)) {
130
- suiFeeAndRewardCoin = tx.splitCoins(tx.gas, [
131
- tx.pure.u64(feeAndReward),
132
- ])[0] as unknown as TransactionResult;
133
- } else {
134
- // Swap to SUI for fee and reward
135
- const suiFeeAndRewardRoutes = await buildSwapRoutes(
136
- {
137
- walletAddress,
138
- coinInType,
139
- coinOutType: SUI_TYPE_ARG,
140
- amountToSwap: feeAndReward,
141
- excludeSources,
142
- },
143
- { defiRouterUrl },
144
- );
145
- const suiFeeTradeBuilder = new TradeBuilder('mainnet', suiFeeAndRewardRoutes.routes);
146
- const suiFeeTrade = suiFeeTradeBuilder
147
- .sender(walletAddress)
148
- .slippage(slippage)
149
- .deadline(Date.now() + ms(ttl))
150
- .build();
151
- suiFeeAndRewardCoin = (await suiFeeTrade.swap({ client: suiClient, tx })) as TransactionResult;
152
- }
153
-
154
- if (types.length > 0) {
155
- if (types.length > 2) {
156
- throw new Error('Unexpected affected types count greater than 2');
157
- }
158
-
159
- let usedSuiCoin = suiFeeAndRewardCoin;
160
- if (types.length === 2) {
161
- const suiFeeAndRewardCoinBalance = tx.moveCall({
162
- target: '0x2::coin::value',
163
- typeArguments: [SUI_TYPE_ARG],
164
- arguments: [suiFeeAndRewardCoin],
165
- });
166
- const halfSuiCoinAmount = tx.moveCall({
167
- target: '0x2::math::divide_and_round_up',
168
- arguments: [suiFeeAndRewardCoinBalance, tx.pure.u64(2)],
169
- });
170
- const [coin] = tx.splitCoins(usedSuiCoin, [halfSuiCoinAmount]);
171
- usedSuiCoin = coin as unknown as TransactionResult; // 1/2 of SUI for 1st iteration, remaining SUI for 2nd iteration
172
- }
173
-
174
- for (const data of types) {
175
- // max 2 iterations
176
- const { nftType, ftType, type } = data;
177
- switch (type) {
178
- case 'liquidNft': {
179
- // Pay 4% fee + reward for liquid nft, the contract will split this balance itself
180
- tx.moveCall({
181
- target: `${DexConstants.commission}::commission::pay`,
182
- arguments: [
183
- tx.object(DexConstants.commissionManager),
184
- tx.pure.string('swap'),
185
- usedSuiCoin,
186
- tx.pure.option('string', nftType),
187
- ],
188
- typeArguments: [SUI_TYPE_ARG],
189
- });
190
- break;
191
- }
192
-
193
- case 'nftStrategy': {
194
- // Pay 1% fee + 3% reward for nft strategy
195
- const suiFeeAndRewardCoinBalance = tx.moveCall({
196
- target: '0x2::coin::value',
197
- typeArguments: [SUI_TYPE_ARG],
198
- arguments: [usedSuiCoin],
199
- });
200
- const feeAmount = tx.moveCall({
201
- target: '0x2::math::divide_and_round_up',
202
- arguments: [suiFeeAndRewardCoinBalance, tx.pure.u64(4)],
203
- });
204
- const [feeCoin] = tx.splitCoins(usedSuiCoin, [feeAmount]);
205
- tx.moveCall({
206
- target: `${DexConstants.commission}::commission::pay`,
207
- arguments: [
208
- tx.object(DexConstants.commissionManager),
209
- tx.pure.string('swap'),
210
- feeCoin,
211
- tx.pure.option('string', undefined),
212
- ],
213
- typeArguments: [SUI_TYPE_ARG],
214
- });
215
- tx.moveCall({
216
- target: `${TRADEPORT_NFT_STRATEGY_PACKAGE_ID}::tradeport_nft_strategy::reward_strategy`,
217
- arguments: [
218
- tx.object(TRADEPORT_NFT_STRATEGY_MANAGER_ID),
219
- usedSuiCoin, // 3/4 retained coin for reward
220
- ],
221
- typeArguments: [nftType, ftType],
222
- });
223
- break;
224
- }
225
-
226
- default: {
227
- throw new Error(`Unexpected type ${type as string}`);
228
- }
229
- }
230
-
231
- usedSuiCoin = suiFeeAndRewardCoin; // remaining SUI after 1st iteration
232
- }
233
- } else {
234
- // Default, get 1% fee
235
- tx.moveCall({
236
- target: `${DexConstants.commission}::commission::pay`,
237
- arguments: [
238
- tx.object(DexConstants.commissionManager),
239
- tx.pure.string('swap'),
240
- suiFeeAndRewardCoin,
241
- tx.pure.option('string', undefined),
242
- ],
243
- typeArguments: [SUI_TYPE_ARG],
244
- });
245
- }
246
-
247
- return {
248
- tx,
249
- coinOut,
250
- };
251
- }
252
-
253
- export async function calculateAmountToSwap({
254
- coinInType,
255
- coinOutType,
256
- coinInAmount,
257
- }: Pick<SwapCoins, 'coinInType' | 'coinOutType' | 'coinInAmount'>): Promise<bigint> {
258
- const types = await getAffectedNftAndFtTypes(coinInType, coinOutType);
259
- const { amountToSwap } = calculateSwapAmounts(types.length, BigInt(coinInAmount));
260
- return amountToSwap;
261
- }
262
-
263
- export async function calculateSwapInputAmountWithSlippage({
264
- coinInType,
265
- coinOutType,
266
- coinOutAmount,
267
- slippage = 0,
268
- }: {
269
- coinInType: string;
270
- coinOutType: string;
271
- coinOutAmount: string | number;
272
- slippage?: number;
273
- }): Promise<BigNumber> {
274
- if (coinInType === coinOutType) {
275
- return new BigNumber(coinOutAmount);
276
- }
277
-
278
- const provider = new CoinProvider('mainnet');
279
-
280
- const coinQueryResult = await provider.getCoins({
281
- limit: 2,
282
- coinTypes: [coinInType, coinOutType],
283
- });
284
-
285
- const coinIn = coinQueryResult?.find(
286
- (coin) => normalizeStructTag(coin.coinType) === normalizeStructTag(coinInType),
287
- );
288
- const coinOut = coinQueryResult?.find(
289
- (coin) => normalizeStructTag(coin.coinType) === normalizeStructTag(coinOutType),
290
- );
291
-
292
- const exchangeRate = new BigNumber(coinOut.derivedPriceInUSD ?? 0)?.gt(0)
293
- ? new BigNumber(coinIn?.derivedPriceInUSD ?? 1)?.div(coinOut?.derivedPriceInUSD ?? 1)
294
- : new BigNumber(0);
295
-
296
- const amountSell = toDecimalValue(coinOutAmount.toString(), coinOut?.decimals).div(exchangeRate);
297
-
298
- const slippageMultiplier = 1 + parseInt(slippage.toString() || '0', 10) / 1000000;
299
-
300
- const amountWithSlippage = amountSell.times(slippageMultiplier);
301
-
302
- const coinInAmount = toPureValue(amountWithSlippage, coinIn?.decimals);
303
-
304
- return coinInAmount;
305
- }
306
-
307
- export async function buildSwapRoutes(
308
- options: SwapRoutesArgs,
309
- { defiRouterUrl }: { defiRouterUrl?: string },
310
- ): Promise<GetRoutesResult<Coin, Coin>> {
311
- const aggregatorQuoter = new AggregatorQuoter('mainnet');
312
- const routes = await aggregatorQuoter.getRoutes({
313
- tokenIn: options.coinInType,
314
- tokenOut: options.coinOutType,
315
- amountIn: options.amountToSwap.toString(),
316
- commission: options.commission,
317
- excludeSources: options.excludeSources,
318
- });
319
-
320
- if (defiRouterUrl && options.walletAddress) {
321
- await makeDefiRouterRequest(`${defiRouterUrl}/start`, {
322
- walletId: options.walletAddress,
323
- coinTypeIn: options.coinInType,
324
- coinTypeOut: options.coinOutType,
325
- amountIn: options.amountToSwap.toString(),
326
- });
327
- }
328
-
329
- return routes;
330
- }
331
-
332
- function calculateSwapAmounts(
333
- affectedTypesCount: number,
334
- inAmount: bigint,
335
- ): { feeAndReward: bigint; amountToSwap: bigint } {
336
- // 4% (or 8%) fee and reward budget or 1% fee if neither nft strategy, nor liquid nft involved
337
- const feeAndReward =
338
- affectedTypesCount > 0 ? (BigInt(affectedTypesCount) * inAmount) / 25n : inAmount / 100n;
339
- const amountToSwap = inAmount - feeAndReward; // amount after deducting fee and reward budget
340
-
341
- if (amountToSwap <= 0n) {
342
- throw new Error('Amount to swap is too small');
343
- }
344
-
345
- return { feeAndReward, amountToSwap };
346
- }
347
-
348
- async function getAffectedNftAndFtTypes(
349
- coinInType: string,
350
- coinOutType: string,
351
- ): Promise<Array<{ nftType: string; ftType: string; type: 'liquidNft' | 'nftStrategy' }>> {
352
- const normalizedSuiType = normalizeStructTag(SUI_TYPE_ARG);
353
- const coinTypes = [coinInType, coinOutType].filter((type) => type !== normalizedSuiType);
354
- const types = await Promise.all(
355
- coinTypes.map(async (type) => {
356
- const chainState = await getCollectionChainStateByFtType(type);
357
- return chainState
358
- ? {
359
- nftType: chainState.nft_type as string,
360
- ftType: type,
361
- type: chainState.ft_type ? 'liquidNft' : 'nftStrategy',
362
- }
363
- : undefined;
364
- }),
365
- );
366
-
367
- return types.filter(Boolean) as Array<{
368
- nftType: string;
369
- ftType: string;
370
- type: 'liquidNft' | 'nftStrategy';
371
- }>;
372
- }
373
-
374
- async function makeDefiRouterRequest(
375
- url: string,
376
- body: Record<string, unknown>,
377
- defaultResponse: unknown = undefined,
378
- ): Promise<unknown> {
379
- try {
380
- const response = await fetch(url, {
381
- method: 'POST',
382
- headers: { 'Content-Type': 'application/json' },
383
- body: JSON.stringify(body),
384
- });
385
-
386
- if (!response.ok) {
387
- console.warn(`DeFi router request failed with status ${response.status}`);
388
- return defaultResponse;
389
- }
390
-
391
- if (response.headers.get('Content-Type')?.includes('application/json')) {
392
- return await response.json();
393
- }
394
-
395
- return defaultResponse;
396
- } catch (error) {
397
- console.warn(`DeFi router request error: ${(error as Error).message}`);
398
- return defaultResponse;
399
- }
400
- }