@rango-dev/widget-embedded 0.42.3-next.2 → 0.42.3-next.4

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 (99) hide show
  1. package/dist/components/NoResult/NoResult.d.ts.map +1 -1
  2. package/dist/components/NoResult/NoResult.types.d.ts +1 -0
  3. package/dist/components/NoResult/NoResult.types.d.ts.map +1 -1
  4. package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.d.ts.map +1 -1
  5. package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.helpers.d.ts +5 -3
  6. package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.helpers.d.ts.map +1 -1
  7. package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.styles.d.ts +440 -0
  8. package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.styles.d.ts.map +1 -1
  9. package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.types.d.ts +2 -0
  10. package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.types.d.ts.map +1 -1
  11. package/dist/components/QuoteWarningsAndErrors/SlippageWariningModal.d.ts.map +1 -1
  12. package/dist/components/Slippage/Slippage.d.ts.map +1 -1
  13. package/dist/components/Slippage/Slippage.styles.d.ts +162 -0
  14. package/dist/components/Slippage/Slippage.styles.d.ts.map +1 -1
  15. package/dist/components/SlippageWarningsAndErrors/SlippageWarningsAndErrors.d.ts +4 -0
  16. package/dist/components/SlippageWarningsAndErrors/SlippageWarningsAndErrors.d.ts.map +1 -0
  17. package/dist/components/SlippageWarningsAndErrors/SlippageWarningsAndErrors.helpers.d.ts +8 -0
  18. package/dist/components/SlippageWarningsAndErrors/SlippageWarningsAndErrors.helpers.d.ts.map +1 -0
  19. package/dist/components/SlippageWarningsAndErrors/SlippageWarningsAndErrors.types.d.ts +4 -0
  20. package/dist/components/SlippageWarningsAndErrors/SlippageWarningsAndErrors.types.d.ts.map +1 -0
  21. package/dist/components/SwapMetrics/SwapMetrics.constants.d.ts +5 -0
  22. package/dist/components/SwapMetrics/SwapMetrics.constants.d.ts.map +1 -0
  23. package/dist/components/SwapMetrics/SwapMetrics.d.ts +4 -0
  24. package/dist/components/SwapMetrics/SwapMetrics.d.ts.map +1 -0
  25. package/dist/components/SwapMetrics/SwapMetrics.helpers.d.ts +11 -0
  26. package/dist/components/SwapMetrics/SwapMetrics.helpers.d.ts.map +1 -0
  27. package/dist/components/SwapMetrics/SwapMetrics.styles.d.ts +482 -0
  28. package/dist/components/SwapMetrics/SwapMetrics.styles.d.ts.map +1 -0
  29. package/dist/components/SwapMetrics/SwapMetrics.types.d.ts +27 -0
  30. package/dist/components/SwapMetrics/SwapMetrics.types.d.ts.map +1 -0
  31. package/dist/components/SwapMetrics/index.d.ts +2 -0
  32. package/dist/components/SwapMetrics/index.d.ts.map +1 -0
  33. package/dist/containers/Inputs/Inputs.d.ts.map +1 -1
  34. package/dist/hooks/useSyncUrlAndStore/useSyncUrlAndStore.d.ts.map +1 -1
  35. package/dist/index.js +2 -2
  36. package/dist/index.js.map +4 -4
  37. package/dist/pages/ConfirmSwapPage.d.ts.map +1 -1
  38. package/dist/pages/Home.d.ts.map +1 -1
  39. package/dist/pages/LiquiditySourcePage.d.ts.map +1 -1
  40. package/dist/store/AppStore.d.ts +2 -0
  41. package/dist/store/AppStore.d.ts.map +1 -1
  42. package/dist/store/app.d.ts +2 -0
  43. package/dist/store/app.d.ts.map +1 -1
  44. package/dist/store/quote.d.ts +2 -0
  45. package/dist/store/quote.d.ts.map +1 -1
  46. package/dist/store/slices/settings.d.ts +3 -0
  47. package/dist/store/slices/settings.d.ts.map +1 -1
  48. package/dist/utils/colors.d.ts.map +1 -1
  49. package/dist/utils/numbers.d.ts +1 -0
  50. package/dist/utils/numbers.d.ts.map +1 -1
  51. package/dist/utils/sanitizers.d.ts +27 -0
  52. package/dist/utils/sanitizers.d.ts.map +1 -0
  53. package/dist/utils/sanitizers.test.d.ts +2 -0
  54. package/dist/utils/sanitizers.test.d.ts.map +1 -0
  55. package/dist/utils/settings.d.ts +2 -1
  56. package/dist/utils/settings.d.ts.map +1 -1
  57. package/dist/utils/validation.d.ts +26 -0
  58. package/dist/utils/validation.d.ts.map +1 -0
  59. package/dist/utils/validation.test.d.ts +2 -0
  60. package/dist/utils/validation.test.d.ts.map +1 -0
  61. package/dist/utils/wallets.d.ts.map +1 -1
  62. package/dist/widget-embedded.build.json +1 -1
  63. package/package.json +3 -3
  64. package/src/components/NoResult/NoResult.tsx +4 -1
  65. package/src/components/NoResult/NoResult.types.ts +1 -0
  66. package/src/components/Quote/Quote.tsx +1 -1
  67. package/src/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.helpers.ts +29 -4
  68. package/src/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.styles.ts +10 -1
  69. package/src/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.tsx +38 -7
  70. package/src/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.types.ts +2 -0
  71. package/src/components/QuoteWarningsAndErrors/SlippageWariningModal.tsx +20 -21
  72. package/src/components/Slippage/Slippage.styles.ts +23 -0
  73. package/src/components/Slippage/Slippage.tsx +28 -22
  74. package/src/components/SlippageWarningsAndErrors/SlippageWarningsAndErrors.helpers.ts +33 -0
  75. package/src/components/SlippageWarningsAndErrors/SlippageWarningsAndErrors.tsx +48 -0
  76. package/src/components/SlippageWarningsAndErrors/SlippageWarningsAndErrors.types.ts +3 -0
  77. package/src/components/SwapMetrics/SwapMetrics.constants.ts +4 -0
  78. package/src/components/SwapMetrics/SwapMetrics.helpers.ts +76 -0
  79. package/src/components/SwapMetrics/SwapMetrics.styles.ts +32 -0
  80. package/src/components/SwapMetrics/SwapMetrics.tsx +134 -0
  81. package/src/components/SwapMetrics/SwapMetrics.types.ts +26 -0
  82. package/src/components/SwapMetrics/index.ts +1 -0
  83. package/src/containers/Inputs/Inputs.tsx +2 -0
  84. package/src/hooks/useSwapInput.ts +1 -1
  85. package/src/hooks/useSyncUrlAndStore/useSyncUrlAndStore.ts +2 -0
  86. package/src/pages/ConfirmSwapPage.tsx +6 -1
  87. package/src/pages/Home.tsx +61 -12
  88. package/src/pages/LiquiditySourcePage.tsx +5 -3
  89. package/src/store/app.ts +1 -0
  90. package/src/store/quote.ts +23 -4
  91. package/src/store/slices/settings.ts +11 -0
  92. package/src/utils/colors.ts +3 -10
  93. package/src/utils/numbers.ts +11 -0
  94. package/src/utils/sanitizers.test.ts +122 -0
  95. package/src/utils/sanitizers.ts +41 -0
  96. package/src/utils/settings.ts +11 -4
  97. package/src/utils/validation.test.ts +121 -0
  98. package/src/utils/validation.ts +45 -0
  99. package/src/utils/wallets.ts +2 -1
@@ -0,0 +1,134 @@
1
+ import type { PropTypes } from './SwapMetrics.types';
2
+
3
+ import { i18n } from '@lingui/core';
4
+ import {
5
+ IconButton,
6
+ ReverseIcon,
7
+ Skeleton,
8
+ Tooltip,
9
+ Typography,
10
+ } from '@rango-dev/ui';
11
+ import React from 'react';
12
+
13
+ import { useTheme } from '../../hooks/useTheme';
14
+ import { useAppStore } from '../../store/AppStore';
15
+ import { getContainer } from '../../utils/common';
16
+ import { getSlippageValidation } from '../../utils/settings';
17
+
18
+ import {
19
+ formatTokenValueInUsd,
20
+ getSlippageColor,
21
+ getUsdExchangeRate,
22
+ } from './SwapMetrics.helpers';
23
+ import { Container, Rate, TokenName } from './SwapMetrics.styles';
24
+
25
+ export function SwapMetrics(props: PropTypes) {
26
+ const { slippage, customSlippage, quoteTokensRate, changeQuoteTokensRate } =
27
+ useAppStore();
28
+ const {
29
+ quoteError,
30
+ quoteWarning,
31
+ fromToken: initialFromToken,
32
+ toToken: initialToToken,
33
+ quote,
34
+ loading,
35
+ } = props;
36
+ const currentSlippage = customSlippage !== null ? customSlippage : slippage;
37
+ const { mode } = useTheme({});
38
+ const slippageValidation = getSlippageValidation(currentSlippage);
39
+ const isDarkTheme = mode === 'dark';
40
+ const isDefaultRate = quoteTokensRate === 'default';
41
+
42
+ const error = {
43
+ quoteError,
44
+ slippageError:
45
+ slippageValidation?.type === 'error' ? slippageValidation.message : null,
46
+ };
47
+ const warning = {
48
+ quoteWarning,
49
+ slippageWarning:
50
+ slippageValidation?.type === 'warning'
51
+ ? slippageValidation.message
52
+ : null,
53
+ };
54
+
55
+ const sourceToken = quote?.swaps[0].from || initialFromToken;
56
+ const destinationToken =
57
+ quote?.swaps[quote?.swaps.length - 1].to || initialToToken;
58
+
59
+ const fromToken = isDefaultRate ? sourceToken : destinationToken;
60
+ const toToken = isDefaultRate ? destinationToken : sourceToken;
61
+
62
+ const fromAmount = Number(
63
+ isDefaultRate ? quote?.outputAmount : quote?.requestAmount
64
+ );
65
+ const toAmount = Number(
66
+ isDefaultRate ? quote?.requestAmount : quote?.outputAmount
67
+ );
68
+
69
+ const fromTokenUsdPrice = fromAmount || fromToken.usdPrice;
70
+ const toTokenUsdPrice = toAmount || toToken.usdPrice;
71
+
72
+ const { rawValue: rawExchangeRate, displayValue: displayExchangeRate } =
73
+ getUsdExchangeRate({
74
+ toTokenUsdPrice,
75
+ fromTokenUsdPrice,
76
+ });
77
+
78
+ return (
79
+ <Container>
80
+ <Typography
81
+ variant={!!error || !!warning ? 'label' : 'body'}
82
+ size={!!error || !!warning ? 'medium' : 'small'}
83
+ color={getSlippageColor({ error, warning, isDarkTheme })}>
84
+ {i18n.t('Slippage:')} {currentSlippage}%
85
+ </Typography>
86
+ {loading ? (
87
+ <Skeleton height={16} width={104} variant="rounded" />
88
+ ) : (
89
+ fromTokenUsdPrice &&
90
+ toTokenUsdPrice && (
91
+ <Rate>
92
+ <Typography className="rate-text" variant="body" size="small">
93
+ 1
94
+ </Typography>
95
+ <TokenName className="rate-text" variant="body" size="small">
96
+ {toToken.symbol}
97
+ </TokenName>
98
+ <IconButton
99
+ id="widget-home-page-change-rate-button"
100
+ onClick={changeQuoteTokensRate}>
101
+ <ReverseIcon size={14} color="secondary" />
102
+ </IconButton>
103
+ <Tooltip
104
+ container={getContainer()}
105
+ side="top"
106
+ sideOffset={4}
107
+ content={
108
+ <Typography className="rate-text" variant="body" size="small">
109
+ {rawExchangeRate}
110
+ </Typography>
111
+ }>
112
+ <Typography className="rate-text" variant="body" size="small">
113
+ {displayExchangeRate}
114
+ </Typography>
115
+ </Tooltip>
116
+
117
+ <TokenName className="rate-text" variant="body" size="small">
118
+ {fromToken.symbol}
119
+ </TokenName>
120
+ {fromToken.usdPrice && (
121
+ <Typography color="neutral600" variant="body" size="small">
122
+ ~
123
+ {formatTokenValueInUsd(
124
+ Number(rawExchangeRate),
125
+ fromToken.usdPrice
126
+ )}
127
+ </Typography>
128
+ )}
129
+ </Rate>
130
+ )
131
+ )}
132
+ </Container>
133
+ );
134
+ }
@@ -0,0 +1,26 @@
1
+ import type { QuoteError, QuoteWarning, SelectedQuote } from '../../types';
2
+ import type { TokenData } from '../TokenList/TokenList.types';
3
+ import type { SwapResultAsset } from 'rango-sdk';
4
+
5
+ export interface PropTypes {
6
+ quoteError: QuoteError | null;
7
+ quoteWarning: QuoteWarning | null;
8
+ fromToken: TokenData;
9
+ toToken: TokenData;
10
+ quote: SelectedQuote | null;
11
+ loading: boolean;
12
+ }
13
+
14
+ export type Tokens = {
15
+ to: TokenData | SwapResultAsset;
16
+ from: TokenData | SwapResultAsset;
17
+ };
18
+
19
+ export type SlippageColorParams = {
20
+ error: { quoteError: QuoteError | null; slippageError: string | null };
21
+ warning: {
22
+ quoteWarning: QuoteWarning | null;
23
+ slippageWarning: string | null;
24
+ };
25
+ isDarkTheme: boolean;
26
+ };
@@ -0,0 +1 @@
1
+ export { SwapMetrics } from './SwapMetrics';
@@ -34,6 +34,7 @@ export function Inputs(props: PropTypes) {
34
34
  toToken,
35
35
  toBlockchain,
36
36
  setInputAmount,
37
+ sanitizeInputAmount,
37
38
  inputAmount,
38
39
  inputUsdValue,
39
40
  outputAmount,
@@ -79,6 +80,7 @@ export function Inputs(props: PropTypes) {
79
80
  label={i18n.t('From')}
80
81
  mode="From"
81
82
  onInputChange={setInputAmount}
83
+ onInputBlur={sanitizeInputAmount}
82
84
  balance={fromTokenFormattedBalance}
83
85
  chain={{
84
86
  displayName: fromBlockchain?.displayName || '',
@@ -205,7 +205,7 @@ export function useSwapInput({
205
205
  }
206
206
  return;
207
207
  }
208
- if (!isPositiveNumber(inputAmount) || inputUsdValue?.eq(0)) {
208
+ if (!isPositiveNumber(inputAmount) && inputUsdValue?.eq(0)) {
209
209
  resetState(false);
210
210
  cancelFetch();
211
211
  return;
@@ -83,6 +83,7 @@ export function useSyncUrlAndStore() {
83
83
  useEffect(() => {
84
84
  const { autoConnect, clientUrl, utmQueryParams, blockchain } =
85
85
  getUrlSearchParams();
86
+
86
87
  if (isInRouterContext && fetchMetaStatus === 'success') {
87
88
  updateUrlSearchParams({
88
89
  [SearchParams.FROM_BLOCKCHAIN]: fromBlockchain?.name,
@@ -107,6 +108,7 @@ export function useSyncUrlAndStore() {
107
108
  toBlockchain,
108
109
  toToken,
109
110
  campaignMode,
111
+ fetchMetaStatus,
110
112
  ]);
111
113
 
112
114
  useEffect(() => {
@@ -1,4 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-magic-numbers */
2
1
  import type {
3
2
  ConfirmSwap,
4
3
  ConfirmSwapFetchResult,
@@ -29,6 +28,7 @@ import { useConfirmSwap } from '../hooks/useConfirmSwap';
29
28
  import { useAppStore } from '../store/AppStore';
30
29
  import { useQuoteStore } from '../store/quote';
31
30
  import { useUiStore } from '../store/ui';
31
+ import { QuoteErrorType, QuoteWarningType } from '../types';
32
32
  import { isQuoteWarningConfirmationRequired } from '../utils/quote';
33
33
  import { joinList } from '../utils/ui';
34
34
 
@@ -121,6 +121,7 @@ export function ConfirmSwapPage() {
121
121
  setInputAmount('');
122
122
  }, 0);
123
123
  } catch (e) {
124
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
124
125
  setDbErrorMessage('Error: ' + (e as any)?.message);
125
126
  }
126
127
  }
@@ -209,6 +210,10 @@ export function ConfirmSwapPage() {
209
210
  error={quoteError}
210
211
  couldChangeSettings={false}
211
212
  refetchQuote={onRefresh}
213
+ skipAlerts={
214
+ quoteError?.type === QuoteErrorType.INSUFFICIENT_SLIPPAGE ||
215
+ quoteWarning?.type === QuoteWarningType.INSUFFICIENT_SLIPPAGE
216
+ }
212
217
  showWarningModal={showQuoteWarningModal}
213
218
  confirmationDisabled={!isActiveTab}
214
219
  onOpenWarningModal={() => setShowQuoteWarningModal(true)}
@@ -10,6 +10,8 @@ import { HeaderButtons } from '../components/HeaderButtons';
10
10
  import { Layout, PageContainer } from '../components/Layout';
11
11
  import { QuoteWarningsAndErrors } from '../components/QuoteWarningsAndErrors';
12
12
  import { SameTokensWarning } from '../components/SameTokensWarning';
13
+ import { SlippageWarningsAndErrors } from '../components/SlippageWarningsAndErrors/SlippageWarningsAndErrors';
14
+ import { SwapMetrics } from '../components/SwapMetrics';
13
15
  import { navigationRoutes } from '../constants/navigationRoutes';
14
16
  import { ExpandedQuotes } from '../containers/ExpandedQuotes';
15
17
  import { Inputs } from '../containers/Inputs';
@@ -22,6 +24,7 @@ import { useUiStore } from '../store/ui';
22
24
  import { UiEventTypes } from '../types';
23
25
  import { isVariantExpandable } from '../utils/configs';
24
26
  import { emitPreventableEvent } from '../utils/events';
27
+ import { getSlippageValidation } from '../utils/settings';
25
28
  import { getSwapButtonState, isTokensIdentical } from '../utils/swap';
26
29
 
27
30
  const MainContainer = styled('div', {
@@ -50,6 +53,7 @@ export function Home() {
50
53
  setQuoteWarningsConfirmed,
51
54
  updateQuotePartialState,
52
55
  } = useQuoteStore();
56
+
53
57
  const [isVisibleExpanded, setIsVisibleExpanded] = useState<boolean>(false);
54
58
  const { isLargeScreen, isExtraLargeScreen } = useScreenDetect();
55
59
 
@@ -58,10 +62,17 @@ export function Home() {
58
62
  config,
59
63
  fetchStatus: fetchMetaStatus,
60
64
  connectedWallets,
65
+ customSlippage,
66
+ slippage,
67
+ setSlippage,
68
+ setCustomSlippage,
61
69
  } = useAppStore();
62
70
 
63
71
  const { isActiveTab } = useUiStore();
64
72
  const [showQuoteWarningModal, setShowQuoteWarningModal] = useState(false);
73
+ const currentSlippage = customSlippage !== null ? customSlippage : slippage;
74
+
75
+ const slippageValidation = getSlippageValidation(currentSlippage);
65
76
 
66
77
  const needsToWarnEthOnPath = false;
67
78
 
@@ -91,19 +102,16 @@ export function Home() {
91
102
 
92
103
  const fetchingQuote = hasInputs && fetchMetaStatus === 'success' && loading;
93
104
 
105
+ const currentQuoteWarning =
106
+ slippageValidation?.quoteValidation || quoteWarning;
107
+
94
108
  const hasValidQuotes =
95
109
  !isExpandable || (isExpandable && quotes?.results.length);
96
- const hasWarningOrError = quoteWarning || quoteError;
110
+ const hasWarningOrError = currentQuoteWarning || quoteError;
97
111
  const showMessages = hasValidQuotes && hasWarningOrError;
98
112
 
99
- useEffect(() => {
100
- resetQuoteWallets();
101
- updateQuotePartialState('refetchQuote', true);
102
- }, []);
103
-
104
- useEffect(() => {
105
- setIsVisibleExpanded(hasInputs);
106
- }, [hasInputs]);
113
+ const showSwapMetrics = !!fromToken && !!toToken;
114
+ const showSlippageAlerts = showSwapMetrics && !!slippageValidation;
107
115
 
108
116
  const onClickRefresh =
109
117
  (!!selectedQuote || quoteError) && !showQuoteWarningModal
@@ -127,6 +135,22 @@ export function Home() {
127
135
  }
128
136
  };
129
137
 
138
+ const onChangeSlippage = (slippage: number | null) => {
139
+ if (slippage) {
140
+ setSlippage(slippage);
141
+ setCustomSlippage(null);
142
+ }
143
+ };
144
+
145
+ useEffect(() => {
146
+ resetQuoteWallets();
147
+ updateQuotePartialState('refetchQuote', true);
148
+ }, []);
149
+
150
+ useEffect(() => {
151
+ setIsVisibleExpanded(hasInputs);
152
+ }, [hasInputs]);
153
+
130
154
  return (
131
155
  <MainContainer>
132
156
  <Layout
@@ -194,7 +218,7 @@ export function Home() {
194
218
  loading={fetchingQuote}
195
219
  error={quoteError}
196
220
  tagHidden={false}
197
- warning={quoteWarning}
221
+ warning={currentQuoteWarning}
198
222
  type="basic"
199
223
  onClickAllRoutes={
200
224
  !!quotes && quotes.results.length > 1
@@ -206,19 +230,33 @@ export function Home() {
206
230
  }
207
231
  />
208
232
  ) : null}
233
+ {showSwapMetrics && (
234
+ <>
235
+ <Divider size={8} />
236
+ <SwapMetrics
237
+ quoteError={quoteError}
238
+ quoteWarning={currentQuoteWarning}
239
+ fromToken={fromToken}
240
+ toToken={toToken}
241
+ quote={selectedQuote}
242
+ loading={fetchingQuote}
243
+ />
244
+ </>
245
+ )}
209
246
 
210
247
  {showMessages ? (
211
248
  <>
212
- <Divider size="10" />
213
249
  <QuoteWarningsAndErrors
214
- warning={quoteWarning}
250
+ warning={currentQuoteWarning}
215
251
  error={quoteError}
252
+ skipAlerts={!!slippageValidation}
216
253
  couldChangeSettings={true}
217
254
  refetchQuote={fetchQuote}
218
255
  showWarningModal={showQuoteWarningModal}
219
256
  confirmationDisabled={!isActiveTab}
220
257
  onOpenWarningModal={() => setShowQuoteWarningModal(true)}
221
258
  onCloseWarningModal={() => setShowQuoteWarningModal(false)}
259
+ onChangeSlippage={onChangeSlippage}
222
260
  onConfirmWarningModal={() => {
223
261
  setShowQuoteWarningModal(false);
224
262
  setQuoteWarningsConfirmed(true);
@@ -230,6 +268,17 @@ export function Home() {
230
268
  />
231
269
  </>
232
270
  ) : null}
271
+
272
+ {showSlippageAlerts && (
273
+ <>
274
+ <Divider size="10" />
275
+ <SlippageWarningsAndErrors
276
+ onChangeSettings={() =>
277
+ onHandleNavigation(navigationRoutes.settings)
278
+ }
279
+ />
280
+ </>
281
+ )}
233
282
  <SameTokensWarning />
234
283
  </PageContainer>
235
284
  </Layout>
@@ -22,6 +22,7 @@ import {
22
22
  } from '../components/SettingsContainer';
23
23
  import { useAppStore } from '../store/AppStore';
24
24
  import { containsText } from '../utils/numbers';
25
+ import { replaceSpacesWithDash } from '../utils/sanitizers';
25
26
  import { getUniqueSwappersGroups } from '../utils/settings';
26
27
 
27
28
  interface PropTypes {
@@ -70,9 +71,10 @@ export function LiquiditySourcePage({ sourceType }: PropTypes) {
70
71
  const list = liquiditySources.map((sourceItem) => {
71
72
  const { selected, groupTitle, logo, id, ...restSourceItem } = sourceItem;
72
73
  return {
73
- id: `widget-setting-liquidity-source-${id
74
- .toLowerCase()
75
- .replace(/\s+/g, '-')}-item-btn`,
74
+ id: `widget-setting-liquidity-source-${replaceSpacesWithDash(
75
+ id.toLowerCase()
76
+ )}-item-btn`,
77
+
76
78
  start: <Image src={logo} size={22} type="circular" />,
77
79
  onClick: () => {
78
80
  if (!campaignMode) {
package/src/store/app.ts CHANGED
@@ -46,6 +46,7 @@ export function createAppStore(initialData?: WidgetConfig) {
46
46
  infiniteApprove: state.infiniteApprove,
47
47
  preferredBlockchains: state.preferredBlockchains,
48
48
  disabledLiquiditySources: state.disabledLiquiditySources,
49
+ quoteTokensRate: state.quoteTokensRate,
49
50
  };
50
51
  },
51
52
  version: 1,
@@ -21,8 +21,13 @@ import {
21
21
  type Wallet,
22
22
  WidgetEvents,
23
23
  } from '../types';
24
- import { isPositiveNumber } from '../utils/numbers';
24
+ import { isPositiveNumber, sanitizeInputAmount } from '../utils/numbers';
25
+ import {
26
+ ensureLeadingZeroForDecimal,
27
+ removeLeadingZeros,
28
+ } from '../utils/sanitizers';
25
29
  import { getUsdInputFrom, getUsdOutputFrom } from '../utils/swap';
30
+ import { isZeroValue } from '../utils/validation';
26
31
 
27
32
  import createSelectors from './selectors';
28
33
 
@@ -87,6 +92,7 @@ export interface QuoteState {
87
92
  value: SomeQuoteState[K]
88
93
  ) => void;
89
94
  setInputAmount: (amount: string) => void;
95
+ sanitizeInputAmount: (amount: string) => void;
90
96
  setSelectedQuote: (quote: SelectedQuote | null) => void;
91
97
  retry: (retryQuote: RetryQuote) => void;
92
98
  switchFromAndTo: () => void;
@@ -223,16 +229,29 @@ export const useQuoteStore = createSelectors(
223
229
  }),
224
230
  }));
225
231
  },
232
+ sanitizeInputAmount: (amount) => {
233
+ const sanitized = sanitizeInputAmount(amount);
234
+
235
+ set(() => ({
236
+ inputAmount: sanitized,
237
+ }));
238
+ },
226
239
  setInputAmount: (amount) => {
240
+ let sanitized = amount;
241
+ if (!isZeroValue(amount)) {
242
+ // sanitize once a meaningful digit is entered (e.g. "00001" → "1")
243
+ sanitized = removeLeadingZeros(sanitized);
244
+ sanitized = ensureLeadingZeroForDecimal(sanitized);
245
+ }
227
246
  set((state) => ({
228
- inputAmount: amount,
229
- ...(!amount && {
247
+ inputAmount: sanitized,
248
+ ...(!sanitized && {
230
249
  outputAmount: null,
231
250
  outputUsdValue: new BigNumber(0),
232
251
  selectedQuote: null,
233
252
  }),
234
253
  ...(!!state.fromToken && {
235
- inputUsdValue: getUsdValue(state.fromToken, amount),
254
+ inputUsdValue: getUsdValue(state.fromToken, sanitized),
236
255
  }),
237
256
  }));
238
257
  },
@@ -16,6 +16,7 @@ import { isFeatureHidden } from '../../utils/settings';
16
16
  import { getSupportedBlockchainsFromConfig } from '../utils';
17
17
 
18
18
  export type ThemeMode = 'auto' | 'dark' | 'light';
19
+ export type QuoteTokensRate = 'default' | 'reversed';
19
20
 
20
21
  export interface SettingsSlice {
21
22
  theme: ThemeMode;
@@ -30,6 +31,8 @@ export interface SettingsSlice {
30
31
  affiliatePercent: number | null;
31
32
  affiliateWallets: { [key: string]: string } | null;
32
33
  _customTokens: Token[];
34
+ quoteTokensRate: QuoteTokensRate;
35
+
33
36
  setSlippage: (slippage: number) => void;
34
37
  setCustomSlippage: (customSlippage: number | null) => void;
35
38
  toggleInfiniteApprove: () => void;
@@ -50,6 +53,7 @@ export interface SettingsSlice {
50
53
  setCustomToken: (token: TokenData) => void;
51
54
  deleteCustomToken: (token: Token) => void;
52
55
  customTokens: () => Token[];
56
+ changeQuoteTokensRate: () => void;
53
57
  }
54
58
 
55
59
  export const createSettingsSlice: StateCreator<
@@ -69,6 +73,13 @@ export const createSettingsSlice: StateCreator<
69
73
  affiliatePercent: null,
70
74
  affiliateWallets: null,
71
75
  _customTokens: [],
76
+ quoteTokensRate: 'default',
77
+
78
+ changeQuoteTokensRate: () =>
79
+ set((state) => ({
80
+ quoteTokensRate:
81
+ state.quoteTokensRate === 'default' ? 'reversed' : 'default',
82
+ })),
72
83
  addPreferredBlockchain: (blockchain) => {
73
84
  const currentPreferredBlockchains = get().preferredBlockchains;
74
85
 
@@ -2,6 +2,8 @@
2
2
  // Types
3
3
  import type { ThemeColors, WidgetColors, WidgetColorsKeys } from '../types';
4
4
 
5
+ import { isColorKeyOverridden } from './validation';
6
+
5
7
  type RGB = {
6
8
  red: number;
7
9
  green: number;
@@ -40,15 +42,6 @@ function expandShortHexColor(hexColor: string) {
40
42
  return `#${hexColor}`;
41
43
  }
42
44
 
43
- /*
44
- * We letting users to override some specific colors (e.g. `primary550`, `secondary100`).
45
- * So we are generating a range of colors if `primary` (or other keys) has passed but if user is passing a specific color,
46
- * we will override the user color to generated range.
47
- */
48
- function isOverridingColor(colorKey: string): boolean {
49
- return /[0-9]+$/.test(colorKey);
50
- }
51
-
52
45
  // pad a hexadecimal string with zeros if it needs it
53
46
  function pad(number: string, length: number) {
54
47
  return number.padStart(length, '0');
@@ -171,7 +164,7 @@ export function expandToGenerateThemeColors(
171
164
  */
172
165
  const isSingleColor = ['background', 'foreground'].includes(colorKey);
173
166
 
174
- if (!isSingleColor && !isOverridingColor(colorKey)) {
167
+ if (!isSingleColor && !isColorKeyOverridden(colorKey)) {
175
168
  const expandedHexColor = expandShortHexColor(expandColor);
176
169
  Object.assign(
177
170
  output,
@@ -4,6 +4,9 @@ import type { BestRouteResponse } from 'rango-sdk';
4
4
 
5
5
  import { BigNumber } from 'bignumber.js';
6
6
 
7
+ import { stripTrailingZeros } from './sanitizers';
8
+ import { isZeroValue } from './validation';
9
+
7
10
  /*
8
11
  * if time > 1h -> rounded with 5 minutes precision
9
12
  * if time < 1h -> rounded with 15 seconds precision
@@ -125,3 +128,11 @@ export const containsText = (text: string, searchText: string) =>
125
128
 
126
129
  export const isPositiveNumber = (text?: string) =>
127
130
  !!text && parseFloat(text) > 0;
131
+
132
+ export function sanitizeInputAmount(amount: string): string {
133
+ if (isZeroValue(amount)) {
134
+ return '0';
135
+ }
136
+
137
+ return stripTrailingZeros(amount);
138
+ }