@lifi/widget 3.0.1 → 3.1.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 (227) hide show
  1. package/CHANGELOG.md +18 -2
  2. package/README.md +24 -5
  3. package/_esm/components/Card/CardIconButton.js +1 -0
  4. package/_esm/components/Card/CardIconButton.js.map +1 -1
  5. package/_esm/components/Card/CardLabel.d.ts +1 -1
  6. package/_esm/components/Card/CardLabel.js +8 -12
  7. package/_esm/components/Card/CardLabel.js.map +1 -1
  8. package/_esm/components/ChainSelect/ChainSelect.js +1 -1
  9. package/_esm/components/ChainSelect/ChainSelect.js.map +1 -1
  10. package/_esm/components/FeeBreakdownTooltip.d.ts +12 -0
  11. package/_esm/components/FeeBreakdownTooltip.js +13 -0
  12. package/_esm/components/FeeBreakdownTooltip.js.map +1 -0
  13. package/_esm/components/Header/CloseDrawerButton.js +1 -1
  14. package/_esm/components/Header/CloseDrawerButton.js.map +1 -1
  15. package/_esm/components/Header/SettingsButton.js +1 -1
  16. package/_esm/components/Header/SettingsButton.js.map +1 -1
  17. package/_esm/components/Header/TransactionHistoryButton.js +1 -1
  18. package/_esm/components/Header/TransactionHistoryButton.js.map +1 -1
  19. package/_esm/components/IconTypography.d.ts +3 -0
  20. package/_esm/components/IconTypography.js +8 -0
  21. package/_esm/components/IconTypography.js.map +1 -0
  22. package/_esm/components/PoweredBy/PoweredBy.js +1 -1
  23. package/_esm/components/PoweredBy/PoweredBy.js.map +1 -1
  24. package/_esm/components/ProgressToNextUpdate.js +1 -1
  25. package/_esm/components/ProgressToNextUpdate.js.map +1 -1
  26. package/_esm/components/RouteCard/RouteCard.js +7 -16
  27. package/_esm/components/RouteCard/RouteCard.js.map +1 -1
  28. package/_esm/components/RouteCard/RouteCard.style.d.ts +0 -3
  29. package/_esm/components/RouteCard/RouteCard.style.js +1 -7
  30. package/_esm/components/RouteCard/RouteCard.style.js.map +1 -1
  31. package/_esm/components/RouteCard/RouteCardEssentials.js +13 -14
  32. package/_esm/components/RouteCard/RouteCardEssentials.js.map +1 -1
  33. package/_esm/components/RouteCard/RouteCardEssentialsExpanded.js +4 -20
  34. package/_esm/components/RouteCard/RouteCardEssentialsExpanded.js.map +1 -1
  35. package/_esm/components/RouteCard/types.d.ts +1 -6
  36. package/_esm/components/Routes/RoutesExpanded.js +2 -4
  37. package/_esm/components/Routes/RoutesExpanded.js.map +1 -1
  38. package/_esm/components/SendToWallet/SendToWalletExpandButton.js +1 -1
  39. package/_esm/components/SendToWallet/SendToWalletExpandButton.js.map +1 -1
  40. package/_esm/components/Step/Step.d.ts +1 -0
  41. package/_esm/components/Step/Step.js +2 -2
  42. package/_esm/components/Step/Step.js.map +1 -1
  43. package/_esm/components/Step/StepList.js +16 -7
  44. package/_esm/components/Step/StepList.js.map +1 -1
  45. package/_esm/components/Step/StepTimer.js +3 -3
  46. package/_esm/components/Step/StepTimer.js.map +1 -1
  47. package/_esm/components/StepActions/StepActions.js +5 -4
  48. package/_esm/components/StepActions/StepActions.js.map +1 -1
  49. package/_esm/components/Token/Token.d.ts +2 -0
  50. package/_esm/components/Token/Token.js +22 -8
  51. package/_esm/components/Token/Token.js.map +1 -1
  52. package/_esm/components/Token/Token.style.js +1 -0
  53. package/_esm/components/Token/Token.style.js.map +1 -1
  54. package/_esm/components/TokenRate.d.ts +7 -0
  55. package/_esm/components/TokenRate.js +41 -0
  56. package/_esm/components/TokenRate.js.map +1 -0
  57. package/_esm/components/TransactionDetails.d.ts +7 -0
  58. package/_esm/components/TransactionDetails.js +46 -0
  59. package/_esm/components/TransactionDetails.js.map +1 -0
  60. package/_esm/config/version.d.ts +1 -1
  61. package/_esm/config/version.js +1 -1
  62. package/_esm/hooks/timer/useInterval.d.ts +1 -0
  63. package/_esm/hooks/timer/useInterval.js +18 -0
  64. package/_esm/hooks/timer/useInterval.js.map +1 -0
  65. package/_esm/hooks/timer/useTimer.d.ts +18 -0
  66. package/_esm/hooks/timer/useTimer.js +61 -0
  67. package/_esm/hooks/timer/useTimer.js.map +1 -0
  68. package/_esm/hooks/timer/utils.d.ts +11 -0
  69. package/_esm/hooks/timer/utils.js +46 -0
  70. package/_esm/hooks/timer/utils.js.map +1 -0
  71. package/_esm/hooks/useAccount.js +4 -5
  72. package/_esm/hooks/useAccount.js.map +1 -1
  73. package/_esm/hooks/useAvailableChains.js +1 -0
  74. package/_esm/hooks/useAvailableChains.js.map +1 -1
  75. package/_esm/hooks/useGasRecommendation.js +1 -0
  76. package/_esm/hooks/useGasRecommendation.js.map +1 -1
  77. package/_esm/hooks/useRoutes.d.ts +4 -2
  78. package/_esm/hooks/useRoutes.js +17 -10
  79. package/_esm/hooks/useRoutes.js.map +1 -1
  80. package/_esm/hooks/useTokens.js +1 -0
  81. package/_esm/hooks/useTokens.js.map +1 -1
  82. package/_esm/hooks/useTools.js +1 -0
  83. package/_esm/hooks/useTools.js.map +1 -1
  84. package/_esm/hooks/useTransactionDetails.d.ts +1 -1
  85. package/_esm/hooks/useTransactionDetails.js +1 -1
  86. package/_esm/hooks/useTransactionDetails.js.map +1 -1
  87. package/_esm/hooks/useTransactionHistory.js.map +1 -1
  88. package/_esm/i18n/en.json +14 -20
  89. package/_esm/i18n/index.js +15 -15
  90. package/_esm/i18n/index.js.map +1 -1
  91. package/_esm/index.d.ts +2 -1
  92. package/_esm/index.js +1 -4
  93. package/_esm/index.js.map +1 -1
  94. package/_esm/pages/MainPage/ReviewButton.js +2 -4
  95. package/_esm/pages/MainPage/ReviewButton.js.map +1 -1
  96. package/_esm/pages/RoutesPage/RoutesPage.js +2 -4
  97. package/_esm/pages/RoutesPage/RoutesPage.js.map +1 -1
  98. package/_esm/pages/SelectEnabledToolsPage.js +1 -1
  99. package/_esm/pages/SelectEnabledToolsPage.js.map +1 -1
  100. package/_esm/pages/SendToWallet/SendToWalletPage.js +1 -1
  101. package/_esm/pages/SendToWallet/SendToWalletPage.js.map +1 -1
  102. package/_esm/pages/SettingsPage/ThemeSettings.js +1 -1
  103. package/_esm/pages/SettingsPage/ThemeSettings.js.map +1 -1
  104. package/_esm/pages/TransactionDetailsPage/TransactionDetailsPage.js +5 -7
  105. package/_esm/pages/TransactionDetailsPage/TransactionDetailsPage.js.map +1 -1
  106. package/_esm/pages/TransactionHistoryPage/TransactionHistoryItem.js +2 -2
  107. package/_esm/pages/TransactionHistoryPage/TransactionHistoryItem.js.map +1 -1
  108. package/_esm/pages/TransactionPage/ExchangeRateBottomSheet.js +2 -2
  109. package/_esm/pages/TransactionPage/ExchangeRateBottomSheet.js.map +1 -1
  110. package/_esm/pages/TransactionPage/RouteTracker.d.ts +7 -0
  111. package/_esm/pages/TransactionPage/RouteTracker.js +39 -0
  112. package/_esm/pages/TransactionPage/RouteTracker.js.map +1 -0
  113. package/_esm/pages/TransactionPage/StartTransactionButton.d.ts +0 -1
  114. package/_esm/pages/TransactionPage/StartTransactionButton.js +0 -9
  115. package/_esm/pages/TransactionPage/StartTransactionButton.js.map +1 -1
  116. package/_esm/pages/TransactionPage/TransactionPage.js +10 -21
  117. package/_esm/pages/TransactionPage/TransactionPage.js.map +1 -1
  118. package/_esm/pages/TransactionPage/types.d.ts +0 -1
  119. package/_esm/providers/I18nProvider/I18nProvider.js +2 -0
  120. package/_esm/providers/I18nProvider/I18nProvider.js.map +1 -1
  121. package/_esm/providers/I18nProvider/currencyExtendedFormatter.d.ts +1 -0
  122. package/_esm/providers/I18nProvider/currencyExtendedFormatter.js +13 -0
  123. package/_esm/providers/I18nProvider/currencyExtendedFormatter.js.map +1 -0
  124. package/_esm/providers/WalletProvider/EVMBaseProvider.js +18 -12
  125. package/_esm/providers/WalletProvider/EVMBaseProvider.js.map +1 -1
  126. package/_esm/stores/routes/createRouteExecutionStore.js +2 -2
  127. package/_esm/stores/routes/createRouteExecutionStore.js.map +1 -1
  128. package/_esm/stores/routes/types.d.ts +1 -1
  129. package/_esm/stores/routes/useSetExecutableRoute.d.ts +1 -1
  130. package/_esm/themes/createTheme.js +6 -0
  131. package/_esm/themes/createTheme.js.map +1 -1
  132. package/_esm/types/widget.d.ts +1 -2
  133. package/_esm/utils/converters.js +5 -13
  134. package/_esm/utils/converters.js.map +1 -1
  135. package/_esm/utils/fees.d.ts +3 -3
  136. package/_esm/utils/fees.js +18 -13
  137. package/_esm/utils/fees.js.map +1 -1
  138. package/_esm/utils/format.d.ts +13 -4
  139. package/_esm/utils/format.js +51 -13
  140. package/_esm/utils/format.js.map +1 -1
  141. package/components/Card/CardIconButton.tsx +1 -0
  142. package/components/Card/CardLabel.tsx +13 -29
  143. package/components/ChainSelect/ChainSelect.tsx +1 -9
  144. package/components/FeeBreakdownTooltip.tsx +66 -0
  145. package/components/Header/CloseDrawerButton.tsx +1 -1
  146. package/components/Header/SettingsButton.tsx +1 -1
  147. package/components/Header/TransactionHistoryButton.tsx +1 -1
  148. package/components/IconTypography.ts +9 -0
  149. package/components/PoweredBy/PoweredBy.tsx +1 -1
  150. package/components/ProgressToNextUpdate.tsx +0 -3
  151. package/components/RouteCard/RouteCard.style.ts +1 -9
  152. package/components/RouteCard/RouteCard.tsx +15 -71
  153. package/components/RouteCard/RouteCardEssentials.tsx +52 -81
  154. package/components/RouteCard/RouteCardEssentialsExpanded.tsx +17 -107
  155. package/components/RouteCard/types.ts +1 -7
  156. package/components/Routes/RoutesExpanded.tsx +3 -4
  157. package/components/SendToWallet/SendToWalletExpandButton.tsx +1 -6
  158. package/components/Step/Step.tsx +11 -2
  159. package/components/Step/StepList.tsx +19 -10
  160. package/components/Step/StepTimer.tsx +9 -6
  161. package/components/StepActions/StepActions.tsx +11 -14
  162. package/components/Token/Token.style.tsx +1 -0
  163. package/components/Token/Token.tsx +49 -9
  164. package/components/TokenRate.tsx +79 -0
  165. package/components/TransactionDetails.tsx +182 -0
  166. package/config/version.ts +1 -1
  167. package/hooks/timer/useInterval.ts +21 -0
  168. package/hooks/timer/useTimer.ts +91 -0
  169. package/hooks/timer/utils.ts +54 -0
  170. package/hooks/useAccount.ts +8 -5
  171. package/hooks/useAvailableChains.ts +2 -1
  172. package/hooks/useGasRecommendation.ts +1 -0
  173. package/hooks/useRoutes.ts +23 -13
  174. package/hooks/useTokens.ts +1 -0
  175. package/hooks/useTools.ts +2 -1
  176. package/hooks/useTransactionDetails.ts +3 -3
  177. package/hooks/useTransactionHistory.ts +1 -1
  178. package/i18n/en.json +14 -20
  179. package/i18n/index.ts +15 -15
  180. package/index.ts +2 -5
  181. package/package.json +15 -16
  182. package/pages/MainPage/ReviewButton.tsx +2 -4
  183. package/pages/RoutesPage/RoutesPage.tsx +3 -4
  184. package/pages/SelectEnabledToolsPage.tsx +1 -1
  185. package/pages/SendToWallet/SendToWalletPage.tsx +1 -1
  186. package/pages/SettingsPage/ThemeSettings.tsx +1 -1
  187. package/pages/TransactionDetailsPage/TransactionDetailsPage.tsx +9 -20
  188. package/pages/TransactionHistoryPage/TransactionHistoryItem.tsx +3 -5
  189. package/pages/TransactionPage/ExchangeRateBottomSheet.tsx +0 -2
  190. package/pages/TransactionPage/RouteTracker.tsx +68 -0
  191. package/pages/TransactionPage/StartTransactionButton.tsx +0 -24
  192. package/pages/TransactionPage/TransactionPage.tsx +21 -48
  193. package/pages/TransactionPage/types.ts +0 -1
  194. package/providers/I18nProvider/I18nProvider.tsx +6 -0
  195. package/providers/I18nProvider/currencyExtendedFormatter.ts +15 -0
  196. package/providers/WalletProvider/EVMBaseProvider.tsx +13 -7
  197. package/stores/routes/createRouteExecutionStore.ts +2 -2
  198. package/stores/routes/types.ts +1 -1
  199. package/themes/createTheme.ts +6 -0
  200. package/types/widget.ts +1 -2
  201. package/utils/converters.ts +5 -13
  202. package/utils/fees.ts +23 -24
  203. package/utils/format.ts +67 -20
  204. package/_esm/components/Insurance/Insurance.d.ts +0 -2
  205. package/_esm/components/Insurance/Insurance.js +0 -8
  206. package/_esm/components/Insurance/Insurance.js.map +0 -1
  207. package/_esm/components/Insurance/InsuranceCard.d.ts +0 -2
  208. package/_esm/components/Insurance/InsuranceCard.js +0 -36
  209. package/_esm/components/Insurance/InsuranceCard.js.map +0 -1
  210. package/_esm/components/Insurance/InsuranceCollapsed.d.ts +0 -2
  211. package/_esm/components/Insurance/InsuranceCollapsed.js +0 -29
  212. package/_esm/components/Insurance/InsuranceCollapsed.js.map +0 -1
  213. package/_esm/components/Insurance/types.d.ts +0 -19
  214. package/_esm/components/Insurance/types.js +0 -2
  215. package/_esm/components/Insurance/types.js.map +0 -1
  216. package/_esm/components/StepActions/StepFeeBreakdown.d.ts +0 -4
  217. package/_esm/components/StepActions/StepFeeBreakdown.js +0 -42
  218. package/_esm/components/StepActions/StepFeeBreakdown.js.map +0 -1
  219. package/_esm/icons/InsuraceLogo.d.ts +0 -2
  220. package/_esm/icons/InsuraceLogo.js +0 -8
  221. package/_esm/icons/InsuraceLogo.js.map +0 -1
  222. package/components/Insurance/Insurance.tsx +0 -22
  223. package/components/Insurance/InsuranceCard.tsx +0 -119
  224. package/components/Insurance/InsuranceCollapsed.tsx +0 -59
  225. package/components/Insurance/types.ts +0 -24
  226. package/components/StepActions/StepFeeBreakdown.tsx +0 -108
  227. package/icons/InsuraceLogo.tsx +0 -46
@@ -12,19 +12,27 @@ export const getStepList = (
12
12
  const lastIndex = steps.length - 1;
13
13
  const fromToken: TokenAmount | undefined =
14
14
  index === 0
15
- ? { ...step.action.fromToken, amount: BigInt(step.action.fromAmount) }
16
- : undefined;
17
- const toToken: TokenAmount | undefined =
18
- index === lastIndex
19
15
  ? {
20
- ...(step.execution?.toToken ?? step.action?.toToken),
21
- amount: step.execution?.toAmount
22
- ? BigInt(step.execution.toAmount)
23
- : subvariant === 'custom'
24
- ? BigInt(route.toAmount)
25
- : BigInt(step.estimate.toAmount),
16
+ ...step.action.fromToken,
17
+ amount: BigInt(step.action.fromAmount),
26
18
  }
27
19
  : undefined;
20
+ let toToken: TokenAmount | undefined;
21
+ let impactToken: TokenAmount | undefined;
22
+ if (index === lastIndex) {
23
+ toToken = {
24
+ ...(step.execution?.toToken ?? step.action.toToken),
25
+ amount: step.execution?.toAmount
26
+ ? BigInt(step.execution.toAmount)
27
+ : subvariant === 'custom'
28
+ ? BigInt(route.toAmount)
29
+ : BigInt(step.estimate.toAmount),
30
+ };
31
+ impactToken = {
32
+ ...steps[0].action.fromToken,
33
+ amount: BigInt(steps[0].action.fromAmount),
34
+ };
35
+ }
28
36
  const toAddress =
29
37
  index === lastIndex && route.fromAddress !== route.toAddress
30
38
  ? route.toAddress
@@ -35,6 +43,7 @@ export const getStepList = (
35
43
  step={step}
36
44
  fromToken={fromToken}
37
45
  toToken={toToken}
46
+ impactToken={impactToken}
38
47
  toAddress={toAddress}
39
48
  />
40
49
  {steps.length > 1 && index !== steps.length - 1 ? (
@@ -1,7 +1,7 @@
1
1
  import type { LiFiStepExtended } from '@lifi/sdk';
2
2
  import { useEffect, useState } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
- import { useTimer } from 'react-timer-hook';
4
+ import { useTimer } from '../../hooks/timer/useTimer.js';
5
5
 
6
6
  const getExpiryTimestamp = (step: LiFiStepExtended) =>
7
7
  new Date(
@@ -52,11 +52,14 @@ export const StepTimer: React.FC<{
52
52
  ]);
53
53
 
54
54
  if (!isExecutionStarted) {
55
- return new Intl.NumberFormat(i18n.language, {
56
- style: 'unit',
57
- unit: 'minute',
58
- unitDisplay: 'narrow',
59
- }).format(Math.ceil(step.estimate.executionDuration / 60));
55
+ return Math.ceil(step.estimate.executionDuration / 60).toLocaleString(
56
+ i18n.language,
57
+ {
58
+ style: 'unit',
59
+ unit: 'minute',
60
+ unitDisplay: 'narrow',
61
+ },
62
+ );
60
63
  }
61
64
 
62
65
  const isTimerExpired = isExpired || (!minutes && !seconds);
@@ -26,7 +26,6 @@ import {
26
26
  StepLabel,
27
27
  StepLabelTypography,
28
28
  } from './StepActions.style.js';
29
- import { StepFeeBreakdown } from './StepFeeBreakdown.js';
30
29
  import { StepFees } from './StepFees.js';
31
30
  import type {
32
31
  IncludedStepsProps,
@@ -74,22 +73,21 @@ export const StepActions: React.FC<StepActionsProps> = ({
74
73
  </Badge>
75
74
  <Box flex={1}>
76
75
  <Typography fontSize={18} fontWeight={600} lineHeight={1.3334} ml={2}>
77
- {t(`main.stepDetails`, {
78
- tool: toolDetails.name,
79
- })}
76
+ {toolDetails.name?.includes('LI.FI')
77
+ ? toolDetails.name
78
+ : t(`main.stepDetails`, {
79
+ tool: toolDetails.name,
80
+ })}
80
81
  </Typography>
81
- <Collapse
82
- timeout={225}
83
- in={dense && !cardExpanded}
84
- mountOnEnter
85
- unmountOnExit
86
- >
87
- <StepFees ml={2} step={step} />
88
- </Collapse>
82
+ <StepFees ml={2} step={step} />
89
83
  </Box>
90
84
  {dense ? (
91
85
  <CardIconButton onClick={handleExpand} size="small">
92
- {cardExpanded ? <ExpandLess /> : <ExpandMore />}
86
+ {cardExpanded ? (
87
+ <ExpandLess fontSize="inherit" />
88
+ ) : (
89
+ <ExpandMore fontSize="inherit" />
90
+ )}
93
91
  </CardIconButton>
94
92
  ) : null}
95
93
  </Box>
@@ -100,7 +98,6 @@ export const StepActions: React.FC<StepActionsProps> = ({
100
98
  subvariant={subvariant}
101
99
  subvariantOptions={subvariantOptions}
102
100
  />
103
- <StepFeeBreakdown step={step} />
104
101
  </Collapse>
105
102
  ) : (
106
103
  <IncludedSteps
@@ -16,6 +16,7 @@ export const TextSecondary = styled(Typography, {
16
16
  color: dot
17
17
  ? alpha(theme.palette.text.secondary, 0.56)
18
18
  : theme.palette.text.secondary,
19
+ whiteSpace: 'nowrap',
19
20
  }));
20
21
 
21
22
  export const TokenDivider = styled(Box)(({ theme }) => ({
@@ -1,12 +1,16 @@
1
1
  /* eslint-disable react/no-array-index-key */
2
2
  import type { LiFiStep, TokenAmount } from '@lifi/sdk';
3
3
  import type { BoxProps } from '@mui/material';
4
- import { Box, Grow, Skeleton } from '@mui/material';
4
+ import { Box, Grow, Skeleton, Tooltip } from '@mui/material';
5
5
  import type { FC, PropsWithChildren, ReactElement } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { useChain } from '../../hooks/useChain.js';
8
8
  import { useToken } from '../../hooks/useToken.js';
9
- import { formatTokenAmount, formatTokenPrice } from '../../utils/format.js';
9
+ import {
10
+ convertToSubscriptFormat,
11
+ formatTokenAmount,
12
+ formatTokenPrice,
13
+ } from '../../utils/format.js';
10
14
  import { AvatarBadgedSkeleton } from '../Avatar/Avatar.js';
11
15
  import { TokenAvatar } from '../Avatar/TokenAvatar.js';
12
16
  import { SmallAvatar } from '../SmallAvatar.js';
@@ -15,6 +19,8 @@ import { TextSecondary, TextSecondaryContainer } from './Token.style.js';
15
19
 
16
20
  interface TokenProps {
17
21
  token: TokenAmount;
22
+ impactToken?: TokenAmount;
23
+ enableImpactTokenTooltip?: boolean;
18
24
  step?: LiFiStep;
19
25
  stepVisible?: boolean;
20
26
  disableDescription?: boolean;
@@ -49,6 +55,8 @@ export const TokenFallback: FC<TokenProps & BoxProps> = ({
49
55
 
50
56
  export const TokenBase: FC<TokenProps & BoxProps> = ({
51
57
  token,
58
+ impactToken,
59
+ enableImpactTokenTooltip,
52
60
  step,
53
61
  stepVisible,
54
62
  disableDescription,
@@ -69,11 +77,29 @@ export const TokenBase: FC<TokenProps & BoxProps> = ({
69
77
  );
70
78
  }
71
79
 
72
- const formattedTokenAmount = formatTokenAmount(token.amount, token.decimals);
73
- const formattedTokenPrice = formatTokenPrice(
74
- formattedTokenAmount,
75
- token.priceUSD,
76
- );
80
+ const tokenAmount = formatTokenAmount(token.amount, token.decimals);
81
+ const tokenPrice = formatTokenPrice(tokenAmount, token.priceUSD);
82
+
83
+ let priceImpact;
84
+ if (impactToken) {
85
+ const impactTokenAmount = formatTokenAmount(
86
+ impactToken.amount,
87
+ impactToken.decimals,
88
+ );
89
+ const impactTokenPrice =
90
+ formatTokenPrice(impactTokenAmount, impactToken.priceUSD) || 0.01;
91
+
92
+ const impact = (tokenPrice / impactTokenPrice - 1) * 100;
93
+
94
+ priceImpact = convertToSubscriptFormat(impact, {
95
+ notation: 'standard',
96
+ roundingPriority: 'morePrecision',
97
+ maximumSignificantDigits: 2,
98
+ maximumFractionDigits: 2,
99
+ useGrouping: false,
100
+ roundingMode: 'trunc',
101
+ });
102
+ }
77
103
 
78
104
  const tokenOnChain = !disableDescription ? (
79
105
  <TextSecondary>
@@ -101,16 +127,30 @@ export const TokenBase: FC<TokenProps & BoxProps> = ({
101
127
  }}
102
128
  >
103
129
  {t('format.number', {
104
- value: formattedTokenAmount,
130
+ value: tokenAmount,
105
131
  })}
106
132
  </TextFitter>
107
133
  </Box>
108
134
  <TextSecondaryContainer component="span">
109
135
  <TextSecondary>
110
136
  {t(`format.currency`, {
111
- value: formattedTokenPrice,
137
+ value: tokenPrice,
112
138
  })}
113
139
  </TextSecondary>
140
+ {impactToken ? (
141
+ <TextSecondary px={0.5} dot>
142
+ &#x2022;
143
+ </TextSecondary>
144
+ ) : null}
145
+ {impactToken ? (
146
+ enableImpactTokenTooltip ? (
147
+ <Tooltip title={t('tooltip.priceImpact')} sx={{ cursor: 'help' }}>
148
+ <TextSecondary>{priceImpact}%</TextSecondary>
149
+ </Tooltip>
150
+ ) : (
151
+ <TextSecondary>{priceImpact}%</TextSecondary>
152
+ )
153
+ ) : null}
114
154
  {!disableDescription ? (
115
155
  <TextSecondary px={0.5} dot>
116
156
  &#x2022;
@@ -0,0 +1,79 @@
1
+ import type { RouteExtended } from '@lifi/sdk';
2
+ import type { TypographyProps } from '@mui/material';
3
+ import { Typography } from '@mui/material';
4
+ import type { MouseEventHandler } from 'react';
5
+ import { formatUnits } from 'viem';
6
+ import { create } from 'zustand';
7
+ import {
8
+ convertToSubscriptFormat,
9
+ precisionFormatter,
10
+ } from '../utils/format.js';
11
+
12
+ interface TokenRateProps extends TypographyProps {
13
+ route: RouteExtended;
14
+ }
15
+
16
+ interface TokenRateState {
17
+ isForward: boolean;
18
+ toggleIsForward(): void;
19
+ }
20
+
21
+ const useTokenRateStore = create<TokenRateState>((set) => ({
22
+ isForward: true,
23
+ toggleIsForward: () => set((state) => ({ isForward: !state.isForward })),
24
+ }));
25
+
26
+ export const TokenRate: React.FC<TokenRateProps> = ({ route, ...props }) => {
27
+ const { isForward, toggleIsForward } = useTokenRateStore();
28
+
29
+ const toggleRate: MouseEventHandler<HTMLSpanElement> = (e) => {
30
+ e.stopPropagation();
31
+ toggleIsForward();
32
+ };
33
+
34
+ const lastStep = route.steps.at(-1);
35
+
36
+ const fromToken = {
37
+ ...route.fromToken,
38
+ amount: BigInt(route.fromAmount),
39
+ };
40
+ const toToken = {
41
+ ...(lastStep?.execution?.toToken ??
42
+ lastStep?.action.toToken ??
43
+ route.toToken),
44
+ amount: lastStep?.execution?.toAmount
45
+ ? BigInt(lastStep.execution.toAmount)
46
+ : BigInt(route.toAmount),
47
+ };
48
+
49
+ const fromToRate =
50
+ parseFloat(formatUnits(toToken.amount!, toToken.decimals)) /
51
+ parseFloat(formatUnits(fromToken.amount!, fromToken.decimals));
52
+ const toFromRate =
53
+ parseFloat(formatUnits(fromToken.amount!, fromToken.decimals)) /
54
+ parseFloat(formatUnits(toToken.amount!, toToken.decimals));
55
+
56
+ const rateText = isForward
57
+ ? `1 ${fromToken.symbol} ≈ ${convertToSubscriptFormat(fromToRate)} ${toToken.symbol}`
58
+ : `1 ${toToken.symbol} ≈ ${convertToSubscriptFormat(toFromRate)} ${fromToken.symbol}`;
59
+
60
+ const rateTitle = isForward
61
+ ? `1 ${fromToken.symbol} ≈ ${precisionFormatter.format(fromToRate)} ${toToken.symbol}`
62
+ : `1 ${toToken.symbol} ≈ ${precisionFormatter.format(toFromRate)} ${fromToken.symbol}`;
63
+
64
+ return (
65
+ <Typography
66
+ fontSize={14}
67
+ lineHeight={1.429}
68
+ color="text.secondary"
69
+ fontWeight="500"
70
+ onClick={toggleRate}
71
+ role="button"
72
+ sx={{ cursor: 'pointer' }}
73
+ title={rateTitle}
74
+ {...props}
75
+ >
76
+ {rateText}
77
+ </Typography>
78
+ );
79
+ };
@@ -0,0 +1,182 @@
1
+ import type { RouteExtended } from '@lifi/sdk';
2
+ import {
3
+ ExpandLess,
4
+ ExpandMore,
5
+ LocalGasStationRounded,
6
+ } from '@mui/icons-material';
7
+ import type { CardProps } from '@mui/material';
8
+ import { Box, Collapse, Tooltip, Typography } from '@mui/material';
9
+ import { useState } from 'react';
10
+ import { useTranslation } from 'react-i18next';
11
+ import { isRouteDone } from '../stores/routes/utils.js';
12
+ import { getFeeCostsBreakdown, getGasCostsBreakdown } from '../utils/fees.js';
13
+ import {
14
+ convertToSubscriptFormat,
15
+ formatTokenAmount,
16
+ formatTokenPrice,
17
+ } from '../utils/format.js';
18
+ import { Card } from './Card/Card.js';
19
+ import { CardIconButton } from './Card/CardIconButton.js';
20
+ import { FeeBreakdownTooltip } from './FeeBreakdownTooltip.js';
21
+ import { IconTypography } from './IconTypography.js';
22
+ import { TokenRate } from './TokenRate.js';
23
+
24
+ interface TransactionDetailsProps extends CardProps {
25
+ route: RouteExtended;
26
+ }
27
+
28
+ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
29
+ route,
30
+ ...props
31
+ }) => {
32
+ const { t } = useTranslation();
33
+ const [cardExpanded, setCardExpanded] = useState(false);
34
+
35
+ const toggleCard = () => {
36
+ setCardExpanded((cardExpanded) => !cardExpanded);
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;
50
+
51
+ const fromTokenAmount = formatTokenAmount(
52
+ BigInt(route.fromAmount),
53
+ route.fromToken.decimals,
54
+ );
55
+ const fromTokenPrice = formatTokenPrice(
56
+ fromTokenAmount,
57
+ route.fromToken.priceUSD,
58
+ );
59
+ const toTokenAmount = formatTokenAmount(
60
+ BigInt(route.toAmount),
61
+ route.toToken.decimals,
62
+ );
63
+ const toTokenPrice =
64
+ formatTokenPrice(toTokenAmount, route.toToken.priceUSD) || 0.01;
65
+
66
+ const impact = (toTokenPrice / fromTokenPrice - 1) * 100;
67
+
68
+ const priceImpact = convertToSubscriptFormat(impact, {
69
+ notation: 'standard',
70
+ roundingPriority: 'morePrecision',
71
+ maximumSignificantDigits: 2,
72
+ maximumFractionDigits: 2,
73
+ useGrouping: false,
74
+ roundingMode: 'trunc',
75
+ });
76
+
77
+ return (
78
+ <Card selectionColor="secondary" {...props}>
79
+ <Box display="flex" alignItems="center" px={2} py={1.75}>
80
+ <Box display="flex" flex={1} alignItems="center" justifyContent="left">
81
+ <TokenRate route={route} />
82
+ </Box>
83
+ <FeeBreakdownTooltip
84
+ route={route}
85
+ gasCosts={gasCosts}
86
+ feeCosts={feeCosts}
87
+ >
88
+ <Box
89
+ display="flex"
90
+ alignItems="center"
91
+ onClick={toggleCard}
92
+ role="button"
93
+ sx={{ cursor: 'pointer' }}
94
+ px={1}
95
+ >
96
+ <IconTypography mr={0.5} fontSize={16}>
97
+ <LocalGasStationRounded fontSize="inherit" />
98
+ </IconTypography>
99
+ <Typography
100
+ fontSize={14}
101
+ color="text.primary"
102
+ fontWeight="600"
103
+ lineHeight={1.429}
104
+ >
105
+ {t(`format.currency`, { value: fees })}
106
+ </Typography>
107
+ </Box>
108
+ </FeeBreakdownTooltip>
109
+ <CardIconButton onClick={toggleCard} size="small">
110
+ {cardExpanded ? (
111
+ <ExpandLess fontSize="inherit" />
112
+ ) : (
113
+ <ExpandMore fontSize="inherit" />
114
+ )}
115
+ </CardIconButton>
116
+ </Box>
117
+ <Collapse timeout={225} in={cardExpanded} mountOnEnter>
118
+ <Box px={2} pb={2}>
119
+ <Box display="flex" justifyContent="space-between" mb={0.5}>
120
+ <Typography variant="body2">{t('main.fees.network')}</Typography>
121
+ <FeeBreakdownTooltip route={route} feeCosts={[]}>
122
+ <Typography variant="body2">
123
+ {t(`format.currency`, {
124
+ value: gasCostUSD,
125
+ })}
126
+ </Typography>
127
+ </FeeBreakdownTooltip>
128
+ </Box>
129
+ {feeCosts.length ? (
130
+ <Box display="flex" justifyContent="space-between" mb={0.5}>
131
+ <Typography variant="body2">{t('main.fees.provider')}</Typography>
132
+ <FeeBreakdownTooltip
133
+ route={route}
134
+ gasCosts={[]}
135
+ feeCosts={feeCosts}
136
+ >
137
+ <Typography variant="body2">
138
+ {t(`format.currency`, {
139
+ value: feeCostUSD,
140
+ })}
141
+ </Typography>
142
+ </FeeBreakdownTooltip>
143
+ </Box>
144
+ ) : null}
145
+ <Box display="flex" justifyContent="space-between" mb={0.5}>
146
+ <Typography variant="body2">{t('main.priceImpact')}</Typography>
147
+ <Tooltip title={t('tooltip.priceImpact')} sx={{ cursor: 'help' }}>
148
+ <Typography variant="body2">{priceImpact}%</Typography>
149
+ </Tooltip>
150
+ </Box>
151
+ {!isRouteDone(route) ? (
152
+ <>
153
+ <Box display="flex" justifyContent="space-between" mb={0.5}>
154
+ <Typography variant="body2">{t('main.maxSlippage')}</Typography>
155
+ <Typography variant="body2">
156
+ {route.steps[0].action.slippage * 100}%
157
+ </Typography>
158
+ </Box>
159
+ <Box display="flex" justifyContent="space-between">
160
+ <Typography variant="body2">{t('main.minReceived')}</Typography>
161
+ <Tooltip
162
+ title={t('tooltip.minReceived')}
163
+ sx={{ cursor: 'help' }}
164
+ >
165
+ <Typography variant="body2">
166
+ {t('format.number', {
167
+ value: formatTokenAmount(
168
+ BigInt(route.toAmountMin),
169
+ route.toToken.decimals,
170
+ ),
171
+ })}{' '}
172
+ {route.toToken.symbol}
173
+ </Typography>
174
+ </Tooltip>
175
+ </Box>
176
+ </>
177
+ ) : null}
178
+ </Box>
179
+ </Collapse>
180
+ </Card>
181
+ );
182
+ };
package/config/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget';
2
- export const version = '3.0.1';
2
+ export const version = '3.1.0';
@@ -0,0 +1,21 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ export function useInterval(callback: Function, delay: number) {
4
+ const callbacRef = useRef<Function>();
5
+
6
+ // update callback function with current render callback that has access to latest props and state
7
+ useEffect(() => {
8
+ callbacRef.current = callback;
9
+ });
10
+
11
+ useEffect(() => {
12
+ if (!delay) {
13
+ return () => {};
14
+ }
15
+
16
+ const interval = setInterval(() => {
17
+ callbacRef.current && callbacRef.current();
18
+ }, delay);
19
+ return () => clearInterval(interval);
20
+ }, [delay]);
21
+ }
@@ -0,0 +1,91 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { useInterval } from './useInterval.js';
3
+ import {
4
+ getDelayFromExpiryTimestamp,
5
+ getSecondsFromExpiry,
6
+ getTimeFromSeconds,
7
+ validateOnExpire,
8
+ } from './utils.js';
9
+
10
+ const DEFAULT_DELAY = 1000;
11
+
12
+ interface UseTimerProps {
13
+ expiryTimestamp: Date;
14
+ onExpire: Function;
15
+ autoStart?: boolean;
16
+ }
17
+
18
+ // This implementation was taken from the common js project - https://www.npmjs.com/package/react-timer-hook
19
+ // modified to work in the Widget codebase with Typescript
20
+ export function useTimer({
21
+ expiryTimestamp: expiry,
22
+ onExpire,
23
+ autoStart = true,
24
+ }: UseTimerProps) {
25
+ const [expiryTimestamp, setExpiryTimestamp] = useState(expiry);
26
+ const [seconds, setSeconds] = useState(getSecondsFromExpiry(expiryTimestamp));
27
+ const [isRunning, setIsRunning] = useState(autoStart);
28
+ const [didStart, setDidStart] = useState(autoStart);
29
+ const [delay, setDelay] = useState(
30
+ getDelayFromExpiryTimestamp(expiryTimestamp, DEFAULT_DELAY),
31
+ );
32
+
33
+ const handleExpire = useCallback(() => {
34
+ validateOnExpire(onExpire) && onExpire();
35
+ setIsRunning(false);
36
+ setDelay(0);
37
+ }, [onExpire]);
38
+
39
+ const pause = useCallback(() => {
40
+ setIsRunning(false);
41
+ }, []);
42
+
43
+ const restart = useCallback(
44
+ (newExpiryTimestamp: Date, newAutoStart = true) => {
45
+ setDelay(getDelayFromExpiryTimestamp(newExpiryTimestamp, DEFAULT_DELAY));
46
+ setDidStart(newAutoStart);
47
+ setIsRunning(newAutoStart);
48
+ setExpiryTimestamp(newExpiryTimestamp);
49
+ setSeconds(getSecondsFromExpiry(newExpiryTimestamp));
50
+ },
51
+ [],
52
+ );
53
+
54
+ const resume = useCallback(() => {
55
+ const time = new Date();
56
+ time.setMilliseconds(time.getMilliseconds() + seconds * 1000);
57
+ restart(time);
58
+ }, [seconds, restart]);
59
+
60
+ const start = useCallback(() => {
61
+ if (didStart) {
62
+ setSeconds(getSecondsFromExpiry(expiryTimestamp));
63
+ setIsRunning(true);
64
+ } else {
65
+ resume();
66
+ }
67
+ }, [expiryTimestamp, didStart, resume]);
68
+
69
+ useInterval(
70
+ () => {
71
+ if (delay !== DEFAULT_DELAY) {
72
+ setDelay(DEFAULT_DELAY);
73
+ }
74
+ const secondsValue = getSecondsFromExpiry(expiryTimestamp);
75
+ setSeconds(secondsValue);
76
+ if (secondsValue <= 0) {
77
+ handleExpire();
78
+ }
79
+ },
80
+ isRunning ? delay : 0,
81
+ );
82
+
83
+ return {
84
+ ...getTimeFromSeconds(seconds),
85
+ start,
86
+ pause,
87
+ resume,
88
+ restart,
89
+ isRunning,
90
+ };
91
+ }
@@ -0,0 +1,54 @@
1
+ export function getTimeFromSeconds(secs: number) {
2
+ const totalSeconds = Math.ceil(secs);
3
+ const days = Math.floor(totalSeconds / (60 * 60 * 24));
4
+ const hours = Math.floor((totalSeconds % (60 * 60 * 24)) / (60 * 60));
5
+ const minutes = Math.floor((totalSeconds % (60 * 60)) / 60);
6
+ const seconds = Math.floor(totalSeconds % 60);
7
+
8
+ return {
9
+ totalSeconds,
10
+ seconds,
11
+ minutes,
12
+ hours,
13
+ days,
14
+ };
15
+ }
16
+
17
+ export function getSecondsFromExpiry(expiry: Date, shouldRound?: boolean) {
18
+ const now = new Date().getTime();
19
+ const milliSecondsDistance = expiry.getTime() - now;
20
+ if (milliSecondsDistance > 0) {
21
+ const val = milliSecondsDistance / 1000;
22
+ return shouldRound ? Math.round(val) : val;
23
+ }
24
+ return 0;
25
+ }
26
+
27
+ export function validateExpiryTimestamp(expiryTimestamp: Date) {
28
+ const isValid = new Date(expiryTimestamp).getTime() > 0;
29
+ if (!isValid) {
30
+ console.warn('useTimer Invalid expiryTimestamp settings', expiryTimestamp); // eslint-disable-line
31
+ }
32
+ return isValid;
33
+ }
34
+
35
+ export function validateOnExpire(onExpire: Function) {
36
+ const isValid = onExpire && typeof onExpire === 'function';
37
+ if (onExpire && !isValid) {
38
+ console.warn('useTimer Invalid onExpire settings function', onExpire);
39
+ }
40
+ return isValid;
41
+ }
42
+
43
+ export function getDelayFromExpiryTimestamp(
44
+ expiryTimestamp: Date,
45
+ defaultDelay: number,
46
+ ) {
47
+ if (!validateExpiryTimestamp(expiryTimestamp)) {
48
+ return 0;
49
+ }
50
+
51
+ const seconds = getSecondsFromExpiry(expiryTimestamp);
52
+ const extraMilliSeconds = Math.floor((seconds - Math.floor(seconds)) * 1000);
53
+ return extraMilliSeconds > 0 ? extraMilliSeconds : defaultDelay;
54
+ }