@lifi/widget 1.12.1 → 1.13.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/components/Initializer.js +1 -1
- package/components/PoweredBy/PoweredBy.js +3 -2
- package/components/Step/CircularProgress.d.ts +0 -1
- package/components/SwapButton/ButtonTooltip.d.ts +0 -1
- package/components/SwapInput/FormPriceHelperText.js +2 -2
- package/components/SwapInput/SwapInputAdornment.d.ts +0 -1
- package/components/SwapInput/SwapInputAdornment.js +2 -2
- package/components/SwapRouteCard/SwapRouteCard.style.js +1 -1
- package/components/TokenAvatar/TokenAvatar.js +3 -2
- package/components/TokenList/TokenList.js +25 -43
- package/components/TokenList/TokenList.style.js +5 -2
- package/components/TokenList/TokenListItem.d.ts +2 -2
- package/components/TokenList/TokenListItem.js +7 -10
- package/components/TokenList/TokenNotFound.d.ts +2 -0
- package/components/TokenList/TokenNotFound.js +15 -0
- package/components/TokenList/VirtualizedTokenList.d.ts +3 -0
- package/components/TokenList/VirtualizedTokenList.js +53 -0
- package/components/TokenList/types.d.ts +16 -1
- package/config/sentry.d.ts +1 -1
- package/config/sentry.js +33 -18
- package/config/version.d.ts +1 -1
- package/config/version.js +1 -1
- package/hooks/index.d.ts +3 -0
- package/hooks/index.js +3 -0
- package/hooks/useFeaturedTokens.d.ts +1 -0
- package/hooks/useFeaturedTokens.js +6 -0
- package/hooks/useToken.d.ts +2 -1
- package/hooks/useToken.js +2 -1
- package/hooks/useTokenBalance.d.ts +2 -4
- package/hooks/useTokenBalance.js +11 -42
- package/hooks/useTokenBalances.d.ts +5 -4
- package/hooks/useTokenBalances.js +25 -13
- package/hooks/useTokenSearch.d.ts +7 -0
- package/hooks/useTokenSearch.js +37 -0
- package/hooks/useTokens.d.ts +2 -1
- package/hooks/useTokens.js +12 -4
- package/i18n/en/translation.json +2 -0
- package/i18n/index.d.ts +2 -0
- package/package.json +8 -9
- package/pages/SelectTokenPage/ChainSelect.d.ts +0 -1
- package/pages/SelectTokenPage/ChainSelect.js +8 -5
- package/pages/SelectTokenPage/SearchTokenInput.d.ts +0 -1
- package/pages/SelectTokenPage/SearchTokenInput.js +2 -2
- package/pages/SelectWalletPage/SelectWalletPage.d.ts +0 -1
- package/pages/SettingsPage/AdvancedPreferences.d.ts +0 -1
- package/pages/SettingsPage/GasPriceSelect.d.ts +0 -1
- package/pages/SettingsPage/SettingsPage.d.ts +0 -1
- package/pages/SettingsPage/SlippageInput.d.ts +0 -1
- package/pages/SwapPage/StatusBottomSheet.js +2 -2
- package/providers/SwapFormProvider/SwapFormProvider.d.ts +1 -1
- package/providers/SwapFormProvider/SwapFormProvider.js +22 -5
- package/providers/SwapFormProvider/types.d.ts +2 -3
- package/providers/SwapFormProvider/types.js +1 -2
- package/providers/WidgetProvider/WidgetProvider.js +24 -15
- package/types/index.d.ts +1 -0
- package/types/index.js +1 -0
- package/types/token.d.ts +4 -0
- package/types/token.js +1 -0
- package/types/widget.d.ts +3 -2
- package/components/TokenList/utils.d.ts +0 -15
- package/components/TokenList/utils.js +0 -10
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Typography } from '@mui/material';
|
|
2
|
+
import { Box, Tooltip, Typography } from '@mui/material';
|
|
3
|
+
import { version } from '../../config/version';
|
|
3
4
|
import { LiFiLogo } from '../LiFiLogo';
|
|
4
5
|
import { Link } from './PoweredBy.style';
|
|
5
6
|
export const PoweredBy = () => {
|
|
@@ -7,5 +8,5 @@ export const PoweredBy = () => {
|
|
|
7
8
|
display: 'flex',
|
|
8
9
|
alignItems: 'flex-end',
|
|
9
10
|
justifyContent: 'flex-end',
|
|
10
|
-
} }, { children: _jsxs(Link, Object.assign({ href: "https://li.fi", target: "_blank", underline: "none", color: "text.primary" }, { children: [_jsx(Typography, Object.assign({ color: "text.secondary", fontSize: 12, px: 0.5 }, { children: "Powered by" })), _jsx(LiFiLogo, { variant: "full", style: { height: 16, width: 42 } })] })) })));
|
|
11
|
+
} }, { children: _jsx(Tooltip, Object.assign({ title: `v${version}`, placement: "top", enterDelay: 5000, arrow: true }, { children: _jsxs(Link, Object.assign({ href: "https://li.fi", target: "_blank", underline: "none", color: "text.primary" }, { children: [_jsx(Typography, Object.assign({ color: "text.secondary", fontSize: 12, px: 0.5 }, { children: "Powered by" })), _jsx(LiFiLogo, { variant: "full", style: { height: 16, width: 42 } })] })) })) })));
|
|
11
12
|
};
|
|
@@ -14,14 +14,14 @@ export const FormPriceHelperText = ({ formType, selected }) => {
|
|
|
14
14
|
SwapFormKeyHelper.getTokenKey(formType),
|
|
15
15
|
],
|
|
16
16
|
});
|
|
17
|
-
const { token, isLoading
|
|
17
|
+
const { token, isLoading } = useTokenBalance(chainId, tokenAddress);
|
|
18
18
|
const fromAmountTokenPrice = formatTokenPrice(amount, token === null || token === void 0 ? void 0 : token.priceUSD);
|
|
19
19
|
return (_jsxs(FormHelperText, Object.assign({ component: "div", sx: { display: 'flex', justifyContent: 'space-between', margin: 0 } }, { children: [_jsx(Typography, Object.assign({ color: fromAmountTokenPrice ? 'text.secondary' : 'grey.600', fontWeight: 400, fontSize: 12, marginLeft: selected ? 8 : 2, lineHeight: 1.3334, flex: 1, sx: {
|
|
20
20
|
wordBreak: 'break-word',
|
|
21
21
|
overflowWrap: 'break-word',
|
|
22
22
|
} }, { children: t(`swap.currency`, {
|
|
23
23
|
value: fromAmountTokenPrice,
|
|
24
|
-
}) })), isLoading &&
|
|
24
|
+
}) })), isLoading && tokenAddress ? (_jsx(Skeleton, { variant: "text", width: 48, height: 16, sx: { borderRadius: 0.25 } })) : (token === null || token === void 0 ? void 0 : token.amount) ? (_jsx(Typography, Object.assign({ fontWeight: 400, fontSize: 12, color: "text.secondary", lineHeight: 1.3334, pl: 0.25 }, { children: t(`swap.maxAmount`, {
|
|
25
25
|
amount: token === null || token === void 0 ? void 0 : token.amount,
|
|
26
26
|
}) }))) : null] })));
|
|
27
27
|
};
|
|
@@ -14,10 +14,10 @@ export const SwapInputAdornment = ({ formType }) => {
|
|
|
14
14
|
SwapFormKeyHelper.getTokenKey(formType),
|
|
15
15
|
],
|
|
16
16
|
});
|
|
17
|
-
const { token, isLoading
|
|
17
|
+
const { token, isLoading } = useTokenBalance(chainId, tokenAddress);
|
|
18
18
|
const handleMax = () => {
|
|
19
19
|
var _a;
|
|
20
20
|
setValue(SwapFormKeyHelper.getAmountKey(formType), (_a = token === null || token === void 0 ? void 0 : token.amount) !== null && _a !== void 0 ? _a : '');
|
|
21
21
|
};
|
|
22
|
-
return (_jsx(InputAdornment, Object.assign({ position: "end" }, { children: isLoading &&
|
|
22
|
+
return (_jsx(InputAdornment, Object.assign({ position: "end" }, { children: isLoading && tokenAddress ? (_jsx(Skeleton, { variant: "rectangular", width: 46, height: 20, sx: { borderRadius: 0.5 } })) : formType === 'from' && (token === null || token === void 0 ? void 0 : token.amount) ? (_jsx(Button, Object.assign({ onClick: handleMax, variant: "outlined" }, { children: t('button.max') }))) : null })));
|
|
23
23
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { getContrastTextColor } from '@lifi/widget/utils';
|
|
2
1
|
import { Typography } from '@mui/material';
|
|
3
2
|
import { styled } from '@mui/material/styles';
|
|
3
|
+
import { getContrastTextColor } from '../../utils';
|
|
4
4
|
export const Label = styled(Typography, {
|
|
5
5
|
shouldForwardProp: (prop) => prop !== 'active',
|
|
6
6
|
})(({ theme, active }) => ({
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Avatar, Badge } from '@mui/material';
|
|
3
|
-
import { useChain } from '../../hooks';
|
|
3
|
+
import { useChain, useToken } from '../../hooks';
|
|
4
4
|
import { SmallAvatar } from '../SmallAvatar';
|
|
5
5
|
export const TokenAvatar = ({ token, sx }) => {
|
|
6
6
|
const { chain } = useChain(token.chainId);
|
|
7
|
-
|
|
7
|
+
const { token: chainToken } = useToken(token.chainId, token.address);
|
|
8
|
+
return (_jsx(Badge, Object.assign({ overlap: "circular", anchorOrigin: { vertical: 'bottom', horizontal: 'right' }, badgeContent: chain ? (_jsx(SmallAvatar, Object.assign({ src: chain.logoURI, alt: chain.name }, { children: chain.name[0] }))) : null, sx: sx }, { children: _jsx(Avatar, Object.assign({ src: token.logoURI || (chainToken === null || chainToken === void 0 ? void 0 : chainToken.logoURI), alt: token.symbol }, { children: token.symbol[0] })) })));
|
|
8
9
|
};
|
|
@@ -1,49 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box
|
|
3
|
-
import { useCallback,
|
|
2
|
+
import { Box } from '@mui/material';
|
|
3
|
+
import { useCallback, useRef } from 'react';
|
|
4
4
|
import { useFormContext, useWatch } from 'react-hook-form';
|
|
5
|
-
import {
|
|
6
|
-
import { useVirtual } from 'react-virtual';
|
|
7
|
-
import { useDebouncedWatch, useTokenBalances } from '../../hooks';
|
|
5
|
+
import { useDebouncedWatch, useTokenBalances, useTokenSearch, } from '../../hooks';
|
|
8
6
|
import { SwapFormKey, SwapFormKeyHelper, } from '../../providers/SwapFormProvider';
|
|
9
7
|
import { useWallet } from '../../providers/WalletProvider';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { TokenNotFound } from './TokenNotFound';
|
|
9
|
+
import { VirtualizedTokenList } from './VirtualizedTokenList';
|
|
12
10
|
export const TokenList = ({ formType, height, onClick, }) => {
|
|
13
|
-
|
|
11
|
+
var _a, _b;
|
|
12
|
+
const parentRef = useRef(null);
|
|
14
13
|
const { account } = useWallet();
|
|
15
14
|
const { setValue, getValues } = useFormContext();
|
|
16
15
|
const [selectedChainId] = useWatch({
|
|
17
16
|
name: [SwapFormKeyHelper.getChainKey(formType)],
|
|
18
17
|
});
|
|
19
|
-
const [
|
|
20
|
-
const { tokens:
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const { virtualItems, totalSize, scrollToIndex } = useVirtual({
|
|
37
|
-
size: chainTokens.length,
|
|
38
|
-
parentRef,
|
|
39
|
-
overscan: 3,
|
|
40
|
-
paddingEnd: 12,
|
|
41
|
-
estimateSize: useCallback(() => 64, []),
|
|
42
|
-
keyExtractor: (index) => { var _a; return (_a = chainTokens[index].address) !== null && _a !== void 0 ? _a : index; },
|
|
43
|
-
});
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
scrollToIndex(0);
|
|
46
|
-
}, [scrollToIndex, selectedChainId]);
|
|
18
|
+
const [tokenSearchFilter] = useDebouncedWatch([SwapFormKey.TokenSearchFilter], 250);
|
|
19
|
+
const { tokens: chainTokens, tokensWithBalance, isLoading: isTokensLoading, isBalanceLoading, featuredTokens, } = useTokenBalances(selectedChainId);
|
|
20
|
+
let filteredTokens = ((_a = tokensWithBalance !== null && tokensWithBalance !== void 0 ? tokensWithBalance : chainTokens) !== null && _a !== void 0 ? _a : []);
|
|
21
|
+
const searchFilter = (_b = tokenSearchFilter === null || tokenSearchFilter === void 0 ? void 0 : tokenSearchFilter.toUpperCase()) !== null && _b !== void 0 ? _b : '';
|
|
22
|
+
filteredTokens = tokenSearchFilter
|
|
23
|
+
? filteredTokens.filter((token) => token.name.toUpperCase().includes(searchFilter) ||
|
|
24
|
+
token.symbol.toUpperCase().includes(searchFilter) ||
|
|
25
|
+
token.address.toUpperCase().includes(searchFilter))
|
|
26
|
+
: filteredTokens;
|
|
27
|
+
const tokenSearchEnabled = !filteredTokens.length && !isTokensLoading;
|
|
28
|
+
const { token: searchedToken, isLoading: isSearchedTokenLoading } = useTokenSearch(tokenSearchFilter, selectedChainId, tokenSearchEnabled);
|
|
29
|
+
const isLoading = isTokensLoading || (tokenSearchEnabled && isSearchedTokenLoading);
|
|
30
|
+
const tokens = filteredTokens.length
|
|
31
|
+
? filteredTokens
|
|
32
|
+
: searchedToken
|
|
33
|
+
? [searchedToken]
|
|
34
|
+
: filteredTokens;
|
|
47
35
|
const handleTokenClick = useCallback((tokenAddress) => {
|
|
48
36
|
setValue(SwapFormKeyHelper.getTokenKey(formType), tokenAddress);
|
|
49
37
|
setValue(SwapFormKeyHelper.getAmountKey(formType), '');
|
|
@@ -59,11 +47,5 @@ export const TokenList = ({ formType, height, onClick, }) => {
|
|
|
59
47
|
}
|
|
60
48
|
onClick === null || onClick === void 0 ? void 0 : onClick();
|
|
61
49
|
}, [formType, getValues, onClick, setValue]);
|
|
62
|
-
return (_jsxs(Box, Object.assign({ ref: parentRef, style: { height, overflow: 'auto' } }, { children: [!
|
|
63
|
-
const token = chainTokens[item.index];
|
|
64
|
-
if (token.name.includes(skeletonKey)) {
|
|
65
|
-
return (_jsx(TokenListItemSkeleton, { size: item.size, start: item.start }, item.key));
|
|
66
|
-
}
|
|
67
|
-
return (_jsx(TokenListItem, { onClick: handleTokenClick, size: item.size, start: item.start, token: token, isBalanceLoading: isBalanceLoading, showBalance: account.isActive }, item.key));
|
|
68
|
-
}) }))] })));
|
|
50
|
+
return (_jsxs(Box, Object.assign({ ref: parentRef, style: { height, overflow: 'auto' } }, { children: [!tokens.length && !isLoading ? _jsx(TokenNotFound, {}) : null, _jsx(VirtualizedTokenList, { tokens: tokens, featuredTokensLength: featuredTokens === null || featuredTokens === void 0 ? void 0 : featuredTokens.length, scrollElementRef: parentRef, chainId: selectedChainId, isLoading: isLoading, isBalanceLoading: isBalanceLoading, showBalance: account.isActive, showFeatured: !tokenSearchFilter, onClick: handleTokenClick })] })));
|
|
69
51
|
};
|
|
@@ -6,6 +6,7 @@ export const ListItemButton = styled(MuiListItemButton)(({ theme }) => ({
|
|
|
6
6
|
borderRadius: theme.shape.borderRadiusSecondary,
|
|
7
7
|
paddingLeft: theme.spacing(2),
|
|
8
8
|
height: 64,
|
|
9
|
+
width: '100%',
|
|
9
10
|
'&:hover': {
|
|
10
11
|
backgroundColor: getContrastAlphaColor(theme, '4%'),
|
|
11
12
|
},
|
|
@@ -14,8 +15,10 @@ export const ListItem = styled(MuiListItem)(({ theme }) => ({
|
|
|
14
15
|
position: 'absolute',
|
|
15
16
|
top: 0,
|
|
16
17
|
left: 0,
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
height: 64,
|
|
19
|
+
flexDirection: 'column',
|
|
20
|
+
alignItems: 'flex-start',
|
|
21
|
+
padding: theme.spacing(0, 3),
|
|
19
22
|
[`.${listItemSecondaryActionClasses.root}`]: {
|
|
20
23
|
right: theme.spacing(5),
|
|
21
24
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import {
|
|
2
|
+
import { TokenListItemProps } from './types';
|
|
3
3
|
export declare const TokenListItem: React.FC<TokenListItemProps>;
|
|
4
|
-
export declare const TokenListItemSkeleton:
|
|
4
|
+
export declare const TokenListItemSkeleton: () => JSX.Element;
|
|
5
5
|
export declare const TokenAmountSkeleton: React.FC;
|
|
@@ -4,22 +4,19 @@ import { memo } from 'react';
|
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
import { formatTokenPrice } from '../../utils';
|
|
6
6
|
import { ListItem, ListItemButton } from './TokenList.style';
|
|
7
|
-
export const TokenListItem = memo(({ onClick, size, start, token, showBalance, isBalanceLoading }) => {
|
|
7
|
+
export const TokenListItem = memo(({ onClick, size, start, token, showBalance, isBalanceLoading, startAdornment, endAdornment, }) => {
|
|
8
8
|
const { t } = useTranslation();
|
|
9
9
|
const handleClick = () => onClick === null || onClick === void 0 ? void 0 : onClick(token.address);
|
|
10
10
|
const tokenPrice = formatTokenPrice(token.amount, token.priceUSD);
|
|
11
|
-
return (
|
|
11
|
+
return (_jsxs(ListItem, Object.assign({ disablePadding: true, style: {
|
|
12
12
|
height: `${size}px`,
|
|
13
13
|
transform: `translateY(${start}px)`,
|
|
14
|
-
} }, { children: _jsxs(ListItemButton, Object.assign({ onClick: handleClick, dense: true, disableRipple: true }, { children: [_jsx(ListItemAvatar, { children: _jsx(Avatar, Object.assign({ src: token.logoURI, alt: token.symbol }, { children: token.symbol[0] })) }), _jsx(ListItemText, { primary: token.symbol, secondary: token.name }), showBalance ? (isBalanceLoading ? (_jsx(TokenAmountSkeleton, {})) : (_jsxs(Box, Object.assign({ sx: { textAlign: 'right' } }, { children: [Number(token.amount) ? (_jsx(Typography, Object.assign({ variant: "body1", noWrap: true }, { children: token.amount }))) : null, tokenPrice ? (_jsx(Typography, Object.assign({ fontWeight: 400, fontSize: 12, color: "text.secondary", "data-price": token.priceUSD }, { children: t(`swap.currency`, {
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
} }, { children: [startAdornment, _jsxs(ListItemButton, Object.assign({ onClick: handleClick, dense: true, disableRipple: true }, { children: [_jsx(ListItemAvatar, { children: _jsx(Avatar, Object.assign({ src: token.logoURI, alt: token.symbol }, { children: token.symbol[0] })) }), _jsx(ListItemText, { primary: token.symbol, secondary: token.name }), showBalance ? (isBalanceLoading ? (_jsx(TokenAmountSkeleton, {})) : (_jsxs(Box, Object.assign({ sx: { textAlign: 'right' } }, { children: [Number(token.amount) ? (_jsx(Typography, Object.assign({ variant: "body1", noWrap: true }, { children: token.amount }))) : null, tokenPrice ? (_jsx(Typography, Object.assign({ fontWeight: 400, fontSize: 12, color: "text.secondary", "data-price": token.priceUSD }, { children: t(`swap.currency`, {
|
|
15
|
+
value: tokenPrice,
|
|
16
|
+
}) }))) : null] })))) : null] })), endAdornment] })));
|
|
17
17
|
});
|
|
18
|
-
export const TokenListItemSkeleton = (
|
|
19
|
-
return (_jsxs(ListItem, Object.assign({ secondaryAction: _jsx(TokenAmountSkeleton, {}), disablePadding: true,
|
|
20
|
-
height: `${size}px`,
|
|
21
|
-
transform: `translateY(${start}px)`,
|
|
22
|
-
} }, { children: [_jsx(ListItemAvatar, { children: _jsx(Skeleton, { variant: "circular", width: 32, height: 32, sx: { marginLeft: 2, marginRight: 2 } }) }), _jsx(ListItemText, { primary: _jsx(Skeleton, { variant: "text", width: 48, height: 20 }), secondary: _jsx(Skeleton, { variant: "text", width: 96, height: 20 }) })] })));
|
|
18
|
+
export const TokenListItemSkeleton = () => {
|
|
19
|
+
return (_jsxs(ListItem, Object.assign({ secondaryAction: _jsx(TokenAmountSkeleton, {}), disablePadding: true, sx: { position: 'relative', flexDirection: 'row', alignItems: 'center' } }, { children: [_jsx(ListItemAvatar, { children: _jsx(Skeleton, { variant: "circular", width: 32, height: 32, sx: { marginLeft: 2, marginRight: 2 } }) }), _jsx(ListItemText, { primary: _jsx(Skeleton, { variant: "text", width: 48, height: 20 }), secondary: _jsx(Skeleton, { variant: "text", width: 96, height: 20 }) })] })));
|
|
23
20
|
};
|
|
24
21
|
export const TokenAmountSkeleton = () => {
|
|
25
22
|
return (_jsxs(Box, Object.assign({ sx: {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SearchOff as SearchOffIcon } from '@mui/icons-material';
|
|
3
|
+
import { Box, Typography } from '@mui/material';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
export const TokenNotFound = () => {
|
|
6
|
+
const { t } = useTranslation();
|
|
7
|
+
return (_jsxs(Box, Object.assign({ sx: {
|
|
8
|
+
display: 'flex',
|
|
9
|
+
justifyContent: 'center',
|
|
10
|
+
alignItems: 'center',
|
|
11
|
+
flexDirection: 'column',
|
|
12
|
+
flex: 1,
|
|
13
|
+
padding: 3,
|
|
14
|
+
} }, { children: [_jsx(Typography, Object.assign({ fontSize: 48, lineHeight: 1 }, { children: _jsx(SearchOffIcon, { fontSize: "inherit" }) })), _jsx(Typography, Object.assign({ fontSize: 14, color: "text.secondary", textAlign: "center", mt: 2, px: 2 }, { children: t('swap.couldntFindTokens') }))] })));
|
|
15
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { List, Typography } from '@mui/material';
|
|
3
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { TokenListItem, TokenListItemSkeleton } from './TokenListItem';
|
|
7
|
+
export const VirtualizedTokenList = ({ tokens, featuredTokensLength, scrollElementRef, chainId, isLoading, isBalanceLoading, showBalance, showFeatured, onClick, }) => {
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
const hasFeaturedTokens = !!featuredTokensLength && showFeatured;
|
|
10
|
+
const featuredTokensLastIndex = (featuredTokensLength !== null && featuredTokensLength !== void 0 ? featuredTokensLength : 0) - 1;
|
|
11
|
+
const tokensLastIndex = tokens.length - 1;
|
|
12
|
+
const { getVirtualItems, getTotalSize, scrollToIndex } = useVirtualizer({
|
|
13
|
+
count: tokens.length,
|
|
14
|
+
getScrollElement: () => scrollElementRef.current,
|
|
15
|
+
overscan: 5,
|
|
16
|
+
paddingEnd: 12,
|
|
17
|
+
estimateSize: (index) => {
|
|
18
|
+
var _a, _b;
|
|
19
|
+
// heigth of TokenListItem
|
|
20
|
+
let size = 64;
|
|
21
|
+
if (!hasFeaturedTokens) {
|
|
22
|
+
return size;
|
|
23
|
+
}
|
|
24
|
+
if (index === 0 && ((_a = tokens[index]) === null || _a === void 0 ? void 0 : _a.featured)) {
|
|
25
|
+
// height of startAdornment
|
|
26
|
+
size += 24;
|
|
27
|
+
}
|
|
28
|
+
if (index === featuredTokensLastIndex &&
|
|
29
|
+
index !== tokensLastIndex &&
|
|
30
|
+
((_b = tokens[index]) === null || _b === void 0 ? void 0 : _b.featured)) {
|
|
31
|
+
// height of endAdornment
|
|
32
|
+
size += 32;
|
|
33
|
+
}
|
|
34
|
+
return size;
|
|
35
|
+
},
|
|
36
|
+
getItemKey: (index) => { var _a; return (_a = tokens[index].address) !== null && _a !== void 0 ? _a : index; },
|
|
37
|
+
});
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
scrollToIndex(0, { align: 'start', smoothScroll: false });
|
|
40
|
+
}, [scrollToIndex, chainId]);
|
|
41
|
+
if (isLoading) {
|
|
42
|
+
return (_jsx(List, Object.assign({ disablePadding: true }, { children: Array.from({ length: 3 }).map((_, index) => (
|
|
43
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
44
|
+
_jsx(TokenListItemSkeleton, {}, index))) })));
|
|
45
|
+
}
|
|
46
|
+
return (_jsx(List, Object.assign({ style: { height: getTotalSize() }, disablePadding: true }, { children: getVirtualItems().map((item) => {
|
|
47
|
+
const token = tokens[item.index];
|
|
48
|
+
return (_jsx(TokenListItem, { onClick: onClick, size: item.size, start: item.start, token: token, isBalanceLoading: isBalanceLoading, showBalance: showBalance, startAdornment: hasFeaturedTokens && token.featured && item.index === 0 ? (_jsx(Typography, Object.assign({ fontSize: 14, fontWeight: 600, lineHeight: 1, px: 2, pb: 1.25 }, { children: t('swap.featuredTokens') }))) : null, endAdornment: hasFeaturedTokens &&
|
|
49
|
+
token.featured &&
|
|
50
|
+
item.index === featuredTokensLastIndex &&
|
|
51
|
+
item.index !== tokensLastIndex ? (_jsx(Typography, Object.assign({ fontSize: 14, fontWeight: 600, lineHeight: 1, px: 2, py: 1.25 }, { children: t('swap.otherTokens') }))) : null }, item.key));
|
|
52
|
+
}) })));
|
|
53
|
+
};
|
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
import { TokenAmount } from '@lifi/sdk';
|
|
2
|
+
import { MutableRefObject } from 'react';
|
|
2
3
|
import { SwapFormDirection } from '../../providers/SwapFormProvider';
|
|
4
|
+
import { Token } from '../../types';
|
|
3
5
|
export interface TokenListProps {
|
|
4
6
|
formType: SwapFormDirection;
|
|
5
7
|
height: number;
|
|
6
8
|
onClick?(): void;
|
|
7
9
|
}
|
|
10
|
+
export interface VirtualizedTokenListProps {
|
|
11
|
+
tokens: Token[];
|
|
12
|
+
featuredTokensLength?: number;
|
|
13
|
+
scrollElementRef: MutableRefObject<HTMLElement | null>;
|
|
14
|
+
isLoading: boolean;
|
|
15
|
+
isBalanceLoading: boolean;
|
|
16
|
+
chainId: number;
|
|
17
|
+
showBalance?: boolean;
|
|
18
|
+
showFeatured?: boolean;
|
|
19
|
+
onClick(tokenAddress: string): void;
|
|
20
|
+
}
|
|
8
21
|
export interface TokenListItemBaseProps {
|
|
9
|
-
onClick?(
|
|
22
|
+
onClick?(tokenAddress: string): void;
|
|
10
23
|
size: number;
|
|
11
24
|
start: number;
|
|
12
25
|
}
|
|
@@ -14,4 +27,6 @@ export interface TokenListItemProps extends TokenListItemBaseProps {
|
|
|
14
27
|
showBalance?: boolean;
|
|
15
28
|
token: TokenAmount;
|
|
16
29
|
isBalanceLoading?: boolean;
|
|
30
|
+
startAdornment?: React.ReactNode;
|
|
31
|
+
endAdornment?: React.ReactNode;
|
|
17
32
|
}
|
package/config/sentry.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const initSentry: (enabled?: boolean) => void
|
|
1
|
+
export declare const initSentry: (enabled?: boolean) => Promise<void>;
|
package/config/sentry.js
CHANGED
|
@@ -1,20 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
integrations: [
|
|
9
|
-
new BrowserTracing(),
|
|
10
|
-
new CaptureConsole({
|
|
11
|
-
levels: ['error'],
|
|
12
|
-
}),
|
|
13
|
-
],
|
|
14
|
-
sampleRate: 1,
|
|
15
|
-
tracesSampleRate: 0.2,
|
|
16
|
-
enabled,
|
|
17
|
-
environment: process.env.NODE_ENV,
|
|
18
|
-
release: version,
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
8
|
});
|
|
20
9
|
};
|
|
10
|
+
import { version } from './version';
|
|
11
|
+
let sentryLoaded = false;
|
|
12
|
+
export const initSentry = (enabled) => __awaiter(void 0, void 0, void 0, function* () {
|
|
13
|
+
if (enabled || sentryLoaded) {
|
|
14
|
+
const [Sentry, { CaptureConsole }, { BrowserTracing }] = yield Promise.all([
|
|
15
|
+
import('@sentry/react'),
|
|
16
|
+
import('@sentry/integrations'),
|
|
17
|
+
import('@sentry/tracing'),
|
|
18
|
+
]);
|
|
19
|
+
Sentry.init({
|
|
20
|
+
dsn: 'https://bc1312161bf948db9b9c82618035ec22@o1302189.ingest.sentry.io/6539228',
|
|
21
|
+
integrations: [
|
|
22
|
+
new BrowserTracing(),
|
|
23
|
+
new CaptureConsole({
|
|
24
|
+
levels: ['error'],
|
|
25
|
+
}),
|
|
26
|
+
],
|
|
27
|
+
sampleRate: 1,
|
|
28
|
+
tracesSampleRate: 0.2,
|
|
29
|
+
enabled,
|
|
30
|
+
environment: process.env.NODE_ENV,
|
|
31
|
+
release: version,
|
|
32
|
+
});
|
|
33
|
+
sentryLoaded = true;
|
|
34
|
+
}
|
|
35
|
+
});
|
package/config/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const name = "@lifi/widget";
|
|
2
|
-
export declare const version = "1.
|
|
2
|
+
export declare const version = "1.13.0";
|
package/config/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@lifi/widget';
|
|
2
|
-
export const version = '1.
|
|
2
|
+
export const version = '1.13.0';
|
package/hooks/index.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ export * from './useChain';
|
|
|
2
2
|
export * from './useChains';
|
|
3
3
|
export * from './useContentHeight';
|
|
4
4
|
export * from './useDebouncedWatch';
|
|
5
|
+
export * from './useFeaturedTokens';
|
|
5
6
|
export * from './useGasSufficiency';
|
|
7
|
+
export * from './useInitializer';
|
|
6
8
|
export * from './useRouteExecution';
|
|
7
9
|
export * from './useScrollableContainer';
|
|
8
10
|
export * from './useSwapRoutes';
|
|
@@ -11,4 +13,5 @@ export * from './useToken';
|
|
|
11
13
|
export * from './useTokenBalance';
|
|
12
14
|
export * from './useTokenBalances';
|
|
13
15
|
export * from './useTokens';
|
|
16
|
+
export * from './useTokenSearch';
|
|
14
17
|
export * from './useTools';
|
package/hooks/index.js
CHANGED
|
@@ -2,7 +2,9 @@ export * from './useChain';
|
|
|
2
2
|
export * from './useChains';
|
|
3
3
|
export * from './useContentHeight';
|
|
4
4
|
export * from './useDebouncedWatch';
|
|
5
|
+
export * from './useFeaturedTokens';
|
|
5
6
|
export * from './useGasSufficiency';
|
|
7
|
+
export * from './useInitializer';
|
|
6
8
|
export * from './useRouteExecution';
|
|
7
9
|
export * from './useScrollableContainer';
|
|
8
10
|
export * from './useSwapRoutes';
|
|
@@ -11,4 +13,5 @@ export * from './useToken';
|
|
|
11
13
|
export * from './useTokenBalance';
|
|
12
14
|
export * from './useTokenBalances';
|
|
13
15
|
export * from './useTokens';
|
|
16
|
+
export * from './useTokenSearch';
|
|
14
17
|
export * from './useTools';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useFeaturedTokens: (selectedChainId: number) => import("@lifi/types").Token[] | undefined;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useWidgetConfig } from '../providers/WidgetProvider';
|
|
3
|
+
export const useFeaturedTokens = (selectedChainId) => {
|
|
4
|
+
const { featuredTokens } = useWidgetConfig();
|
|
5
|
+
return useMemo(() => featuredTokens === null || featuredTokens === void 0 ? void 0 : featuredTokens.filter((token) => token.chainId === selectedChainId), [featuredTokens, selectedChainId]);
|
|
6
|
+
};
|
package/hooks/useToken.d.ts
CHANGED
package/hooks/useToken.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { TokenAmount } from '@lifi/sdk';
|
|
2
1
|
export declare const useTokenBalance: (chainId: number, tokenAddress: string) => {
|
|
3
|
-
token:
|
|
2
|
+
token: import("..").Token | undefined;
|
|
4
3
|
isLoading: boolean;
|
|
5
|
-
|
|
6
|
-
refetchBalance: (chainId?: number, tokenAddress?: string) => Promise<void>;
|
|
4
|
+
refetch: <TPageData>(options?: (import("@tanstack/query-core").RefetchOptions & import("@tanstack/query-core").RefetchQueryFilters<TPageData>) | undefined) => Promise<import("@tanstack/query-core").QueryObserverResult<import("..").Token[] | undefined, unknown>>;
|
|
7
5
|
};
|
package/hooks/useTokenBalance.js
CHANGED
|
@@ -1,46 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
11
|
-
import { useCallback } from 'react';
|
|
12
|
-
import { LiFi } from '../config/lifi';
|
|
13
|
-
import { useWallet } from '../providers/WalletProvider';
|
|
14
|
-
import { formatTokenAmount } from '../utils';
|
|
15
|
-
import { useToken } from './useToken';
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useTokenBalances } from './useTokenBalances';
|
|
16
3
|
export const useTokenBalance = (chainId, tokenAddress) => {
|
|
17
|
-
const {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
const tokenBalance = yield LiFi.getTokenBalance(address, token);
|
|
25
|
-
return Object.assign(Object.assign(Object.assign({}, token), tokenBalance), { amount: formatTokenAmount(tokenBalance === null || tokenBalance === void 0 ? void 0 : tokenBalance.amount) });
|
|
26
|
-
}), {
|
|
27
|
-
enabled: Boolean(account.address) && Boolean(token),
|
|
28
|
-
refetchIntervalInBackground: true,
|
|
29
|
-
refetchInterval: 30000,
|
|
30
|
-
staleTime: 30000,
|
|
31
|
-
cacheTime: 30000,
|
|
32
|
-
});
|
|
33
|
-
const refetchBalance = useCallback((chainId, tokenAddress) => __awaiter(void 0, void 0, void 0, function* () {
|
|
34
|
-
if (!chainId && !tokenAddress) {
|
|
35
|
-
refetch();
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
yield queryClient.invalidateQueries(['token', account.address, chainId, tokenAddress], { type: 'all', exact: true });
|
|
39
|
-
}), [account.address, queryClient, refetch]);
|
|
4
|
+
const { tokens, tokensWithBalance, isBalanceLoading, refetch } = useTokenBalances(chainId);
|
|
5
|
+
const token = useMemo(() => {
|
|
6
|
+
var _a;
|
|
7
|
+
const token = (_a = (tokensWithBalance !== null && tokensWithBalance !== void 0 ? tokensWithBalance : tokens)) === null || _a === void 0 ? void 0 : _a.find((token) => token.address === tokenAddress && token.chainId === chainId);
|
|
8
|
+
return token;
|
|
9
|
+
}, [chainId, tokenAddress, tokens, tokensWithBalance]);
|
|
40
10
|
return {
|
|
41
|
-
token
|
|
42
|
-
isLoading,
|
|
43
|
-
|
|
44
|
-
refetchBalance,
|
|
11
|
+
token,
|
|
12
|
+
isLoading: isBalanceLoading,
|
|
13
|
+
refetch,
|
|
45
14
|
};
|
|
46
15
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Token } from '../types';
|
|
2
2
|
export declare const useTokenBalances: (selectedChainId: number) => {
|
|
3
|
-
tokens:
|
|
4
|
-
tokensWithBalance:
|
|
3
|
+
tokens: Token[] | undefined;
|
|
4
|
+
tokensWithBalance: Token[] | undefined;
|
|
5
|
+
featuredTokens: import("@lifi/types").Token[] | undefined;
|
|
5
6
|
isLoading: boolean;
|
|
6
7
|
isBalanceLoading: boolean;
|
|
7
8
|
isBalanceFetched: boolean;
|
|
8
|
-
|
|
9
|
+
refetch: <TPageData>(options?: (import("@tanstack/react-query").RefetchOptions & import("@tanstack/react-query").RefetchQueryFilters<TPageData>) | undefined) => Promise<import("@tanstack/react-query").QueryObserverResult<Token[] | undefined, unknown>>;
|
|
9
10
|
};
|
|
@@ -7,20 +7,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
/* eslint-disable consistent-return */
|
|
10
11
|
import { useQuery } from '@tanstack/react-query';
|
|
11
12
|
import { useState } from 'react';
|
|
12
13
|
import { LiFi } from '../config/lifi';
|
|
13
14
|
import { useWallet } from '../providers/WalletProvider';
|
|
14
15
|
import { formatTokenAmount } from '../utils';
|
|
16
|
+
import { useFeaturedTokens } from './useFeaturedTokens';
|
|
15
17
|
import { useTokens } from './useTokens';
|
|
16
18
|
const defaultRefetchInterval = 60000;
|
|
17
19
|
const minRefetchInterval = 1000;
|
|
18
20
|
export const useTokenBalances = (selectedChainId) => {
|
|
19
21
|
const { account } = useWallet();
|
|
22
|
+
const featuredTokens = useFeaturedTokens(selectedChainId);
|
|
20
23
|
const { tokens, isLoading } = useTokens(selectedChainId);
|
|
21
24
|
const [refetchInterval, setRefetchInterval] = useState(defaultRefetchInterval);
|
|
22
|
-
const isBalanceLoadingEnabled = Boolean(account.address) && Boolean(tokens);
|
|
23
|
-
const { data: tokensWithBalance, isLoading: isBalanceLoading, isFetched: isBalanceFetched, refetch, } = useQuery(['token-balances', selectedChainId,
|
|
25
|
+
const isBalanceLoadingEnabled = Boolean(account.address) && Boolean(tokens === null || tokens === void 0 ? void 0 : tokens.length);
|
|
26
|
+
const { data: tokensWithBalance, isLoading: isBalanceLoading, isFetched: isBalanceFetched, refetch, } = useQuery(['token-balances', account.address, selectedChainId, tokens === null || tokens === void 0 ? void 0 : tokens.length], ({ queryKey: [, accountAddress] }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
24
27
|
if (!accountAddress || !tokens) {
|
|
25
28
|
return;
|
|
26
29
|
}
|
|
@@ -33,20 +36,28 @@ export const useTokenBalances = (selectedChainId) => {
|
|
|
33
36
|
: interval * 2);
|
|
34
37
|
return;
|
|
35
38
|
}
|
|
36
|
-
const
|
|
39
|
+
const featuredTokenAddresses = new Set(featuredTokens === null || featuredTokens === void 0 ? void 0 : featuredTokens.map((token) => token.address));
|
|
40
|
+
const sortFn = (a, b) => {
|
|
41
|
+
var _a, _b, _c, _d;
|
|
42
|
+
return parseFloat((_a = b.amount) !== null && _a !== void 0 ? _a : '0') * parseFloat((_b = b.priceUSD) !== null && _b !== void 0 ? _b : '0') -
|
|
43
|
+
parseFloat((_c = a.amount) !== null && _c !== void 0 ? _c : '0') * parseFloat((_d = a.priceUSD) !== null && _d !== void 0 ? _d : '0');
|
|
44
|
+
};
|
|
45
|
+
const formattedTokens = (tokenBalances.length === 0 ? tokens : tokenBalances).map((token) => {
|
|
37
46
|
token.amount = formatTokenAmount(token.amount);
|
|
38
47
|
return token;
|
|
39
48
|
});
|
|
40
|
-
|
|
41
|
-
...
|
|
42
|
-
.filter((token) => token.amount !== '0')
|
|
43
|
-
.sort(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
const result = [
|
|
50
|
+
...formattedTokens
|
|
51
|
+
.filter((token) => token.amount !== '0' && featuredTokenAddresses.has(token.address))
|
|
52
|
+
.sort(sortFn),
|
|
53
|
+
...formattedTokens.filter((token) => token.amount === '0' && featuredTokenAddresses.has(token.address)),
|
|
54
|
+
...formattedTokens
|
|
55
|
+
.filter((token) => token.amount !== '0' &&
|
|
56
|
+
!featuredTokenAddresses.has(token.address))
|
|
57
|
+
.sort(sortFn),
|
|
58
|
+
...formattedTokens.filter((token) => token.amount === '0' && !featuredTokenAddresses.has(token.address)),
|
|
49
59
|
];
|
|
60
|
+
return result;
|
|
50
61
|
}), {
|
|
51
62
|
enabled: isBalanceLoadingEnabled,
|
|
52
63
|
refetchIntervalInBackground: true,
|
|
@@ -56,9 +67,10 @@ export const useTokenBalances = (selectedChainId) => {
|
|
|
56
67
|
return {
|
|
57
68
|
tokens,
|
|
58
69
|
tokensWithBalance,
|
|
70
|
+
featuredTokens,
|
|
59
71
|
isLoading,
|
|
60
72
|
isBalanceLoading: isBalanceLoading && isBalanceLoadingEnabled,
|
|
61
73
|
isBalanceFetched,
|
|
62
|
-
|
|
74
|
+
refetch,
|
|
63
75
|
};
|
|
64
76
|
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
11
|
+
import { LiFi } from '../config/lifi';
|
|
12
|
+
export const useTokenSearch = (token, chainId, enabled) => {
|
|
13
|
+
const queryClient = useQueryClient();
|
|
14
|
+
const { data, isLoading, isFetching, isFetched } = useQuery(['token-search', chainId, token], ({ queryKey: [, chainId, token], signal }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
|
+
const data = yield LiFi.getToken(chainId, token, {
|
|
16
|
+
signal,
|
|
17
|
+
});
|
|
18
|
+
if (data) {
|
|
19
|
+
queryClient.setQueriesData(['tokens', chainId], (tokens) => {
|
|
20
|
+
if (!(tokens === null || tokens === void 0 ? void 0 : tokens.some((token) => token.address === data.address))) {
|
|
21
|
+
tokens === null || tokens === void 0 ? void 0 : tokens.push(data);
|
|
22
|
+
}
|
|
23
|
+
return tokens;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return data;
|
|
27
|
+
}), {
|
|
28
|
+
enabled,
|
|
29
|
+
retry: false,
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
token: data,
|
|
33
|
+
isLoading,
|
|
34
|
+
isFetching,
|
|
35
|
+
isFetched,
|
|
36
|
+
};
|
|
37
|
+
};
|
package/hooks/useTokens.d.ts
CHANGED
package/hooks/useTokens.js
CHANGED
|
@@ -9,12 +9,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { useQuery } from '@tanstack/react-query';
|
|
11
11
|
import { LiFi } from '../config/lifi';
|
|
12
|
+
import { useFeaturedTokens } from './useFeaturedTokens';
|
|
12
13
|
export const useTokens = (selectedChainId) => {
|
|
13
|
-
const
|
|
14
|
-
|
|
14
|
+
const featuredTokens = useFeaturedTokens(selectedChainId);
|
|
15
|
+
const { data: tokens, isLoading, isFetching, } = useQuery(['tokens', selectedChainId, featuredTokens === null || featuredTokens === void 0 ? void 0 : featuredTokens.length], () => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
var _a, _b, _c;
|
|
15
17
|
const data = yield LiFi.getTokens({ chains: [selectedChainId] });
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
const featuredTokenAddresses = new Set(featuredTokens === null || featuredTokens === void 0 ? void 0 : featuredTokens.map((token) => token.address));
|
|
19
|
+
return [
|
|
20
|
+
...((_a = featuredTokens === null || featuredTokens === void 0 ? void 0 : featuredTokens.map((token) => {
|
|
21
|
+
token.featured = true;
|
|
22
|
+
return token;
|
|
23
|
+
})) !== null && _a !== void 0 ? _a : []),
|
|
24
|
+
...((_c = (_b = data.tokens) === null || _b === void 0 ? void 0 : _b[selectedChainId].filter((token) => !featuredTokenAddresses.has(token.address))) !== null && _c !== void 0 ? _c : []),
|
|
25
|
+
];
|
|
18
26
|
}));
|
|
19
27
|
return {
|
|
20
28
|
tokens,
|
package/i18n/en/translation.json
CHANGED
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"selectChain": "Chain",
|
|
51
51
|
"selectToken": "Token",
|
|
52
52
|
"selectChainAndToken": "Select chain and token",
|
|
53
|
+
"featuredTokens": "Featured tokens",
|
|
54
|
+
"otherTokens": "Other tokens",
|
|
53
55
|
"inProgress": "In progress",
|
|
54
56
|
"couldntFindTokens": "We couldn't find tokens on this chain or you don't have any.",
|
|
55
57
|
"stepSwap": "Swap",
|
package/i18n/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifi/widget",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.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
|
"sideEffects": false,
|
|
6
6
|
"main": "./index.js",
|
|
@@ -44,23 +44,22 @@
|
|
|
44
44
|
"@lifi/sdk": "^1.1.3",
|
|
45
45
|
"@lifi/wallet-management": "^1.1.3",
|
|
46
46
|
"@mui/icons-material": "^5.8.4",
|
|
47
|
-
"@mui/lab": "^5.0.0-alpha.
|
|
48
|
-
"@mui/material": "^5.
|
|
49
|
-
"@sentry/integrations": "^7.
|
|
50
|
-
"@sentry/react": "^7.
|
|
51
|
-
"@sentry/tracing": "^7.
|
|
47
|
+
"@mui/lab": "^5.0.0-alpha.94",
|
|
48
|
+
"@mui/material": "^5.10.0",
|
|
49
|
+
"@sentry/integrations": "^7.10.0",
|
|
50
|
+
"@sentry/react": "^7.10.0",
|
|
51
|
+
"@sentry/tracing": "^7.10.0",
|
|
52
52
|
"@tanstack/react-query": "^4.0.10",
|
|
53
|
+
"@tanstack/react-virtual": "^3.0.0-beta.17",
|
|
53
54
|
"big.js": "^6.2.1",
|
|
54
|
-
"i18next": "^21.
|
|
55
|
+
"i18next": "^21.9.0",
|
|
55
56
|
"immer": "^9.0.15",
|
|
56
57
|
"react": "^18.2.0",
|
|
57
58
|
"react-dom": "^18.2.0",
|
|
58
59
|
"react-hook-form": "^7.34.0",
|
|
59
60
|
"react-i18next": "^11.18.3",
|
|
60
|
-
"react-resize-detector": "^7.1.2",
|
|
61
61
|
"react-router-dom": "^6.3.0",
|
|
62
62
|
"react-timer-hook": "^3.0.5",
|
|
63
|
-
"react-virtual": "^2.10.4",
|
|
64
63
|
"zustand": "^4.0.0"
|
|
65
64
|
},
|
|
66
65
|
"eslintConfig": {
|
|
@@ -6,20 +6,23 @@ import { useTranslation } from 'react-i18next';
|
|
|
6
6
|
import { Card, CardTitle } from '../../components/Card';
|
|
7
7
|
import { Select } from '../../components/Select';
|
|
8
8
|
import { useChains } from '../../hooks';
|
|
9
|
-
import { SwapFormKeyHelper, } from '../../providers/SwapFormProvider';
|
|
9
|
+
import { SwapFormKey, SwapFormKeyHelper, } from '../../providers/SwapFormProvider';
|
|
10
10
|
import { useWidgetConfig } from '../../providers/WidgetProvider';
|
|
11
11
|
export const ChainSelect = ({ formType }) => {
|
|
12
12
|
const { t } = useTranslation();
|
|
13
|
-
const { setValue } = useFormContext();
|
|
13
|
+
const { setValue, register } = useFormContext();
|
|
14
14
|
const { fromChain, toChain } = useWidgetConfig();
|
|
15
15
|
const { chains, isLoading } = useChains();
|
|
16
|
+
const chainKey = SwapFormKeyHelper.getChainKey(formType);
|
|
16
17
|
const [chainId] = useWatch({
|
|
17
|
-
name: [
|
|
18
|
+
name: [chainKey],
|
|
18
19
|
});
|
|
20
|
+
const { onChange, onBlur, name, ref } = register(chainKey);
|
|
19
21
|
const handleChain = (event) => {
|
|
20
|
-
|
|
22
|
+
onChange(event);
|
|
21
23
|
setValue(SwapFormKeyHelper.getTokenKey(formType), '');
|
|
22
24
|
setValue(SwapFormKeyHelper.getAmountKey(formType), '');
|
|
25
|
+
setValue(SwapFormKey.TokenSearchFilter, '');
|
|
23
26
|
};
|
|
24
|
-
return !isLoading ? (_jsxs(Card, { children: [_jsx(CardTitle, { children: t(`swap.selectChain`) }), _jsx(FormControl, Object.assign({ fullWidth: true }, { children: _jsx(Select, Object.assign({ labelId:
|
|
27
|
+
return !isLoading ? (_jsxs(Card, { children: [_jsx(CardTitle, { children: t(`swap.selectChain`) }), _jsx(FormControl, Object.assign({ fullWidth: true }, { children: _jsx(Select, Object.assign({ ref: ref, labelId: chainKey, name: name, MenuProps: { elevation: 2 }, defaultValue: formType === 'from' ? fromChain : toChain, value: chainId, onChange: handleChain, onBlur: onBlur, IconComponent: KeyboardArrowDownIcon }, { children: chains === null || chains === void 0 ? void 0 : chains.map((chain) => (_jsxs(MenuItem, Object.assign({ value: chain.id }, { children: [_jsx(ListItemAvatar, { children: _jsx(Avatar, Object.assign({ src: chain.logoURI, alt: chain.key }, { children: chain.name[0] })) }), chain.name] }), chain.key))) })) }))] })) : (_jsx(Skeleton, { variant: "rectangular", width: "100%", height: 98, sx: { borderRadius: 1 } }));
|
|
25
28
|
};
|
|
@@ -11,7 +11,7 @@ export const SearchTokenInput = () => {
|
|
|
11
11
|
const { t } = useTranslation();
|
|
12
12
|
const { register, setValue } = useFormContext();
|
|
13
13
|
useEffect(() => () => {
|
|
14
|
-
setValue(SwapFormKey.
|
|
14
|
+
setValue(SwapFormKey.TokenSearchFilter, '');
|
|
15
15
|
}, [setValue]);
|
|
16
|
-
return (_jsx(Card, { children: _jsx(FormControl, Object.assign({ fullWidth: true }, { children: _jsx(Input, { size: "small", placeholder: t(`swap.tokenSearch`), defaultValue: "", endAdornment: _jsx(InputAdornment, Object.assign({ position: "end" }, { children: _jsx(SearchIcon, {}) })), inputProps: Object.assign({ inputMode: 'search' }, register(SwapFormKey.
|
|
16
|
+
return (_jsx(Card, { children: _jsx(FormControl, Object.assign({ fullWidth: true }, { children: _jsx(Input, { size: "small", placeholder: t(`swap.tokenSearch`), defaultValue: "", endAdornment: _jsx(InputAdornment, Object.assign({ position: "end" }, { children: _jsx(SearchIcon, {}) })), inputProps: Object.assign({ inputMode: 'search' }, register(SwapFormKey.TokenSearchFilter)), autoComplete: "off" }) })) }));
|
|
17
17
|
};
|
|
@@ -17,10 +17,10 @@ export const StatusBottomSheet = ({ status, route, }) => {
|
|
|
17
17
|
const navigate = useNavigate();
|
|
18
18
|
const ref = useRef(null);
|
|
19
19
|
const { getChainById } = useChains();
|
|
20
|
-
const { token, refetchBalance } = useTokenBalance(route.toChainId, route.toToken.address);
|
|
20
|
+
const { token, refetch: refetchBalance } = useTokenBalance(route.toChainId, route.toToken.address);
|
|
21
21
|
const { setValue } = useFormContext();
|
|
22
22
|
const clearFromAmount = () => {
|
|
23
|
-
refetchBalance(
|
|
23
|
+
refetchBalance();
|
|
24
24
|
setValue(SwapFormKey.FromAmount, '');
|
|
25
25
|
};
|
|
26
26
|
const handleDone = () => {
|
|
@@ -1,18 +1,35 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ChainId } from '@lifi/sdk';
|
|
3
|
+
import { useEffect } from 'react';
|
|
2
4
|
import { FormProvider, useForm } from 'react-hook-form';
|
|
5
|
+
import { useWallet } from '../WalletProvider';
|
|
3
6
|
import { useWidgetConfig } from '../WidgetProvider';
|
|
4
7
|
import { SwapFormKey } from './types';
|
|
5
8
|
export const formDefaultValues = {
|
|
6
9
|
[SwapFormKey.FromAmount]: '',
|
|
7
|
-
[SwapFormKey.
|
|
10
|
+
[SwapFormKey.TokenSearchFilter]: '',
|
|
8
11
|
};
|
|
9
12
|
export const SwapFormProvider = ({ children, }) => {
|
|
10
|
-
|
|
13
|
+
const { account } = useWallet();
|
|
11
14
|
const { fromChain, fromToken, fromAmount, toChain, toToken } = useWidgetConfig();
|
|
12
15
|
const methods = useForm({
|
|
13
|
-
defaultValues: Object.assign(Object.assign({}, formDefaultValues), { fromChain,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
defaultValues: Object.assign(Object.assign({}, formDefaultValues), { fromChain: fromChain !== null && fromChain !== void 0 ? fromChain : ChainId.ETH, fromToken, fromAmount: (typeof fromAmount === 'number'
|
|
17
|
+
? fromAmount === null || fromAmount === void 0 ? void 0 : fromAmount.toPrecision()
|
|
18
|
+
: fromAmount) || formDefaultValues.fromAmount, toChain: toChain !== null && toChain !== void 0 ? toChain : ChainId.ETH, toToken }),
|
|
16
19
|
});
|
|
20
|
+
// Set wallet chain as default if no fromChain is provided by config and if it wasn't changed during widget usage
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const { isDirty, isTouched } = methods.getFieldState(SwapFormKey.FromChain, methods.formState);
|
|
23
|
+
if (account.isActive &&
|
|
24
|
+
account.chainId &&
|
|
25
|
+
!fromChain &&
|
|
26
|
+
!isDirty &&
|
|
27
|
+
!isTouched) {
|
|
28
|
+
methods.setValue(SwapFormKey.FromChain, account.chainId, {
|
|
29
|
+
shouldDirty: false,
|
|
30
|
+
shouldTouch: false,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}, [account.chainId, account.isActive, fromChain, methods]);
|
|
17
34
|
return _jsx(FormProvider, Object.assign({}, methods, { children: children }));
|
|
18
35
|
};
|
|
@@ -2,7 +2,7 @@ export declare enum SwapFormKey {
|
|
|
2
2
|
FromAmount = "fromAmount",
|
|
3
3
|
FromChain = "fromChain",
|
|
4
4
|
FromToken = "fromToken",
|
|
5
|
-
|
|
5
|
+
TokenSearchFilter = "tokenSearchFilter",
|
|
6
6
|
ToChain = "toChain",
|
|
7
7
|
ToToken = "toToken"
|
|
8
8
|
}
|
|
@@ -10,7 +10,7 @@ export declare type SwapFormValues = {
|
|
|
10
10
|
[SwapFormKey.FromAmount]: string;
|
|
11
11
|
[SwapFormKey.FromChain]: number;
|
|
12
12
|
[SwapFormKey.FromToken]: string;
|
|
13
|
-
[SwapFormKey.
|
|
13
|
+
[SwapFormKey.TokenSearchFilter]: string;
|
|
14
14
|
[SwapFormKey.ToChain]: number;
|
|
15
15
|
[SwapFormKey.ToToken]: string;
|
|
16
16
|
};
|
|
@@ -19,7 +19,6 @@ export declare const SwapFormKeyHelper: {
|
|
|
19
19
|
getChainKey: (formType: SwapFormDirection) => 'fromChain' | 'toChain';
|
|
20
20
|
getTokenKey: (formType: SwapFormDirection) => 'fromToken' | 'toToken';
|
|
21
21
|
getAmountKey: (formType: SwapFormDirection) => 'fromAmount' | 'toAmount';
|
|
22
|
-
getSearchTokensFilterKey: (formType: SwapFormDirection) => string;
|
|
23
22
|
};
|
|
24
23
|
export interface SwapFormTypeProps {
|
|
25
24
|
formType: SwapFormDirection;
|
|
@@ -3,7 +3,7 @@ export var SwapFormKey;
|
|
|
3
3
|
SwapFormKey["FromAmount"] = "fromAmount";
|
|
4
4
|
SwapFormKey["FromChain"] = "fromChain";
|
|
5
5
|
SwapFormKey["FromToken"] = "fromToken";
|
|
6
|
-
SwapFormKey["
|
|
6
|
+
SwapFormKey["TokenSearchFilter"] = "tokenSearchFilter";
|
|
7
7
|
SwapFormKey["ToChain"] = "toChain";
|
|
8
8
|
SwapFormKey["ToToken"] = "toToken";
|
|
9
9
|
})(SwapFormKey || (SwapFormKey = {}));
|
|
@@ -11,5 +11,4 @@ export const SwapFormKeyHelper = {
|
|
|
11
11
|
getChainKey: (formType) => `${formType}Chain`,
|
|
12
12
|
getTokenKey: (formType) => `${formType}Token`,
|
|
13
13
|
getAmountKey: (formType) => `${formType}Amount`,
|
|
14
|
-
getSearchTokensFilterKey: (formType) => `${formType}SearchTokensFilter`,
|
|
15
14
|
};
|
|
@@ -10,10 +10,9 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
-
import {
|
|
13
|
+
import { getChainByKey } from '@lifi/sdk';
|
|
14
14
|
import { createContext, useContext, useEffect, useMemo } from 'react';
|
|
15
15
|
import { updateLiFiConfig } from '../../config/lifi';
|
|
16
|
-
import { useWallet } from '../WalletProvider';
|
|
17
16
|
const stub = () => {
|
|
18
17
|
throw new Error('You forgot to wrap your component in <WidgetProvider>.');
|
|
19
18
|
};
|
|
@@ -23,26 +22,36 @@ const initialContext = {
|
|
|
23
22
|
const WidgetContext = createContext(initialContext);
|
|
24
23
|
export const useWidgetConfig = () => useContext(WidgetContext);
|
|
25
24
|
export const WidgetProvider = (_a) => {
|
|
26
|
-
var { children } = _a, _b = _a.config, _c = _b === void 0 ? {} : _b, { fromChain, fromToken, toChain, toToken, integrator } = _c, config = __rest(_c, ["fromChain", "fromToken", "toChain", "toToken", "integrator"]);
|
|
27
|
-
const { account } = useWallet();
|
|
25
|
+
var { children } = _a, _b = _a.config, _c = _b === void 0 ? {} : _b, { fromChain, fromToken, toChain, toToken, fromAmount, integrator } = _c, config = __rest(_c, ["fromChain", "fromToken", "toChain", "toToken", "fromAmount", "integrator"]);
|
|
28
26
|
const value = useMemo(() => {
|
|
29
|
-
var _a;
|
|
27
|
+
var _a, _b, _c, _d;
|
|
30
28
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
const searchParams = Object.fromEntries(new URLSearchParams(window === null || window === void 0 ? void 0 : window.location.search));
|
|
30
|
+
return Object.assign(Object.assign({}, config), { fromChain: (searchParams.fromChain &&
|
|
31
|
+
isNaN(parseInt(searchParams.fromChain, 10))) ||
|
|
32
|
+
typeof fromChain === 'string'
|
|
33
|
+
? getChainByKey((_a = (searchParams.fromChain || fromChain)) === null || _a === void 0 ? void 0 : _a.toLowerCase()).id
|
|
34
|
+
: (searchParams.fromChain &&
|
|
35
|
+
!isNaN(parseInt(searchParams.fromChain, 10))) ||
|
|
36
|
+
typeof fromChain === 'number'
|
|
37
|
+
? parseInt(searchParams.fromChain, 10) || fromChain
|
|
38
|
+
: undefined, toChain: (searchParams.toChain && isNaN(parseInt(searchParams.toChain, 10))) ||
|
|
39
|
+
typeof toChain === 'string'
|
|
40
|
+
? getChainByKey((_b = (searchParams.toChain || toChain)) === null || _b === void 0 ? void 0 : _b.toLowerCase()).id
|
|
41
|
+
: (searchParams.toChain &&
|
|
42
|
+
!isNaN(parseInt(searchParams.toChain, 10))) ||
|
|
43
|
+
typeof toChain === 'number'
|
|
44
|
+
? parseInt(searchParams.toChain, 10) || toChain
|
|
45
|
+
: undefined, fromToken: ((_c = searchParams.fromToken) === null || _c === void 0 ? void 0 : _c.toLowerCase()) || (fromToken === null || fromToken === void 0 ? void 0 : fromToken.toLowerCase()), toToken: ((_d = searchParams.toToken) === null || _d === void 0 ? void 0 : _d.toLowerCase()) || (toToken === null || toToken === void 0 ? void 0 : toToken.toLowerCase()), fromAmount: typeof searchParams.fromAmount === 'string' &&
|
|
46
|
+
!isNaN(parseFloat(searchParams.fromAmount))
|
|
47
|
+
? searchParams.fromAmount
|
|
48
|
+
: fromAmount });
|
|
40
49
|
}
|
|
41
50
|
catch (e) {
|
|
42
51
|
console.warn(e);
|
|
43
52
|
return config;
|
|
44
53
|
}
|
|
45
|
-
}, [
|
|
54
|
+
}, [config, fromAmount, fromChain, fromToken, toChain, toToken]);
|
|
46
55
|
useEffect(() => {
|
|
47
56
|
updateLiFiConfig({
|
|
48
57
|
defaultRouteOptions: {
|
package/types/index.d.ts
CHANGED
package/types/index.js
CHANGED
package/types/token.d.ts
ADDED
package/types/token.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/types/widget.d.ts
CHANGED
|
@@ -18,8 +18,7 @@ export interface WidgetWalletManagement {
|
|
|
18
18
|
signer?: Signer;
|
|
19
19
|
}
|
|
20
20
|
interface WidgetConfigBase {
|
|
21
|
-
fromAmount?: number;
|
|
22
|
-
disabledChains?: number[];
|
|
21
|
+
fromAmount?: number | string;
|
|
23
22
|
containerStyle?: CSSProperties;
|
|
24
23
|
theme?: ThemeConfig;
|
|
25
24
|
appearance?: Appearance;
|
|
@@ -27,6 +26,8 @@ interface WidgetConfigBase {
|
|
|
27
26
|
disableTelemetry?: boolean;
|
|
28
27
|
walletManagement?: WidgetWalletManagement;
|
|
29
28
|
integrator?: string;
|
|
29
|
+
disabledChains?: number[];
|
|
30
|
+
featuredTokens?: Token[];
|
|
30
31
|
}
|
|
31
32
|
declare type WidgetFromTokenConfig = {
|
|
32
33
|
fromChain: `${ChainKey}` | number;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { TokenAmount } from '@lifi/sdk';
|
|
2
|
-
export declare const tokenAmountMock: TokenAmount;
|
|
3
|
-
export declare const skeletonKey = "skeleton";
|
|
4
|
-
export declare const createTokenAmountSkeletons: () => {
|
|
5
|
-
address: string;
|
|
6
|
-
name: string;
|
|
7
|
-
amount: string;
|
|
8
|
-
blockNumber?: number | undefined;
|
|
9
|
-
symbol: string;
|
|
10
|
-
decimals: number;
|
|
11
|
-
chainId: number;
|
|
12
|
-
coinKey?: import("@lifi/sdk").CoinKey | undefined;
|
|
13
|
-
priceUSD?: string | undefined;
|
|
14
|
-
logoURI?: string | undefined;
|
|
15
|
-
}[];
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export const tokenAmountMock = {
|
|
2
|
-
address: '-1x0',
|
|
3
|
-
amount: '',
|
|
4
|
-
chainId: -1,
|
|
5
|
-
decimals: 0,
|
|
6
|
-
name: '',
|
|
7
|
-
symbol: '',
|
|
8
|
-
};
|
|
9
|
-
export const skeletonKey = 'skeleton';
|
|
10
|
-
export const createTokenAmountSkeletons = () => Array.from({ length: 3 }).map((_, index) => (Object.assign(Object.assign({}, tokenAmountMock), { address: `${tokenAmountMock.address}-${index}`, name: `${skeletonKey}-${index}` })));
|