@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.
Files changed (183) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/constants.js +61 -43
  3. package/dist/cjs/swappers/AcrossSwapper/AcrossSwapper.d.ts +2 -0
  4. package/dist/cjs/swappers/AcrossSwapper/AcrossSwapper.js +8 -0
  5. package/dist/cjs/swappers/AcrossSwapper/constant.d.ts +9 -0
  6. package/dist/cjs/swappers/AcrossSwapper/constant.js +48 -0
  7. package/dist/cjs/swappers/AcrossSwapper/endpoints.d.ts +2 -0
  8. package/dist/cjs/swappers/AcrossSwapper/endpoints.js +113 -0
  9. package/dist/cjs/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.d.ts +3 -0
  10. package/dist/cjs/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.js +31 -0
  11. package/dist/cjs/swappers/AcrossSwapper/getTradeRate/getTradeRate.d.ts +3 -0
  12. package/dist/cjs/swappers/AcrossSwapper/getTradeRate/getTradeRate.js +19 -0
  13. package/dist/cjs/swappers/AcrossSwapper/index.d.ts +1 -0
  14. package/dist/cjs/swappers/AcrossSwapper/index.js +5 -0
  15. package/dist/cjs/swappers/AcrossSwapper/utils/acrossService.d.ts +5 -0
  16. package/dist/cjs/swappers/AcrossSwapper/utils/acrossService.js +17 -0
  17. package/dist/cjs/swappers/AcrossSwapper/utils/fetchAcrossTrade.d.ts +19 -0
  18. package/dist/cjs/swappers/AcrossSwapper/utils/fetchAcrossTrade.js +27 -0
  19. package/dist/cjs/swappers/AcrossSwapper/utils/getTrade.d.ts +11 -0
  20. package/dist/cjs/swappers/AcrossSwapper/utils/getTrade.js +335 -0
  21. package/dist/cjs/swappers/AcrossSwapper/utils/types.d.ts +177 -0
  22. package/dist/cjs/swappers/AcrossSwapper/utils/types.js +17 -0
  23. package/dist/cjs/swappers/AvnuSwapper/endpoints.js +42 -14
  24. package/dist/cjs/swappers/AvnuSwapper/swapperApi/getTradeQuote.js +10 -5
  25. package/dist/cjs/swappers/AvnuSwapper/swapperApi/getTradeRate.js +8 -4
  26. package/dist/cjs/swappers/BebopSwapper/utils/helpers/helpers.d.ts +1 -1
  27. package/dist/cjs/swappers/ButterSwap/ButterSwap.js +1 -0
  28. package/dist/cjs/swappers/ButterSwap/endpoints.js +56 -0
  29. package/dist/cjs/swappers/ButterSwap/swapperApi/checkTradeStatus.js +19 -0
  30. package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeQuote.js +27 -14
  31. package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeQuote.test.js +1 -0
  32. package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeRate.js +23 -16
  33. package/dist/cjs/swappers/ButterSwap/swapperApi/getTradeRate.test.js +2 -0
  34. package/dist/cjs/swappers/ButterSwap/types.d.ts +1 -0
  35. package/dist/cjs/swappers/ButterSwap/utils/helpers.js +1 -0
  36. package/dist/cjs/swappers/ButterSwap/xhr.d.ts +2 -2
  37. package/dist/cjs/swappers/ButterSwap/xhr.js +7 -2
  38. package/dist/cjs/swappers/CetusSwapper/endpoints.js +3 -8
  39. package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeQuote.js +1 -1
  40. package/dist/cjs/swappers/CetusSwapper/swapperApi/getTradeRate.js +1 -1
  41. package/dist/cjs/swappers/ChainflipSwapper/utils/getQuoteOrRate.js +11 -5
  42. package/dist/cjs/swappers/ChainflipSwapper/utils/helpers.js +8 -2
  43. package/dist/cjs/swappers/CowSwapper/CowSwapper.js +1 -1
  44. package/dist/cjs/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.js +1 -1
  45. package/dist/cjs/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.test.js +5 -5
  46. package/dist/cjs/swappers/CowSwapper/getCowSwapTradeRate/getCowSwapTradeRate.js +1 -1
  47. package/dist/cjs/swappers/CowSwapper/utils/helpers/helpers.js +8 -2
  48. package/dist/cjs/swappers/NearIntentsSwapper/NearIntentsSwapper.js +1 -0
  49. package/dist/cjs/swappers/NearIntentsSwapper/endpoints.js +31 -0
  50. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +36 -1
  51. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +61 -1
  52. package/dist/cjs/swappers/NearIntentsSwapper/types.d.ts +1 -1
  53. package/dist/cjs/swappers/NearIntentsSwapper/types.js +6 -0
  54. package/dist/cjs/swappers/NearIntentsSwapper/utils/helpers/helpers.js +23 -4
  55. package/dist/cjs/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.js +8 -3
  56. package/dist/cjs/swappers/RelaySwapper/constant.js +26 -1
  57. package/dist/cjs/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.d.ts +1 -1
  58. package/dist/cjs/swappers/RelaySwapper/utils/getTrade.js +13 -2
  59. package/dist/cjs/swappers/RelaySwapper/utils/relayTokenToAssetId.js +100 -0
  60. package/dist/cjs/swappers/RelaySwapper/utils/types.d.ts +15 -0
  61. package/dist/cjs/swappers/StonfiSwapper/StonfiSwapper.d.ts +2 -0
  62. package/dist/cjs/swappers/StonfiSwapper/StonfiSwapper.js +8 -0
  63. package/dist/cjs/swappers/StonfiSwapper/endpoints.d.ts +2 -0
  64. package/dist/cjs/swappers/StonfiSwapper/endpoints.js +227 -0
  65. package/dist/cjs/swappers/StonfiSwapper/index.d.ts +4 -0
  66. package/dist/cjs/swappers/StonfiSwapper/index.js +25 -0
  67. package/dist/cjs/swappers/StonfiSwapper/swapperApi/getTradeQuote.d.ts +2 -0
  68. package/dist/cjs/swappers/StonfiSwapper/swapperApi/getTradeQuote.js +119 -0
  69. package/dist/cjs/swappers/StonfiSwapper/swapperApi/getTradeRate.d.ts +2 -0
  70. package/dist/cjs/swappers/StonfiSwapper/swapperApi/getTradeRate.js +105 -0
  71. package/dist/cjs/swappers/StonfiSwapper/types.d.ts +60 -0
  72. package/dist/cjs/swappers/StonfiSwapper/types.js +5 -0
  73. package/dist/cjs/swappers/StonfiSwapper/utils/constants.d.ts +5 -0
  74. package/dist/cjs/swappers/StonfiSwapper/utils/constants.js +8 -0
  75. package/dist/cjs/swappers/StonfiSwapper/utils/helpers.d.ts +11 -0
  76. package/dist/cjs/swappers/StonfiSwapper/utils/helpers.js +135 -0
  77. package/dist/cjs/swappers/StonfiSwapper/utils/omnistonManager.d.ts +5 -0
  78. package/dist/cjs/swappers/StonfiSwapper/utils/omnistonManager.js +20 -0
  79. package/dist/cjs/swappers/SunioSwapper/endpoints.js +1 -1
  80. package/dist/cjs/swappers/SunioSwapper/utils/getQuoteOrRate.js +4 -3
  81. package/dist/cjs/swappers/ZrxSwapper/utils/helpers/helpers.d.ts +1 -1
  82. package/dist/cjs/swappers/utils/filterBuyAssetsBySellAssetId/filterBuyAssetsBySellAssetId.js +2 -5
  83. package/dist/cjs/swappers/utils/helpers/helpers.js +3 -0
  84. package/dist/cjs/thorchain-utils/getCallDataFromQuote.d.ts +1 -1
  85. package/dist/cjs/thorchain-utils/getL1RateOrQuote.js +30 -5
  86. package/dist/cjs/thorchain-utils/getQuote.js +8 -2
  87. package/dist/cjs/tron-utils/getUnsignedTronTransaction.js +22 -2
  88. package/dist/cjs/types.d.ts +79 -8
  89. package/dist/cjs/types.js +2 -0
  90. package/dist/cjs/utils.d.ts +4 -3
  91. package/dist/cjs/utils.js +35 -7
  92. package/dist/esm/.tsbuildinfo +1 -1
  93. package/dist/esm/constants.js +31 -13
  94. package/dist/esm/swappers/AcrossSwapper/AcrossSwapper.d.ts +2 -0
  95. package/dist/esm/swappers/AcrossSwapper/AcrossSwapper.js +5 -0
  96. package/dist/esm/swappers/AcrossSwapper/constant.d.ts +9 -0
  97. package/dist/esm/swappers/AcrossSwapper/constant.js +42 -0
  98. package/dist/esm/swappers/AcrossSwapper/endpoints.d.ts +2 -0
  99. package/dist/esm/swappers/AcrossSwapper/endpoints.js +107 -0
  100. package/dist/esm/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.d.ts +3 -0
  101. package/dist/esm/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.js +27 -0
  102. package/dist/esm/swappers/AcrossSwapper/getTradeRate/getTradeRate.d.ts +3 -0
  103. package/dist/esm/swappers/AcrossSwapper/getTradeRate/getTradeRate.js +15 -0
  104. package/dist/esm/swappers/AcrossSwapper/index.d.ts +1 -0
  105. package/dist/esm/swappers/AcrossSwapper/index.js +1 -0
  106. package/dist/esm/swappers/AcrossSwapper/utils/acrossService.d.ts +5 -0
  107. package/dist/esm/swappers/AcrossSwapper/utils/acrossService.js +11 -0
  108. package/dist/esm/swappers/AcrossSwapper/utils/fetchAcrossTrade.d.ts +19 -0
  109. package/dist/esm/swappers/AcrossSwapper/utils/fetchAcrossTrade.js +23 -0
  110. package/dist/esm/swappers/AcrossSwapper/utils/getTrade.d.ts +11 -0
  111. package/dist/esm/swappers/AcrossSwapper/utils/getTrade.js +328 -0
  112. package/dist/esm/swappers/AcrossSwapper/utils/types.d.ts +177 -0
  113. package/dist/esm/swappers/AcrossSwapper/utils/types.js +13 -0
  114. package/dist/esm/swappers/AvnuSwapper/endpoints.js +43 -15
  115. package/dist/esm/swappers/AvnuSwapper/swapperApi/getTradeQuote.js +10 -5
  116. package/dist/esm/swappers/AvnuSwapper/swapperApi/getTradeRate.js +8 -4
  117. package/dist/esm/swappers/BebopSwapper/utils/helpers/helpers.d.ts +1 -1
  118. package/dist/esm/swappers/ButterSwap/ButterSwap.js +2 -1
  119. package/dist/esm/swappers/ButterSwap/endpoints.js +56 -0
  120. package/dist/esm/swappers/ButterSwap/swapperApi/checkTradeStatus.js +20 -1
  121. package/dist/esm/swappers/ButterSwap/swapperApi/getTradeQuote.js +29 -16
  122. package/dist/esm/swappers/ButterSwap/swapperApi/getTradeQuote.test.js +1 -0
  123. package/dist/esm/swappers/ButterSwap/swapperApi/getTradeRate.js +25 -18
  124. package/dist/esm/swappers/ButterSwap/swapperApi/getTradeRate.test.js +2 -0
  125. package/dist/esm/swappers/ButterSwap/types.d.ts +1 -0
  126. package/dist/esm/swappers/ButterSwap/utils/helpers.js +1 -0
  127. package/dist/esm/swappers/ButterSwap/xhr.d.ts +2 -2
  128. package/dist/esm/swappers/ButterSwap/xhr.js +8 -3
  129. package/dist/esm/swappers/CetusSwapper/endpoints.js +3 -8
  130. package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeQuote.js +1 -1
  131. package/dist/esm/swappers/CetusSwapper/swapperApi/getTradeRate.js +1 -1
  132. package/dist/esm/swappers/ChainflipSwapper/utils/getQuoteOrRate.js +12 -6
  133. package/dist/esm/swappers/ChainflipSwapper/utils/helpers.js +9 -3
  134. package/dist/esm/swappers/CowSwapper/CowSwapper.js +1 -1
  135. package/dist/esm/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.js +1 -1
  136. package/dist/esm/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.test.js +5 -5
  137. package/dist/esm/swappers/CowSwapper/getCowSwapTradeRate/getCowSwapTradeRate.js +1 -1
  138. package/dist/esm/swappers/CowSwapper/utils/helpers/helpers.js +9 -3
  139. package/dist/esm/swappers/NearIntentsSwapper/NearIntentsSwapper.js +2 -1
  140. package/dist/esm/swappers/NearIntentsSwapper/endpoints.js +31 -0
  141. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +36 -1
  142. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +61 -1
  143. package/dist/esm/swappers/NearIntentsSwapper/types.d.ts +1 -1
  144. package/dist/esm/swappers/NearIntentsSwapper/types.js +6 -0
  145. package/dist/esm/swappers/NearIntentsSwapper/utils/helpers/helpers.js +23 -4
  146. package/dist/esm/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.js +8 -3
  147. package/dist/esm/swappers/RelaySwapper/constant.js +28 -3
  148. package/dist/esm/swappers/RelaySwapper/utils/getRelayDefaultUserAddress.d.ts +1 -1
  149. package/dist/esm/swappers/RelaySwapper/utils/getTrade.js +13 -2
  150. package/dist/esm/swappers/RelaySwapper/utils/relayTokenToAssetId.js +100 -0
  151. package/dist/esm/swappers/RelaySwapper/utils/types.d.ts +15 -0
  152. package/dist/esm/swappers/StonfiSwapper/StonfiSwapper.d.ts +2 -0
  153. package/dist/esm/swappers/StonfiSwapper/StonfiSwapper.js +5 -0
  154. package/dist/esm/swappers/StonfiSwapper/endpoints.d.ts +2 -0
  155. package/dist/esm/swappers/StonfiSwapper/endpoints.js +224 -0
  156. package/dist/esm/swappers/StonfiSwapper/index.d.ts +4 -0
  157. package/dist/esm/swappers/StonfiSwapper/index.js +4 -0
  158. package/dist/esm/swappers/StonfiSwapper/swapperApi/getTradeQuote.d.ts +2 -0
  159. package/dist/esm/swappers/StonfiSwapper/swapperApi/getTradeQuote.js +115 -0
  160. package/dist/esm/swappers/StonfiSwapper/swapperApi/getTradeRate.d.ts +2 -0
  161. package/dist/esm/swappers/StonfiSwapper/swapperApi/getTradeRate.js +101 -0
  162. package/dist/esm/swappers/StonfiSwapper/types.d.ts +60 -0
  163. package/dist/esm/swappers/StonfiSwapper/types.js +2 -0
  164. package/dist/esm/swappers/StonfiSwapper/utils/constants.d.ts +5 -0
  165. package/dist/esm/swappers/StonfiSwapper/utils/constants.js +5 -0
  166. package/dist/esm/swappers/StonfiSwapper/utils/helpers.d.ts +11 -0
  167. package/dist/esm/swappers/StonfiSwapper/utils/helpers.js +124 -0
  168. package/dist/esm/swappers/StonfiSwapper/utils/omnistonManager.d.ts +5 -0
  169. package/dist/esm/swappers/StonfiSwapper/utils/omnistonManager.js +17 -0
  170. package/dist/esm/swappers/SunioSwapper/endpoints.js +1 -1
  171. package/dist/esm/swappers/SunioSwapper/utils/getQuoteOrRate.js +5 -4
  172. package/dist/esm/swappers/ZrxSwapper/utils/helpers/helpers.d.ts +1 -1
  173. package/dist/esm/swappers/utils/filterBuyAssetsBySellAssetId/filterBuyAssetsBySellAssetId.js +2 -5
  174. package/dist/esm/swappers/utils/helpers/helpers.js +4 -1
  175. package/dist/esm/thorchain-utils/getCallDataFromQuote.d.ts +1 -1
  176. package/dist/esm/thorchain-utils/getL1RateOrQuote.js +31 -6
  177. package/dist/esm/thorchain-utils/getQuote.js +9 -3
  178. package/dist/esm/tron-utils/getUnsignedTronTransaction.js +22 -2
  179. package/dist/esm/types.d.ts +79 -8
  180. package/dist/esm/types.js +2 -0
  181. package/dist/esm/utils.d.ts +4 -3
  182. package/dist/esm/utils.js +34 -7
  183. 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.01;
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: from,
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
- fullCalldata.push(call.contractAddress, selector, calldataArray.length.toString(), ...calldataArray.map(cd => cd.toString()));
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 numbers to hex)
42
- const formattedCalldata = fullCalldata.map(data => {
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(from);
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: from,
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: from,
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: from,
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 { getTreasuryAddressFromChainId } from 'packages/swapper/src/swappers/utils/helpers/helpers';
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: sendAddress,
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: receiveAddress,
76
+ to: normalizedReceiveAddress,
72
77
  value: sellAmount,
73
78
  chainSpecific: {
74
- from: sendAddress,
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 ??