@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.
- package/CHANGELOG.md +12 -0
- package/_esm/components/ActiveTransactions/ActiveTransactionItem.js +1 -1
- package/_esm/components/Card/CardIconButton.d.ts +4 -1
- package/_esm/components/Card/CardIconButton.js.map +1 -1
- package/_esm/components/FeeBreakdownTooltip.d.ts +0 -2
- package/_esm/components/FeeBreakdownTooltip.js +2 -5
- package/_esm/components/FeeBreakdownTooltip.js.map +1 -1
- package/_esm/components/IconTypography.d.ts +3 -3
- package/_esm/components/IconTypography.js +2 -2
- package/_esm/components/IconTypography.js.map +1 -1
- package/_esm/components/RouteCard/RouteCardEssentials.js +10 -11
- package/_esm/components/RouteCard/RouteCardEssentials.js.map +1 -1
- package/_esm/components/Step/DestinationWalletAddress.js +2 -2
- package/_esm/components/Step/DestinationWalletAddress.js.map +1 -1
- package/_esm/components/Step/Step.js +1 -1
- package/_esm/components/Step/StepProcess.js +3 -3
- package/_esm/components/Step/StepProcess.js.map +1 -1
- package/_esm/components/Step/StepTimer.js +44 -27
- package/_esm/components/Step/StepTimer.js.map +1 -1
- package/_esm/components/StepActions/StepActions.style.d.ts +0 -3
- package/_esm/components/StepActions/StepActions.style.js +0 -6
- package/_esm/components/StepActions/StepActions.style.js.map +1 -1
- package/_esm/components/TransactionDetails.js +4 -8
- package/_esm/components/TransactionDetails.js.map +1 -1
- package/_esm/config/version.d.ts +1 -1
- package/_esm/config/version.js +1 -1
- package/_esm/hooks/timer/useTimer.js +2 -2
- package/_esm/hooks/timer/useTimer.js.map +1 -1
- package/_esm/i18n/en.json +1 -2
- package/_esm/i18n/index.js +15 -15
- package/_esm/i18n/index.js.map +1 -1
- package/_esm/pages/TransactionDetailsPage/TransactionDetailsPage.js +3 -2
- package/_esm/pages/TransactionDetailsPage/TransactionDetailsPage.js.map +1 -1
- package/_esm/pages/TransactionPage/TokenValueBottomSheet.d.ts +0 -1
- package/_esm/pages/TransactionPage/TokenValueBottomSheet.js +7 -14
- package/_esm/pages/TransactionPage/TokenValueBottomSheet.js.map +1 -1
- package/_esm/pages/TransactionPage/TransactionPage.js +16 -8
- package/_esm/pages/TransactionPage/TransactionPage.js.map +1 -1
- package/_esm/pages/TransactionPage/utils.d.ts +2 -2
- package/_esm/pages/TransactionPage/utils.js +9 -5
- package/_esm/pages/TransactionPage/utils.js.map +1 -1
- package/_esm/stores/routes/types.d.ts +6 -6
- package/_esm/stores/routes/types.js +6 -6
- package/_esm/stores/routes/types.js.map +1 -1
- package/_esm/types/events.d.ts +5 -4
- package/_esm/utils/converters.js +1 -1
- package/_esm/utils/converters.js.map +1 -1
- package/_esm/utils/fees.d.ts +7 -0
- package/_esm/utils/fees.js +14 -0
- package/_esm/utils/fees.js.map +1 -1
- package/components/ActiveTransactions/ActiveTransactionItem.tsx +1 -1
- package/components/Card/CardIconButton.tsx +4 -1
- package/components/FeeBreakdownTooltip.tsx +4 -10
- package/components/IconTypography.ts +2 -2
- package/components/RouteCard/RouteCardEssentials.tsx +19 -15
- package/components/Step/DestinationWalletAddress.tsx +3 -3
- package/components/Step/Step.tsx +1 -1
- package/components/Step/StepProcess.tsx +6 -6
- package/components/Step/StepTimer.tsx +77 -32
- package/components/StepActions/StepActions.style.tsx +0 -8
- package/components/TransactionDetails.tsx +8 -25
- package/config/version.ts +1 -1
- package/hooks/timer/useTimer.ts +4 -2
- package/i18n/en.json +1 -2
- package/i18n/index.ts +15 -15
- package/package.json +10 -10
- package/pages/TransactionDetailsPage/TransactionDetailsPage.tsx +6 -5
- package/pages/TransactionPage/TokenValueBottomSheet.tsx +32 -19
- package/pages/TransactionPage/TransactionPage.tsx +27 -10
- package/pages/TransactionPage/utils.ts +23 -8
- package/stores/routes/types.ts +6 -6
- package/types/events.ts +5 -4
- package/utils/converters.ts +1 -1
- package/utils/fees.ts +24 -0
- package/_esm/components/Step/StepProcess.style.d.ts +0 -9
- package/_esm/components/Step/StepProcess.style.js +0 -6
- package/_esm/components/Step/StepProcess.style.js.map +0 -1
- package/components/Step/StepProcess.style.tsx +0 -9
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { LiFiStep, Process } from '@lifi/sdk';
|
|
2
|
-
import {
|
|
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
|
-
<
|
|
32
|
-
size="
|
|
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
|
-
<
|
|
39
|
-
</
|
|
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
|
|
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(
|
|
19
|
-
|
|
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
|
-
|
|
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 (
|
|
57
|
+
if (isExecutionStarted && isExpired) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (!isExecutionStarted && shouldStart) {
|
|
61
|
+
const expiryTimestamp = getExpiryTimestamp(step);
|
|
31
62
|
setExecutionStarted(true);
|
|
32
|
-
|
|
63
|
+
setExpired(false);
|
|
64
|
+
setExpiryTimestamp(expiryTimestamp);
|
|
65
|
+
restart(expiryTimestamp);
|
|
66
|
+
return;
|
|
33
67
|
}
|
|
34
|
-
|
|
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 &&
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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 {
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
+
export const version = '3.3.0';
|
package/hooks/timer/useTimer.ts
CHANGED
|
@@ -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(
|
|
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
|
|
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'
|
|
2
|
-
import de from './de.json'
|
|
3
|
-
import en from './en.json'
|
|
4
|
-
import es from './es.json'
|
|
5
|
-
import fr from './fr.json'
|
|
6
|
-
import id from './id.json'
|
|
7
|
-
import it from './it.json'
|
|
8
|
-
import ja from './ja.json'
|
|
9
|
-
import ko from './ko.json'
|
|
10
|
-
import pt from './pt.json'
|
|
11
|
-
import th from './th.json'
|
|
12
|
-
import tr from './tr.json'
|
|
13
|
-
import uk from './uk.json'
|
|
14
|
-
import vi from './vi.json'
|
|
15
|
-
import zh from './zh.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.
|
|
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.
|
|
39
|
-
"@mui/icons-material": "^5.16.
|
|
40
|
-
"@mui/lab": "^5.0.0-alpha.
|
|
41
|
-
"@mui/material": "^5.16.
|
|
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.
|
|
45
|
-
"@tanstack/react-query": "^5.51.
|
|
46
|
-
"@tanstack/react-virtual": "^3.8.
|
|
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.
|
|
57
|
-
"wagmi": "^2.12.
|
|
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,
|
|
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={
|
|
128
|
-
<
|
|
129
|
-
<ContentCopyRounded fontSize="
|
|
130
|
-
</
|
|
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 {
|
|
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.
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
|
|
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}>
|
|
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
|
-
|
|
29
|
+
calculateValueLossPercentage,
|
|
28
30
|
getTokenValueLossThreshold,
|
|
29
|
-
} from './
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
{
|
|
217
|
+
{subvariant !== 'custom' ? (
|
|
201
218
|
<TokenValueBottomSheet
|
|
202
219
|
route={route}
|
|
203
220
|
ref={tokenValueBottomSheetRef}
|
|
@@ -1,10 +1,25 @@
|
|
|
1
|
-
|
|
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
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
};
|
package/stores/routes/types.ts
CHANGED
|
@@ -15,10 +15,10 @@ export interface RouteExecutionState {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export enum RouteExecutionStatus {
|
|
18
|
-
Idle = 0,
|
|
19
|
-
Pending = 1 <<
|
|
20
|
-
Done = 1 <<
|
|
21
|
-
Failed = 1 <<
|
|
22
|
-
Partial = 1 <<
|
|
23
|
-
Refunded = 1 <<
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
fromAmountUSD: number;
|
|
41
|
+
toAmountUSD: number;
|
|
42
|
+
gasCostUSD?: number;
|
|
43
|
+
feeCostUSD?: number;
|
|
44
|
+
valueLoss: number;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
export interface RouteExecutionUpdate {
|
package/utils/converters.ts
CHANGED