@shapeshiftoss/swapper 17.6.6-alpha.1 → 17.6.7

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 (93) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/constants.js +27 -18
  3. package/dist/cjs/index.d.ts +1 -0
  4. package/dist/cjs/index.js +1 -0
  5. package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeQuote.test.js +2 -0
  6. package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeRate.test.js +4 -0
  7. package/dist/cjs/swappers/CetusSwapper/CetusSwapper.d.ts +2 -0
  8. package/dist/cjs/swappers/CetusSwapper/CetusSwapper.js +7 -0
  9. package/dist/cjs/swappers/CetusSwapper/endpoints.d.ts +2 -0
  10. package/dist/cjs/swappers/CetusSwapper/endpoints.js +87 -0
  11. package/dist/cjs/swappers/CetusSwapper/index.d.ts +1 -0
  12. package/dist/cjs/swappers/CetusSwapper/index.js +17 -0
  13. package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeQuote.d.ts +3 -0
  14. package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeQuote.js +122 -0
  15. package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeRate.d.ts +3 -0
  16. package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeRate.js +110 -0
  17. package/dist/cjs/swappers/CetusSwapper/utils/constants.d.ts +3 -0
  18. package/dist/cjs/swappers/CetusSwapper/utils/constants.js +9 -0
  19. package/dist/cjs/swappers/CetusSwapper/utils/helpers.d.ts +13 -0
  20. package/dist/cjs/swappers/CetusSwapper/utils/helpers.js +118 -0
  21. package/dist/cjs/swappers/NearIntentsSwapper/NearIntentsSwapper.js +2 -0
  22. package/dist/cjs/swappers/NearIntentsSwapper/endpoints.js +43 -0
  23. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +50 -10
  24. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +65 -9
  25. package/dist/cjs/swappers/NearIntentsSwapper/types.d.ts +2 -2
  26. package/dist/cjs/swappers/NearIntentsSwapper/types.js +4 -0
  27. package/dist/cjs/swappers/NearIntentsSwapper/utils/helpers/helpers.js +12 -7
  28. package/dist/cjs/swappers/RelaySwapper/RelaySwapper.js +1 -0
  29. package/dist/cjs/swappers/RelaySwapper/constant.d.ts +2 -2
  30. package/dist/cjs/swappers/RelaySwapper/constant.js +27 -36
  31. package/dist/cjs/swappers/RelaySwapper/endpoints.js +4 -0
  32. package/dist/cjs/swappers/RelaySwapper/utils/getRelayAssetAddress.js +2 -0
  33. package/dist/cjs/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.d.ts +1 -1
  34. package/dist/cjs/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.js +2 -0
  35. package/dist/cjs/swappers/RelaySwapper/utils/getRelayPsbtRelayer.js +1 -1
  36. package/dist/cjs/swappers/RelaySwapper/utils/getTrade.js +7 -2
  37. package/dist/cjs/swappers/RelaySwapper/utils/relayTokenToAssetId.js +15 -0
  38. package/dist/cjs/thorchain-utils/getL1RateOrQuote.js +12 -0
  39. package/dist/cjs/tron-utils/getTronTransactionFees.d.ts +2 -0
  40. package/dist/cjs/tron-utils/getTronTransactionFees.js +14 -0
  41. package/dist/cjs/tron-utils/getUnsignedTronTransaction.d.ts +2 -0
  42. package/dist/cjs/tron-utils/getUnsignedTronTransaction.js +28 -0
  43. package/dist/cjs/types.d.ts +56 -11
  44. package/dist/cjs/types.js +1 -0
  45. package/dist/cjs/utils.d.ts +11 -3
  46. package/dist/cjs/utils.js +34 -1
  47. package/dist/esm/.tsbuildinfo +1 -1
  48. package/dist/esm/constants.js +9 -0
  49. package/dist/esm/index.d.ts +1 -0
  50. package/dist/esm/index.js +1 -0
  51. package/dist/esm/swappers/ButterSwap/swapperApi/getTradeQuote.test.js +2 -0
  52. package/dist/esm/swappers/ButterSwap/swapperApi/getTradeRate.test.js +4 -0
  53. package/dist/esm/swappers/CetusSwapper/CetusSwapper.d.ts +2 -0
  54. package/dist/esm/swappers/CetusSwapper/CetusSwapper.js +4 -0
  55. package/dist/esm/swappers/CetusSwapper/endpoints.d.ts +2 -0
  56. package/dist/esm/swappers/CetusSwapper/endpoints.js +84 -0
  57. package/dist/esm/swappers/CetusSwapper/index.d.ts +1 -0
  58. package/dist/esm/swappers/CetusSwapper/index.js +1 -0
  59. package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeQuote.d.ts +3 -0
  60. package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeQuote.js +118 -0
  61. package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeRate.d.ts +3 -0
  62. package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeRate.js +106 -0
  63. package/dist/esm/swappers/CetusSwapper/utils/constants.d.ts +3 -0
  64. package/dist/esm/swappers/CetusSwapper/utils/constants.js +5 -0
  65. package/dist/esm/swappers/CetusSwapper/utils/helpers.d.ts +13 -0
  66. package/dist/esm/swappers/CetusSwapper/utils/helpers.js +86 -0
  67. package/dist/esm/swappers/NearIntentsSwapper/NearIntentsSwapper.js +3 -1
  68. package/dist/esm/swappers/NearIntentsSwapper/endpoints.js +43 -0
  69. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +51 -11
  70. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +66 -10
  71. package/dist/esm/swappers/NearIntentsSwapper/types.d.ts +2 -2
  72. package/dist/esm/swappers/NearIntentsSwapper/types.js +4 -0
  73. package/dist/esm/swappers/NearIntentsSwapper/utils/helpers/helpers.js +13 -8
  74. package/dist/esm/swappers/RelaySwapper/RelaySwapper.js +2 -1
  75. package/dist/esm/swappers/RelaySwapper/constant.d.ts +2 -2
  76. package/dist/esm/swappers/RelaySwapper/constant.js +6 -15
  77. package/dist/esm/swappers/RelaySwapper/endpoints.js +4 -0
  78. package/dist/esm/swappers/RelaySwapper/utils/getRelayAssetAddress.js +4 -2
  79. package/dist/esm/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.d.ts +1 -1
  80. package/dist/esm/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.js +4 -2
  81. package/dist/esm/swappers/RelaySwapper/utils/getRelayPsbtRelayer.js +1 -1
  82. package/dist/esm/swappers/RelaySwapper/utils/getTrade.js +8 -3
  83. package/dist/esm/swappers/RelaySwapper/utils/relayTokenToAssetId.js +17 -2
  84. package/dist/esm/thorchain-utils/getL1RateOrQuote.js +12 -0
  85. package/dist/esm/tron-utils/getTronTransactionFees.d.ts +2 -0
  86. package/dist/esm/tron-utils/getTronTransactionFees.js +10 -0
  87. package/dist/esm/tron-utils/getUnsignedTronTransaction.d.ts +2 -0
  88. package/dist/esm/tron-utils/getUnsignedTronTransaction.js +24 -0
  89. package/dist/esm/types.d.ts +56 -11
  90. package/dist/esm/types.js +1 -0
  91. package/dist/esm/utils.d.ts +11 -3
  92. package/dist/esm/utils.js +31 -1
  93. package/package.json +17 -10
@@ -5,6 +5,8 @@ import { bebopSwapper } from './swappers/BebopSwapper/BebopSwapper.js';
5
5
  import { bebopApi } from './swappers/BebopSwapper/endpoints.js';
6
6
  import { butterSwap } from './swappers/ButterSwap/ButterSwap.js';
7
7
  import { butterSwapApi } from './swappers/ButterSwap/endpoints.js';
8
+ import { cetusSwapper } from './swappers/CetusSwapper/CetusSwapper.js';
9
+ import { cetusApi } from './swappers/CetusSwapper/endpoints.js';
8
10
  import { chainflipSwapper } from './swappers/ChainflipSwapper/ChainflipSwapper.js';
9
11
  import { chainflipApi } from './swappers/ChainflipSwapper/endpoints.js';
10
12
  import { cowSwapper } from './swappers/CowSwapper/CowSwapper.js';
@@ -81,6 +83,10 @@ export const swappers = {
81
83
  ...nearIntentsSwapper,
82
84
  ...nearIntentsApi,
83
85
  },
86
+ [SwapperName.Cetus]: {
87
+ ...cetusSwapper,
88
+ ...cetusApi,
89
+ },
84
90
  [SwapperName.Test]: undefined,
85
91
  };
86
92
  // Slippage defaults. Don't export these to ensure the getDefaultSlippageDecimalPercentageForSwapper helper function is used.
@@ -93,6 +99,7 @@ const DEFAULT_MAYA_SLIPPAGE_DECIMAL_PERCENTAGE = '0.01'; // 1%
93
99
  const DEFAULT_ARBITRUM_BRIDGE_SLIPPAGE_DECIMAL_PERCENTAGE = '0'; // no slippage for Arbitrum Bridge, so no slippage tolerance
94
100
  const DEFAULT_CHAINFLIP_SLIPPAGE_DECIMAL_PERCENTAGE = '0.02'; // 2%
95
101
  const DEFAULT_BUTTERSWAP_SLIPPAGE_DECIMAL_PERCENTAGE = '0.015'; // 1.5%
102
+ const DEFAULT_CETUS_SLIPPAGE_DECIMAL_PERCENTAGE = '0.005'; // .5%
96
103
  export const getDefaultSlippageDecimalPercentageForSwapper = (swapperName) => {
97
104
  if (swapperName === undefined)
98
105
  return DEFAULT_SLIPPAGE_DECIMAL_PERCENTAGE;
@@ -121,6 +128,8 @@ export const getDefaultSlippageDecimalPercentageForSwapper = (swapperName) => {
121
128
  return DEFAULT_BUTTERSWAP_SLIPPAGE_DECIMAL_PERCENTAGE;
122
129
  case SwapperName.NearIntents:
123
130
  return DEFAULT_NEAR_INTENTS_SLIPPAGE_DECIMAL_PERCENTAGE;
131
+ case SwapperName.Cetus:
132
+ return DEFAULT_CETUS_SLIPPAGE_DECIMAL_PERCENTAGE;
124
133
  default:
125
134
  return assertUnreachable(swapperName);
126
135
  }
@@ -4,6 +4,7 @@ export * from './safe-utils/index.js';
4
4
  export * from './swapper.js';
5
5
  export * from './swappers/ArbitrumBridgeSwapper/index.js';
6
6
  export * from './swappers/BebopSwapper/index.js';
7
+ export * from './swappers/CetusSwapper/index.js';
7
8
  export * from './swappers/ChainflipSwapper/index.js';
8
9
  export * from './swappers/CowSwapper/index.js';
9
10
  export * from './swappers/JupiterSwapper/index.js';
package/dist/esm/index.js CHANGED
@@ -4,6 +4,7 @@ export * from './safe-utils/index.js';
4
4
  export * from './swapper.js';
5
5
  export * from './swappers/ArbitrumBridgeSwapper/index.js';
6
6
  export * from './swappers/BebopSwapper/index.js';
7
+ export * from './swappers/CetusSwapper/index.js';
7
8
  export * from './swappers/ChainflipSwapper/index.js';
8
9
  export * from './swappers/CowSwapper/index.js';
9
10
  export * from './swappers/JupiterSwapper/index.js';
@@ -30,6 +30,8 @@ describe('getTradeQuote', () => {
30
30
  assertGetUtxoChainAdapter: () => vi.fn(),
31
31
  assertGetCosmosSdkChainAdapter: () => vi.fn(),
32
32
  assertGetSolanaChainAdapter: () => vi.fn(),
33
+ assertGetTronChainAdapter: () => vi.fn(),
34
+ assertGetSuiChainAdapter: () => vi.fn(),
33
35
  config: {
34
36
  VITE_BUTTERSWAP_CLIENT_ID: 'test',
35
37
  },
@@ -21,6 +21,8 @@ describe('getTradeRate', () => {
21
21
  assertGetUtxoChainAdapter: () => vi.fn(),
22
22
  assertGetCosmosSdkChainAdapter: () => vi.fn(),
23
23
  assertGetSolanaChainAdapter: () => vi.fn(),
24
+ assertGetTronChainAdapter: () => vi.fn(),
25
+ assertGetSuiChainAdapter: () => vi.fn(),
24
26
  config: {
25
27
  VITE_BUTTERSWAP_CLIENT_ID: 'test',
26
28
  },
@@ -53,6 +55,8 @@ describe('getTradeRate', () => {
53
55
  assertGetUtxoChainAdapter: () => vi.fn(),
54
56
  assertGetCosmosSdkChainAdapter: () => vi.fn(),
55
57
  assertGetSolanaChainAdapter: () => vi.fn(),
58
+ assertGetTronChainAdapter: () => vi.fn(),
59
+ assertGetSuiChainAdapter: () => vi.fn(),
56
60
  config: {
57
61
  VITE_BUTTERSWAP_CLIENT_ID: 'test',
58
62
  },
@@ -0,0 +1,2 @@
1
+ import type { Swapper } from '../../types.js';
2
+ export declare const cetusSwapper: Swapper;
@@ -0,0 +1,4 @@
1
+ import { executeSuiTransaction } from '../../index.js';
2
+ export const cetusSwapper = {
3
+ executeSuiTransaction,
4
+ };
@@ -0,0 +1,2 @@
1
+ import type { SwapperApi } from '../../types.js';
2
+ export declare const cetusApi: SwapperApi;
@@ -0,0 +1,84 @@
1
+ import { TxStatus } from '@shapeshiftoss/unchained-client';
2
+ import { checkSuiSwapStatus, getExecutableTradeStep, isExecutableTradeQuote } from '../../utils.js';
3
+ import { getTradeQuote } from './swapperApi/getTradeQuote.js';
4
+ import { getTradeRate } from './swapperApi/getTradeRate.js';
5
+ import { calculateAmountLimit, calculateSwapAmounts, determineSwapDirection, findBestPool, getCetusSDK, getCoinType, } from './utils/helpers.js';
6
+ export const cetusApi = {
7
+ getTradeQuote: (input, deps) => {
8
+ return getTradeQuote(input, deps);
9
+ },
10
+ getTradeRate: (input, deps) => {
11
+ return getTradeRate(input, deps);
12
+ },
13
+ getUnsignedSuiTransaction: async ({ stepIndex, tradeQuote, from, assertGetSuiChainAdapter, }) => {
14
+ if (!isExecutableTradeQuote(tradeQuote))
15
+ throw new Error('Unable to execute a trade rate quote');
16
+ const step = getExecutableTradeStep(tradeQuote, stepIndex);
17
+ const { accountNumber, sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit } = step;
18
+ const adapter = assertGetSuiChainAdapter(sellAsset.chainId);
19
+ const sdk = await getCetusSDK();
20
+ sdk.senderAddress = from;
21
+ const sellCoinType = getCoinType(sellAsset);
22
+ const buyCoinType = getCoinType(buyAsset);
23
+ const pool = await findBestPool(sdk, sellCoinType, buyCoinType);
24
+ if (!pool) {
25
+ throw new Error(`No liquidity pool found for ${sellAsset.symbol}/${buyAsset.symbol}`);
26
+ }
27
+ const swapResult = await calculateSwapAmounts(sdk, pool, sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit);
28
+ if (!swapResult) {
29
+ throw new Error(`Failed to calculate swap for ${sellAsset.symbol}/${buyAsset.symbol}`);
30
+ }
31
+ const a2b = determineSwapDirection(pool, sellCoinType, buyCoinType);
32
+ const amountLimit = calculateAmountLimit(swapResult.estimatedAmountOut, tradeQuote.slippageTolerancePercentageDecimal, true);
33
+ const tx = await sdk.Swap.createSwapTransactionPayload({
34
+ pool_id: pool.poolAddress,
35
+ coinTypeA: pool.coinTypeA,
36
+ coinTypeB: pool.coinTypeB,
37
+ a2b,
38
+ by_amount_in: true,
39
+ amount: sellAmountIncludingProtocolFeesCryptoBaseUnit,
40
+ amount_limit: amountLimit,
41
+ });
42
+ const transactionBytes = await tx.build({ client: adapter.getSuiClient() });
43
+ const intentMessage = new Uint8Array(3 + transactionBytes.length);
44
+ intentMessage[0] = 0;
45
+ intentMessage[1] = 0;
46
+ intentMessage[2] = 0;
47
+ intentMessage.set(transactionBytes, 3);
48
+ const bip44Params = adapter.getBip44Params({ accountNumber });
49
+ const addressNList = [
50
+ bip44Params.purpose,
51
+ bip44Params.coinType,
52
+ bip44Params.accountNumber,
53
+ ...(bip44Params.isChange !== undefined ? [bip44Params.isChange ? 1 : 0] : []),
54
+ ...(bip44Params.addressIndex !== undefined ? [bip44Params.addressIndex] : []),
55
+ ];
56
+ return {
57
+ addressNList,
58
+ intentMessageBytes: intentMessage,
59
+ };
60
+ },
61
+ getSuiTransactionFees: ({ tradeQuote, stepIndex }) => {
62
+ if (!isExecutableTradeQuote(tradeQuote))
63
+ throw new Error('Unable to execute a trade rate quote');
64
+ const step = getExecutableTradeStep(tradeQuote, stepIndex);
65
+ if (!step.feeData.networkFeeCryptoBaseUnit) {
66
+ throw new Error('Missing network fee in quote');
67
+ }
68
+ return Promise.resolve(step.feeData.networkFeeCryptoBaseUnit);
69
+ },
70
+ checkTradeStatus: ({ swap, assertGetSuiChainAdapter }) => {
71
+ if (!swap?.sellTxHash) {
72
+ return Promise.resolve({
73
+ status: TxStatus.Unknown,
74
+ buyTxHash: undefined,
75
+ message: undefined,
76
+ });
77
+ }
78
+ return checkSuiSwapStatus({
79
+ txHash: swap.sellTxHash,
80
+ address: swap.receiveAddress,
81
+ assertGetSuiChainAdapter,
82
+ });
83
+ },
84
+ };
@@ -0,0 +1 @@
1
+ export * from './utils/constants.js';
@@ -0,0 +1 @@
1
+ export * from './utils/constants.js';
@@ -0,0 +1,3 @@
1
+ import type { Result } from '@sniptt/monads';
2
+ import type { CommonTradeQuoteInput, SwapErrorRight, SwapperDeps, TradeQuote } from '../../../types.js';
3
+ export declare const getTradeQuote: (input: CommonTradeQuoteInput, deps: SwapperDeps) => Promise<Result<TradeQuote[], SwapErrorRight>>;
@@ -0,0 +1,118 @@
1
+ import { suiAssetId } from '@shapeshiftoss/caip';
2
+ import { Err, Ok } from '@sniptt/monads';
3
+ import { v4 as uuid } from 'uuid';
4
+ import { SwapperName, TradeQuoteError } from '../../../types.js';
5
+ import { getInputOutputRate, makeSwapErrorRight } from '../../../utils.js';
6
+ import { isSupportedChainId } from '../utils/constants.js';
7
+ import { calculateSwapAmounts, findBestPool, getCetusSDK, getCoinType } from '../utils/helpers.js';
8
+ export const getTradeQuote = async (input, deps) => {
9
+ const { sellAsset, buyAsset, receiveAddress, accountNumber, sellAmountIncludingProtocolFeesCryptoBaseUnit: sellAmount, affiliateBps, slippageTolerancePercentageDecimal, } = input;
10
+ const { assetsById } = deps;
11
+ if (accountNumber === undefined) {
12
+ return Err(makeSwapErrorRight({
13
+ message: `accountNumber is required`,
14
+ code: TradeQuoteError.InternalError,
15
+ }));
16
+ }
17
+ if (!isSupportedChainId(sellAsset.chainId)) {
18
+ return Err(makeSwapErrorRight({
19
+ message: `unsupported chainId`,
20
+ code: TradeQuoteError.UnsupportedChain,
21
+ details: { chainId: sellAsset.chainId },
22
+ }));
23
+ }
24
+ if (!isSupportedChainId(buyAsset.chainId)) {
25
+ return Err(makeSwapErrorRight({
26
+ message: `unsupported chainId`,
27
+ code: TradeQuoteError.UnsupportedChain,
28
+ details: { chainId: buyAsset.chainId },
29
+ }));
30
+ }
31
+ const suiAsset = assetsById[suiAssetId];
32
+ if (!suiAsset) {
33
+ return Err(makeSwapErrorRight({
34
+ message: `suiAsset is required`,
35
+ code: TradeQuoteError.InternalError,
36
+ }));
37
+ }
38
+ try {
39
+ const sdk = await getCetusSDK();
40
+ const sellCoinType = getCoinType(sellAsset);
41
+ const buyCoinType = getCoinType(buyAsset);
42
+ const pool = await findBestPool(sdk, sellCoinType, buyCoinType);
43
+ if (!pool) {
44
+ return Err(makeSwapErrorRight({
45
+ message: `No liquidity pool found for ${sellAsset.symbol}/${buyAsset.symbol}`,
46
+ code: TradeQuoteError.NoRouteFound,
47
+ }));
48
+ }
49
+ const swapResult = await calculateSwapAmounts(sdk, pool, sellAsset, buyAsset, sellAmount);
50
+ if (!swapResult) {
51
+ return Err(makeSwapErrorRight({
52
+ message: `Failed to calculate swap for ${sellAsset.symbol}/${buyAsset.symbol}`,
53
+ code: TradeQuoteError.QueryFailed,
54
+ }));
55
+ }
56
+ const buyAmountAfterFeesCryptoBaseUnit = swapResult.estimatedAmountOut;
57
+ const rate = getInputOutputRate({
58
+ sellAmountCryptoBaseUnit: sellAmount,
59
+ buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit,
60
+ sellAsset,
61
+ buyAsset,
62
+ });
63
+ const adapter = deps.assertGetSuiChainAdapter(sellAsset.chainId);
64
+ const dummyAddress = '0x0000000000000000000000000000000000000000000000000000000000000000';
65
+ const addressForFeeEstimate = receiveAddress ?? dummyAddress;
66
+ const getFeeDataInput = {
67
+ to: addressForFeeEstimate,
68
+ value: sellAmount,
69
+ chainSpecific: {
70
+ from: addressForFeeEstimate,
71
+ tokenId: getCoinType(sellAsset),
72
+ },
73
+ };
74
+ const feeData = await adapter.getFeeData(getFeeDataInput);
75
+ const protocolFees = {};
76
+ const quotes = [];
77
+ const tradeQuote = {
78
+ id: uuid(),
79
+ quoteOrRate: 'quote',
80
+ rate,
81
+ affiliateBps,
82
+ receiveAddress,
83
+ slippageTolerancePercentageDecimal,
84
+ swapperName: SwapperName.Cetus,
85
+ steps: [
86
+ {
87
+ accountNumber,
88
+ buyAmountBeforeFeesCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit,
89
+ buyAmountAfterFeesCryptoBaseUnit,
90
+ sellAmountIncludingProtocolFeesCryptoBaseUnit: sellAmount,
91
+ feeData: {
92
+ protocolFees,
93
+ networkFeeCryptoBaseUnit: feeData.fast.txFee,
94
+ chainSpecific: {
95
+ gasBudget: feeData.fast.chainSpecific.gasBudget,
96
+ gasPrice: feeData.fast.chainSpecific.gasPrice,
97
+ },
98
+ },
99
+ rate,
100
+ source: SwapperName.Cetus,
101
+ buyAsset,
102
+ sellAsset,
103
+ allowanceContract: '0x0',
104
+ estimatedExecutionTimeMs: undefined,
105
+ },
106
+ ],
107
+ };
108
+ quotes.push(tradeQuote);
109
+ return Ok(quotes);
110
+ }
111
+ catch (error) {
112
+ console.error(error);
113
+ return Err(makeSwapErrorRight({
114
+ message: error instanceof Error ? error.message : 'Unknown error getting Cetus quote',
115
+ code: TradeQuoteError.QueryFailed,
116
+ }));
117
+ }
118
+ };
@@ -0,0 +1,3 @@
1
+ import type { Result } from '@sniptt/monads';
2
+ import type { GetTradeRateInput, SwapErrorRight, SwapperDeps, TradeRate } from '../../../types.js';
3
+ export declare const getTradeRate: (input: GetTradeRateInput, deps: SwapperDeps) => Promise<Result<TradeRate[], SwapErrorRight>>;
@@ -0,0 +1,106 @@
1
+ import { suiAssetId } from '@shapeshiftoss/caip';
2
+ import { Err, Ok } from '@sniptt/monads';
3
+ import { v4 as uuid } from 'uuid';
4
+ import { SwapperName, TradeQuoteError } from '../../../types.js';
5
+ import { getInputOutputRate, makeSwapErrorRight } from '../../../utils.js';
6
+ import { isSupportedChainId } from '../utils/constants.js';
7
+ import { calculateSwapAmounts, findBestPool, getCetusSDK, getCoinType } from '../utils/helpers.js';
8
+ export const getTradeRate = async (input, deps) => {
9
+ const { sellAsset, buyAsset, receiveAddress, sellAmountIncludingProtocolFeesCryptoBaseUnit: sellAmount, affiliateBps, slippageTolerancePercentageDecimal, } = input;
10
+ const { assetsById } = deps;
11
+ if (!isSupportedChainId(sellAsset.chainId)) {
12
+ return Err(makeSwapErrorRight({
13
+ message: `unsupported chainId`,
14
+ code: TradeQuoteError.UnsupportedChain,
15
+ details: { chainId: sellAsset.chainId },
16
+ }));
17
+ }
18
+ if (!isSupportedChainId(buyAsset.chainId)) {
19
+ return Err(makeSwapErrorRight({
20
+ message: `unsupported chainId`,
21
+ code: TradeQuoteError.UnsupportedChain,
22
+ details: { chainId: buyAsset.chainId },
23
+ }));
24
+ }
25
+ const suiAsset = assetsById[suiAssetId];
26
+ if (!suiAsset) {
27
+ return Err(makeSwapErrorRight({
28
+ message: `suiAsset is required`,
29
+ code: TradeQuoteError.UnknownError,
30
+ }));
31
+ }
32
+ try {
33
+ const sdk = await getCetusSDK();
34
+ const sellCoinType = getCoinType(sellAsset);
35
+ const buyCoinType = getCoinType(buyAsset);
36
+ const pool = await findBestPool(sdk, sellCoinType, buyCoinType);
37
+ if (!pool) {
38
+ return Err(makeSwapErrorRight({
39
+ message: `No liquidity pool found for ${sellAsset.symbol}/${buyAsset.symbol}`,
40
+ code: TradeQuoteError.NoRouteFound,
41
+ }));
42
+ }
43
+ const swapResult = await calculateSwapAmounts(sdk, pool, sellAsset, buyAsset, sellAmount);
44
+ if (!swapResult) {
45
+ return Err(makeSwapErrorRight({
46
+ message: `Failed to calculate swap for ${sellAsset.symbol}/${buyAsset.symbol}`,
47
+ code: TradeQuoteError.QueryFailed,
48
+ }));
49
+ }
50
+ const buyAmountAfterFeesCryptoBaseUnit = swapResult.estimatedAmountOut;
51
+ const rate = getInputOutputRate({
52
+ sellAmountCryptoBaseUnit: sellAmount,
53
+ buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit,
54
+ sellAsset,
55
+ buyAsset,
56
+ });
57
+ const adapter = deps.assertGetSuiChainAdapter(sellAsset.chainId);
58
+ const dummyAddress = '0x0000000000000000000000000000000000000000000000000000000000000000';
59
+ const addressForFeeEstimate = receiveAddress ?? dummyAddress;
60
+ const { fast: feeDataFast } = await adapter.getFeeData({
61
+ to: addressForFeeEstimate,
62
+ value: sellAmount,
63
+ chainSpecific: {
64
+ from: addressForFeeEstimate,
65
+ tokenId: getCoinType(sellAsset),
66
+ },
67
+ });
68
+ const protocolFees = {};
69
+ const rates = [];
70
+ const tradeRate = {
71
+ id: uuid(),
72
+ quoteOrRate: 'rate',
73
+ rate,
74
+ affiliateBps,
75
+ receiveAddress,
76
+ slippageTolerancePercentageDecimal,
77
+ swapperName: SwapperName.Cetus,
78
+ steps: [
79
+ {
80
+ accountNumber: undefined,
81
+ buyAmountBeforeFeesCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit,
82
+ buyAmountAfterFeesCryptoBaseUnit,
83
+ sellAmountIncludingProtocolFeesCryptoBaseUnit: sellAmount,
84
+ feeData: {
85
+ protocolFees,
86
+ networkFeeCryptoBaseUnit: feeDataFast.txFee,
87
+ },
88
+ rate,
89
+ source: SwapperName.Cetus,
90
+ buyAsset,
91
+ sellAsset,
92
+ allowanceContract: '0x0',
93
+ estimatedExecutionTimeMs: undefined,
94
+ },
95
+ ],
96
+ };
97
+ rates.push(tradeRate);
98
+ return Ok(rates);
99
+ }
100
+ catch (error) {
101
+ return Err(makeSwapErrorRight({
102
+ message: error instanceof Error ? error.message : 'Unknown error getting Cetus rate',
103
+ code: TradeQuoteError.QueryFailed,
104
+ }));
105
+ }
106
+ };
@@ -0,0 +1,3 @@
1
+ import type { ChainId } from '@shapeshiftoss/caip';
2
+ export declare const SUPPORTED_CETUS_CHAIN_IDS: readonly ChainId[];
3
+ export declare const isSupportedChainId: (chainId: ChainId) => chainId is ChainId;
@@ -0,0 +1,5 @@
1
+ import { suiChainId } from '@shapeshiftoss/caip';
2
+ export const SUPPORTED_CETUS_CHAIN_IDS = [suiChainId];
3
+ export const isSupportedChainId = (chainId) => {
4
+ return SUPPORTED_CETUS_CHAIN_IDS.includes(chainId);
5
+ };
@@ -0,0 +1,13 @@
1
+ import type { CetusClmmSDK, Pool } from '@cetusprotocol/cetus-sui-clmm-sdk';
2
+ import type { Asset } from '@shapeshiftoss/types';
3
+ export declare const getCetusSDK: () => Promise<CetusClmmSDK>;
4
+ export declare const getCoinType: (asset: Asset) => string;
5
+ export declare const determineSwapDirection: (pool: Pool, sellCoinType: string, buyCoinType: string) => boolean;
6
+ export declare const calculateAmountLimit: (estimatedAmount: string, slippageTolerancePercentageDecimal: string | undefined, isBuyAmount: boolean) => string;
7
+ export declare const findBestPool: (sdk: CetusClmmSDK, sellCoinType: string, buyCoinType: string) => Promise<Pool | undefined>;
8
+ export type CalculateSwapResult = {
9
+ estimatedAmountOut: string;
10
+ estimatedAmountIn: string;
11
+ estimatedFeeAmount: string;
12
+ };
13
+ export declare const calculateSwapAmounts: (sdk: CetusClmmSDK, pool: Pool, sellAsset: Asset, buyAsset: Asset, sellAmount: string) => Promise<CalculateSwapResult | undefined>;
@@ -0,0 +1,86 @@
1
+ import { fromAssetId } from '@shapeshiftoss/caip';
2
+ import { bn, bnOrZero } from '@shapeshiftoss/utils';
3
+ let sdkInstance;
4
+ export const getCetusSDK = async () => {
5
+ if (!sdkInstance) {
6
+ const { initMainnetSDK } = await import('@cetusprotocol/cetus-sui-clmm-sdk');
7
+ sdkInstance = initMainnetSDK();
8
+ }
9
+ return sdkInstance;
10
+ };
11
+ export const getCoinType = (asset) => {
12
+ const { assetReference, assetNamespace } = fromAssetId(asset.assetId);
13
+ // For native SUI token, the assetReference is 'slip44:784' but Cetus expects '0x2::sui::SUI'
14
+ if (assetNamespace === 'slip44') {
15
+ return '0x2::sui::SUI';
16
+ }
17
+ // For other tokens, the assetReference should be the full coin type
18
+ return assetReference;
19
+ };
20
+ export const determineSwapDirection = (pool, sellCoinType, buyCoinType) => {
21
+ if (pool.coinTypeA === sellCoinType && pool.coinTypeB === buyCoinType) {
22
+ return true;
23
+ }
24
+ if (pool.coinTypeA === buyCoinType && pool.coinTypeB === sellCoinType) {
25
+ return false;
26
+ }
27
+ throw new Error('Pool does not match the swap pair');
28
+ };
29
+ export const calculateAmountLimit = (estimatedAmount, slippageTolerancePercentageDecimal, isBuyAmount) => {
30
+ if (!slippageTolerancePercentageDecimal) {
31
+ return estimatedAmount;
32
+ }
33
+ const slippageFactor = bn(1).minus(slippageTolerancePercentageDecimal);
34
+ if (isBuyAmount) {
35
+ return bnOrZero(estimatedAmount).times(slippageFactor).toFixed(0);
36
+ }
37
+ const maxSlippageFactor = bn(1).plus(slippageTolerancePercentageDecimal);
38
+ return bnOrZero(estimatedAmount).times(maxSlippageFactor).toFixed(0);
39
+ };
40
+ export const findBestPool = async (sdk, sellCoinType, buyCoinType) => {
41
+ const pools = await sdk.Pool.getPoolByCoins([sellCoinType, buyCoinType]);
42
+ if (!pools || pools.length === 0) {
43
+ return undefined;
44
+ }
45
+ const poolWithLiquidity = pools.find(pool => {
46
+ const hasLiquidity = pool.liquidity && bnOrZero(pool.liquidity).gt(0);
47
+ return hasLiquidity;
48
+ });
49
+ if (!poolWithLiquidity) {
50
+ console.warn('[Cetus] No pools with liquidity found for', sellCoinType, buyCoinType, {
51
+ totalPools: pools.length,
52
+ pools: pools.map(p => ({ id: p.poolAddress, liquidity: p.liquidity })),
53
+ });
54
+ }
55
+ return poolWithLiquidity;
56
+ };
57
+ export const calculateSwapAmounts = async (sdk, pool, sellAsset, buyAsset, sellAmount) => {
58
+ const sellCoinType = getCoinType(sellAsset);
59
+ const buyCoinType = getCoinType(buyAsset);
60
+ const a2b = determineSwapDirection(pool, sellCoinType, buyCoinType);
61
+ const decimalsA = pool.coinTypeA === sellCoinType ? sellAsset.precision : buyAsset.precision;
62
+ const decimalsB = pool.coinTypeA === sellCoinType ? buyAsset.precision : sellAsset.precision;
63
+ const preSwapParams = {
64
+ pool,
65
+ currentSqrtPrice: pool.current_sqrt_price,
66
+ coinTypeA: pool.coinTypeA,
67
+ coinTypeB: pool.coinTypeB,
68
+ decimalsA,
69
+ decimalsB,
70
+ a2b,
71
+ byAmountIn: true,
72
+ amount: sellAmount,
73
+ };
74
+ const preSwapResult = await sdk.Swap.preswap(preSwapParams);
75
+ if (!preSwapResult) {
76
+ return undefined;
77
+ }
78
+ if (preSwapResult.estimatedAmountOut === '0' || preSwapResult.estimatedAmountOut === '') {
79
+ return undefined;
80
+ }
81
+ return {
82
+ estimatedAmountOut: preSwapResult.estimatedAmountOut,
83
+ estimatedAmountIn: preSwapResult.estimatedAmountIn,
84
+ estimatedFeeAmount: preSwapResult.estimatedFeeAmount,
85
+ };
86
+ };
@@ -1,7 +1,9 @@
1
- import { executeEvmTransaction, executeSolanaTransaction } from '../../utils.js';
1
+ import { executeEvmTransaction, executeSolanaTransaction, executeSuiTransaction, executeTronTransaction, } from '../../utils.js';
2
2
  export const nearIntentsSwapper = {
3
3
  executeEvmTransaction,
4
4
  executeSolanaTransaction,
5
+ executeTronTransaction,
6
+ executeSuiTransaction,
5
7
  executeUtxoTransaction: (txToSign, { signAndBroadcastTransaction }) => {
6
8
  return signAndBroadcastTransaction(txToSign);
7
9
  },
@@ -1,5 +1,7 @@
1
1
  import { evm } from '@shapeshiftoss/chain-adapters';
2
2
  import { contractAddressOrUndefined } from '@shapeshiftoss/utils';
3
+ import { getTronTransactionFees } from '../../tron-utils/getTronTransactionFees.js';
4
+ import { getUnsignedTronTransaction } from '../../tron-utils/getUnsignedTronTransaction.js';
3
5
  import { createDefaultStatusResponse, getExecutableTradeStep, isExecutableTradeQuote, } from '../../utils.js';
4
6
  import { isNativeEvmAsset } from '../utils/helpers/helpers.js';
5
7
  import { getTradeQuote } from './swapperApi/getTradeQuote.js';
@@ -136,6 +138,45 @@ export const nearIntentsApi = {
136
138
  }
137
139
  return Promise.resolve(step.feeData.networkFeeCryptoBaseUnit);
138
140
  },
141
+ getUnsignedTronTransaction,
142
+ getTronTransactionFees,
143
+ getUnsignedSuiTransaction: async ({ stepIndex, tradeQuote, from, assertGetSuiChainAdapter, }) => {
144
+ if (!isExecutableTradeQuote(tradeQuote))
145
+ throw new Error('Unable to execute a trade rate quote');
146
+ const step = getExecutableTradeStep(tradeQuote, stepIndex);
147
+ const { accountNumber, sellAsset, nearIntentsSpecific } = step;
148
+ if (!nearIntentsSpecific)
149
+ throw new Error('nearIntentsSpecific is required');
150
+ const adapter = assertGetSuiChainAdapter(sellAsset.chainId);
151
+ const to = nearIntentsSpecific.depositAddress;
152
+ const value = step.sellAmountIncludingProtocolFeesCryptoBaseUnit;
153
+ const tokenId = contractAddressOrUndefined(sellAsset.assetId);
154
+ const { fast } = await adapter.getFeeData({
155
+ to,
156
+ value,
157
+ chainSpecific: { from, tokenId },
158
+ });
159
+ return adapter.buildSendApiTransaction({
160
+ to,
161
+ from,
162
+ value,
163
+ accountNumber,
164
+ chainSpecific: {
165
+ tokenId,
166
+ gasBudget: fast.chainSpecific.gasBudget,
167
+ gasPrice: fast.chainSpecific.gasPrice,
168
+ },
169
+ });
170
+ },
171
+ getSuiTransactionFees: ({ tradeQuote, stepIndex }) => {
172
+ if (!isExecutableTradeQuote(tradeQuote))
173
+ throw new Error('Unable to execute a trade rate quote');
174
+ const step = getExecutableTradeStep(tradeQuote, stepIndex);
175
+ if (!step.feeData.networkFeeCryptoBaseUnit) {
176
+ throw new Error('Missing network fee in quote');
177
+ }
178
+ return Promise.resolve(step.feeData.networkFeeCryptoBaseUnit);
179
+ },
139
180
  checkTradeStatus: async ({ config, swap }) => {
140
181
  const { nearIntentsSpecific } = swap?.metadata ?? {};
141
182
  if (!nearIntentsSpecific?.depositAddress) {
@@ -149,10 +190,12 @@ export const nearIntentsApi = {
149
190
  const message = getNearIntentsStatusMessage(statusResponse.status);
150
191
  // Extract buyTxHash from destination chain transactions
151
192
  const buyTxHash = statusResponse.swapDetails?.destinationChainTxHashes?.[0]?.hash;
193
+ const actualBuyAmountCryptoBaseUnit = statusResponse.swapDetails?.amountOut;
152
194
  return {
153
195
  status: txStatus,
154
196
  buyTxHash,
155
197
  message,
198
+ actualBuyAmountCryptoBaseUnit,
156
199
  };
157
200
  }
158
201
  catch (error) {