@imtbl/sdk 1.78.0-alpha.3 → 1.78.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.
Files changed (58) hide show
  1. package/dist/{blockchain_data-Dtwb301L.js → blockchain_data-DBsuvv16.js} +2 -2
  2. package/dist/blockchain_data.js +3 -3
  3. package/dist/browser/checkout/{AddTokensWidget-phRi8isq.js → AddTokensWidget-TbB4PmLW.js} +555 -237
  4. package/dist/browser/checkout/{BridgeWidget-DqBqp6iY.js → BridgeWidget-BhrCocw2.js} +7 -6
  5. package/dist/browser/checkout/{CommerceWidget-BakoRfZj.js → CommerceWidget-Be5DHzdC.js} +15 -13
  6. package/dist/browser/checkout/{FeesBreakdown-BeOGIvgY.js → FeesBreakdown-Ck9YzdSC.js} +1 -1
  7. package/dist/browser/checkout/HandoverContent-DB23bXer.js +23 -0
  8. package/dist/browser/checkout/OnRampWidget-CqVk3g3X.js +1635 -0
  9. package/dist/browser/checkout/{SaleWidget-B6lZ8hGg.js → SaleWidget-BN4NOhbW.js} +169 -10
  10. package/dist/browser/checkout/{SpendingCapHero-DW6X8cGr.js → SpendingCapHero-CPvvFrji.js} +1 -1
  11. package/dist/browser/checkout/{SwapWidget-UFkXHv2L.js → SwapWidget-CwFYyKSt.js} +7 -7
  12. package/dist/browser/checkout/TokenImage-BKsKixKT.js +45 -0
  13. package/dist/browser/checkout/{TopUpView-BoCJELBq.js → TopUpView-B4EZHPvY.js} +1 -1
  14. package/dist/browser/checkout/{WalletApproveHero-BxkUQIWN.js → WalletApproveHero--5umxYJp.js} +3 -3
  15. package/dist/browser/checkout/{WalletWidget-C8iWV1OY.js → WalletWidget-DVqX_Zms.js} +3 -3
  16. package/dist/browser/checkout/{auto-track-Densi2-k.js → auto-track-B82Gtc5w.js} +1 -1
  17. package/dist/browser/checkout/{index-BWF1wMhZ.js → index-AFLAGIsQ.js} +1 -1
  18. package/dist/browser/checkout/{index-DSuIrOmV.js → index-Ba4jcykl.js} +1 -1
  19. package/dist/browser/checkout/{index-CGZ3kTp1.js → index-Bkd29y5M.js} +2 -2
  20. package/dist/browser/checkout/{index-fbNLtQZy.js → index-BsW6upSH.js} +1 -1
  21. package/dist/browser/checkout/{index-Djor6tHu.js → index-C0AbsZnL.js} +1 -1
  22. package/dist/browser/checkout/{index-CJ-HyQqV.js → index-CE6PQBKk.js} +1 -1
  23. package/dist/browser/checkout/{index-Bz7QkIrz.js → index-CwvzJ6GW.js} +1 -1
  24. package/dist/browser/checkout/index-Smb16jHM.js +885 -0
  25. package/dist/browser/checkout/{index-C-4DEd8a.js → index-uoFT0va2.js} +1878 -5716
  26. package/dist/browser/checkout/{index.umd-BI-_-mXw.js → index.umd-DHRFESK1.js} +1 -1
  27. package/dist/browser/checkout/sdk.js +4 -4
  28. package/dist/browser/checkout/{useInterval-CfVGG_W7.js → useInterval-DoDljSUa.js} +1 -1
  29. package/dist/browser/checkout/widgets-esm.js +1 -1
  30. package/dist/browser/checkout/widgets.js +6026 -7124
  31. package/dist/{checkout-BymciB9v.js → checkout-BX4BMX31.js} +6 -6
  32. package/dist/checkout.js +5 -5
  33. package/dist/{config-BskomF73.js → config-d4_WLWVX.js} +1 -1
  34. package/dist/config.js +1 -1
  35. package/dist/{index-BoeSZBBL.js → index-BFY7ef9q.js} +1 -1
  36. package/dist/{index-B44hBOSm.js → index-BOa7TPFI.js} +1 -1
  37. package/dist/{index-nAn9d-qB.js → index-BVC9tGIa.js} +4 -4
  38. package/dist/{index-BJV9b4fG.js → index-BbCunE2k.js} +3 -3
  39. package/dist/{index-gGENaQms.js → index-Bd-KXMjV.js} +1 -1
  40. package/dist/{index-DwQ8JxG1.js → index-Dvk4T7xM.js} +1 -1
  41. package/dist/index.browser.js +4 -4
  42. package/dist/index.browser.js.map +1 -1
  43. package/dist/index.cjs +8 -8
  44. package/dist/index.js +14 -14
  45. package/dist/{minting_backend-B6smE-5l.js → minting_backend-DSYjNnIR.js} +3 -3
  46. package/dist/minting_backend.js +5 -5
  47. package/dist/{orderbook-CqH1yhJO.js → orderbook-y-wZqTX7.js} +1 -1
  48. package/dist/orderbook.js +2 -2
  49. package/dist/{passport-Cxq9DY_u.js → passport-Hck6nDL8.js} +3 -3
  50. package/dist/passport.js +4 -4
  51. package/dist/version.json +1 -1
  52. package/dist/{webhook-DfGoH4cH.js → webhook-ByqpJy1j.js} +1 -1
  53. package/dist/webhook.js +2 -2
  54. package/dist/{x-CCG4tF0S.js → x-U0dfyoEE.js} +3 -3
  55. package/dist/x.js +4 -4
  56. package/package.json +1 -1
  57. package/dist/browser/checkout/OnRampWidget-BID9kc9s.js +0 -293
  58. package/dist/browser/checkout/TokenImage-DtkH2h2e.js +0 -17
@@ -1,6 +1,8 @@
1
- import { r as reactExports, I as IMTBLWidgetEvents, A as AddTokensEventType, y as commonjsGlobal, z as getDefaultExportFromCjs, p as jsx, q as jsxs, D as Box, u as useTranslation, F as Drawer, G as Divider, H as Heading, J as Button, K as useProvidersContext, M as useAnalytics, N as WalletDrawer, U as UnableToConnectDrawer, Q as Fragment, R as WalletProviderRdns, T as UserJourney, X as removeSpace, Y as getProviderSlugFromRdns, Z as Web3Provider, _ as isPassportProvider, $ as connectEIP6963Provider, a0 as identifyUser, a1 as ConnectEIP6963ProviderError, a2 as ProvidersContextActions, a3 as MenuItem, a4 as SQUID_NATIVE_TOKEN, k as ChainId, a5 as commonjsRequire, a6 as getRemoteImage, a7 as vFlex, a8 as OnboardingPagination, a9 as ViewContext, aa as useHandover, ab as HandoverTarget, ac as EventTargetContext, V as ViewActions, ad as getRemoteRive, ae as APPROVE_TXN_ANIMATION, af as RiveStateMachineInput, ag as HandoverContent, ah as keyframes, ai as getDefaultTokenImage, aj as TokenFilterTypes, i as getL2ChainId, ak as isNativeToken, al as getTokenImageByAddress, am as TOKEN_PRIORITY_ORDER, an as SmartClone, ao as FramedImage, ap as FramedIcon, aq as ButtCon, ar as TextInput, as as VerticalMenu, at as Body, au as EllipsizedText, av as Tooltip, aw as Sticker, ax as getDurationFormatted, ay as getTotalRouteFees, az as getRouteAndTokenBalances, aA as Stack, aB as hFlex, aC as centerFlexChildren, aD as Icon, aE as Badge, aF as getFormattedAmounts, aG as getRemoteVideo, aH as FramedVideo, aI as listVariants, aJ as motion, aK as listItemVariants, aL as Banner, aM as fetchRiskAssessment, aN as isAddressSanctioned, aO as useRoutes, aP as useInjectedProviders, aQ as trackFlow, aR as RouteError, aS as HeroFormControl, aT as SimpleLayout, aU as dist, n as SharedViews, aV as orchestrationEvents, aW as HeroTextInput, aX as SquidFooter, aY as TOOLKIT_SQUID_URL, aZ as merge, a_ as SvgIcon, a$ as WalletWarningHero, b0 as getFormattedNumber, b1 as FIXED_HANDOVER_DURATION, b2 as EXECUTE_TXN_ANIMATION, b3 as Link, b4 as Trans, b5 as useExecute, b6 as getRouteChains, b7 as t, b8 as convertToNetworkChangeableProvider, b9 as verifyAndSwitchChain, ba as PriceDisplay, L as LoadingView, bb as useTheme, bc as viewReducer, bd as initialViewState, be as useSquid, bf as useTokens, bg as v4, bh as Environment, bi as isValidAddress, bj as amountInputValidation, bk as fetchChains, bl as fetchBalances, bm as CloudImage, E as ErrorView, bn as ServiceUnavailableErrorView } from './index-C-4DEd8a.js';
2
- import { T as TokenImage } from './TokenImage-DtkH2h2e.js';
3
- import { F as FeesBreakdown } from './FeesBreakdown-BeOGIvgY.js';
1
+ import { r as reactExports, I as IMTBLWidgetEvents, A as AddTokensEventType, y as commonjsGlobal, z as getDefaultExportFromCjs, D as parseUnits, F as BigNumber, G as useProvidersContext, H as dist, J as formatUnits, K as isPassportProvider, M as SQUID_NATIVE_TOKEN, p as jsx, q as jsxs, N as Box, u as useTranslation, Q as Drawer, R as Divider, T as Heading, U as Button, X as useAnalytics, Y as WalletDrawer, Z as UnableToConnectDrawer, _ as Fragment, $ as WalletProviderRdns, a0 as UserJourney, a1 as removeSpace, a2 as getProviderSlugFromRdns, a3 as Web3Provider, a4 as connectEIP6963Provider, a5 as identifyUser, a6 as ConnectEIP6963ProviderError, a7 as ProvidersContextActions, a8 as MenuItem, k as ChainId, a9 as commonjsRequire, aa as getRemoteImage, ab as vFlex, ac as OnboardingPagination, ad as ViewContext, ae as useHandover, af as HandoverTarget, ag as EventTargetContext, ah as AddTokensErrorTypes, V as ViewActions, ai as getRemoteRive, aj as APPROVE_TXN_ANIMATION, ak as keyframes, al as getDefaultTokenImage, am as TokenFilterTypes, i as getL2ChainId, an as isNativeToken, ao as getTokenImageByAddress, ap as TOKEN_PRIORITY_ORDER, aq as AddTokensActions, ar as SmartClone, as as FramedImage, at as FramedIcon, au as ButtCon, av as TextInput, aw as VerticalMenu, ax as Body, ay as EllipsizedText, az as Tooltip, aA as Sticker, aB as getRouteAndTokenBalances, aC as getRemoteVideo, aD as Stack, aE as fetchRiskAssessment, aF as isAddressSanctioned, aG as AddTokensContext, aH as useInjectedProviders, aI as trackFlow, aJ as AddTokensExperiments, aK as HeroFormControl, aL as getFormattedAmounts, aM as SimpleLayout, n as SharedViews, aN as orchestrationEvents, aO as HeroTextInput, aP as SquidFooter, aQ as RouteOptionsDrawer, aR as TOOLKIT_SQUID_URL, aS as merge, aT as SvgIcon, aU as trackError, aV as MaxUint256, aW as WalletWarningHero, aX as getFormattedNumber, aY as FIXED_HANDOVER_DURATION, aZ as EXECUTE_TXN_ANIMATION, a_ as Link, a$ as Trans, b0 as getTotalRouteFees, b1 as hFlex, b2 as t, b3 as getDurationFormatted, b4 as PriceDisplay, L as LoadingView, b5 as Icon, b6 as useTheme, b7 as viewReducer, b8 as initialViewState, b9 as addTokensReducer, ba as initialAddTokensState, bb as useSquid, bc as useTokens, bd as v4, be as Environment, bf as isValidAddress, bg as amountInputValidation, bh as fetchChains, bi as CloudImage, E as ErrorView, bj as ServiceUnavailableErrorView } from './index-uoFT0va2.js';
2
+ import { r as retry, T as TokenImage } from './TokenImage-BKsKixKT.js';
3
+ import { H as HandoverContent } from './HandoverContent-DB23bXer.js';
4
+ import { C as Contract } from './index-Smb16jHM.js';
5
+ import { F as FeesBreakdown } from './FeesBreakdown-Ck9YzdSC.js';
4
6
 
5
7
  /**
6
8
  * The function `useInterval` sets up an interval that repeatedly calls a given callback function with
@@ -89,108 +91,6 @@ const sendAddTokensFailedEvent = (eventTarget, reason) => {
89
91
  eventTarget.dispatchEvent(failedEvent);
90
92
  };
91
93
 
92
- const initialAddTokensState = {
93
- id: '',
94
- allowedTokens: null,
95
- squid: null,
96
- chains: null,
97
- balances: null,
98
- tokens: null,
99
- routes: [],
100
- selectedRouteData: undefined,
101
- selectedToken: undefined,
102
- selectedAmount: '',
103
- isSwapAvailable: undefined,
104
- experiments: undefined,
105
- };
106
- var AddTokensActions;
107
- (function (AddTokensActions) {
108
- AddTokensActions["SET_ID"] = "SET_ID";
109
- AddTokensActions["SET_ALLOWED_TOKENS"] = "SET_ALLOWED_TOKENS";
110
- AddTokensActions["SET_SQUID"] = "SET_SQUID";
111
- AddTokensActions["SET_CHAINS"] = "SET_CHAINS";
112
- AddTokensActions["SET_BALANCES"] = "SET_BALANCES";
113
- AddTokensActions["SET_TOKENS"] = "SET_TOKENS";
114
- AddTokensActions["SET_ROUTES"] = "SET_ROUTES";
115
- AddTokensActions["SET_SELECTED_ROUTE_DATA"] = "SET_SELECTED_ROUTE_DATA";
116
- AddTokensActions["SET_SELECTED_TOKEN"] = "SET_SELECTED_TOKEN";
117
- AddTokensActions["SET_SELECTED_AMOUNT"] = "SET_SELECTED_AMOUNT";
118
- AddTokensActions["SET_IS_SWAP_AVAILABLE"] = "SET_IS_SWAP_AVAILABLE";
119
- AddTokensActions["SET_EXPERIMENTS"] = "SET_EXPERIMENTS";
120
- })(AddTokensActions || (AddTokensActions = {}));
121
- // eslint-disable-next-line @typescript-eslint/naming-convention
122
- const AddTokensContext = reactExports.createContext({
123
- addTokensState: initialAddTokensState,
124
- addTokensDispatch: () => { },
125
- });
126
- AddTokensContext.displayName = 'AddTokensContext';
127
- const addTokensReducer = (state, action) => {
128
- switch (action.payload.type) {
129
- case AddTokensActions.SET_ID:
130
- return {
131
- ...state,
132
- id: action.payload.id,
133
- };
134
- case AddTokensActions.SET_ALLOWED_TOKENS:
135
- return {
136
- ...state,
137
- allowedTokens: action.payload.allowedTokens,
138
- };
139
- case AddTokensActions.SET_SQUID:
140
- return {
141
- ...state,
142
- squid: action.payload.squid,
143
- };
144
- case AddTokensActions.SET_CHAINS:
145
- return {
146
- ...state,
147
- chains: action.payload.chains,
148
- };
149
- case AddTokensActions.SET_BALANCES:
150
- return {
151
- ...state,
152
- balances: action.payload.balances,
153
- };
154
- case AddTokensActions.SET_TOKENS:
155
- return {
156
- ...state,
157
- tokens: action.payload.tokens,
158
- };
159
- case AddTokensActions.SET_ROUTES:
160
- return {
161
- ...state,
162
- routes: action.payload.routes,
163
- };
164
- case AddTokensActions.SET_SELECTED_ROUTE_DATA:
165
- return {
166
- ...state,
167
- selectedRouteData: action.payload.selectedRouteData,
168
- };
169
- case AddTokensActions.SET_SELECTED_TOKEN:
170
- return {
171
- ...state,
172
- selectedToken: action.payload.selectedToken,
173
- };
174
- case AddTokensActions.SET_SELECTED_AMOUNT:
175
- return {
176
- ...state,
177
- selectedAmount: action.payload.selectedAmount,
178
- };
179
- case AddTokensActions.SET_IS_SWAP_AVAILABLE:
180
- return {
181
- ...state,
182
- isSwapAvailable: action.payload.isSwapAvailable,
183
- };
184
- case AddTokensActions.SET_EXPERIMENTS:
185
- return {
186
- ...state,
187
- experiments: action.payload.experiments,
188
- };
189
- default:
190
- return state;
191
- }
192
- };
193
-
194
94
  var AddTokensWidgetViews;
195
95
  (function (AddTokensWidgetViews) {
196
96
  AddTokensWidgetViews["ADD_TOKENS"] = "ADD_TOKENS";
@@ -578,30 +478,290 @@ var lodash_debounce = debounce;
578
478
 
579
479
  var debounce$1 = /*@__PURE__*/getDefaultExportFromCjs(lodash_debounce);
580
480
 
581
- var FiatOptionType;
582
- (function (FiatOptionType) {
583
- FiatOptionType["CREDIT"] = "credit";
584
- FiatOptionType["DEBIT"] = "debit";
585
- })(FiatOptionType || (FiatOptionType = {}));
586
- var AddTokensErrorTypes;
587
- (function (AddTokensErrorTypes) {
588
- AddTokensErrorTypes["DEFAULT"] = "DEFAULT_ERROR";
589
- AddTokensErrorTypes["INVALID_PARAMETERS"] = "INVALID_PARAMETERS";
590
- AddTokensErrorTypes["SERVICE_BREAKDOWN"] = "SERVICE_BREAKDOWN";
591
- AddTokensErrorTypes["TRANSACTION_FAILED"] = "TRANSACTION_FAILED";
592
- AddTokensErrorTypes["UNRECOGNISED_CHAIN"] = "UNRECOGNISED_CHAIN";
593
- AddTokensErrorTypes["PROVIDER_ERROR"] = "PROVIDER_ERROR";
594
- AddTokensErrorTypes["WALLET_FAILED"] = "WALLET_FAILED";
595
- AddTokensErrorTypes["WALLET_REJECTED"] = "WALLET_REJECTED";
596
- AddTokensErrorTypes["WALLET_REJECTED_NO_FUNDS"] = "WALLET_REJECTED_NO_FUNDS";
597
- AddTokensErrorTypes["WALLET_POPUP_BLOCKED"] = "WALLET_POPUP_BLOCKED";
598
- AddTokensErrorTypes["ENVIRONMENT_ERROR"] = "ENVIRONMENT_ERROR";
599
- AddTokensErrorTypes["ROUTE_ERROR"] = "ROUTE_ERROR";
600
- })(AddTokensErrorTypes || (AddTokensErrorTypes = {}));
601
- var AddTokensExperiments;
602
- (function (AddTokensExperiments) {
603
- AddTokensExperiments["PRESELECTED_TOKEN"] = "addTokensPreselectedToken";
604
- })(AddTokensExperiments || (AddTokensExperiments = {}));
481
+ const delay = (ms) => new Promise((resolve) => {
482
+ setTimeout(resolve, ms);
483
+ });
484
+
485
+ const sortRoutesByFastestTime = (routes) => {
486
+ if (!routes)
487
+ return [];
488
+ return routes.slice().sort((a, b) => {
489
+ // Prioritize isInsufficientGas = false
490
+ if (a.isInsufficientGas !== b.isInsufficientGas) {
491
+ return a.isInsufficientGas ? 1 : -1;
492
+ }
493
+ // Sort by estimatedRouteDuration if isInsufficientGas is the same
494
+ const timeA = a.route.route.estimate.estimatedRouteDuration;
495
+ const timeB = b.route.route.estimate.estimatedRouteDuration;
496
+ return timeA - timeB;
497
+ });
498
+ };
499
+
500
+ const findToken = (tokens, address, chainId) => tokens.find((value) => value.address.toLowerCase() === address.toLowerCase()
501
+ && value.chainId === chainId);
502
+
503
+ const isRouteToAmountGreaterThanToAmount = (routeResponse, toAmount) => {
504
+ if (!routeResponse?.route?.estimate?.toAmount || !routeResponse?.route?.estimate?.toToken?.decimals) {
505
+ throw new Error('Invalid route response or token decimals');
506
+ }
507
+ const toAmountInBaseUnits = parseUnits(toAmount, routeResponse?.route.estimate.toToken.decimals);
508
+ const routeToAmountInBaseUnits = BigNumber.from(routeResponse.route.estimate.toAmount);
509
+ return routeToAmountInBaseUnits.gt(toAmountInBaseUnits);
510
+ };
511
+
512
+ const BASE_SLIPPAGE_HIGH_TIER = 0.005;
513
+ const BASE_SLIPPAGE_MEDIUM_TIER = 0.01;
514
+ const BASE_SLIPPAGE_LOW_TIER = 0.015;
515
+ const SLIPPAGE_TIERS = {
516
+ high: {
517
+ threshold: 999,
518
+ value: BASE_SLIPPAGE_HIGH_TIER,
519
+ },
520
+ medium: {
521
+ threshold: 99,
522
+ value: BASE_SLIPPAGE_MEDIUM_TIER,
523
+ },
524
+ low: {
525
+ threshold: 0,
526
+ value: BASE_SLIPPAGE_LOW_TIER,
527
+ },
528
+ };
529
+ /**
530
+ * Hook to calculate slippage based on thresholds
531
+ */
532
+ const useSlippage = () => {
533
+ const getSlippageTier = (usdAmount) => {
534
+ if (usdAmount >= SLIPPAGE_TIERS.high.threshold)
535
+ return SLIPPAGE_TIERS.high.value;
536
+ if (usdAmount >= SLIPPAGE_TIERS.medium.threshold)
537
+ return SLIPPAGE_TIERS.medium.value;
538
+ return SLIPPAGE_TIERS.low.value;
539
+ };
540
+ const calculateAdjustedAmount = (baseAmount, usdAmount, additionalBuffer = 0) => {
541
+ const slippage = getSlippageTier(usdAmount);
542
+ return baseAmount * (1 + slippage + additionalBuffer);
543
+ };
544
+ return reactExports.useMemo(() => ({
545
+ getSlippageTier,
546
+ calculateAdjustedAmount,
547
+ }), []);
548
+ };
549
+
550
+ /**
551
+ * Hook to handle route amount calculations.
552
+ */
553
+ const useRouteCalculation = () => {
554
+ const { calculateAdjustedAmount } = useSlippage();
555
+ /**
556
+ * Calculate the fromAmount based on USD prices and slippage.
557
+ */
558
+ const calculateFromAmount = (fromToken, toToken, toAmount, additionalBuffer = 0) => {
559
+ const toAmountNumber = parseFloat(toAmount);
560
+ // Calculate the USD value of the toAmount
561
+ const toAmountInUsd = toAmountNumber * toToken.usdPrice;
562
+ // Calculate the amount of fromToken needed to match this USD value
563
+ const baseFromAmount = toAmountInUsd / fromToken.usdPrice;
564
+ // Add a buffer for price fluctuations and fees
565
+ const fromAmountWithBuffer = calculateAdjustedAmount(baseFromAmount, toAmountInUsd, additionalBuffer);
566
+ return fromAmountWithBuffer.toString();
567
+ };
568
+ /**
569
+ * Calculate the fromAmount using exchange rate returned from the route.
570
+ */
571
+ const calculateFromAmountFromRoute = (exchangeRate, toAmount, toAmountUSD) => {
572
+ const toAmountUSDNumber = toAmountUSD ? parseFloat(toAmountUSD) : 0;
573
+ const fromAmount = parseFloat(toAmount) / parseFloat(exchangeRate);
574
+ const fromAmountWithBuffer = calculateAdjustedAmount(fromAmount, toAmountUSDNumber);
575
+ return fromAmountWithBuffer.toString();
576
+ };
577
+ /**
578
+ * Convert a string amount to a formatted amount with the specified number of decimals.
579
+ */
580
+ const convertToFormattedFromAmount = (amount, decimals) => {
581
+ const parsedFromAmount = parseFloat(amount).toFixed(decimals);
582
+ const formattedFromAmount = parseUnits(parsedFromAmount, decimals);
583
+ return formattedFromAmount.toString();
584
+ };
585
+ return reactExports.useMemo(() => ({
586
+ calculateFromAmount,
587
+ calculateFromAmountFromRoute,
588
+ convertToFormattedFromAmount,
589
+ }), []);
590
+ };
591
+
592
+ class RouteError extends Error {
593
+ message;
594
+ data;
595
+ constructor(message, data) {
596
+ super(message);
597
+ this.message = message;
598
+ this.data = data;
599
+ Object.setPrototypeOf(this, RouteError.prototype);
600
+ }
601
+ }
602
+
603
+ const useRoutes = () => {
604
+ const latestRequestIdRef = reactExports.useRef(0);
605
+ const { providersState: { toProvider, }, } = useProvidersContext();
606
+ const { calculateFromAmount, calculateFromAmountFromRoute, convertToFormattedFromAmount } = useRouteCalculation();
607
+ const getFromAmountData = (tokens, balance, toAmount, toChainId, toTokenAddress, additionalBuffer = 0) => {
608
+ const fromToken = findToken(tokens, balance.address, balance.chainId.toString());
609
+ const toToken = findToken(tokens, toTokenAddress, toChainId);
610
+ if (!fromToken || !toToken) {
611
+ return undefined;
612
+ }
613
+ return {
614
+ fromToken,
615
+ fromAmount: calculateFromAmount(fromToken, toToken, toAmount, additionalBuffer),
616
+ toToken,
617
+ toAmount,
618
+ balance,
619
+ additionalBuffer,
620
+ };
621
+ };
622
+ const getSufficientFromAmounts = (tokens, balances, toChainId, toTokenAddress, toAmount) => {
623
+ const filteredBalances = balances.filter((balance) => !(balance.address.toLowerCase() === toTokenAddress.toLowerCase()
624
+ && balance.chainId === toChainId));
625
+ const fromAmountDataArray = filteredBalances
626
+ .map((balance) => getFromAmountData(tokens, balance, toAmount, toChainId, toTokenAddress))
627
+ .filter((value) => value !== undefined);
628
+ return fromAmountDataArray.filter((data) => {
629
+ const formattedBalance = formatUnits(data.balance.balance, data.balance.decimals);
630
+ return (parseFloat(formattedBalance.toString()) > parseFloat(data.fromAmount));
631
+ });
632
+ };
633
+ const getRouteWithRetry = async (squid, fromToken, toToken, toAddress, fromAmount, fromAddress, quoteOnly = true, postHook) => retry(() => squid.getRoute({
634
+ fromChain: fromToken.chainId,
635
+ fromToken: fromToken.address,
636
+ fromAmount: convertToFormattedFromAmount(fromAmount, fromToken.decimals),
637
+ toChain: toToken.chainId,
638
+ toToken: toToken.address,
639
+ fromAddress,
640
+ toAddress,
641
+ quoteOnly,
642
+ enableBoost: true,
643
+ receiveGasOnDestination: !isPassportProvider(toProvider),
644
+ postHook,
645
+ }), {
646
+ retryIntervalMs: 1000,
647
+ retries: 5,
648
+ nonRetryable: (err) => err.response?.status !== 429,
649
+ });
650
+ const getRoute = async (squid, fromToken, toToken, toAddress, fromAmount, toAmount, fromAddress, quoteOnly = true, postHook) => {
651
+ const routeResponse = await getRouteWithRetry(squid, fromToken, toToken, toAddress, fromAmount, fromAddress, quoteOnly, postHook);
652
+ if (!routeResponse?.route) {
653
+ return {};
654
+ }
655
+ if (isRouteToAmountGreaterThanToAmount(routeResponse, toAmount)) {
656
+ return { route: routeResponse };
657
+ }
658
+ const newFromAmount = calculateFromAmountFromRoute(routeResponse.route.estimate.exchangeRate, toAmount, routeResponse.route.estimate.toAmountUSD);
659
+ const newRoute = await getRouteWithRetry(squid, fromToken, toToken, toAddress, newFromAmount, fromAddress, quoteOnly, postHook);
660
+ if (!newRoute?.route) {
661
+ return {};
662
+ }
663
+ if (isRouteToAmountGreaterThanToAmount(newRoute, toAmount)) {
664
+ return { route: newRoute };
665
+ }
666
+ throw new Error('Unable to find a route with sufficient toAmount');
667
+ };
668
+ const getRoutesWithFeesValidation = async (squid, toTokenAddress, balances, fromAmountArray, postHook) => {
669
+ const getGasCost = (route, chainId) => (route.route?.route.estimate.gasCosts || [])
670
+ .filter((gasCost) => gasCost.token.chainId === chainId.toString())
671
+ .reduce((sum, gasCost) => sum + parseFloat(formatUnits(gasCost.amount, gasCost.token.decimals)), 0);
672
+ const getTotalFees = (route, chainId) => (route.route?.route.estimate.feeCosts || [])
673
+ .filter((fee) => fee.token.chainId === chainId.toString())
674
+ .reduce((sum, fee) => sum + parseFloat(formatUnits(fee.amount, fee.token.decimals)), 0);
675
+ const findUserGasBalance = (chainId) => balances.find((balance) => balance.address.toLowerCase() === SQUID_NATIVE_TOKEN.toLowerCase()
676
+ && balance.chainId.toString() === chainId.toString());
677
+ const hasSufficientNativeTokenBalance = (userGasBalance, fromAmount, fromToken, totalGasCost, totalFeeCost) => {
678
+ if (!userGasBalance)
679
+ return false;
680
+ const userBalance = parseFloat(formatUnits(userGasBalance.balance, userGasBalance.decimals));
681
+ // If the fromToken is the native token, validate balance for both fromAmount and gas + fee costs
682
+ // Otherwise, only validate balance for gas + fee costs
683
+ const requiredAmount = fromToken.address.toLowerCase() === SQUID_NATIVE_TOKEN.toLowerCase()
684
+ ? parseFloat(fromAmount) + totalGasCost + totalFeeCost
685
+ : totalGasCost + totalFeeCost;
686
+ return userBalance >= requiredAmount;
687
+ };
688
+ const routePromises = fromAmountArray.map(async (data) => {
689
+ const routeResponse = await getRoute(squid, data.fromToken, data.toToken, toTokenAddress, data.fromAmount, data.toAmount, undefined, true, postHook);
690
+ if (!routeResponse?.route)
691
+ return null;
692
+ const gasCost = getGasCost(routeResponse, data.balance.chainId);
693
+ const feeCost = getTotalFees(routeResponse, data.balance.chainId);
694
+ const userGasBalance = findUserGasBalance(data.balance.chainId);
695
+ return {
696
+ amountData: data,
697
+ route: routeResponse.route,
698
+ isInsufficientGas: !hasSufficientNativeTokenBalance(userGasBalance, data.fromAmount, data.fromToken, gasCost, feeCost),
699
+ };
700
+ });
701
+ const routesData = (await Promise.allSettled(routePromises))
702
+ .filter((result) => result.status === 'fulfilled')
703
+ .map((result) => result.value)
704
+ .filter((route) => route !== null);
705
+ return routesData;
706
+ };
707
+ const fetchRoutes = async (squid, tokens, balances, toChainId, toTokenAddress, toAmount, bulkNumber = 5, delayMs = 1000, isSwapAllowed = true) => {
708
+ const currentRequestId = ++latestRequestIdRef.current;
709
+ try {
710
+ let fromAmountDataArray = getSufficientFromAmounts(tokens, balances, toChainId, toTokenAddress, toAmount);
711
+ if (!isSwapAllowed) {
712
+ fromAmountDataArray = fromAmountDataArray.filter((amountData) => amountData.balance.chainId !== toChainId);
713
+ }
714
+ let allRoutes = [];
715
+ await Promise.all(fromAmountDataArray
716
+ .reduce((acc, _, i) => {
717
+ if (i % bulkNumber === 0) {
718
+ acc.push(fromAmountDataArray.slice(i, i + bulkNumber));
719
+ }
720
+ return acc;
721
+ }, [])
722
+ .map(async (slicedFromAmountDataArray) => {
723
+ allRoutes.push(...(await getRoutesWithFeesValidation(squid, toTokenAddress, balances, slicedFromAmountDataArray)));
724
+ await delay(delayMs);
725
+ }));
726
+ if (!isSwapAllowed) {
727
+ allRoutes = allRoutes.filter((routeData) => !routeData.route.route.estimate.actions.find((action) => action.type === dist.ActionType.SWAP));
728
+ }
729
+ const sortedRoutes = sortRoutesByFastestTime(allRoutes);
730
+ // Only return routes if the request is the latest one
731
+ if (currentRequestId === latestRequestIdRef.current) {
732
+ return sortedRoutes;
733
+ }
734
+ return [];
735
+ }
736
+ catch (error) {
737
+ throw new RouteError('Failed to fetch routes', {
738
+ fromToken: tokens.find((token) => token.address === toTokenAddress)?.symbol,
739
+ toToken: tokens.find((token) => token.address === toTokenAddress)?.symbol,
740
+ toChain: toChainId,
741
+ errorStatus: error.response?.status,
742
+ errorMessage: error.response?.data?.message,
743
+ errorStack: error.stack,
744
+ });
745
+ }
746
+ };
747
+ const hasSufficientBalance = (balances, toTokenAddress, toChainId, toAmount) => {
748
+ const matchingTokens = balances.filter((balance) => balance.address.toLowerCase() === toTokenAddress.toLowerCase()
749
+ && balance.chainId.toString() === toChainId.toString());
750
+ if (matchingTokens.length > 0) {
751
+ return matchingTokens.some((balance) => {
752
+ const tokenAmount = parseFloat(formatUnits(balance.balance, balance.decimals));
753
+ return tokenAmount >= parseFloat(toAmount);
754
+ });
755
+ }
756
+ return false;
757
+ };
758
+ return {
759
+ hasSufficientBalance,
760
+ fetchRoutes,
761
+ getFromAmountData,
762
+ getRoute,
763
+ };
764
+ };
605
765
 
606
766
  function WarningHero() {
607
767
  return (jsx(Box, { testId: "warning-hero", sx: {
@@ -3695,6 +3855,15 @@ function OnboardingDrawer({ environment }) {
3695
3855
  }, children: t(`views.ADD_TOKENS.onboarding.screen${screenIndex}.title`) }), jsx(OnboardingPagination, { disabled: true, size: "small", currentPage: screenIndex, totalPages: 3, sx: { mt: 'base.spacing.x11', mb: 'base.spacing.x8' } }), jsx(Button, { variant: screenIndex === 3 ? 'primary' : 'tertiary', onClick: handleCtaOnClick, size: "large", sx: { alignSelf: 'stretch' }, children: t(`views.ADD_TOKENS.onboarding.screen${screenIndex}.buttonText`) })] }) }));
3696
3856
  }
3697
3857
 
3858
+ var RiveStateMachineInput;
3859
+ (function (RiveStateMachineInput) {
3860
+ RiveStateMachineInput[RiveStateMachineInput["START"] = 0] = "START";
3861
+ RiveStateMachineInput[RiveStateMachineInput["WAITING"] = 1] = "WAITING";
3862
+ RiveStateMachineInput[RiveStateMachineInput["PROCESSING"] = 2] = "PROCESSING";
3863
+ RiveStateMachineInput[RiveStateMachineInput["COMPLETED"] = 3] = "COMPLETED";
3864
+ RiveStateMachineInput[RiveStateMachineInput["ERROR"] = 4] = "ERROR";
3865
+ })(RiveStateMachineInput || (RiveStateMachineInput = {}));
3866
+
3698
3867
  const useError = (environment) => {
3699
3868
  const { viewDispatch } = reactExports.useContext(ViewContext);
3700
3869
  const { page } = useAnalytics();
@@ -4026,114 +4195,6 @@ function SelectedWallet({ label, caption, children, onClick, providerInfo, chain
4026
4195
  }, children: [getMenuItemImage(), jsx(MenuItem.Label, { children: label }), providerInfo?.name ? (jsxs(MenuItem.Caption, { children: [providerInfo?.name, ' • ', jsx(EllipsizedText, { text: providerInfo.address ?? '', sx: { c: 'inherit', fontSize: 'inherit' } })] })) : (jsx(MenuItem.Caption, { children: caption })), jsx(MenuItem.BottomSlot, { children: children })] }));
4027
4196
  }
4028
4197
 
4029
- function RouteOption({ routeData, onClick, chains, disabled = false, isFastest = false, size = 'small', rc = jsx("span", {}), selected = false, }) {
4030
- const { t } = useTranslation();
4031
- const { fromToken } = routeData.amountData;
4032
- const { estimate } = routeData.route.route;
4033
- const chain = chains?.find((c) => c.id === fromToken.chainId);
4034
- const estimatedDurationFormatted = getDurationFormatted(estimate.estimatedRouteDuration, t('views.ADD_TOKENS.routeSelection.minutesText'), t('views.ADD_TOKENS.routeSelection.minuteText'), t('views.ADD_TOKENS.routeSelection.secondsText'));
4035
- const { totalFeesUsd } = reactExports.useMemo(() => getTotalRouteFees(routeData.route), [routeData]);
4036
- const { routeBalanceUsd, fromAmount, fromAmountUsd } = reactExports.useMemo(() => getRouteAndTokenBalances(routeData), [routeData]);
4037
- const handleClick = () => {
4038
- onClick(routeData);
4039
- };
4040
- const menuItemProps = {
4041
- selected,
4042
- disabled,
4043
- emphasized: true,
4044
- rc,
4045
- size,
4046
- onClick: disabled ? undefined : handleClick,
4047
- };
4048
- return (jsxs(MenuItem, { ...menuItemProps, children: [jsx(MenuItem.Label, { children: fromToken.name }), chain && (jsxs(Sticker, { position: { x: 'right', y: 'bottom' }, children: [jsx(Sticker.FramedImage, { use: jsx("img", { src: chain.iconUrl, alt: chain.name }), size: "xSmall" }), jsx(MenuItem.FramedImage, { circularFrame: true, use: jsx("img", { src: fromToken.iconUrl, alt: fromToken.name }) })] })), jsxs(MenuItem.Caption, { children: [`${t('views.ADD_TOKENS.fees.balance')} ${t('views.ADD_TOKENS.fees.fiatPricePrefix')} $${routeBalanceUsd}`, routeData.isInsufficientGas && (jsxs(Fragment, { children: [jsx("br", {}), jsx("span", { style: { color: '#FF637F' }, children: t('views.ADD_TOKENS.noGasRouteMessage', {
4049
- token: routeData.route.route.estimate.gasCosts[0].token.symbol,
4050
- }) })] }))] }), jsx(MenuItem.PriceDisplay, { price: fromAmount, children: jsx(MenuItem.PriceDisplay.Caption, { children: `${t('views.ADD_TOKENS.fees.fiatPricePrefix')} $${fromAmountUsd}` }) }), jsxs(MenuItem.BottomSlot, { children: [jsx(MenuItem.BottomSlot.Divider, {}), jsxs(Stack, { rc: jsx("span", {}), direction: "row", justifyContent: "space-between", sx: {
4051
- w: '100%',
4052
- }, children: [jsxs(Body, { sx: {
4053
- ...hFlex,
4054
- ...centerFlexChildren,
4055
- gap: 'base.spacing.x1',
4056
- c: 'base.color.text.body.secondary',
4057
- }, size: "xSmall", children: [jsx(Icon, { icon: "Countdown", sx: {
4058
- w: 'base.icon.size.200',
4059
- fill: 'base.color.text.body.secondary',
4060
- }, variant: "bold" }), estimatedDurationFormatted] }), jsxs(Body, { size: "xSmall", sx: { ...hFlex, ...centerFlexChildren }, children: [isFastest && (jsx(Badge, { badgeContent: t('views.ADD_TOKENS.routeSelection.fastestBadge'), variant: "emphasis", sx: { mr: 'base.spacing.x2' } })), `${t('views.ADD_TOKENS.fees.fee')} ${t('views.ADD_TOKENS.fees.fiatPricePrefix')}
4061
- $${getFormattedAmounts(totalFeesUsd)}`] })] })] })] }));
4062
- }
4063
-
4064
- function FiatOption({ type, onClick, disabled = false, size = 'small', rc = jsx("span", {}), }) {
4065
- const { t } = useTranslation();
4066
- const icon = {
4067
- [FiatOptionType.DEBIT]: 'BankCard',
4068
- [FiatOptionType.CREDIT]: 'Craft',
4069
- };
4070
- const handleClick = () => {
4071
- onClick?.(type);
4072
- };
4073
- const menuItemProps = {
4074
- disabled,
4075
- emphasized: true,
4076
- onClick: disabled ? undefined : handleClick,
4077
- size,
4078
- rc,
4079
- };
4080
- return (jsxs(MenuItem, { ...menuItemProps, children: [jsx(MenuItem.FramedIcon, { icon: icon[type], variant: "bold", emphasized: false }), jsx(MenuItem.Label, { children: t(`views.ADD_TOKENS.drawer.options.${type}.heading`) }), jsx(MenuItem.Caption, { children: t(`views.ADD_TOKENS.drawer.options.${type}.${disabled ? 'disabledCaption' : 'caption'}`) }), !disabled && jsx(MenuItem.IntentIcon, {})] }));
4081
- }
4082
-
4083
- const defaultFiatOptions = [
4084
- FiatOptionType.DEBIT,
4085
- FiatOptionType.CREDIT,
4086
- ];
4087
- function RouteOptions({ checkout, routes, chains, onCardClick, onRouteClick, size, showOnrampOption, insufficientBalance, selectedIndex, }) {
4088
- const { t } = useTranslation();
4089
- // @NOTE: early exit with loading related UI, when the
4090
- // routes are not yet available
4091
- if (!routes?.length && !insufficientBalance) {
4092
- return (jsxs(Stack, { sx: { pt: 'base.spacing.x3' }, alignItems: "stretch", gap: "base.spacing.x1", children: [jsx(MenuItem, { shimmer: "withBottomSlot", size: "small", emphasized: true }), jsx(MenuItem, { shimmer: "withBottomSlot", size: "small", emphasized: true }), jsxs(Body, { sx: { textAlign: 'center', mt: 'base.spacing.x6' }, size: "small", children: [t('views.ADD_TOKENS.drawer.options.loadingText1'), jsx("br", {}), t('views.ADD_TOKENS.drawer.options.loadingText2')] }), jsx(FramedVideo, { mimeType: "video/mp4", videoUrl: getRemoteVideo(checkout.config.environment, '/loading_bubble-small.mp4'), sx: { alignSelf: 'center', mt: 'base.spacing.x2' }, size: "large", circularFrame: true })] }));
4093
- }
4094
- const noRoutes = !(!insufficientBalance || routes?.length);
4095
- return (jsxs(Stack, { sx: { py: 'base.spacing.x3' }, alignItems: "stretch", gap: "base.spacing.x1", testId: "options-list", justifyContent: "center", rc: jsx(motion.div, { variants: listVariants, initial: "hidden", animate: "show" }), children: [routes?.map((routeData, index) => (jsx(RouteOption, { size: size, routeData: routeData, chains: chains, onClick: () => onRouteClick(routeData, index), isFastest: index === 0, selected: index === selectedIndex, rc: jsx(motion.div, { variants: listItemVariants }) }, `route-option-${routeData.amountData.fromToken.chainId}-${routeData.amountData.fromToken.address}`))), noRoutes && (jsxs(Banner, { children: [jsx(Banner.Icon, { icon: "InformationCircle" }), jsx(Banner.Title, { children: t('views.ADD_TOKENS.drawer.options.noRoutes.heading') }), jsx(Banner.Caption, { children: t('views.ADD_TOKENS.drawer.options.noRoutes.caption') })] })), showOnrampOption && (jsxs(Fragment, { children: [jsx(Divider, { size: "xSmall", sx: { my: 'base.spacing.x2' }, children: t('views.ADD_TOKENS.drawer.options.moreOptionsDividerText') }), defaultFiatOptions.map((type, idx) => (jsx(FiatOption, { type: type, size: size, onClick: onCardClick, rc: jsx(motion.div, { custom: idx, variants: listItemVariants }) }, `fiat-option-${type}`)))] }))] }));
4096
- }
4097
-
4098
- function RouteOptionsDrawer({ checkout, routes, visible, onClose, onRouteClick, onCardClick, showOnrampOption,
4099
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
4100
- showSwapOption,
4101
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
4102
- showBridgeOption, insufficientBalance, }) {
4103
- const { t } = useTranslation();
4104
- const { track } = useAnalytics();
4105
- const { addTokensState: { id, chains }, } = reactExports.useContext(AddTokensContext);
4106
- const { providersState: { fromProviderInfo, fromAddress }, } = useProvidersContext();
4107
- const selectedRouteIndex = reactExports.useRef(0);
4108
- const handleOnRouteClick = (route, index) => {
4109
- selectedRouteIndex.current = index;
4110
- onRouteClick(route);
4111
- };
4112
- reactExports.useEffect(() => {
4113
- if (!visible) {
4114
- return;
4115
- }
4116
- track({
4117
- userJourney: UserJourney.ADD_TOKENS,
4118
- screen: 'InputScreen',
4119
- control: 'RoutesMenu',
4120
- controlType: 'MenuItem',
4121
- action: 'Opened',
4122
- extras: {
4123
- contextId: id,
4124
- showOnrampOption: Boolean(showOnrampOption),
4125
- showSwapOption: Boolean(showSwapOption),
4126
- insufficientBalance: Boolean(insufficientBalance),
4127
- routesAvailable: routes?.length ?? 0,
4128
- },
4129
- });
4130
- }, [visible]);
4131
- return (jsx(Drawer, { size: "full", visible: visible, showHeaderBar: false, onCloseDrawer: onClose, children: jsxs(Drawer.Content, { rc: jsx(motion.div, { variants: listVariants, initial: "hidden", animate: "show" }), sx: {
4132
- pt: 'base.spacing.x3',
4133
- px: 'base.spacing.x3',
4134
- }, children: [jsxs(MenuItem, { size: "xSmall", children: [jsx(MenuItem.FramedImage, { padded: true, emphasized: true, use: (jsx("img", { src: fromProviderInfo?.icon, alt: fromProviderInfo?.name })), sx: { mx: 'base.spacing.x2' } }), jsx(MenuItem.Label, { children: t('views.ADD_TOKENS.drawer.options.heading') }), jsxs(MenuItem.Caption, { children: [fromProviderInfo?.name, ' • ', jsx(EllipsizedText, { text: fromAddress ?? '', sx: { c: 'inherit', fontSize: 'inherit' } })] }), jsx(MenuItem.StatefulButtCon, { icon: "ChevronExpand", onClick: onClose })] }), jsx(RouteOptions, { size: "small", checkout: checkout, routes: routes, chains: chains, onCardClick: onCardClick, onRouteClick: handleOnRouteClick, showOnrampOption: showOnrampOption, insufficientBalance: insufficientBalance, selectedIndex: selectedRouteIndex.current })] }) }));
4135
- }
4136
-
4137
4198
  function SelectedRouteOptionContainer({ children, onClick, selected, }) {
4138
4199
  return (jsx(Stack, { direction: "row", alignItems: "center", gap: "base.spacing.x4", sx: {
4139
4200
  ml: ({ base }) => (selected
@@ -4651,6 +4712,207 @@ function SquidIcon({ sx = {}, className, }) {
4651
4712
  d: "M23.7183 11.2782C23.8008 10.984 23.8565 10.6787 23.8891 10.3676C23.8891 10.3691 23.8896 10.371 23.8896 10.371C23.7207 9.12732 23.3616 7.94412 22.8423 6.85164C22.9051 7.21308 22.9464 7.61484 22.9738 8.08236C23.0458 9.30588 22.5907 11.2187 21.829 12.267C21.1258 13.2347 19.5394 14.1476 18.0279 13.3417C17.2296 12.916 16.3743 11.2883 17.3319 7.87404C17.8498 6.02796 18.0979 3.95148 16.825 2.19084C15.7546 0.710036 13.7962 0.331317 12.1051 0.793076C9.99025 1.37052 8.81665 3.03948 8.33569 5.10972C7.99345 6.58092 8.22145 8.07132 8.80657 9.4806C9.80833 11.8936 11.1394 12.6486 11.1835 14.7428C11.1979 15.4321 11.1456 16.8059 12.1935 16.7056C13.1856 16.6105 13.0627 15.484 12.8467 14.7865C12.5117 13.7036 11.6357 12.7878 10.8139 11.3776C10.1986 10.322 9.79105 9.54924 9.70369 8.37036C9.61345 7.14684 9.94801 5.80812 10.6618 4.7382C11.3237 3.74604 12.7459 2.80956 14.1999 3.49068C14.7298 3.73884 15.0533 4.19772 15.2055 4.6926C15.6821 6.24588 15.0634 7.76364 14.9079 9.67308C14.6487 12.8526 16.7928 17.2144 20.9165 15.1955C22.2413 14.548 23.2776 12.8521 23.7183 11.2782Z", fill: "black" })] }));
4652
4713
  }
4653
4714
 
4715
+ const isSquidNativeToken = (token) => token.toLowerCase() === SQUID_NATIVE_TOKEN.toLowerCase();
4716
+
4717
+ const withMetricsAsync = async (fn, flowName, anonymousId, errorType) => {
4718
+ const flow = trackFlow('commerce', flowName);
4719
+ if (anonymousId) {
4720
+ flow.addEvent(`anonymousId_${anonymousId}`);
4721
+ }
4722
+ try {
4723
+ return await fn(flow);
4724
+ }
4725
+ catch (error) {
4726
+ if (error instanceof Error) {
4727
+ trackError('commerce', flowName, error);
4728
+ }
4729
+ if (errorType && errorType(error)) {
4730
+ flow.addEvent(`errored_${errorType(error)}`);
4731
+ }
4732
+ else {
4733
+ flow.addEvent('errored');
4734
+ }
4735
+ throw error;
4736
+ }
4737
+ finally {
4738
+ flow.addEvent('End');
4739
+ }
4740
+ };
4741
+
4742
+ function isRejectedError(err) {
4743
+ const reason = `${err?.reason || err?.message || ''}`.toLowerCase();
4744
+ return reason.includes('rejected') && reason.includes('user');
4745
+ }
4746
+
4747
+ const TRANSACTION_NOT_COMPLETED = 'transaction not completed';
4748
+ const useExecute = (userJourney, onTransactionError) => {
4749
+ const { user } = useAnalytics();
4750
+ const waitForReceipt = async (provider, txHash, maxAttempts = 120) => {
4751
+ const result = await retry(async () => {
4752
+ const receipt = await provider.getTransactionReceipt(txHash);
4753
+ if (!receipt) {
4754
+ throw new Error('receipt not found');
4755
+ }
4756
+ if (receipt.status === 0) {
4757
+ throw new Error('status failed');
4758
+ }
4759
+ return receipt;
4760
+ }, {
4761
+ retries: maxAttempts,
4762
+ retryIntervalMs: 1000,
4763
+ nonRetryable: (error) => error.message === 'status failed',
4764
+ });
4765
+ if (!result) {
4766
+ throw new Error(`Transaction receipt not found after ${maxAttempts} attempts`);
4767
+ }
4768
+ return result;
4769
+ };
4770
+ const getAllowance = async (provider, routeResponse) => {
4771
+ try {
4772
+ if (!isSquidNativeToken(routeResponse?.route?.params.fromToken)) {
4773
+ const erc20Abi = [
4774
+ 'function allowance(address owner, address spender) public view returns (uint256)',
4775
+ ];
4776
+ const fromToken = routeResponse?.route.params.fromToken;
4777
+ const signer = provider.getSigner();
4778
+ const tokenContract = new Contract(fromToken, erc20Abi, signer);
4779
+ const ownerAddress = await signer.getAddress();
4780
+ const transactionRequestTarget = routeResponse?.route?.transactionRequest?.target;
4781
+ if (!transactionRequestTarget) {
4782
+ throw new Error('transactionRequest target is undefined');
4783
+ }
4784
+ return await tokenContract.allowance(ownerAddress, transactionRequestTarget);
4785
+ }
4786
+ return MaxUint256; // no approval is needed for native tokens
4787
+ }
4788
+ catch (error) {
4789
+ onTransactionError?.(error);
4790
+ return undefined;
4791
+ }
4792
+ };
4793
+ const callApprove = async (flow, fromProviderInfo, provider, routeResponse) => {
4794
+ flow.addEvent(`provider_${fromProviderInfo.name}`);
4795
+ const erc20Abi = [
4796
+ 'function approve(address spender, uint256 amount) public returns (bool)',
4797
+ ];
4798
+ const fromToken = routeResponse?.route.params.fromToken;
4799
+ const signer = provider.getSigner();
4800
+ const tokenContract = new Contract(fromToken, erc20Abi, signer);
4801
+ const fromAmount = routeResponse?.route.params.fromAmount;
4802
+ if (!fromAmount) {
4803
+ throw new Error('fromAmount is undefined');
4804
+ }
4805
+ const transactionRequestTarget = routeResponse?.route?.transactionRequest?.target;
4806
+ if (!transactionRequestTarget) {
4807
+ throw new Error('transactionRequest target is undefined');
4808
+ }
4809
+ const tx = await tokenContract.approve(transactionRequestTarget, fromAmount);
4810
+ flow.addEvent('transactionSent');
4811
+ return await waitForReceipt(provider, tx.hash);
4812
+ };
4813
+ const getAnonymousId = async () => {
4814
+ try {
4815
+ const userData = await user();
4816
+ return userData?.anonymousId() ?? undefined;
4817
+ }
4818
+ catch (error) {
4819
+ return undefined;
4820
+ }
4821
+ };
4822
+ const approve = async (fromProviderInfo, provider, routeResponse) => {
4823
+ try {
4824
+ if (!isSquidNativeToken(routeResponse?.route?.params.fromToken)) {
4825
+ return await withMetricsAsync((flow) => callApprove(flow, fromProviderInfo, provider, routeResponse), `${userJourney}_Approve`, await getAnonymousId(), (error) => (isRejectedError(error) ? 'rejected' : ''));
4826
+ }
4827
+ return undefined;
4828
+ }
4829
+ catch (error) {
4830
+ onTransactionError?.(error);
4831
+ return undefined;
4832
+ }
4833
+ };
4834
+ const callExecute = async (flow, squid, fromProviderInfo, provider, routeResponse) => {
4835
+ flow.addEvent(`provider_${fromProviderInfo.name}`);
4836
+ const tx = (await squid.executeRoute({
4837
+ signer: provider.getSigner(),
4838
+ route: routeResponse.route,
4839
+ }));
4840
+ flow.addEvent('transactionSent');
4841
+ return await waitForReceipt(provider, tx.hash);
4842
+ };
4843
+ const execute = async (squid, fromProviderInfo, provider, routeResponse) => {
4844
+ if (!provider.provider.request) {
4845
+ throw new Error('provider does not have request method');
4846
+ }
4847
+ try {
4848
+ return await withMetricsAsync((flow) => callExecute(flow, squid, fromProviderInfo, provider, routeResponse), `${userJourney}_Execute`, await getAnonymousId(), (error) => (isRejectedError(error) ? 'rejected' : ''));
4849
+ }
4850
+ catch (error) {
4851
+ onTransactionError?.(error);
4852
+ return undefined;
4853
+ }
4854
+ };
4855
+ const getStatus = async (squid, transactionHash) => {
4856
+ const completedTransactionStatus = [
4857
+ 'success',
4858
+ 'partial_success',
4859
+ 'needs_gas',
4860
+ 'not_found',
4861
+ ];
4862
+ try {
4863
+ return await retry(async () => {
4864
+ const result = await squid.getStatus({
4865
+ transactionId: transactionHash,
4866
+ });
4867
+ if (completedTransactionStatus.includes(result.squidTransactionStatus ?? '')) {
4868
+ return result;
4869
+ }
4870
+ return Promise.reject(TRANSACTION_NOT_COMPLETED);
4871
+ }, {
4872
+ retries: 240,
4873
+ retryIntervalMs: 5000,
4874
+ nonRetryable: (err) => {
4875
+ if (err.response) {
4876
+ return err.response.status === 400 || err.response.status === 500;
4877
+ }
4878
+ return err !== TRANSACTION_NOT_COMPLETED;
4879
+ },
4880
+ });
4881
+ }
4882
+ catch (error) {
4883
+ onTransactionError?.(error);
4884
+ return undefined;
4885
+ }
4886
+ };
4887
+ return {
4888
+ getAllowance,
4889
+ approve,
4890
+ execute,
4891
+ getStatus,
4892
+ waitForReceipt,
4893
+ };
4894
+ };
4895
+
4896
+ /**
4897
+ * Find a chain by its id
4898
+ * @param chainId - The id of the chain to find
4899
+ * @param chains - The chains to search through
4900
+ * @returns The chain with the matching id, or undefined if no chain is found
4901
+ */
4902
+ function findChainById(chainId, chains) {
4903
+ return chains?.find((chain) => chain.id === chainId);
4904
+ }
4905
+ /**
4906
+ * Get the chains from the route
4907
+ * @param chains - The chains to search through
4908
+ * @param route - The route to get the chains from
4909
+ * @returns The chains from the route
4910
+ */
4911
+ const getRouteChains = (chains, route) => ({
4912
+ fromChain: findChainById(route?.route.estimate.fromToken.chainId, chains),
4913
+ toChain: findChainById(route?.route.estimate.toToken.chainId, chains),
4914
+ });
4915
+
4654
4916
  function AddressMissmatchDrawer({ visible, onClick, }) {
4655
4917
  const { t } = useTranslation();
4656
4918
  return (jsx(Drawer, { size: "full", visible: visible, showHeaderBar: false, children: jsxs(Drawer.Content, { children: [jsx(WalletWarningHero, {}), jsxs(Box, { sx: { px: 'base.spacing.x6' }, children: [jsx(Heading, { sx: {
@@ -4707,6 +4969,45 @@ function RouteFees({ visible, onClose, routeData, totalAmount, totalFiatAmount,
4707
4969
  return (jsx(FeesBreakdown, { visible: visible, loading: !routeData, fees: [...feeCosts, ...gasCosts], tokenSymbol: tokenSymbol, totalAmount: getFormattedNumber(totalAmount, feesToken?.decimals), totalFiatAmount: getFormattedAmounts(totalFiatAmount), onCloseDrawer: onClose }));
4708
4970
  }
4709
4971
 
4972
+ const convertToNetworkChangeableProvider = async (provider) => new Web3Provider(provider.provider, 'any');
4973
+
4974
+ /**
4975
+ * Ensure the provider is connected to the correct chain.
4976
+ * If not, attempts to switch to the desired chain.
4977
+ */
4978
+ const verifyAndSwitchChain = async (provider, chainId) => {
4979
+ if (!provider.provider.request) {
4980
+ return {
4981
+ isChainCorrect: false,
4982
+ error: 'Provider does not support the request method.',
4983
+ };
4984
+ }
4985
+ try {
4986
+ const targetChainHex = `0x${parseInt(chainId, 10).toString(16)}`;
4987
+ const currentChainId = await provider.provider.request({
4988
+ method: 'eth_chainId',
4989
+ });
4990
+ if (targetChainHex !== currentChainId) {
4991
+ await provider.provider.request({
4992
+ method: 'wallet_switchEthereumChain',
4993
+ params: [
4994
+ {
4995
+ chainId: targetChainHex,
4996
+ },
4997
+ ],
4998
+ });
4999
+ }
5000
+ return { isChainCorrect: true };
5001
+ }
5002
+ catch (error) {
5003
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
5004
+ return {
5005
+ isChainCorrect: false,
5006
+ error: errorMessage,
5007
+ };
5008
+ }
5009
+ };
5010
+
4710
5011
  const useErrorHandler = () => {
4711
5012
  const { addTokensState: { id: contextId }, } = reactExports.useContext(AddTokensContext);
4712
5013
  const { providersState: { checkout, }, } = useProvidersContext();
@@ -5158,6 +5459,23 @@ function Review({ data, showBackButton = false, onBackButtonClick, onCloseButton
5158
5459
  } })] }));
5159
5460
  }
5160
5461
 
5462
+ const fetchBalances = async (squid, chains, provider) => {
5463
+ const chainIds = chains.map((chain) => chain.id);
5464
+ const address = await provider?.getSigner().getAddress();
5465
+ const promises = [];
5466
+ for (const chainId of chainIds) {
5467
+ const balancePromise = squid.getEvmBalances({
5468
+ chains: [chainId],
5469
+ userAddress: address,
5470
+ });
5471
+ promises.push(balancePromise);
5472
+ }
5473
+ const balances = await Promise.all(promises);
5474
+ return balances
5475
+ .flatMap((balance) => balance)
5476
+ .filter((balance) => balance.balance !== '0');
5477
+ };
5478
+
5161
5479
  function AddTokensWidget({ showOnrampOption = true, showSwapOption = true, showBridgeOption = true, toTokenAddress: toTokenAddressParameter, toAmount, showBackButton, config, experiments, }) {
5162
5480
  const fetchingBalances = reactExports.useRef(false);
5163
5481
  const { base: { colorMode } } = useTheme();