@shapeshiftoss/swapper 20.3.0 → 20.4.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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/constants.js +15 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.js +0 -2
- package/dist/cjs/swappers/AvnuSwapper/swapperApi/getTradeQuote.js +1 -2
- package/dist/cjs/swappers/BebopSwapper/utils/fetchFromBebop.d.ts +2 -1
- package/dist/cjs/swappers/BebopSwapper/utils/fetchFromBebop.js +21 -15
- package/dist/cjs/swappers/BebopSwapper/utils/getBebopSolanaTradeContext.js +1 -0
- package/dist/cjs/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.js +0 -2
- package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeQuote.js +0 -2
- package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeQuote.js +0 -2
- package/dist/cjs/swappers/ChainflipSwapper/swapperApi/getTradeQuote.js +1 -2
- package/dist/cjs/swappers/ChainflipSwapper/types.d.ts +1 -0
- package/dist/cjs/swappers/DebridgeSwapper/getTradeQuote/getTradeQuote.js +0 -2
- package/dist/cjs/swappers/FyndSwapper/FyndSwapper.d.ts +2 -0
- package/dist/cjs/swappers/FyndSwapper/FyndSwapper.js +5 -0
- package/dist/cjs/swappers/FyndSwapper/endpoints.d.ts +2 -0
- package/dist/cjs/swappers/FyndSwapper/endpoints.js +14 -0
- package/dist/cjs/swappers/FyndSwapper/getTradeQuote/getTradeQuote.d.ts +4 -0
- package/dist/cjs/swappers/FyndSwapper/getTradeQuote/getTradeQuote.js +43 -0
- package/dist/cjs/swappers/FyndSwapper/getTradeRate/getTradeRate.d.ts +4 -0
- package/dist/cjs/swappers/FyndSwapper/getTradeRate/getTradeRate.js +34 -0
- package/dist/cjs/swappers/FyndSwapper/index.d.ts +2 -0
- package/dist/cjs/swappers/FyndSwapper/index.js +7 -0
- package/dist/cjs/swappers/FyndSwapper/types.d.ts +41 -0
- package/dist/cjs/swappers/FyndSwapper/types.js +2 -0
- package/dist/cjs/swappers/FyndSwapper/utils/constants.d.ts +33 -0
- package/dist/cjs/swappers/FyndSwapper/utils/constants.js +38 -0
- package/dist/cjs/swappers/FyndSwapper/utils/fetchFromFynd.d.ts +13 -0
- package/dist/cjs/swappers/FyndSwapper/utils/fetchFromFynd.js +42 -0
- package/dist/cjs/swappers/FyndSwapper/utils/fyndService.d.ts +5 -0
- package/dist/cjs/swappers/FyndSwapper/utils/fyndService.js +9 -0
- package/dist/cjs/swappers/FyndSwapper/utils/getFyndStepData.d.ts +25 -0
- package/dist/cjs/swappers/FyndSwapper/utils/getFyndStepData.js +48 -0
- package/dist/cjs/swappers/FyndSwapper/utils/getFyndTradeContext.d.ts +18 -0
- package/dist/cjs/swappers/FyndSwapper/utils/getFyndTradeContext.js +94 -0
- package/dist/cjs/swappers/FyndSwapper/utils/helpers.d.ts +24 -0
- package/dist/cjs/swappers/FyndSwapper/utils/helpers.js +174 -0
- package/dist/cjs/swappers/FyndSwapper/utils/helpers.test.d.ts +1 -0
- package/dist/cjs/swappers/FyndSwapper/utils/helpers.test.js +101 -0
- package/dist/cjs/swappers/MayachainSwapper/generated/generatedTradableAssetMap.json +0 -1
- package/dist/cjs/swappers/PortalsSwapper/endpoints.js +4 -1
- package/dist/cjs/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.js +1 -1
- package/dist/cjs/swappers/RelaySwapper/getTradeQuote/getTradeQuote.js +2 -3
- package/dist/cjs/swappers/RelaySwapper/utils/getRelayTradeContext.d.ts +1 -0
- package/dist/cjs/swappers/RelaySwapper/utils/getRelayTradeContext.js +4 -0
- package/dist/cjs/swappers/RelaySwapper/utils/relayStatus.js +3 -3
- package/dist/cjs/swappers/RelaySwapper/utils/types.d.ts +9 -4
- package/dist/cjs/swappers/SymbiosisSwapper/SymbiosisSwapper.d.ts +2 -0
- package/dist/cjs/swappers/SymbiosisSwapper/SymbiosisSwapper.js +8 -0
- package/dist/cjs/swappers/SymbiosisSwapper/endpoints.d.ts +2 -0
- package/dist/cjs/swappers/SymbiosisSwapper/endpoints.js +66 -0
- package/dist/cjs/swappers/SymbiosisSwapper/getTradeQuote/getTradeQuote.d.ts +4 -0
- package/dist/cjs/swappers/SymbiosisSwapper/getTradeQuote/getTradeQuote.js +42 -0
- package/dist/cjs/swappers/SymbiosisSwapper/getTradeRate/getTradeRate.d.ts +4 -0
- package/dist/cjs/swappers/SymbiosisSwapper/getTradeRate/getTradeRate.js +34 -0
- package/dist/cjs/swappers/SymbiosisSwapper/index.d.ts +2 -0
- package/dist/cjs/swappers/SymbiosisSwapper/index.js +7 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/constants.d.ts +19 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/constants.js +64 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/getSymbiosisStepData.d.ts +26 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/getSymbiosisStepData.js +106 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/getSymbiosisStepData.test.d.ts +1 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/getSymbiosisStepData.test.js +161 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/getSymbiosisTradeContext.d.ts +17 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/getSymbiosisTradeContext.js +146 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/helpers.d.ts +38 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/helpers.js +172 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/helpers.test.d.ts +1 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/helpers.test.js +151 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/symbiosisService.d.ts +5 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/symbiosisService.js +18 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/types.d.ts +83 -0
- package/dist/cjs/swappers/SymbiosisSwapper/utils/types.js +11 -0
- package/dist/cjs/swappers/ThorchainSwapper/generated/generatedThorLongtailTokens.json +1 -0
- package/dist/cjs/swappers/ThorchainSwapper/generated/generatedTradableAssetMap.json +1 -0
- package/dist/cjs/swappers/ThorchainSwapper/utils/getLongtailQuote.js +1 -1
- package/dist/cjs/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.js +24 -1
- package/dist/cjs/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.test.d.ts +1 -0
- package/dist/cjs/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.test.js +110 -0
- package/dist/cjs/swappers/ZrxSwapper/types.d.ts +2 -1
- package/dist/cjs/swappers/ZrxSwapper/utils/getZrxNativeCheckDeadline.d.ts +1 -0
- package/dist/cjs/swappers/ZrxSwapper/utils/getZrxNativeCheckDeadline.js +26 -0
- package/dist/cjs/types.d.ts +6 -5
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/utils/helpers.d.ts +1 -2
- package/dist/cjs/utils/helpers.js +8 -32
- package/dist/cjs/utils/helpers.test.js +6 -2
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/constants.js +15 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.js +0 -2
- package/dist/esm/swappers/AvnuSwapper/swapperApi/getTradeQuote.js +1 -2
- package/dist/esm/swappers/BebopSwapper/utils/fetchFromBebop.d.ts +2 -1
- package/dist/esm/swappers/BebopSwapper/utils/fetchFromBebop.js +21 -15
- package/dist/esm/swappers/BebopSwapper/utils/getBebopSolanaTradeContext.js +1 -0
- package/dist/esm/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.js +0 -2
- package/dist/esm/swappers/ButterSwap/swapperApi/getTradeQuote.js +0 -2
- package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeQuote.js +0 -2
- package/dist/esm/swappers/ChainflipSwapper/swapperApi/getTradeQuote.js +1 -2
- package/dist/esm/swappers/ChainflipSwapper/types.d.ts +1 -0
- package/dist/esm/swappers/DebridgeSwapper/getTradeQuote/getTradeQuote.js +0 -2
- package/dist/esm/swappers/FyndSwapper/FyndSwapper.d.ts +2 -0
- package/dist/esm/swappers/FyndSwapper/FyndSwapper.js +2 -0
- package/dist/esm/swappers/FyndSwapper/endpoints.d.ts +2 -0
- package/dist/esm/swappers/FyndSwapper/endpoints.js +11 -0
- package/dist/esm/swappers/FyndSwapper/getTradeQuote/getTradeQuote.d.ts +4 -0
- package/dist/esm/swappers/FyndSwapper/getTradeQuote/getTradeQuote.js +39 -0
- package/dist/esm/swappers/FyndSwapper/getTradeRate/getTradeRate.d.ts +4 -0
- package/dist/esm/swappers/FyndSwapper/getTradeRate/getTradeRate.js +30 -0
- package/dist/esm/swappers/FyndSwapper/index.d.ts +2 -0
- package/dist/esm/swappers/FyndSwapper/index.js +2 -0
- package/dist/esm/swappers/FyndSwapper/types.d.ts +41 -0
- package/dist/esm/swappers/FyndSwapper/types.js +1 -0
- package/dist/esm/swappers/FyndSwapper/utils/constants.d.ts +33 -0
- package/dist/esm/swappers/FyndSwapper/utils/constants.js +35 -0
- package/dist/esm/swappers/FyndSwapper/utils/fetchFromFynd.d.ts +13 -0
- package/dist/esm/swappers/FyndSwapper/utils/fetchFromFynd.js +38 -0
- package/dist/esm/swappers/FyndSwapper/utils/fyndService.d.ts +5 -0
- package/dist/esm/swappers/FyndSwapper/utils/fyndService.js +3 -0
- package/dist/esm/swappers/FyndSwapper/utils/getFyndStepData.d.ts +25 -0
- package/dist/esm/swappers/FyndSwapper/utils/getFyndStepData.js +44 -0
- package/dist/esm/swappers/FyndSwapper/utils/getFyndTradeContext.d.ts +18 -0
- package/dist/esm/swappers/FyndSwapper/utils/getFyndTradeContext.js +90 -0
- package/dist/esm/swappers/FyndSwapper/utils/helpers.d.ts +24 -0
- package/dist/esm/swappers/FyndSwapper/utils/helpers.js +162 -0
- package/dist/esm/swappers/FyndSwapper/utils/helpers.test.d.ts +1 -0
- package/dist/esm/swappers/FyndSwapper/utils/helpers.test.js +99 -0
- package/dist/esm/swappers/MayachainSwapper/generated/generatedTradableAssetMap.json +0 -1
- package/dist/esm/swappers/PortalsSwapper/endpoints.js +4 -1
- package/dist/esm/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.js +2 -2
- package/dist/esm/swappers/RelaySwapper/getTradeQuote/getTradeQuote.js +2 -3
- package/dist/esm/swappers/RelaySwapper/utils/getRelayTradeContext.d.ts +1 -0
- package/dist/esm/swappers/RelaySwapper/utils/getRelayTradeContext.js +5 -1
- package/dist/esm/swappers/RelaySwapper/utils/relayStatus.js +3 -3
- package/dist/esm/swappers/RelaySwapper/utils/types.d.ts +9 -4
- package/dist/esm/swappers/SymbiosisSwapper/SymbiosisSwapper.d.ts +2 -0
- package/dist/esm/swappers/SymbiosisSwapper/SymbiosisSwapper.js +5 -0
- package/dist/esm/swappers/SymbiosisSwapper/endpoints.d.ts +2 -0
- package/dist/esm/swappers/SymbiosisSwapper/endpoints.js +63 -0
- package/dist/esm/swappers/SymbiosisSwapper/getTradeQuote/getTradeQuote.d.ts +4 -0
- package/dist/esm/swappers/SymbiosisSwapper/getTradeQuote/getTradeQuote.js +38 -0
- package/dist/esm/swappers/SymbiosisSwapper/getTradeRate/getTradeRate.d.ts +4 -0
- package/dist/esm/swappers/SymbiosisSwapper/getTradeRate/getTradeRate.js +30 -0
- package/dist/esm/swappers/SymbiosisSwapper/index.d.ts +2 -0
- package/dist/esm/swappers/SymbiosisSwapper/index.js +2 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/constants.d.ts +19 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/constants.js +61 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/getSymbiosisStepData.d.ts +26 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/getSymbiosisStepData.js +102 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/getSymbiosisStepData.test.d.ts +1 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/getSymbiosisStepData.test.js +159 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/getSymbiosisTradeContext.d.ts +17 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/getSymbiosisTradeContext.js +139 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/helpers.d.ts +38 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/helpers.js +156 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/helpers.test.d.ts +1 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/helpers.test.js +149 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/symbiosisService.d.ts +5 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/symbiosisService.js +12 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/types.d.ts +83 -0
- package/dist/esm/swappers/SymbiosisSwapper/utils/types.js +8 -0
- package/dist/esm/swappers/ThorchainSwapper/generated/generatedThorLongtailTokens.json +1 -0
- package/dist/esm/swappers/ThorchainSwapper/generated/generatedTradableAssetMap.json +1 -0
- package/dist/esm/swappers/ThorchainSwapper/utils/getLongtailQuote.js +1 -1
- package/dist/esm/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.js +25 -2
- package/dist/esm/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.test.d.ts +1 -0
- package/dist/esm/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.test.js +108 -0
- package/dist/esm/swappers/ZrxSwapper/types.d.ts +2 -1
- package/dist/esm/swappers/ZrxSwapper/utils/getZrxNativeCheckDeadline.d.ts +1 -0
- package/dist/esm/swappers/ZrxSwapper/utils/getZrxNativeCheckDeadline.js +22 -0
- package/dist/esm/types.d.ts +6 -5
- package/dist/esm/types.js +2 -0
- package/dist/esm/utils/helpers.d.ts +1 -2
- package/dist/esm/utils/helpers.js +9 -33
- package/dist/esm/utils/helpers.test.js +6 -2
- package/package.json +7 -7
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { bn, isToken } from '@shapeshiftoss/utils';
|
|
2
|
+
import { Err, Ok } from '@sniptt/monads';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import { v4 as uuid } from 'uuid';
|
|
5
|
+
import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constants.js';
|
|
6
|
+
import { SwapperName, TradeQuoteError } from '../../../types.js';
|
|
7
|
+
import { getInputOutputRate, makeSwapErrorRight } from '../../../utils/index.js';
|
|
8
|
+
import { buildAffiliateFee } from '../../../utils/affiliateFee.js';
|
|
9
|
+
import { SYMBIOSIS_DISABLED_PROVIDERS, SYMBIOSIS_PARTNER_FEE_BPS, SYMBIOSIS_PARTNER_FEE_DESCRIPTION, } from './constants.js';
|
|
10
|
+
import { assertValidTrade, getSymbiosisPartnerAddress, getSymbiosisProtocolFees, getSymbiosisSlippageBps, getSymbiosisTokenAddress, getSymbiosisTronFallbackEnergy, isSymbiosisRouteSupported, symbiosisErrorToTradeQuoteError, } from './helpers.js';
|
|
11
|
+
import { symbiosisService } from './symbiosisService.js';
|
|
12
|
+
export const getSymbiosisTradeContext = async ({ input, deps, from, to, }) => {
|
|
13
|
+
const { sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit, affiliateBps } = input;
|
|
14
|
+
const assertion = assertValidTrade({ sellAsset, buyAsset });
|
|
15
|
+
if (assertion.isErr())
|
|
16
|
+
return Err(assertion.unwrapErr());
|
|
17
|
+
const { sellSymbiosisChainId, buySymbiosisChainId } = assertion.unwrap();
|
|
18
|
+
const slippageBps = getSymbiosisSlippageBps(input.slippageTolerancePercentageDecimal ??
|
|
19
|
+
getDefaultSlippageDecimalPercentageForSwapper(SwapperName.Symbiosis));
|
|
20
|
+
const maybePartnerAddress = getSymbiosisPartnerAddress(affiliateBps);
|
|
21
|
+
if (maybePartnerAddress.isErr())
|
|
22
|
+
return Err(maybePartnerAddress.unwrapErr());
|
|
23
|
+
const partnerAddress = maybePartnerAddress.unwrap();
|
|
24
|
+
const request = {
|
|
25
|
+
tokenAmountIn: {
|
|
26
|
+
chainId: sellSymbiosisChainId,
|
|
27
|
+
address: getSymbiosisTokenAddress(sellAsset),
|
|
28
|
+
decimals: sellAsset.precision,
|
|
29
|
+
amount: sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
30
|
+
},
|
|
31
|
+
tokenOut: {
|
|
32
|
+
chainId: buySymbiosisChainId,
|
|
33
|
+
address: getSymbiosisTokenAddress(buyAsset),
|
|
34
|
+
decimals: buyAsset.precision,
|
|
35
|
+
},
|
|
36
|
+
from,
|
|
37
|
+
to,
|
|
38
|
+
slippage: slippageBps,
|
|
39
|
+
disabledProviders: SYMBIOSIS_DISABLED_PROVIDERS,
|
|
40
|
+
partnerAddress,
|
|
41
|
+
};
|
|
42
|
+
const maybeQuote = await symbiosisService.post(`${deps.config.VITE_SYMBIOSIS_API_URL}/v2/quote`, request);
|
|
43
|
+
if (maybeQuote.isErr()) {
|
|
44
|
+
const { cause } = maybeQuote.unwrapErr();
|
|
45
|
+
if (!axios.isAxiosError(cause)) {
|
|
46
|
+
return Err(makeSwapErrorRight({
|
|
47
|
+
message: 'Unknown error',
|
|
48
|
+
code: TradeQuoteError.UnknownError,
|
|
49
|
+
cause,
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
if (cause.response?.status === 429) {
|
|
53
|
+
return Err(makeSwapErrorRight({
|
|
54
|
+
message: 'Symbiosis rate limit exceeded',
|
|
55
|
+
code: TradeQuoteError.RateLimitExceeded,
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
const message = cause.response?.data?.message;
|
|
59
|
+
return Err(makeSwapErrorRight({
|
|
60
|
+
message: message ?? 'Symbiosis quote request failed',
|
|
61
|
+
code: symbiosisErrorToTradeQuoteError(message),
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
const { data: quote } = maybeQuote.unwrap();
|
|
65
|
+
if (!isSymbiosisRouteSupported({ quote, sellAsset })) {
|
|
66
|
+
return Err(makeSwapErrorRight({
|
|
67
|
+
message: 'No Symbiosis route through its own liquidity',
|
|
68
|
+
code: TradeQuoteError.NoRouteFound,
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
// The quote must carry the fee we report, otherwise Symbiosis is not collecting it on this route
|
|
72
|
+
if (partnerAddress &&
|
|
73
|
+
!quote.fees.some(fee => fee.description === SYMBIOSIS_PARTNER_FEE_DESCRIPTION)) {
|
|
74
|
+
return Err(makeSwapErrorRight({
|
|
75
|
+
message: 'Symbiosis quote is missing the partner fee',
|
|
76
|
+
code: TradeQuoteError.InvalidResponse,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
const buyAmountAfterFeesCryptoBaseUnit = quote.tokenAmountOut.amount;
|
|
80
|
+
const protocolFees = getSymbiosisProtocolFees(quote.fees);
|
|
81
|
+
const buyAmountBeforeFeesCryptoBaseUnit = bn(buyAmountAfterFeesCryptoBaseUnit)
|
|
82
|
+
.plus(protocolFees[buyAsset.assetId]?.amountCryptoBaseUnit ?? 0)
|
|
83
|
+
.toFixed(0);
|
|
84
|
+
const rate = getInputOutputRate({
|
|
85
|
+
sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
86
|
+
buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit,
|
|
87
|
+
sellAsset,
|
|
88
|
+
buyAsset,
|
|
89
|
+
});
|
|
90
|
+
const allowanceContract = isToken(sellAsset.assetId) ? quote.approveTo : '';
|
|
91
|
+
if (allowanceContract === undefined) {
|
|
92
|
+
return Err(makeSwapErrorRight({
|
|
93
|
+
message: 'Symbiosis returned no spender for a token sell',
|
|
94
|
+
code: TradeQuoteError.QueryFailed,
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
return Ok({
|
|
98
|
+
tradeCommon: {
|
|
99
|
+
id: uuid(),
|
|
100
|
+
rate,
|
|
101
|
+
swapperName: SwapperName.Symbiosis,
|
|
102
|
+
affiliateBps: partnerAddress ? SYMBIOSIS_PARTNER_FEE_BPS : '0',
|
|
103
|
+
slippageTolerancePercentageDecimal: bn(slippageBps).div(10000).toString(),
|
|
104
|
+
},
|
|
105
|
+
stepCommon: {
|
|
106
|
+
allowanceContract,
|
|
107
|
+
rate,
|
|
108
|
+
buyAmountBeforeFeesCryptoBaseUnit,
|
|
109
|
+
buyAmountAfterFeesCryptoBaseUnit,
|
|
110
|
+
sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
111
|
+
buyAsset,
|
|
112
|
+
sellAsset,
|
|
113
|
+
source: SwapperName.Symbiosis,
|
|
114
|
+
estimatedExecutionTimeMs: quote.estimatedTime * 1000,
|
|
115
|
+
// The fee accrues in sTokens on the Symbiosis chain - the buy asset figure is a display estimate
|
|
116
|
+
affiliateFee: partnerAddress
|
|
117
|
+
? buildAffiliateFee({
|
|
118
|
+
strategy: 'buy_asset',
|
|
119
|
+
affiliateBps: SYMBIOSIS_PARTNER_FEE_BPS,
|
|
120
|
+
sellAsset,
|
|
121
|
+
buyAsset,
|
|
122
|
+
sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
123
|
+
buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit,
|
|
124
|
+
isEstimate: true,
|
|
125
|
+
})
|
|
126
|
+
: undefined,
|
|
127
|
+
},
|
|
128
|
+
protocolFees,
|
|
129
|
+
stepDataArgs: {
|
|
130
|
+
swapTx: quote,
|
|
131
|
+
sellAsset,
|
|
132
|
+
sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
133
|
+
spenderAddress: allowanceContract,
|
|
134
|
+
tronFallbackEnergy: getSymbiosisTronFallbackEnergy(quote.labels),
|
|
135
|
+
from,
|
|
136
|
+
deps,
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AssetId, ChainId } from '@shapeshiftoss/caip';
|
|
2
|
+
import type { Asset } from '@shapeshiftoss/types';
|
|
3
|
+
import type { Result } from '@sniptt/monads';
|
|
4
|
+
import type { QuoteFeeData, SwapErrorRight, TradeStatus } from '../../../types.js';
|
|
5
|
+
import { TradeQuoteError } from '../../../types.js';
|
|
6
|
+
import type { SymbiosisFee, SymbiosisQuoteResponse, SymbiosisToken, SymbiosisTxResponse } from './types.js';
|
|
7
|
+
export declare const assertValidTrade: ({ sellAsset, buyAsset, }: {
|
|
8
|
+
sellAsset: Asset;
|
|
9
|
+
buyAsset: Asset;
|
|
10
|
+
}) => Result<{
|
|
11
|
+
sellSymbiosisChainId: number;
|
|
12
|
+
buySymbiosisChainId: number;
|
|
13
|
+
}, SwapErrorRight>;
|
|
14
|
+
export declare const getSymbiosisTokenAddress: (asset: Asset) => string;
|
|
15
|
+
export declare const getSymbiosisSlippageBps: (slippageTolerancePercentageDecimal: string) => number;
|
|
16
|
+
export declare const getDefaultUserAddress: (chainId: ChainId) => string;
|
|
17
|
+
export declare const getSymbiosisPartnerAddress: (affiliateBps: string) => Result<string | undefined, SwapErrorRight>;
|
|
18
|
+
export declare const buildSymbiosisTronCallData: ({ functionSelector, data, }: {
|
|
19
|
+
functionSelector: string;
|
|
20
|
+
data: string;
|
|
21
|
+
}) => string;
|
|
22
|
+
export declare const getSymbiosisTronFallbackEnergy: (labels: string[]) => string;
|
|
23
|
+
export declare const isSymbiosisRouteSupported: ({ quote, sellAsset, }: {
|
|
24
|
+
quote: SymbiosisQuoteResponse;
|
|
25
|
+
sellAsset: Asset;
|
|
26
|
+
}) => boolean;
|
|
27
|
+
export declare const getSymbiosisFeeAssetId: ({ chainId: symbiosisChainId, address, }: Pick<SymbiosisToken, 'chainId' | 'address'>) => AssetId | undefined;
|
|
28
|
+
export declare const getSymbiosisProtocolFees: (fees: SymbiosisFee[]) => NonNullable<QuoteFeeData['protocolFees']>;
|
|
29
|
+
export declare const symbiosisErrorToTradeQuoteError: (message: string | undefined) => TradeQuoteError;
|
|
30
|
+
export declare const isTronSourceTxFailed: (tx: {
|
|
31
|
+
ret?: {
|
|
32
|
+
contractRet?: string;
|
|
33
|
+
}[];
|
|
34
|
+
}) => boolean;
|
|
35
|
+
export declare const getSymbiosisTradeStatus: ({ response, buySymbiosisChainId, }: {
|
|
36
|
+
response: SymbiosisTxResponse;
|
|
37
|
+
buySymbiosisChainId: number | undefined;
|
|
38
|
+
}) => Pick<TradeStatus, 'status' | 'buyTxHash' | 'message'>;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { ASSET_NAMESPACE, fromAssetId, toAssetId, tronChainId } from '@shapeshiftoss/caip';
|
|
2
|
+
import { tron } from '@shapeshiftoss/chain-adapters';
|
|
3
|
+
import { TxStatus } from '@shapeshiftoss/unchained-client';
|
|
4
|
+
import { bn, bnOrZero, chainIdToFeeAssetId, isToken } from '@shapeshiftoss/utils';
|
|
5
|
+
import { Err, Ok } from '@sniptt/monads';
|
|
6
|
+
import { getAddress, keccak256, toHex } from 'viem';
|
|
7
|
+
import { TradeQuoteError } from '../../../types.js';
|
|
8
|
+
import { makeSwapErrorRight } from '../../../utils/index.js';
|
|
9
|
+
import { chainIdToSymbiosisChainId, DEFAULT_SYMBIOSIS_EVM_USER_ADDRESS, DEFAULT_SYMBIOSIS_TRON_USER_ADDRESS, SYMBIOSIS_CROSSCHAIN_SWAP_KIND, SYMBIOSIS_MAX_SLIPPAGE_BPS, SYMBIOSIS_MIN_SLIPPAGE_BPS, SYMBIOSIS_PARTNER_ADDRESS, SYMBIOSIS_PARTNER_FEE_BPS, SYMBIOSIS_PARTNER_SWAP_LABEL, SYMBIOSIS_SEMI_CENTRALIZED_LABEL, SYMBIOSIS_SRC_CHAIN_SWAP_LABEL, SYMBIOSIS_TRON_BRIDGE_ENERGY, SYMBIOSIS_TRON_SOURCE_SWAP_ENERGY, symbiosisChainIdToChainId, } from './constants.js';
|
|
10
|
+
import { SymbiosisStatusCode } from './types.js';
|
|
11
|
+
export const assertValidTrade = ({ sellAsset, buyAsset, }) => {
|
|
12
|
+
const sellSymbiosisChainId = chainIdToSymbiosisChainId[sellAsset.chainId];
|
|
13
|
+
const buySymbiosisChainId = chainIdToSymbiosisChainId[buyAsset.chainId];
|
|
14
|
+
if (sellSymbiosisChainId === undefined) {
|
|
15
|
+
return Err(makeSwapErrorRight({
|
|
16
|
+
message: `Sell asset chain '${sellAsset.chainId}' not supported by Symbiosis`,
|
|
17
|
+
code: TradeQuoteError.UnsupportedChain,
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
if (buySymbiosisChainId === undefined) {
|
|
21
|
+
return Err(makeSwapErrorRight({
|
|
22
|
+
message: `Buy asset chain '${buyAsset.chainId}' not supported by Symbiosis`,
|
|
23
|
+
code: TradeQuoteError.UnsupportedChain,
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
if (sellAsset.chainId === buyAsset.chainId) {
|
|
27
|
+
return Err(makeSwapErrorRight({
|
|
28
|
+
message: 'Symbiosis same-chain swaps are not supported',
|
|
29
|
+
code: TradeQuoteError.UnsupportedTradePair,
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
return Ok({ sellSymbiosisChainId, buySymbiosisChainId });
|
|
33
|
+
};
|
|
34
|
+
export const getSymbiosisTokenAddress = (asset) => {
|
|
35
|
+
if (!isToken(asset.assetId))
|
|
36
|
+
return '';
|
|
37
|
+
const { assetReference } = fromAssetId(asset.assetId);
|
|
38
|
+
if (asset.chainId === tronChainId)
|
|
39
|
+
return tron.toTronHex(assetReference);
|
|
40
|
+
return getAddress(assetReference);
|
|
41
|
+
};
|
|
42
|
+
export const getSymbiosisSlippageBps = (slippageTolerancePercentageDecimal) => {
|
|
43
|
+
const bps = Math.round(bnOrZero(slippageTolerancePercentageDecimal).times(10000).toNumber());
|
|
44
|
+
return Math.min(Math.max(bps, SYMBIOSIS_MIN_SLIPPAGE_BPS), SYMBIOSIS_MAX_SLIPPAGE_BPS);
|
|
45
|
+
};
|
|
46
|
+
export const getDefaultUserAddress = (chainId) => chainId === tronChainId ? DEFAULT_SYMBIOSIS_TRON_USER_ADDRESS : DEFAULT_SYMBIOSIS_EVM_USER_ADDRESS;
|
|
47
|
+
// Symbiosis fixes the partner rate on its side, so only a fee-free swap or the registered rate can be honored
|
|
48
|
+
export const getSymbiosisPartnerAddress = (affiliateBps) => {
|
|
49
|
+
const requestedBps = bnOrZero(affiliateBps);
|
|
50
|
+
if (requestedBps.isZero())
|
|
51
|
+
return Ok(undefined);
|
|
52
|
+
if (requestedBps.eq(SYMBIOSIS_PARTNER_FEE_BPS))
|
|
53
|
+
return Ok(SYMBIOSIS_PARTNER_ADDRESS);
|
|
54
|
+
return Err(makeSwapErrorRight({
|
|
55
|
+
message: `Symbiosis only supports a ${SYMBIOSIS_PARTNER_FEE_BPS} bps affiliate fee, got ${affiliateBps}`,
|
|
56
|
+
code: TradeQuoteError.UnsupportedTradePair,
|
|
57
|
+
}));
|
|
58
|
+
};
|
|
59
|
+
// Symbiosis returns the function signature and its encoded parameters separately
|
|
60
|
+
export const buildSymbiosisTronCallData = ({ functionSelector, data, }) => {
|
|
61
|
+
const selector = keccak256(toHex(functionSelector)).slice(0, 10);
|
|
62
|
+
const parameters = data.startsWith('0x') ? data.slice(2) : data;
|
|
63
|
+
return `${selector}${parameters}`;
|
|
64
|
+
};
|
|
65
|
+
export const getSymbiosisTronFallbackEnergy = (labels) => labels.includes(SYMBIOSIS_SRC_CHAIN_SWAP_LABEL)
|
|
66
|
+
? SYMBIOSIS_TRON_SOURCE_SWAP_ENERGY
|
|
67
|
+
: SYMBIOSIS_TRON_BRIDGE_ENERGY;
|
|
68
|
+
export const isSymbiosisRouteSupported = ({ quote, sellAsset, }) => {
|
|
69
|
+
if (quote.kind !== SYMBIOSIS_CROSSCHAIN_SWAP_KIND)
|
|
70
|
+
return false;
|
|
71
|
+
if (quote.labels.includes(SYMBIOSIS_PARTNER_SWAP_LABEL))
|
|
72
|
+
return false;
|
|
73
|
+
if (quote.labels.includes(SYMBIOSIS_SEMI_CENTRALIZED_LABEL))
|
|
74
|
+
return false;
|
|
75
|
+
const expectedType = sellAsset.chainId === tronChainId ? 'tron' : 'evm';
|
|
76
|
+
return quote.type === expectedType;
|
|
77
|
+
};
|
|
78
|
+
export const getSymbiosisFeeAssetId = ({ chainId: symbiosisChainId, address, }) => {
|
|
79
|
+
const chainId = symbiosisChainIdToChainId[symbiosisChainId];
|
|
80
|
+
if (!chainId)
|
|
81
|
+
return;
|
|
82
|
+
if (!address)
|
|
83
|
+
return chainIdToFeeAssetId(chainId);
|
|
84
|
+
if (chainId === tronChainId) {
|
|
85
|
+
return toAssetId({
|
|
86
|
+
chainId,
|
|
87
|
+
assetNamespace: ASSET_NAMESPACE.trc20,
|
|
88
|
+
assetReference: tron.toTronBase58(address),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return toAssetId({
|
|
92
|
+
chainId,
|
|
93
|
+
assetNamespace: ASSET_NAMESPACE.erc20,
|
|
94
|
+
assetReference: address.toLowerCase(),
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
// Fees on the Symbiosis host chain are charged in sTokens we have no asset for - they are already net of the buy amount
|
|
98
|
+
export const getSymbiosisProtocolFees = (fees) => fees.reduce((acc, { value }) => {
|
|
99
|
+
const assetId = getSymbiosisFeeAssetId(value);
|
|
100
|
+
const chainId = symbiosisChainIdToChainId[value.chainId];
|
|
101
|
+
if (!assetId || !chainId)
|
|
102
|
+
return acc;
|
|
103
|
+
acc[assetId] = {
|
|
104
|
+
amountCryptoBaseUnit: bnOrZero(acc[assetId]?.amountCryptoBaseUnit)
|
|
105
|
+
.plus(value.amount)
|
|
106
|
+
.toFixed(0),
|
|
107
|
+
asset: { symbol: value.symbol, chainId, precision: value.decimals },
|
|
108
|
+
requiresBalance: false,
|
|
109
|
+
};
|
|
110
|
+
return acc;
|
|
111
|
+
}, {});
|
|
112
|
+
export const symbiosisErrorToTradeQuoteError = (message) => {
|
|
113
|
+
if (message?.startsWith('Amount is too low'))
|
|
114
|
+
return TradeQuoteError.SellAmountBelowTradeFee;
|
|
115
|
+
if (message === 'This swap is not available')
|
|
116
|
+
return TradeQuoteError.NoRouteFound;
|
|
117
|
+
return TradeQuoteError.QueryFailed;
|
|
118
|
+
};
|
|
119
|
+
// A mined Tron call that did not succeed (REVERT, OUT_OF_ENERGY, ...) never reaches Symbiosis, which answers not found for it
|
|
120
|
+
export const isTronSourceTxFailed = (tx) => {
|
|
121
|
+
const contractRet = tx.ret?.[0]?.contractRet;
|
|
122
|
+
return contractRet !== undefined && contractRet !== 'SUCCESS';
|
|
123
|
+
};
|
|
124
|
+
export const getSymbiosisTradeStatus = ({ response, buySymbiosisChainId, }) => {
|
|
125
|
+
const { status, tx, transitTokenSent } = response;
|
|
126
|
+
switch (status.code) {
|
|
127
|
+
case SymbiosisStatusCode.Success: {
|
|
128
|
+
// Until the operation is indexed, Symbiosis answers Success for the plain source transaction
|
|
129
|
+
if (tx?.chainId !== buySymbiosisChainId) {
|
|
130
|
+
return { status: TxStatus.Pending, buyTxHash: undefined, message: undefined };
|
|
131
|
+
}
|
|
132
|
+
const message = transitTokenSent
|
|
133
|
+
? `Received ${bn(transitTokenSent.amount)
|
|
134
|
+
.shiftedBy(-transitTokenSent.decimals)
|
|
135
|
+
.toFixed()} ${transitTokenSent.symbol} instead of the buy asset`
|
|
136
|
+
: undefined;
|
|
137
|
+
return { status: TxStatus.Confirmed, buyTxHash: tx?.hash, message };
|
|
138
|
+
}
|
|
139
|
+
case SymbiosisStatusCode.Pending:
|
|
140
|
+
return { status: TxStatus.Pending, buyTxHash: undefined, message: undefined };
|
|
141
|
+
case SymbiosisStatusCode.Stuck:
|
|
142
|
+
return {
|
|
143
|
+
status: TxStatus.Pending,
|
|
144
|
+
buyTxHash: undefined,
|
|
145
|
+
message: 'Swap is stuck and is being reverted by Symbiosis',
|
|
146
|
+
};
|
|
147
|
+
case SymbiosisStatusCode.Reverted:
|
|
148
|
+
return {
|
|
149
|
+
status: TxStatus.Failed,
|
|
150
|
+
buyTxHash: undefined,
|
|
151
|
+
message: 'Swap was reverted by Symbiosis',
|
|
152
|
+
};
|
|
153
|
+
default:
|
|
154
|
+
return { status: TxStatus.Unknown, buyTxHash: undefined, message: undefined };
|
|
155
|
+
}
|
|
156
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { usdtOnTronAssetId } from '@shapeshiftoss/caip';
|
|
2
|
+
import { TxStatus } from '@shapeshiftoss/unchained-client';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { SYMBIOSIS_PARTNER_ADDRESS } from './constants.js';
|
|
5
|
+
import { buildSymbiosisTronCallData, getSymbiosisFeeAssetId, getSymbiosisPartnerAddress, getSymbiosisProtocolFees, getSymbiosisSlippageBps, getSymbiosisTradeStatus, isTronSourceTxFailed, } from './helpers.js';
|
|
6
|
+
import { SymbiosisStatusCode } from './types.js';
|
|
7
|
+
const BSC_USDC_ADDRESS = '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d';
|
|
8
|
+
const BSC_USDC_ASSET_ID = 'eip155:56/erc20:0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d';
|
|
9
|
+
// The fees array of a live Arbitrum USDC -> BSC quote: one host chain sToken entry, one destination entry
|
|
10
|
+
const liveFees = [
|
|
11
|
+
{
|
|
12
|
+
provider: 'symbiosis',
|
|
13
|
+
description: 'Cross-chain fee',
|
|
14
|
+
value: {
|
|
15
|
+
symbol: 'sUSDC',
|
|
16
|
+
address: '0xc317169a336b484f65B0AB4A794bBe66a7491E83',
|
|
17
|
+
amount: '250000',
|
|
18
|
+
chainId: 13863860,
|
|
19
|
+
decimals: 6,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
provider: 'symbiosis',
|
|
24
|
+
description: 'Cross-chain fee',
|
|
25
|
+
value: {
|
|
26
|
+
symbol: 'USDC',
|
|
27
|
+
address: BSC_USDC_ADDRESS,
|
|
28
|
+
amount: '250000000000000000',
|
|
29
|
+
chainId: 56,
|
|
30
|
+
decimals: 18,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
describe('getSymbiosisSlippageBps', () => {
|
|
35
|
+
it('raises slippage below the Symbiosis minimum to 10 bps', () => {
|
|
36
|
+
expect(getSymbiosisSlippageBps('0.0005')).toBe(10);
|
|
37
|
+
expect(getSymbiosisSlippageBps('0')).toBe(10);
|
|
38
|
+
});
|
|
39
|
+
it('lowers slippage above the Symbiosis maximum to 1000 bps', () => {
|
|
40
|
+
expect(getSymbiosisSlippageBps('0.25')).toBe(1000);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('getSymbiosisPartnerAddress', () => {
|
|
44
|
+
it('sends the partner address at the registered rate', () => {
|
|
45
|
+
expect(getSymbiosisPartnerAddress('60').unwrap()).toBe(SYMBIOSIS_PARTNER_ADDRESS);
|
|
46
|
+
});
|
|
47
|
+
it('omits the partner address for a fee-free swap', () => {
|
|
48
|
+
expect(getSymbiosisPartnerAddress('0').unwrap()).toBeUndefined();
|
|
49
|
+
});
|
|
50
|
+
it('rejects any other rate instead of silently dropping the fee', () => {
|
|
51
|
+
expect(getSymbiosisPartnerAddress('30').isErr()).toBe(true);
|
|
52
|
+
expect(getSymbiosisPartnerAddress('80').isErr()).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('buildSymbiosisTronCallData', () => {
|
|
56
|
+
const functionSelector = 'metaRoute((bytes,bytes,address[],address,address,uint256,bool,address,bytes))';
|
|
57
|
+
it('prefixes the parameters with the 4-byte selector of the signature', () => {
|
|
58
|
+
const data = `0x${'00'.repeat(31)}20`;
|
|
59
|
+
expect(buildSymbiosisTronCallData({ functionSelector, data })).toBe(`0xa11b1198${'00'.repeat(31)}20`);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('getSymbiosisFeeAssetId', () => {
|
|
63
|
+
it('maps a Tron hex address to its base58 trc20 asset id', () => {
|
|
64
|
+
expect(getSymbiosisFeeAssetId({
|
|
65
|
+
chainId: 728126428,
|
|
66
|
+
address: '0xa614f803b6fd780986a42c78ec9c7f77e6ded13c',
|
|
67
|
+
})).toBe(usdtOnTronAssetId);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('getSymbiosisProtocolFees', () => {
|
|
71
|
+
it('keeps resolvable fees and skips host chain sToken fees', () => {
|
|
72
|
+
expect(getSymbiosisProtocolFees(liveFees)).toEqual({
|
|
73
|
+
[BSC_USDC_ASSET_ID]: {
|
|
74
|
+
amountCryptoBaseUnit: '250000000000000000',
|
|
75
|
+
asset: { symbol: 'USDC', chainId: 'eip155:56', precision: 18 },
|
|
76
|
+
requiresBalance: false,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
it('sums fees charged in the same asset', () => {
|
|
81
|
+
const fees = getSymbiosisProtocolFees([liveFees[1], liveFees[1]]);
|
|
82
|
+
expect(fees[BSC_USDC_ASSET_ID]?.amountCryptoBaseUnit).toBe('500000000000000000');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe('getSymbiosisTradeStatus', () => {
|
|
86
|
+
const buySymbiosisChainId = 56;
|
|
87
|
+
const makeResponse = (overrides) => ({
|
|
88
|
+
status: { code: SymbiosisStatusCode.Success, text: 'Success' },
|
|
89
|
+
txIn: { hash: '0xsell', chainId: 42161 },
|
|
90
|
+
tx: { hash: '0xbuy', chainId: 56 },
|
|
91
|
+
...overrides,
|
|
92
|
+
});
|
|
93
|
+
it('confirms with the destination hash once the destination tx is on the buy chain', () => {
|
|
94
|
+
expect(getSymbiosisTradeStatus({ response: makeResponse({}), buySymbiosisChainId })).toEqual({
|
|
95
|
+
status: TxStatus.Confirmed,
|
|
96
|
+
buyTxHash: '0xbuy',
|
|
97
|
+
message: undefined,
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
it('stays pending when Success describes the source transaction itself', () => {
|
|
101
|
+
const response = makeResponse({ tx: { hash: '0xsell', chainId: 42161 } });
|
|
102
|
+
expect(getSymbiosisTradeStatus({ response, buySymbiosisChainId })).toEqual({
|
|
103
|
+
status: TxStatus.Pending,
|
|
104
|
+
buyTxHash: undefined,
|
|
105
|
+
message: undefined,
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
it('names the transit token when the destination swap did not complete', () => {
|
|
109
|
+
const response = makeResponse({
|
|
110
|
+
transitTokenSent: {
|
|
111
|
+
chainId: 56,
|
|
112
|
+
address: BSC_USDC_ADDRESS,
|
|
113
|
+
decimals: 18,
|
|
114
|
+
symbol: 'USDC',
|
|
115
|
+
amount: '250000000000000000',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
expect(getSymbiosisTradeStatus({ response, buySymbiosisChainId })).toEqual({
|
|
119
|
+
status: TxStatus.Confirmed,
|
|
120
|
+
buyTxHash: '0xbuy',
|
|
121
|
+
message: 'Received 0.25 USDC instead of the buy asset',
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
it('maps pending, stuck, reverted and not found', () => {
|
|
125
|
+
const pending = makeResponse({ status: { code: SymbiosisStatusCode.Pending, text: '' } });
|
|
126
|
+
const stuck = makeResponse({ status: { code: SymbiosisStatusCode.Stuck, text: '' } });
|
|
127
|
+
const reverted = makeResponse({ status: { code: SymbiosisStatusCode.Reverted, text: '' } });
|
|
128
|
+
const notFound = makeResponse({ status: { code: SymbiosisStatusCode.NotFound, text: '' } });
|
|
129
|
+
expect(getSymbiosisTradeStatus({ response: pending, buySymbiosisChainId }).status).toBe(TxStatus.Pending);
|
|
130
|
+
expect(getSymbiosisTradeStatus({ response: stuck, buySymbiosisChainId })).toEqual({
|
|
131
|
+
status: TxStatus.Pending,
|
|
132
|
+
buyTxHash: undefined,
|
|
133
|
+
message: 'Swap is stuck and is being reverted by Symbiosis',
|
|
134
|
+
});
|
|
135
|
+
expect(getSymbiosisTradeStatus({ response: reverted, buySymbiosisChainId })).toEqual({
|
|
136
|
+
status: TxStatus.Failed,
|
|
137
|
+
buyTxHash: undefined,
|
|
138
|
+
message: 'Swap was reverted by Symbiosis',
|
|
139
|
+
});
|
|
140
|
+
expect(getSymbiosisTradeStatus({ response: notFound, buySymbiosisChainId }).status).toBe(TxStatus.Unknown);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe('isTronSourceTxFailed', () => {
|
|
144
|
+
it('is true for any contract result other than SUCCESS', () => {
|
|
145
|
+
expect(isTronSourceTxFailed({ ret: [{ contractRet: 'REVERT' }] })).toBe(true);
|
|
146
|
+
expect(isTronSourceTxFailed({ ret: [{ contractRet: 'OUT_OF_ENERGY' }] })).toBe(true);
|
|
147
|
+
expect(isTronSourceTxFailed({ ret: [{ contractRet: 'SUCCESS' }] })).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const symbiosisService: {
|
|
2
|
+
get: <T = any>(url: string, config?: import("axios").AxiosRequestConfig<any> | undefined) => Promise<import("@sniptt/monads").Result<import("axios").AxiosResponse<T, any, {}>, import("../../../index.js").SwapErrorRight>>;
|
|
3
|
+
post: <T_1 = any>(url: string, data: any, config?: import("axios").AxiosRequestConfig<any> | undefined) => Promise<import("@sniptt/monads").Result<import("axios").AxiosResponse<T_1, any, {}>, import("../../../index.js").SwapErrorRight>>;
|
|
4
|
+
delete: <T_2 = any>(url: string, data: any, config?: import("axios").AxiosRequestConfig<any> | undefined) => Promise<import("@sniptt/monads").Result<import("axios").AxiosResponse<T_2, any, {}>, import("../../../index.js").SwapErrorRight>>;
|
|
5
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { makeSwapperAxiosServiceMonadic } from '../../../utils/index.js';
|
|
3
|
+
import { SYMBIOSIS_PARTNER_ID } from './constants.js';
|
|
4
|
+
const symbiosisServiceBase = axios.create({
|
|
5
|
+
timeout: 10000,
|
|
6
|
+
headers: {
|
|
7
|
+
Accept: 'application/json',
|
|
8
|
+
'Content-Type': 'application/json',
|
|
9
|
+
'X-Partner-Id': SYMBIOSIS_PARTNER_ID,
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
export const symbiosisService = makeSwapperAxiosServiceMonadic(symbiosisServiceBase);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { GetEvmTradeQuoteInput, GetEvmTradeRateInput, GetTronTradeQuoteInput, GetTronTradeRateInput } from '../../../types.js';
|
|
2
|
+
export type SymbiosisTradeQuoteInput = GetEvmTradeQuoteInput | GetTronTradeQuoteInput;
|
|
3
|
+
export type SymbiosisTradeRateInput = GetEvmTradeRateInput | GetTronTradeRateInput;
|
|
4
|
+
export type SymbiosisToken = {
|
|
5
|
+
chainId: number;
|
|
6
|
+
address: string;
|
|
7
|
+
decimals: number;
|
|
8
|
+
symbol: string;
|
|
9
|
+
};
|
|
10
|
+
export type SymbiosisTokenAmount = SymbiosisToken & {
|
|
11
|
+
amount: string;
|
|
12
|
+
};
|
|
13
|
+
export type SymbiosisQuoteRequest = {
|
|
14
|
+
tokenAmountIn: Omit<SymbiosisTokenAmount, 'symbol'>;
|
|
15
|
+
tokenOut: Omit<SymbiosisToken, 'symbol'>;
|
|
16
|
+
from: string;
|
|
17
|
+
to: string;
|
|
18
|
+
slippage: number;
|
|
19
|
+
disabledProviders: string;
|
|
20
|
+
partnerAddress?: string;
|
|
21
|
+
};
|
|
22
|
+
export type SymbiosisFee = {
|
|
23
|
+
provider: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
value: SymbiosisTokenAmount;
|
|
26
|
+
};
|
|
27
|
+
export type SymbiosisEvmTx = {
|
|
28
|
+
chainId: number;
|
|
29
|
+
to: string;
|
|
30
|
+
data: string;
|
|
31
|
+
value?: string;
|
|
32
|
+
};
|
|
33
|
+
export type SymbiosisTronTx = {
|
|
34
|
+
chainId: number;
|
|
35
|
+
from: string;
|
|
36
|
+
to: string;
|
|
37
|
+
data: string;
|
|
38
|
+
value?: string;
|
|
39
|
+
feeLimit: number;
|
|
40
|
+
functionSelector?: string;
|
|
41
|
+
};
|
|
42
|
+
export type SymbiosisSwapTx = {
|
|
43
|
+
type: 'evm';
|
|
44
|
+
tx: SymbiosisEvmTx;
|
|
45
|
+
} | {
|
|
46
|
+
type: 'tron';
|
|
47
|
+
tx: SymbiosisTronTx;
|
|
48
|
+
};
|
|
49
|
+
export type SymbiosisQuoteResponse = SymbiosisSwapTx & {
|
|
50
|
+
kind: string;
|
|
51
|
+
labels: string[];
|
|
52
|
+
approveTo?: string;
|
|
53
|
+
tokenAmountOut: SymbiosisTokenAmount;
|
|
54
|
+
tokenAmountOutMin: SymbiosisTokenAmount;
|
|
55
|
+
fees: SymbiosisFee[];
|
|
56
|
+
estimatedTime: number;
|
|
57
|
+
};
|
|
58
|
+
export type SymbiosisErrorResponse = {
|
|
59
|
+
code: number;
|
|
60
|
+
message: string;
|
|
61
|
+
};
|
|
62
|
+
export declare enum SymbiosisStatusCode {
|
|
63
|
+
NotFound = -1,
|
|
64
|
+
Success = 0,
|
|
65
|
+
Pending = 1,
|
|
66
|
+
Stuck = 2,
|
|
67
|
+
Reverted = 3
|
|
68
|
+
}
|
|
69
|
+
export type SymbiosisTxResponse = {
|
|
70
|
+
status: {
|
|
71
|
+
code: SymbiosisStatusCode;
|
|
72
|
+
text: string;
|
|
73
|
+
};
|
|
74
|
+
tx?: {
|
|
75
|
+
hash?: string;
|
|
76
|
+
chainId: number;
|
|
77
|
+
};
|
|
78
|
+
txIn?: {
|
|
79
|
+
hash?: string;
|
|
80
|
+
chainId: number;
|
|
81
|
+
};
|
|
82
|
+
transitTokenSent?: SymbiosisTokenAmount;
|
|
83
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export var SymbiosisStatusCode;
|
|
2
|
+
(function (SymbiosisStatusCode) {
|
|
3
|
+
SymbiosisStatusCode[SymbiosisStatusCode["NotFound"] = -1] = "NotFound";
|
|
4
|
+
SymbiosisStatusCode[SymbiosisStatusCode["Success"] = 0] = "Success";
|
|
5
|
+
SymbiosisStatusCode[SymbiosisStatusCode["Pending"] = 1] = "Pending";
|
|
6
|
+
SymbiosisStatusCode[SymbiosisStatusCode["Stuck"] = 2] = "Stuck";
|
|
7
|
+
SymbiosisStatusCode[SymbiosisStatusCode["Reverted"] = 3] = "Reverted";
|
|
8
|
+
})(SymbiosisStatusCode || (SymbiosisStatusCode = {}));
|
|
@@ -1017,6 +1017,7 @@
|
|
|
1017
1017
|
"eip155:1/erc20:0x2c06ba9e7f0daccbc1f6a33ea67e85bb68fbee3a",
|
|
1018
1018
|
"eip155:1/erc20:0xcbb8f1bda10b9696c57e13bc128fe674769dcec0",
|
|
1019
1019
|
"eip155:1/erc20:0x72dfb2e44f59c5ad2bafe84314e5b99a7cd5075e",
|
|
1020
|
+
"eip155:1/erc20:0xce682c89c63d2850cb2ca898e44d6c7c30d897a6",
|
|
1020
1021
|
"eip155:43114/erc20:0xc891eb4cbdeff6e073e859e987815ed1505c2acd",
|
|
1021
1022
|
"eip155:43114/erc20:0xd24c2ad096400b6fbcd2ad8b24e7acbc21a1da64",
|
|
1022
1023
|
"eip155:43114/erc20:0x47c3118ad183712acd42648e9e522e13690f29a0",
|
|
@@ -41,5 +41,6 @@
|
|
|
41
41
|
"THOR.TCY": "cosmos:thorchain-1/slip44:tcy",
|
|
42
42
|
"TRON.TRX": "tron:0x2b6653dc/slip44:195",
|
|
43
43
|
"TRON.USDT-TR7NHQJEKQXGTCI8Q8ZY4PL8OTSZGJLJ6T": "tron:0x2b6653dc/trc20:tr7nhqjekqxgtci8q8zy4pl8otszgjlj6t",
|
|
44
|
+
"ZEC.ZEC": "bip122:00040fe8ec8471911baa1db1266ea15d/slip44:133",
|
|
44
45
|
"THOR.RUNE": "cosmos:thorchain-1/slip44:931"
|
|
45
46
|
}
|
|
@@ -74,7 +74,7 @@ export const getLongtailToL1Quote = async (input, deps, streamingInterval, swapp
|
|
|
74
74
|
return Ok({
|
|
75
75
|
...quote,
|
|
76
76
|
// The aggregator swapIn deadline is tighter than the daemon quote expiry
|
|
77
|
-
deadline: Math.min(quote.deadline, Number(deadline) * 1000),
|
|
77
|
+
deadline: Math.min(quote.deadline ?? Infinity, Number(deadline) * 1000),
|
|
78
78
|
data: data ?? quote.data,
|
|
79
79
|
// This logic will need to be updated to support multi-hop, if that's ever implemented for THORChain
|
|
80
80
|
steps: quote.steps.map(s => ({
|