@rango-dev/widget-embedded 0.36.1-next.11 → 0.36.1-next.13

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 (61) hide show
  1. package/dist/components/ConfirmWalletsModal/ConfirmWalletsModal.d.ts.map +1 -1
  2. package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.helpers.d.ts.map +1 -1
  3. package/dist/components/Quotes/Quotes.d.ts.map +1 -1
  4. package/dist/components/WalletStatefulConnect/Namespaces.d.ts.map +1 -1
  5. package/dist/constants/quote.d.ts +8 -0
  6. package/dist/constants/quote.d.ts.map +1 -1
  7. package/dist/containers/QuoteInfo/QuoteInfo.d.ts.map +1 -1
  8. package/dist/containers/Wallets/Wallets.d.ts.map +1 -1
  9. package/dist/hooks/useConfirmSwap/useConfirmSwap.d.ts.map +1 -1
  10. package/dist/hooks/useConfirmSwap/useConfirmSwap.helpers.d.ts +6 -4
  11. package/dist/hooks/useConfirmSwap/useConfirmSwap.helpers.d.ts.map +1 -1
  12. package/dist/hooks/useStatefulConnect/useStatefulConnect.d.ts.map +1 -1
  13. package/dist/hooks/useStatefulConnect/useStatefulConnect.types.d.ts +2 -4
  14. package/dist/hooks/useStatefulConnect/useStatefulConnect.types.d.ts.map +1 -1
  15. package/dist/hooks/useSwapInput.d.ts.map +1 -1
  16. package/dist/index.js +2 -2
  17. package/dist/index.js.map +4 -4
  18. package/dist/pages/ConfirmSwapPage.d.ts.map +1 -1
  19. package/dist/store/quote.d.ts.map +1 -1
  20. package/dist/store/slices/wallets.d.ts +3 -1
  21. package/dist/store/slices/wallets.d.ts.map +1 -1
  22. package/dist/types/quote.d.ts +8 -17
  23. package/dist/types/quote.d.ts.map +1 -1
  24. package/dist/types/wallets.d.ts +1 -6
  25. package/dist/types/wallets.d.ts.map +1 -1
  26. package/dist/utils/providers.d.ts.map +1 -1
  27. package/dist/utils/quote.d.ts +5 -6
  28. package/dist/utils/quote.d.ts.map +1 -1
  29. package/dist/utils/swap.d.ts +4 -4
  30. package/dist/utils/swap.d.ts.map +1 -1
  31. package/dist/utils/wallets.d.ts.map +1 -1
  32. package/dist/widget-embedded.build.json +1 -1
  33. package/package.json +3 -3
  34. package/src/components/ConfirmWalletsModal/ConfirmWalletsModal.tsx +2 -19
  35. package/src/components/ConfirmWalletsModal/WalletList.tsx +1 -1
  36. package/src/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.helpers.ts +42 -22
  37. package/src/components/Quotes/Quotes.tsx +2 -3
  38. package/src/components/WalletStatefulConnect/Namespaces.tsx +30 -37
  39. package/src/constants/quote.ts +23 -0
  40. package/src/containers/QuoteInfo/QuoteInfo.tsx +3 -9
  41. package/src/containers/Wallets/Wallets.tsx +63 -3
  42. package/src/hooks/useConfirmSwap/useConfirmSwap.helpers.ts +29 -66
  43. package/src/hooks/useConfirmSwap/useConfirmSwap.ts +10 -15
  44. package/src/hooks/useStatefulConnect/useStatefulConnect.ts +20 -22
  45. package/src/hooks/useStatefulConnect/useStatefulConnect.types.ts +2 -4
  46. package/src/hooks/useSwapInput.ts +2 -9
  47. package/src/pages/ConfirmSwapPage.tsx +7 -15
  48. package/src/store/quote.ts +3 -13
  49. package/src/store/slices/wallets.ts +13 -2
  50. package/src/types/quote.ts +10 -24
  51. package/src/types/wallets.ts +1 -6
  52. package/src/utils/providers.ts +43 -36
  53. package/src/utils/quote.ts +57 -45
  54. package/src/utils/swap.ts +55 -36
  55. package/src/utils/wallets.ts +2 -4
  56. package/dist/constants/warnings.d.ts +0 -3
  57. package/dist/constants/warnings.d.ts.map +0 -1
  58. package/dist/utils/hub.d.ts +0 -4
  59. package/dist/utils/hub.d.ts.map +0 -1
  60. package/src/constants/warnings.ts +0 -26
  61. package/src/utils/hub.ts +0 -21
@@ -8,8 +8,6 @@ import { WalletState } from '@rango-dev/ui';
8
8
  import { useWallets } from '@rango-dev/wallets-react';
9
9
  import { useReducer } from 'react';
10
10
 
11
- import { convertCommonNamespacesKeysToLegacyNamespace } from '../../utils/hub';
12
-
13
11
  import {
14
12
  isStateOnDerivationPathStep,
15
13
  isStateOnNamespace,
@@ -99,50 +97,48 @@ export function useStatefulConnect(): UseStatefulConnect {
99
97
  detachedInstances && wallet.state !== 'connected';
100
98
 
101
99
  if (needsNamespace) {
102
- const availableNamespaces =
103
- convertCommonNamespacesKeysToLegacyNamespace(
104
- detachedInstances.value
105
- );
106
-
107
100
  dispatch({
108
101
  type: 'needsNamespace',
109
102
  payload: {
110
- providerType: wallet.type,
111
- providerImage: wallet.image,
112
- availableNamespaces,
113
- singleNamespace: false,
103
+ targetWallet: wallet,
114
104
  },
115
105
  });
116
106
  return { status: ResultStatus.Namespace };
117
107
  }
118
108
  }
119
109
 
120
- // Legacy
121
- if (!wallet.namespaces) {
110
+ /*
111
+ * Legacy
112
+ *
113
+ * For legacy there are 3 states:
114
+ * 1. a wallet doesn't have any namespace defined, we call the connect.
115
+ * 2. a wallet has more than two namespaces, we should show namepsace modal, and in that place user will be checked to needs derivation path or not.
116
+ * 3. a wallet has exactly one namespacesape, in this case we check if that needs derivation or not, if it needs we will do it here by dispatching the action accordingly.
117
+ */
118
+ if (!wallet.needsNamespace) {
122
119
  return await runConnect(wallet.type, undefined, options);
123
120
  }
124
121
 
125
- const needsNamespace = wallet.namespaces.length > 1;
122
+ const needsNamespace = wallet.needsNamespace.data.length > 1;
126
123
  const needsDerivationPath = wallet.needsDerivationPath;
127
124
 
128
125
  if (needsNamespace) {
129
126
  dispatch({
130
127
  type: 'needsNamespace',
131
128
  payload: {
132
- providerType: wallet.type,
133
- providerImage: wallet.image,
134
- availableNamespaces: wallet.namespaces,
135
- singleNamespace: wallet.singleNamespace,
129
+ targetWallet: wallet,
136
130
  },
137
131
  });
138
132
  return { status: ResultStatus.Namespace };
139
133
  } else if (needsDerivationPath) {
134
+ const namespace = wallet.needsNamespace.data[0];
135
+
140
136
  dispatch({
141
137
  type: 'needsDerivationPath',
142
138
  payload: {
143
139
  providerType: wallet.type,
144
140
  providerImage: wallet.image,
145
- namespace: wallet.namespaces[0],
141
+ namespace: namespace.value,
146
142
  },
147
143
  });
148
144
  return { status: ResultStatus.DerivationPath };
@@ -150,7 +146,9 @@ export function useStatefulConnect(): UseStatefulConnect {
150
146
 
151
147
  return await runConnect(
152
148
  wallet.type,
153
- wallet.namespaces.map((namespace) => ({ namespace })),
149
+ wallet.needsNamespace?.data.map((namespace) => ({
150
+ namespace: namespace.value,
151
+ })),
154
152
  options
155
153
  );
156
154
  }
@@ -167,7 +165,7 @@ export function useStatefulConnect(): UseStatefulConnect {
167
165
  wallet: WalletInfoWithExtra,
168
166
  selectedNamespaces: Namespace[]
169
167
  ): Promise<Result> => {
170
- const isSingleNamespace = wallet.singleNamespace;
168
+ const isSingleNamespace = wallet.needsNamespace?.selection === 'single';
171
169
  const needsDerivationPath = wallet.needsDerivationPath;
172
170
  const firstSelectedNamespace = selectedNamespaces[0];
173
171
 
@@ -207,7 +205,7 @@ export function useStatefulConnect(): UseStatefulConnect {
207
205
  );
208
206
  }
209
207
 
210
- const type = connectState.namespace.providerType;
208
+ const type = connectState.namespace.targetWallet.type;
211
209
  const namespaces = selectedNamespaces.map((namespace) => ({
212
210
  namespace,
213
211
  }));
@@ -1,3 +1,4 @@
1
+ import type { ExtendedModalWalletInfo } from '../../utils/wallets';
1
2
  import type { Namespace } from '@rango-dev/wallets-core/namespaces/common';
2
3
 
3
4
  export interface HandleConnectOptions {
@@ -6,10 +7,7 @@ export interface HandleConnectOptions {
6
7
  }
7
8
 
8
9
  export interface NeedsNamespacesState {
9
- providerType: string;
10
- providerImage: string;
11
- availableNamespaces?: Namespace[];
12
- singleNamespace?: boolean;
10
+ targetWallet: ExtendedModalWalletInfo;
13
11
  }
14
12
 
15
13
  export interface NeedsDerivationPathState {
@@ -10,8 +10,6 @@ import { isPositiveNumber } from '../utils/numbers';
10
10
  import {
11
11
  generateQuoteWarnings,
12
12
  getDefaultQuote,
13
- getQuoteFromTokenUsdPrice,
14
- getQuoteToTokenUsdPrice,
15
13
  sortQuotesBy,
16
14
  } from '../utils/quote';
17
15
  import { isRoutingEnabled } from '../utils/settings';
@@ -156,15 +154,10 @@ export function useSwapInput({
156
154
  requestId: quote?.requestId || '',
157
155
  swaps: quote?.swaps,
158
156
  });
159
- const outputUsdValue =
160
- getQuoteToTokenUsdPrice(quote) || toToken?.usdPrice;
161
- const inputUsdValue =
162
- getQuoteFromTokenUsdPrice(quote) || fromToken?.usdPrice;
163
157
  const quoteWarning =
164
158
  quote &&
165
- generateQuoteWarnings(quote, {
166
- fromToken: { ...fromToken, usdPrice: inputUsdValue },
167
- toToken: { ...toToken, usdPrice: outputUsdValue },
159
+ generateQuoteWarnings({
160
+ currentQuote: quote,
168
161
  userSlippage,
169
162
  findToken,
170
163
  });
@@ -24,12 +24,12 @@ import { RefreshButton } from '../components/HeaderButtons/RefreshButton';
24
24
  import { Layout, PageContainer } from '../components/Layout';
25
25
  import { QuoteWarningsAndErrors } from '../components/QuoteWarningsAndErrors';
26
26
  import { navigationRoutes } from '../constants/navigationRoutes';
27
- import { getQuoteUpdateWarningMessage } from '../constants/warnings';
28
27
  import { QuoteInfo } from '../containers/QuoteInfo';
29
28
  import { useConfirmSwap } from '../hooks/useConfirmSwap';
30
29
  import { useAppStore } from '../store/AppStore';
31
30
  import { useQuoteStore } from '../store/quote';
32
31
  import { useUiStore } from '../store/ui';
32
+ import { isQuoteWarningConfirmationRequired } from '../utils/quote';
33
33
  import { joinList } from '../utils/ui';
34
34
 
35
35
  const Buttons = styled('div', {
@@ -133,7 +133,12 @@ export function ConfirmSwapPage() {
133
133
  };
134
134
 
135
135
  const onStartConfirmSwap = async () => {
136
- if (confirmSwapResult?.warnings?.quote && !quoteWarningsConfirmed) {
136
+ const shouldShowWarningModal =
137
+ confirmSwapResult.warnings?.quote &&
138
+ isQuoteWarningConfirmationRequired(confirmSwapResult.warnings.quote) &&
139
+ !quoteWarningsConfirmed;
140
+
141
+ if (shouldShowWarningModal) {
137
142
  setShowQuoteWarningModal(true);
138
143
  } else {
139
144
  await onConfirm();
@@ -196,19 +201,6 @@ export function ConfirmSwapPage() {
196
201
  alerts.push(<Alert type="error" variant="alarm" title={dbErrorMessage} />);
197
202
  }
198
203
 
199
- if (confirmSwapResult.warnings?.quoteUpdate) {
200
- alerts.push(
201
- <Alert
202
- variant="alarm"
203
- type="warning"
204
- title={
205
- confirmSwapResult.warnings.quoteUpdate &&
206
- getQuoteUpdateWarningMessage(confirmSwapResult.warnings.quoteUpdate)
207
- }
208
- />
209
- );
210
- }
211
-
212
204
  if (quoteWarning || quoteError) {
213
205
  const settings_url = `../${navigationRoutes.settings}`;
214
206
  alerts.push(
@@ -21,11 +21,7 @@ import {
21
21
  WidgetEvents,
22
22
  } from '../types';
23
23
  import { isPositiveNumber } from '../utils/numbers';
24
- import {
25
- getQuoteFromTokenUsdPrice,
26
- getQuoteToTokenUsdPrice,
27
- } from '../utils/quote';
28
- import { calcOutputUsdValue } from '../utils/swap';
24
+ import { getUsdInputFrom, getUsdOutputFrom } from '../utils/swap';
29
25
 
30
26
  import createSelectors from './selectors';
31
27
 
@@ -137,14 +133,8 @@ export const useQuoteStore = createSelectors(
137
133
  outputAmount = !!quote?.outputAmount
138
134
  ? new BigNumber(quote?.outputAmount)
139
135
  : null;
140
- inputUsdValue = calcOutputUsdValue(
141
- state.inputAmount,
142
- getQuoteFromTokenUsdPrice(quote) || state.fromToken?.usdPrice
143
- );
144
- outputUsdValue = calcOutputUsdValue(
145
- quote.outputAmount,
146
- getQuoteToTokenUsdPrice(quote) || state.toToken?.usdPrice
147
- );
136
+ inputUsdValue = getUsdInputFrom(quote) ?? ZERO;
137
+ outputUsdValue = getUsdOutputFrom(quote) ?? ZERO;
148
138
  }
149
139
  return {
150
140
  selectedQuote: quote,
@@ -55,7 +55,12 @@ export interface WalletsSlice {
55
55
  setConnectedWalletAsRefetching: (walletType: string) => void;
56
56
  setConnectedWalletHasError: (walletType: string) => void;
57
57
  setConnectedWalletRetrievedData: (walletType: string) => void;
58
- removeBalancesForWallet: (walletType: string) => void;
58
+ removeBalancesForWallet: (
59
+ walletType: string,
60
+ options?: {
61
+ chains?: string[];
62
+ }
63
+ ) => void;
59
64
  addConnectedWallet: (accounts: Wallet[]) => void;
60
65
  setWalletsAsSelected: (
61
66
  wallets: { walletType: string; chain: string }[]
@@ -254,7 +259,7 @@ export const createWalletsSlice: StateCreator<
254
259
 
255
260
  void get().fetchBalances(accounts);
256
261
  },
257
- removeBalancesForWallet: (walletType) => {
262
+ removeBalancesForWallet: (walletType, options) => {
258
263
  let walletsNeedsToBeRemoved = get().connectedWallets.filter(
259
264
  (connectedWallet) => connectedWallet.walletType === walletType
260
265
  );
@@ -275,6 +280,12 @@ export const createWalletsSlice: StateCreator<
275
280
  }
276
281
  });
277
282
 
283
+ if (!!options?.chains && options.chains.length > 0) {
284
+ walletsNeedsToBeRemoved = walletsNeedsToBeRemoved.filter((wallet) => {
285
+ return options.chains?.includes(wallet.chain);
286
+ });
287
+ }
288
+
278
289
  const nextBalancesState: BalanceState = {};
279
290
  let nextAggregatedBalanceState: AggregatedBalanceState =
280
291
  get()._aggregatedBalances;
@@ -51,29 +51,9 @@ export enum QuoteWarningType {
51
51
  UNKNOWN_PRICE,
52
52
  INSUFFICIENT_SLIPPAGE,
53
53
  HIGH_SLIPPAGE,
54
+ EXCESSIVE_OUTPUT_AMOUNT_CHANGE,
54
55
  }
55
56
 
56
- export enum QuoteUpdateType {
57
- QUOTE_UPDATED,
58
- QUOTE_SWAPPERS_UPDATED,
59
- QUOTE_COINS_UPDATED,
60
- QUOTE_AND_OUTPUT_AMOUNT_UPDATED,
61
- QUOTE_UPDATED_WITH_HIGH_VALUE_LOSS,
62
- }
63
-
64
- export type QuoteUpdateWarning =
65
- | {
66
- type: QuoteUpdateType.QUOTE_AND_OUTPUT_AMOUNT_UPDATED;
67
- newOutputAmount: string;
68
- percentageChange: string;
69
- }
70
- | {
71
- type: Exclude<
72
- QuoteUpdateType,
73
- QuoteUpdateType.QUOTE_AND_OUTPUT_AMOUNT_UPDATED
74
- >;
75
- };
76
-
77
57
  export type RecommendedSlippages = Map<number, string>;
78
58
 
79
59
  export type InsufficientSlippageWarning = {
@@ -100,15 +80,21 @@ export type UnknownPriceWarning = {
100
80
  type: QuoteWarningType.UNKNOWN_PRICE;
101
81
  };
102
82
 
83
+ export type ExcessiveOutputAmountChange = {
84
+ type: QuoteWarningType.EXCESSIVE_OUTPUT_AMOUNT_CHANGE;
85
+ usdValueChange: string;
86
+ percentageChange: string;
87
+ };
88
+
103
89
  export type QuoteWarning =
104
90
  | HighValueLossWarning
91
+ | ExcessiveOutputAmountChange
92
+ | UnknownPriceWarning
105
93
  | InsufficientSlippageWarning
106
- | HighSlippageWarning
107
- | UnknownPriceWarning;
94
+ | HighSlippageWarning;
108
95
 
109
96
  export type ConfirmSwapWarnings = {
110
97
  quote: QuoteWarning | null;
111
- quoteUpdate: QuoteUpdateWarning | null;
112
98
  balance: { messages: string[] } | null;
113
99
  };
114
100
 
@@ -1,5 +1,4 @@
1
1
  import type { WalletInfo } from '@rango-dev/ui';
2
- import type { Namespace } from '@rango-dev/wallets-core/namespaces/common';
3
2
  import type { WalletType } from '@rango-dev/wallets-shared';
4
3
 
5
4
  export interface Wallet {
@@ -25,8 +24,4 @@ export type TokensBalance = {
25
24
  [key: TokenHash]: Balance;
26
25
  };
27
26
 
28
- export type WalletInfoWithExtra = WalletInfo & {
29
- namespaces?: Namespace[];
30
- singleNamespace?: boolean;
31
- needsDerivationPath?: boolean;
32
- };
27
+ export type WalletInfoWithExtra = WalletInfo;
@@ -48,6 +48,10 @@ export function matchAndGenerateProviders(
48
48
  const all = allProviders(envs);
49
49
 
50
50
  if (providers) {
51
+ /*
52
+ * If `wallets` is included in widget config,
53
+ * allProviders should be filtered based on wallets list
54
+ */
51
55
  const selectedProviders: VersionedProviders[] = [];
52
56
 
53
57
  providers.forEach((requestedProvider) => {
@@ -57,29 +61,36 @@ export function matchAndGenerateProviders(
57
61
  * The second way is passing a custom provider which implemented ProviderInterface.
58
62
  */
59
63
  if (typeof requestedProvider === 'string') {
60
- const result: BothProvidersInterface | undefined =
61
- pickVersionWithFallbackToLegacy(all, options).find((provider) => {
62
- if (provider instanceof Provider) {
63
- return provider.id === requestedProvider;
64
- }
65
- return provider.config.type === requestedProvider;
66
- });
64
+ const result = all.find((provider) => {
65
+ /*
66
+ * To find a provider in allProviders,
67
+ * a version of each provider should be picked
68
+ * and validated based on that version scheme.
69
+ * If the corresponding provider to a wallet was found in allProvider,
70
+ * it will be add to selected providers.
71
+ */
72
+ const versionedProvider = pickProviderVersionWithFallbackToLegacy(
73
+ provider,
74
+ options
75
+ );
76
+ if (versionedProvider instanceof Provider) {
77
+ return versionedProvider.id === requestedProvider;
78
+ }
79
+ return versionedProvider.config.type === requestedProvider;
80
+ });
67
81
 
82
+ /*
83
+ * A provider may have multiple versions
84
+ * (e.g., 0.0, also known as legacy, and 1.0, also known as hub).
85
+ * We should ensure that all existing versions of a provider are added to selectedProviders.
86
+ */
68
87
  if (result) {
69
- if (result instanceof Provider) {
70
- selectedProviders.push(
71
- defineVersions().version('1.0.0', result).build()
72
- );
73
- } else {
74
- selectedProviders.push(
75
- defineVersions().version('0.0.0', result).build()
76
- );
77
- }
78
- } else {
79
- console.warn(
80
- `Couldn't find ${requestedProvider} provider. Please make sure you are passing the correct name.`
81
- );
88
+ selectedProviders.push(result);
82
89
  }
90
+ console.warn(
91
+ // A provider name is included in config but was not found in allProviders
92
+ `Couldn't find ${requestedProvider} provider. Please make sure you are passing the correct name.`
93
+ );
83
94
  } else {
84
95
  // It's a custom provider so we directly push it to the list.
85
96
  if (requestedProvider instanceof Provider) {
@@ -100,31 +111,27 @@ export function matchAndGenerateProviders(
100
111
  return all;
101
112
  }
102
113
 
103
- // TODO: this is a duplication with what we do in core.
104
- function pickVersionWithFallbackToLegacy(
105
- providers: VersionedProviders[],
114
+ function pickProviderVersionWithFallbackToLegacy(
115
+ provider: VersionedProviders,
106
116
  options?: ProvidersOptions
107
- ): BothProvidersInterface[] {
117
+ ): BothProvidersInterface {
108
118
  const { experimentalWallet = 'enabled' } = options || {};
119
+ const version = experimentalWallet == 'disabled' ? '0.0.0' : '1.0.0';
109
120
 
110
- return providers.map((provider) => {
111
- const version = experimentalWallet == 'disabled' ? '0.0.0' : '1.0.0';
112
- try {
113
- return pickVersion(provider, version)[1];
114
- } catch {
115
- // Fallback to legacy version, if target version doesn't exists.
116
- return pickVersion(provider, '0.0.0')[1];
117
- }
118
- });
121
+ try {
122
+ return pickVersion(provider, version)[1];
123
+ } catch {
124
+ // Fallback to legacy version, if target version doesn't exists.
125
+ return pickVersion(provider, '0.0.0')[1];
126
+ }
119
127
  }
120
128
 
121
129
  export function configWalletsToWalletName(
122
130
  config: WidgetConfig['wallets'],
123
131
  options?: ProvidersOptions
124
132
  ): string[] {
125
- const providers = pickVersionWithFallbackToLegacy(
126
- matchAndGenerateProviders(config, options),
127
- options
133
+ const providers = matchAndGenerateProviders(config, options).map((provider) =>
134
+ pickProviderVersionWithFallbackToLegacy(provider, options)
128
135
  );
129
136
  const names = providers.map((provider) => {
130
137
  if (provider instanceof Provider) {
@@ -15,7 +15,6 @@ import type {
15
15
  MultiRouteSimulationResult,
16
16
  PreferenceType,
17
17
  RouteTag,
18
- Token,
19
18
  } from 'rango-sdk';
20
19
  import type { PendingSwap } from 'rango-types';
21
20
 
@@ -29,30 +28,19 @@ import { QuoteWarningType } from '../types';
29
28
 
30
29
  import { areEqual } from './common';
31
30
  import { createTokenHash, findBlockchain } from './meta';
31
+ import { numberToString } from './numbers';
32
32
  import {
33
- calcOutputUsdValue,
34
33
  checkSlippageWarnings,
35
34
  getMinRequiredSlippage,
36
35
  getPercentageChange,
37
36
  getTotalFeeInUsd,
37
+ getUsdInputFrom,
38
+ getUsdOutputFrom,
38
39
  hasHighValueLoss,
39
40
  hasProperSlippage,
41
+ isOutputAmountChangedExcessively,
40
42
  } from './swap';
41
43
 
42
- export function getQuoteToTokenUsdPrice(
43
- quote: SelectedQuote | null
44
- ): number | null | undefined {
45
- const swaps = quote?.swaps || [];
46
- return swaps[swaps.length - 1].to.usdPrice;
47
- }
48
-
49
- export function getQuoteFromTokenUsdPrice(
50
- quote: SelectedQuote | null
51
- ): number | null | undefined {
52
- const swaps = quote?.swaps || [];
53
- return swaps[0].from.usdPrice;
54
- }
55
-
56
44
  export function isNumberOfSwapsChanged(
57
45
  quoteA: SelectedQuote,
58
46
  quoteB: SelectedQuote
@@ -195,56 +183,73 @@ export function createRetryQuote(
195
183
  };
196
184
  }
197
185
 
198
- export function generateQuoteWarnings(
199
- quote: SelectedQuote,
200
- params: {
201
- fromToken: Token;
202
- toToken: Token;
203
- findToken: FindToken;
204
- userSlippage: number;
205
- }
206
- ): QuoteWarning | null {
207
- const { fromToken, toToken, findToken, userSlippage } = params;
208
- const inputUsdValue = calcOutputUsdValue(
209
- quote.requestAmount,
210
- getQuoteFromTokenUsdPrice(quote) || fromToken?.usdPrice
211
- );
212
- const outputUsdValue = calcOutputUsdValue(
213
- quote.outputAmount,
214
- getQuoteToTokenUsdPrice(quote) || toToken?.usdPrice
215
- );
216
-
217
- if (!!quote && inputUsdValue && outputUsdValue) {
186
+ export function generateQuoteWarnings(params: {
187
+ previousQuote?: SelectedQuote;
188
+ currentQuote: SelectedQuote;
189
+ findToken: FindToken;
190
+ userSlippage: number;
191
+ }): QuoteWarning | null {
192
+ const { previousQuote, currentQuote, findToken, userSlippage } = params;
193
+ const usdInput = getUsdInputFrom(currentQuote);
194
+ const usdOutput = getUsdOutputFrom(currentQuote);
195
+
196
+ if (!!currentQuote && usdInput && usdOutput) {
218
197
  const priceImpact = getPriceImpact(
219
- inputUsdValue.toString(),
220
- outputUsdValue.toString()
198
+ usdInput.toString(),
199
+ usdOutput.toString()
221
200
  );
222
201
  const highValueLoss =
223
- !!priceImpact && hasHighValueLoss(inputUsdValue, priceImpact);
202
+ !!priceImpact && hasHighValueLoss(usdInput, priceImpact);
224
203
 
225
204
  if (highValueLoss) {
226
- const totalFee = getTotalFeeInUsd(quote?.swaps, findToken);
205
+ const totalFee = getTotalFeeInUsd(currentQuote?.swaps, findToken);
227
206
  const warningLevel = getPriceImpactLevel(priceImpact);
228
207
  return {
229
208
  type: QuoteWarningType.HIGH_VALUE_LOSS,
230
- inputUsdValue,
231
- outputUsdValue,
209
+ inputUsdValue: usdInput,
210
+ outputUsdValue: usdOutput,
232
211
  priceImpact,
233
212
  totalFee,
234
213
  warningLevel,
235
214
  };
236
215
  }
237
- } else if (quote && (!inputUsdValue || !outputUsdValue)) {
216
+ }
217
+
218
+ if (
219
+ previousQuote &&
220
+ isOutputAmountChangedExcessively(previousQuote, currentQuote)
221
+ ) {
222
+ return {
223
+ type: QuoteWarningType.EXCESSIVE_OUTPUT_AMOUNT_CHANGE,
224
+ usdValueChange: numberToString(
225
+ getUsdOutputFrom(currentQuote)
226
+ ?.minus(getUsdOutputFrom(previousQuote) ?? 0)
227
+ .toString() ?? '0',
228
+ null,
229
+ 2
230
+ ),
231
+ percentageChange: numberToString(
232
+ getPriceImpact(
233
+ getUsdOutputFrom(previousQuote) ?? '1',
234
+ getUsdOutputFrom(currentQuote) ?? '1'
235
+ ),
236
+ null,
237
+ 2
238
+ ),
239
+ };
240
+ }
241
+
242
+ if (currentQuote && (!usdInput || !usdOutput)) {
238
243
  return { type: QuoteWarningType.UNKNOWN_PRICE };
239
244
  }
240
245
 
241
- const minRequiredSlippage = getMinRequiredSlippage(quote.swaps);
246
+ const minRequiredSlippage = getMinRequiredSlippage(currentQuote.swaps);
242
247
  const highSlippage = userSlippage > HIGH_SLIPPAGE;
243
248
 
244
249
  if (!hasProperSlippage(params.userSlippage.toString(), minRequiredSlippage)) {
245
250
  return {
246
251
  type: QuoteWarningType.INSUFFICIENT_SLIPPAGE,
247
- recommendedSlippages: checkSlippageWarnings(quote, userSlippage),
252
+ recommendedSlippages: checkSlippageWarnings(currentQuote, userSlippage),
248
253
  minRequiredSlippage: minRequiredSlippage,
249
254
  };
250
255
  } else if (
@@ -260,6 +265,13 @@ export function generateQuoteWarnings(
260
265
  return null;
261
266
  }
262
267
 
268
+ export function isQuoteWarningConfirmationRequired(warning: QuoteWarning) {
269
+ const WARNINGS_NOT_REQUIRING_CONFIRMATION = [
270
+ QuoteWarningType.EXCESSIVE_OUTPUT_AMOUNT_CHANGE,
271
+ ];
272
+ return !WARNINGS_NOT_REQUIRING_CONFIRMATION.includes(warning.type);
273
+ }
274
+
263
275
  export function getPriceImpact(
264
276
  inputUsdValue: BigNumber | string | null,
265
277
  outputUsdValue: BigNumber | string | null