@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rango-dev/widget-embedded",
3
- "version": "0.42.3-next.2",
3
+ "version": "0.42.3-next.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "source": "./src/index.ts",
@@ -30,7 +30,7 @@
30
30
  "@rango-dev/queue-manager-rango-preset": "^0.45.2-next.4",
31
31
  "@rango-dev/queue-manager-react": "^0.29.0",
32
32
  "@rango-dev/signer-solana": "^0.39.1-next.1",
33
- "@rango-dev/ui": "^0.46.2-next.5",
33
+ "@rango-dev/ui": "^0.46.2-next.6",
34
34
  "@rango-dev/wallets-core": "^0.43.1-next.3",
35
35
  "@rango-dev/wallets-react": "^0.30.2-next.4",
36
36
  "@rango-dev/wallets-shared": "^0.44.2-next.4",
@@ -54,4 +54,4 @@
54
54
  "publishConfig": {
55
55
  "access": "public"
56
56
  }
57
- }
57
+ }
@@ -26,7 +26,7 @@ import {
26
26
  } from './NoResult.styles';
27
27
 
28
28
  export function NoResult(props: PropTypes) {
29
- const { fetch, error, size = 'small' } = props;
29
+ const { fetch, error, size = 'small', skipAlerts } = props;
30
30
  const disabledLiquiditySources = useAppStore().getDisabledLiquiditySources();
31
31
  const toggleAllLiquiditySources = useAppStore().toggleAllLiquiditySources;
32
32
 
@@ -37,6 +37,9 @@ export function NoResult(props: PropTypes) {
37
37
  () => toggleAllLiquiditySources(swappers, true),
38
38
  fetch
39
39
  );
40
+ if (skipAlerts) {
41
+ info.alert = null;
42
+ }
40
43
 
41
44
  return (
42
45
  <Container>
@@ -4,6 +4,7 @@ export interface PropTypes {
4
4
  fetch: () => void;
5
5
  error: NoResultError | QuoteRequestFailed | null;
6
6
  size?: 'small' | 'large';
7
+ skipAlerts?: boolean;
7
8
  }
8
9
 
9
10
  type AlertAction = {
@@ -263,7 +263,7 @@ export function Quote(props: QuoteProps) {
263
263
  variant="body"
264
264
  color="neutral900">
265
265
  {i18n.t({
266
- id: 'Minimum required slippage: {minRequiredSlippage}',
266
+ id: 'Minimum suggested slippage: {minRequiredSlippage}',
267
267
  values: {
268
268
  ...(error?.type ===
269
269
  QuoteErrorType.INSUFFICIENT_SLIPPAGE && {
@@ -10,10 +10,17 @@ import { errorMessages } from '../../constants/errors';
10
10
  import { QuoteErrorType, QuoteWarningType } from '../../types';
11
11
  import { getPriceImpactLevel } from '../../utils/quote';
12
12
 
13
- type AlertInfo = {
13
+ export type ActionType =
14
+ | 'show-info'
15
+ | 'change-settings'
16
+ | 'change-slippage'
17
+ | null;
18
+
19
+ export type AlertInfo = {
14
20
  alertType: 'error' | 'warning';
15
21
  title: string;
16
- action: 'show-info' | 'change-settings' | null;
22
+ action: ActionType;
23
+ actionButtonTitle: string | null;
17
24
  };
18
25
 
19
26
  export function makeAlerts(
@@ -24,7 +31,9 @@ export function makeAlerts(
24
31
  alertType: 'warning',
25
32
  title: '',
26
33
  action: null,
34
+ actionButtonTitle: null,
27
35
  };
36
+
28
37
  if (error) {
29
38
  alertInfo.alertType = 'error';
30
39
  if (error.type === QuoteErrorType.BRIDGE_LIMIT) {
@@ -38,7 +47,8 @@ export function makeAlerts(
38
47
  minRequiredSlippage: error.minRequiredSlippage,
39
48
  },
40
49
  });
41
- alertInfo.action = 'change-settings';
50
+ alertInfo.action = 'change-slippage';
51
+ alertInfo.actionButtonTitle = i18n.t('Increase');
42
52
  }
43
53
 
44
54
  return alertInfo;
@@ -75,7 +85,9 @@ export function makeAlerts(
75
85
  minRequiredSlippage: warning.minRequiredSlippage,
76
86
  },
77
87
  });
78
- alertInfo.action = 'change-settings';
88
+ alertInfo.action = 'change-slippage';
89
+ alertInfo.actionButtonTitle = i18n.t('Increase');
90
+
79
91
  break;
80
92
  }
81
93
  case QuoteWarningType.HIGH_SLIPPAGE: {
@@ -92,3 +104,16 @@ export function makeAlerts(
92
104
  }
93
105
  return null;
94
106
  }
107
+
108
+ export function getRequiredSlippage(
109
+ warning: QuoteWarning | null,
110
+ error: BridgeLimitError | InsufficientSlippageError | null
111
+ ) {
112
+ if (error?.type === QuoteErrorType.INSUFFICIENT_SLIPPAGE) {
113
+ return Number(error.minRequiredSlippage);
114
+ }
115
+ if (warning?.type === QuoteWarningType.INSUFFICIENT_SLIPPAGE) {
116
+ return Number(warning.minRequiredSlippage);
117
+ }
118
+ return null;
119
+ }
@@ -1,4 +1,4 @@
1
- import { darkTheme, styled } from '@rango-dev/ui';
1
+ import { Button, darkTheme, styled } from '@rango-dev/ui';
2
2
 
3
3
  export const Alerts = styled('div', {
4
4
  width: '100%',
@@ -32,3 +32,12 @@ export const Action = styled('div', {
32
32
  alignSelf: 'flex-start',
33
33
  cursor: 'pointer',
34
34
  });
35
+
36
+ export const SwapButton = styled(Button, {
37
+ '& ._text': {
38
+ gap: '$5',
39
+ display: 'flex',
40
+ alignItems: 'center',
41
+ justifyContent: 'center',
42
+ },
43
+ });
@@ -1,14 +1,18 @@
1
+ import type { ActionType } from './QuoteWarningsAndErrors.helpers';
1
2
  import type { PropTypes } from './QuoteWarningsAndErrors.types';
2
3
 
3
4
  import { i18n } from '@lingui/core';
4
- import { Alert, Button, InfoIcon } from '@rango-dev/ui';
5
+ import { Alert, Button, Divider, InfoIcon } from '@rango-dev/ui';
5
6
  import React from 'react';
6
7
 
7
8
  import { QuoteErrorType, QuoteWarningType } from '../../types';
8
9
  import { NoResult } from '../NoResult';
9
10
 
10
11
  import { HighValueLossWarningModal } from './HighValueLossWarningModal';
11
- import { makeAlerts } from './QuoteWarningsAndErrors.helpers';
12
+ import {
13
+ getRequiredSlippage,
14
+ makeAlerts,
15
+ } from './QuoteWarningsAndErrors.helpers';
12
16
  import { Action, Alerts } from './QuoteWarningsAndErrors.styles';
13
17
  import { SlippageWarningModal } from './SlippageWariningModal';
14
18
  import { UnknownPriceWarningModal } from './UnknownPriceWarningModal';
@@ -20,11 +24,13 @@ export function QuoteWarningsAndErrors(props: PropTypes) {
20
24
  couldChangeSettings,
21
25
  showWarningModal,
22
26
  confirmationDisabled,
27
+ skipAlerts,
23
28
  refetchQuote,
24
29
  onOpenWarningModal,
25
30
  onCloseWarningModal,
26
31
  onConfirmWarningModal,
27
32
  onChangeSettings,
33
+ onChangeSlippage,
28
34
  } = props;
29
35
 
30
36
  const warningModalHandlers = {
@@ -49,14 +55,38 @@ export function QuoteWarningsAndErrors(props: PropTypes) {
49
55
  alertInfo.action = null;
50
56
  }
51
57
 
52
- const showAlerts = !!alertInfo;
58
+ const showAlerts = !!alertInfo && !skipAlerts;
59
+
60
+ const onclickActionButton = (action: ActionType) => {
61
+ if (action === 'change-slippage') {
62
+ const quoteError =
63
+ error?.type === QuoteErrorType.BRIDGE_LIMIT ||
64
+ error?.type === QuoteErrorType.INSUFFICIENT_SLIPPAGE
65
+ ? error
66
+ : null;
67
+ const requestedSlippage = getRequiredSlippage(warning, quoteError);
68
+ onChangeSlippage?.(requestedSlippage);
69
+ } else if (action === 'change-settings') {
70
+ onChangeSettings();
71
+ }
72
+ };
53
73
 
54
74
  return (
55
75
  <>
56
- {showNoResultMessage && <NoResult error={error} fetch={refetchQuote} />}
76
+ {showNoResultMessage && (
77
+ <>
78
+ <Divider size={10} />
79
+ <NoResult
80
+ skipAlerts={skipAlerts}
81
+ error={error}
82
+ fetch={refetchQuote}
83
+ />
84
+ </>
85
+ )}
57
86
 
58
87
  {showAlerts && (
59
88
  <Alerts>
89
+ <Divider size={10} />
60
90
  <Alert
61
91
  title={alertInfo.title}
62
92
  type={alertInfo.alertType}
@@ -68,14 +98,15 @@ export function QuoteWarningsAndErrors(props: PropTypes) {
68
98
  </Action>
69
99
  ),
70
100
  })}
71
- {...(alertInfo.action === 'change-settings' && {
101
+ {...((alertInfo.action === 'change-settings' ||
102
+ alertInfo.action === 'change-slippage') && {
72
103
  action: (
73
104
  <Button
74
105
  id="widget-quote-warning-error-change-settings-btn"
75
106
  size="xxsmall"
76
107
  type={alertInfo.alertType}
77
- onClick={onChangeSettings}>
78
- {i18n.t('Change')}
108
+ onClick={() => onclickActionButton(alertInfo.action)}>
109
+ {alertInfo.actionButtonTitle || i18n.t('Change')}
79
110
  </Button>
80
111
  ),
81
112
  })}
@@ -6,11 +6,13 @@ export interface PropTypes {
6
6
  showWarningModal: boolean;
7
7
  confirmationDisabled: boolean;
8
8
  couldChangeSettings: boolean;
9
+ skipAlerts?: boolean;
9
10
  refetchQuote: () => void;
10
11
  onOpenWarningModal: () => void;
11
12
  onCloseWarningModal: () => void;
12
13
  onConfirmWarningModal: () => void;
13
14
  onChangeSettings: () => void;
15
+ onChangeSlippage?: (slippage: number | null) => void;
14
16
  }
15
17
 
16
18
  type ModalPropTypesKeys = keyof Omit<PropTypes, 'extraSpace' | 'loading'>;
@@ -4,16 +4,17 @@ import type {
4
4
  } from '../../types';
5
5
 
6
6
  import { i18n } from '@lingui/core';
7
- import { Button, Divider, MessageBox, Typography } from '@rango-dev/ui';
7
+ import { Button, Divider, MessageBox, WarningIcon } from '@rango-dev/ui';
8
8
  import React from 'react';
9
9
  import { useNavigate } from 'react-router-dom';
10
10
 
11
11
  import { navigationRoutes } from '../../constants/navigationRoutes';
12
- import { useAppStore } from '../../store/AppStore';
13
12
  import { QuoteWarningType } from '../../types';
14
13
  import { getContainer } from '../../utils/common';
15
14
  import { WatermarkedModal } from '../common/WatermarkedModal';
16
15
 
16
+ import { SwapButton } from './QuoteWarningsAndErrors.styles';
17
+
17
18
  type PropsTypes = {
18
19
  open: boolean;
19
20
  confirmationDisabled: boolean;
@@ -23,26 +24,13 @@ type PropsTypes = {
23
24
  };
24
25
 
25
26
  export function SlippageWarningModal(props: PropsTypes) {
26
- const { customSlippage, slippage } = useAppStore();
27
27
  const { open, onClose, onConfirm, warning, confirmationDisabled } = props;
28
28
  const navigate = useNavigate();
29
- const userSlippage = customSlippage ?? slippage;
30
29
 
31
30
  return (
32
31
  <WatermarkedModal
33
32
  anchor="bottom"
34
33
  open={open}
35
- prefix={
36
- <Button
37
- size="small"
38
- id="widget-slippage-warning-modal-change-settings-btn"
39
- variant="ghost"
40
- onClick={() => navigate('../' + navigationRoutes.settings)}>
41
- <Typography variant="label" size="medium" color="$neutral900">
42
- {i18n.t('Change settings')}
43
- </Typography>
44
- </Button>
45
- }
46
34
  container={getContainer()}
47
35
  onClose={onClose}>
48
36
  <MessageBox
@@ -54,10 +42,9 @@ export function SlippageWarningModal(props: PropsTypes) {
54
42
  }
55
43
  description={
56
44
  warning.type === QuoteWarningType.HIGH_SLIPPAGE
57
- ? i18n.t({
58
- id: ' Caution, your slippage is high (={userSlippage}). Your trade may be front run.',
59
- values: { userSlippage },
60
- })
45
+ ? i18n.t(
46
+ 'Caution, your slippage is high. Your trade may be front run.'
47
+ )
61
48
  : i18n.t({
62
49
  id: 'We recommend you to increase slippage to at least {minRequiredSlippage} for this route.',
63
50
  values: {
@@ -67,7 +54,7 @@ export function SlippageWarningModal(props: PropsTypes) {
67
54
  }>
68
55
  <Divider size={18} />
69
56
  <Divider size={32} />
70
- <Button
57
+ <SwapButton
71
58
  id="widget-slippage-warning-modal-confirm-anyway-btn"
72
59
  size="large"
73
60
  type="primary"
@@ -75,7 +62,19 @@ export function SlippageWarningModal(props: PropsTypes) {
75
62
  fullWidth
76
63
  disabled={confirmationDisabled}
77
64
  onClick={onConfirm}>
78
- {i18n.t('Confirm anyway')}
65
+ <WarningIcon color="white" size={16} />
66
+ {i18n.t('Swap anyway')}
67
+ </SwapButton>
68
+ <Divider size={10} />
69
+ <Button
70
+ id="widget-slippage-warning-modal-change-slippage-btn"
71
+ size="large"
72
+ type="primary"
73
+ variant="outlined"
74
+ fullWidth
75
+ disabled={confirmationDisabled}
76
+ onClick={() => navigate('../' + navigationRoutes.settings)}>
77
+ {i18n.t('Change Slippage')}
79
78
  </Button>
80
79
  </MessageBox>
81
80
  </WatermarkedModal>
@@ -32,3 +32,26 @@ export const SlippageChip = styled(Chip, {
32
32
  width: '61px',
33
33
  flexShrink: 0,
34
34
  });
35
+
36
+ export const SlippageTextFieldContainer = styled('div', {
37
+ borderWidth: 1,
38
+ borderStyle: 'solid',
39
+ borderRadius: '$xs',
40
+ flex: 1,
41
+ variants: {
42
+ status: {
43
+ safe: {
44
+ borderColor: '$secondary500',
45
+ },
46
+ error: {
47
+ borderColor: '$error500',
48
+ },
49
+ warning: {
50
+ borderColor: '$warning500',
51
+ },
52
+ empty: {
53
+ borderWidth: 0,
54
+ },
55
+ },
56
+ },
57
+ });
@@ -14,12 +14,14 @@ import { MAX_SLIPPAGE, SLIPPAGES } from '../../constants/swapSettings';
14
14
  import { useAppStore } from '../../store/AppStore';
15
15
  import { getContainer } from '../../utils/common';
16
16
  import { getSlippageValidation } from '../../utils/settings';
17
+ import { isValidCurrencyFormat } from '../../utils/validation';
17
18
 
18
19
  import {
19
20
  BaseContainer,
20
21
  Head,
21
22
  SlippageChip,
22
23
  SlippageChipsContainer,
24
+ SlippageTextFieldContainer,
23
25
  } from './Slippage.styles';
24
26
  import { SlippageTooltipContent } from './SlippageTooltipContent';
25
27
 
@@ -54,9 +56,8 @@ export function Slippage() {
54
56
 
55
57
  const onInput = (event: React.FormEvent<HTMLInputElement>) => {
56
58
  const input = event.target as HTMLInputElement;
57
- const regex = /^(0|[1-9]\d*)(\.\d{1,2})?$/;
58
59
  const value = input.value;
59
- if (!regex.test(value)) {
60
+ if (!isValidCurrencyFormat(value)) {
60
61
  input.value = value.slice(0, -1);
61
62
  }
62
63
  };
@@ -90,26 +91,31 @@ export function Slippage() {
90
91
  />
91
92
  );
92
93
  })}
93
- <TextField
94
- type="number"
95
- min="0.01"
96
- max="30"
97
- step="0.01"
98
- onInput={onInput}
99
- fullWidth
100
- variant="contained"
101
- value={customSlippage === null ? '' : customSlippage}
102
- color="dark"
103
- onChange={onSlippageValueChange}
104
- suffix={
105
- customSlippage && (
106
- <Typography variant="body" size="small">
107
- %
108
- </Typography>
109
- )
110
- }
111
- placeholder={i18n.t('Custom')}
112
- />
94
+ <SlippageTextFieldContainer
95
+ status={
96
+ slippageValidation?.type || (customSlippage ? 'safe' : 'empty')
97
+ }>
98
+ <TextField
99
+ type="number"
100
+ min="0.01"
101
+ max="30"
102
+ step="0.01"
103
+ onInput={onInput}
104
+ fullWidth
105
+ variant="contained"
106
+ value={customSlippage === null ? '' : customSlippage}
107
+ color="dark"
108
+ onChange={onSlippageValueChange}
109
+ suffix={
110
+ customSlippage && (
111
+ <Typography variant="body" size="small">
112
+ %
113
+ </Typography>
114
+ )
115
+ }
116
+ placeholder={i18n.t('Custom')}
117
+ />
118
+ </SlippageTextFieldContainer>
113
119
  </SlippageChipsContainer>
114
120
 
115
121
  {slippageValidation && (
@@ -0,0 +1,33 @@
1
+ import type { AlertInfo } from '../QuoteWarningsAndErrors/QuoteWarningsAndErrors.helpers';
2
+
3
+ import { i18n } from '@lingui/core';
4
+
5
+ import { HIGH_SLIPPAGE, MIN_SLIPPAGE } from '../../constants/swapSettings';
6
+
7
+ export type ActionType = AlertInfo['action'] | 'reset-slippage';
8
+
9
+ type Alert = Omit<AlertInfo, 'action'> & {
10
+ action: ActionType;
11
+ };
12
+
13
+ export function makeAlerts(slippage: number): Alert | null {
14
+ let alertInfo: Alert | null = null;
15
+ if (slippage === MIN_SLIPPAGE) {
16
+ alertInfo = {
17
+ alertType: 'error',
18
+ action: 'reset-slippage',
19
+ actionButtonTitle: i18n.t('Reset'),
20
+ title: i18n.t('Slippage cannot be set lower than 0.01%.'),
21
+ };
22
+ return alertInfo;
23
+ } else if (slippage > HIGH_SLIPPAGE) {
24
+ alertInfo = {
25
+ alertType: 'warning',
26
+ action: 'change-settings',
27
+ actionButtonTitle: i18n.t('Change'),
28
+ title: i18n.t('Caution, your slippage is high!'),
29
+ };
30
+ return alertInfo;
31
+ }
32
+ return null;
33
+ }
@@ -0,0 +1,48 @@
1
+ import type { ActionType } from './SlippageWarningsAndErrors.helpers';
2
+ import type { PropTypes } from './SlippageWarningsAndErrors.types';
3
+
4
+ import { Alert, Button } from '@rango-dev/ui';
5
+ import React from 'react';
6
+
7
+ import { DEFAULT_SLIPPAGE } from '../../constants/swapSettings';
8
+ import { useAppStore } from '../../store/AppStore';
9
+
10
+ import { makeAlerts } from './SlippageWarningsAndErrors.helpers';
11
+
12
+ export function SlippageWarningsAndErrors(props: PropTypes) {
13
+ const { slippage, customSlippage, setSlippage, setCustomSlippage } =
14
+ useAppStore();
15
+ const { onChangeSettings } = props;
16
+ const currentSlippage = customSlippage !== null ? customSlippage : slippage;
17
+
18
+ const alertInfo = makeAlerts(currentSlippage);
19
+
20
+ const onClickActionButton = (action: ActionType) => {
21
+ if (action === 'reset-slippage') {
22
+ setSlippage(DEFAULT_SLIPPAGE);
23
+ setCustomSlippage(null);
24
+ } else if (action === 'change-settings') {
25
+ onChangeSettings();
26
+ }
27
+ };
28
+
29
+ if (!alertInfo) {
30
+ return null;
31
+ }
32
+ return (
33
+ <Alert
34
+ title={alertInfo.title}
35
+ type={alertInfo.alertType}
36
+ variant="alarm"
37
+ action={
38
+ <Button
39
+ id="widget-slippage-warning-error-change-settings-or-reset-slippage-btn"
40
+ size="xxsmall"
41
+ type={alertInfo.alertType}
42
+ onClick={() => onClickActionButton(alertInfo.action)}>
43
+ {alertInfo.actionButtonTitle}
44
+ </Button>
45
+ }
46
+ />
47
+ );
48
+ }
@@ -0,0 +1,3 @@
1
+ export interface PropTypes {
2
+ onChangeSettings: () => void;
3
+ }
@@ -0,0 +1,4 @@
1
+ export const USD_FORMAT_DECIMALS = 2;
2
+ export const USD_EXCHANGE_MINIMUM = 0.001;
3
+ export const SMALL_VALUE_DECIMALS = 14;
4
+ export const LARGE_VALUE_MAX_DIGITS = 10;
@@ -0,0 +1,76 @@
1
+ import type { SlippageColorParams } from './SwapMetrics.types';
2
+
3
+ import BigNumber from 'bignumber.js';
4
+
5
+ import { QuoteErrorType, QuoteWarningType } from '../../types';
6
+
7
+ import {
8
+ LARGE_VALUE_MAX_DIGITS,
9
+ SMALL_VALUE_DECIMALS,
10
+ USD_EXCHANGE_MINIMUM,
11
+ USD_FORMAT_DECIMALS,
12
+ } from './SwapMetrics.constants';
13
+
14
+ export function getSlippageColor(params: SlippageColorParams) {
15
+ const { error, isDarkTheme, warning } = params;
16
+ const { quoteError, slippageError } = error;
17
+ const { quoteWarning, slippageWarning } = warning;
18
+ const hasSlippageError =
19
+ !!slippageError ||
20
+ quoteError?.type === QuoteErrorType.INSUFFICIENT_SLIPPAGE;
21
+ const hasSlippageWarning =
22
+ !!slippageWarning ||
23
+ quoteWarning?.type === QuoteWarningType.INSUFFICIENT_SLIPPAGE;
24
+
25
+ if (hasSlippageError) {
26
+ return '$error500';
27
+ } else if (hasSlippageWarning) {
28
+ return '$warning500';
29
+ }
30
+ if (isDarkTheme) {
31
+ return '$neutral600';
32
+ }
33
+ return '$neutral700';
34
+ }
35
+
36
+ export function getUsdExchangeRate(params: {
37
+ toTokenUsdPrice: number | null;
38
+ fromTokenUsdPrice: number | null;
39
+ }) {
40
+ const { toTokenUsdPrice, fromTokenUsdPrice } = params;
41
+ if (!toTokenUsdPrice || !fromTokenUsdPrice) {
42
+ return { rawValue: '0', displayValue: '0' };
43
+ }
44
+
45
+ const toPrice = new BigNumber(toTokenUsdPrice);
46
+ const fromPrice = new BigNumber(fromTokenUsdPrice);
47
+ const rawValue = toPrice.dividedBy(fromPrice);
48
+ let displayValue: string;
49
+
50
+ if (rawValue.isLessThan(1)) {
51
+ displayValue = rawValue.toFixed(SMALL_VALUE_DECIMALS);
52
+ } else if (rawValue.toFixed(0).length > LARGE_VALUE_MAX_DIGITS) {
53
+ displayValue = rawValue.toFixed(0).slice(0, LARGE_VALUE_MAX_DIGITS);
54
+ } else {
55
+ displayValue = rawValue.toFixed(USD_FORMAT_DECIMALS);
56
+ }
57
+ return {
58
+ displayValue,
59
+ rawValue: rawValue.toFixed(),
60
+ };
61
+ }
62
+
63
+ export function formatTokenValueInUsd(
64
+ usdExchangeRate: number,
65
+ tokenUsdPrice: number
66
+ ): string {
67
+ const value = new BigNumber(usdExchangeRate).multipliedBy(tokenUsdPrice);
68
+ if (value.isLessThan(USD_EXCHANGE_MINIMUM)) {
69
+ return '$0';
70
+ }
71
+ const result = value
72
+ .decimalPlaces(USD_FORMAT_DECIMALS, BigNumber.ROUND_DOWN)
73
+ .toFormat(USD_FORMAT_DECIMALS);
74
+
75
+ return `$${result}`;
76
+ }
@@ -0,0 +1,32 @@
1
+ import { darkTheme, styled, Typography } from '@rango-dev/ui';
2
+
3
+ export const Container = styled('div', {
4
+ display: 'flex',
5
+ padding: '$4',
6
+ justifyContent: 'space-between',
7
+ alignItems: 'center',
8
+ });
9
+
10
+ export const Rate = styled('div', {
11
+ display: 'flex',
12
+ alignItems: 'center',
13
+ gap: '$2',
14
+ '& .rate-text': {
15
+ color: '$neutral700',
16
+ [`.${darkTheme} &`]: {
17
+ color: '$neutral700',
18
+ },
19
+ },
20
+ '& ._icon-button': {
21
+ transform: 'rotate(90deg)',
22
+ width: '$16',
23
+ height: '$16',
24
+ },
25
+ });
26
+
27
+ export const TokenName = styled(Typography, {
28
+ maxWidth: '$32',
29
+ overflow: 'hidden',
30
+ textOverflow: 'ellipsis',
31
+ whiteSpace: 'nowrap',
32
+ });