@lifi/widget 1.19.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/cjs/components/AppContainer.js +0 -1
  2. package/cjs/components/BottomSheet/BottomSheet.js +2 -1
  3. package/cjs/components/ChainSelect/ChainSelect.js +1 -1
  4. package/cjs/components/Dialog.js +2 -1
  5. package/cjs/components/Header/NavigationHeader.js +1 -1
  6. package/cjs/components/ProgressToNextUpdate/ProgressToNextUpdate.js +1 -1
  7. package/cjs/components/SendToWallet/SendToWalletButton.js +1 -1
  8. package/cjs/components/SwapInput/SwapInputAdornment.js +1 -1
  9. package/cjs/components/SwapInput/SwapInputAdornment.style.js +1 -1
  10. package/cjs/components/SwapRouteCard/SwapRouteCard.js +2 -2
  11. package/cjs/config/version.d.ts +1 -1
  12. package/cjs/config/version.js +1 -1
  13. package/cjs/hooks/useContentHeight.d.ts +2 -0
  14. package/cjs/hooks/useContentHeight.js +18 -1
  15. package/cjs/i18n/en/translation.json +9 -2
  16. package/cjs/i18n/index.d.ts +7 -0
  17. package/cjs/pages/SwapPage/StatusBottomSheet.js +1 -1
  18. package/cjs/pages/SwapPage/StatusBottomSheet.style.d.ts +0 -4
  19. package/cjs/pages/SwapPage/StatusBottomSheet.style.js +23 -17
  20. package/cjs/pages/SwapPage/SwapPage.js +38 -17
  21. package/cjs/pages/SwapPage/TokenValueBottomSheet.d.ts +11 -0
  22. package/cjs/pages/SwapPage/TokenValueBottomSheet.js +42 -0
  23. package/cjs/stores/routes/types.d.ts +1 -1
  24. package/components/AppContainer.js +0 -1
  25. package/components/BottomSheet/BottomSheet.js +2 -1
  26. package/components/ChainSelect/ChainSelect.js +1 -1
  27. package/components/Dialog.js +2 -1
  28. package/components/Header/NavigationHeader.js +1 -1
  29. package/components/ProgressToNextUpdate/ProgressToNextUpdate.js +1 -1
  30. package/components/SendToWallet/SendToWalletButton.js +1 -1
  31. package/components/SwapInput/SwapInputAdornment.js +1 -1
  32. package/components/SwapInput/SwapInputAdornment.style.js +1 -1
  33. package/components/SwapRouteCard/SwapRouteCard.js +2 -2
  34. package/config/version.d.ts +1 -1
  35. package/config/version.js +1 -1
  36. package/hooks/useContentHeight.d.ts +2 -0
  37. package/hooks/useContentHeight.js +16 -0
  38. package/i18n/en/translation.json +9 -2
  39. package/i18n/index.d.ts +7 -0
  40. package/package.json +1 -1
  41. package/pages/SwapPage/StatusBottomSheet.js +2 -2
  42. package/pages/SwapPage/StatusBottomSheet.style.d.ts +0 -4
  43. package/pages/SwapPage/StatusBottomSheet.style.js +23 -17
  44. package/pages/SwapPage/SwapPage.js +40 -19
  45. package/pages/SwapPage/TokenValueBottomSheet.d.ts +11 -0
  46. package/pages/SwapPage/TokenValueBottomSheet.js +38 -0
  47. package/stores/routes/types.d.ts +1 -1
  48. package/tsconfig.cjs.tsbuildinfo +1 -1
@@ -40,7 +40,7 @@ export const SwapRouteCardEssentials = ({ route, dense }) => {
40
40
  .map((step) => step.estimate.executionDuration)
41
41
  .reduce((duration, x) => duration + x, 0) / 60);
42
42
  const gasCostUSD = parseFloat((_a = route.gasCostUSD) !== null && _a !== void 0 ? _a : '') || 0.01;
43
- return (_jsxs(Box, Object.assign({ display: "flex", justifyContent: dense ? 'space-between' : 'flex-end', flex: 1, pl: dense ? 0 : 2, mt: dense ? 2 : 0 }, { children: [_jsx(Tooltip, Object.assign({ title: t(`tooltip.estimatedNetworkFee`), placement: "top", enterDelay: 400, enterNextDelay: 400, arrow: true }, { children: _jsxs(Box, Object.assign({ display: "flex", alignItems: "center", mr: dense ? 0 : 2 }, { children: [_jsx(Typography, Object.assign({ lineHeight: 0, mr: 0.5, color: "grey.500" }, { children: _jsx(EvStationIcon, { fontSize: dense ? 'medium' : 'small' }) })), _jsx(Typography, Object.assign({ fontSize: 14, color: "text.primary", fontWeight: "500", lineHeight: 1 }, { children: t(`swap.currency`, { value: gasCostUSD }) }))] })) })), _jsx(Tooltip, Object.assign({ title: t(`tooltip.estimatedTime`), placement: "top", enterDelay: 400, enterNextDelay: 400, arrow: true }, { children: _jsxs(Box, Object.assign({ display: "flex", alignItems: "center", mr: dense ? 0 : 2 }, { children: [_jsx(Typography, Object.assign({ lineHeight: 0, mr: 0.5, color: "grey.500" }, { children: _jsx(AccessTimeIcon, { fontSize: dense ? 'medium' : 'small' }) })), _jsx(Typography, Object.assign({ fontSize: 14, color: "text.primary", fontWeight: "500", lineHeight: 1 }, { children: t('swap.estimatedTime', {
43
+ return (_jsxs(Box, Object.assign({ display: "flex", justifyContent: dense ? 'space-between' : 'flex-end', flex: 1, pl: dense ? 0 : 2, mt: dense ? 2 : 0 }, { children: [_jsx(Tooltip, Object.assign({ title: t(`tooltip.estimatedNetworkFee`), placement: "top", enterDelay: 400, arrow: true }, { children: _jsxs(Box, Object.assign({ display: "flex", alignItems: "center", mr: dense ? 0 : 2 }, { children: [_jsx(Typography, Object.assign({ lineHeight: 0, mr: 0.5, color: "grey.500" }, { children: _jsx(EvStationIcon, { fontSize: dense ? 'medium' : 'small' }) })), _jsx(Typography, Object.assign({ fontSize: 14, color: "text.primary", fontWeight: "500", lineHeight: 1 }, { children: t(`swap.currency`, { value: gasCostUSD }) }))] })) })), _jsx(Tooltip, Object.assign({ title: t(`tooltip.estimatedTime`), placement: "top", enterDelay: 400, arrow: true }, { children: _jsxs(Box, Object.assign({ display: "flex", alignItems: "center", mr: dense ? 0 : 2 }, { children: [_jsx(Typography, Object.assign({ lineHeight: 0, mr: 0.5, color: "grey.500" }, { children: _jsx(AccessTimeIcon, { fontSize: dense ? 'medium' : 'small' }) })), _jsx(Typography, Object.assign({ fontSize: 14, color: "text.primary", fontWeight: "500", lineHeight: 1 }, { children: t('swap.estimatedTime', {
44
44
  value: executionTimeMinutes,
45
- }) }))] })) })), !dense ? (_jsx(Tooltip, Object.assign({ title: t(`tooltip.numberOfSteps`), placement: "top", enterDelay: 400, enterNextDelay: 400, arrow: true }, { children: _jsxs(Box, Object.assign({ display: "flex", alignItems: "center" }, { children: [_jsx(Typography, Object.assign({ lineHeight: 0, mr: 0.5, color: "grey.500" }, { children: _jsx(LayersIcon, { fontSize: dense ? 'medium' : 'small' }) })), _jsx(Typography, Object.assign({ fontSize: 14, color: "text.primary", fontWeight: "500", lineHeight: 1 }, { children: route.steps.length }))] })) }))) : null] })));
45
+ }) }))] })) })), !dense ? (_jsx(Tooltip, Object.assign({ title: t(`tooltip.numberOfSteps`), placement: "top", enterDelay: 400, arrow: true }, { children: _jsxs(Box, Object.assign({ display: "flex", alignItems: "center" }, { children: [_jsx(Typography, Object.assign({ lineHeight: 0, mr: 0.5, color: "grey.500" }, { children: _jsx(LayersIcon, { fontSize: dense ? 'medium' : 'small' }) })), _jsx(Typography, Object.assign({ fontSize: 14, color: "text.primary", fontWeight: "500", lineHeight: 1 }, { children: route.steps.length }))] })) }))) : null] })));
46
46
  };
@@ -1,2 +1,2 @@
1
1
  export declare const name = "@lifi/widget";
2
- export declare const version = "1.19.0";
2
+ export declare const version = "1.20.0";
package/config/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget';
2
- export const version = '1.19.0';
2
+ export const version = '1.20.0';
@@ -1 +1,3 @@
1
+ import type { MutableRefObject } from 'react';
1
2
  export declare const useContentHeight: () => number;
3
+ export declare const useSetContentHeight: (ref: MutableRefObject<HTMLElement | undefined>) => void;
@@ -1,5 +1,6 @@
1
1
  import { useLayoutEffect, useState } from 'react';
2
2
  import { ElementId } from '../utils';
3
+ import { getScrollableContainer } from './useScrollableContainer';
3
4
  const getContentHeight = () => {
4
5
  const headerElement = document.getElementById(ElementId.Header);
5
6
  const containerElement = document.getElementById(ElementId.ScrollableContainer);
@@ -21,3 +22,18 @@ export const useContentHeight = () => {
21
22
  }, []);
22
23
  return contentHeight;
23
24
  };
25
+ export const useSetContentHeight = (ref) => {
26
+ useLayoutEffect(() => {
27
+ var _a;
28
+ const scrollableContainer = getScrollableContainer();
29
+ if (!scrollableContainer ||
30
+ !ref.current ||
31
+ ((_a = ref.current) === null || _a === void 0 ? void 0 : _a.clientHeight) <= (scrollableContainer === null || scrollableContainer === void 0 ? void 0 : scrollableContainer.clientHeight)) {
32
+ return;
33
+ }
34
+ scrollableContainer.style.height = `${ref.current.clientHeight}px`;
35
+ return () => {
36
+ scrollableContainer.style.removeProperty('height');
37
+ };
38
+ }, [ref]);
39
+ };
@@ -22,6 +22,7 @@
22
22
  "removeSwap": "Remove swap",
23
23
  "done": "Done",
24
24
  "okay": "Okay",
25
+ "continue": "Continue",
25
26
  "seeDetails": "See details",
26
27
  "tryAgain": "Try again",
27
28
  "light": "Light",
@@ -62,6 +63,10 @@
62
63
  "supportId": "Support ID",
63
64
  "sendToWallet": "Send to a different wallet",
64
65
  "walletAddressOrEns": "Wallet address or ENS name",
66
+ "swapping": "Swapping",
67
+ "gasCost": "Gas cost",
68
+ "receiving": "Receiving",
69
+ "valueLoss": "Value loss",
65
70
  "tags": {
66
71
  "RECOMMENDED": "RECOMMENDED",
67
72
  "FASTEST": "FAST",
@@ -95,13 +100,15 @@
95
100
  "insufficientGas": "Insufficient gas",
96
101
  "deleteSwap": "Delete this swap?",
97
102
  "deleteSwapHistory": "Delete swap history?",
98
- "deleteActiveSwaps": "Delete all active swaps?"
103
+ "deleteActiveSwaps": "Delete all active swaps?",
104
+ "highValueLoss": "High value loss"
99
105
  },
100
106
  "message": {
101
107
  "insufficientFunds": "You don't have enough funds to execute the swap.",
102
108
  "insufficientGas": "You need to add at least:",
103
109
  "deleteSwapHistory": "Swap history is only stored locally and can't be recovered if you delete it.",
104
- "deleteActiveSwaps": "Active swaps are only stored locally and can't be recovered if you delete them."
110
+ "deleteActiveSwaps": "Active swaps are only stored locally and can't be recovered if you delete them.",
111
+ "highValueLoss": "The value of the received tokens is significantly lower than the swapped tokens and transaction cost."
105
112
  }
106
113
  },
107
114
  "error": {
package/i18n/index.d.ts CHANGED
@@ -25,6 +25,7 @@ export declare const resources: {
25
25
  removeSwap: string;
26
26
  done: string;
27
27
  okay: string;
28
+ continue: string;
28
29
  seeDetails: string;
29
30
  tryAgain: string;
30
31
  light: string;
@@ -65,6 +66,10 @@ export declare const resources: {
65
66
  supportId: string;
66
67
  sendToWallet: string;
67
68
  walletAddressOrEns: string;
69
+ swapping: string;
70
+ gasCost: string;
71
+ receiving: string;
72
+ valueLoss: string;
68
73
  tags: {
69
74
  RECOMMENDED: string;
70
75
  FASTEST: string;
@@ -99,12 +104,14 @@ export declare const resources: {
99
104
  deleteSwap: string;
100
105
  deleteSwapHistory: string;
101
106
  deleteActiveSwaps: string;
107
+ highValueLoss: string;
102
108
  };
103
109
  message: {
104
110
  insufficientFunds: string;
105
111
  insufficientGas: string;
106
112
  deleteSwapHistory: string;
107
113
  deleteActiveSwaps: string;
114
+ highValueLoss: string;
108
115
  };
109
116
  };
110
117
  error: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/widget",
3
- "version": "1.19.0",
3
+ "version": "1.20.0",
4
4
  "description": "LI.FI Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./index.js",
@@ -10,7 +10,7 @@ import { Token } from '../../components/Token';
10
10
  import { getProcessMessage, useChains, useNavigateBack, useTokenBalance, } from '../../hooks';
11
11
  import { SwapFormKey } from '../../providers';
12
12
  import { navigationRoutes, shortenWalletAddress } from '../../utils';
13
- import { IconCircle, IconContainer, iconStyles, } from './StatusBottomSheet.style';
13
+ import { IconCircle, IconContainer } from './StatusBottomSheet.style';
14
14
  export const StatusBottomSheet = ({ status, route, }) => {
15
15
  var _a, _b, _c, _d, _e, _f, _g;
16
16
  const { t } = useTranslation();
@@ -76,5 +76,5 @@ export const StatusBottomSheet = ({ status, route, }) => {
76
76
  (_b = ref.current) === null || _b === void 0 ? void 0 : _b.open();
77
77
  }
78
78
  }, [refetch, refetchNewBalance, status]);
79
- return (_jsx(BottomSheet, Object.assign({ ref: ref }, { children: _jsxs(Box, Object.assign({ p: 3 }, { children: [_jsxs(IconContainer, { children: [_jsxs(IconCircle, Object.assign({ status: status, mb: 1 }, { children: [status === 'idle' ? (_jsx(InfoIcon, { color: "primary", sx: iconStyles })) : null, status === 'success' ? (_jsx(DoneIcon, { color: "success", sx: iconStyles })) : null, status === 'error' ? (_jsx(WarningIcon, { color: "error", sx: iconStyles })) : null] })), _jsx(Typography, Object.assign({ py: 1, fontSize: 18, fontWeight: 700 }, { children: title })), status === 'success' ? (_jsx(Token, { token: Object.assign(Object.assign({}, route.toToken), { amount: (_g = (_e = (_d = (_c = route.steps.at(-1)) === null || _c === void 0 ? void 0 : _c.execution) === null || _d === void 0 ? void 0 : _d.toAmount) !== null && _e !== void 0 ? _e : (_f = route.steps.at(-1)) === null || _f === void 0 ? void 0 : _f.estimate.toAmount) !== null && _g !== void 0 ? _g : route.toAmount }), py: 1 })) : null] }), _jsx(Typography, Object.assign({ pt: 2, pb: 1 }, { children: message })), _jsx(Box, Object.assign({ mt: 2 }, { children: _jsxs(Button, Object.assign({ variant: "contained", fullWidth: true, onClick: status === 'success' ? handleDone : handleClose }, { children: [status === 'idle' ? t('button.okay') : null, status === 'success' ? t('button.done') : null, status === 'error' ? t('button.tryAgain') : null] })) })), status === 'success' ? (_jsx(Box, Object.assign({ mt: 2 }, { children: _jsx(Button, Object.assign({ variant: "text", fullWidth: true, onClick: handleSeeDetails }, { children: t('button.seeDetails') })) }))) : null] })) })));
79
+ return (_jsx(BottomSheet, Object.assign({ ref: ref }, { children: _jsxs(Box, Object.assign({ p: 3 }, { children: [_jsxs(IconContainer, { children: [_jsxs(IconCircle, Object.assign({ status: status, mb: 1 }, { children: [status === 'idle' ? _jsx(InfoIcon, { color: "primary" }) : null, status === 'success' ? _jsx(DoneIcon, { color: "success" }) : null, status === 'error' ? _jsx(WarningIcon, { color: "error" }) : null] })), _jsx(Typography, Object.assign({ py: 1, fontSize: 18, fontWeight: 700 }, { children: title })), status === 'success' ? (_jsx(Token, { token: Object.assign(Object.assign({}, route.toToken), { amount: (_g = (_e = (_d = (_c = route.steps.at(-1)) === null || _c === void 0 ? void 0 : _c.execution) === null || _d === void 0 ? void 0 : _d.toAmount) !== null && _e !== void 0 ? _e : (_f = route.steps.at(-1)) === null || _f === void 0 ? void 0 : _f.estimate.toAmount) !== null && _g !== void 0 ? _g : route.toAmount }), py: 1 })) : null] }), _jsx(Typography, Object.assign({ py: 1 }, { children: message })), _jsx(Box, Object.assign({ mt: 2 }, { children: _jsxs(Button, Object.assign({ variant: "contained", fullWidth: true, onClick: status === 'success' ? handleDone : handleClose }, { children: [status === 'idle' ? t('button.okay') : null, status === 'success' ? t('button.done') : null, status === 'error' ? t('button.seeDetails') : null] })) })), status === 'success' ? (_jsx(Box, Object.assign({ mt: 2 }, { children: _jsx(Button, Object.assign({ variant: "text", fullWidth: true, onClick: handleSeeDetails }, { children: t('button.seeDetails') })) }))) : null] })) })));
80
80
  };
@@ -19,7 +19,3 @@ export declare const IconCircle: import("@emotion/styled").StyledComponent<impor
19
19
  }, keyof import("@mui/material/OverridableComponent").CommonProps | "children" | "sx" | "ref" | ("p" | "color" | "border" | "boxShadow" | "fontWeight" | "zIndex" | "alignContent" | "alignItems" | "alignSelf" | "bottom" | "boxSizing" | "columnGap" | "display" | "flexBasis" | "flexDirection" | "flexGrow" | "flexShrink" | "flexWrap" | "fontFamily" | "fontSize" | "fontStyle" | "gridAutoColumns" | "gridAutoFlow" | "gridAutoRows" | "gridTemplateAreas" | "gridTemplateColumns" | "gridTemplateRows" | "height" | "justifyContent" | "justifyItems" | "justifySelf" | "left" | "letterSpacing" | "lineHeight" | "marginBottom" | "marginLeft" | "marginRight" | "marginTop" | "maxHeight" | "maxWidth" | "minHeight" | "minWidth" | "order" | "paddingBottom" | "paddingLeft" | "paddingRight" | "paddingTop" | "position" | "right" | "rowGap" | "textAlign" | "textOverflow" | "textTransform" | "top" | "visibility" | "whiteSpace" | "width" | "borderBottom" | "borderColor" | "borderLeft" | "borderRadius" | "borderRight" | "borderTop" | "flex" | "gap" | "gridArea" | "gridColumn" | "gridRow" | "margin" | "overflow" | "padding" | "bgcolor" | "m" | "mt" | "mr" | "mb" | "ml" | "mx" | "marginX" | "my" | "marginY" | "pt" | "pr" | "pb" | "pl" | "px" | "paddingX" | "py" | "paddingY" | "typography" | "displayPrint") | "component"> & import("@mui/system").MUIStyledCommonProps<Theme> & {
20
20
  status: RouteExecutionStatus;
21
21
  }, {}, {}>;
22
- export declare const iconStyles: {
23
- position: string;
24
- fontSize: string;
25
- };
@@ -1,13 +1,15 @@
1
1
  import { Box } from '@mui/material';
2
- import { styled } from '@mui/material/styles';
2
+ import { alpha, darken, styled } from '@mui/material/styles';
3
3
  const getStatusColor = (status, theme) => {
4
4
  switch (status) {
5
5
  case 'success':
6
- return theme.palette.success.main;
6
+ return { color: theme.palette.success.main, alpha: 0.15, darken: 0 };
7
7
  case 'error':
8
- return theme.palette.error.main;
8
+ return { color: theme.palette.error.main, alpha: 0.2, darken: 0 };
9
+ case 'warning':
10
+ return { color: theme.palette.warning.main, alpha: 0.7, darken: 0.4 };
9
11
  default:
10
- return theme.palette.primary.main;
12
+ return { color: theme.palette.primary.main, alpha: 0.15, darken: 0 };
11
13
  }
12
14
  };
13
15
  export const IconContainer = styled(Box)(({ theme }) => ({
@@ -17,16 +19,20 @@ export const IconContainer = styled(Box)(({ theme }) => ({
17
19
  }));
18
20
  export const IconCircle = styled(Box, {
19
21
  shouldForwardProp: (prop) => prop !== 'status',
20
- })(({ theme, status }) => ({
21
- border: `3px solid ${getStatusColor(status, theme)}`,
22
- borderRadius: '50%',
23
- width: 64,
24
- height: 64,
25
- display: 'grid',
26
- position: 'relative',
27
- placeItems: 'center',
28
- }));
29
- export const iconStyles = {
30
- position: 'absolute',
31
- fontSize: '2.5rem',
32
- };
22
+ })(({ theme, status }) => {
23
+ const { color, alpha: alphaValue, darken: darkenValue, } = getStatusColor(status, theme);
24
+ return {
25
+ backgroundColor: alpha(color, alphaValue),
26
+ borderRadius: '50%',
27
+ width: 64,
28
+ height: 64,
29
+ display: 'grid',
30
+ position: 'relative',
31
+ placeItems: 'center',
32
+ '& > svg': {
33
+ color: darken(color, darkenValue),
34
+ width: 32,
35
+ height: 32,
36
+ },
37
+ };
38
+ });
@@ -9,8 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
11
11
  import { Delete as DeleteIcon } from '@mui/icons-material';
12
- import { Box, Button } from '@mui/material';
13
- import { Fragment } from 'react';
12
+ import { Box, Button, Tooltip } from '@mui/material';
13
+ import { Fragment, useCallback, useRef } from 'react';
14
14
  import { useFormContext } from 'react-hook-form';
15
15
  import { useTranslation } from 'react-i18next';
16
16
  import { useLocation } from 'react-router-dom';
@@ -22,32 +22,51 @@ import { useNavigateBack, useRouteExecution } from '../../hooks';
22
22
  import { SwapFormKey } from '../../providers';
23
23
  import { StatusBottomSheet } from './StatusBottomSheet';
24
24
  import { Container } from './SwapPage.style';
25
+ import { getTokenValueLossThreshold, TokenValueBottomSheet, } from './TokenValueBottomSheet';
25
26
  export const SwapPage = () => {
26
27
  const { t } = useTranslation();
27
28
  const { state } = useLocation();
28
29
  const { navigateBack } = useNavigateBack();
29
- const { setValue, formState: { isValid, isValidating }, } = useFormContext();
30
+ const tokenValueBottomSheetRef = useRef(null);
31
+ const { setValue,
32
+ // formState: { isValid, isValidating },
33
+ } = useFormContext();
30
34
  const { route, status, executeRoute, restartRoute, deleteRoute } = useRouteExecution(state === null || state === void 0 ? void 0 : state.routeId);
31
- const handleRemoveRoute = () => {
32
- navigateBack();
33
- deleteRoute();
34
- };
35
+ const handleExecuteRoute = useCallback(() => {
36
+ var _a, _b;
37
+ if ((_a = tokenValueBottomSheetRef.current) === null || _a === void 0 ? void 0 : _a.isOpen()) {
38
+ (_b = tokenValueBottomSheetRef.current) === null || _b === void 0 ? void 0 : _b.close();
39
+ }
40
+ executeRoute();
41
+ setValue(SwapFormKey.FromAmount, '');
42
+ }, [executeRoute, setValue]);
35
43
  const handleSwapClick = () => __awaiter(void 0, void 0, void 0, function* () {
44
+ var _a;
36
45
  if (status === 'idle') {
37
- executeRoute();
38
- setValue(SwapFormKey.FromAmount, '');
46
+ const thresholdExceeded = getTokenValueLossThreshold(route);
47
+ if (thresholdExceeded) {
48
+ (_a = tokenValueBottomSheetRef.current) === null || _a === void 0 ? void 0 : _a.open();
49
+ }
50
+ else {
51
+ handleExecuteRoute();
52
+ }
39
53
  }
40
54
  if (status === 'error') {
41
55
  restartRoute();
42
56
  }
43
57
  });
44
- // eslint-disable-next-line consistent-return
58
+ const handleRemoveRoute = () => {
59
+ navigateBack();
60
+ deleteRoute();
61
+ };
45
62
  const getSwapButtonText = () => {
46
- if (status === 'idle') {
47
- return t('button.startSwap');
48
- }
49
- if (status === 'error') {
50
- return t('button.restartSwap');
63
+ switch (status) {
64
+ case 'idle':
65
+ return t('button.startSwap');
66
+ case 'error':
67
+ return t('button.restartSwap');
68
+ default:
69
+ return '';
51
70
  }
52
71
  };
53
72
  return (_jsxs(Container, { children: [route === null || route === void 0 ? void 0 : route.steps.map((step, index, steps) => {
@@ -55,8 +74,10 @@ export const SwapPage = () => {
55
74
  return (_jsxs(Fragment, { children: [_jsx(Step, { step: step, fromToken: index === 0
56
75
  ? Object.assign(Object.assign({}, step.action.fromToken), { amount: step.action.fromAmount }) : undefined, toToken: index === steps.length - 1
57
76
  ? Object.assign(Object.assign({}, step.action.toToken), { amount: (_b = (_a = step.execution) === null || _a === void 0 ? void 0 : _a.toAmount) !== null && _b !== void 0 ? _b : step.estimate.toAmount }) : undefined }), steps.length > 1 && index !== steps.length - 1 ? (_jsx(StepDivider, {})) : null] }, step.id));
58
- }), status === 'idle' || status === 'error' ? (_jsxs(_Fragment, { children: [_jsx(GasSufficiencyMessage, { route: route, mt: 2 }), _jsxs(Box, Object.assign({ mt: 2, display: "flex" }, { children: [_jsx(SwapButton, { text: getSwapButtonText(), onClick: handleSwapClick, currentRoute: route, disable: status === 'idle' && (isValidating || !isValid), enableLoading: true }), status === 'error' ? (_jsx(Button, Object.assign({ onClick: handleRemoveRoute, sx: {
59
- minWidth: 48,
60
- marginLeft: 1,
61
- } }, { children: _jsx(DeleteIcon, {}) }))) : null] }))] })) : null, route && status ? (_jsx(StatusBottomSheet, { status: status, route: route })) : null] }));
77
+ }), status === 'idle' || status === 'error' ? (_jsxs(_Fragment, { children: [_jsx(GasSufficiencyMessage, { route: route, mt: 2 }), _jsxs(Box, Object.assign({ mt: 2, display: "flex" }, { children: [_jsx(SwapButton, { text: getSwapButtonText(), onClick: handleSwapClick, currentRoute: route,
78
+ // disable={status === 'idle' && (isValidating || !isValid)}
79
+ enableLoading: true }), status === 'error' ? (_jsx(Tooltip, Object.assign({ title: t('button.removeSwap'), placement: "bottom-end", enterDelay: 400, arrow: true }, { children: _jsx(Button, Object.assign({ onClick: handleRemoveRoute, sx: {
80
+ minWidth: 48,
81
+ marginLeft: 1,
82
+ } }, { children: _jsx(DeleteIcon, {}) })) }))) : null] }))] })) : null, route && status ? (_jsx(StatusBottomSheet, { status: status, route: route })) : null, route ? (_jsx(TokenValueBottomSheet, { route: route, ref: tokenValueBottomSheetRef, onContinue: handleExecuteRoute })) : null] }));
62
83
  };
@@ -0,0 +1,11 @@
1
+ /// <reference types="react" />
2
+ import type { Route } from '@lifi/sdk';
3
+ import type { BottomSheetBase } from '../../components/BottomSheet';
4
+ interface TokenValueBottomSheetProps {
5
+ route: Route;
6
+ onContinue(): void;
7
+ onCancel?(): void;
8
+ }
9
+ export declare const TokenValueBottomSheet: import("react").ForwardRefExoticComponent<TokenValueBottomSheetProps & import("react").RefAttributes<BottomSheetBase>>;
10
+ export declare const getTokenValueLossThreshold: (route?: Route) => boolean;
11
+ export {};
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Warning as WarningIcon } from '@mui/icons-material';
3
+ import { Box, Button, Typography } from '@mui/material';
4
+ import Big from 'big.js';
5
+ import { forwardRef, useRef } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { BottomSheet } from '../../components/BottomSheet';
8
+ import { useSetContentHeight } from '../../hooks';
9
+ import { IconCircle, IconContainer } from './StatusBottomSheet.style';
10
+ export const TokenValueBottomSheet = forwardRef(({ route, onContinue, onCancel }, ref) => {
11
+ const handleCancel = () => {
12
+ var _a;
13
+ (_a = ref.current) === null || _a === void 0 ? void 0 : _a.close();
14
+ onCancel === null || onCancel === void 0 ? void 0 : onCancel();
15
+ };
16
+ return (_jsx(BottomSheet, Object.assign({ ref: ref }, { children: _jsx(TokenValueBottomSheetContent, { route: route, onContinue: onContinue, onCancel: handleCancel }) })));
17
+ });
18
+ const TokenValueBottomSheetContent = ({ route, onCancel, onContinue, }) => {
19
+ const { t } = useTranslation();
20
+ const ref = useRef();
21
+ useSetContentHeight(ref);
22
+ return (_jsxs(Box, Object.assign({ p: 3, ref: ref }, { children: [_jsxs(IconContainer, { children: [_jsx(IconCircle, Object.assign({ status: "warning", mb: 1 }, { children: _jsx(WarningIcon, { color: "warning" }) })), _jsx(Typography, Object.assign({ py: 1, fontSize: 18, fontWeight: 700 }, { children: t('swap.warning.title.highValueLoss') }))] }), _jsx(Typography, Object.assign({ py: 1 }, { children: t('swap.warning.message.highValueLoss') })), _jsxs(Box, Object.assign({ display: "flex", justifyContent: "space-between", mt: 1 }, { children: [_jsx(Typography, { children: t('swap.swapping') }), _jsx(Typography, Object.assign({ fontWeight: 600 }, { children: t('swap.currency', { value: route.fromAmountUSD }) }))] })), _jsxs(Box, Object.assign({ display: "flex", justifyContent: "space-between", mt: 0.25 }, { children: [_jsx(Typography, { children: t('swap.gasCost') }), _jsx(Typography, Object.assign({ fontWeight: 600 }, { children: t('swap.currency', { value: route.gasCostUSD }) }))] })), _jsxs(Box, Object.assign({ display: "flex", justifyContent: "space-between", mt: 0.25 }, { children: [_jsx(Typography, { children: t('swap.receiving') }), _jsx(Typography, Object.assign({ fontWeight: 600 }, { children: t('swap.currency', { value: route.toAmountUSD }) }))] })), _jsxs(Box, Object.assign({ display: "flex", justifyContent: "space-between", mt: 0.25 }, { children: [_jsx(Typography, { children: t('swap.valueLoss') }), _jsxs(Typography, Object.assign({ fontWeight: 600 }, { children: [Big(route.toAmountUSD || 0)
23
+ .div(Big(route.fromAmountUSD || 0).plus(Big(route.gasCostUSD || 0)))
24
+ .mul(-100)
25
+ .toFixed(1), "%"] }))] })), _jsxs(Box, Object.assign({ display: "flex", mt: 3 }, { children: [_jsx(Button, Object.assign({ variant: "text", onClick: onCancel, fullWidth: true }, { children: t('button.cancel') })), _jsx(Box, { display: "flex", p: 1 }), _jsx(Button, Object.assign({ variant: "contained", onClick: onContinue, fullWidth: true }, { children: t('button.continue') }))] }))] })));
26
+ };
27
+ export const getTokenValueLossThreshold = (route) => {
28
+ if (!route) {
29
+ return false;
30
+ }
31
+ const fromAmountUSD = Big((route === null || route === void 0 ? void 0 : route.fromAmountUSD) || 0);
32
+ const toAmountUSD = Big((route === null || route === void 0 ? void 0 : route.toAmountUSD) || 0);
33
+ const gasCostUSD = Big((route === null || route === void 0 ? void 0 : route.gasCostUSD) || 0);
34
+ if (fromAmountUSD.eq(0) && toAmountUSD.eq(0)) {
35
+ return false;
36
+ }
37
+ return toAmountUSD.div(fromAmountUSD.plus(gasCostUSD)).lt(0.9);
38
+ };
@@ -7,7 +7,7 @@ export interface RouteExecutionStore {
7
7
  deleteRoute: (routeId: string) => void;
8
8
  deleteRoutes: (type: 'completed' | 'active') => void;
9
9
  }
10
- export declare type RouteExecutionStatus = 'error' | 'idle' | 'loading' | 'success';
10
+ export declare type RouteExecutionStatus = 'error' | 'idle' | 'loading' | 'success' | 'warning';
11
11
  export interface RouteExecution {
12
12
  route: Route;
13
13
  status: RouteExecutionStatus;