@shapeshiftoss/swapper 17.6.8 → 17.6.10
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 +61 -43
- package/dist/cjs/swappers/AcrossSwapper/AcrossSwapper.d.ts +2 -0
- package/dist/cjs/swappers/AcrossSwapper/AcrossSwapper.js +8 -0
- package/dist/cjs/swappers/AcrossSwapper/constant.d.ts +9 -0
- package/dist/cjs/swappers/AcrossSwapper/constant.js +48 -0
- package/dist/cjs/swappers/AcrossSwapper/endpoints.d.ts +2 -0
- package/dist/cjs/swappers/AcrossSwapper/endpoints.js +113 -0
- package/dist/cjs/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.d.ts +3 -0
- package/dist/cjs/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.js +31 -0
- package/dist/cjs/swappers/AcrossSwapper/getTradeRate/getTradeRate.d.ts +3 -0
- package/dist/cjs/swappers/AcrossSwapper/getTradeRate/getTradeRate.js +19 -0
- package/dist/cjs/swappers/AcrossSwapper/index.d.ts +1 -0
- package/dist/cjs/swappers/AcrossSwapper/index.js +5 -0
- package/dist/cjs/swappers/AcrossSwapper/utils/acrossService.d.ts +5 -0
- package/dist/cjs/swappers/AcrossSwapper/utils/acrossService.js +17 -0
- package/dist/cjs/swappers/AcrossSwapper/utils/fetchAcrossTrade.d.ts +19 -0
- package/dist/cjs/swappers/AcrossSwapper/utils/fetchAcrossTrade.js +27 -0
- package/dist/cjs/swappers/AcrossSwapper/utils/getTrade.d.ts +11 -0
- package/dist/cjs/swappers/AcrossSwapper/utils/getTrade.js +335 -0
- package/dist/cjs/swappers/AcrossSwapper/utils/types.d.ts +177 -0
- package/dist/cjs/swappers/AcrossSwapper/utils/types.js +17 -0
- package/dist/cjs/swappers/AvnuSwapper/endpoints.js +42 -14
- package/dist/cjs/swappers/AvnuSwapper/swapperApi/getTradeQuote.js +10 -5
- package/dist/cjs/swappers/AvnuSwapper/swapperApi/getTradeRate.js +8 -4
- package/dist/cjs/swappers/BebopSwapper/utils/helpers/helpers.d.ts +1 -1
- package/dist/cjs/swappers/ButterSwap/ButterSwap.js +1 -0
- package/dist/cjs/swappers/ButterSwap/endpoints.js +56 -0
- package/dist/cjs/swappers/ButterSwap/swapperApi/checkTradeStatus.js +19 -0
- package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeQuote.js +27 -14
- package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeQuote.test.js +1 -0
- package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeRate.js +23 -16
- package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeRate.test.js +2 -0
- package/dist/cjs/swappers/ButterSwap/types.d.ts +1 -0
- package/dist/cjs/swappers/ButterSwap/utils/helpers.js +1 -0
- package/dist/cjs/swappers/ButterSwap/xhr.d.ts +2 -2
- package/dist/cjs/swappers/ButterSwap/xhr.js +7 -2
- package/dist/cjs/swappers/CetusSwapper/endpoints.js +3 -8
- package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeQuote.js +1 -1
- package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeRate.js +1 -1
- package/dist/cjs/swappers/ChainflipSwapper/utils/getQuoteOrRate.js +11 -5
- package/dist/cjs/swappers/ChainflipSwapper/utils/helpers.js +8 -2
- package/dist/cjs/swappers/CowSwapper/CowSwapper.js +1 -1
- package/dist/cjs/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.js +1 -1
- package/dist/cjs/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.test.js +5 -5
- package/dist/cjs/swappers/CowSwapper/getCowSwapTradeRate/getCowSwapTradeRate.js +1 -1
- package/dist/cjs/swappers/CowSwapper/utils/helpers/helpers.js +8 -2
- package/dist/cjs/swappers/NearIntentsSwapper/NearIntentsSwapper.js +1 -0
- package/dist/cjs/swappers/NearIntentsSwapper/endpoints.js +31 -0
- package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +36 -1
- package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +61 -1
- package/dist/cjs/swappers/NearIntentsSwapper/types.d.ts +1 -1
- package/dist/cjs/swappers/NearIntentsSwapper/types.js +6 -0
- package/dist/cjs/swappers/NearIntentsSwapper/utils/helpers/helpers.js +23 -4
- package/dist/cjs/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.js +8 -3
- package/dist/cjs/swappers/RelaySwapper/constant.js +26 -1
- package/dist/cjs/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.d.ts +1 -1
- package/dist/cjs/swappers/RelaySwapper/utils/getTrade.js +13 -2
- package/dist/cjs/swappers/RelaySwapper/utils/relayTokenToAssetId.js +100 -0
- package/dist/cjs/swappers/RelaySwapper/utils/types.d.ts +15 -0
- package/dist/cjs/swappers/StonfiSwapper/StonfiSwapper.d.ts +2 -0
- package/dist/cjs/swappers/StonfiSwapper/StonfiSwapper.js +8 -0
- package/dist/cjs/swappers/StonfiSwapper/endpoints.d.ts +2 -0
- package/dist/cjs/swappers/StonfiSwapper/endpoints.js +227 -0
- package/dist/cjs/swappers/StonfiSwapper/index.d.ts +4 -0
- package/dist/cjs/swappers/StonfiSwapper/index.js +25 -0
- package/dist/cjs/swappers/StonfiSwapper/swapperApi/getTradeQuote.d.ts +2 -0
- package/dist/cjs/swappers/StonfiSwapper/swapperApi/getTradeQuote.js +119 -0
- package/dist/cjs/swappers/StonfiSwapper/swapperApi/getTradeRate.d.ts +2 -0
- package/dist/cjs/swappers/StonfiSwapper/swapperApi/getTradeRate.js +105 -0
- package/dist/cjs/swappers/StonfiSwapper/types.d.ts +60 -0
- package/dist/cjs/swappers/StonfiSwapper/types.js +5 -0
- package/dist/cjs/swappers/StonfiSwapper/utils/constants.d.ts +5 -0
- package/dist/cjs/swappers/StonfiSwapper/utils/constants.js +8 -0
- package/dist/cjs/swappers/StonfiSwapper/utils/helpers.d.ts +11 -0
- package/dist/cjs/swappers/StonfiSwapper/utils/helpers.js +135 -0
- package/dist/cjs/swappers/StonfiSwapper/utils/omnistonManager.d.ts +5 -0
- package/dist/cjs/swappers/StonfiSwapper/utils/omnistonManager.js +20 -0
- package/dist/cjs/swappers/SunioSwapper/endpoints.js +1 -1
- package/dist/cjs/swappers/SunioSwapper/utils/getQuoteOrRate.js +4 -3
- package/dist/cjs/swappers/ZrxSwapper/utils/helpers/helpers.d.ts +1 -1
- package/dist/cjs/swappers/utils/filterBuyAssetsBySellAssetId/filterBuyAssetsBySellAssetId.js +2 -5
- package/dist/cjs/swappers/utils/helpers/helpers.js +3 -0
- package/dist/cjs/thorchain-utils/getCallDataFromQuote.d.ts +1 -1
- package/dist/cjs/thorchain-utils/getL1RateOrQuote.js +30 -5
- package/dist/cjs/thorchain-utils/getQuote.js +8 -2
- package/dist/cjs/tron-utils/getUnsignedTronTransaction.js +22 -2
- package/dist/cjs/types.d.ts +79 -8
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/utils.d.ts +4 -3
- package/dist/cjs/utils.js +35 -7
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/constants.js +31 -13
- package/dist/esm/swappers/AcrossSwapper/AcrossSwapper.d.ts +2 -0
- package/dist/esm/swappers/AcrossSwapper/AcrossSwapper.js +5 -0
- package/dist/esm/swappers/AcrossSwapper/constant.d.ts +9 -0
- package/dist/esm/swappers/AcrossSwapper/constant.js +42 -0
- package/dist/esm/swappers/AcrossSwapper/endpoints.d.ts +2 -0
- package/dist/esm/swappers/AcrossSwapper/endpoints.js +107 -0
- package/dist/esm/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.d.ts +3 -0
- package/dist/esm/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.js +27 -0
- package/dist/esm/swappers/AcrossSwapper/getTradeRate/getTradeRate.d.ts +3 -0
- package/dist/esm/swappers/AcrossSwapper/getTradeRate/getTradeRate.js +15 -0
- package/dist/esm/swappers/AcrossSwapper/index.d.ts +1 -0
- package/dist/esm/swappers/AcrossSwapper/index.js +1 -0
- package/dist/esm/swappers/AcrossSwapper/utils/acrossService.d.ts +5 -0
- package/dist/esm/swappers/AcrossSwapper/utils/acrossService.js +11 -0
- package/dist/esm/swappers/AcrossSwapper/utils/fetchAcrossTrade.d.ts +19 -0
- package/dist/esm/swappers/AcrossSwapper/utils/fetchAcrossTrade.js +23 -0
- package/dist/esm/swappers/AcrossSwapper/utils/getTrade.d.ts +11 -0
- package/dist/esm/swappers/AcrossSwapper/utils/getTrade.js +328 -0
- package/dist/esm/swappers/AcrossSwapper/utils/types.d.ts +177 -0
- package/dist/esm/swappers/AcrossSwapper/utils/types.js +13 -0
- package/dist/esm/swappers/AvnuSwapper/endpoints.js +43 -15
- package/dist/esm/swappers/AvnuSwapper/swapperApi/getTradeQuote.js +10 -5
- package/dist/esm/swappers/AvnuSwapper/swapperApi/getTradeRate.js +8 -4
- package/dist/esm/swappers/BebopSwapper/utils/helpers/helpers.d.ts +1 -1
- package/dist/esm/swappers/ButterSwap/ButterSwap.js +2 -1
- package/dist/esm/swappers/ButterSwap/endpoints.js +56 -0
- package/dist/esm/swappers/ButterSwap/swapperApi/checkTradeStatus.js +20 -1
- package/dist/esm/swappers/ButterSwap/swapperApi/getTradeQuote.js +29 -16
- package/dist/esm/swappers/ButterSwap/swapperApi/getTradeQuote.test.js +1 -0
- package/dist/esm/swappers/ButterSwap/swapperApi/getTradeRate.js +25 -18
- package/dist/esm/swappers/ButterSwap/swapperApi/getTradeRate.test.js +2 -0
- package/dist/esm/swappers/ButterSwap/types.d.ts +1 -0
- package/dist/esm/swappers/ButterSwap/utils/helpers.js +1 -0
- package/dist/esm/swappers/ButterSwap/xhr.d.ts +2 -2
- package/dist/esm/swappers/ButterSwap/xhr.js +8 -3
- package/dist/esm/swappers/CetusSwapper/endpoints.js +3 -8
- package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeQuote.js +1 -1
- package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeRate.js +1 -1
- package/dist/esm/swappers/ChainflipSwapper/utils/getQuoteOrRate.js +12 -6
- package/dist/esm/swappers/ChainflipSwapper/utils/helpers.js +9 -3
- package/dist/esm/swappers/CowSwapper/CowSwapper.js +1 -1
- package/dist/esm/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.js +1 -1
- package/dist/esm/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.test.js +5 -5
- package/dist/esm/swappers/CowSwapper/getCowSwapTradeRate/getCowSwapTradeRate.js +1 -1
- package/dist/esm/swappers/CowSwapper/utils/helpers/helpers.js +9 -3
- package/dist/esm/swappers/NearIntentsSwapper/NearIntentsSwapper.js +2 -1
- package/dist/esm/swappers/NearIntentsSwapper/endpoints.js +31 -0
- package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +36 -1
- package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +61 -1
- package/dist/esm/swappers/NearIntentsSwapper/types.d.ts +1 -1
- package/dist/esm/swappers/NearIntentsSwapper/types.js +6 -0
- package/dist/esm/swappers/NearIntentsSwapper/utils/helpers/helpers.js +23 -4
- package/dist/esm/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.js +8 -3
- package/dist/esm/swappers/RelaySwapper/constant.js +28 -3
- package/dist/esm/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.d.ts +1 -1
- package/dist/esm/swappers/RelaySwapper/utils/getTrade.js +13 -2
- package/dist/esm/swappers/RelaySwapper/utils/relayTokenToAssetId.js +100 -0
- package/dist/esm/swappers/RelaySwapper/utils/types.d.ts +15 -0
- package/dist/esm/swappers/StonfiSwapper/StonfiSwapper.d.ts +2 -0
- package/dist/esm/swappers/StonfiSwapper/StonfiSwapper.js +5 -0
- package/dist/esm/swappers/StonfiSwapper/endpoints.d.ts +2 -0
- package/dist/esm/swappers/StonfiSwapper/endpoints.js +224 -0
- package/dist/esm/swappers/StonfiSwapper/index.d.ts +4 -0
- package/dist/esm/swappers/StonfiSwapper/index.js +4 -0
- package/dist/esm/swappers/StonfiSwapper/swapperApi/getTradeQuote.d.ts +2 -0
- package/dist/esm/swappers/StonfiSwapper/swapperApi/getTradeQuote.js +115 -0
- package/dist/esm/swappers/StonfiSwapper/swapperApi/getTradeRate.d.ts +2 -0
- package/dist/esm/swappers/StonfiSwapper/swapperApi/getTradeRate.js +101 -0
- package/dist/esm/swappers/StonfiSwapper/types.d.ts +60 -0
- package/dist/esm/swappers/StonfiSwapper/types.js +2 -0
- package/dist/esm/swappers/StonfiSwapper/utils/constants.d.ts +5 -0
- package/dist/esm/swappers/StonfiSwapper/utils/constants.js +5 -0
- package/dist/esm/swappers/StonfiSwapper/utils/helpers.d.ts +11 -0
- package/dist/esm/swappers/StonfiSwapper/utils/helpers.js +124 -0
- package/dist/esm/swappers/StonfiSwapper/utils/omnistonManager.d.ts +5 -0
- package/dist/esm/swappers/StonfiSwapper/utils/omnistonManager.js +17 -0
- package/dist/esm/swappers/SunioSwapper/endpoints.js +1 -1
- package/dist/esm/swappers/SunioSwapper/utils/getQuoteOrRate.js +5 -4
- package/dist/esm/swappers/ZrxSwapper/utils/helpers/helpers.d.ts +1 -1
- package/dist/esm/swappers/utils/filterBuyAssetsBySellAssetId/filterBuyAssetsBySellAssetId.js +2 -5
- package/dist/esm/swappers/utils/helpers/helpers.js +4 -1
- package/dist/esm/thorchain-utils/getCallDataFromQuote.d.ts +1 -1
- package/dist/esm/thorchain-utils/getL1RateOrQuote.js +31 -6
- package/dist/esm/thorchain-utils/getQuote.js +9 -3
- package/dist/esm/tron-utils/getUnsignedTronTransaction.js +22 -2
- package/dist/esm/types.d.ts +79 -8
- package/dist/esm/types.js +2 -0
- package/dist/esm/utils.d.ts +4 -3
- package/dist/esm/utils.js +34 -7
- package/package.json +17 -15
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { fromAssetId, isAssetReference, solanaChainId, usdcOnSolanaAssetId, } from '@shapeshiftoss/caip';
|
|
2
|
+
import { evm, isEvmChainId } from '@shapeshiftoss/chain-adapters';
|
|
3
|
+
import { BigAmount, bnOrZero, chainIdToFeeAssetId, convertPrecision, isTreasuryChainId, } from '@shapeshiftoss/utils';
|
|
4
|
+
import { Err, Ok } from '@sniptt/monads';
|
|
5
|
+
import { AddressLookupTableAccount, ComputeBudgetProgram, PublicKey, TransactionMessage, VersionedTransaction, } from '@solana/web3.js';
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
import { zeroAddress } from 'viem';
|
|
8
|
+
import { SwapperName, TradeQuoteError } from '../../../types.js';
|
|
9
|
+
import { getInputOutputRate, makeSwapErrorRight } from '../../../utils.js';
|
|
10
|
+
import { getTreasuryAddressFromChainId, isNativeEvmAsset } from '../../utils/helpers/helpers.js';
|
|
11
|
+
import { ACROSS_SOLANA_TOKEN_ADDRESS, acrossChainIdToChainId, acrossErrorCodeToTradeQuoteError, chainIdToAcrossChainId, DEFAULT_ACROSS_EVM_TOKEN_ADDRESS, DEFAULT_ACROSS_EVM_USER_ADDRESS, DEFAULT_ACROSS_SOLANA_USER_ADDRESS, } from '../constant.js';
|
|
12
|
+
import { fetchAcrossTrade } from './fetchAcrossTrade.js';
|
|
13
|
+
import { isAcrossError } from './types.js';
|
|
14
|
+
const getAcrossAssetAddress = (assetId) => {
|
|
15
|
+
if (isNativeEvmAsset(assetId))
|
|
16
|
+
return DEFAULT_ACROSS_EVM_TOKEN_ADDRESS;
|
|
17
|
+
const { chainId, assetReference } = fromAssetId(assetId);
|
|
18
|
+
if (chainId === solanaChainId && !isAssetReference(assetReference))
|
|
19
|
+
return assetReference;
|
|
20
|
+
if (chainId === solanaChainId)
|
|
21
|
+
return ACROSS_SOLANA_TOKEN_ADDRESS;
|
|
22
|
+
return isAssetReference(assetReference) ? zeroAddress : assetReference;
|
|
23
|
+
};
|
|
24
|
+
const getDefaultUserAddress = (chainId) => {
|
|
25
|
+
if (chainId === solanaChainId)
|
|
26
|
+
return DEFAULT_ACROSS_SOLANA_USER_ADDRESS;
|
|
27
|
+
return DEFAULT_ACROSS_EVM_USER_ADDRESS;
|
|
28
|
+
};
|
|
29
|
+
export async function getTrade({ input, deps, }) {
|
|
30
|
+
const { sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit, affiliateBps } = input;
|
|
31
|
+
if (sellAsset.chainId === buyAsset.chainId) {
|
|
32
|
+
return Err(makeSwapErrorRight({
|
|
33
|
+
message: 'Across does not support same-chain swaps',
|
|
34
|
+
code: TradeQuoteError.UnsupportedTradePair,
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
const sellAcrossChainId = chainIdToAcrossChainId[sellAsset.chainId];
|
|
38
|
+
const buyAcrossChainId = chainIdToAcrossChainId[buyAsset.chainId];
|
|
39
|
+
if (sellAcrossChainId === undefined) {
|
|
40
|
+
return Err(makeSwapErrorRight({
|
|
41
|
+
message: `Sell asset chain '${sellAsset.chainId}' not supported by Across`,
|
|
42
|
+
code: TradeQuoteError.UnsupportedChain,
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
if (buyAcrossChainId === undefined) {
|
|
46
|
+
return Err(makeSwapErrorRight({
|
|
47
|
+
message: `Buy asset chain '${buyAsset.chainId}' not supported by Across`,
|
|
48
|
+
code: TradeQuoteError.UnsupportedChain,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
// Across only supports USDC as the bridgeable token on Solana destinations
|
|
52
|
+
if (buyAsset.chainId === solanaChainId && buyAsset.assetId !== usdcOnSolanaAssetId) {
|
|
53
|
+
return Err(makeSwapErrorRight({
|
|
54
|
+
message: 'Across only supports USDC as destination token on Solana',
|
|
55
|
+
code: TradeQuoteError.UnsupportedTradePair,
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
const depositor = (() => {
|
|
59
|
+
if (input.quoteOrRate === 'rate') {
|
|
60
|
+
if (input.sendAddress)
|
|
61
|
+
return input.sendAddress;
|
|
62
|
+
return getDefaultUserAddress(sellAsset.chainId);
|
|
63
|
+
}
|
|
64
|
+
return input.sendAddress ?? getDefaultUserAddress(sellAsset.chainId);
|
|
65
|
+
})();
|
|
66
|
+
const recipient = (() => {
|
|
67
|
+
if (input.quoteOrRate === 'rate') {
|
|
68
|
+
if (input.receiveAddress)
|
|
69
|
+
return input.receiveAddress;
|
|
70
|
+
return getDefaultUserAddress(buyAsset.chainId);
|
|
71
|
+
}
|
|
72
|
+
return input.receiveAddress;
|
|
73
|
+
})();
|
|
74
|
+
const isSolanaRoute = sellAsset.chainId === solanaChainId || buyAsset.chainId === solanaChainId;
|
|
75
|
+
const appFee = (() => {
|
|
76
|
+
if (isSolanaRoute)
|
|
77
|
+
return undefined;
|
|
78
|
+
if (!isTreasuryChainId(buyAsset.chainId))
|
|
79
|
+
return undefined;
|
|
80
|
+
const bps = bnOrZero(affiliateBps);
|
|
81
|
+
if (!bps.isFinite() || bps.lte(0))
|
|
82
|
+
return undefined;
|
|
83
|
+
return bps.div(10000).toNumber();
|
|
84
|
+
})();
|
|
85
|
+
const appFeeRecipient = (() => {
|
|
86
|
+
if (appFee === undefined)
|
|
87
|
+
return undefined;
|
|
88
|
+
try {
|
|
89
|
+
return getTreasuryAddressFromChainId(buyAsset.chainId).toLowerCase();
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
console.error(`[getTrade] Failed to get treasury address for chainId ${buyAsset.chainId}, affiliate fee will not be applied`, e);
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
})();
|
|
96
|
+
const slippage = input.slippageTolerancePercentageDecimal ?? 'auto';
|
|
97
|
+
const integratorId = deps.config.VITE_ACROSS_INTEGRATOR_ID || undefined;
|
|
98
|
+
const maybeQuote = await fetchAcrossTrade({
|
|
99
|
+
tradeType: 'exactInput',
|
|
100
|
+
amount: sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
101
|
+
inputToken: getAcrossAssetAddress(sellAsset.assetId),
|
|
102
|
+
outputToken: getAcrossAssetAddress(buyAsset.assetId),
|
|
103
|
+
originChainId: sellAcrossChainId,
|
|
104
|
+
destinationChainId: buyAcrossChainId,
|
|
105
|
+
depositor,
|
|
106
|
+
recipient,
|
|
107
|
+
appFee,
|
|
108
|
+
appFeeRecipient,
|
|
109
|
+
integratorId,
|
|
110
|
+
slippage,
|
|
111
|
+
}, deps.config);
|
|
112
|
+
if (maybeQuote.isErr()) {
|
|
113
|
+
const error = maybeQuote.unwrapErr();
|
|
114
|
+
if (!axios.isAxiosError(error.cause)) {
|
|
115
|
+
return Err(makeSwapErrorRight({
|
|
116
|
+
message: 'Unknown error',
|
|
117
|
+
code: TradeQuoteError.UnknownError,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
const acrossError = error.cause?.response?.data;
|
|
121
|
+
if (!isAcrossError(acrossError)) {
|
|
122
|
+
return Err(makeSwapErrorRight({
|
|
123
|
+
message: 'Unknown error',
|
|
124
|
+
code: TradeQuoteError.UnknownError,
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
const tradeQuoteErrorCode = acrossErrorCodeToTradeQuoteError[acrossError.code];
|
|
128
|
+
return Err(makeSwapErrorRight({
|
|
129
|
+
message: acrossError.message,
|
|
130
|
+
code: tradeQuoteErrorCode ?? TradeQuoteError.UnknownError,
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
const { data: quote } = maybeQuote.unwrap();
|
|
134
|
+
const buyAmountAfterFeesCryptoBaseUnit = quote.expectedOutputAmount;
|
|
135
|
+
const rate = getInputOutputRate({
|
|
136
|
+
sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
137
|
+
buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit,
|
|
138
|
+
sellAsset,
|
|
139
|
+
buyAsset,
|
|
140
|
+
});
|
|
141
|
+
const bridgeFeeAmount = quote.fees.total.amount;
|
|
142
|
+
const bridgeFeeAsset = quote.fees.total.token;
|
|
143
|
+
const buyAssetAcrossAddress = getAcrossAssetAddress(buyAsset.assetId);
|
|
144
|
+
const isBridgeFeeInBuyAsset = bridgeFeeAsset.chainId === buyAcrossChainId &&
|
|
145
|
+
(bridgeFeeAsset.address === buyAssetAcrossAddress ||
|
|
146
|
+
(isEvmChainId(buyAsset.chainId) &&
|
|
147
|
+
bridgeFeeAsset.address.toLowerCase() === buyAssetAcrossAddress.toLowerCase()));
|
|
148
|
+
const buyAmountBeforeFeesCryptoBaseUnit = (() => {
|
|
149
|
+
if (!isBridgeFeeInBuyAsset)
|
|
150
|
+
return buyAmountAfterFeesCryptoBaseUnit;
|
|
151
|
+
const bridgeFeeInBuyAssetPrecision = BigAmount.fromBaseUnit({
|
|
152
|
+
value: convertPrecision({
|
|
153
|
+
value: bridgeFeeAmount,
|
|
154
|
+
inputExponent: bridgeFeeAsset.decimals,
|
|
155
|
+
outputExponent: buyAsset.precision,
|
|
156
|
+
}).toFixed(0),
|
|
157
|
+
precision: buyAsset.precision,
|
|
158
|
+
});
|
|
159
|
+
return BigAmount.fromBaseUnit({
|
|
160
|
+
value: buyAmountAfterFeesCryptoBaseUnit,
|
|
161
|
+
precision: buyAsset.precision,
|
|
162
|
+
})
|
|
163
|
+
.plus(bridgeFeeInBuyAssetPrecision)
|
|
164
|
+
.toBaseUnit();
|
|
165
|
+
})();
|
|
166
|
+
const allowanceContract = isEvmChainId(sellAsset.chainId) ? quote.checks.allowance.spender : '';
|
|
167
|
+
const acrossTransactionMetadata = {
|
|
168
|
+
to: quote.swapTx.to,
|
|
169
|
+
data: quote.swapTx.data,
|
|
170
|
+
value: quote.swapTx.value ?? '0',
|
|
171
|
+
gasLimit: quote.swapTx.gas,
|
|
172
|
+
ecosystem: quote.swapTx.ecosystem,
|
|
173
|
+
quoteId: quote.id,
|
|
174
|
+
};
|
|
175
|
+
// For SVM transactions, decompile the pre-built base64 blob into instructions
|
|
176
|
+
// so getUnsignedSolanaTransaction can rebuild it at execution time
|
|
177
|
+
const maybeSolanaTransactionMetadata = await (async () => {
|
|
178
|
+
if (quote.swapTx.ecosystem !== 'svm')
|
|
179
|
+
return Ok(undefined);
|
|
180
|
+
try {
|
|
181
|
+
const versionedTransaction = VersionedTransaction.deserialize(new Uint8Array(Buffer.from(quote.swapTx.data, 'base64')));
|
|
182
|
+
const adapter = deps.assertGetSolanaChainAdapter(sellAsset.chainId);
|
|
183
|
+
const addressLookupTableAccountKeys = versionedTransaction.message.addressTableLookups.map(lookup => lookup.accountKey.toString());
|
|
184
|
+
const addressLookupTableAccountsInfos = await adapter.getAddressLookupTableAccounts(addressLookupTableAccountKeys);
|
|
185
|
+
const addressLookupTableAccounts = addressLookupTableAccountsInfos.map(info => new AddressLookupTableAccount({
|
|
186
|
+
key: new PublicKey(info.key),
|
|
187
|
+
state: AddressLookupTableAccount.deserialize(new Uint8Array(info.data)),
|
|
188
|
+
}));
|
|
189
|
+
const instructions = TransactionMessage.decompile(versionedTransaction.message, {
|
|
190
|
+
addressLookupTableAccounts,
|
|
191
|
+
}).instructions;
|
|
192
|
+
const computeBudgetProgramId = ComputeBudgetProgram.programId.toString();
|
|
193
|
+
const instructionsWithoutComputeBudget = instructions.filter(instruction => instruction.programId.toString() !== computeBudgetProgramId);
|
|
194
|
+
return Ok({
|
|
195
|
+
instructions: instructionsWithoutComputeBudget,
|
|
196
|
+
addressLookupTableAddresses: addressLookupTableAccountKeys,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
return Err(makeSwapErrorRight({
|
|
201
|
+
message: `[getTrade] Failed to decompile Solana transaction: ${e}`,
|
|
202
|
+
code: TradeQuoteError.UnknownError,
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
})();
|
|
206
|
+
if (maybeSolanaTransactionMetadata.isErr())
|
|
207
|
+
return Err(maybeSolanaTransactionMetadata.unwrapErr());
|
|
208
|
+
const solanaTransactionMetadata = maybeSolanaTransactionMetadata.unwrap();
|
|
209
|
+
const networkFeeCryptoBaseUnit = await (async () => {
|
|
210
|
+
if (solanaTransactionMetadata) {
|
|
211
|
+
const adapter = deps.assertGetSolanaChainAdapter(sellAsset.chainId);
|
|
212
|
+
const { fast } = await adapter.getFeeData({
|
|
213
|
+
to: '',
|
|
214
|
+
value: '0',
|
|
215
|
+
chainSpecific: {
|
|
216
|
+
from: depositor,
|
|
217
|
+
addressLookupTableAccounts: solanaTransactionMetadata.addressLookupTableAddresses,
|
|
218
|
+
instructions: solanaTransactionMetadata.instructions,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
return fast.txFee;
|
|
222
|
+
}
|
|
223
|
+
if (isEvmChainId(sellAsset.chainId)) {
|
|
224
|
+
const adapter = deps.assertGetEvmChainAdapter(sellAsset.chainId);
|
|
225
|
+
const { average } = await adapter.getGasFeeData();
|
|
226
|
+
const supportsEIP1559 = 'maxFeePerGas' in average;
|
|
227
|
+
if (bnOrZero(acrossTransactionMetadata.gasLimit).gt(0)) {
|
|
228
|
+
return evm.calcNetworkFeeCryptoBaseUnit({
|
|
229
|
+
...average,
|
|
230
|
+
supportsEIP1559,
|
|
231
|
+
gasLimit: acrossTransactionMetadata.gasLimit ?? '0',
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const feeData = await evm.getFees({
|
|
236
|
+
adapter,
|
|
237
|
+
data: acrossTransactionMetadata.data,
|
|
238
|
+
to: acrossTransactionMetadata.to,
|
|
239
|
+
value: acrossTransactionMetadata.value,
|
|
240
|
+
from: depositor,
|
|
241
|
+
supportsEIP1559,
|
|
242
|
+
});
|
|
243
|
+
return feeData.networkFeeCryptoBaseUnit;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return quote.fees.originGas.amount;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return quote.fees.originGas.amount;
|
|
250
|
+
})();
|
|
251
|
+
// Build protocol fee asset ID — Across returns its own numeric chain IDs, convert to CAIP
|
|
252
|
+
const bridgeFeeAssetCaipChainId = acrossChainIdToChainId[bridgeFeeAsset.chainId.toString()];
|
|
253
|
+
const bridgeFeeAssetId = (() => {
|
|
254
|
+
if (!bridgeFeeAssetCaipChainId)
|
|
255
|
+
return undefined;
|
|
256
|
+
const isNativeFeeAsset = (() => {
|
|
257
|
+
if (bridgeFeeAssetCaipChainId === solanaChainId) {
|
|
258
|
+
return (bridgeFeeAsset.address === 'So11111111111111111111111111111111111111112' ||
|
|
259
|
+
bridgeFeeAsset.address === zeroAddress);
|
|
260
|
+
}
|
|
261
|
+
return bridgeFeeAsset.address === zeroAddress;
|
|
262
|
+
})();
|
|
263
|
+
if (isNativeFeeAsset)
|
|
264
|
+
return chainIdToFeeAssetId(bridgeFeeAssetCaipChainId);
|
|
265
|
+
const tokenStandard = bridgeFeeAssetCaipChainId.startsWith('solana') ? 'token' : 'erc20';
|
|
266
|
+
return `${bridgeFeeAssetCaipChainId}/${tokenStandard}:${bridgeFeeAsset.address}`;
|
|
267
|
+
})();
|
|
268
|
+
const accountNumber = input.accountNumber;
|
|
269
|
+
const step = {
|
|
270
|
+
allowanceContract,
|
|
271
|
+
rate,
|
|
272
|
+
buyAmountBeforeFeesCryptoBaseUnit,
|
|
273
|
+
buyAmountAfterFeesCryptoBaseUnit,
|
|
274
|
+
sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
275
|
+
buyAsset,
|
|
276
|
+
sellAsset,
|
|
277
|
+
accountNumber,
|
|
278
|
+
feeData: {
|
|
279
|
+
networkFeeCryptoBaseUnit,
|
|
280
|
+
protocolFees: bridgeFeeAssetId
|
|
281
|
+
? {
|
|
282
|
+
[bridgeFeeAssetId]: {
|
|
283
|
+
amountCryptoBaseUnit: bridgeFeeAmount,
|
|
284
|
+
asset: {
|
|
285
|
+
symbol: bridgeFeeAsset.symbol,
|
|
286
|
+
chainId: bridgeFeeAssetCaipChainId,
|
|
287
|
+
precision: bridgeFeeAsset.decimals,
|
|
288
|
+
},
|
|
289
|
+
requiresBalance: false,
|
|
290
|
+
},
|
|
291
|
+
}
|
|
292
|
+
: {},
|
|
293
|
+
},
|
|
294
|
+
source: SwapperName.Across,
|
|
295
|
+
estimatedExecutionTimeMs: quote.expectedFillTime * 1000,
|
|
296
|
+
acrossTransactionMetadata,
|
|
297
|
+
solanaTransactionMetadata,
|
|
298
|
+
};
|
|
299
|
+
const baseQuoteOrRate = {
|
|
300
|
+
id: quote.id,
|
|
301
|
+
rate,
|
|
302
|
+
swapperName: SwapperName.Across,
|
|
303
|
+
affiliateBps,
|
|
304
|
+
slippageTolerancePercentageDecimal: input.slippageTolerancePercentageDecimal,
|
|
305
|
+
};
|
|
306
|
+
if (input.quoteOrRate === 'quote') {
|
|
307
|
+
if (!input.receiveAddress) {
|
|
308
|
+
return Err(makeSwapErrorRight({
|
|
309
|
+
message: 'Receive address is required for quote',
|
|
310
|
+
code: TradeQuoteError.InternalError,
|
|
311
|
+
}));
|
|
312
|
+
}
|
|
313
|
+
const tradeQuote = {
|
|
314
|
+
...baseQuoteOrRate,
|
|
315
|
+
steps: [step],
|
|
316
|
+
receiveAddress: input.receiveAddress,
|
|
317
|
+
quoteOrRate: 'quote',
|
|
318
|
+
};
|
|
319
|
+
return Ok([tradeQuote]);
|
|
320
|
+
}
|
|
321
|
+
const tradeRate = {
|
|
322
|
+
...baseQuoteOrRate,
|
|
323
|
+
steps: [step],
|
|
324
|
+
receiveAddress: recipient,
|
|
325
|
+
quoteOrRate: 'rate',
|
|
326
|
+
};
|
|
327
|
+
return Ok([tradeRate]);
|
|
328
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { Asset } from '@shapeshiftoss/types';
|
|
2
|
+
export type AcrossTradeBaseParams = {
|
|
3
|
+
buyAsset: Asset;
|
|
4
|
+
sellAsset: Asset;
|
|
5
|
+
sellAmountIncludingProtocolFeesCryptoBaseUnit: string;
|
|
6
|
+
affiliateBps: string;
|
|
7
|
+
};
|
|
8
|
+
export type AcrossTradeInputParams<T extends 'rate' | 'quote'> = AcrossTradeBaseParams & {
|
|
9
|
+
quoteOrRate: T;
|
|
10
|
+
receiveAddress: T extends 'rate' ? string | undefined : string;
|
|
11
|
+
sendAddress: T extends 'rate' ? undefined : string;
|
|
12
|
+
accountNumber: T extends 'rate' ? undefined : number;
|
|
13
|
+
slippageTolerancePercentageDecimal?: string;
|
|
14
|
+
};
|
|
15
|
+
export type AcrossTransactionMetadata = {
|
|
16
|
+
to: string;
|
|
17
|
+
data: string;
|
|
18
|
+
value: string;
|
|
19
|
+
gasLimit?: string;
|
|
20
|
+
ecosystem: 'evm' | 'svm';
|
|
21
|
+
quoteId: string;
|
|
22
|
+
};
|
|
23
|
+
export type AcrossTokenInfo = {
|
|
24
|
+
address: string;
|
|
25
|
+
decimals: number;
|
|
26
|
+
symbol: string;
|
|
27
|
+
name: string;
|
|
28
|
+
chainId: number;
|
|
29
|
+
};
|
|
30
|
+
export type AcrossSwapStep = {
|
|
31
|
+
tokenIn: AcrossTokenInfo;
|
|
32
|
+
tokenOut: AcrossTokenInfo;
|
|
33
|
+
inputAmount: string;
|
|
34
|
+
outputAmount: string;
|
|
35
|
+
minOutputAmount: string;
|
|
36
|
+
maxInputAmount: string;
|
|
37
|
+
swapProvider: {
|
|
38
|
+
name: string;
|
|
39
|
+
sources: string[];
|
|
40
|
+
};
|
|
41
|
+
slippage: number;
|
|
42
|
+
};
|
|
43
|
+
export type AcrossFeePart = {
|
|
44
|
+
amount: string;
|
|
45
|
+
pct: string;
|
|
46
|
+
token: AcrossTokenInfo;
|
|
47
|
+
};
|
|
48
|
+
export type AcrossBridgeFeeDetails = {
|
|
49
|
+
type: 'across';
|
|
50
|
+
relayerCapital: AcrossFeePart;
|
|
51
|
+
destinationGas: AcrossFeePart;
|
|
52
|
+
lp: AcrossFeePart;
|
|
53
|
+
};
|
|
54
|
+
export type AcrossBridgeStep = {
|
|
55
|
+
inputAmount: string;
|
|
56
|
+
outputAmount: string;
|
|
57
|
+
tokenIn: AcrossTokenInfo;
|
|
58
|
+
tokenOut: AcrossTokenInfo;
|
|
59
|
+
fees: {
|
|
60
|
+
amount: string;
|
|
61
|
+
pct: string;
|
|
62
|
+
token: AcrossTokenInfo;
|
|
63
|
+
details: AcrossBridgeFeeDetails;
|
|
64
|
+
};
|
|
65
|
+
provider: string;
|
|
66
|
+
};
|
|
67
|
+
export type AcrossFeeBreakdown = {
|
|
68
|
+
amount: string;
|
|
69
|
+
amountUsd: string;
|
|
70
|
+
token: AcrossTokenInfo;
|
|
71
|
+
pct: string;
|
|
72
|
+
details: {
|
|
73
|
+
type: string;
|
|
74
|
+
swapImpact: {
|
|
75
|
+
amount: string;
|
|
76
|
+
amountUsd: string;
|
|
77
|
+
token: AcrossTokenInfo;
|
|
78
|
+
pct: string;
|
|
79
|
+
};
|
|
80
|
+
app: {
|
|
81
|
+
amount: string;
|
|
82
|
+
amountUsd: string;
|
|
83
|
+
pct: string;
|
|
84
|
+
token: AcrossTokenInfo;
|
|
85
|
+
};
|
|
86
|
+
bridge: AcrossBridgeStep['fees'];
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
export type AcrossSwapTx = {
|
|
90
|
+
simulationSuccess: boolean;
|
|
91
|
+
chainId: number;
|
|
92
|
+
to: string;
|
|
93
|
+
data: string;
|
|
94
|
+
value?: string;
|
|
95
|
+
gas?: string;
|
|
96
|
+
maxFeePerGas?: string;
|
|
97
|
+
maxPriorityFeePerGas?: string;
|
|
98
|
+
ecosystem: 'evm' | 'svm';
|
|
99
|
+
};
|
|
100
|
+
export type AcrossSwapApprovalResponse = {
|
|
101
|
+
crossSwapType: 'bridgeableToBridgeable' | 'bridgeableToBridgeableIndirect' | 'bridgeableToAny' | 'anyToBridgeable' | 'anyToAny';
|
|
102
|
+
amountType: string;
|
|
103
|
+
approvalTxns: {
|
|
104
|
+
chainId: number;
|
|
105
|
+
to: string;
|
|
106
|
+
data: string;
|
|
107
|
+
}[];
|
|
108
|
+
swapTx: AcrossSwapTx;
|
|
109
|
+
checks: {
|
|
110
|
+
allowance: {
|
|
111
|
+
token: string;
|
|
112
|
+
spender: string;
|
|
113
|
+
actual: string;
|
|
114
|
+
expected: string;
|
|
115
|
+
};
|
|
116
|
+
balance: {
|
|
117
|
+
token: string;
|
|
118
|
+
actual: string;
|
|
119
|
+
expected: string;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
steps: {
|
|
123
|
+
originSwap?: AcrossSwapStep;
|
|
124
|
+
bridge: AcrossBridgeStep;
|
|
125
|
+
destinationSwap?: AcrossSwapStep;
|
|
126
|
+
};
|
|
127
|
+
inputToken: AcrossTokenInfo;
|
|
128
|
+
outputToken: AcrossTokenInfo;
|
|
129
|
+
refundToken: AcrossTokenInfo;
|
|
130
|
+
inputAmount: string;
|
|
131
|
+
maxInputAmount: string;
|
|
132
|
+
expectedOutputAmount: string;
|
|
133
|
+
minOutputAmount: string;
|
|
134
|
+
fees: {
|
|
135
|
+
total: AcrossFeeBreakdown;
|
|
136
|
+
totalMax: AcrossFeeBreakdown;
|
|
137
|
+
originGas: {
|
|
138
|
+
amount: string;
|
|
139
|
+
amountUsd: string;
|
|
140
|
+
token: AcrossTokenInfo;
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
expectedFillTime: number;
|
|
144
|
+
quoteExpiryTimestamp: number;
|
|
145
|
+
id: string;
|
|
146
|
+
};
|
|
147
|
+
export type AcrossDepositStatus = {
|
|
148
|
+
status: 'filled' | 'pending' | 'expired' | 'refunded' | 'slowFillRequested';
|
|
149
|
+
fillTxnRef?: string;
|
|
150
|
+
destinationChainId: number;
|
|
151
|
+
originChainId: number;
|
|
152
|
+
depositId: number;
|
|
153
|
+
depositTxnRef: string;
|
|
154
|
+
depositRefundTxnRef?: string;
|
|
155
|
+
actionsSucceeded?: boolean;
|
|
156
|
+
pagination: {
|
|
157
|
+
currentIndex: number;
|
|
158
|
+
maxIndex: number;
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
export type AcrossError = {
|
|
162
|
+
type: string;
|
|
163
|
+
code: string;
|
|
164
|
+
status: number;
|
|
165
|
+
message: string;
|
|
166
|
+
id: string;
|
|
167
|
+
};
|
|
168
|
+
export declare const isAcrossError: (error: unknown) => error is AcrossError;
|
|
169
|
+
export declare enum AcrossErrorCode {
|
|
170
|
+
InvalidParam = "INVALID_PARAM",
|
|
171
|
+
AmountTooLow = "AMOUNT_TOO_LOW",
|
|
172
|
+
RouteNotFound = "ROUTE_NOT_FOUND",
|
|
173
|
+
NoBridgeRoutes = "NO_BRIDGE_ROUTES",
|
|
174
|
+
UnsupportedToken = "UNSUPPORTED_TOKEN",
|
|
175
|
+
InsufficientLiquidity = "INSUFFICIENT_LIQUIDITY",
|
|
176
|
+
InternalError = "INTERNAL_ERROR"
|
|
177
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const isAcrossError = (error) => {
|
|
2
|
+
return typeof error === 'object' && error !== null && 'code' in error && 'status' in error;
|
|
3
|
+
};
|
|
4
|
+
export var AcrossErrorCode;
|
|
5
|
+
(function (AcrossErrorCode) {
|
|
6
|
+
AcrossErrorCode["InvalidParam"] = "INVALID_PARAM";
|
|
7
|
+
AcrossErrorCode["AmountTooLow"] = "AMOUNT_TOO_LOW";
|
|
8
|
+
AcrossErrorCode["RouteNotFound"] = "ROUTE_NOT_FOUND";
|
|
9
|
+
AcrossErrorCode["NoBridgeRoutes"] = "NO_BRIDGE_ROUTES";
|
|
10
|
+
AcrossErrorCode["UnsupportedToken"] = "UNSUPPORTED_TOKEN";
|
|
11
|
+
AcrossErrorCode["InsufficientLiquidity"] = "INSUFFICIENT_LIQUIDITY";
|
|
12
|
+
AcrossErrorCode["InternalError"] = "INTERNAL_ERROR";
|
|
13
|
+
})(AcrossErrorCode || (AcrossErrorCode = {}));
|
|
@@ -1,9 +1,33 @@
|
|
|
1
1
|
import { quoteToCalls } from '@avnu/avnu-sdk';
|
|
2
2
|
import { toAddressNList } from '@shapeshiftoss/chain-adapters';
|
|
3
|
-
import { CallData, hash, num } from 'starknet';
|
|
3
|
+
import { CallData, hash, num, validateAndParseAddress } from 'starknet';
|
|
4
4
|
import { checkStarknetSwapStatus, createDefaultStatusResponse, getExecutableTradeStep, isExecutableTradeQuote, } from '../../utils.js';
|
|
5
5
|
import { getTradeQuote } from './swapperApi/getTradeQuote.js';
|
|
6
6
|
import { getTradeRate } from './swapperApi/getTradeRate.js';
|
|
7
|
+
/**
|
|
8
|
+
* Normalize a value to hex format for Starknet RPC
|
|
9
|
+
* Handles various input types: decimal strings, hex strings (with/without 0x), numbers, BigInts
|
|
10
|
+
*/
|
|
11
|
+
const toHexString = (value) => {
|
|
12
|
+
const strValue = String(value);
|
|
13
|
+
// Already a proper hex string with 0x prefix
|
|
14
|
+
if (strValue.startsWith('0x')) {
|
|
15
|
+
return strValue;
|
|
16
|
+
}
|
|
17
|
+
// Check if it looks like a hex string without 0x prefix (contains a-f characters)
|
|
18
|
+
// Starknet addresses and felts often come as hex without 0x prefix
|
|
19
|
+
if (/^[0-9a-fA-F]+$/.test(strValue) && /[a-fA-F]/.test(strValue)) {
|
|
20
|
+
return `0x${strValue}`;
|
|
21
|
+
}
|
|
22
|
+
// Otherwise treat as decimal and convert to hex
|
|
23
|
+
try {
|
|
24
|
+
return num.toHex(strValue);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// If conversion fails, assume it's already hex and add prefix
|
|
28
|
+
return `0x${strValue}`;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
7
31
|
export const avnuApi = {
|
|
8
32
|
getTradeQuote,
|
|
9
33
|
getTradeRate: (input, deps) => {
|
|
@@ -19,16 +43,23 @@ export const avnuApi = {
|
|
|
19
43
|
if (!avnuSpecific.quoteId)
|
|
20
44
|
throw new Error('quoteId is required in avnuSpecific');
|
|
21
45
|
const adapter = assertGetStarknetChainAdapter(sellAsset.chainId);
|
|
46
|
+
// Normalize the from address to ensure consistent format
|
|
47
|
+
// Starknet addresses can have different representations (with/without leading zeros)
|
|
48
|
+
const normalizedFrom = validateAndParseAddress(from);
|
|
22
49
|
// Convert slippage from decimal percentage string to number for AVNU format (e.g., "0.01" = 1%)
|
|
50
|
+
// Use a slightly higher default slippage (2%) to account for quote staleness
|
|
23
51
|
const slippage = slippageTolerancePercentageDecimal
|
|
24
52
|
? parseFloat(slippageTolerancePercentageDecimal)
|
|
25
|
-
: 0.
|
|
53
|
+
: 0.02;
|
|
26
54
|
// Get the swap calls from AVNU SDK
|
|
27
55
|
const { calls: avnuCalls } = await quoteToCalls({
|
|
28
56
|
quoteId: avnuSpecific.quoteId,
|
|
29
57
|
slippage,
|
|
30
|
-
takerAddress:
|
|
58
|
+
takerAddress: normalizedFrom,
|
|
31
59
|
});
|
|
60
|
+
if (!avnuCalls || avnuCalls.length === 0) {
|
|
61
|
+
throw new Error('No swap calls returned from AVNU - quote may have expired');
|
|
62
|
+
}
|
|
32
63
|
// Build the full invoke transaction calldata from AVNU calls
|
|
33
64
|
// Format: [call_array_length, contract1, selector1, calldata_len1, ...calldata1, ...]
|
|
34
65
|
const fullCalldata = [avnuCalls.length.toString()];
|
|
@@ -36,24 +67,21 @@ export const avnuApi = {
|
|
|
36
67
|
const rawCalldata = call.calldata ?? [];
|
|
37
68
|
const calldataArray = Array.isArray(rawCalldata) ? rawCalldata : CallData.compile(rawCalldata);
|
|
38
69
|
const selector = hash.getSelectorFromName(call.entrypoint);
|
|
39
|
-
|
|
70
|
+
// Normalize contract address from AVNU to ensure consistent format
|
|
71
|
+
const normalizedContractAddress = validateAndParseAddress(call.contractAddress);
|
|
72
|
+
fullCalldata.push(normalizedContractAddress, selector, calldataArray.length.toString(), ...calldataArray.map(cd => String(cd)));
|
|
40
73
|
}
|
|
41
|
-
// Format calldata for RPC (convert
|
|
42
|
-
const formattedCalldata = fullCalldata.map(
|
|
43
|
-
if (!data.startsWith('0x')) {
|
|
44
|
-
return num.toHex(data);
|
|
45
|
-
}
|
|
46
|
-
return data;
|
|
47
|
-
});
|
|
74
|
+
// Format calldata for RPC (convert all values to proper hex format)
|
|
75
|
+
const formattedCalldata = fullCalldata.map(toHexString);
|
|
48
76
|
// Get nonce using adapter method (checks deployment status and returns appropriate nonce)
|
|
49
77
|
const chainIdHex = await adapter.getStarknetProvider().getChainId();
|
|
50
|
-
const nonce = await adapter.getNonce(
|
|
78
|
+
const nonce = await adapter.getNonce(normalizedFrom);
|
|
51
79
|
// Estimate fees for the multi-call swap transaction
|
|
52
80
|
const version = '0x3';
|
|
53
81
|
const estimateTx = {
|
|
54
82
|
type: 'INVOKE',
|
|
55
83
|
version,
|
|
56
|
-
sender_address:
|
|
84
|
+
sender_address: normalizedFrom,
|
|
57
85
|
calldata: formattedCalldata,
|
|
58
86
|
signature: [],
|
|
59
87
|
nonce,
|
|
@@ -112,7 +140,7 @@ export const avnuApi = {
|
|
|
112
140
|
};
|
|
113
141
|
// Calculate transaction hash for signing
|
|
114
142
|
const invokeHashInputs = {
|
|
115
|
-
senderAddress:
|
|
143
|
+
senderAddress: normalizedFrom,
|
|
116
144
|
version,
|
|
117
145
|
compiledCalldata: formattedCalldata,
|
|
118
146
|
chainId: chainIdHex,
|
|
@@ -143,7 +171,7 @@ export const avnuApi = {
|
|
|
143
171
|
addressNList: toAddressNList(adapter.getBip44Params({ accountNumber })),
|
|
144
172
|
txHash,
|
|
145
173
|
_txDetails: {
|
|
146
|
-
fromAddress:
|
|
174
|
+
fromAddress: normalizedFrom,
|
|
147
175
|
calldata: formattedCalldata,
|
|
148
176
|
nonce,
|
|
149
177
|
version,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { getQuotes } from '@avnu/avnu-sdk';
|
|
2
2
|
import { bn } from '@shapeshiftoss/utils';
|
|
3
3
|
import { Err, Ok } from '@sniptt/monads';
|
|
4
|
-
import {
|
|
4
|
+
import { validateAndParseAddress } from 'starknet';
|
|
5
5
|
import { v4 as uuid } from 'uuid';
|
|
6
6
|
import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constants.js';
|
|
7
7
|
import { SwapperName, TradeQuoteError } from '../../../types.js';
|
|
8
8
|
import { getInputOutputRate, makeSwapErrorRight } from '../../../utils.js';
|
|
9
|
+
import { getTreasuryAddressFromChainId } from '../../utils/helpers/helpers.js';
|
|
9
10
|
import { AVNU_SUPPORTED_CHAIN_IDS } from '../utils/constants.js';
|
|
10
11
|
import { getTokenAddress } from '../utils/helpers.js';
|
|
11
12
|
export const getTradeQuote = async (input, deps) => {
|
|
@@ -43,11 +44,15 @@ export const getTradeQuote = async (input, deps) => {
|
|
|
43
44
|
try {
|
|
44
45
|
const sellTokenAddress = getTokenAddress(sellAsset);
|
|
45
46
|
const buyTokenAddress = getTokenAddress(buyAsset);
|
|
47
|
+
// Normalize addresses to ensure consistent format across quote and execution
|
|
48
|
+
// Starknet addresses can have different representations (with/without leading zeros)
|
|
49
|
+
const normalizedSendAddress = validateAndParseAddress(sendAddress);
|
|
50
|
+
const normalizedReceiveAddress = validateAndParseAddress(receiveAddress);
|
|
46
51
|
const quotes = await getQuotes({
|
|
47
52
|
sellTokenAddress,
|
|
48
53
|
buyTokenAddress,
|
|
49
54
|
sellAmount: BigInt(sellAmount),
|
|
50
|
-
takerAddress:
|
|
55
|
+
takerAddress: normalizedSendAddress,
|
|
51
56
|
size: 1,
|
|
52
57
|
integratorFees: affiliateBps ? BigInt(affiliateBps) : undefined,
|
|
53
58
|
integratorFeeRecipient: getTreasuryAddressFromChainId(sellAsset.chainId),
|
|
@@ -68,10 +73,10 @@ export const getTradeQuote = async (input, deps) => {
|
|
|
68
73
|
const buyAmountAfterFeesCryptoBaseUnit = bestQuote.buyAmount.toString();
|
|
69
74
|
const sellAdapter = deps.assertGetStarknetChainAdapter(sellAsset.chainId);
|
|
70
75
|
const feeData = await sellAdapter.getFeeData({
|
|
71
|
-
to:
|
|
76
|
+
to: normalizedReceiveAddress,
|
|
72
77
|
value: sellAmount,
|
|
73
78
|
chainSpecific: {
|
|
74
|
-
from:
|
|
79
|
+
from: normalizedSendAddress,
|
|
75
80
|
tokenContractAddress: sellTokenAddress,
|
|
76
81
|
},
|
|
77
82
|
sendMax: false,
|
|
@@ -96,7 +101,7 @@ export const getTradeQuote = async (input, deps) => {
|
|
|
96
101
|
: {};
|
|
97
102
|
const tradeQuote = {
|
|
98
103
|
id: uuid(),
|
|
99
|
-
receiveAddress,
|
|
104
|
+
receiveAddress: normalizedReceiveAddress,
|
|
100
105
|
affiliateBps,
|
|
101
106
|
rate,
|
|
102
107
|
slippageTolerancePercentageDecimal: slippageTolerancePercentageDecimal ??
|