@lifi/widget 4.8.0 → 4.10.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 (136) hide show
  1. package/README.md +26 -3
  2. package/dist/esm/components/AmountInputCard/AmountInputCardPair.js +5 -2
  3. package/dist/esm/components/AmountInputCard/AmountInputCardPair.js.map +1 -1
  4. package/dist/esm/components/SelectChainAndToken.js +5 -2
  5. package/dist/esm/components/SelectChainAndToken.js.map +1 -1
  6. package/dist/esm/components/SendToWallet/SendToWalletButton.js +5 -4
  7. package/dist/esm/components/SendToWallet/SendToWalletButton.js.map +1 -1
  8. package/dist/esm/components/TokenList/TokenDetailsSheet.js +5 -8
  9. package/dist/esm/components/TokenList/TokenDetailsSheet.js.map +1 -1
  10. package/dist/esm/components/TokenList/TokenDetailsSheetContent.js +30 -2
  11. package/dist/esm/components/TokenList/TokenDetailsSheetContent.js.map +1 -1
  12. package/dist/esm/components/TokenList/TokenListItem.js +36 -47
  13. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  14. package/dist/esm/components/TokenList/VirtualizedTokenList.d.ts.map +1 -1
  15. package/dist/esm/components/TokenList/VirtualizedTokenList.js +2 -2
  16. package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  17. package/dist/esm/components/TokenList/tokenVerificationIcons.js +29 -0
  18. package/dist/esm/components/TokenList/tokenVerificationIcons.js.map +1 -0
  19. package/dist/esm/components/TokenList/useTokenSelect.d.ts.map +1 -1
  20. package/dist/esm/components/TokenList/useTokenSelect.js +10 -0
  21. package/dist/esm/components/TokenList/useTokenSelect.js.map +1 -1
  22. package/dist/esm/config/constants.js.map +1 -1
  23. package/dist/esm/config/version.d.ts +1 -1
  24. package/dist/esm/config/version.js +1 -1
  25. package/dist/esm/config/version.js.map +1 -1
  26. package/dist/esm/hooks/useAccountsBalancesData.js +5 -2
  27. package/dist/esm/hooks/useAccountsBalancesData.js.map +1 -1
  28. package/dist/esm/hooks/useAddressValidation.d.ts +3 -2
  29. package/dist/esm/hooks/useAddressValidation.d.ts.map +1 -1
  30. package/dist/esm/hooks/useAddressValidation.js +48 -21
  31. package/dist/esm/hooks/useAddressValidation.js.map +1 -1
  32. package/dist/esm/hooks/useGasRefuel.js +4 -1
  33. package/dist/esm/hooks/useGasRefuel.js.map +1 -1
  34. package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
  35. package/dist/esm/hooks/useRoutes.js +11 -6
  36. package/dist/esm/hooks/useRoutes.js.map +1 -1
  37. package/dist/esm/hooks/useToAddressAutoPopulate.js +6 -8
  38. package/dist/esm/hooks/useToAddressAutoPopulate.js.map +1 -1
  39. package/dist/esm/hooks/useToAddressRequirements.d.ts +3 -2
  40. package/dist/esm/hooks/useToAddressRequirements.d.ts.map +1 -1
  41. package/dist/esm/hooks/useToAddressRequirements.js +8 -7
  42. package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
  43. package/dist/esm/hooks/useToAddressReset.js +6 -3
  44. package/dist/esm/hooks/useToAddressReset.js.map +1 -1
  45. package/dist/esm/hooks/useTokenBalance.js +2 -1
  46. package/dist/esm/hooks/useTokenBalance.js.map +1 -1
  47. package/dist/esm/hooks/useTokens.js +1 -1
  48. package/dist/esm/i18n/bn.json +7 -2
  49. package/dist/esm/i18n/de.json +7 -2
  50. package/dist/esm/i18n/en.json +7 -2
  51. package/dist/esm/i18n/es.json +7 -2
  52. package/dist/esm/i18n/fr.json +7 -2
  53. package/dist/esm/i18n/hi.json +7 -2
  54. package/dist/esm/i18n/id.json +7 -2
  55. package/dist/esm/i18n/it.json +7 -2
  56. package/dist/esm/i18n/ja.json +7 -2
  57. package/dist/esm/i18n/ko.json +7 -2
  58. package/dist/esm/i18n/pl.json +7 -2
  59. package/dist/esm/i18n/pt.json +7 -2
  60. package/dist/esm/i18n/th.json +7 -2
  61. package/dist/esm/i18n/tr.json +7 -2
  62. package/dist/esm/i18n/uk.json +7 -2
  63. package/dist/esm/i18n/vi.json +7 -2
  64. package/dist/esm/i18n/zh.json +7 -2
  65. package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js +7 -4
  66. package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js.map +1 -1
  67. package/dist/esm/pages/SendToWallet/BookmarksPage.js +35 -31
  68. package/dist/esm/pages/SendToWallet/BookmarksPage.js.map +1 -1
  69. package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js +4 -3
  70. package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js.map +1 -1
  71. package/dist/esm/pages/SendToWallet/RecentWalletsPage.js +43 -39
  72. package/dist/esm/pages/SendToWallet/RecentWalletsPage.js.map +1 -1
  73. package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js +32 -26
  74. package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js.map +1 -1
  75. package/dist/esm/pages/SendToWallet/SendToWalletPage.js +5 -10
  76. package/dist/esm/pages/SendToWallet/SendToWalletPage.js.map +1 -1
  77. package/dist/esm/providers/WalletProvider/WalletProvider.d.ts.map +1 -1
  78. package/dist/esm/providers/WalletProvider/WalletProvider.js +14 -2
  79. package/dist/esm/providers/WalletProvider/WalletProvider.js.map +1 -1
  80. package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts.map +1 -1
  81. package/dist/esm/providers/WidgetProvider/WidgetProvider.js +10 -14
  82. package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
  83. package/dist/esm/stores/bookmarks/types.d.ts +3 -0
  84. package/dist/esm/stores/bookmarks/types.d.ts.map +1 -1
  85. package/dist/esm/stores/chains/ChainOrderStore.d.ts.map +1 -1
  86. package/dist/esm/stores/chains/ChainOrderStore.js +4 -1
  87. package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
  88. package/dist/esm/stores/form/FormStore.js +3 -2
  89. package/dist/esm/stores/form/FormStore.js.map +1 -1
  90. package/dist/esm/stores/form/URLSearchParamsBuilder.js +7 -3
  91. package/dist/esm/stores/form/URLSearchParamsBuilder.js.map +1 -1
  92. package/dist/esm/stores/form/getDefaultValuesFromQueryString.js +4 -1
  93. package/dist/esm/stores/form/getDefaultValuesFromQueryString.js.map +1 -1
  94. package/dist/esm/utils/chainType.js +32 -1
  95. package/dist/esm/utils/chainType.js.map +1 -1
  96. package/dist/esm/utils/customReceiver.js +6 -1
  97. package/dist/esm/utils/customReceiver.js.map +1 -1
  98. package/dist/esm/utils/token.js +13 -1
  99. package/dist/esm/utils/token.js.map +1 -1
  100. package/package.json +7 -7
  101. package/src/components/AmountInputCard/AmountInputCardPair.tsx +6 -2
  102. package/src/components/SelectChainAndToken.tsx +5 -1
  103. package/src/components/SendToWallet/SendToWalletButton.tsx +13 -8
  104. package/src/components/TokenList/TokenDetailsSheet.tsx +5 -9
  105. package/src/components/TokenList/TokenDetailsSheetContent.tsx +46 -4
  106. package/src/components/TokenList/TokenListItem.tsx +44 -68
  107. package/src/components/TokenList/VirtualizedTokenList.tsx +3 -6
  108. package/src/components/TokenList/tokenVerificationIcons.ts +15 -0
  109. package/src/components/TokenList/types.ts +3 -11
  110. package/src/components/TokenList/useTokenSelect.ts +15 -0
  111. package/src/config/constants.ts +3 -0
  112. package/src/config/version.ts +1 -1
  113. package/src/hooks/useAccountsBalancesData.ts +6 -2
  114. package/src/hooks/useAddressValidation.ts +87 -32
  115. package/src/hooks/useGasRefuel.ts +11 -1
  116. package/src/hooks/useRoutes.ts +23 -18
  117. package/src/hooks/useToAddressAutoPopulate.ts +13 -17
  118. package/src/hooks/useToAddressRequirements.ts +19 -4
  119. package/src/hooks/useToAddressReset.ts +13 -4
  120. package/src/hooks/useTokenBalance.ts +6 -1
  121. package/src/pages/SendToWallet/BookmarkAddressSheet.tsx +5 -1
  122. package/src/pages/SendToWallet/BookmarksPage.tsx +42 -52
  123. package/src/pages/SendToWallet/ConnectedWalletsPage.tsx +4 -14
  124. package/src/pages/SendToWallet/RecentWalletsPage.tsx +59 -66
  125. package/src/pages/SendToWallet/SendToConfiguredWalletPage.tsx +45 -50
  126. package/src/pages/SendToWallet/SendToWalletPage.tsx +4 -16
  127. package/src/providers/WalletProvider/WalletProvider.tsx +28 -3
  128. package/src/providers/WidgetProvider/WidgetProvider.tsx +20 -19
  129. package/src/stores/bookmarks/types.ts +3 -0
  130. package/src/stores/chains/ChainOrderStore.tsx +4 -1
  131. package/src/stores/form/FormStore.tsx +2 -0
  132. package/src/stores/form/URLSearchParamsBuilder.tsx +4 -2
  133. package/src/stores/form/getDefaultValuesFromQueryString.ts +19 -0
  134. package/src/utils/chainType.ts +73 -1
  135. package/src/utils/customReceiver.ts +36 -0
  136. package/src/utils/token.ts +48 -0
@@ -1 +1 @@
1
- {"version":3,"file":"TokenListItem.js","names":["ListItem","IconButton","InfoOutlinedIcon","ListItemButton"],"sources":["../../../../src/components/TokenList/TokenListItem.tsx"],"sourcesContent":["import type { StaticToken } from '@lifi/sdk'\nimport { ChainType } from '@lifi/sdk'\nimport InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'\nimport ReportRoundedIcon from '@mui/icons-material/ReportRounded'\nimport VerifiedIcon from '@mui/icons-material/Verified'\nimport {\n Avatar,\n Box,\n ListItemAvatar,\n ListItemText,\n listItemSecondaryActionClasses,\n Skeleton,\n Slide,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport type { JSX, MouseEventHandler } from 'react'\nimport { memo, useMemo, useRef, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { useLongPress } from '../../hooks/useLongPress.js'\nimport { formatTokenAmount, formatTokenPrice } from '../../utils/format.js'\nimport { getTokenVerificationProvider } from '../../utils/token.js'\nimport { shortenAddress } from '../../utils/wallet.js'\nimport { TokenAvatar } from '../Avatar/TokenAvatar.js'\nimport { ListItemButton } from '../ListItem/ListItemButton.js'\nimport { PinTokenButton } from './PinTokenButton.js'\nimport { RemoveRecentTokenButton } from './RemoveRecentTokenButton.js'\nimport { IconButton, ListItem } from './TokenList.style.js'\nimport type {\n TokenListItemAvatarProps,\n TokenListItemButtonProps,\n TokenListItemProps,\n} from './types.js'\n\nexport const TokenListItem: React.FC<TokenListItemProps> = memo(\n ({\n onClick,\n size,\n start,\n token,\n chain,\n chainName,\n isBalanceLoading,\n startAdornment,\n endAdornment,\n selected,\n onShowTokenDetails,\n }) => {\n return (\n <ListItem\n style={{\n height: `${size}px`,\n transform: `translateY(${start}px)`,\n padding: 0,\n }}\n >\n {startAdornment}\n <TokenListItemButton\n token={token}\n isBalanceLoading={isBalanceLoading}\n onClick={onClick}\n chain={chain}\n chainName={chainName}\n selected={selected}\n onShowTokenDetails={onShowTokenDetails}\n />\n {endAdornment}\n </ListItem>\n )\n }\n)\n\nconst TokenListItemAvatar = memo(({ token }: TokenListItemAvatarProps) => {\n const [isImageLoading, setIsImageLoading] = useState(true)\n\n return (\n <Avatar\n src={token.logoURI}\n alt={token.symbol}\n sx={(theme) =>\n isImageLoading ? { bgcolor: theme.vars.palette.grey[300] } : null\n }\n onLoad={() => setIsImageLoading(false)}\n >\n {token.symbol?.[0]}\n </Avatar>\n )\n})\n\ninterface OpenTokenDetailsButtonProps {\n tokenAddress: string | undefined\n withoutContractAddress: boolean\n chainId: number\n onClick: (\n tokenAddress: string,\n withoutContractAddress: boolean,\n chainId: number\n ) => void\n}\n\nconst OpenTokenDetailsButton = ({\n tokenAddress,\n withoutContractAddress,\n chainId,\n onClick,\n}: OpenTokenDetailsButtonProps) => {\n if (!tokenAddress) {\n return null\n }\n return (\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation()\n e.currentTarget.blur() // Remove focus to prevent accessibility issues when opening drawer\n onClick(tokenAddress, withoutContractAddress, chainId)\n }}\n >\n <InfoOutlinedIcon />\n </IconButton>\n )\n}\n\nconst TokenListItemButton: React.FC<TokenListItemButtonProps> = memo(\n ({\n onClick,\n token,\n chain,\n chainName,\n isBalanceLoading,\n selected,\n onShowTokenDetails,\n }) => {\n const { t } = useTranslation()\n const container = useRef(null)\n const timeoutId = useRef<ReturnType<typeof setTimeout>>(undefined)\n const [showAddress, setShowAddress] = useState(false)\n\n const withoutContractAddress = chain?.chainType === ChainType.UTXO\n\n const onMouseEnter = () => {\n timeoutId.current = setTimeout(() => {\n if (token.address) {\n setShowAddress(true)\n }\n }, 350)\n }\n\n const onMouseLeave = () => {\n clearTimeout(timeoutId.current)\n if (showAddress) {\n setShowAddress(false)\n }\n }\n\n const handleClick: MouseEventHandler<HTMLDivElement> = (e) => {\n e.stopPropagation()\n onClick?.(token.address, token.chainId)\n }\n\n const tokenAmount = useMemo(\n () => formatTokenAmount(token.amount, token.decimals),\n [token.amount, token.decimals]\n )\n\n const tokenPrice = useMemo(\n () => formatTokenPrice(token.amount, token.priceUSD, token.decimals),\n [token.amount, token.priceUSD, token.decimals]\n )\n\n const longPressEvents = useLongPress(() =>\n onShowTokenDetails(token.address, withoutContractAddress, token.chainId)\n )\n\n // The native token is badged ahead of any verdict: the provider screens\n // the native-address convention and calls it a scam on some chains.\n // A token the main list or the integrator vouches for needs no warning;\n // anything else without a clean verdict keeps the amber one.\n let verificationBadge:\n | { Icon: typeof VerifiedIcon; color: string; title: string }\n | undefined\n if (token.native) {\n verificationBadge = {\n Icon: VerifiedIcon,\n color: 'info.main',\n title: chainName\n ? t('tooltip.tokenNativeOnChain', {\n tokenSymbol: token.symbol,\n chainName,\n })\n : t('tooltip.tokenNative', { tokenSymbol: token.symbol }),\n }\n } else if (token.verificationStatus === 'flagged') {\n verificationBadge = {\n Icon: ReportRoundedIcon,\n color: 'error.main',\n title: t('warning.message.tokenFlagged', {\n tokenSymbol: token.symbol,\n }),\n }\n } else if (token.verificationStatus === 'verified') {\n const provider = getTokenVerificationProvider(token)\n verificationBadge = {\n Icon: VerifiedIcon,\n color: 'success.main',\n title: provider\n ? t('tooltip.tokenVerifiedByProvider', {\n tokenSymbol: token.symbol,\n provider,\n })\n : t('tooltip.tokenVerified', { tokenSymbol: token.symbol }),\n }\n } else if (\n token.verificationStatus === 'unverified' &&\n !token.listed &&\n !token.verified\n ) {\n verificationBadge = {\n Icon: ReportRoundedIcon,\n color: 'warning.main',\n title: t('warning.message.tokenUnverifiedByProvider', {\n tokenSymbol: token.symbol,\n }),\n }\n } else if (!token.verificationStatus && !token.listed && !token.verified) {\n verificationBadge = {\n Icon: ReportRoundedIcon,\n color: 'warning.main',\n title: t('warning.message.tokenUnverified'),\n }\n }\n\n return (\n <ListItemButton\n onClick={handleClick}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n {...longPressEvents}\n dense\n selected={selected}\n sx={{\n height: 60,\n marginBottom: '4px',\n // Not a flex-grow item: a band slot's spare height must not stretch the card.\n flex: 'none',\n }}\n >\n <ListItemAvatar>\n {chain ? (\n <TokenAvatar\n token={token as StaticToken}\n chain={chain}\n tokenAvatarSize={40}\n chainAvatarSize={16}\n />\n ) : (\n <TokenListItemAvatar token={token} />\n )}\n </ListItemAvatar>\n <ListItemText\n primary={\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>\n {token.symbol}\n {verificationBadge && (\n <Tooltip title={verificationBadge.title} placement=\"top\" arrow>\n <verificationBadge.Icon\n sx={{\n display: 'flex',\n fontSize: 16,\n color: verificationBadge.color,\n cursor: 'help',\n }}\n />\n </Tooltip>\n )}\n </Box>\n }\n slotProps={{\n secondary: {\n component: 'div',\n },\n }}\n secondary={\n withoutContractAddress ? (\n <Box\n ref={container}\n sx={{\n height: 20,\n display: 'flex',\n }}\n >\n <Box\n sx={{\n pt: 0.25,\n }}\n >\n {token.name}\n </Box>\n <Box\n sx={{\n position: 'relative',\n }}\n >\n <Slide\n direction=\"up\"\n in={showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n mountOnEnter\n >\n <Box>\n <OpenTokenDetailsButton\n tokenAddress={token.address}\n withoutContractAddress={withoutContractAddress}\n chainId={token.chainId}\n onClick={onShowTokenDetails}\n />\n <PinTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n {token.recent ? (\n <RemoveRecentTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n ) : null}\n </Box>\n </Slide>\n </Box>\n </Box>\n ) : (\n <Box\n ref={container}\n sx={{\n position: 'relative',\n height: 20,\n }}\n >\n <Slide\n direction=\"down\"\n in={!showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n >\n <Box\n sx={{\n pt: 0.25,\n }}\n >\n {token.name}\n </Box>\n </Slide>\n <Slide\n direction=\"up\"\n in={showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n mountOnEnter\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n gap: 0.5,\n }}\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n pt: 0.125,\n }}\n >\n {shortenAddress(token.address)}\n </Box>\n <Box>\n <OpenTokenDetailsButton\n tokenAddress={token.address}\n withoutContractAddress={withoutContractAddress}\n chainId={token.chainId}\n onClick={onShowTokenDetails}\n />\n <PinTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n {token.recent ? (\n <RemoveRecentTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n ) : null}\n </Box>\n </Box>\n </Slide>\n </Box>\n )\n }\n />\n {isBalanceLoading ? (\n <TokenAmountSkeleton />\n ) : (\n <Box sx={{ textAlign: 'right' }}>\n {token.amount ? (\n <Typography\n noWrap\n sx={{\n fontWeight: 600,\n }}\n title={tokenAmount}\n >\n {t('format.tokenAmount', {\n value: tokenAmount,\n })}\n </Typography>\n ) : null}\n {token.amount && token.priceUSD ? (\n <Typography\n data-price={token.priceUSD}\n sx={{\n fontWeight: 500,\n fontSize: 12,\n color: 'text.secondary',\n }}\n >\n {t('format.currency', {\n value: tokenPrice,\n })}\n </Typography>\n ) : null}\n </Box>\n )}\n </ListItemButton>\n )\n }\n)\n\nexport const TokenListItemSkeleton = (): JSX.Element => {\n return (\n <ListItem\n secondaryAction={<TokenAmountSkeleton />}\n disablePadding\n sx={(theme) => ({\n position: 'relative',\n flexDirection: 'row',\n alignItems: 'center',\n padding: 0,\n [`& .${listItemSecondaryActionClasses.root}`]: {\n right: theme.spacing(1.5),\n },\n })}\n >\n <ListItemAvatar>\n <Skeleton\n variant=\"circular\"\n width={40}\n height={40}\n sx={{ marginLeft: 1.5, marginRight: 2 }}\n />\n </ListItemAvatar>\n <ListItemText\n primary={<Skeleton variant=\"text\" width={56} height={24} />}\n secondary={<Skeleton variant=\"text\" width={96} height={16} />}\n />\n </ListItem>\n )\n}\n\nconst TokenAmountSkeleton: React.FC = () => {\n return (\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'flex-end',\n }}\n >\n <Skeleton variant=\"text\" width={56} height={24} />\n <Skeleton variant=\"text\" width={48} height={16} />\n </Box>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TokenListItem.js","names":["ListItem","IconButton","InfoOutlinedIcon","ListItemButton"],"sources":["../../../../src/components/TokenList/TokenListItem.tsx"],"sourcesContent":["import type { StaticToken } from '@lifi/sdk'\nimport { ChainType } from '@lifi/sdk'\nimport InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'\nimport {\n Avatar,\n Box,\n ListItemAvatar,\n ListItemText,\n listItemSecondaryActionClasses,\n Skeleton,\n Slide,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport type { JSX, MouseEventHandler } from 'react'\nimport { memo, useMemo, useRef, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { useLongPress } from '../../hooks/useLongPress.js'\nimport type { TokenAmount } from '../../types/token.js'\nimport { formatTokenAmount, formatTokenPrice } from '../../utils/format.js'\nimport {\n getTokenVerificationBadge,\n getTokenVerificationProvider,\n} from '../../utils/token.js'\nimport { shortenAddress } from '../../utils/wallet.js'\nimport { TokenAvatar } from '../Avatar/TokenAvatar.js'\nimport { ListItemButton } from '../ListItem/ListItemButton.js'\nimport { PinTokenButton } from './PinTokenButton.js'\nimport { RemoveRecentTokenButton } from './RemoveRecentTokenButton.js'\nimport { IconButton, ListItem } from './TokenList.style.js'\nimport { tokenVerificationIcons } from './tokenVerificationIcons.js'\nimport type {\n TokenListItemAvatarProps,\n TokenListItemButtonProps,\n TokenListItemProps,\n} from './types.js'\n\nexport const TokenListItem: React.FC<TokenListItemProps> = memo(\n ({\n onClick,\n size,\n start,\n token,\n chain,\n chainName,\n isBalanceLoading,\n startAdornment,\n endAdornment,\n selected,\n onShowTokenDetails,\n }) => {\n return (\n <ListItem\n style={{\n height: `${size}px`,\n transform: `translateY(${start}px)`,\n padding: 0,\n }}\n >\n {startAdornment}\n <TokenListItemButton\n token={token}\n isBalanceLoading={isBalanceLoading}\n onClick={onClick}\n chain={chain}\n chainName={chainName}\n selected={selected}\n onShowTokenDetails={onShowTokenDetails}\n />\n {endAdornment}\n </ListItem>\n )\n }\n)\n\nconst TokenListItemAvatar = memo(({ token }: TokenListItemAvatarProps) => {\n const [isImageLoading, setIsImageLoading] = useState(true)\n\n return (\n <Avatar\n src={token.logoURI}\n alt={token.symbol}\n sx={(theme) =>\n isImageLoading ? { bgcolor: theme.vars.palette.grey[300] } : null\n }\n onLoad={() => setIsImageLoading(false)}\n >\n {token.symbol?.[0]}\n </Avatar>\n )\n})\n\ninterface OpenTokenDetailsButtonProps {\n token: TokenAmount\n withoutContractAddress: boolean\n onClick: (token: TokenAmount, withoutContractAddress: boolean) => void\n}\n\nconst OpenTokenDetailsButton = ({\n token,\n withoutContractAddress,\n onClick,\n}: OpenTokenDetailsButtonProps) => {\n if (!token.address) {\n return null\n }\n return (\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation()\n e.currentTarget.blur() // Remove focus to prevent accessibility issues when opening drawer\n onClick(token, withoutContractAddress)\n }}\n >\n <InfoOutlinedIcon />\n </IconButton>\n )\n}\n\nconst TokenListItemButton: React.FC<TokenListItemButtonProps> = memo(\n ({\n onClick,\n token,\n chain,\n chainName,\n isBalanceLoading,\n selected,\n onShowTokenDetails,\n }) => {\n const { t } = useTranslation()\n const container = useRef(null)\n const timeoutId = useRef<ReturnType<typeof setTimeout>>(undefined)\n const [showAddress, setShowAddress] = useState(false)\n\n const withoutContractAddress = chain?.chainType === ChainType.UTXO\n\n const onMouseEnter = () => {\n timeoutId.current = setTimeout(() => {\n if (token.address) {\n setShowAddress(true)\n }\n }, 350)\n }\n\n const onMouseLeave = () => {\n clearTimeout(timeoutId.current)\n if (showAddress) {\n setShowAddress(false)\n }\n }\n\n const handleClick: MouseEventHandler<HTMLDivElement> = (e) => {\n e.stopPropagation()\n onClick?.(token.address, token.chainId)\n }\n\n const tokenAmount = useMemo(\n () => formatTokenAmount(token.amount, token.decimals),\n [token.amount, token.decimals]\n )\n\n const tokenPrice = useMemo(\n () => formatTokenPrice(token.amount, token.priceUSD, token.decimals),\n [token.amount, token.priceUSD, token.decimals]\n )\n\n const longPressEvents = useLongPress(() =>\n onShowTokenDetails(token, withoutContractAddress)\n )\n\n const verificationBadge = getTokenVerificationBadge(token)\n const verificationIcon = verificationBadge\n ? tokenVerificationIcons[verificationBadge]\n : undefined\n let verificationTitle = ''\n switch (verificationBadge) {\n case 'native':\n verificationTitle = chainName\n ? t('tooltip.tokenNativeOnChain', {\n tokenSymbol: token.symbol,\n chainName,\n })\n : t('tooltip.tokenNative', { tokenSymbol: token.symbol })\n break\n case 'flagged':\n verificationTitle = t('warning.message.tokenFlagged', {\n tokenSymbol: token.symbol,\n })\n break\n case 'verified': {\n const provider = getTokenVerificationProvider(token)\n verificationTitle = provider\n ? t('tooltip.tokenVerifiedByProvider', {\n tokenSymbol: token.symbol,\n provider,\n })\n : t('tooltip.tokenVerified', { tokenSymbol: token.symbol })\n break\n }\n case 'unverifiedByProvider':\n verificationTitle = t('warning.message.tokenUnverifiedByProvider', {\n tokenSymbol: token.symbol,\n })\n break\n case 'unverified':\n verificationTitle = t('warning.message.tokenUnverified')\n break\n }\n\n return (\n <ListItemButton\n onClick={handleClick}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n {...longPressEvents}\n dense\n selected={selected}\n sx={{\n height: 60,\n marginBottom: '4px',\n // Not a flex-grow item: a band slot's spare height must not stretch the card.\n flex: 'none',\n }}\n >\n <ListItemAvatar>\n {chain ? (\n <TokenAvatar\n token={token as StaticToken}\n chain={chain}\n tokenAvatarSize={40}\n chainAvatarSize={16}\n />\n ) : (\n <TokenListItemAvatar token={token} />\n )}\n </ListItemAvatar>\n <ListItemText\n primary={\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>\n {token.symbol}\n {verificationIcon && (\n <Tooltip title={verificationTitle} placement=\"top\" arrow>\n <verificationIcon.Icon\n sx={{\n display: 'flex',\n fontSize: 16,\n color: verificationIcon.color,\n cursor: 'help',\n }}\n />\n </Tooltip>\n )}\n </Box>\n }\n slotProps={{\n secondary: {\n component: 'div',\n },\n }}\n secondary={\n withoutContractAddress ? (\n <Box\n ref={container}\n sx={{\n height: 20,\n display: 'flex',\n }}\n >\n <Box\n sx={{\n pt: 0.25,\n }}\n >\n {token.name}\n </Box>\n <Box\n sx={{\n position: 'relative',\n }}\n >\n <Slide\n direction=\"up\"\n in={showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n mountOnEnter\n >\n <Box>\n <OpenTokenDetailsButton\n token={token}\n withoutContractAddress={withoutContractAddress}\n onClick={onShowTokenDetails}\n />\n <PinTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n {token.recent ? (\n <RemoveRecentTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n ) : null}\n </Box>\n </Slide>\n </Box>\n </Box>\n ) : (\n <Box\n ref={container}\n sx={{\n position: 'relative',\n height: 20,\n }}\n >\n <Slide\n direction=\"down\"\n in={!showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n >\n <Box\n sx={{\n pt: 0.25,\n }}\n >\n {token.name}\n </Box>\n </Slide>\n <Slide\n direction=\"up\"\n in={showAddress}\n container={container.current}\n style={{\n position: 'absolute',\n }}\n appear={false}\n mountOnEnter\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n gap: 0.5,\n }}\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n pt: 0.125,\n }}\n >\n {shortenAddress(token.address)}\n </Box>\n <Box>\n <OpenTokenDetailsButton\n token={token}\n withoutContractAddress={withoutContractAddress}\n onClick={onShowTokenDetails}\n />\n <PinTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n {token.recent ? (\n <RemoveRecentTokenButton\n chainId={token.chainId}\n tokenAddress={token.address}\n />\n ) : null}\n </Box>\n </Box>\n </Slide>\n </Box>\n )\n }\n />\n {isBalanceLoading ? (\n <TokenAmountSkeleton />\n ) : (\n <Box sx={{ textAlign: 'right' }}>\n {token.amount ? (\n <Typography\n noWrap\n sx={{\n fontWeight: 600,\n }}\n title={tokenAmount}\n >\n {t('format.tokenAmount', {\n value: tokenAmount,\n })}\n </Typography>\n ) : null}\n {token.amount && token.priceUSD ? (\n <Typography\n data-price={token.priceUSD}\n sx={{\n fontWeight: 500,\n fontSize: 12,\n color: 'text.secondary',\n }}\n >\n {t('format.currency', {\n value: tokenPrice,\n })}\n </Typography>\n ) : null}\n </Box>\n )}\n </ListItemButton>\n )\n }\n)\n\nexport const TokenListItemSkeleton = (): JSX.Element => {\n return (\n <ListItem\n secondaryAction={<TokenAmountSkeleton />}\n disablePadding\n sx={(theme) => ({\n position: 'relative',\n flexDirection: 'row',\n alignItems: 'center',\n padding: 0,\n [`& .${listItemSecondaryActionClasses.root}`]: {\n right: theme.spacing(1.5),\n },\n })}\n >\n <ListItemAvatar>\n <Skeleton\n variant=\"circular\"\n width={40}\n height={40}\n sx={{ marginLeft: 1.5, marginRight: 2 }}\n />\n </ListItemAvatar>\n <ListItemText\n primary={<Skeleton variant=\"text\" width={56} height={24} />}\n secondary={<Skeleton variant=\"text\" width={96} height={16} />}\n />\n </ListItem>\n )\n}\n\nconst TokenAmountSkeleton: React.FC = () => {\n return (\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'flex-end',\n }}\n >\n <Skeleton variant=\"text\" width={56} height={24} />\n <Skeleton variant=\"text\" width={48} height={16} />\n </Box>\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VirtualizedTokenList.d.ts","names":[],"sources":["../../../../src/components/TokenList/VirtualizedTokenList.tsx"],"mappings":";;;qBAqBa,sBAAsB,GAAG"}
1
+ {"version":3,"file":"VirtualizedTokenList.d.ts","names":[],"sources":["../../../../src/components/TokenList/VirtualizedTokenList.tsx"],"mappings":";;;qBAsBa,sBAAsB,GAAG"}
@@ -22,8 +22,8 @@ const VirtualizedTokenList = ({ tokens, scrollElementRef, chainId, selectedToken
22
22
  return new Map(chains.map((chain) => [chain.id, chain]));
23
23
  }, [chains]);
24
24
  const tokenDetailsSheetRef = useRef(null);
25
- const onShowTokenDetails = useCallback((tokenAddress, noContractAddress, chainId) => {
26
- tokenDetailsSheetRef.current?.open(tokenAddress, noContractAddress, chainId);
25
+ const onShowTokenDetails = useCallback((token, noContractAddress) => {
26
+ tokenDetailsSheetRef.current?.open(token, noContractAddress);
27
27
  }, []);
28
28
  const getItemKey = useCallback((index) => {
29
29
  const token = tokens[index];
@@ -1 +1 @@
1
- {"version":3,"file":"VirtualizedTokenList.js","names":["List"],"sources":["../../../../src/components/TokenList/VirtualizedTokenList.tsx"],"sourcesContent":["import { Typography } from '@mui/material'\nimport { useVirtualizer } from '@tanstack/react-virtual'\nimport type { FC } from 'react'\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { useAvailableChains } from '../../hooks/useAvailableChains.js'\nimport { getTokenKey } from '../../stores/recentTokens/utils.js'\nimport { isHoistableNative } from '../../utils/tokenList.js'\nimport { createBandResolver } from '../../utils/tokenListBands.js'\nimport { RecentTokensHeader } from './RecentTokensHeader.js'\nimport { RecentTokensToggle } from './RecentTokensToggle.js'\nimport { TokenDetailsSheet } from './TokenDetailsSheet.js'\nimport { List } from './TokenList.style.js'\nimport { TokenListItem, TokenListItemSkeleton } from './TokenListItem.js'\nimport type {\n TokenDetailsSheetBase,\n VirtualizedTokenListProps,\n} from './types.js'\n\nconst tokenItemHeight = 64 // 60 + 4px margin-bottom\n\nexport const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({\n tokens,\n scrollElementRef,\n chainId,\n selectedTokenAddress,\n isLoading,\n isBalanceLoading,\n showCategories,\n showPinnedTokens,\n onClick,\n isAllNetworks,\n recentStartIndex = 0,\n recentCount = 0,\n hiddenRecentCount = 0,\n recentExpanded = false,\n onToggleRecent,\n onClearRecent,\n nativeHoisted,\n}) => {\n const { t } = useTranslation()\n\n const { chains } = useAvailableChains()\n\n // Create Set for O(1) chain lookup instead of O(n) find\n const chainsSet = useMemo(() => {\n if (!chains) {\n return undefined\n }\n return new Map(chains.map((chain) => [chain.id, chain]))\n }, [chains])\n\n const tokenDetailsSheetRef = useRef<TokenDetailsSheetBase>(null)\n\n const onShowTokenDetails = useCallback(\n (tokenAddress: string, noContractAddress: boolean, chainId: number) => {\n tokenDetailsSheetRef.current?.open(\n tokenAddress,\n noContractAddress,\n chainId\n )\n },\n []\n )\n\n // Rows below the band keep their key as it changes size, so none remount.\n const getItemKey = useCallback(\n (index: number) => {\n const token = tokens[index]\n if (token.recent) {\n return `recent-${token.chainId}-${token.address}`\n }\n const shift = index >= recentStartIndex + recentCount ? recentCount : 0\n return `${token.chainId}-${token.address}-${index - shift}`\n },\n [tokens, recentStartIndex, recentCount]\n )\n\n const resolvedNativeHoisted = useMemo(\n () => nativeHoisted ?? isHoistableNative(tokens[0]),\n [nativeHoisted, tokens]\n )\n\n const showRecentToggle = recentCount > 0 && !!onToggleRecent\n\n // The band copy is on screen; its list row may be far below the fold.\n const bandKeys = useMemo(() => {\n if (!recentCount) {\n return undefined\n }\n const keys = new Set<string>()\n for (let i = recentStartIndex; i < recentStartIndex + recentCount; i++) {\n const token = tokens[i]\n if (token) {\n keys.add(getTokenKey(token.chainId, token.address))\n }\n }\n return keys\n }, [tokens, recentStartIndex, recentCount])\n\n const bands = useMemo(\n () =>\n createBandResolver(tokens, {\n showCategories: !!showCategories,\n showPinnedTokens: !!showPinnedTokens,\n nativeHoisted: resolvedNativeHoisted,\n recentStartIndex,\n recentCount,\n showRecentToggle,\n }),\n [\n tokens,\n showCategories,\n showPinnedTokens,\n resolvedNativeHoisted,\n recentStartIndex,\n recentCount,\n showRecentToggle,\n ]\n )\n\n const estimateSize = useCallback(\n (index: number) => tokenItemHeight + bands.getRowExtraHeight(index),\n [bands]\n )\n\n const virtualizerConfig = useMemo(\n () => ({\n count: tokens.length,\n overscan: 5,\n getScrollElement: () => scrollElementRef.current,\n estimateSize,\n getItemKey,\n paddingEnd: 8,\n }),\n [tokens.length, estimateSize, getItemKey, scrollElementRef]\n )\n\n const { getVirtualItems, getTotalSize, scrollToIndex, measure } =\n useVirtualizer(virtualizerConfig)\n\n // Address the issue of disappearing tokens on rerender\n useEffect(() => {\n if (scrollElementRef.current) {\n measure()\n }\n }, [measure, scrollElementRef.current])\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: run only when chainId changes\n useEffect(() => {\n // Scroll to the top of the list when switching the chains\n if (getVirtualItems().length) {\n scrollToIndex(0, { align: 'start' })\n }\n // Close the token details sheet when switching the chains\n tokenDetailsSheetRef.current?.close()\n }, [scrollToIndex, isAllNetworks, chainId, getVirtualItems])\n\n return (\n <>\n <List\n className=\"long-list\"\n style={{ height: getTotalSize() }}\n disablePadding\n >\n {getVirtualItems().map((item) => {\n const currentToken = tokens[item.index]\n const chain = chainsSet?.get(currentToken.chainId)\n const band = bands.getRowBandLabel(item.index)\n\n const isSelected =\n selectedTokenAddress === currentToken.address &&\n chainId === currentToken.chainId &&\n (!!currentToken.recent ||\n !bandKeys?.has(\n getTokenKey(currentToken.chainId, currentToken.address)\n ))\n\n return (\n <TokenListItem\n key={item.key}\n onClick={onClick}\n size={item.size}\n start={item.start}\n token={currentToken}\n chain={isAllNetworks ? chain : undefined}\n chainName={chain?.name}\n selected={isSelected}\n onShowTokenDetails={onShowTokenDetails}\n isBalanceLoading={isBalanceLoading}\n startAdornment={\n band?.kind === 'recent' ? (\n // Unconditional: the resolver always reserves this height.\n <RecentTokensHeader\n atListStart={band.atListStart}\n onClear={onClearRecent}\n />\n ) : band?.kind === 'text' ? (\n <Typography\n sx={{\n fontSize: 14,\n fontWeight: 600,\n lineHeight: '16px',\n px: 1.5,\n pt: band.atListStart ? 0 : 1,\n pb: 1,\n }}\n >\n {t(band.key)}\n </Typography>\n ) : null\n }\n endAdornment={\n onToggleRecent && bands.isToggleRow(item.index) ? (\n <RecentTokensToggle\n expanded={recentExpanded}\n hiddenCount={hiddenRecentCount}\n onToggle={onToggleRecent}\n />\n ) : null\n }\n />\n )\n })}\n </List>\n <TokenDetailsSheet ref={tokenDetailsSheetRef} />\n {isLoading && (\n <List disablePadding sx={{ cursor: 'default' }}>\n {Array.from({ length: 3 }).map((_, index) => (\n <TokenListItemSkeleton key={index} />\n ))}\n </List>\n )}\n </>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"VirtualizedTokenList.js","names":["List"],"sources":["../../../../src/components/TokenList/VirtualizedTokenList.tsx"],"sourcesContent":["import { Typography } from '@mui/material'\nimport { useVirtualizer } from '@tanstack/react-virtual'\nimport type { FC } from 'react'\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { useAvailableChains } from '../../hooks/useAvailableChains.js'\nimport { getTokenKey } from '../../stores/recentTokens/utils.js'\nimport type { TokenAmount } from '../../types/token.js'\nimport { isHoistableNative } from '../../utils/tokenList.js'\nimport { createBandResolver } from '../../utils/tokenListBands.js'\nimport { RecentTokensHeader } from './RecentTokensHeader.js'\nimport { RecentTokensToggle } from './RecentTokensToggle.js'\nimport { TokenDetailsSheet } from './TokenDetailsSheet.js'\nimport { List } from './TokenList.style.js'\nimport { TokenListItem, TokenListItemSkeleton } from './TokenListItem.js'\nimport type {\n TokenDetailsSheetBase,\n VirtualizedTokenListProps,\n} from './types.js'\n\nconst tokenItemHeight = 64 // 60 + 4px margin-bottom\n\nexport const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({\n tokens,\n scrollElementRef,\n chainId,\n selectedTokenAddress,\n isLoading,\n isBalanceLoading,\n showCategories,\n showPinnedTokens,\n onClick,\n isAllNetworks,\n recentStartIndex = 0,\n recentCount = 0,\n hiddenRecentCount = 0,\n recentExpanded = false,\n onToggleRecent,\n onClearRecent,\n nativeHoisted,\n}) => {\n const { t } = useTranslation()\n\n const { chains } = useAvailableChains()\n\n // Create Set for O(1) chain lookup instead of O(n) find\n const chainsSet = useMemo(() => {\n if (!chains) {\n return undefined\n }\n return new Map(chains.map((chain) => [chain.id, chain]))\n }, [chains])\n\n const tokenDetailsSheetRef = useRef<TokenDetailsSheetBase>(null)\n\n const onShowTokenDetails = useCallback(\n (token: TokenAmount, noContractAddress: boolean) => {\n tokenDetailsSheetRef.current?.open(token, noContractAddress)\n },\n []\n )\n\n // Rows below the band keep their key as it changes size, so none remount.\n const getItemKey = useCallback(\n (index: number) => {\n const token = tokens[index]\n if (token.recent) {\n return `recent-${token.chainId}-${token.address}`\n }\n const shift = index >= recentStartIndex + recentCount ? recentCount : 0\n return `${token.chainId}-${token.address}-${index - shift}`\n },\n [tokens, recentStartIndex, recentCount]\n )\n\n const resolvedNativeHoisted = useMemo(\n () => nativeHoisted ?? isHoistableNative(tokens[0]),\n [nativeHoisted, tokens]\n )\n\n const showRecentToggle = recentCount > 0 && !!onToggleRecent\n\n // The band copy is on screen; its list row may be far below the fold.\n const bandKeys = useMemo(() => {\n if (!recentCount) {\n return undefined\n }\n const keys = new Set<string>()\n for (let i = recentStartIndex; i < recentStartIndex + recentCount; i++) {\n const token = tokens[i]\n if (token) {\n keys.add(getTokenKey(token.chainId, token.address))\n }\n }\n return keys\n }, [tokens, recentStartIndex, recentCount])\n\n const bands = useMemo(\n () =>\n createBandResolver(tokens, {\n showCategories: !!showCategories,\n showPinnedTokens: !!showPinnedTokens,\n nativeHoisted: resolvedNativeHoisted,\n recentStartIndex,\n recentCount,\n showRecentToggle,\n }),\n [\n tokens,\n showCategories,\n showPinnedTokens,\n resolvedNativeHoisted,\n recentStartIndex,\n recentCount,\n showRecentToggle,\n ]\n )\n\n const estimateSize = useCallback(\n (index: number) => tokenItemHeight + bands.getRowExtraHeight(index),\n [bands]\n )\n\n const virtualizerConfig = useMemo(\n () => ({\n count: tokens.length,\n overscan: 5,\n getScrollElement: () => scrollElementRef.current,\n estimateSize,\n getItemKey,\n paddingEnd: 8,\n }),\n [tokens.length, estimateSize, getItemKey, scrollElementRef]\n )\n\n const { getVirtualItems, getTotalSize, scrollToIndex, measure } =\n useVirtualizer(virtualizerConfig)\n\n // Address the issue of disappearing tokens on rerender\n useEffect(() => {\n if (scrollElementRef.current) {\n measure()\n }\n }, [measure, scrollElementRef.current])\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: run only when chainId changes\n useEffect(() => {\n // Scroll to the top of the list when switching the chains\n if (getVirtualItems().length) {\n scrollToIndex(0, { align: 'start' })\n }\n // Close the token details sheet when switching the chains\n tokenDetailsSheetRef.current?.close()\n }, [scrollToIndex, isAllNetworks, chainId, getVirtualItems])\n\n return (\n <>\n <List\n className=\"long-list\"\n style={{ height: getTotalSize() }}\n disablePadding\n >\n {getVirtualItems().map((item) => {\n const currentToken = tokens[item.index]\n const chain = chainsSet?.get(currentToken.chainId)\n const band = bands.getRowBandLabel(item.index)\n\n const isSelected =\n selectedTokenAddress === currentToken.address &&\n chainId === currentToken.chainId &&\n (!!currentToken.recent ||\n !bandKeys?.has(\n getTokenKey(currentToken.chainId, currentToken.address)\n ))\n\n return (\n <TokenListItem\n key={item.key}\n onClick={onClick}\n size={item.size}\n start={item.start}\n token={currentToken}\n chain={isAllNetworks ? chain : undefined}\n chainName={chain?.name}\n selected={isSelected}\n onShowTokenDetails={onShowTokenDetails}\n isBalanceLoading={isBalanceLoading}\n startAdornment={\n band?.kind === 'recent' ? (\n // Unconditional: the resolver always reserves this height.\n <RecentTokensHeader\n atListStart={band.atListStart}\n onClear={onClearRecent}\n />\n ) : band?.kind === 'text' ? (\n <Typography\n sx={{\n fontSize: 14,\n fontWeight: 600,\n lineHeight: '16px',\n px: 1.5,\n pt: band.atListStart ? 0 : 1,\n pb: 1,\n }}\n >\n {t(band.key)}\n </Typography>\n ) : null\n }\n endAdornment={\n onToggleRecent && bands.isToggleRow(item.index) ? (\n <RecentTokensToggle\n expanded={recentExpanded}\n hiddenCount={hiddenRecentCount}\n onToggle={onToggleRecent}\n />\n ) : null\n }\n />\n )\n })}\n </List>\n <TokenDetailsSheet ref={tokenDetailsSheetRef} />\n {isLoading && (\n <List disablePadding sx={{ cursor: 'default' }}>\n {Array.from({ length: 3 }).map((_, index) => (\n <TokenListItemSkeleton key={index} />\n ))}\n </List>\n )}\n </>\n )\n}\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import ReportRoundedIcon from "@mui/icons-material/ReportRounded";
2
+ import VerifiedIcon from "@mui/icons-material/Verified";
3
+ //#region src/components/TokenList/tokenVerificationIcons.ts
4
+ const tokenVerificationIcons = {
5
+ native: {
6
+ Icon: VerifiedIcon,
7
+ color: "info.main"
8
+ },
9
+ flagged: {
10
+ Icon: ReportRoundedIcon,
11
+ color: "error.main"
12
+ },
13
+ verified: {
14
+ Icon: VerifiedIcon,
15
+ color: "success.main"
16
+ },
17
+ unverifiedByProvider: {
18
+ Icon: ReportRoundedIcon,
19
+ color: "warning.main"
20
+ },
21
+ unverified: {
22
+ Icon: ReportRoundedIcon,
23
+ color: "warning.main"
24
+ }
25
+ };
26
+ //#endregion
27
+ export { tokenVerificationIcons };
28
+
29
+ //# sourceMappingURL=tokenVerificationIcons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenVerificationIcons.js","names":[],"sources":["../../../../src/components/TokenList/tokenVerificationIcons.ts"],"sourcesContent":["import ReportRoundedIcon from '@mui/icons-material/ReportRounded'\nimport VerifiedIcon from '@mui/icons-material/Verified'\nimport type { SvgIcon } from '@mui/material'\nimport type { TokenVerificationBadge } from '../../utils/token.js'\n\nexport const tokenVerificationIcons: Record<\n TokenVerificationBadge,\n { Icon: typeof SvgIcon; color: string }\n> = {\n native: { Icon: VerifiedIcon, color: 'info.main' },\n flagged: { Icon: ReportRoundedIcon, color: 'error.main' },\n verified: { Icon: VerifiedIcon, color: 'success.main' },\n unverifiedByProvider: { Icon: ReportRoundedIcon, color: 'warning.main' },\n unverified: { Icon: ReportRoundedIcon, color: 'warning.main' },\n}\n"],"mappings":";;;AAKA,MAAa,yBAGT;CACF,QAAQ;EAAE,MAAM;EAAc,OAAO;CAAY;CACjD,SAAS;EAAE,MAAM;EAAmB,OAAO;CAAa;CACxD,UAAU;EAAE,MAAM;EAAc,OAAO;CAAe;CACtD,sBAAsB;EAAE,MAAM;EAAmB,OAAO;CAAe;CACvE,YAAY;EAAE,MAAM;EAAmB,OAAO;CAAe;AAC/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"useTokenSelect.d.ts","names":[],"sources":["../../../../src/components/TokenList/useTokenSelect.ts"],"mappings":";;qBAaa,iBACX,UAAU,UACV,2BACG,sBAAsB"}
1
+ {"version":3,"file":"useTokenSelect.d.ts","names":[],"sources":["../../../../src/components/TokenList/useTokenSelect.ts"],"mappings":";;qBAea,iBACX,UAAU,UACV,2BACG,sBAAsB"}
@@ -3,7 +3,9 @@ import "../../types/events.js";
3
3
  import { isItemAllowed } from "../../utils/item.js";
4
4
  import { useWidgetConfig } from "../../providers/WidgetProvider/WidgetProvider.js";
5
5
  import { useSplitMode } from "../../stores/navigationTabs/useNavigationTabsStore.js";
6
+ import { useAvailableChains } from "../../hooks/useAvailableChains.js";
6
7
  import { useFieldActions } from "../../stores/form/useFieldActions.js";
8
+ import { useToAddressReset } from "../../hooks/useToAddressReset.js";
7
9
  import { useChainOrderStore } from "../../stores/chains/ChainOrderStore.js";
8
10
  import { FormKeyHelper } from "../../stores/form/types.js";
9
11
  import { useToAddressAutoPopulate } from "../../hooks/useToAddressAutoPopulate.js";
@@ -15,9 +17,12 @@ const useTokenSelect = (formType, onClick) => {
15
17
  const emitter = useWidgetEvents();
16
18
  const { setFieldValue, getFieldValues } = useFieldActions();
17
19
  const autoPopulateToAddress = useToAddressAutoPopulate();
20
+ const { tryResetToAddress } = useToAddressReset();
21
+ const { getChainById } = useAvailableChains();
18
22
  const [setChain, setIsAllNetworks] = useChainOrderStore((state) => [state.setChain, state.setIsAllNetworks]);
19
23
  const tokenKey = FormKeyHelper.getTokenKey(formType);
20
24
  return useCallback((tokenAddress, chainId) => {
25
+ const [toChainIdBefore] = getFieldValues("toChain");
21
26
  setFieldValue(tokenKey, tokenAddress, {
22
27
  isDirty: true,
23
28
  isTouched: true
@@ -43,6 +48,9 @@ const useTokenSelect = (formType, onClick) => {
43
48
  });
44
49
  setChain(selectedChainId, oppositeFormType);
45
50
  }
51
+ const [toChainId] = getFieldValues("toChain");
52
+ const toChain = getChainById(toChainId);
53
+ if (toChainId !== toChainIdBefore && toChain) tryResetToAddress(toChain);
46
54
  autoPopulateToAddress({
47
55
  formType,
48
56
  selectedToAddress,
@@ -58,6 +66,8 @@ const useTokenSelect = (formType, onClick) => {
58
66
  onClick?.();
59
67
  }, [
60
68
  autoPopulateToAddress,
69
+ tryResetToAddress,
70
+ getChainById,
61
71
  disabledUI,
62
72
  emitter,
63
73
  formType,
@@ -1 +1 @@
1
- {"version":3,"file":"useTokenSelect.js","names":[],"sources":["../../../../src/components/TokenList/useTokenSelect.ts"],"sourcesContent":["import { useCallback } from 'react'\nimport { useToAddressAutoPopulate } from '../../hooks/useToAddressAutoPopulate.js'\nimport { useWidgetEvents } from '../../hooks/useWidgetEvents.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { useChainOrderStore } from '../../stores/chains/ChainOrderStore.js'\nimport type { FormType } from '../../stores/form/types.js'\nimport { FormKeyHelper } from '../../stores/form/types.js'\nimport { useFieldActions } from '../../stores/form/useFieldActions.js'\nimport { useSplitMode } from '../../stores/navigationTabs/useNavigationTabsStore.js'\nimport { WidgetEvent } from '../../types/events.js'\nimport type { DisabledUIConfig } from '../../types/widget.js'\nimport { isItemAllowed } from '../../utils/item.js'\n\nexport const useTokenSelect = (\n formType: FormType,\n onClick?: () => void\n): ((tokenAddress: string, chainId?: number) => void) => {\n const { mode, disabledUI, chains: chainsConfig } = useWidgetConfig()\n const splitMode = useSplitMode()\n const emitter = useWidgetEvents()\n const { setFieldValue, getFieldValues } = useFieldActions()\n const autoPopulateToAddress = useToAddressAutoPopulate()\n const [setChain, setIsAllNetworks] = useChainOrderStore((state) => [\n state.setChain,\n state.setIsAllNetworks,\n ])\n\n const tokenKey = FormKeyHelper.getTokenKey(formType)\n\n return useCallback(\n (tokenAddress: string, chainId?: number) => {\n setFieldValue(tokenKey, tokenAddress, { isDirty: true, isTouched: true })\n const selectedChainId =\n chainId ?? getFieldValues(FormKeyHelper.getChainKey(formType))[0]\n // Set chain again to trigger URL builder update\n setFieldValue(FormKeyHelper.getChainKey(formType), selectedChainId, {\n isDirty: true,\n isTouched: true,\n })\n const amountKey = FormKeyHelper.getAmountKey(formType)\n if (!disabledUI?.[amountKey as keyof DisabledUIConfig]) {\n setFieldValue(amountKey, '')\n }\n const oppositeFormType = formType === 'from' ? 'to' : 'from'\n const [\n selectedOppositeTokenAddress,\n selectedOppositeChainId,\n selectedToAddress,\n ] = getFieldValues(\n FormKeyHelper.getTokenKey(oppositeFormType),\n FormKeyHelper.getChainKey(oppositeFormType),\n 'toAddress'\n )\n\n // TODO: remove when we enable same chain/token transfers\n const isSameTokenTransfer =\n selectedOppositeTokenAddress === tokenAddress &&\n selectedOppositeChainId === selectedChainId\n\n const isBridgeToSameChain =\n mode === 'split' &&\n splitMode === 'bridge' &&\n selectedOppositeChainId === selectedChainId\n\n if ((isSameTokenTransfer || isBridgeToSameChain) && mode !== 'custom') {\n setFieldValue(FormKeyHelper.getTokenKey(oppositeFormType), '', {\n isDirty: true,\n isTouched: true,\n })\n }\n\n // If no opposite token is selected, synchronize the opposite chain\n // to match the currently selected chain (if allowed).\n // In default exchange mode, also collapse \"All Networks\" on the opposite\n // side - same-chain swap is the most common action.\n if (\n !selectedOppositeTokenAddress &&\n selectedChainId &&\n isItemAllowed(selectedChainId, chainsConfig?.[oppositeFormType])\n ) {\n const isDefaultExchange = !mode || mode === 'default'\n if (isDefaultExchange) {\n setIsAllNetworks(false, oppositeFormType)\n }\n setFieldValue(\n FormKeyHelper.getChainKey(oppositeFormType),\n selectedChainId,\n {\n isDirty: true,\n isTouched: true,\n }\n )\n setChain(selectedChainId, oppositeFormType)\n }\n\n // Automatically populate toAddress field if bridging across ecosystems and compatible wallet is connected\n autoPopulateToAddress({\n formType,\n selectedToAddress,\n selectedChainId,\n selectedOppositeChainId,\n selectedOppositeTokenAddress,\n })\n\n const eventToEmit =\n formType === 'from'\n ? WidgetEvent.SourceChainTokenSelected\n : WidgetEvent.DestinationChainTokenSelected\n\n if (selectedChainId) {\n emitter.emit(eventToEmit, {\n chainId: selectedChainId,\n tokenAddress,\n })\n }\n\n onClick?.()\n },\n [\n autoPopulateToAddress,\n disabledUI,\n emitter,\n formType,\n getFieldValues,\n onClick,\n setChain,\n setIsAllNetworks,\n setFieldValue,\n mode,\n splitMode,\n tokenKey,\n chainsConfig,\n ]\n )\n}\n"],"mappings":";;;;;;;;;;;AAaA,MAAa,kBACX,UACA,YACuD;CACvD,MAAM,EAAE,MAAM,YAAY,QAAQ,iBAAiB,gBAAgB;CACnE,MAAM,YAAY,aAAa;CAC/B,MAAM,UAAU,gBAAgB;CAChC,MAAM,EAAE,eAAe,mBAAmB,gBAAgB;CAC1D,MAAM,wBAAwB,yBAAyB;CACvD,MAAM,CAAC,UAAU,oBAAoB,oBAAoB,UAAU,CACjE,MAAM,UACN,MAAM,gBACR,CAAC;CAED,MAAM,WAAW,cAAc,YAAY,QAAQ;CAEnD,OAAO,aACJ,cAAsB,YAAqB;EAC1C,cAAc,UAAU,cAAc;GAAE,SAAS;GAAM,WAAW;EAAK,CAAC;EACxE,MAAM,kBACJ,WAAW,eAAe,cAAc,YAAY,QAAQ,CAAC,CAAC,CAAC;EAEjE,cAAc,cAAc,YAAY,QAAQ,GAAG,iBAAiB;GAClE,SAAS;GACT,WAAW;EACb,CAAC;EACD,MAAM,YAAY,cAAc,aAAa,QAAQ;EACrD,IAAI,CAAC,aAAa,YAChB,cAAc,WAAW,EAAE;EAE7B,MAAM,mBAAmB,aAAa,SAAS,OAAO;EACtD,MAAM,CACJ,8BACA,yBACA,qBACE,eACF,cAAc,YAAY,gBAAgB,GAC1C,cAAc,YAAY,gBAAgB,GAC1C,WACF;EAYA,KARE,iCAAiC,gBACjC,4BAA4B,mBAG5B,SAAS,WACT,cAAc,YACd,4BAA4B,oBAEsB,SAAS,UAC3D,cAAc,cAAc,YAAY,gBAAgB,GAAG,IAAI;GAC7D,SAAS;GACT,WAAW;EACb,CAAC;EAOH,IACE,CAAC,gCACD,mBACA,cAAc,iBAAiB,eAAe,iBAAiB,GAC/D;GAEA,IAD0B,CAAC,QAAQ,SAAS,WAE1C,iBAAiB,OAAO,gBAAgB;GAE1C,cACE,cAAc,YAAY,gBAAgB,GAC1C,iBACA;IACE,SAAS;IACT,WAAW;GACb,CACF;GACA,SAAS,iBAAiB,gBAAgB;EAC5C;EAGA,sBAAsB;GACpB;GACA;GACA;GACA;GACA;EACF,CAAC;EAED,MAAM,cACJ,aAAa,SAAA,6BAAA;EAIf,IAAI,iBACF,QAAQ,KAAK,aAAa;GACxB,SAAS;GACT;EACF,CAAC;EAGH,UAAU;CACZ,GACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;AACF"}
1
+ {"version":3,"file":"useTokenSelect.js","names":[],"sources":["../../../../src/components/TokenList/useTokenSelect.ts"],"sourcesContent":["import { useCallback } from 'react'\nimport { useAvailableChains } from '../../hooks/useAvailableChains.js'\nimport { useToAddressAutoPopulate } from '../../hooks/useToAddressAutoPopulate.js'\nimport { useToAddressReset } from '../../hooks/useToAddressReset.js'\nimport { useWidgetEvents } from '../../hooks/useWidgetEvents.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { useChainOrderStore } from '../../stores/chains/ChainOrderStore.js'\nimport type { FormType } from '../../stores/form/types.js'\nimport { FormKeyHelper } from '../../stores/form/types.js'\nimport { useFieldActions } from '../../stores/form/useFieldActions.js'\nimport { useSplitMode } from '../../stores/navigationTabs/useNavigationTabsStore.js'\nimport { WidgetEvent } from '../../types/events.js'\nimport type { DisabledUIConfig } from '../../types/widget.js'\nimport { isItemAllowed } from '../../utils/item.js'\n\nexport const useTokenSelect = (\n formType: FormType,\n onClick?: () => void\n): ((tokenAddress: string, chainId?: number) => void) => {\n const { mode, disabledUI, chains: chainsConfig } = useWidgetConfig()\n const splitMode = useSplitMode()\n const emitter = useWidgetEvents()\n const { setFieldValue, getFieldValues } = useFieldActions()\n const autoPopulateToAddress = useToAddressAutoPopulate()\n const { tryResetToAddress } = useToAddressReset()\n const { getChainById } = useAvailableChains()\n const [setChain, setIsAllNetworks] = useChainOrderStore((state) => [\n state.setChain,\n state.setIsAllNetworks,\n ])\n\n const tokenKey = FormKeyHelper.getTokenKey(formType)\n\n return useCallback(\n (tokenAddress: string, chainId?: number) => {\n const [toChainIdBefore] = getFieldValues('toChain')\n setFieldValue(tokenKey, tokenAddress, { isDirty: true, isTouched: true })\n const selectedChainId =\n chainId ?? getFieldValues(FormKeyHelper.getChainKey(formType))[0]\n // Set chain again to trigger URL builder update\n setFieldValue(FormKeyHelper.getChainKey(formType), selectedChainId, {\n isDirty: true,\n isTouched: true,\n })\n const amountKey = FormKeyHelper.getAmountKey(formType)\n if (!disabledUI?.[amountKey as keyof DisabledUIConfig]) {\n setFieldValue(amountKey, '')\n }\n const oppositeFormType = formType === 'from' ? 'to' : 'from'\n const [\n selectedOppositeTokenAddress,\n selectedOppositeChainId,\n selectedToAddress,\n ] = getFieldValues(\n FormKeyHelper.getTokenKey(oppositeFormType),\n FormKeyHelper.getChainKey(oppositeFormType),\n 'toAddress'\n )\n\n // TODO: remove when we enable same chain/token transfers\n const isSameTokenTransfer =\n selectedOppositeTokenAddress === tokenAddress &&\n selectedOppositeChainId === selectedChainId\n\n const isBridgeToSameChain =\n mode === 'split' &&\n splitMode === 'bridge' &&\n selectedOppositeChainId === selectedChainId\n\n if ((isSameTokenTransfer || isBridgeToSameChain) && mode !== 'custom') {\n setFieldValue(FormKeyHelper.getTokenKey(oppositeFormType), '', {\n isDirty: true,\n isTouched: true,\n })\n }\n\n // If no opposite token is selected, synchronize the opposite chain\n // to match the currently selected chain (if allowed).\n // In default exchange mode, also collapse \"All Networks\" on the opposite\n // side - same-chain swap is the most common action.\n if (\n !selectedOppositeTokenAddress &&\n selectedChainId &&\n isItemAllowed(selectedChainId, chainsConfig?.[oppositeFormType])\n ) {\n const isDefaultExchange = !mode || mode === 'default'\n if (isDefaultExchange) {\n setIsAllNetworks(false, oppositeFormType)\n }\n setFieldValue(\n FormKeyHelper.getChainKey(oppositeFormType),\n selectedChainId,\n {\n isDirty: true,\n isTouched: true,\n }\n )\n setChain(selectedChainId, oppositeFormType)\n }\n\n // A token from \"All networks\" moves the destination without the chain select,\n // which resets a receiver the new chain cannot take.\n const [toChainId] = getFieldValues('toChain')\n const toChain = getChainById(toChainId)\n if (toChainId !== toChainIdBefore && toChain) {\n tryResetToAddress(toChain)\n }\n\n // Automatically populate toAddress field if bridging across ecosystems and compatible wallet is connected\n autoPopulateToAddress({\n formType,\n selectedToAddress,\n selectedChainId,\n selectedOppositeChainId,\n selectedOppositeTokenAddress,\n })\n\n const eventToEmit =\n formType === 'from'\n ? WidgetEvent.SourceChainTokenSelected\n : WidgetEvent.DestinationChainTokenSelected\n\n if (selectedChainId) {\n emitter.emit(eventToEmit, {\n chainId: selectedChainId,\n tokenAddress,\n })\n }\n\n onClick?.()\n },\n [\n autoPopulateToAddress,\n tryResetToAddress,\n getChainById,\n disabledUI,\n emitter,\n formType,\n getFieldValues,\n onClick,\n setChain,\n setIsAllNetworks,\n setFieldValue,\n mode,\n splitMode,\n tokenKey,\n chainsConfig,\n ]\n )\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,MAAa,kBACX,UACA,YACuD;CACvD,MAAM,EAAE,MAAM,YAAY,QAAQ,iBAAiB,gBAAgB;CACnE,MAAM,YAAY,aAAa;CAC/B,MAAM,UAAU,gBAAgB;CAChC,MAAM,EAAE,eAAe,mBAAmB,gBAAgB;CAC1D,MAAM,wBAAwB,yBAAyB;CACvD,MAAM,EAAE,sBAAsB,kBAAkB;CAChD,MAAM,EAAE,iBAAiB,mBAAmB;CAC5C,MAAM,CAAC,UAAU,oBAAoB,oBAAoB,UAAU,CACjE,MAAM,UACN,MAAM,gBACR,CAAC;CAED,MAAM,WAAW,cAAc,YAAY,QAAQ;CAEnD,OAAO,aACJ,cAAsB,YAAqB;EAC1C,MAAM,CAAC,mBAAmB,eAAe,SAAS;EAClD,cAAc,UAAU,cAAc;GAAE,SAAS;GAAM,WAAW;EAAK,CAAC;EACxE,MAAM,kBACJ,WAAW,eAAe,cAAc,YAAY,QAAQ,CAAC,CAAC,CAAC;EAEjE,cAAc,cAAc,YAAY,QAAQ,GAAG,iBAAiB;GAClE,SAAS;GACT,WAAW;EACb,CAAC;EACD,MAAM,YAAY,cAAc,aAAa,QAAQ;EACrD,IAAI,CAAC,aAAa,YAChB,cAAc,WAAW,EAAE;EAE7B,MAAM,mBAAmB,aAAa,SAAS,OAAO;EACtD,MAAM,CACJ,8BACA,yBACA,qBACE,eACF,cAAc,YAAY,gBAAgB,GAC1C,cAAc,YAAY,gBAAgB,GAC1C,WACF;EAYA,KARE,iCAAiC,gBACjC,4BAA4B,mBAG5B,SAAS,WACT,cAAc,YACd,4BAA4B,oBAEsB,SAAS,UAC3D,cAAc,cAAc,YAAY,gBAAgB,GAAG,IAAI;GAC7D,SAAS;GACT,WAAW;EACb,CAAC;EAOH,IACE,CAAC,gCACD,mBACA,cAAc,iBAAiB,eAAe,iBAAiB,GAC/D;GAEA,IAD0B,CAAC,QAAQ,SAAS,WAE1C,iBAAiB,OAAO,gBAAgB;GAE1C,cACE,cAAc,YAAY,gBAAgB,GAC1C,iBACA;IACE,SAAS;IACT,WAAW;GACb,CACF;GACA,SAAS,iBAAiB,gBAAgB;EAC5C;EAIA,MAAM,CAAC,aAAa,eAAe,SAAS;EAC5C,MAAM,UAAU,aAAa,SAAS;EACtC,IAAI,cAAc,mBAAmB,SACnC,kBAAkB,OAAO;EAI3B,sBAAsB;GACpB;GACA;GACA;GACA;GACA;EACF,CAAC;EAED,MAAM,cACJ,aAAa,SAAA,6BAAA;EAIf,IAAI,iBACF,QAAQ,KAAK,aAAa;GACxB,SAAS;GACT;EACF,CAAC;EAGH,UAAU;CACZ,GACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","names":[],"sources":["../../../src/config/constants.ts"],"sourcesContent":["export const defaultMaxHeight = 686\n\nexport const internalExplorerUrl = 'https://scan.li.fi'\n"],"mappings":";AAAA,MAAa,mBAAmB;AAEhC,MAAa,sBAAsB"}
1
+ {"version":3,"file":"constants.js","names":[],"sources":["../../../src/config/constants.ts"],"sourcesContent":["export const defaultMaxHeight = 686\n\nexport const internalExplorerUrl = 'https://scan.li.fi'\n\n// Fits the longest Zcash unified address in the official test vectors (509 characters).\nexport const addressInputMaxLength = 512\n"],"mappings":";AAAA,MAAa,mBAAmB;AAEhC,MAAa,sBAAsB"}
@@ -1,5 +1,5 @@
1
1
  //#region src/config/version.d.ts
2
2
  export declare const name = "@lifi/widget";
3
- export declare const version = "4.8.0";
3
+ export declare const version = "4.10.0";
4
4
  //#endregion
5
5
  //# sourceMappingURL=version.d.ts.map
@@ -1,6 +1,6 @@
1
1
  //#region src/config/version.ts
2
2
  const name = "@lifi/widget";
3
- const version = "4.8.0";
3
+ const version = "4.10.0";
4
4
  //#endregion
5
5
  export { name, version };
6
6
 
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","names":[],"sources":["../../../src/config/version.ts"],"sourcesContent":["export const name = '@lifi/widget'\nexport const version = '4.8.0'\n"],"mappings":";AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
1
+ {"version":3,"file":"version.js","names":[],"sources":["../../../src/config/version.ts"],"sourcesContent":["export const name = '@lifi/widget'\nexport const version = '4.10.0'\n"],"mappings":";AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
@@ -1,5 +1,6 @@
1
1
  import { useChains } from "./useChains.js";
2
2
  import { useMemo } from "react";
3
+ import { useAddressForChain } from "@lifi/widget-provider";
3
4
  import { useAccount } from "@lifi/wallet-management";
4
5
  //#region src/hooks/useAccountsBalancesData.ts
5
6
  const useAccountsBalancesData = (selectedChainId, formType, isAllNetworks, allTokens) => {
@@ -19,6 +20,7 @@ const useAccountsBalancesData = (selectedChainId, formType, isAllNetworks, allTo
19
20
  currentChain
20
21
  ]);
21
22
  const { accounts: allAccounts, account: currentAccount } = useAccount(isAllNetworks ? void 0 : { chainType: currentChain?.chainType });
23
+ const { isAddressForChain } = useAddressForChain();
22
24
  const accounts = useMemo(() => {
23
25
  return isAllNetworks ? allAccounts : currentAccount ? [currentAccount] : void 0;
24
26
  }, [
@@ -31,7 +33,7 @@ const useAccountsBalancesData = (selectedChainId, formType, isAllNetworks, allTo
31
33
  if (!chains || !allTokens || !accounts?.length) return;
32
34
  return accounts?.filter((account) => account.address).reduce((acc, account) => {
33
35
  if (account.address) {
34
- const accountChains = chains?.filter((chain) => account.chainType === chain?.chainType);
36
+ const accountChains = chains?.filter((chain) => account.chainType === chain?.chainType && isAddressForChain(account.address, chain));
35
37
  if (accountChains) {
36
38
  const chainIdSet = new Set(accountChains.map((chain) => chain.id));
37
39
  acc[account.address] = Object.entries(allTokens).reduce((tokenAcc, [chainIdStr, tokens]) => {
@@ -46,7 +48,8 @@ const useAccountsBalancesData = (selectedChainId, formType, isAllNetworks, allTo
46
48
  }, [
47
49
  accounts,
48
50
  chains,
49
- allTokens
51
+ allTokens,
52
+ isAddressForChain
50
53
  ]),
51
54
  isLoading: isChainsLoading
52
55
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useAccountsBalancesData.js","names":[],"sources":["../../../src/hooks/useAccountsBalancesData.ts"],"sourcesContent":["import type { TokenExtended } from '@lifi/sdk'\nimport { useAccount } from '@lifi/wallet-management'\nimport { useMemo } from 'react'\nimport type { FormType } from '../stores/form/types.js'\nimport { useChains } from './useChains.js'\n\nexport const useAccountsBalancesData = (\n selectedChainId?: number,\n formType?: FormType,\n isAllNetworks?: boolean,\n allTokens?: Record<number, TokenExtended[]>\n): {\n data: Record<string, Record<number, TokenExtended[]>> | undefined\n isLoading: boolean\n} => {\n const {\n chains: allChains,\n isLoading: isChainsLoading,\n getChainById,\n } = useChains(formType)\n const currentChain = useMemo(() => {\n return selectedChainId\n ? getChainById(selectedChainId, allChains)\n : undefined\n }, [selectedChainId, allChains, getChainById])\n const chains = useMemo(() => {\n return isAllNetworks ? allChains : currentChain ? [currentChain] : undefined\n }, [allChains, isAllNetworks, currentChain])\n\n const { accounts: allAccounts, account: currentAccount } = useAccount(\n isAllNetworks ? undefined : { chainType: currentChain?.chainType }\n )\n const accounts = useMemo(() => {\n return isAllNetworks\n ? allAccounts\n : currentAccount\n ? [currentAccount]\n : undefined\n }, [allAccounts, currentAccount, isAllNetworks])\n\n const accountsWithTokens = useMemo(() => {\n if (!chains || !allTokens || !accounts?.length) {\n return undefined\n }\n return accounts\n ?.filter((account) => account.address)\n .reduce(\n (acc, account) => {\n if (account.address) {\n const accountChains = chains?.filter(\n (chain) => account.chainType === chain?.chainType\n )\n if (accountChains) {\n const chainIdSet = new Set(accountChains.map((chain) => chain.id))\n acc[account.address] = Object.entries(allTokens).reduce(\n (tokenAcc, [chainIdStr, tokens]) => {\n const chainId = Number(chainIdStr)\n if (chainIdSet.has(chainId)) {\n tokenAcc[chainId] = tokens\n }\n return tokenAcc\n },\n {} as Record<number, TokenExtended[]>\n )\n }\n }\n return acc\n },\n {} as Record<string, Record<number, TokenExtended[]>>\n )\n }, [accounts, chains, allTokens])\n\n return {\n data: accountsWithTokens,\n isLoading: isChainsLoading,\n }\n}\n"],"mappings":";;;;AAMA,MAAa,2BACX,iBACA,UACA,eACA,cAIG;CACH,MAAM,EACJ,QAAQ,WACR,WAAW,iBACX,iBACE,UAAU,QAAQ;CACtB,MAAM,eAAe,cAAc;EACjC,OAAO,kBACH,aAAa,iBAAiB,SAAS,IACvC,KAAA;CACN,GAAG;EAAC;EAAiB;EAAW;CAAY,CAAC;CAC7C,MAAM,SAAS,cAAc;EAC3B,OAAO,gBAAgB,YAAY,eAAe,CAAC,YAAY,IAAI,KAAA;CACrE,GAAG;EAAC;EAAW;EAAe;CAAY,CAAC;CAE3C,MAAM,EAAE,UAAU,aAAa,SAAS,mBAAmB,WACzD,gBAAgB,KAAA,IAAY,EAAE,WAAW,cAAc,UAAU,CACnE;CACA,MAAM,WAAW,cAAc;EAC7B,OAAO,gBACH,cACA,iBACE,CAAC,cAAc,IACf,KAAA;CACR,GAAG;EAAC;EAAa;EAAgB;CAAa,CAAC;CAkC/C,OAAO;EACL,MAjCyB,cAAc;GACvC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,QACtC;GAEF,OAAO,UACH,QAAQ,YAAY,QAAQ,OAAO,CAAC,CACrC,QACE,KAAK,YAAY;IAChB,IAAI,QAAQ,SAAS;KACnB,MAAM,gBAAgB,QAAQ,QAC3B,UAAU,QAAQ,cAAc,OAAO,SAC1C;KACA,IAAI,eAAe;MACjB,MAAM,aAAa,IAAI,IAAI,cAAc,KAAK,UAAU,MAAM,EAAE,CAAC;MACjE,IAAI,QAAQ,WAAW,OAAO,QAAQ,SAAS,CAAC,CAAC,QAC9C,UAAU,CAAC,YAAY,YAAY;OAClC,MAAM,UAAU,OAAO,UAAU;OACjC,IAAI,WAAW,IAAI,OAAO,GACxB,SAAS,WAAW;OAEtB,OAAO;MACT,GACA,CAAC,CACH;KACF;IACF;IACA,OAAO;GACT,GACA,CAAC,CACH;EACJ,GAAG;GAAC;GAAU;GAAQ;EAAS,CAGN;EACvB,WAAW;CACb;AACF"}
1
+ {"version":3,"file":"useAccountsBalancesData.js","names":[],"sources":["../../../src/hooks/useAccountsBalancesData.ts"],"sourcesContent":["import type { TokenExtended } from '@lifi/sdk'\nimport { useAccount } from '@lifi/wallet-management'\nimport { useAddressForChain } from '@lifi/widget-provider'\nimport { useMemo } from 'react'\nimport type { FormType } from '../stores/form/types.js'\nimport { useChains } from './useChains.js'\n\nexport const useAccountsBalancesData = (\n selectedChainId?: number,\n formType?: FormType,\n isAllNetworks?: boolean,\n allTokens?: Record<number, TokenExtended[]>\n): {\n data: Record<string, Record<number, TokenExtended[]>> | undefined\n isLoading: boolean\n} => {\n const {\n chains: allChains,\n isLoading: isChainsLoading,\n getChainById,\n } = useChains(formType)\n const currentChain = useMemo(() => {\n return selectedChainId\n ? getChainById(selectedChainId, allChains)\n : undefined\n }, [selectedChainId, allChains, getChainById])\n const chains = useMemo(() => {\n return isAllNetworks ? allChains : currentChain ? [currentChain] : undefined\n }, [allChains, isAllNetworks, currentChain])\n\n const { accounts: allAccounts, account: currentAccount } = useAccount(\n isAllNetworks ? undefined : { chainType: currentChain?.chainType }\n )\n const { isAddressForChain } = useAddressForChain()\n const accounts = useMemo(() => {\n return isAllNetworks\n ? allAccounts\n : currentAccount\n ? [currentAccount]\n : undefined\n }, [allAccounts, currentAccount, isAllNetworks])\n\n const accountsWithTokens = useMemo(() => {\n if (!chains || !allTokens || !accounts?.length) {\n return undefined\n }\n return accounts\n ?.filter((account) => account.address)\n .reduce(\n (acc, account) => {\n if (account.address) {\n const accountChains = chains?.filter(\n (chain) =>\n account.chainType === chain?.chainType &&\n isAddressForChain(account.address as string, chain)\n )\n if (accountChains) {\n const chainIdSet = new Set(accountChains.map((chain) => chain.id))\n acc[account.address] = Object.entries(allTokens).reduce(\n (tokenAcc, [chainIdStr, tokens]) => {\n const chainId = Number(chainIdStr)\n if (chainIdSet.has(chainId)) {\n tokenAcc[chainId] = tokens\n }\n return tokenAcc\n },\n {} as Record<number, TokenExtended[]>\n )\n }\n }\n return acc\n },\n {} as Record<string, Record<number, TokenExtended[]>>\n )\n }, [accounts, chains, allTokens, isAddressForChain])\n\n return {\n data: accountsWithTokens,\n isLoading: isChainsLoading,\n }\n}\n"],"mappings":";;;;;AAOA,MAAa,2BACX,iBACA,UACA,eACA,cAIG;CACH,MAAM,EACJ,QAAQ,WACR,WAAW,iBACX,iBACE,UAAU,QAAQ;CACtB,MAAM,eAAe,cAAc;EACjC,OAAO,kBACH,aAAa,iBAAiB,SAAS,IACvC,KAAA;CACN,GAAG;EAAC;EAAiB;EAAW;CAAY,CAAC;CAC7C,MAAM,SAAS,cAAc;EAC3B,OAAO,gBAAgB,YAAY,eAAe,CAAC,YAAY,IAAI,KAAA;CACrE,GAAG;EAAC;EAAW;EAAe;CAAY,CAAC;CAE3C,MAAM,EAAE,UAAU,aAAa,SAAS,mBAAmB,WACzD,gBAAgB,KAAA,IAAY,EAAE,WAAW,cAAc,UAAU,CACnE;CACA,MAAM,EAAE,sBAAsB,mBAAmB;CACjD,MAAM,WAAW,cAAc;EAC7B,OAAO,gBACH,cACA,iBACE,CAAC,cAAc,IACf,KAAA;CACR,GAAG;EAAC;EAAa;EAAgB;CAAa,CAAC;CAoC/C,OAAO;EACL,MAnCyB,cAAc;GACvC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,QACtC;GAEF,OAAO,UACH,QAAQ,YAAY,QAAQ,OAAO,CAAC,CACrC,QACE,KAAK,YAAY;IAChB,IAAI,QAAQ,SAAS;KACnB,MAAM,gBAAgB,QAAQ,QAC3B,UACC,QAAQ,cAAc,OAAO,aAC7B,kBAAkB,QAAQ,SAAmB,KAAK,CACtD;KACA,IAAI,eAAe;MACjB,MAAM,aAAa,IAAI,IAAI,cAAc,KAAK,UAAU,MAAM,EAAE,CAAC;MACjE,IAAI,QAAQ,WAAW,OAAO,QAAQ,SAAS,CAAC,CAAC,QAC9C,UAAU,CAAC,YAAY,YAAY;OAClC,MAAM,UAAU,OAAO,UAAU;OACjC,IAAI,WAAW,IAAI,OAAO,GACxB,SAAS,WAAW;OAEtB,OAAO;MACT,GACA,CAAC,CACH;KACF;IACF;IACA,OAAO;GACT,GACA,CAAC,CACH;EACJ,GAAG;GAAC;GAAU;GAAQ;GAAW;EAAiB,CAGzB;EACvB,WAAW;CACb;AACF"}
@@ -1,4 +1,4 @@
1
- import { Chain, ChainType } from "@lifi/sdk";
1
+ import { Chain, ChainId, ChainType } from "@lifi/sdk";
2
2
  //#region src/hooks/useAddressValidation.d.ts
3
3
  export declare enum AddressType {
4
4
  Address = 0,
@@ -6,13 +6,14 @@ export declare enum AddressType {
6
6
  }
7
7
  type ValidationArgs = {
8
8
  value: string;
9
- chainType?: ChainType;
9
+ /** Strict: the receiver must be valid on this chain. */
10
10
  chain?: Chain;
11
11
  };
12
12
  type ValidResponse = {
13
13
  address: string;
14
14
  addressType: AddressType;
15
15
  chainType: ChainType;
16
+ chainId?: ChainId;
16
17
  isValid: true;
17
18
  };
18
19
  type InvalidResponse = {
@@ -1 +1 @@
1
- {"version":3,"file":"useAddressValidation.d.ts","names":[],"sources":["../../../src/hooks/useAddressValidation.ts"],"mappings":";;oBAOY;EACV;EACA;;KAGG;EACH;EACA,YAAY;EACZ,QAAQ;;KAGL;EACH;EACA,aAAa;EACb,WAAW;EACX;;KAGG;EACH;EACA;;qBAGW;EACX,kBACE,MAAM,mBACH,QAAQ,gBAAgB;EAC7B"}
1
+ {"version":3,"file":"useAddressValidation.d.ts","names":[],"sources":["../../../src/hooks/useAddressValidation.ts"],"mappings":";;oBAUY;EACV;EACA;;KAGG;EACH;;EAEA,QAAQ;;KAGL;EACH;EACA,aAAa;EACb,WAAW;EACX,UAAU;EACV;;KAGG;EACH;EACA;;qBAGW;EACX,kBACE,MAAM,mBACH,QAAQ,gBAAgB;EAC7B"}
@@ -1,8 +1,8 @@
1
1
  import { useSDKClient } from "../providers/SDKClientProvider.js";
2
- import { getNameServiceAddress } from "@lifi/sdk";
2
+ import { ChainId, getNameServiceAddress } from "@lifi/sdk";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import { useMutation } from "@tanstack/react-query";
5
- import { useChainTypeFromAddress } from "@lifi/widget-provider";
5
+ import { useAddressForChain, useChainTypeFromAddress } from "@lifi/widget-provider";
6
6
  //#region src/hooks/useAddressValidation.ts
7
7
  let AddressType = /* @__PURE__ */ function(AddressType) {
8
8
  AddressType[AddressType["Address"] = 0] = "Address";
@@ -11,28 +11,55 @@ let AddressType = /* @__PURE__ */ function(AddressType) {
11
11
  }({});
12
12
  const useAddressValidation = () => {
13
13
  const { t } = useTranslation();
14
- const { getChainTypeFromAddress } = useChainTypeFromAddress();
14
+ const { getChainFromAddress } = useChainTypeFromAddress();
15
+ const { isAddressForChain } = useAddressForChain();
15
16
  const sdkClient = useSDKClient();
16
- const { mutateAsync: validateAddress, isPending: isValidating } = useMutation({ mutationFn: async ({ value, chainType, chain }) => {
17
+ const validFor = (address, addressType, chain) => ({
18
+ address,
19
+ addressType,
20
+ chainType: chain.chainType,
21
+ chainId: getChainFromAddress(address)?.chainId,
22
+ isValid: true
23
+ });
24
+ const validateForChain = async (value, chain) => {
25
+ if (isAddressForChain(value, chain)) return validFor(value, 0, chain);
26
+ if (getChainFromAddress(value)) return {
27
+ isValid: false,
28
+ error: t("error.title.walletChainTypeInvalid", { chainName: chain.name })
29
+ };
30
+ const resolved = await getNameServiceAddress(sdkClient, value, chain.chainType);
31
+ if (resolved && isAddressForChain(resolved, chain)) return validFor(resolved, 1, chain);
32
+ return {
33
+ isValid: false,
34
+ error: chain.id === ChainId.ZEC ? t("error.title.zcashAddressInvalid") : t("error.title.walletAddressInvalid", {
35
+ context: "chain",
36
+ chainName: chain.name
37
+ })
38
+ };
39
+ };
40
+ const validateWithoutChain = async (value) => {
41
+ const detected = getChainFromAddress(value);
42
+ if (detected) return {
43
+ address: value,
44
+ addressType: 0,
45
+ ...detected,
46
+ isValid: true
47
+ };
48
+ const address = await getNameServiceAddress(sdkClient, value);
49
+ const resolved = address ? getChainFromAddress(address) : void 0;
50
+ if (address && resolved) return {
51
+ address,
52
+ addressType: 1,
53
+ ...resolved,
54
+ isValid: true
55
+ };
56
+ };
57
+ const { mutateAsync: validateAddress, isPending: isValidating } = useMutation({ mutationFn: async ({ value, chain }) => {
17
58
  try {
18
59
  if (!value) throw new Error();
19
- const _chainType = getChainTypeFromAddress(value);
20
- if (_chainType) return {
21
- address: value,
22
- addressType: 0,
23
- chainType: _chainType,
24
- isValid: true
25
- };
26
- const address = await getNameServiceAddress(sdkClient, value, chainType);
27
- if (address) {
28
- const _chainType = getChainTypeFromAddress(address);
29
- if (_chainType) return {
30
- address,
31
- addressType: 1,
32
- chainType: _chainType,
33
- isValid: true
34
- };
35
- }
60
+ if (chain) return await validateForChain(value, chain);
61
+ const result = await validateWithoutChain(value);
62
+ if (result) return result;
36
63
  throw new Error();
37
64
  } catch (_) {
38
65
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"useAddressValidation.js","names":[],"sources":["../../../src/hooks/useAddressValidation.ts"],"sourcesContent":["import type { Chain, ChainType } from '@lifi/sdk'\nimport { getNameServiceAddress } from '@lifi/sdk'\nimport { useChainTypeFromAddress } from '@lifi/widget-provider'\nimport { useMutation } from '@tanstack/react-query'\nimport { useTranslation } from 'react-i18next'\nimport { useSDKClient } from '../providers/SDKClientProvider.js'\n\nexport enum AddressType {\n Address = 0,\n NameService = 1,\n}\n\ntype ValidationArgs = {\n value: string\n chainType?: ChainType\n chain?: Chain\n}\n\ntype ValidResponse = {\n address: string\n addressType: AddressType\n chainType: ChainType\n isValid: true\n}\n\ntype InvalidResponse = {\n error: string\n isValid: false\n}\n\nexport const useAddressValidation = (): {\n validateAddress: (\n args: ValidationArgs\n ) => Promise<ValidResponse | InvalidResponse>\n isValidating: boolean\n} => {\n const { t } = useTranslation()\n const { getChainTypeFromAddress } = useChainTypeFromAddress()\n const sdkClient = useSDKClient()\n\n const { mutateAsync: validateAddress, isPending: isValidating } = useMutation(\n {\n mutationFn: async ({\n value,\n chainType,\n chain,\n }: ValidationArgs): Promise<ValidResponse | InvalidResponse> => {\n try {\n if (!value) {\n throw new Error()\n }\n\n const _chainType = getChainTypeFromAddress(value)\n if (_chainType) {\n return {\n address: value,\n addressType: AddressType.Address,\n chainType: _chainType,\n isValid: true,\n }\n }\n\n const address = await getNameServiceAddress(\n sdkClient,\n value,\n chainType\n )\n\n if (address) {\n const _chainType = getChainTypeFromAddress(address)\n if (_chainType) {\n return {\n address: address,\n addressType: AddressType.NameService,\n chainType: _chainType,\n isValid: true,\n }\n }\n }\n\n throw new Error()\n } catch (_) {\n return {\n isValid: false,\n error: t(\n 'error.title.walletAddressInvalid',\n chain?.name\n ? { context: 'chain', chainName: chain.name }\n : undefined\n ),\n }\n }\n },\n }\n )\n\n return {\n validateAddress,\n isValidating,\n }\n}\n"],"mappings":";;;;;;AAOA,IAAY,cAAL,yBAAA,aAAA;CACL,YAAA,YAAA,aAAA,KAAA;CACA,YAAA,YAAA,iBAAA,KAAA;;AACF,EAAA,CAAA,CAAA;AAoBA,MAAa,6BAKR;CACH,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,EAAE,4BAA4B,wBAAwB;CAC5D,MAAM,YAAY,aAAa;CAE/B,MAAM,EAAE,aAAa,iBAAiB,WAAW,iBAAiB,YAChE,EACE,YAAY,OAAO,EACjB,OACA,WACA,YAC8D;EAC9D,IAAI;GACF,IAAI,CAAC,OACH,MAAM,IAAI,MAAM;GAGlB,MAAM,aAAa,wBAAwB,KAAK;GAChD,IAAI,YACF,OAAO;IACL,SAAS;IACT,aAAA;IACA,WAAW;IACX,SAAS;GACX;GAGF,MAAM,UAAU,MAAM,sBACpB,WACA,OACA,SACF;GAEA,IAAI,SAAS;IACX,MAAM,aAAa,wBAAwB,OAAO;IAClD,IAAI,YACF,OAAO;KACI;KACT,aAAA;KACA,WAAW;KACX,SAAS;IACX;GAEJ;GAEA,MAAM,IAAI,MAAM;EAClB,SAAS,GAAG;GACV,OAAO;IACL,SAAS;IACT,OAAO,EACL,oCACA,OAAO,OACH;KAAE,SAAS;KAAS,WAAW,MAAM;IAAK,IAC1C,KAAA,CACN;GACF;EACF;CACF,EACF,CACF;CAEA,OAAO;EACL;EACA;CACF;AACF"}
1
+ {"version":3,"file":"useAddressValidation.js","names":[],"sources":["../../../src/hooks/useAddressValidation.ts"],"sourcesContent":["import type { Chain, ChainType } from '@lifi/sdk'\nimport { ChainId, getNameServiceAddress } from '@lifi/sdk'\nimport {\n useAddressForChain,\n useChainTypeFromAddress,\n} from '@lifi/widget-provider'\nimport { useMutation } from '@tanstack/react-query'\nimport { useTranslation } from 'react-i18next'\nimport { useSDKClient } from '../providers/SDKClientProvider.js'\n\nexport enum AddressType {\n Address = 0,\n NameService = 1,\n}\n\ntype ValidationArgs = {\n value: string\n /** Strict: the receiver must be valid on this chain. */\n chain?: Chain\n}\n\ntype ValidResponse = {\n address: string\n addressType: AddressType\n chainType: ChainType\n chainId?: ChainId\n isValid: true\n}\n\ntype InvalidResponse = {\n error: string\n isValid: false\n}\n\nexport const useAddressValidation = (): {\n validateAddress: (\n args: ValidationArgs\n ) => Promise<ValidResponse | InvalidResponse>\n isValidating: boolean\n} => {\n const { t } = useTranslation()\n const { getChainFromAddress } = useChainTypeFromAddress()\n const { isAddressForChain } = useAddressForChain()\n const sdkClient = useSDKClient()\n\n const validFor = (\n address: string,\n addressType: AddressType,\n chain: Chain\n ): ValidResponse => ({\n address,\n addressType,\n chainType: chain.chainType,\n chainId: getChainFromAddress(address)?.chainId,\n isValid: true,\n })\n\n const validateForChain = async (\n value: string,\n chain: Chain\n ): Promise<ValidResponse | InvalidResponse> => {\n if (isAddressForChain(value, chain)) {\n return validFor(value, AddressType.Address, chain)\n }\n // A recognised address is no name to resolve.\n if (getChainFromAddress(value)) {\n return {\n isValid: false,\n error: t('error.title.walletChainTypeInvalid', {\n chainName: chain.name,\n }),\n }\n }\n const resolved = await getNameServiceAddress(\n sdkClient,\n value,\n chain.chainType\n )\n if (resolved && isAddressForChain(resolved, chain)) {\n return validFor(resolved, AddressType.NameService, chain)\n }\n return {\n isValid: false,\n error:\n chain.id === ChainId.ZEC\n ? t('error.title.zcashAddressInvalid')\n : t('error.title.walletAddressInvalid', {\n context: 'chain',\n chainName: chain.name,\n }),\n }\n }\n\n const validateWithoutChain = async (\n value: string\n ): Promise<ValidResponse | undefined> => {\n const detected = getChainFromAddress(value)\n if (detected) {\n return {\n address: value,\n addressType: AddressType.Address,\n ...detected,\n isValid: true,\n }\n }\n const address = await getNameServiceAddress(sdkClient, value)\n const resolved = address ? getChainFromAddress(address) : undefined\n if (address && resolved) {\n return {\n address,\n addressType: AddressType.NameService,\n ...resolved,\n isValid: true,\n }\n }\n return undefined\n }\n\n const { mutateAsync: validateAddress, isPending: isValidating } = useMutation(\n {\n mutationFn: async ({\n value,\n chain,\n }: ValidationArgs): Promise<ValidResponse | InvalidResponse> => {\n try {\n if (!value) {\n throw new Error()\n }\n if (chain) {\n return await validateForChain(value, chain)\n }\n const result = await validateWithoutChain(value)\n if (result) {\n return result\n }\n throw new Error()\n } catch (_) {\n return {\n isValid: false,\n error: t(\n 'error.title.walletAddressInvalid',\n chain?.name\n ? { context: 'chain', chainName: chain.name }\n : undefined\n ),\n }\n }\n },\n }\n )\n\n return {\n validateAddress,\n isValidating,\n }\n}\n"],"mappings":";;;;;;AAUA,IAAY,cAAL,yBAAA,aAAA;CACL,YAAA,YAAA,aAAA,KAAA;CACA,YAAA,YAAA,iBAAA,KAAA;;AACF,EAAA,CAAA,CAAA;AAqBA,MAAa,6BAKR;CACH,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,EAAE,wBAAwB,wBAAwB;CACxD,MAAM,EAAE,sBAAsB,mBAAmB;CACjD,MAAM,YAAY,aAAa;CAE/B,MAAM,YACJ,SACA,aACA,WACmB;EACnB;EACA;EACA,WAAW,MAAM;EACjB,SAAS,oBAAoB,OAAO,CAAC,EAAE;EACvC,SAAS;CACX;CAEA,MAAM,mBAAmB,OACvB,OACA,UAC6C;EAC7C,IAAI,kBAAkB,OAAO,KAAK,GAChC,OAAO,SAAS,OAAA,GAA4B,KAAK;EAGnD,IAAI,oBAAoB,KAAK,GAC3B,OAAO;GACL,SAAS;GACT,OAAO,EAAE,sCAAsC,EAC7C,WAAW,MAAM,KACnB,CAAC;EACH;EAEF,MAAM,WAAW,MAAM,sBACrB,WACA,OACA,MAAM,SACR;EACA,IAAI,YAAY,kBAAkB,UAAU,KAAK,GAC/C,OAAO,SAAS,UAAA,GAAmC,KAAK;EAE1D,OAAO;GACL,SAAS;GACT,OACE,MAAM,OAAO,QAAQ,MACjB,EAAE,iCAAiC,IACnC,EAAE,oCAAoC;IACpC,SAAS;IACT,WAAW,MAAM;GACnB,CAAC;EACT;CACF;CAEA,MAAM,uBAAuB,OAC3B,UACuC;EACvC,MAAM,WAAW,oBAAoB,KAAK;EAC1C,IAAI,UACF,OAAO;GACL,SAAS;GACT,aAAA;GACA,GAAG;GACH,SAAS;EACX;EAEF,MAAM,UAAU,MAAM,sBAAsB,WAAW,KAAK;EAC5D,MAAM,WAAW,UAAU,oBAAoB,OAAO,IAAI,KAAA;EAC1D,IAAI,WAAW,UACb,OAAO;GACL;GACA,aAAA;GACA,GAAG;GACH,SAAS;EACX;CAGJ;CAEA,MAAM,EAAE,aAAa,iBAAiB,WAAW,iBAAiB,YAChE,EACE,YAAY,OAAO,EACjB,OACA,YAC8D;EAC9D,IAAI;GACF,IAAI,CAAC,OACH,MAAM,IAAI,MAAM;GAElB,IAAI,OACF,OAAO,MAAM,iBAAiB,OAAO,KAAK;GAE5C,MAAM,SAAS,MAAM,qBAAqB,KAAK;GAC/C,IAAI,QACF,OAAO;GAET,MAAM,IAAI,MAAM;EAClB,SAAS,GAAG;GACV,OAAO;IACL,SAAS;IACT,OAAO,EACL,oCACA,OAAO,OACH;KAAE,SAAS;KAAS,WAAW,MAAM;IAAK,IAC1C,KAAA,CACN;GACF;EACF;CACF,EACF,CACF;CAEA,OAAO;EACL;EACA;CACF;AACF"}
@@ -5,6 +5,7 @@ import { useGasRecommendation } from "./useGasRecommendation.js";
5
5
  import { useIsContractAddress } from "./useIsContractAddress.js";
6
6
  import { useTokenBalance } from "./useTokenBalance.js";
7
7
  import { useMemo } from "react";
8
+ import { useAddressForChain } from "@lifi/widget-provider";
8
9
  import { useAccount } from "@lifi/wallet-management";
9
10
  //#region src/hooks/useGasRefuel.ts
10
11
  const useGasRefuel = () => {
@@ -14,7 +15,9 @@ const useGasRefuel = () => {
14
15
  const toChain = getChainById(toChainId);
15
16
  const fromChain = getChainById(fromChainId);
16
17
  const { account: toAccount } = useAccount({ chainType: toChain?.chainType });
17
- const effectiveToAddress = toAddress || toAccount?.address;
18
+ const { isAddressForChain } = useAddressForChain();
19
+ const toAccountAddress = toAccount?.address && toChain && isAddressForChain(toAccount.address, toChain) ? toAccount.address : void 0;
20
+ const effectiveToAddress = toAddress || toAccountAddress;
18
21
  const { isContractAddress: isToContractAddress } = useIsContractAddress(effectiveToAddress, toChainId, toChain?.chainType);
19
22
  const { token: destinationNativeToken } = useTokenBalance(effectiveToAddress, toChainId ? toChain?.nativeToken : void 0);
20
23
  const { data: gasRecommendation, isLoading } = useGasRecommendation(toChainId, fromChainId, fromTokenAddress);
@@ -1 +1 @@
1
- {"version":3,"file":"useGasRefuel.js","names":[],"sources":["../../../src/hooks/useGasRefuel.ts"],"sourcesContent":["import type { ExtendedChain } from '@lifi/sdk'\nimport { useAccount } from '@lifi/wallet-management'\nimport { useMemo } from 'react'\nimport { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'\nimport { useFieldValues } from '../stores/form/useFieldValues.js'\nimport { useAvailableChains } from './useAvailableChains.js'\nimport { useGasRecommendation } from './useGasRecommendation.js'\nimport { useIsContractAddress } from './useIsContractAddress.js'\nimport { useTokenBalance } from './useTokenBalance.js'\n\nexport const useGasRefuel = (): {\n enabled: boolean\n availble: boolean | undefined\n isLoading: boolean\n chain: ExtendedChain | undefined\n fromAmount: string | undefined\n} => {\n const { getChainById } = useAvailableChains()\n const { hiddenUI } = useWidgetConfig()\n const [fromChainId, fromTokenAddress, toChainId, toAddress] = useFieldValues(\n 'fromChain',\n 'fromToken',\n 'toChain',\n 'toAddress'\n )\n\n const toChain = getChainById(toChainId)\n const fromChain = getChainById(fromChainId)\n\n const { account: toAccount } = useAccount({ chainType: toChain?.chainType })\n\n const effectiveToAddress = toAddress || toAccount?.address\n\n const { isContractAddress: isToContractAddress } = useIsContractAddress(\n effectiveToAddress,\n toChainId,\n toChain?.chainType\n )\n\n const { token: destinationNativeToken } = useTokenBalance(\n effectiveToAddress,\n toChainId ? toChain?.nativeToken : undefined\n )\n\n const { data: gasRecommendation, isLoading } = useGasRecommendation(\n toChainId,\n fromChainId,\n fromTokenAddress\n )\n\n // When we bridge between ecosystems we need to be sure toAddress is set\n const isChainTypeSatisfied =\n fromChain?.chainType !== toChain?.chainType ? Boolean(toAddress) : true\n\n // We should not refuel to the contract address\n const isToAddressSatisfied = effectiveToAddress && !isToContractAddress\n\n const enabled = useMemo(() => {\n if (\n // Same chain refuel is not allowed since users need gas on source chain to initiate transaction\n // We allow refuel when bridging to native token since bridging routes may require gas for destination swaps\n fromChainId === toChainId ||\n !gasRecommendation?.available ||\n !gasRecommendation?.recommended ||\n !destinationNativeToken ||\n !isChainTypeSatisfied ||\n !isToAddressSatisfied ||\n hiddenUI?.gasRefuelMessage\n ) {\n return false\n }\n const tokenBalance = destinationNativeToken.amount ?? 0n\n\n // Check if the user balance < 50% of the recommended amount\n const recommendedAmount = BigInt(gasRecommendation.recommended.amount) / 2n\n\n const insufficientGas = tokenBalance < recommendedAmount\n return insufficientGas\n }, [\n fromChainId,\n gasRecommendation,\n isChainTypeSatisfied,\n isToAddressSatisfied,\n destinationNativeToken,\n toChainId,\n hiddenUI,\n ])\n\n return {\n enabled: enabled,\n availble: gasRecommendation?.available,\n isLoading: isLoading,\n chain: toChain,\n fromAmount: gasRecommendation?.available\n ? gasRecommendation.fromAmount\n : undefined,\n }\n}\n"],"mappings":";;;;;;;;;AAUA,MAAa,qBAMR;CACH,MAAM,EAAE,iBAAiB,mBAAmB;CAC5C,MAAM,EAAE,aAAa,gBAAgB;CACrC,MAAM,CAAC,aAAa,kBAAkB,WAAW,aAAa,eAC5D,aACA,aACA,WACA,WACF;CAEA,MAAM,UAAU,aAAa,SAAS;CACtC,MAAM,YAAY,aAAa,WAAW;CAE1C,MAAM,EAAE,SAAS,cAAc,WAAW,EAAE,WAAW,SAAS,UAAU,CAAC;CAE3E,MAAM,qBAAqB,aAAa,WAAW;CAEnD,MAAM,EAAE,mBAAmB,wBAAwB,qBACjD,oBACA,WACA,SAAS,SACX;CAEA,MAAM,EAAE,OAAO,2BAA2B,gBACxC,oBACA,YAAY,SAAS,cAAc,KAAA,CACrC;CAEA,MAAM,EAAE,MAAM,mBAAmB,cAAc,qBAC7C,WACA,aACA,gBACF;CAGA,MAAM,uBACJ,WAAW,cAAc,SAAS,YAAY,QAAQ,SAAS,IAAI;CAGrE,MAAM,uBAAuB,sBAAsB,CAAC;CAiCpD,OAAO;EACL,SAhCc,cAAc;GAC5B,IAGE,gBAAgB,aAChB,CAAC,mBAAmB,aACpB,CAAC,mBAAmB,eACpB,CAAC,0BACD,CAAC,wBACD,CAAC,wBACD,UAAU,kBAEV,OAAO;GAQT,QANqB,uBAAuB,UAAU,MAG5B,OAAO,kBAAkB,YAAY,MAAM,IAAI;EAI3E,GAAG;GACD;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAGiB;EACf,UAAU,mBAAmB;EAClB;EACX,OAAO;EACP,YAAY,mBAAmB,YAC3B,kBAAkB,aAClB,KAAA;CACN;AACF"}
1
+ {"version":3,"file":"useGasRefuel.js","names":[],"sources":["../../../src/hooks/useGasRefuel.ts"],"sourcesContent":["import type { ExtendedChain } from '@lifi/sdk'\nimport { useAccount } from '@lifi/wallet-management'\nimport { useAddressForChain } from '@lifi/widget-provider'\nimport { useMemo } from 'react'\nimport { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'\nimport { useFieldValues } from '../stores/form/useFieldValues.js'\nimport { useAvailableChains } from './useAvailableChains.js'\nimport { useGasRecommendation } from './useGasRecommendation.js'\nimport { useIsContractAddress } from './useIsContractAddress.js'\nimport { useTokenBalance } from './useTokenBalance.js'\n\nexport const useGasRefuel = (): {\n enabled: boolean\n availble: boolean | undefined\n isLoading: boolean\n chain: ExtendedChain | undefined\n fromAmount: string | undefined\n} => {\n const { getChainById } = useAvailableChains()\n const { hiddenUI } = useWidgetConfig()\n const [fromChainId, fromTokenAddress, toChainId, toAddress] = useFieldValues(\n 'fromChain',\n 'fromToken',\n 'toChain',\n 'toAddress'\n )\n\n const toChain = getChainById(toChainId)\n const fromChain = getChainById(fromChainId)\n\n const { account: toAccount } = useAccount({ chainType: toChain?.chainType })\n const { isAddressForChain } = useAddressForChain()\n\n // A shared chain type is not a shared format: a Bitcoin account cannot receive on ZEC.\n const toAccountAddress =\n toAccount?.address &&\n toChain &&\n isAddressForChain(toAccount.address, toChain)\n ? toAccount.address\n : undefined\n\n const effectiveToAddress = toAddress || toAccountAddress\n\n const { isContractAddress: isToContractAddress } = useIsContractAddress(\n effectiveToAddress,\n toChainId,\n toChain?.chainType\n )\n\n const { token: destinationNativeToken } = useTokenBalance(\n effectiveToAddress,\n toChainId ? toChain?.nativeToken : undefined\n )\n\n const { data: gasRecommendation, isLoading } = useGasRecommendation(\n toChainId,\n fromChainId,\n fromTokenAddress\n )\n\n // When we bridge between ecosystems we need to be sure toAddress is set\n const isChainTypeSatisfied =\n fromChain?.chainType !== toChain?.chainType ? Boolean(toAddress) : true\n\n // We should not refuel to the contract address\n const isToAddressSatisfied = effectiveToAddress && !isToContractAddress\n\n const enabled = useMemo(() => {\n if (\n // Same chain refuel is not allowed since users need gas on source chain to initiate transaction\n // We allow refuel when bridging to native token since bridging routes may require gas for destination swaps\n fromChainId === toChainId ||\n !gasRecommendation?.available ||\n !gasRecommendation?.recommended ||\n !destinationNativeToken ||\n !isChainTypeSatisfied ||\n !isToAddressSatisfied ||\n hiddenUI?.gasRefuelMessage\n ) {\n return false\n }\n const tokenBalance = destinationNativeToken.amount ?? 0n\n\n // Check if the user balance < 50% of the recommended amount\n const recommendedAmount = BigInt(gasRecommendation.recommended.amount) / 2n\n\n const insufficientGas = tokenBalance < recommendedAmount\n return insufficientGas\n }, [\n fromChainId,\n gasRecommendation,\n isChainTypeSatisfied,\n isToAddressSatisfied,\n destinationNativeToken,\n toChainId,\n hiddenUI,\n ])\n\n return {\n enabled: enabled,\n availble: gasRecommendation?.available,\n isLoading: isLoading,\n chain: toChain,\n fromAmount: gasRecommendation?.available\n ? gasRecommendation.fromAmount\n : undefined,\n }\n}\n"],"mappings":";;;;;;;;;;AAWA,MAAa,qBAMR;CACH,MAAM,EAAE,iBAAiB,mBAAmB;CAC5C,MAAM,EAAE,aAAa,gBAAgB;CACrC,MAAM,CAAC,aAAa,kBAAkB,WAAW,aAAa,eAC5D,aACA,aACA,WACA,WACF;CAEA,MAAM,UAAU,aAAa,SAAS;CACtC,MAAM,YAAY,aAAa,WAAW;CAE1C,MAAM,EAAE,SAAS,cAAc,WAAW,EAAE,WAAW,SAAS,UAAU,CAAC;CAC3E,MAAM,EAAE,sBAAsB,mBAAmB;CAGjD,MAAM,mBACJ,WAAW,WACX,WACA,kBAAkB,UAAU,SAAS,OAAO,IACxC,UAAU,UACV,KAAA;CAEN,MAAM,qBAAqB,aAAa;CAExC,MAAM,EAAE,mBAAmB,wBAAwB,qBACjD,oBACA,WACA,SAAS,SACX;CAEA,MAAM,EAAE,OAAO,2BAA2B,gBACxC,oBACA,YAAY,SAAS,cAAc,KAAA,CACrC;CAEA,MAAM,EAAE,MAAM,mBAAmB,cAAc,qBAC7C,WACA,aACA,gBACF;CAGA,MAAM,uBACJ,WAAW,cAAc,SAAS,YAAY,QAAQ,SAAS,IAAI;CAGrE,MAAM,uBAAuB,sBAAsB,CAAC;CAiCpD,OAAO;EACL,SAhCc,cAAc;GAC5B,IAGE,gBAAgB,aAChB,CAAC,mBAAmB,aACpB,CAAC,mBAAmB,eACpB,CAAC,0BACD,CAAC,wBACD,CAAC,wBACD,UAAU,kBAEV,OAAO;GAQT,QANqB,uBAAuB,UAAU,MAG5B,OAAO,kBAAkB,YAAY,MAAM,IAAI;EAI3E,GAAG;GACD;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAGiB;EACf,UAAU,mBAAmB;EAClB;EACX,OAAO;EACP,YAAY,mBAAmB,YAC3B,kBAAkB,aAClB,KAAA;CACN;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useRoutes.d.ts","names":[],"sources":["../../../src/hooks/useRoutes.ts"],"mappings":";;UA4CU;EACR,kBAAkB;;;;;EAKlB;;;;;EAKA;;qBAGW,cACX,iBACA,kBACA,kBAAkB,4BACjB;EACD,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA,WAAW;EACX,SAAS;EACT;EACA,qBAAqB,OAAO"}
1
+ {"version":3,"file":"useRoutes.d.ts","names":[],"sources":["../../../src/hooks/useRoutes.ts"],"mappings":";;UA6CU;EACR,kBAAkB;;;;;EAKlB;;;;;EAKA;;qBAGW,cACX,iBACA,kBACA,kBAAkB,4BACjB;EACD,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA,WAAW;EACX,SAAS;EACT;EACA,qBAAqB,OAAO"}