@lifi/widget 3.2.2 → 3.3.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 (78) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/_esm/components/ActiveTransactions/ActiveTransactionItem.js +1 -1
  3. package/_esm/components/Card/CardIconButton.d.ts +4 -1
  4. package/_esm/components/Card/CardIconButton.js.map +1 -1
  5. package/_esm/components/FeeBreakdownTooltip.d.ts +0 -2
  6. package/_esm/components/FeeBreakdownTooltip.js +2 -5
  7. package/_esm/components/FeeBreakdownTooltip.js.map +1 -1
  8. package/_esm/components/IconTypography.d.ts +3 -3
  9. package/_esm/components/IconTypography.js +2 -2
  10. package/_esm/components/IconTypography.js.map +1 -1
  11. package/_esm/components/RouteCard/RouteCardEssentials.js +10 -11
  12. package/_esm/components/RouteCard/RouteCardEssentials.js.map +1 -1
  13. package/_esm/components/Step/DestinationWalletAddress.js +2 -2
  14. package/_esm/components/Step/DestinationWalletAddress.js.map +1 -1
  15. package/_esm/components/Step/Step.js +1 -1
  16. package/_esm/components/Step/StepProcess.js +3 -3
  17. package/_esm/components/Step/StepProcess.js.map +1 -1
  18. package/_esm/components/Step/StepTimer.js +44 -27
  19. package/_esm/components/Step/StepTimer.js.map +1 -1
  20. package/_esm/components/StepActions/StepActions.style.d.ts +0 -3
  21. package/_esm/components/StepActions/StepActions.style.js +0 -6
  22. package/_esm/components/StepActions/StepActions.style.js.map +1 -1
  23. package/_esm/components/TransactionDetails.js +4 -8
  24. package/_esm/components/TransactionDetails.js.map +1 -1
  25. package/_esm/config/version.d.ts +1 -1
  26. package/_esm/config/version.js +1 -1
  27. package/_esm/hooks/timer/useTimer.js +2 -2
  28. package/_esm/hooks/timer/useTimer.js.map +1 -1
  29. package/_esm/i18n/en.json +1 -2
  30. package/_esm/i18n/index.js +15 -15
  31. package/_esm/i18n/index.js.map +1 -1
  32. package/_esm/pages/TransactionDetailsPage/TransactionDetailsPage.js +3 -2
  33. package/_esm/pages/TransactionDetailsPage/TransactionDetailsPage.js.map +1 -1
  34. package/_esm/pages/TransactionPage/TokenValueBottomSheet.d.ts +0 -1
  35. package/_esm/pages/TransactionPage/TokenValueBottomSheet.js +7 -14
  36. package/_esm/pages/TransactionPage/TokenValueBottomSheet.js.map +1 -1
  37. package/_esm/pages/TransactionPage/TransactionPage.js +16 -8
  38. package/_esm/pages/TransactionPage/TransactionPage.js.map +1 -1
  39. package/_esm/pages/TransactionPage/utils.d.ts +2 -2
  40. package/_esm/pages/TransactionPage/utils.js +9 -5
  41. package/_esm/pages/TransactionPage/utils.js.map +1 -1
  42. package/_esm/stores/routes/types.d.ts +6 -6
  43. package/_esm/stores/routes/types.js +6 -6
  44. package/_esm/stores/routes/types.js.map +1 -1
  45. package/_esm/types/events.d.ts +5 -4
  46. package/_esm/utils/converters.js +1 -1
  47. package/_esm/utils/converters.js.map +1 -1
  48. package/_esm/utils/fees.d.ts +7 -0
  49. package/_esm/utils/fees.js +14 -0
  50. package/_esm/utils/fees.js.map +1 -1
  51. package/components/ActiveTransactions/ActiveTransactionItem.tsx +1 -1
  52. package/components/Card/CardIconButton.tsx +4 -1
  53. package/components/FeeBreakdownTooltip.tsx +4 -10
  54. package/components/IconTypography.ts +2 -2
  55. package/components/RouteCard/RouteCardEssentials.tsx +19 -15
  56. package/components/Step/DestinationWalletAddress.tsx +3 -3
  57. package/components/Step/Step.tsx +1 -1
  58. package/components/Step/StepProcess.tsx +6 -6
  59. package/components/Step/StepTimer.tsx +77 -32
  60. package/components/StepActions/StepActions.style.tsx +0 -8
  61. package/components/TransactionDetails.tsx +8 -25
  62. package/config/version.ts +1 -1
  63. package/hooks/timer/useTimer.ts +4 -2
  64. package/i18n/en.json +1 -2
  65. package/i18n/index.ts +15 -15
  66. package/package.json +10 -10
  67. package/pages/TransactionDetailsPage/TransactionDetailsPage.tsx +6 -5
  68. package/pages/TransactionPage/TokenValueBottomSheet.tsx +32 -19
  69. package/pages/TransactionPage/TransactionPage.tsx +27 -10
  70. package/pages/TransactionPage/utils.ts +23 -8
  71. package/stores/routes/types.ts +6 -6
  72. package/types/events.ts +5 -4
  73. package/utils/converters.ts +1 -1
  74. package/utils/fees.ts +24 -0
  75. package/_esm/components/Step/StepProcess.style.d.ts +0 -9
  76. package/_esm/components/Step/StepProcess.style.js +0 -6
  77. package/_esm/components/Step/StepProcess.style.js.map +0 -1
  78. package/components/Step/StepProcess.style.tsx +0 -9
@@ -1,9 +1,9 @@
1
1
  import type { LiFiStep, Process } from '@lifi/sdk';
2
- import { LinkRounded } from '@mui/icons-material';
2
+ import { OpenInNewRounded } from '@mui/icons-material';
3
3
  import { Box, Link, Typography } from '@mui/material';
4
4
  import { useProcessMessage } from '../../hooks/useProcessMessage.js';
5
+ import { CardIconButton } from '../Card/CardIconButton.js';
5
6
  import { CircularProgress } from './CircularProgress.js';
6
- import { LinkButton } from './StepProcess.style.js';
7
7
 
8
8
  export const StepProcess: React.FC<{
9
9
  step: LiFiStep;
@@ -28,15 +28,15 @@ export const StepProcess: React.FC<{
28
28
  {title}
29
29
  </Typography>
30
30
  {process.txLink ? (
31
- <LinkButton
32
- size="medium"
31
+ <CardIconButton
32
+ size="small"
33
33
  LinkComponent={Link}
34
34
  href={process.txLink}
35
35
  target="_blank"
36
36
  rel="nofollow noreferrer"
37
37
  >
38
- <LinkRounded />
39
- </LinkButton>
38
+ <OpenInNewRounded fontSize="inherit" />
39
+ </CardIconButton>
40
40
  ) : null}
41
41
  </Box>
42
42
  {message ? (
@@ -1,11 +1,22 @@
1
1
  import type { LiFiStepExtended } from '@lifi/sdk';
2
+ import { AccessTimeFilled } from '@mui/icons-material';
3
+ import { Box, Tooltip } from '@mui/material';
2
4
  import { useEffect, useState } from 'react';
3
5
  import { useTranslation } from 'react-i18next';
4
6
  import { useTimer } from '../../hooks/timer/useTimer.js';
7
+ import { IconTypography } from '../IconTypography.js';
8
+
9
+ const getExecutionProcess = (step: LiFiStepExtended) =>
10
+ step.execution?.process.findLast(
11
+ (process) =>
12
+ process.type === 'SWAP' ||
13
+ process.type === 'CROSS_CHAIN' ||
14
+ process.type === 'RECEIVING_CHAIN',
15
+ );
5
16
 
6
17
  const getExpiryTimestamp = (step: LiFiStepExtended) =>
7
18
  new Date(
8
- (step.execution?.process[0]?.startedAt ?? Date.now()) +
19
+ (getExecutionProcess(step)?.startedAt ?? Date.now()) +
9
20
  step.estimate.executionDuration * 1000,
10
21
  );
11
22
 
@@ -15,8 +26,12 @@ export const StepTimer: React.FC<{
15
26
  }> = ({ step, hideInProgress }) => {
16
27
  const { t, i18n } = useTranslation();
17
28
  const [isExpired, setExpired] = useState(false);
18
- const [isExecutionStarted, setExecutionStarted] = useState(!!step.execution);
19
- const [expiryTimestamp] = useState(() => getExpiryTimestamp(step));
29
+ const [isExecutionStarted, setExecutionStarted] = useState(
30
+ () => !!getExecutionProcess(step),
31
+ );
32
+ const [expiryTimestamp, setExpiryTimestamp] = useState(() =>
33
+ getExpiryTimestamp(step),
34
+ );
20
35
  const { seconds, minutes, isRunning, pause, resume, restart } = useTimer({
21
36
  autoStart: false,
22
37
  expiryTimestamp,
@@ -24,41 +39,59 @@ export const StepTimer: React.FC<{
24
39
  });
25
40
 
26
41
  useEffect(() => {
27
- if (isExpired || !step.execution) {
42
+ const executionProcess = getExecutionProcess(step);
43
+ if (!executionProcess) {
44
+ return;
45
+ }
46
+ const shouldRestart = executionProcess.status === 'FAILED';
47
+ const shouldPause = executionProcess.status === 'ACTION_REQUIRED';
48
+ const shouldStart =
49
+ executionProcess.status === 'STARTED' ||
50
+ executionProcess.status === 'PENDING';
51
+ const shouldResume = executionProcess.status === 'PENDING';
52
+ if (isExecutionStarted && shouldRestart) {
53
+ setExecutionStarted(false);
54
+ setExpired(false);
28
55
  return;
29
56
  }
30
- if (!isExecutionStarted) {
57
+ if (isExecutionStarted && isExpired) {
58
+ return;
59
+ }
60
+ if (!isExecutionStarted && shouldStart) {
61
+ const expiryTimestamp = getExpiryTimestamp(step);
31
62
  setExecutionStarted(true);
32
- restart(getExpiryTimestamp(step));
63
+ setExpired(false);
64
+ setExpiryTimestamp(expiryTimestamp);
65
+ restart(expiryTimestamp);
66
+ return;
33
67
  }
34
- const shouldBePaused = step.execution.process.some(
35
- (process) =>
36
- process.status === 'ACTION_REQUIRED' || process.status === 'FAILED',
37
- );
38
- if (isRunning && shouldBePaused) {
68
+ if (isRunning && shouldPause) {
39
69
  pause();
40
- } else if (!isRunning && !shouldBePaused) {
70
+ } else if (!isRunning && shouldResume) {
41
71
  resume();
42
72
  }
43
- }, [
44
- expiryTimestamp,
45
- isExecutionStarted,
46
- isExpired,
47
- isRunning,
48
- pause,
49
- restart,
50
- resume,
51
- step,
52
- ]);
73
+ }, [isExecutionStarted, isExpired, isRunning, pause, restart, resume, step]);
53
74
 
54
75
  if (!isExecutionStarted) {
55
- return Math.ceil(step.estimate.executionDuration / 60).toLocaleString(
56
- i18n.language,
57
- {
58
- style: 'unit',
59
- unit: 'minute',
60
- unitDisplay: 'narrow',
61
- },
76
+ const showSeconds = step.estimate.executionDuration < 60;
77
+ const duration = showSeconds
78
+ ? Math.floor(step.estimate.executionDuration)
79
+ : Math.floor(step.estimate.executionDuration / 60);
80
+ return (
81
+ <Tooltip title={t(`tooltip.estimatedTime`)} sx={{ cursor: 'help' }}>
82
+ <Box component="span" display="flex" alignItems="center">
83
+ <IconTypography component="span" mr={0.5} fontSize={16}>
84
+ <AccessTimeFilled fontSize="inherit" />
85
+ </IconTypography>
86
+ <Box component="span">
87
+ {duration.toLocaleString(i18n.language, {
88
+ style: 'unit',
89
+ unit: showSeconds ? 'second' : 'minute',
90
+ unitDisplay: 'narrow',
91
+ })}
92
+ </Box>
93
+ </Box>
94
+ </Tooltip>
62
95
  );
63
96
  }
64
97
 
@@ -72,7 +105,19 @@ export const StepTimer: React.FC<{
72
105
  return null;
73
106
  }
74
107
 
75
- return isTimerExpired
76
- ? t('main.inProgress')
77
- : `${minutes}:${seconds < 10 ? `0${seconds}` : seconds}`;
108
+ return isTimerExpired ? (
109
+ t('main.inProgress')
110
+ ) : (
111
+ <Tooltip title={t(`tooltip.estimatedTime`)} sx={{ cursor: 'help' }}>
112
+ <Box component="span" display="flex" alignItems="center">
113
+ <IconTypography component="span" mr={0.5} fontSize={16}>
114
+ <AccessTimeFilled fontSize="inherit" />
115
+ </IconTypography>
116
+ <Box
117
+ component="span"
118
+ sx={{ fontVariantNumeric: 'tabular-nums', cursor: 'help' }}
119
+ >{`${minutes}:${seconds < 10 ? `0${seconds}` : seconds}`}</Box>
120
+ </Box>
121
+ </Tooltip>
122
+ );
78
123
  };
@@ -71,11 +71,3 @@ export const StepAvatar = styled(AvatarMasked)(({ theme }) => ({
71
71
  color: theme.palette.text.primary,
72
72
  backgroundColor: 'transparent',
73
73
  }));
74
-
75
- export const IconTypography = styled(Typography)(({ theme }) => ({
76
- color:
77
- theme.palette.mode === 'light'
78
- ? alpha(theme.palette.common.black, 0.32)
79
- : alpha(theme.palette.common.white, 0.4),
80
- lineHeight: 0,
81
- }));
@@ -9,7 +9,7 @@ import { Box, Collapse, Tooltip, Typography } from '@mui/material';
9
9
  import { useState } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import { isRouteDone } from '../stores/routes/utils.js';
12
- import { getFeeCostsBreakdown, getGasCostsBreakdown } from '../utils/fees.js';
12
+ import { getAccumulatedFeeCostsBreakdown } from '../utils/fees.js';
13
13
  import {
14
14
  convertToSubscriptFormat,
15
15
  formatTokenAmount,
@@ -35,18 +35,8 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
35
35
  const toggleCard = () => {
36
36
  setCardExpanded((cardExpanded) => !cardExpanded);
37
37
  };
38
-
39
- const gasCosts = getGasCostsBreakdown(route);
40
- const feeCosts = getFeeCostsBreakdown(route, false);
41
- const gasCostUSD = gasCosts.reduce(
42
- (sum, gasCost) => sum + gasCost.amountUSD,
43
- 0,
44
- );
45
- const feeCostUSD = feeCosts.reduce(
46
- (sum, feeCost) => sum + feeCost.amountUSD,
47
- 0,
48
- );
49
- const fees = gasCostUSD + feeCostUSD;
38
+ const { gasCosts, feeCosts, gasCostUSD, feeCostUSD, combinedFeesUSD } =
39
+ getAccumulatedFeeCostsBreakdown(route);
50
40
 
51
41
  const fromTokenAmount = formatTokenAmount(
52
42
  BigInt(route.fromAmount),
@@ -80,11 +70,7 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
80
70
  <Box display="flex" flex={1} alignItems="center" justifyContent="left">
81
71
  <TokenRate route={route} />
82
72
  </Box>
83
- <FeeBreakdownTooltip
84
- route={route}
85
- gasCosts={gasCosts}
86
- feeCosts={feeCosts}
87
- >
73
+ <FeeBreakdownTooltip gasCosts={gasCosts} feeCosts={feeCosts}>
88
74
  <Box
89
75
  display="flex"
90
76
  alignItems="center"
@@ -101,8 +87,9 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
101
87
  color="text.primary"
102
88
  fontWeight="600"
103
89
  lineHeight={1.429}
90
+ data-value={combinedFeesUSD}
104
91
  >
105
- {t(`format.currency`, { value: fees })}
92
+ {t(`format.currency`, { value: combinedFeesUSD })}
106
93
  </Typography>
107
94
  </Box>
108
95
  </FeeBreakdownTooltip>
@@ -118,7 +105,7 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
118
105
  <Box px={2} pb={2}>
119
106
  <Box display="flex" justifyContent="space-between" mb={0.5}>
120
107
  <Typography variant="body2">{t('main.fees.network')}</Typography>
121
- <FeeBreakdownTooltip route={route} feeCosts={[]}>
108
+ <FeeBreakdownTooltip gasCosts={gasCosts}>
122
109
  <Typography variant="body2">
123
110
  {t(`format.currency`, {
124
111
  value: gasCostUSD,
@@ -129,11 +116,7 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
129
116
  {feeCosts.length ? (
130
117
  <Box display="flex" justifyContent="space-between" mb={0.5}>
131
118
  <Typography variant="body2">{t('main.fees.provider')}</Typography>
132
- <FeeBreakdownTooltip
133
- route={route}
134
- gasCosts={[]}
135
- feeCosts={feeCosts}
136
- >
119
+ <FeeBreakdownTooltip feeCosts={feeCosts}>
137
120
  <Typography variant="body2">
138
121
  {t(`format.currency`, {
139
122
  value: feeCostUSD,
package/config/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget';
2
- export const version = '3.2.2';
2
+ export const version = '3.3.0';
@@ -23,10 +23,12 @@ export function useTimer({
23
23
  autoStart = true,
24
24
  }: UseTimerProps) {
25
25
  const [expiryTimestamp, setExpiryTimestamp] = useState(expiry);
26
- const [seconds, setSeconds] = useState(getSecondsFromExpiry(expiryTimestamp));
26
+ const [seconds, setSeconds] = useState(() =>
27
+ getSecondsFromExpiry(expiryTimestamp),
28
+ );
27
29
  const [isRunning, setIsRunning] = useState(autoStart);
28
30
  const [didStart, setDidStart] = useState(autoStart);
29
- const [delay, setDelay] = useState(
31
+ const [delay, setDelay] = useState(() =>
30
32
  getDelayFromExpiryTimestamp(expiryTimestamp, DEFAULT_DELAY),
31
33
  );
32
34
 
package/i18n/en.json CHANGED
@@ -169,7 +169,7 @@
169
169
  },
170
170
  "tooltip": {
171
171
  "deselectAll": "Deselect all",
172
- "estimatedTime": "Estimated execution time in minutes.",
172
+ "estimatedTime": "Estimated time to complete the swap or bridge transaction, excluding chain switching and token approval.",
173
173
  "minReceived": "The estimated minimum amount may change until the swapping/bridging transaction is signed. For 2-step transfers, this applies until the second step transaction is signed.",
174
174
  "notFound": {
175
175
  "text": "We couldn't find this page.",
@@ -196,7 +196,6 @@
196
196
  "provider": "Provider fee"
197
197
  },
198
198
  "from": "From",
199
- "gasCost": "Gas cost",
200
199
  "inProgress": "in progress",
201
200
  "maxSlippage": "Max. slippage",
202
201
  "minReceived": "Min. received",
package/i18n/index.ts CHANGED
@@ -1,17 +1,17 @@
1
- import bn from './bn.json' assert { type: 'json' };
2
- import de from './de.json' assert { type: 'json' };
3
- import en from './en.json' assert { type: 'json' };
4
- import es from './es.json' assert { type: 'json' };
5
- import fr from './fr.json' assert { type: 'json' };
6
- import id from './id.json' assert { type: 'json' };
7
- import it from './it.json' assert { type: 'json' };
8
- import ja from './ja.json' assert { type: 'json' };
9
- import ko from './ko.json' assert { type: 'json' };
10
- import pt from './pt.json' assert { type: 'json' };
11
- import th from './th.json' assert { type: 'json' };
12
- import tr from './tr.json' assert { type: 'json' };
13
- import uk from './uk.json' assert { type: 'json' };
14
- import vi from './vi.json' assert { type: 'json' };
15
- import zh from './zh.json' assert { type: 'json' };
1
+ import bn from './bn.json' with { type: 'json' };
2
+ import de from './de.json' with { type: 'json' };
3
+ import en from './en.json' with { type: 'json' };
4
+ import es from './es.json' with { type: 'json' };
5
+ import fr from './fr.json' with { type: 'json' };
6
+ import id from './id.json' with { type: 'json' };
7
+ import it from './it.json' with { type: 'json' };
8
+ import ja from './ja.json' with { type: 'json' };
9
+ import ko from './ko.json' with { type: 'json' };
10
+ import pt from './pt.json' with { type: 'json' };
11
+ import th from './th.json' with { type: 'json' };
12
+ import tr from './tr.json' with { type: 'json' };
13
+ import uk from './uk.json' with { type: 'json' };
14
+ import vi from './vi.json' with { type: 'json' };
15
+ import zh from './zh.json' with { type: 'json' };
16
16
 
17
17
  export { bn, de, en, es, fr, id, it, ja, ko, pt, th, tr, uk, vi, zh };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/widget",
3
- "version": "3.2.2",
3
+ "version": "3.3.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
  "type": "module",
6
6
  "main": "./_esm/index.js",
@@ -35,15 +35,15 @@
35
35
  "@emotion/react": "^11.13.0",
36
36
  "@emotion/styled": "^11.13.0",
37
37
  "@lifi/sdk": "^3.1.3",
38
- "@lifi/wallet-management": "^3.0.5",
39
- "@mui/icons-material": "^5.16.4",
40
- "@mui/lab": "^5.0.0-alpha.172",
41
- "@mui/material": "^5.16.4",
38
+ "@lifi/wallet-management": "^3.0.6",
39
+ "@mui/icons-material": "^5.16.5",
40
+ "@mui/lab": "^5.0.0-alpha.173",
41
+ "@mui/material": "^5.16.5",
42
42
  "@solana/wallet-adapter-base": "^0.9.23",
43
43
  "@solana/wallet-adapter-react": "^0.15.35",
44
- "@solana/web3.js": "^1.95.1",
45
- "@tanstack/react-query": "^5.51.11",
46
- "@tanstack/react-virtual": "^3.8.3",
44
+ "@solana/web3.js": "^1.95.2",
45
+ "@tanstack/react-query": "^5.51.15",
46
+ "@tanstack/react-virtual": "^3.8.4",
47
47
  "i18next": "^23.12.2",
48
48
  "microdiff": "^1.4.0",
49
49
  "mitt": "^3.0.1",
@@ -53,8 +53,8 @@
53
53
  "react-intersection-observer": "^9.13.0",
54
54
  "react-router-dom": "^6.25.1",
55
55
  "uuid": "^10.0.0",
56
- "viem": "^2.17.11",
57
- "wagmi": "^2.12.0",
56
+ "viem": "^2.18.5",
57
+ "wagmi": "^2.12.1",
58
58
  "zustand": "^4.5.4"
59
59
  },
60
60
  "peerDependencies": {
@@ -1,10 +1,11 @@
1
1
  import type { FullStatusData } from '@lifi/sdk';
2
2
  import { ContentCopyRounded } from '@mui/icons-material';
3
- import { Box, IconButton, Typography } from '@mui/material';
3
+ import { Box, Typography } from '@mui/material';
4
4
  import { useEffect, useMemo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { useLocation } from 'react-router-dom';
7
7
  import { Card } from '../../components/Card/Card.js';
8
+ import { CardIconButton } from '../../components/Card/CardIconButton.js';
8
9
  import { CardTitle } from '../../components/Card/CardTitle.js';
9
10
  import { ContractComponent } from '../../components/ContractComponent/ContractComponent.js';
10
11
  import { PageContainer } from '../../components/PageContainer.js';
@@ -124,10 +125,10 @@ export const TransactionDetailsPage: React.FC = () => {
124
125
  }}
125
126
  >
126
127
  <CardTitle flex={1}>{t('main.supportId')}</CardTitle>
127
- <Box mr={1} mt={1}>
128
- <IconButton size="medium" onClick={copySupportId}>
129
- <ContentCopyRounded fontSize="small" />
130
- </IconButton>
128
+ <Box mr={2} mt={1}>
129
+ <CardIconButton size="small" onClick={copySupportId}>
130
+ <ContentCopyRounded fontSize="inherit" />
131
+ </CardIconButton>
131
132
  </Box>
132
133
  </Box>
133
134
  <Typography
@@ -6,9 +6,11 @@ import { forwardRef, useRef } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { BottomSheet } from '../../components/BottomSheet/BottomSheet.js';
8
8
  import type { BottomSheetBase } from '../../components/BottomSheet/types.js';
9
+ import { FeeBreakdownTooltip } from '../../components/FeeBreakdownTooltip.js';
9
10
  import { useSetContentHeight } from '../../hooks/useContentHeight.js';
11
+ import { getAccumulatedFeeCostsBreakdown } from '../../utils/fees.js';
10
12
  import { CenterContainer, IconCircle } from './StatusBottomSheet.style.js';
11
- import { calcValueLoss } from './utils.js';
13
+ import { calculateValueLossPercentage } from './utils.js';
12
14
 
13
15
  interface TokenValueBottomSheetProps {
14
16
  route: Route;
@@ -44,6 +46,10 @@ const TokenValueBottomSheetContent: React.FC<TokenValueBottomSheetProps> = ({
44
46
  const { t } = useTranslation();
45
47
  const ref = useRef<HTMLElement>();
46
48
  useSetContentHeight(ref);
49
+ const { gasCosts, feeCosts, gasCostUSD, feeCostUSD } =
50
+ getAccumulatedFeeCostsBreakdown(route);
51
+ const fromAmountUSD = parseFloat(route.fromAmountUSD);
52
+ const toAmountUSD = parseFloat(route.toAmountUSD);
47
53
  return (
48
54
  <Box p={3} ref={ref}>
49
55
  <CenterContainer>
@@ -62,11 +68,23 @@ const TokenValueBottomSheetContent: React.FC<TokenValueBottomSheetProps> = ({
62
68
  </Typography>
63
69
  </Box>
64
70
  <Box display="flex" justifyContent="space-between" mt={0.25}>
65
- <Typography>{t('main.gasCost')}</Typography>
66
- <Typography fontWeight={600}>
67
- {t('format.currency', { value: route.gasCostUSD })}
68
- </Typography>
71
+ <Typography>{t('main.fees.network')}</Typography>
72
+ <FeeBreakdownTooltip gasCosts={gasCosts}>
73
+ <Typography fontWeight={600}>
74
+ {t('format.currency', { value: gasCostUSD })}
75
+ </Typography>
76
+ </FeeBreakdownTooltip>
69
77
  </Box>
78
+ {feeCostUSD ? (
79
+ <Box display="flex" justifyContent="space-between" mt={0.25}>
80
+ <Typography>{t('main.fees.provider')}</Typography>
81
+ <FeeBreakdownTooltip feeCosts={feeCosts}>
82
+ <Typography fontWeight={600}>
83
+ {t('format.currency', { value: feeCostUSD })}
84
+ </Typography>
85
+ </FeeBreakdownTooltip>
86
+ </Box>
87
+ ) : null}
70
88
  <Box display="flex" justifyContent="space-between" mt={0.25}>
71
89
  <Typography>{t('main.receiving')}</Typography>
72
90
  <Typography fontWeight={600}>
@@ -75,7 +93,15 @@ const TokenValueBottomSheetContent: React.FC<TokenValueBottomSheetProps> = ({
75
93
  </Box>
76
94
  <Box display="flex" justifyContent="space-between" mt={0.25}>
77
95
  <Typography>{t('main.valueLoss')}</Typography>
78
- <Typography fontWeight={600}>{calcValueLoss(route)}</Typography>
96
+ <Typography fontWeight={600}>
97
+ {calculateValueLossPercentage(
98
+ fromAmountUSD,
99
+ toAmountUSD,
100
+ gasCostUSD,
101
+ feeCostUSD,
102
+ )}
103
+ %
104
+ </Typography>
79
105
  </Box>
80
106
  <Box display="flex" mt={3}>
81
107
  <Button variant="text" onClick={onCancel} fullWidth>
@@ -89,16 +115,3 @@ const TokenValueBottomSheetContent: React.FC<TokenValueBottomSheetProps> = ({
89
115
  </Box>
90
116
  );
91
117
  };
92
-
93
- export const getTokenValueLossThreshold = (route?: Route) => {
94
- if (!route) {
95
- return false;
96
- }
97
- const fromAmountUSD = Number(route.fromAmountUSD || 0);
98
- const toAmountUSD = Number(route.toAmountUSD || 0);
99
- const gasCostUSD = Number(route.gasCostUSD || 0);
100
- if (!fromAmountUSD && !toAmountUSD) {
101
- return false;
102
- }
103
- return toAmountUSD / (fromAmountUSD + gasCostUSD) < 0.9;
104
- };
@@ -18,16 +18,17 @@ import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.j
18
18
  import { useFieldActions } from '../../stores/form/useFieldActions.js';
19
19
  import { RouteExecutionStatus } from '../../stores/routes/types.js';
20
20
  import { WidgetEvent } from '../../types/events.js';
21
+ import { getAccumulatedFeeCostsBreakdown } from '../../utils/fees.js';
21
22
  import type { ExchangeRateBottomSheetBase } from './ExchangeRateBottomSheet.js';
22
23
  import { ExchangeRateBottomSheet } from './ExchangeRateBottomSheet.js';
23
24
  import { RouteTracker } from './RouteTracker.js';
24
25
  import { StartTransactionButton } from './StartTransactionButton.js';
25
26
  import { StatusBottomSheet } from './StatusBottomSheet.js';
27
+ import { TokenValueBottomSheet } from './TokenValueBottomSheet.js';
26
28
  import {
27
- TokenValueBottomSheet,
29
+ calculateValueLossPercentage,
28
30
  getTokenValueLossThreshold,
29
- } from './TokenValueBottomSheet.js';
30
- import { calcValueLoss } from './utils.js';
31
+ } from './utils.js';
31
32
 
32
33
  export const TransactionPage: React.FC = () => {
33
34
  const { t } = useTranslation();
@@ -99,15 +100,22 @@ export const TransactionPage: React.FC = () => {
99
100
  return null;
100
101
  }
101
102
 
102
- const tokenValueLossThresholdExceeded = getTokenValueLossThreshold(route);
103
-
104
103
  const handleExecuteRoute = () => {
105
104
  if (tokenValueBottomSheetRef.current?.isOpen()) {
105
+ const { gasCostUSD, feeCostUSD } = getAccumulatedFeeCostsBreakdown(route);
106
+ const fromAmountUSD = parseFloat(route.fromAmountUSD);
107
+ const toAmountUSD = parseFloat(route.toAmountUSD);
106
108
  emitter.emit(WidgetEvent.RouteHighValueLoss, {
107
- fromAmountUsd: route.fromAmountUSD,
108
- gasCostUSD: route.gasCostUSD,
109
- toAmountUSD: route.toAmountUSD,
110
- valueLoss: calcValueLoss(route),
109
+ fromAmountUSD,
110
+ toAmountUSD,
111
+ gasCostUSD,
112
+ feeCostUSD,
113
+ valueLoss: calculateValueLossPercentage(
114
+ fromAmountUSD,
115
+ toAmountUSD,
116
+ gasCostUSD,
117
+ feeCostUSD,
118
+ ),
111
119
  });
112
120
  }
113
121
  tokenValueBottomSheetRef.current?.close();
@@ -121,6 +129,15 @@ export const TransactionPage: React.FC = () => {
121
129
 
122
130
  const handleStartClick = async () => {
123
131
  if (status === RouteExecutionStatus.Idle) {
132
+ const { gasCostUSD, feeCostUSD } = getAccumulatedFeeCostsBreakdown(route);
133
+ const fromAmountUSD = parseFloat(route.fromAmountUSD);
134
+ const toAmountUSD = parseFloat(route.toAmountUSD);
135
+ const tokenValueLossThresholdExceeded = getTokenValueLossThreshold(
136
+ fromAmountUSD,
137
+ toAmountUSD,
138
+ gasCostUSD,
139
+ feeCostUSD,
140
+ );
124
141
  if (tokenValueLossThresholdExceeded && subvariant !== 'custom') {
125
142
  tokenValueBottomSheetRef.current?.open();
126
143
  } else {
@@ -197,7 +214,7 @@ export const TransactionPage: React.FC = () => {
197
214
  </>
198
215
  ) : null}
199
216
  {status ? <StatusBottomSheet status={status} route={route} /> : null}
200
- {tokenValueLossThresholdExceeded && subvariant !== 'custom' ? (
217
+ {subvariant !== 'custom' ? (
201
218
  <TokenValueBottomSheet
202
219
  route={route}
203
220
  ref={tokenValueBottomSheetRef}
@@ -1,10 +1,25 @@
1
- import type { Route } from '@lifi/sdk';
1
+ export const calculateValueLossPercentage = (
2
+ fromAmountUSD: number,
3
+ toAmountUSD: number,
4
+ gasCostUSD: number,
5
+ feeCostUSD: number,
6
+ ) => {
7
+ return parseFloat(
8
+ (
9
+ (toAmountUSD / (fromAmountUSD + gasCostUSD + feeCostUSD) - 1) *
10
+ 100
11
+ ).toFixed(2),
12
+ );
13
+ };
2
14
 
3
- export const calcValueLoss = (route: Route) => {
4
- return `${(
5
- (Number(route.toAmountUSD || 0) /
6
- (Number(route.fromAmountUSD || 0) + Number(route.gasCostUSD || 0)) -
7
- 1) *
8
- 100
9
- ).toFixed(2)}%`;
15
+ export const getTokenValueLossThreshold = (
16
+ fromAmountUSD: number,
17
+ toAmountUSD: number,
18
+ gasCostUSD: number,
19
+ feeCostUSD: number,
20
+ ) => {
21
+ if (!fromAmountUSD || !toAmountUSD) {
22
+ return false;
23
+ }
24
+ return toAmountUSD / (fromAmountUSD + gasCostUSD + feeCostUSD) < 0.9;
10
25
  };
@@ -15,10 +15,10 @@ export interface RouteExecutionState {
15
15
  }
16
16
 
17
17
  export enum RouteExecutionStatus {
18
- Idle = 0,
19
- Pending = 1 << 0,
20
- Done = 1 << 1,
21
- Failed = 1 << 2,
22
- Partial = 1 << 3,
23
- Refunded = 1 << 4,
18
+ Idle = 1 << 0,
19
+ Pending = 1 << 1,
20
+ Done = 1 << 2,
21
+ Failed = 1 << 3,
22
+ Partial = 1 << 4,
23
+ Refunded = 1 << 5,
24
24
  }
package/types/events.ts CHANGED
@@ -37,10 +37,11 @@ export interface ContactSupport {
37
37
  }
38
38
 
39
39
  export interface RouteHighValueLossUpdate {
40
- fromAmountUsd: string;
41
- gasCostUSD?: string;
42
- toAmountUSD: string;
43
- valueLoss: string;
40
+ fromAmountUSD: number;
41
+ toAmountUSD: number;
42
+ gasCostUSD?: number;
43
+ feeCostUSD?: number;
44
+ valueLoss: number;
44
45
  }
45
46
 
46
47
  export interface RouteExecutionUpdate {
@@ -167,7 +167,7 @@ export const buildRouteFromTxHistory = (
167
167
  toAmountMin: receiving.amount ?? '',
168
168
  toAmount: receiving.amount ?? '',
169
169
  toAmountUSD: receiving.amountUSD ?? '',
170
- executionDuration: 30,
170
+ executionDuration: 0,
171
171
  },
172
172
  includedSteps: [
173
173
  {