@shapeshiftoss/swapper 17.7.3 → 17.8.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 +35 -26
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.test.js +12 -4
- package/dist/cjs/swappers/BebopSwapper/getBebopSolanaTradeRate/getBebopSolanaTradeRate.js +9 -5
- package/dist/cjs/swappers/BebopSwapper/getBebopTradeQuote/getBebopTradeQuote.js +1 -9
- package/dist/cjs/swappers/BebopSwapper/getBebopTradeRate/getBebopTradeRate.js +9 -13
- package/dist/cjs/swappers/BebopSwapper/types.d.ts +64 -78
- package/dist/cjs/swappers/BebopSwapper/utils/fetchFromBebop.d.ts +0 -16
- package/dist/cjs/swappers/BebopSwapper/utils/fetchFromBebop.js +49 -82
- package/dist/cjs/swappers/BobGatewaySwapper/BobGatewaySwapper.d.ts +2 -0
- package/dist/cjs/swappers/BobGatewaySwapper/BobGatewaySwapper.js +8 -0
- package/dist/cjs/swappers/BobGatewaySwapper/endpoints.d.ts +2 -0
- package/dist/cjs/swappers/BobGatewaySwapper/endpoints.js +143 -0
- package/dist/cjs/swappers/BobGatewaySwapper/index.d.ts +3 -0
- package/dist/cjs/swappers/BobGatewaySwapper/index.js +22 -0
- package/dist/cjs/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.d.ts +3 -0
- package/dist/cjs/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.js +90 -0
- package/dist/cjs/swappers/BobGatewaySwapper/swapperApi/getTradeRate.d.ts +3 -0
- package/dist/cjs/swappers/BobGatewaySwapper/swapperApi/getTradeRate.js +73 -0
- package/dist/cjs/swappers/BobGatewaySwapper/types.d.ts +20 -0
- package/dist/cjs/swappers/BobGatewaySwapper/types.js +2 -0
- package/dist/cjs/swappers/BobGatewaySwapper/utils/constants.d.ts +13 -0
- package/dist/cjs/swappers/BobGatewaySwapper/utils/constants.js +31 -0
- package/dist/cjs/swappers/BobGatewaySwapper/utils/helpers.d.ts +49 -0
- package/dist/cjs/swappers/BobGatewaySwapper/utils/helpers.js +314 -0
- package/dist/cjs/swappers/ChainflipSwapper/ChainflipSwapper.js +1 -0
- package/dist/cjs/swappers/ChainflipSwapper/constants.d.ts +1 -1
- package/dist/cjs/swappers/ChainflipSwapper/constants.js +3 -0
- package/dist/cjs/swappers/ChainflipSwapper/endpoints.js +20 -0
- package/dist/cjs/swappers/ChainflipSwapper/types.d.ts +2 -1
- package/dist/cjs/swappers/ChainflipSwapper/types.js +1 -0
- package/dist/cjs/swappers/ChainflipSwapper/utils/getQuoteOrRate.js +23 -3
- package/dist/cjs/swappers/ChainflipSwapper/utils/helpers.d.ts +2 -2
- package/dist/cjs/swappers/ChainflipSwapper/utils/helpers.js +2 -1
- package/dist/cjs/swappers/CowSwapper/utils/helpers/helpers.test.js +3 -3
- package/dist/cjs/swappers/RelaySwapper/constant.js +1 -1
- package/dist/cjs/swappers/SunioSwapper/endpoints.js +19 -36
- package/dist/cjs/swappers/SunioSwapper/utils/buildSwapContractCall.d.ts +6 -0
- package/dist/cjs/swappers/SunioSwapper/utils/buildSwapContractCall.js +33 -0
- package/dist/cjs/swappers/SunioSwapper/utils/buildSwapRouteParameters.js +6 -6
- package/dist/cjs/swappers/SunioSwapper/utils/constants.d.ts +2 -0
- package/dist/cjs/swappers/SunioSwapper/utils/constants.js +7 -1
- package/dist/cjs/swappers/SunioSwapper/utils/estimateSunioNetworkFee.d.ts +12 -0
- package/dist/cjs/swappers/SunioSwapper/utils/estimateSunioNetworkFee.js +78 -0
- package/dist/cjs/swappers/SunioSwapper/utils/getQuoteOrRate.js +16 -74
- package/dist/cjs/swappers/SunioSwapper/utils/getSunioTransactionFees.d.ts +2 -0
- package/dist/cjs/swappers/SunioSwapper/utils/getSunioTransactionFees.js +36 -0
- package/dist/cjs/swappers/utils/helpers/helpers.js +1 -0
- package/dist/cjs/thorchain-utils/getL1RateOrQuote.js +5 -2
- package/dist/cjs/thorchain-utils/getQuote.js +27 -29
- package/dist/cjs/thorchain-utils/tron/getTronTransactionFees.js +8 -5
- package/dist/cjs/thorchain-utils/types.d.ts +1 -1
- package/dist/cjs/types.d.ts +10 -3
- package/dist/cjs/types.js +5 -0
- 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/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.test.js +12 -4
- package/dist/esm/swappers/BebopSwapper/getBebopSolanaTradeRate/getBebopSolanaTradeRate.js +10 -6
- package/dist/esm/swappers/BebopSwapper/getBebopTradeQuote/getBebopTradeQuote.js +1 -9
- package/dist/esm/swappers/BebopSwapper/getBebopTradeRate/getBebopTradeRate.js +10 -14
- package/dist/esm/swappers/BebopSwapper/types.d.ts +64 -78
- package/dist/esm/swappers/BebopSwapper/utils/fetchFromBebop.d.ts +0 -16
- package/dist/esm/swappers/BebopSwapper/utils/fetchFromBebop.js +45 -76
- package/dist/esm/swappers/BobGatewaySwapper/BobGatewaySwapper.d.ts +2 -0
- package/dist/esm/swappers/BobGatewaySwapper/BobGatewaySwapper.js +5 -0
- package/dist/esm/swappers/BobGatewaySwapper/endpoints.d.ts +2 -0
- package/dist/esm/swappers/BobGatewaySwapper/endpoints.js +140 -0
- package/dist/esm/swappers/BobGatewaySwapper/index.d.ts +3 -0
- package/dist/esm/swappers/BobGatewaySwapper/index.js +3 -0
- package/dist/esm/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.d.ts +3 -0
- package/dist/esm/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.js +86 -0
- package/dist/esm/swappers/BobGatewaySwapper/swapperApi/getTradeRate.d.ts +3 -0
- package/dist/esm/swappers/BobGatewaySwapper/swapperApi/getTradeRate.js +69 -0
- package/dist/esm/swappers/BobGatewaySwapper/types.d.ts +20 -0
- package/dist/esm/swappers/BobGatewaySwapper/utils/constants.d.ts +13 -0
- package/dist/esm/swappers/BobGatewaySwapper/utils/constants.js +27 -0
- package/dist/esm/swappers/BobGatewaySwapper/utils/helpers.d.ts +49 -0
- package/dist/esm/swappers/BobGatewaySwapper/utils/helpers.js +299 -0
- package/dist/esm/swappers/ChainflipSwapper/ChainflipSwapper.js +2 -1
- package/dist/esm/swappers/ChainflipSwapper/constants.d.ts +1 -1
- package/dist/esm/swappers/ChainflipSwapper/constants.js +4 -1
- package/dist/esm/swappers/ChainflipSwapper/endpoints.js +20 -0
- package/dist/esm/swappers/ChainflipSwapper/types.d.ts +2 -1
- package/dist/esm/swappers/ChainflipSwapper/types.js +1 -0
- package/dist/esm/swappers/ChainflipSwapper/utils/getQuoteOrRate.js +24 -4
- package/dist/esm/swappers/ChainflipSwapper/utils/helpers.d.ts +2 -2
- package/dist/esm/swappers/ChainflipSwapper/utils/helpers.js +2 -1
- package/dist/esm/swappers/CowSwapper/utils/helpers/helpers.test.js +3 -3
- package/dist/esm/swappers/RelaySwapper/constant.js +1 -1
- package/dist/esm/swappers/SunioSwapper/endpoints.js +19 -36
- package/dist/esm/swappers/SunioSwapper/utils/buildSwapContractCall.d.ts +6 -0
- package/dist/esm/swappers/SunioSwapper/utils/buildSwapContractCall.js +29 -0
- package/dist/esm/swappers/SunioSwapper/utils/buildSwapRouteParameters.js +6 -6
- package/dist/esm/swappers/SunioSwapper/utils/constants.d.ts +2 -0
- package/dist/esm/swappers/SunioSwapper/utils/constants.js +6 -0
- package/dist/esm/swappers/SunioSwapper/utils/estimateSunioNetworkFee.d.ts +12 -0
- package/dist/esm/swappers/SunioSwapper/utils/estimateSunioNetworkFee.js +74 -0
- package/dist/esm/swappers/SunioSwapper/utils/getQuoteOrRate.js +16 -74
- package/dist/esm/swappers/SunioSwapper/utils/getSunioTransactionFees.d.ts +2 -0
- package/dist/esm/swappers/SunioSwapper/utils/getSunioTransactionFees.js +32 -0
- package/dist/esm/swappers/utils/helpers/helpers.js +2 -1
- package/dist/esm/thorchain-utils/getL1RateOrQuote.js +5 -2
- package/dist/esm/thorchain-utils/getQuote.js +27 -29
- package/dist/esm/thorchain-utils/tron/getTronTransactionFees.js +8 -5
- package/dist/esm/thorchain-utils/types.d.ts +1 -1
- package/dist/esm/types.d.ts +10 -3
- package/dist/esm/types.js +5 -0
- package/package.json +8 -7
- package/dist/cjs/swappers/SunioSwapper/utils/buildSwapTransaction.d.ts +0 -11
- package/dist/cjs/swappers/SunioSwapper/utils/buildSwapTransaction.js +0 -57
- package/dist/cjs/thorchain-utils/getThresholdedAffiliateBps/getThresholdedAffiliateBps.d.ts +0 -12
- package/dist/cjs/thorchain-utils/getThresholdedAffiliateBps/getThresholdedAffiliateBps.js +0 -47
- package/dist/cjs/thorchain-utils/getThresholdedAffiliateBps/getThresholdedAffiliateBps.test.js +0 -100
- package/dist/esm/swappers/SunioSwapper/utils/buildSwapTransaction.d.ts +0 -11
- package/dist/esm/swappers/SunioSwapper/utils/buildSwapTransaction.js +0 -53
- package/dist/esm/thorchain-utils/getThresholdedAffiliateBps/getThresholdedAffiliateBps.d.ts +0 -12
- package/dist/esm/thorchain-utils/getThresholdedAffiliateBps/getThresholdedAffiliateBps.js +0 -41
- package/dist/esm/thorchain-utils/getThresholdedAffiliateBps/getThresholdedAffiliateBps.test.d.ts +0 -1
- package/dist/esm/thorchain-utils/getThresholdedAffiliateBps/getThresholdedAffiliateBps.test.js +0 -98
- /package/dist/{cjs/thorchain-utils/getThresholdedAffiliateBps/getThresholdedAffiliateBps.test.d.ts → esm/swappers/BobGatewaySwapper/types.js} +0 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { GatewayErrorCode, GatewaySDK, isGatewayError } from '@gobob/bob-sdk';
|
|
2
|
+
import { ASSET_NAMESPACE, ethChainId, fromAssetId, toAssetId } from '@shapeshiftoss/caip';
|
|
3
|
+
import { evm, isEvmChainId } from '@shapeshiftoss/chain-adapters';
|
|
4
|
+
import { TxStatus } from '@shapeshiftoss/unchained-client';
|
|
5
|
+
import { bnOrZero, chainIdToFeeAssetId, contractAddressOrUndefined, isToken, } from '@shapeshiftoss/utils';
|
|
6
|
+
import { Err, Ok } from '@sniptt/monads';
|
|
7
|
+
import { getAddress, zeroAddress } from 'viem';
|
|
8
|
+
import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constants.js';
|
|
9
|
+
import { SwapperName, TradeQuoteError } from '../../../types.js';
|
|
10
|
+
import { createTradeAmountTooSmallErr, makeSwapErrorRight } from '../../../utils.js';
|
|
11
|
+
import { getTreasuryAddressFromChainId } from '../../utils/helpers/helpers.js';
|
|
12
|
+
import { BOB_GATEWAY_BASE_URL, BOB_GATEWAY_OFFRAMP_DEFAULT_GAS_LIMIT, BOB_GATEWAY_ONRAMP_DEFAULT_TX_VSIZE, BOB_GATEWAY_TOKENSWAP_DEFAULT_GAS_LIMIT, bobGatewayChainNameToChainId, chainIdToBobGatewayChainName, decimalSlippageToBobBps, } from './constants.js';
|
|
13
|
+
export const getBobGatewayClient = (config) => {
|
|
14
|
+
return new GatewaySDK({ basePath: BOB_GATEWAY_BASE_URL, apiKey: config.VITE_BOB_GATEWAY_API_KEY });
|
|
15
|
+
};
|
|
16
|
+
export const assetIdToBobGatewayToken = (assetId) => {
|
|
17
|
+
// BOB Gateway uses the zero address as the token address for native assets
|
|
18
|
+
if (!isToken(assetId))
|
|
19
|
+
return zeroAddress;
|
|
20
|
+
return fromAssetId(assetId).assetReference;
|
|
21
|
+
};
|
|
22
|
+
export const getBobGatewayAffiliates = (affiliateBps) => {
|
|
23
|
+
const bps = bnOrZero(affiliateBps);
|
|
24
|
+
if (!bps.isFinite() || bps.lte(0))
|
|
25
|
+
return undefined;
|
|
26
|
+
const affiliateAddress = getTreasuryAddressFromChainId(ethChainId);
|
|
27
|
+
return [{ address: getAddress(affiliateAddress), bps: bps.toNumber() }];
|
|
28
|
+
};
|
|
29
|
+
export const getBobGatewayQuote = async ({ config, sellAsset, buyAsset, sellChainName, buyChainName, sender, recipient, amount, affiliateBps, slippageTolerancePercentageDecimal, }) => {
|
|
30
|
+
const slippage = decimalSlippageToBobBps(slippageTolerancePercentageDecimal ??
|
|
31
|
+
getDefaultSlippageDecimalPercentageForSwapper(SwapperName.BobGateway));
|
|
32
|
+
try {
|
|
33
|
+
const quote = await getBobGatewayClient(config).getQuote({
|
|
34
|
+
fromChain: sellChainName,
|
|
35
|
+
toChain: buyChainName,
|
|
36
|
+
fromToken: assetIdToBobGatewayToken(sellAsset.assetId),
|
|
37
|
+
toToken: assetIdToBobGatewayToken(buyAsset.assetId),
|
|
38
|
+
fromUserAddress: sender,
|
|
39
|
+
toUserAddress: recipient,
|
|
40
|
+
amount,
|
|
41
|
+
maxSlippage: Number(slippage),
|
|
42
|
+
affiliates: getBobGatewayAffiliates(affiliateBps),
|
|
43
|
+
});
|
|
44
|
+
return Ok(quote);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
if (isGatewayError(err)) {
|
|
48
|
+
switch (err.code) {
|
|
49
|
+
case GatewayErrorCode.QuoteAmountTooLow:
|
|
50
|
+
return Err(createTradeAmountTooSmallErr({
|
|
51
|
+
minAmountCryptoBaseUnit: err.details.minimum,
|
|
52
|
+
assetId: sellAsset.assetId,
|
|
53
|
+
}));
|
|
54
|
+
case GatewayErrorCode.UnableToCoverFees:
|
|
55
|
+
return Err(makeSwapErrorRight({
|
|
56
|
+
message: '[BobGateway] sell amount does not cover fees',
|
|
57
|
+
code: TradeQuoteError.SellAmountBelowTradeFee,
|
|
58
|
+
cause: err,
|
|
59
|
+
}));
|
|
60
|
+
case GatewayErrorCode.NoRoute:
|
|
61
|
+
return Err(makeSwapErrorRight({
|
|
62
|
+
message: '[BobGateway] no route found for trade pair',
|
|
63
|
+
code: TradeQuoteError.NoRouteFound,
|
|
64
|
+
cause: err,
|
|
65
|
+
}));
|
|
66
|
+
case GatewayErrorCode.DisabledChain:
|
|
67
|
+
return Err(makeSwapErrorRight({
|
|
68
|
+
message: '[BobGateway] trading is temporarily halted for this chain',
|
|
69
|
+
code: TradeQuoteError.TradingHalted,
|
|
70
|
+
cause: err,
|
|
71
|
+
}));
|
|
72
|
+
default:
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return Err(makeSwapErrorRight({
|
|
77
|
+
message: '[BobGateway] failed to fetch quote',
|
|
78
|
+
code: TradeQuoteError.QueryFailed,
|
|
79
|
+
cause: err,
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
export const createBobGatewayOrderMetadata = async (config, gatewayQuote) => {
|
|
84
|
+
try {
|
|
85
|
+
const orderResponse = await getBobGatewayClient(config).api.createOrderV2({
|
|
86
|
+
gatewayQuoteV2: gatewayQuote,
|
|
87
|
+
});
|
|
88
|
+
// onramp (BTC→EVM)
|
|
89
|
+
if ('onramp' in orderResponse) {
|
|
90
|
+
return Ok({
|
|
91
|
+
orderId: orderResponse.onramp.orderId,
|
|
92
|
+
utxoTx: {
|
|
93
|
+
depositAddress: orderResponse.onramp.address,
|
|
94
|
+
opReturnData: orderResponse.onramp.opReturnData ?? undefined,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// offramp (EVM→BTC) and tokenSwap (EVM→EVM) orders share the same tx shape
|
|
99
|
+
const order = 'offramp' in orderResponse ? orderResponse.offramp : orderResponse.tokenSwap;
|
|
100
|
+
return Ok({
|
|
101
|
+
orderId: order.orderId,
|
|
102
|
+
evmTx: {
|
|
103
|
+
to: order.tx.to,
|
|
104
|
+
data: order.tx.data,
|
|
105
|
+
value: order.tx.value,
|
|
106
|
+
chain: order.tx.chain,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (isGatewayError(err) && err.code === GatewayErrorCode.InsufficientConfirmedFunds) {
|
|
112
|
+
return Err(makeSwapErrorRight({
|
|
113
|
+
message: '[BobGateway] insufficient confirmed balance',
|
|
114
|
+
code: TradeQuoteError.InsufficientFundsUnconfirmed,
|
|
115
|
+
cause: err,
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
return Err(makeSwapErrorRight({
|
|
119
|
+
message: '[BobGateway] failed to create order',
|
|
120
|
+
code: TradeQuoteError.QueryFailed,
|
|
121
|
+
cause: err,
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
export const registerBobGatewayTx = async ({ config, orderId, txHash, sellAsset, buyAsset, }) => {
|
|
126
|
+
const registerTxV2 = (() => {
|
|
127
|
+
// BTC→EVM
|
|
128
|
+
if (chainIdToBobGatewayChainName[sellAsset.chainId] === 'bitcoin') {
|
|
129
|
+
return { onramp: { orderId, bitcoinTxid: txHash } };
|
|
130
|
+
}
|
|
131
|
+
// EVM→BTC
|
|
132
|
+
if (chainIdToBobGatewayChainName[buyAsset.chainId] === 'bitcoin') {
|
|
133
|
+
return { offramp: { orderId, evmTxhash: txHash } };
|
|
134
|
+
}
|
|
135
|
+
// EVM→EVM
|
|
136
|
+
return { tokenSwap: { orderId, evmTxhash: txHash } };
|
|
137
|
+
})();
|
|
138
|
+
await getBobGatewayClient(config).api.registerTxV2({ registerTxV2 });
|
|
139
|
+
};
|
|
140
|
+
export const getBobGatewayQuoteFeeData = async (input, { assertGetUtxoChainAdapter, assertGetEvmChainAdapter }, orderMetadata) => {
|
|
141
|
+
const { sellAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit } = input;
|
|
142
|
+
try {
|
|
143
|
+
if (orderMetadata.utxoTx && 'xpub' in input) {
|
|
144
|
+
const { depositAddress, opReturnData } = orderMetadata.utxoTx;
|
|
145
|
+
const { fast } = await assertGetUtxoChainAdapter(sellAsset.chainId).getFeeData({
|
|
146
|
+
to: depositAddress,
|
|
147
|
+
value: sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
148
|
+
chainSpecific: { pubkey: input.xpub, opReturnData },
|
|
149
|
+
sendMax: false,
|
|
150
|
+
});
|
|
151
|
+
return Ok({
|
|
152
|
+
networkFeeCryptoBaseUnit: fast.txFee,
|
|
153
|
+
chainSpecific: { satsPerByte: fast.chainSpecific.satoshiPerByte },
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
if (orderMetadata.evmTx && input.sendAddress && 'supportsEIP1559' in input) {
|
|
157
|
+
const { to, data, value } = orderMetadata.evmTx;
|
|
158
|
+
const { networkFeeCryptoBaseUnit } = await evm.getFees({
|
|
159
|
+
adapter: assertGetEvmChainAdapter(sellAsset.chainId),
|
|
160
|
+
to,
|
|
161
|
+
data,
|
|
162
|
+
value,
|
|
163
|
+
from: input.sendAddress,
|
|
164
|
+
supportsEIP1559: input.supportsEIP1559,
|
|
165
|
+
});
|
|
166
|
+
return Ok({ networkFeeCryptoBaseUnit });
|
|
167
|
+
}
|
|
168
|
+
throw new Error('[BobGateway] invalid quote');
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
return Err(makeSwapErrorRight({
|
|
172
|
+
message: '[BobGateway] failed to estimate network fee',
|
|
173
|
+
code: TradeQuoteError.NetworkFeeEstimationFailed,
|
|
174
|
+
cause: err,
|
|
175
|
+
}));
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
export const getBobGatewayRateNetworkFeeCryptoBaseUnit = async (quote, sellAsset, { assertGetEvmChainAdapter, assertGetUtxoChainAdapter }) => {
|
|
179
|
+
try {
|
|
180
|
+
if ('onramp' in quote) {
|
|
181
|
+
const adapter = assertGetUtxoChainAdapter(sellAsset.chainId);
|
|
182
|
+
const { fast } = await adapter.httpProvider.getNetworkFees();
|
|
183
|
+
if (!fast?.satsPerKiloByte)
|
|
184
|
+
return undefined;
|
|
185
|
+
const satsPerByte = Math.max(1, Math.ceil(fast.satsPerKiloByte / 1000));
|
|
186
|
+
return bnOrZero(satsPerByte).times(BOB_GATEWAY_ONRAMP_DEFAULT_TX_VSIZE).toFixed(0);
|
|
187
|
+
}
|
|
188
|
+
const defaultGasLimit = 'offramp' in quote
|
|
189
|
+
? BOB_GATEWAY_OFFRAMP_DEFAULT_GAS_LIMIT
|
|
190
|
+
: BOB_GATEWAY_TOKENSWAP_DEFAULT_GAS_LIMIT;
|
|
191
|
+
const { average } = await assertGetEvmChainAdapter(sellAsset.chainId).getGasFeeData();
|
|
192
|
+
const feePerGas = bnOrZero(average.maxFeePerGas).gt(0) ? average.maxFeePerGas : average.gasPrice;
|
|
193
|
+
return bnOrZero(defaultGasLimit).times(bnOrZero(feePerGas)).toFixed(0);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
export const mapBobGatewayOrderStatusToTxStatus = (status) => {
|
|
200
|
+
if ('inProgress' in status)
|
|
201
|
+
return TxStatus.Pending;
|
|
202
|
+
if ('success' in status)
|
|
203
|
+
return TxStatus.Confirmed;
|
|
204
|
+
if ('failed' in status)
|
|
205
|
+
return TxStatus.Failed;
|
|
206
|
+
if ('refunded' in status)
|
|
207
|
+
return TxStatus.Failed;
|
|
208
|
+
return TxStatus.Unknown;
|
|
209
|
+
};
|
|
210
|
+
const bobGatewayFeeToAssetId = (fee) => {
|
|
211
|
+
const chainId = bobGatewayChainNameToChainId[fee.chain];
|
|
212
|
+
if (!chainId)
|
|
213
|
+
return;
|
|
214
|
+
if (fee.address.toLowerCase() === zeroAddress)
|
|
215
|
+
return chainIdToFeeAssetId(chainId);
|
|
216
|
+
return toAssetId({
|
|
217
|
+
chainId,
|
|
218
|
+
assetNamespace: ASSET_NAMESPACE.erc20,
|
|
219
|
+
assetReference: fee.address,
|
|
220
|
+
});
|
|
221
|
+
};
|
|
222
|
+
export const parseBobGatewayQuote = (quote, buyAsset, assetsById) => {
|
|
223
|
+
const { outputAmount, estimatedTimeInSecs, fees } = (() => {
|
|
224
|
+
if ('onramp' in quote) {
|
|
225
|
+
const { outputAmount, estimatedTimeInSecs, fees } = quote.onramp;
|
|
226
|
+
return { outputAmount, estimatedTimeInSecs, fees: [fees] };
|
|
227
|
+
}
|
|
228
|
+
if ('offramp' in quote) {
|
|
229
|
+
const { outputAmount, estimatedTimeInSecs, feeBreakdown } = quote.offramp;
|
|
230
|
+
const { affiliateFee, inclusionFee, protocolFee, solverFee } = feeBreakdown;
|
|
231
|
+
return {
|
|
232
|
+
outputAmount,
|
|
233
|
+
estimatedTimeInSecs,
|
|
234
|
+
fees: [affiliateFee, inclusionFee, protocolFee, solverFee],
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const { outputAmount, estimatedTimeInSecs, fees } = quote.tokenSwap;
|
|
238
|
+
return { outputAmount, estimatedTimeInSecs, fees: [fees] };
|
|
239
|
+
})();
|
|
240
|
+
const protocolFees = fees.reduce((acc, fee) => {
|
|
241
|
+
const amountCryptoBaseUnit = bnOrZero(fee.amount);
|
|
242
|
+
if (amountCryptoBaseUnit.lte(0))
|
|
243
|
+
return acc;
|
|
244
|
+
const assetId = bobGatewayFeeToAssetId(fee);
|
|
245
|
+
if (!assetId)
|
|
246
|
+
return acc;
|
|
247
|
+
const asset = assetId === buyAsset.assetId ? buyAsset : assetsById[assetId];
|
|
248
|
+
if (!asset)
|
|
249
|
+
return acc;
|
|
250
|
+
acc[assetId] = {
|
|
251
|
+
amountCryptoBaseUnit: bnOrZero(acc[assetId]?.amountCryptoBaseUnit)
|
|
252
|
+
.plus(amountCryptoBaseUnit)
|
|
253
|
+
.toFixed(),
|
|
254
|
+
asset,
|
|
255
|
+
requiresBalance: false,
|
|
256
|
+
};
|
|
257
|
+
return acc;
|
|
258
|
+
}, {});
|
|
259
|
+
// buyAmountBeforeFees is denominated in the buy asset, so only add back buy-asset-denominated fees
|
|
260
|
+
const buyAssetFeeCryptoBaseUnit = bnOrZero(protocolFees[buyAsset.assetId]?.amountCryptoBaseUnit);
|
|
261
|
+
return {
|
|
262
|
+
buyAmountBeforeFeesCryptoBaseUnit: buyAssetFeeCryptoBaseUnit
|
|
263
|
+
.plus(bnOrZero(outputAmount.amount))
|
|
264
|
+
.toFixed(),
|
|
265
|
+
buyAmountAfterFeesCryptoBaseUnit: outputAmount.amount,
|
|
266
|
+
protocolFees,
|
|
267
|
+
estimatedExecutionTimeMs: typeof estimatedTimeInSecs === 'number' ? estimatedTimeInSecs * 1000 : undefined,
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
export const getBobGatewayAllowanceContract = (quote, sellAsset) => {
|
|
271
|
+
if (!isEvmChainId(sellAsset.chainId))
|
|
272
|
+
return '';
|
|
273
|
+
if (!contractAddressOrUndefined(sellAsset.assetId))
|
|
274
|
+
return '';
|
|
275
|
+
if ('offramp' in quote)
|
|
276
|
+
return quote.offramp.txTo;
|
|
277
|
+
if ('tokenSwap' in quote)
|
|
278
|
+
return quote.tokenSwap.txTo;
|
|
279
|
+
return '';
|
|
280
|
+
};
|
|
281
|
+
export const assertValidTrade = ({ sellAsset, buyAsset, }) => {
|
|
282
|
+
const sellChainName = chainIdToBobGatewayChainName[sellAsset.chainId];
|
|
283
|
+
const buyChainName = chainIdToBobGatewayChainName[buyAsset.chainId];
|
|
284
|
+
if (!sellChainName) {
|
|
285
|
+
return Err(makeSwapErrorRight({
|
|
286
|
+
message: `[BobGateway] unsupported sell chain: ${sellAsset.chainId}`,
|
|
287
|
+
code: TradeQuoteError.UnsupportedChain,
|
|
288
|
+
details: { chainId: sellAsset.chainId },
|
|
289
|
+
}));
|
|
290
|
+
}
|
|
291
|
+
if (!buyChainName) {
|
|
292
|
+
return Err(makeSwapErrorRight({
|
|
293
|
+
message: `[BobGateway] unsupported buy chain: ${buyAsset.chainId}`,
|
|
294
|
+
code: TradeQuoteError.UnsupportedChain,
|
|
295
|
+
details: { chainId: buyAsset.chainId },
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
return Ok({ sellChainName, buyChainName });
|
|
299
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { executeEvmTransaction, executeSolanaTransaction } from '../../utils.js';
|
|
1
|
+
import { executeEvmTransaction, executeSolanaTransaction, executeTronTransaction, } from '../../utils.js';
|
|
2
2
|
export const chainflipSwapper = {
|
|
3
3
|
executeEvmTransaction,
|
|
4
4
|
executeSolanaTransaction,
|
|
5
|
+
executeTronTransaction,
|
|
5
6
|
executeUtxoTransaction: (txToSign, { signAndBroadcastTransaction }) => {
|
|
6
7
|
return signAndBroadcastTransaction(txToSign);
|
|
7
8
|
},
|
|
@@ -5,7 +5,7 @@ import type { SwapSource } from '../../types.js';
|
|
|
5
5
|
import { ChainflipNetwork } from './types.js';
|
|
6
6
|
export declare const CHAINFLIP_REGULAR_QUOTE: "regular";
|
|
7
7
|
export declare const CHAINFLIP_DCA_QUOTE: "dca";
|
|
8
|
-
export declare const ChainflipSupportedChainIds: readonly [KnownChainIds.EthereumMainnet, KnownChainIds.ArbitrumMainnet, KnownChainIds.BitcoinMainnet, KnownChainIds.SolanaMainnet];
|
|
8
|
+
export declare const ChainflipSupportedChainIds: readonly [KnownChainIds.EthereumMainnet, KnownChainIds.ArbitrumMainnet, KnownChainIds.BitcoinMainnet, KnownChainIds.SolanaMainnet, KnownChainIds.TronMainnet];
|
|
9
9
|
export type ChainflipSupportedChainId = (typeof ChainflipSupportedChainIds)[number];
|
|
10
10
|
export declare const ChainflipSupportedAssetIdsByChainId: Partial<Record<KnownChainIds, AssetId[]>>;
|
|
11
11
|
export declare const chainIdToChainflipNetwork: Partial<Record<ChainId, ChainflipNetwork>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { arbitrumAssetId, btcAssetId, ethAssetId, flipAssetId, solAssetId, usdcAssetId, usdcOnArbitrumOneAssetId, usdcOnSolanaAssetId, usdtAssetId, usdtOnArbitrumOneAssetId, usdtOnSolanaAssetId, wbtcAssetId, } from '@shapeshiftoss/caip';
|
|
1
|
+
import { arbitrumAssetId, btcAssetId, ethAssetId, flipAssetId, solAssetId, tronAssetId, usdcAssetId, usdcOnArbitrumOneAssetId, usdcOnSolanaAssetId, usdtAssetId, usdtOnArbitrumOneAssetId, usdtOnSolanaAssetId, usdtOnTronAssetId, wbtcAssetId, } from '@shapeshiftoss/caip';
|
|
2
2
|
import { KnownChainIds } from '@shapeshiftoss/types';
|
|
3
3
|
import { SwapperName } from '../../types.js';
|
|
4
4
|
import { ChainflipNetwork } from './types.js';
|
|
@@ -9,6 +9,7 @@ export const ChainflipSupportedChainIds = [
|
|
|
9
9
|
KnownChainIds.ArbitrumMainnet,
|
|
10
10
|
KnownChainIds.BitcoinMainnet,
|
|
11
11
|
KnownChainIds.SolanaMainnet,
|
|
12
|
+
KnownChainIds.TronMainnet,
|
|
12
13
|
];
|
|
13
14
|
export const ChainflipSupportedAssetIdsByChainId = {
|
|
14
15
|
[KnownChainIds.EthereumMainnet]: [ethAssetId, flipAssetId, usdcAssetId, usdtAssetId, wbtcAssetId],
|
|
@@ -19,12 +20,14 @@ export const ChainflipSupportedAssetIdsByChainId = {
|
|
|
19
20
|
],
|
|
20
21
|
[KnownChainIds.BitcoinMainnet]: [btcAssetId],
|
|
21
22
|
[KnownChainIds.SolanaMainnet]: [solAssetId, usdcOnSolanaAssetId, usdtOnSolanaAssetId],
|
|
23
|
+
[KnownChainIds.TronMainnet]: [tronAssetId, usdtOnTronAssetId],
|
|
22
24
|
};
|
|
23
25
|
export const chainIdToChainflipNetwork = {
|
|
24
26
|
[KnownChainIds.EthereumMainnet]: ChainflipNetwork.Ethereum,
|
|
25
27
|
[KnownChainIds.ArbitrumMainnet]: ChainflipNetwork.Arbitrum,
|
|
26
28
|
[KnownChainIds.BitcoinMainnet]: ChainflipNetwork.Bitcoin,
|
|
27
29
|
[KnownChainIds.SolanaMainnet]: ChainflipNetwork.Solana,
|
|
30
|
+
[KnownChainIds.TronMainnet]: ChainflipNetwork.Tron,
|
|
28
31
|
};
|
|
29
32
|
export const CHAINFLIP_SWAP_SOURCE = SwapperName.Chainflip;
|
|
30
33
|
export const CHAINFLIP_BOOST_SWAP_SOURCE = `${SwapperName.Chainflip} • Boost`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { evm } from '@shapeshiftoss/chain-adapters';
|
|
2
2
|
import { TxStatus } from '@shapeshiftoss/unchained-client';
|
|
3
3
|
import { contractAddressOrUndefined } from '@shapeshiftoss/utils';
|
|
4
|
+
import { getTronTransactionFees } from '../../tron-utils/getTronTransactionFees.js';
|
|
4
5
|
import { getExecutableTradeStep, isExecutableTradeQuote } from '../../utils.js';
|
|
5
6
|
import { isNativeEvmAsset } from '../utils/helpers/helpers.js';
|
|
6
7
|
import { ChainflipStatusMessage } from './constants.js';
|
|
@@ -163,6 +164,25 @@ export const chainflipApi = {
|
|
|
163
164
|
const ratio = simulatedUnits > 0 ? boostedUnits / simulatedUnits : 1;
|
|
164
165
|
return String(Math.ceil(Number(fast.txFee) * ratio));
|
|
165
166
|
},
|
|
167
|
+
getUnsignedTronTransaction: ({ stepIndex, tradeQuote, from, assertGetTronChainAdapter }) => {
|
|
168
|
+
if (!isExecutableTradeQuote(tradeQuote))
|
|
169
|
+
throw new Error('Unable to execute a trade rate quote');
|
|
170
|
+
const step = getExecutableTradeStep(tradeQuote, stepIndex);
|
|
171
|
+
const { accountNumber, chainflipSpecific, sellAsset } = step;
|
|
172
|
+
if (!chainflipSpecific?.chainflipDepositAddress)
|
|
173
|
+
throw Error('Missing deposit address');
|
|
174
|
+
if (!chainflipSpecific?.chainflipSwapId)
|
|
175
|
+
throw Error('Missing swap id');
|
|
176
|
+
const adapter = assertGetTronChainAdapter(sellAsset.chainId);
|
|
177
|
+
return adapter.buildSendApiTransaction({
|
|
178
|
+
to: chainflipSpecific.chainflipDepositAddress,
|
|
179
|
+
from,
|
|
180
|
+
value: step.sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
181
|
+
accountNumber,
|
|
182
|
+
chainSpecific: { contractAddress: contractAddressOrUndefined(sellAsset.assetId) },
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
getTronTransactionFees,
|
|
166
186
|
checkTradeStatus: async ({ config, swap }) => {
|
|
167
187
|
const chainflipSwapId = swap?.metadata.chainflipSwapId;
|
|
168
188
|
if (chainflipSwapId == null)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CHAIN_NAMESPACE, fromAssetId, solAssetId } from '@shapeshiftoss/caip';
|
|
2
|
-
import { assertUnreachable, BigAmount, bnOrZero } from '@shapeshiftoss/utils';
|
|
2
|
+
import { assertUnreachable, BigAmount, bnOrZero, contractAddressOrUndefined, } from '@shapeshiftoss/utils';
|
|
3
3
|
import { Err, Ok } from '@sniptt/monads';
|
|
4
4
|
import { v4 as uuid } from 'uuid';
|
|
5
5
|
import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constants.js';
|
|
@@ -67,11 +67,14 @@ export const getQuoteOrRate = async (input, deps) => {
|
|
|
67
67
|
if (maybeQuoteResponse.isErr()) {
|
|
68
68
|
const error = maybeQuoteResponse.unwrapErr();
|
|
69
69
|
const cause = error.cause;
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
// Chainflip returns a 400 with `errors.minimalAmountNative` when the sell amount is below the
|
|
71
|
+
// route minimum. The `detail` wording varies ("Amount outside asset bounds", "Amount below
|
|
72
|
+
// minimum. Min: ..."), so key off the field rather than string-matching the message.
|
|
73
|
+
const minAmountCryptoBaseUnit = cause?.response?.data?.errors?.minimalAmountNative?.[0];
|
|
74
|
+
if (cause?.response?.status === 400 && minAmountCryptoBaseUnit !== undefined) {
|
|
72
75
|
return Err(createTradeAmountTooSmallErr({
|
|
73
76
|
assetId: sellAsset.assetId,
|
|
74
|
-
minAmountCryptoBaseUnit
|
|
77
|
+
minAmountCryptoBaseUnit,
|
|
75
78
|
}));
|
|
76
79
|
}
|
|
77
80
|
return Err(makeSwapErrorRight({
|
|
@@ -119,6 +122,21 @@ export const getQuoteOrRate = async (input, deps) => {
|
|
|
119
122
|
const { fast } = await sellAdapter.getFeeData(getFeeDataInput);
|
|
120
123
|
return { networkFeeCryptoBaseUnit: fast.txFee };
|
|
121
124
|
}
|
|
125
|
+
case CHAIN_NAMESPACE.Tron: {
|
|
126
|
+
if (!input.sendAddress)
|
|
127
|
+
return { networkFeeCryptoBaseUnit: undefined };
|
|
128
|
+
const sellAdapter = deps.assertGetTronChainAdapter(sellAsset.chainId);
|
|
129
|
+
const { fast } = await sellAdapter.getFeeData({
|
|
130
|
+
// Simulates a self-send, since we don't know the 'to' just yet at this stage
|
|
131
|
+
to: input.sendAddress,
|
|
132
|
+
value: sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
133
|
+
chainSpecific: {
|
|
134
|
+
from: input.sendAddress,
|
|
135
|
+
contractAddress: contractAddressOrUndefined(sellAsset.assetId),
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
return { networkFeeCryptoBaseUnit: fast.txFee };
|
|
139
|
+
}
|
|
122
140
|
default:
|
|
123
141
|
throw new Error('Unsupported chainNamespace');
|
|
124
142
|
}
|
|
@@ -182,6 +200,8 @@ export const getQuoteOrRate = async (input, deps) => {
|
|
|
182
200
|
return 10;
|
|
183
201
|
case CHAIN_NAMESPACE.Solana:
|
|
184
202
|
return 0;
|
|
203
|
+
case CHAIN_NAMESPACE.Tron:
|
|
204
|
+
return 0;
|
|
185
205
|
default:
|
|
186
206
|
throw new Error('Unsupported chainNamespace');
|
|
187
207
|
}
|
|
@@ -20,8 +20,8 @@ type GetChainFlipSwapArgs = ChainFlipBrokerBaseArgs & {
|
|
|
20
20
|
numberOfChunks?: number;
|
|
21
21
|
chunkIntervalBlocks?: number;
|
|
22
22
|
};
|
|
23
|
-
export declare const isSupportedChainId: (chainId: ChainId) => chainId is import("@shapeshiftoss/types").KnownChainIds.EthereumMainnet | import("@shapeshiftoss/types").KnownChainIds.ArbitrumMainnet | import("@shapeshiftoss/types").KnownChainIds.BitcoinMainnet | import("@shapeshiftoss/types").KnownChainIds.SolanaMainnet;
|
|
24
|
-
export declare const isSupportedAssetId: (chainId: ChainId, assetId: AssetId) => chainId is import("@shapeshiftoss/types").KnownChainIds.EthereumMainnet | import("@shapeshiftoss/types").KnownChainIds.ArbitrumMainnet | import("@shapeshiftoss/types").KnownChainIds.BitcoinMainnet | import("@shapeshiftoss/types").KnownChainIds.SolanaMainnet;
|
|
23
|
+
export declare const isSupportedChainId: (chainId: ChainId) => chainId is import("@shapeshiftoss/types").KnownChainIds.EthereumMainnet | import("@shapeshiftoss/types").KnownChainIds.ArbitrumMainnet | import("@shapeshiftoss/types").KnownChainIds.BitcoinMainnet | import("@shapeshiftoss/types").KnownChainIds.SolanaMainnet | import("@shapeshiftoss/types").KnownChainIds.TronMainnet;
|
|
24
|
+
export declare const isSupportedAssetId: (chainId: ChainId, assetId: AssetId) => chainId is import("@shapeshiftoss/types").KnownChainIds.EthereumMainnet | import("@shapeshiftoss/types").KnownChainIds.ArbitrumMainnet | import("@shapeshiftoss/types").KnownChainIds.BitcoinMainnet | import("@shapeshiftoss/types").KnownChainIds.SolanaMainnet | import("@shapeshiftoss/types").KnownChainIds.TronMainnet;
|
|
25
25
|
export declare const calculateChainflipMinPrice: ({ sellAmountIncludingProtocolFeesCryptoBaseUnit, buyAmountAfterFeesCryptoBaseUnit, slippageTolerancePercentageDecimal, sellAsset, buyAsset, }: {
|
|
26
26
|
sellAmountIncludingProtocolFeesCryptoBaseUnit: string;
|
|
27
27
|
buyAmountAfterFeesCryptoBaseUnit: string;
|
|
@@ -45,7 +45,8 @@ export const getChainFlipSwap = ({ brokerUrl, apiKey, sourceAsset, destinationAs
|
|
|
45
45
|
swapUrl += `&numberOfChunks=${numberOfChunks}`;
|
|
46
46
|
swapUrl += `&chunkIntervalBlocks=${chunkIntervalBlocks}`;
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
// Opening a deposit channel can spike past the default 10s timeout (notably on Tron).
|
|
49
|
+
return chainflipService.get(swapUrl, { timeout: 30_000 });
|
|
49
50
|
};
|
|
50
51
|
const fetchChainFlipAssets = async ({ brokerUrl, }) => {
|
|
51
52
|
const result = await chainflipService.get(`${brokerUrl}/assets`);
|
|
@@ -13,9 +13,9 @@ describe('utils', () => {
|
|
|
13
13
|
const mockDay = '2020-12-31';
|
|
14
14
|
const mockTime = 'T23:59:59.000Z';
|
|
15
15
|
const mockDate = `${mockDay}${mockTime}`;
|
|
16
|
-
beforeEach(() =>
|
|
17
|
-
.useFakeTimers()
|
|
18
|
-
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
vi.useFakeTimers().setSystemTime(new Date(mockDate));
|
|
18
|
+
});
|
|
19
19
|
afterEach(() => {
|
|
20
20
|
vi.restoreAllMocks();
|
|
21
21
|
vi.useRealTimers();
|
|
@@ -68,7 +68,7 @@ export const relayErrorCodeToTradeQuoteError = {
|
|
|
68
68
|
[RelayErrorCode.ChainDisabled]: TradeQuoteError.TradingHalted,
|
|
69
69
|
[RelayErrorCode.Erc20RouterAddressNotFound]: TradeQuoteError.UnsupportedTradePair,
|
|
70
70
|
[RelayErrorCode.ExtraTransactionsNotSupported]: TradeQuoteError.UnsupportedTradePair,
|
|
71
|
-
[RelayErrorCode.InsufficientFunds]: TradeQuoteError.
|
|
71
|
+
[RelayErrorCode.InsufficientFunds]: TradeQuoteError.InsufficientFunds,
|
|
72
72
|
[RelayErrorCode.InsufficientLiquidity]: TradeQuoteError.SellAmountBelowTradeFee,
|
|
73
73
|
[RelayErrorCode.InvalidAddress]: TradeQuoteError.UnsupportedTradePair,
|
|
74
74
|
[RelayErrorCode.InvalidExtraTransactions]: TradeQuoteError.UnsupportedTradePair,
|
|
@@ -2,23 +2,14 @@ import { tronAssetId, tronChainId } from '@shapeshiftoss/caip';
|
|
|
2
2
|
import { toAddressNList } from '@shapeshiftoss/chain-adapters';
|
|
3
3
|
import { TxStatus } from '@shapeshiftoss/unchained-client';
|
|
4
4
|
import { TronWeb } from 'tronweb';
|
|
5
|
-
import { getTronTransactionFees } from '../../tron-utils/getTronTransactionFees.js';
|
|
6
5
|
import { createDefaultStatusResponse, getExecutableTradeStep, isExecutableTradeQuote, } from '../../utils.js';
|
|
7
6
|
import { getSunioTradeQuote } from './getSunioTradeQuote/getSunioTradeQuote.js';
|
|
8
7
|
import { getSunioTradeRate } from './getSunioTradeRate/getSunioTradeRate.js';
|
|
8
|
+
import { buildSwapExactInputParameters, SUNIO_SWAP_EXACT_INPUT_SELECTOR, } from './utils/buildSwapContractCall.js';
|
|
9
9
|
import { buildSwapRouteParameters } from './utils/buildSwapRouteParameters.js';
|
|
10
10
|
import { SUNIO_SMART_ROUTER_CONTRACT } from './utils/constants.js';
|
|
11
|
+
import { getSunioTransactionFees } from './utils/getSunioTransactionFees.js';
|
|
11
12
|
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
12
|
-
const convertAddressesToEvmFormat = (value) => {
|
|
13
|
-
if (Array.isArray(value)) {
|
|
14
|
-
return value.map(v => convertAddressesToEvmFormat(v));
|
|
15
|
-
}
|
|
16
|
-
if (typeof value === 'string' && value.startsWith('T') && TronWeb.isAddress(value)) {
|
|
17
|
-
const hex = TronWeb.address.toHex(value);
|
|
18
|
-
return hex.replace(/^41/, '0x');
|
|
19
|
-
}
|
|
20
|
-
return value;
|
|
21
|
-
};
|
|
22
13
|
export const sunioApi = {
|
|
23
14
|
getTradeQuote: async (input, deps) => {
|
|
24
15
|
const maybeTradeQuote = await getSunioTradeQuote(input, deps);
|
|
@@ -29,7 +20,7 @@ export const sunioApi = {
|
|
|
29
20
|
return maybeTradeRate.map(rate => [rate]);
|
|
30
21
|
},
|
|
31
22
|
getUnsignedTronTransaction: async (args) => {
|
|
32
|
-
const { tradeQuote, stepIndex, from, slippageTolerancePercentageDecimal, assertGetTronChainAdapter, } = args;
|
|
23
|
+
const { tradeQuote, stepIndex, from, slippageTolerancePercentageDecimal, assertGetTronChainAdapter, config, } = args;
|
|
33
24
|
if (!isExecutableTradeQuote(tradeQuote)) {
|
|
34
25
|
throw new Error('Unable to execute a trade rate quote');
|
|
35
26
|
}
|
|
@@ -42,24 +33,12 @@ export const sunioApi = {
|
|
|
42
33
|
const rpcUrl = adapter.httpProvider.getRpcUrl();
|
|
43
34
|
const tronWeb = new TronWeb({
|
|
44
35
|
fullHost: rpcUrl,
|
|
36
|
+
headers: config.VITE_TRON_GRID_API_KEY
|
|
37
|
+
? { 'TRON-PRO-API-KEY': config.VITE_TRON_GRID_API_KEY }
|
|
38
|
+
: {},
|
|
45
39
|
});
|
|
46
40
|
const routeParams = buildSwapRouteParameters(sunioMetadata.route, step.sellAmountIncludingProtocolFeesCryptoBaseUnit, step.buyAmountAfterFeesCryptoBaseUnit, tradeQuote.receiveAddress, slippageTolerancePercentageDecimal);
|
|
47
|
-
const parameters =
|
|
48
|
-
{ type: 'address[]', value: routeParams.path },
|
|
49
|
-
{ type: 'string[]', value: routeParams.poolVersion },
|
|
50
|
-
{ type: 'uint256[]', value: routeParams.versionLen },
|
|
51
|
-
{ type: 'uint24[]', value: routeParams.fees },
|
|
52
|
-
{
|
|
53
|
-
type: 'tuple(uint256,uint256,address,uint256)',
|
|
54
|
-
value: convertAddressesToEvmFormat([
|
|
55
|
-
routeParams.swapData.amountIn,
|
|
56
|
-
routeParams.swapData.amountOutMin,
|
|
57
|
-
routeParams.swapData.recipient,
|
|
58
|
-
routeParams.swapData.deadline,
|
|
59
|
-
]),
|
|
60
|
-
},
|
|
61
|
-
];
|
|
62
|
-
const functionSelector = 'swapExactInput(address[],string[],uint256[],uint24[],(uint256,uint256,address,uint256))';
|
|
41
|
+
const parameters = buildSwapExactInputParameters(routeParams);
|
|
63
42
|
const isSellingNativeTrx = step.sellAsset.assetId === tronAssetId;
|
|
64
43
|
const callValue = isSellingNativeTrx
|
|
65
44
|
? Number(step.sellAmountIncludingProtocolFeesCryptoBaseUnit)
|
|
@@ -68,7 +47,7 @@ export const sunioApi = {
|
|
|
68
47
|
feeLimit: 100_000_000,
|
|
69
48
|
callValue,
|
|
70
49
|
};
|
|
71
|
-
const txData = await tronWeb.transactionBuilder.triggerSmartContract(SUNIO_SMART_ROUTER_CONTRACT,
|
|
50
|
+
const txData = await tronWeb.transactionBuilder.triggerSmartContract(SUNIO_SMART_ROUTER_CONTRACT, SUNIO_SWAP_EXACT_INPUT_SELECTOR, options, parameters, from);
|
|
72
51
|
if (!txData.result || !txData.result.result) {
|
|
73
52
|
throw new Error('[Sun.io] Failed to build swap transaction');
|
|
74
53
|
}
|
|
@@ -97,7 +76,7 @@ export const sunioApi = {
|
|
|
97
76
|
transaction,
|
|
98
77
|
};
|
|
99
78
|
},
|
|
100
|
-
getTronTransactionFees,
|
|
79
|
+
getTronTransactionFees: getSunioTransactionFees,
|
|
101
80
|
checkTradeStatus: async ({ txHash, assertGetTronChainAdapter }) => {
|
|
102
81
|
try {
|
|
103
82
|
// Wait for TronGrid indexing to avoid false "REVERT"
|
|
@@ -108,12 +87,16 @@ export const sunioApi = {
|
|
|
108
87
|
return createDefaultStatusResponse(txHash);
|
|
109
88
|
}
|
|
110
89
|
const contractRet = tx.ret?.[0]?.contractRet;
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
90
|
+
// Tron reports many failure codes (REVERT, OUT_OF_ENERGY, OUT_OF_TIME, ...). A missing
|
|
91
|
+
// contractRet means it isn't mined yet; any non-SUCCESS value is a terminal failure.
|
|
92
|
+
const status = (() => {
|
|
93
|
+
if (!contractRet)
|
|
94
|
+
return TxStatus.Pending;
|
|
95
|
+
if (contractRet === 'SUCCESS') {
|
|
96
|
+
return tx.confirmations > 0 ? TxStatus.Confirmed : TxStatus.Pending;
|
|
97
|
+
}
|
|
98
|
+
return TxStatus.Failed;
|
|
99
|
+
})();
|
|
117
100
|
return {
|
|
118
101
|
status,
|
|
119
102
|
buyTxHash: txHash,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SwapRouteParameters } from './buildSwapRouteParameters.js';
|
|
2
|
+
export declare const SUNIO_SWAP_EXACT_INPUT_SELECTOR = "swapExactInput(address[],string[],uint256[],uint24[],(uint256,uint256,address,uint256))";
|
|
3
|
+
export declare const buildSwapExactInputParameters: (routeParams: SwapRouteParameters) => {
|
|
4
|
+
type: string;
|
|
5
|
+
value: unknown;
|
|
6
|
+
}[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { TronWeb } from 'tronweb';
|
|
2
|
+
export const SUNIO_SWAP_EXACT_INPUT_SELECTOR = 'swapExactInput(address[],string[],uint256[],uint24[],(uint256,uint256,address,uint256))';
|
|
3
|
+
const convertAddressesToEvmFormat = (value) => {
|
|
4
|
+
if (Array.isArray(value)) {
|
|
5
|
+
return value.map(v => convertAddressesToEvmFormat(v));
|
|
6
|
+
}
|
|
7
|
+
if (typeof value === 'string' && value.startsWith('T') && TronWeb.isAddress(value)) {
|
|
8
|
+
const hex = TronWeb.address.toHex(value);
|
|
9
|
+
return hex.replace(/^41/, '0x');
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
};
|
|
13
|
+
// Parameter list for the SmartExchangeRouter swapExactInput call, shared between fee
|
|
14
|
+
// estimation and execution so both encode the call identically.
|
|
15
|
+
export const buildSwapExactInputParameters = (routeParams) => [
|
|
16
|
+
{ type: 'address[]', value: routeParams.path },
|
|
17
|
+
{ type: 'string[]', value: routeParams.poolVersion },
|
|
18
|
+
{ type: 'uint256[]', value: routeParams.versionLen },
|
|
19
|
+
{ type: 'uint24[]', value: routeParams.fees },
|
|
20
|
+
{
|
|
21
|
+
type: 'tuple(uint256,uint256,address,uint256)',
|
|
22
|
+
value: convertAddressesToEvmFormat([
|
|
23
|
+
routeParams.swapData.amountIn,
|
|
24
|
+
routeParams.swapData.amountOutMin,
|
|
25
|
+
routeParams.swapData.recipient,
|
|
26
|
+
routeParams.swapData.deadline,
|
|
27
|
+
]),
|
|
28
|
+
},
|
|
29
|
+
];
|