@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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/constants.js +27 -18
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeQuote.test.js +2 -0
- package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeRate.test.js +4 -0
- package/dist/cjs/swappers/CetusSwapper/CetusSwapper.d.ts +2 -0
- package/dist/cjs/swappers/CetusSwapper/CetusSwapper.js +7 -0
- package/dist/cjs/swappers/CetusSwapper/endpoints.d.ts +2 -0
- package/dist/cjs/swappers/CetusSwapper/endpoints.js +87 -0
- package/dist/cjs/swappers/CetusSwapper/index.d.ts +1 -0
- package/dist/cjs/swappers/CetusSwapper/index.js +17 -0
- package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeQuote.d.ts +3 -0
- package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeQuote.js +122 -0
- package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeRate.d.ts +3 -0
- package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeRate.js +110 -0
- package/dist/cjs/swappers/CetusSwapper/utils/constants.d.ts +3 -0
- package/dist/cjs/swappers/CetusSwapper/utils/constants.js +9 -0
- package/dist/cjs/swappers/CetusSwapper/utils/helpers.d.ts +13 -0
- package/dist/cjs/swappers/CetusSwapper/utils/helpers.js +118 -0
- package/dist/cjs/swappers/NearIntentsSwapper/NearIntentsSwapper.js +2 -0
- package/dist/cjs/swappers/NearIntentsSwapper/endpoints.js +43 -0
- package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +50 -10
- package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +65 -9
- package/dist/cjs/swappers/NearIntentsSwapper/types.d.ts +2 -2
- package/dist/cjs/swappers/NearIntentsSwapper/types.js +4 -0
- package/dist/cjs/swappers/NearIntentsSwapper/utils/helpers/helpers.js +12 -7
- package/dist/cjs/swappers/RelaySwapper/RelaySwapper.js +1 -0
- package/dist/cjs/swappers/RelaySwapper/constant.d.ts +2 -2
- package/dist/cjs/swappers/RelaySwapper/constant.js +27 -36
- package/dist/cjs/swappers/RelaySwapper/endpoints.js +4 -0
- package/dist/cjs/swappers/RelaySwapper/utils/getRelayAssetAddress.js +2 -0
- package/dist/cjs/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.d.ts +1 -1
- package/dist/cjs/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.js +2 -0
- package/dist/cjs/swappers/RelaySwapper/utils/getRelayPsbtRelayer.js +1 -1
- package/dist/cjs/swappers/RelaySwapper/utils/getTrade.js +7 -2
- package/dist/cjs/swappers/RelaySwapper/utils/relayTokenToAssetId.js +15 -0
- package/dist/cjs/thorchain-utils/getL1RateOrQuote.js +12 -0
- package/dist/cjs/tron-utils/getTronTransactionFees.d.ts +2 -0
- package/dist/cjs/tron-utils/getTronTransactionFees.js +14 -0
- package/dist/cjs/tron-utils/getUnsignedTronTransaction.d.ts +2 -0
- package/dist/cjs/tron-utils/getUnsignedTronTransaction.js +28 -0
- package/dist/cjs/types.d.ts +56 -11
- package/dist/cjs/types.js +1 -0
- package/dist/cjs/utils.d.ts +11 -3
- package/dist/cjs/utils.js +34 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/constants.js +9 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/swappers/ButterSwap/swapperApi/getTradeQuote.test.js +2 -0
- package/dist/esm/swappers/ButterSwap/swapperApi/getTradeRate.test.js +4 -0
- package/dist/esm/swappers/CetusSwapper/CetusSwapper.d.ts +2 -0
- package/dist/esm/swappers/CetusSwapper/CetusSwapper.js +4 -0
- package/dist/esm/swappers/CetusSwapper/endpoints.d.ts +2 -0
- package/dist/esm/swappers/CetusSwapper/endpoints.js +84 -0
- package/dist/esm/swappers/CetusSwapper/index.d.ts +1 -0
- package/dist/esm/swappers/CetusSwapper/index.js +1 -0
- package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeQuote.d.ts +3 -0
- package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeQuote.js +118 -0
- package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeRate.d.ts +3 -0
- package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeRate.js +106 -0
- package/dist/esm/swappers/CetusSwapper/utils/constants.d.ts +3 -0
- package/dist/esm/swappers/CetusSwapper/utils/constants.js +5 -0
- package/dist/esm/swappers/CetusSwapper/utils/helpers.d.ts +13 -0
- package/dist/esm/swappers/CetusSwapper/utils/helpers.js +86 -0
- package/dist/esm/swappers/NearIntentsSwapper/NearIntentsSwapper.js +3 -1
- package/dist/esm/swappers/NearIntentsSwapper/endpoints.js +43 -0
- package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +51 -11
- package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +66 -10
- package/dist/esm/swappers/NearIntentsSwapper/types.d.ts +2 -2
- package/dist/esm/swappers/NearIntentsSwapper/types.js +4 -0
- package/dist/esm/swappers/NearIntentsSwapper/utils/helpers/helpers.js +13 -8
- package/dist/esm/swappers/RelaySwapper/RelaySwapper.js +2 -1
- package/dist/esm/swappers/RelaySwapper/constant.d.ts +2 -2
- package/dist/esm/swappers/RelaySwapper/constant.js +6 -15
- package/dist/esm/swappers/RelaySwapper/endpoints.js +4 -0
- package/dist/esm/swappers/RelaySwapper/utils/getRelayAssetAddress.js +4 -2
- package/dist/esm/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.d.ts +1 -1
- package/dist/esm/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.js +4 -2
- package/dist/esm/swappers/RelaySwapper/utils/getRelayPsbtRelayer.js +1 -1
- package/dist/esm/swappers/RelaySwapper/utils/getTrade.js +8 -3
- package/dist/esm/swappers/RelaySwapper/utils/relayTokenToAssetId.js +17 -2
- package/dist/esm/thorchain-utils/getL1RateOrQuote.js +12 -0
- package/dist/esm/tron-utils/getTronTransactionFees.d.ts +2 -0
- package/dist/esm/tron-utils/getTronTransactionFees.js +10 -0
- package/dist/esm/tron-utils/getUnsignedTronTransaction.d.ts +2 -0
- package/dist/esm/tron-utils/getUnsignedTronTransaction.js +24 -0
- package/dist/esm/types.d.ts +56 -11
- package/dist/esm/types.js +1 -0
- package/dist/esm/utils.d.ts +11 -3
- package/dist/esm/utils.js +31 -1
- package/package.json +17 -10
package/dist/esm/constants.js
CHANGED
|
@@ -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
|
}
|
package/dist/esm/index.d.ts
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';
|
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,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,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) {
|