@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.
- package/dist/{blockchain_data-Dtwb301L.js → blockchain_data-DBsuvv16.js} +2 -2
- package/dist/blockchain_data.js +3 -3
- package/dist/browser/checkout/{AddTokensWidget-phRi8isq.js → AddTokensWidget-TbB4PmLW.js} +555 -237
- package/dist/browser/checkout/{BridgeWidget-DqBqp6iY.js → BridgeWidget-BhrCocw2.js} +7 -6
- package/dist/browser/checkout/{CommerceWidget-BakoRfZj.js → CommerceWidget-Be5DHzdC.js} +15 -13
- package/dist/browser/checkout/{FeesBreakdown-BeOGIvgY.js → FeesBreakdown-Ck9YzdSC.js} +1 -1
- package/dist/browser/checkout/HandoverContent-DB23bXer.js +23 -0
- package/dist/browser/checkout/OnRampWidget-CqVk3g3X.js +1635 -0
- package/dist/browser/checkout/{SaleWidget-B6lZ8hGg.js → SaleWidget-BN4NOhbW.js} +169 -10
- package/dist/browser/checkout/{SpendingCapHero-DW6X8cGr.js → SpendingCapHero-CPvvFrji.js} +1 -1
- package/dist/browser/checkout/{SwapWidget-UFkXHv2L.js → SwapWidget-CwFYyKSt.js} +7 -7
- package/dist/browser/checkout/TokenImage-BKsKixKT.js +45 -0
- package/dist/browser/checkout/{TopUpView-BoCJELBq.js → TopUpView-B4EZHPvY.js} +1 -1
- package/dist/browser/checkout/{WalletApproveHero-BxkUQIWN.js → WalletApproveHero--5umxYJp.js} +3 -3
- package/dist/browser/checkout/{WalletWidget-C8iWV1OY.js → WalletWidget-DVqX_Zms.js} +3 -3
- package/dist/browser/checkout/{auto-track-Densi2-k.js → auto-track-B82Gtc5w.js} +1 -1
- package/dist/browser/checkout/{index-BWF1wMhZ.js → index-AFLAGIsQ.js} +1 -1
- package/dist/browser/checkout/{index-DSuIrOmV.js → index-Ba4jcykl.js} +1 -1
- package/dist/browser/checkout/{index-CGZ3kTp1.js → index-Bkd29y5M.js} +2 -2
- package/dist/browser/checkout/{index-fbNLtQZy.js → index-BsW6upSH.js} +1 -1
- package/dist/browser/checkout/{index-Djor6tHu.js → index-C0AbsZnL.js} +1 -1
- package/dist/browser/checkout/{index-CJ-HyQqV.js → index-CE6PQBKk.js} +1 -1
- package/dist/browser/checkout/{index-Bz7QkIrz.js → index-CwvzJ6GW.js} +1 -1
- package/dist/browser/checkout/index-Smb16jHM.js +885 -0
- package/dist/browser/checkout/{index-C-4DEd8a.js → index-uoFT0va2.js} +1878 -5716
- package/dist/browser/checkout/{index.umd-BI-_-mXw.js → index.umd-DHRFESK1.js} +1 -1
- package/dist/browser/checkout/sdk.js +4 -4
- package/dist/browser/checkout/{useInterval-CfVGG_W7.js → useInterval-DoDljSUa.js} +1 -1
- package/dist/browser/checkout/widgets-esm.js +1 -1
- package/dist/browser/checkout/widgets.js +6026 -7124
- package/dist/{checkout-BymciB9v.js → checkout-BX4BMX31.js} +6 -6
- package/dist/checkout.js +5 -5
- package/dist/{config-BskomF73.js → config-d4_WLWVX.js} +1 -1
- package/dist/config.js +1 -1
- package/dist/{index-BoeSZBBL.js → index-BFY7ef9q.js} +1 -1
- package/dist/{index-B44hBOSm.js → index-BOa7TPFI.js} +1 -1
- package/dist/{index-nAn9d-qB.js → index-BVC9tGIa.js} +4 -4
- package/dist/{index-BJV9b4fG.js → index-BbCunE2k.js} +3 -3
- package/dist/{index-gGENaQms.js → index-Bd-KXMjV.js} +1 -1
- package/dist/{index-DwQ8JxG1.js → index-Dvk4T7xM.js} +1 -1
- package/dist/index.browser.js +4 -4
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +8 -8
- package/dist/index.js +14 -14
- package/dist/{minting_backend-B6smE-5l.js → minting_backend-DSYjNnIR.js} +3 -3
- package/dist/minting_backend.js +5 -5
- package/dist/{orderbook-CqH1yhJO.js → orderbook-y-wZqTX7.js} +1 -1
- package/dist/orderbook.js +2 -2
- package/dist/{passport-Cxq9DY_u.js → passport-Hck6nDL8.js} +3 -3
- package/dist/passport.js +4 -4
- package/dist/version.json +1 -1
- package/dist/{webhook-DfGoH4cH.js → webhook-ByqpJy1j.js} +1 -1
- package/dist/webhook.js +2 -2
- package/dist/{x-CCG4tF0S.js → x-U0dfyoEE.js} +3 -3
- package/dist/x.js +4 -4
- package/package.json +1 -1
- package/dist/browser/checkout/OnRampWidget-BID9kc9s.js +0 -293
- 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,
|
|
2
|
-
import { T as TokenImage } from './TokenImage-
|
|
3
|
-
import {
|
|
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
|
-
|
|
582
|
-
(
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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();
|