@lifi/widget 3.21.4-alpha.0 → 3.22.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 (132) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/esm/AppProvider.js +2 -3
  3. package/dist/esm/AppProvider.js.map +1 -1
  4. package/dist/esm/components/AppContainer.js +23 -22
  5. package/dist/esm/components/AppContainer.js.map +1 -1
  6. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js +12 -7
  7. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js.map +1 -1
  8. package/dist/esm/components/BaseTransactionButton/types.d.ts +2 -0
  9. package/dist/esm/components/ChainSelect/ChainSelect.js +3 -1
  10. package/dist/esm/components/ChainSelect/ChainSelect.js.map +1 -1
  11. package/dist/esm/components/ChainSelect/ChainSelect.style.d.ts +3 -1
  12. package/dist/esm/components/ChainSelect/ChainSelect.style.js +23 -9
  13. package/dist/esm/components/ChainSelect/ChainSelect.style.js.map +1 -1
  14. package/dist/esm/components/Messages/MissingRouteRequiredAccountMessage.d.ts +8 -0
  15. package/dist/esm/components/Messages/MissingRouteRequiredAccountMessage.js +20 -0
  16. package/dist/esm/components/Messages/MissingRouteRequiredAccountMessage.js.map +1 -0
  17. package/dist/esm/components/Messages/WarningMessages.js +3 -0
  18. package/dist/esm/components/Messages/WarningMessages.js.map +1 -1
  19. package/dist/esm/components/Messages/useMessageQueue.js +15 -4
  20. package/dist/esm/components/Messages/useMessageQueue.js.map +1 -1
  21. package/dist/esm/components/RouteCard/RouteCard.js +3 -3
  22. package/dist/esm/components/RouteCard/RouteCard.js.map +1 -1
  23. package/dist/esm/components/TokenList/TokenListItem.js +2 -2
  24. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  25. package/dist/esm/config/version.d.ts +1 -1
  26. package/dist/esm/config/version.js +1 -1
  27. package/dist/esm/config/version.js.map +1 -1
  28. package/dist/esm/hooks/useAvailableChains.js +3 -2
  29. package/dist/esm/hooks/useAvailableChains.js.map +1 -1
  30. package/dist/esm/hooks/useExplorer.d.ts +0 -1
  31. package/dist/esm/hooks/useExplorer.js +26 -31
  32. package/dist/esm/hooks/useExplorer.js.map +1 -1
  33. package/dist/esm/hooks/useFromTokenSufficiency.js +4 -1
  34. package/dist/esm/hooks/useFromTokenSufficiency.js.map +1 -1
  35. package/dist/esm/hooks/useGasRecommendation.js +9 -1
  36. package/dist/esm/hooks/useGasRecommendation.js.map +1 -1
  37. package/dist/esm/hooks/useGasSufficiency.js +50 -16
  38. package/dist/esm/hooks/useGasSufficiency.js.map +1 -1
  39. package/dist/esm/hooks/useIsBatchingSupported.js +8 -1
  40. package/dist/esm/hooks/useIsBatchingSupported.js.map +1 -1
  41. package/dist/esm/hooks/useIsContractAddress.js +1 -1
  42. package/dist/esm/hooks/useIsContractAddress.js.map +1 -1
  43. package/dist/esm/hooks/useRouteExecution.js +10 -4
  44. package/dist/esm/hooks/useRouteExecution.js.map +1 -1
  45. package/dist/esm/hooks/useRouteRequiredAccountConnection.d.ts +10 -0
  46. package/dist/esm/hooks/useRouteRequiredAccountConnection.js +45 -0
  47. package/dist/esm/hooks/useRouteRequiredAccountConnection.js.map +1 -0
  48. package/dist/esm/hooks/useRoutes.d.ts +1 -1
  49. package/dist/esm/hooks/useRoutes.js +13 -4
  50. package/dist/esm/hooks/useRoutes.js.map +1 -1
  51. package/dist/esm/hooks/useTokenBalance.js +12 -3
  52. package/dist/esm/hooks/useTokenBalance.js.map +1 -1
  53. package/dist/esm/hooks/useTokenBalances.js +4 -1
  54. package/dist/esm/hooks/useTokenBalances.js.map +1 -1
  55. package/dist/esm/hooks/useTokenSearch.js +5 -2
  56. package/dist/esm/hooks/useTokenSearch.js.map +1 -1
  57. package/dist/esm/hooks/useTokens.js +3 -2
  58. package/dist/esm/hooks/useTokens.js.map +1 -1
  59. package/dist/esm/hooks/useTools.js +3 -2
  60. package/dist/esm/hooks/useTools.js.map +1 -1
  61. package/dist/esm/hooks/useTransactionDetails.js +9 -4
  62. package/dist/esm/hooks/useTransactionDetails.js.map +1 -1
  63. package/dist/esm/hooks/useTransactionHistory.js +7 -1
  64. package/dist/esm/hooks/useTransactionHistory.js.map +1 -1
  65. package/dist/esm/i18n/en.json +6 -4
  66. package/dist/esm/pages/MainPage/ReviewButton.js +1 -1
  67. package/dist/esm/pages/MainPage/ReviewButton.js.map +1 -1
  68. package/dist/esm/pages/TransactionPage/StartTransactionButton.js +1 -1
  69. package/dist/esm/pages/TransactionPage/StartTransactionButton.js.map +1 -1
  70. package/dist/esm/pages/TransactionPage/StatusBottomSheet.js +26 -43
  71. package/dist/esm/pages/TransactionPage/StatusBottomSheet.js.map +1 -1
  72. package/dist/esm/pages/TransactionPage/StatusBottomSheet.style.d.ts +0 -1
  73. package/dist/esm/pages/TransactionPage/StatusBottomSheet.style.js +4 -11
  74. package/dist/esm/pages/TransactionPage/StatusBottomSheet.style.js.map +1 -1
  75. package/dist/esm/providers/QueryClientProvider.d.ts +2 -0
  76. package/dist/esm/providers/QueryClientProvider.js +9 -0
  77. package/dist/esm/providers/QueryClientProvider.js.map +1 -0
  78. package/dist/esm/providers/WalletProvider/SuiBaseProvider.js +3 -8
  79. package/dist/esm/providers/WalletProvider/SuiBaseProvider.js.map +1 -1
  80. package/dist/esm/stores/form/FormStore.js +5 -12
  81. package/dist/esm/stores/form/FormStore.js.map +1 -1
  82. package/dist/esm/stores/form/useFormRef.js +1 -7
  83. package/dist/esm/stores/form/useFormRef.js.map +1 -1
  84. package/dist/esm/types/widget.d.ts +6 -1
  85. package/dist/esm/utils/queries.d.ts +1 -0
  86. package/dist/esm/utils/queries.js +2 -0
  87. package/dist/esm/utils/queries.js.map +1 -0
  88. package/dist/esm/utils/timer.js +8 -1
  89. package/dist/esm/utils/timer.js.map +1 -1
  90. package/package.json +11 -11
  91. package/package.json.tmp +10 -10
  92. package/src/AppProvider.tsx +2 -3
  93. package/src/components/AppContainer.tsx +24 -23
  94. package/src/components/BaseTransactionButton/BaseTransactionButton.tsx +15 -6
  95. package/src/components/BaseTransactionButton/types.ts +3 -0
  96. package/src/components/ChainSelect/ChainSelect.style.tsx +25 -9
  97. package/src/components/ChainSelect/ChainSelect.tsx +4 -1
  98. package/src/components/Messages/MissingRouteRequiredAccountMessage.tsx +44 -0
  99. package/src/components/Messages/WarningMessages.tsx +9 -0
  100. package/src/components/Messages/useMessageQueue.ts +17 -4
  101. package/src/components/RouteCard/RouteCard.tsx +3 -3
  102. package/src/components/TokenList/TokenListItem.tsx +2 -2
  103. package/src/config/version.ts +1 -1
  104. package/src/hooks/useAvailableChains.ts +4 -2
  105. package/src/hooks/useExplorer.ts +35 -36
  106. package/src/hooks/useFromTokenSufficiency.ts +4 -1
  107. package/src/hooks/useGasRecommendation.ts +9 -1
  108. package/src/hooks/useGasSufficiency.ts +76 -22
  109. package/src/hooks/useIsBatchingSupported.ts +9 -1
  110. package/src/hooks/useIsContractAddress.ts +1 -1
  111. package/src/hooks/useRouteExecution.ts +10 -4
  112. package/src/hooks/useRouteRequiredAccountConnection.ts +58 -0
  113. package/src/hooks/useRoutes.ts +13 -7
  114. package/src/hooks/useTokenBalance.ts +12 -3
  115. package/src/hooks/useTokenBalances.ts +4 -1
  116. package/src/hooks/useTokenSearch.ts +5 -2
  117. package/src/hooks/useTokens.ts +3 -2
  118. package/src/hooks/useTools.ts +3 -2
  119. package/src/hooks/useTransactionDetails.ts +13 -4
  120. package/src/hooks/useTransactionHistory.ts +7 -1
  121. package/src/i18n/en.json +6 -4
  122. package/src/pages/MainPage/ReviewButton.tsx +1 -0
  123. package/src/pages/TransactionPage/StartTransactionButton.tsx +1 -0
  124. package/src/pages/TransactionPage/StatusBottomSheet.style.tsx +4 -12
  125. package/src/pages/TransactionPage/StatusBottomSheet.tsx +47 -77
  126. package/src/providers/QueryClientProvider.tsx +20 -0
  127. package/src/providers/WalletProvider/SuiBaseProvider.tsx +3 -9
  128. package/src/stores/form/FormStore.tsx +4 -15
  129. package/src/stores/form/useFormRef.ts +1 -8
  130. package/src/types/widget.ts +10 -2
  131. package/src/utils/queries.ts +2 -0
  132. package/src/utils/timer.ts +8 -1
@@ -1,17 +1,33 @@
1
1
  import { Box, styled } from '@mui/material'
2
2
  import { Card } from '../../components/Card/Card.js'
3
+ import { maxChainsToShow } from '../../stores/chains/createChainOrderStore.js'
4
+
5
+ const chainCardWidthPx = 52
6
+ const chainCardHeightPx = 56
7
+
8
+ // Chain grid layout settings:
9
+ // These should remain consistent with `maxChainsToShow` to ensure
10
+ // proper row/column distribution in the grid layout logic.
11
+ const maxRows = 2
12
+ const maxChainsPerRow = Math.ceil(maxChainsToShow / maxRows)
3
13
 
4
14
  export const ChainCard = styled(Card)({
5
15
  display: 'grid',
6
16
  placeItems: 'center',
7
- minWidth: 52,
8
- height: 56,
17
+ minWidth: chainCardWidthPx,
18
+ height: chainCardHeightPx,
9
19
  })
10
20
 
11
- export const ChainContainer = styled(Box)(({ theme }) => ({
12
- display: 'grid',
13
- gridTemplateColumns: 'repeat(auto-fit, minmax(52px, 1fr))',
14
- gridAutoRows: '56px',
15
- justifyContent: 'space-between',
16
- gap: theme.spacing(1.5),
17
- }))
21
+ export const ChainContainer = styled(Box, {
22
+ shouldForwardProp: (prop) => prop !== 'itemCount',
23
+ })<{ itemCount: number }>(({ theme, itemCount }) => {
24
+ const rowCount = Math.min(Math.ceil(itemCount / maxChainsPerRow), maxRows)
25
+ const columnsPerRow = Math.ceil(itemCount / rowCount)
26
+ return {
27
+ display: 'grid',
28
+ gridTemplateColumns: `repeat(${rowCount > 1 ? columnsPerRow : 'auto-fit'}, minmax(${chainCardWidthPx}px, 1fr))`,
29
+ gridTemplateRows: `repeat(${rowCount}, ${chainCardHeightPx}px)`,
30
+ justifyContent: 'space-between',
31
+ gap: theme.spacing(1.5),
32
+ }
33
+ })
@@ -50,8 +50,11 @@ export const ChainSelect = ({ formType }: FormTypeProps) => {
50
50
  // When there is less than 10 chains we don't care about the order
51
51
  const chainsToShow = chainsToHide > 0 ? getChains() : chains
52
52
 
53
+ // Number of tiles to show in the grid
54
+ const tilesCount = (chainsToShow?.length ?? 0) + (chainsToHide > 0 ? 1 : 0)
55
+
53
56
  return (
54
- <ChainContainer>
57
+ <ChainContainer itemCount={tilesCount}>
55
58
  {isLoading
56
59
  ? Array.from({ length: maxChainsToOrder }).map((_, index) => (
57
60
  <Skeleton
@@ -0,0 +1,44 @@
1
+ import type { ExtendedChain } from '@lifi/sdk'
2
+ import Wallet from '@mui/icons-material/Wallet'
3
+ import type { BoxProps } from '@mui/material'
4
+ import { Typography } from '@mui/material'
5
+ import { useTranslation } from 'react-i18next'
6
+ import { shortenAddress } from '../../utils/wallet.js'
7
+ import { AlertMessage } from './AlertMessage.js'
8
+
9
+ interface MissingRouteRequiredAccountMessageProps extends BoxProps {
10
+ chain?: ExtendedChain
11
+ address?: string
12
+ }
13
+
14
+ export const MissingRouteRequiredAccountMessage: React.FC<
15
+ MissingRouteRequiredAccountMessageProps
16
+ > = ({ chain, address, ...props }) => {
17
+ const { t } = useTranslation()
18
+
19
+ if (!chain) {
20
+ return null
21
+ }
22
+
23
+ return (
24
+ <AlertMessage
25
+ title={
26
+ <Typography
27
+ variant="body2"
28
+ sx={{
29
+ px: 1,
30
+ color: 'text.primary',
31
+ }}
32
+ >
33
+ {t('info.message.missingRouteRequiredAccount', {
34
+ chainName: chain.name,
35
+ address: address ? `(${shortenAddress(address)})` : '',
36
+ })}
37
+ </Typography>
38
+ }
39
+ icon={<Wallet />}
40
+ multiline
41
+ {...props}
42
+ />
43
+ )
44
+ }
@@ -4,6 +4,7 @@ import { Collapse } from '@mui/material'
4
4
  import { AccountNotDeployedMessage } from './AccountNotDeployedMessage.js'
5
5
  import { FundsSufficiencyMessage } from './FundsSufficiencyMessage.js'
6
6
  import { GasSufficiencyMessage } from './GasSufficiencyMessage.js'
7
+ import { MissingRouteRequiredAccountMessage } from './MissingRouteRequiredAccountMessage.js'
7
8
  import { ToAddressRequiredMessage } from './ToAddressRequiredMessage.js'
8
9
  import { useMessageQueue } from './useMessageQueue.js'
9
10
 
@@ -21,6 +22,14 @@ export const WarningMessages: React.FC<WarningMessagesProps> = ({
21
22
 
22
23
  const getMessage = () => {
23
24
  switch (messages[0]?.id) {
25
+ case 'MISSING_ROUTE_REQUIRED_ACCOUNT':
26
+ return (
27
+ <MissingRouteRequiredAccountMessage
28
+ chain={messages[0].props?.chain}
29
+ address={messages[0].props?.address}
30
+ {...props}
31
+ />
32
+ )
24
33
  case 'INSUFFICIENT_FUNDS':
25
34
  return <FundsSufficiencyMessage {...props} />
26
35
  case 'INSUFFICIENT_GAS':
@@ -2,6 +2,7 @@ import type { Route } from '@lifi/sdk'
2
2
  import { useMemo } from 'react'
3
3
  import { useFromTokenSufficiency } from '../../hooks/useFromTokenSufficiency.js'
4
4
  import { useGasSufficiency } from '../../hooks/useGasSufficiency.js'
5
+ import { useRouteRequiredAccountConnection } from '../../hooks/useRouteRequiredAccountConnection.js'
5
6
  import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'
6
7
 
7
8
  interface QueuedMessage {
@@ -21,21 +22,31 @@ export const useMessageQueue = (route?: Route, allowInteraction?: boolean) => {
21
22
  useFromTokenSufficiency(route)
22
23
  const { insufficientGas, isLoading: isGasSufficiencyLoading } =
23
24
  useGasSufficiency(route)
25
+ const { missingChain, missingAccountAddress } =
26
+ useRouteRequiredAccountConnection(route)
24
27
 
25
28
  const messageQueue = useMemo(() => {
26
29
  const queue: QueuedMessage[] = []
27
30
 
31
+ if (missingChain) {
32
+ queue.push({
33
+ id: 'MISSING_ROUTE_REQUIRED_ACCOUNT',
34
+ priority: 1,
35
+ props: { chain: missingChain, address: missingAccountAddress },
36
+ })
37
+ }
38
+
28
39
  if (insufficientFromToken) {
29
40
  queue.push({
30
41
  id: 'INSUFFICIENT_FUNDS',
31
- priority: 1,
42
+ priority: 2,
32
43
  })
33
44
  }
34
45
 
35
46
  if (insufficientGas?.length) {
36
47
  queue.push({
37
48
  id: 'INSUFFICIENT_GAS',
38
- priority: 2,
49
+ priority: 3,
39
50
  props: { insufficientGas },
40
51
  })
41
52
  }
@@ -43,14 +54,14 @@ export const useMessageQueue = (route?: Route, allowInteraction?: boolean) => {
43
54
  if (accountNotDeployedAtDestination && !allowInteraction) {
44
55
  queue.push({
45
56
  id: 'ACCOUNT_NOT_DEPLOYED',
46
- priority: 3,
57
+ priority: 4,
47
58
  })
48
59
  }
49
60
 
50
61
  if (requiredToAddress && !toAddress) {
51
62
  queue.push({
52
63
  id: 'TO_ADDRESS_REQUIRED',
53
- priority: 4,
64
+ priority: 5,
54
65
  })
55
66
  }
56
67
 
@@ -62,6 +73,8 @@ export const useMessageQueue = (route?: Route, allowInteraction?: boolean) => {
62
73
  accountNotDeployedAtDestination,
63
74
  requiredToAddress,
64
75
  toAddress,
76
+ missingChain,
77
+ missingAccountAddress,
65
78
  ])
66
79
 
67
80
  return {
@@ -26,13 +26,13 @@ export const RouteCard: React.FC<
26
26
  route,
27
27
  active,
28
28
  variant = 'default',
29
- expanded: defaulExpanded,
29
+ expanded: defaultExpanded,
30
30
  ...other
31
31
  }) => {
32
32
  const { t } = useTranslation()
33
33
  const { subvariant, subvariantOptions, routeLabels, hiddenUI } =
34
34
  useWidgetConfig()
35
- const [cardExpanded, setCardExpanded] = useState(defaulExpanded)
35
+ const [cardExpanded, setCardExpanded] = useState(defaultExpanded)
36
36
 
37
37
  const handleExpand: MouseEventHandler<HTMLButtonElement> = (e) => {
38
38
  e.stopPropagation()
@@ -103,7 +103,7 @@ export const RouteCard: React.FC<
103
103
  HiddenUI.RouteTokenDescription
104
104
  )}
105
105
  />
106
- {!defaulExpanded ? (
106
+ {!defaultExpanded ? (
107
107
  <CardIconButton onClick={handleExpand} size="small">
108
108
  {cardExpanded ? (
109
109
  <ExpandLess fontSize="inherit" />
@@ -85,7 +85,7 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
85
85
  isBalanceLoading,
86
86
  }) => {
87
87
  const { t } = useTranslation()
88
- const { getTokenAddressLink } = useExplorer()
88
+ const { getAddressLink } = useExplorer()
89
89
 
90
90
  const container = useRef(null)
91
91
  const timeoutId = useRef<ReturnType<typeof setTimeout>>(undefined)
@@ -184,7 +184,7 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
184
184
  <IconButton
185
185
  size="small"
186
186
  LinkComponent={Link}
187
- href={getTokenAddressLink(tokenAddress!, chain)}
187
+ href={getAddressLink(tokenAddress!, chain)}
188
188
  target="_blank"
189
189
  rel="nofollow noreferrer"
190
190
  onClick={(e) => e.stopPropagation()}
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget'
2
- export const version = '3.21.4-alpha.0'
2
+ export const version = '3.22.0'
@@ -4,6 +4,7 @@ import { useQuery } from '@tanstack/react-query'
4
4
  import { useCallback } from 'react'
5
5
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
6
6
  import { isItemAllowed } from '../utils/item.js'
7
+ import { getQueryKey } from '../utils/queries.js'
7
8
 
8
9
  export type GetChainById = (
9
10
  chainId?: number,
@@ -18,11 +19,12 @@ const supportedChainTypes = [
18
19
  ]
19
20
 
20
21
  export const useAvailableChains = (chainTypes?: ChainType[]) => {
21
- const { chains } = useWidgetConfig()
22
+ const { chains, keyPrefix } = useWidgetConfig()
22
23
  // const { providers } = useHasExternalWalletProvider();
24
+
23
25
  const { data, isLoading } = useQuery({
24
26
  queryKey: [
25
- 'chains',
27
+ getQueryKey('chains', keyPrefix),
26
28
  // providers,
27
29
  chains?.types,
28
30
  chains?.allow,
@@ -1,4 +1,4 @@
1
- import type { Chain, EVMChain } from '@lifi/sdk'
1
+ import type { Chain } from '@lifi/sdk'
2
2
  import { ChainId } from '@lifi/sdk'
3
3
  import { lifiExplorerUrl } from '../config/constants.js'
4
4
  import { useAvailableChains } from '../hooks/useAvailableChains.js'
@@ -21,16 +21,37 @@ export const useExplorer = () => {
21
21
  const { explorerUrls } = useWidgetConfig()
22
22
  const { getChainById } = useAvailableChains()
23
23
 
24
- const getBaseUrl = (chain: Chain) => {
25
- const baseUrl = explorerUrls?.[chain.id]
26
- ? explorerUrls[chain.id][0]
27
- : chain.metamask.blockExplorerUrls[0]
24
+ const getExplorerConfig = (chain?: Chain | number) => {
25
+ const resolvedChain = Number.isFinite(chain)
26
+ ? getChainById(chain as number)
27
+ : (chain as Chain)
28
28
 
29
- return sanitiseBaseUrl(baseUrl)
30
- }
29
+ const explorerUrl =
30
+ (resolvedChain
31
+ ? (explorerUrls?.[resolvedChain.id]?.[0] ??
32
+ resolvedChain.metamask.blockExplorerUrls[0])
33
+ : explorerUrls?.internal?.[0]) || lifiExplorerUrl
34
+
35
+ const url = typeof explorerUrl === 'string' ? explorerUrl : explorerUrl.url
36
+
37
+ const defaultTxPath = resolvedChain?.id === ChainId.SUI ? 'txblock' : 'tx'
38
+ const defaultAddressPath =
39
+ resolvedChain?.id === ChainId.SUI ? 'coin' : 'address'
40
+ const txPath =
41
+ typeof explorerUrl === 'string'
42
+ ? defaultTxPath
43
+ : explorerUrl.txPath || defaultTxPath
44
+ const addressPath =
45
+ typeof explorerUrl === 'string'
46
+ ? defaultAddressPath
47
+ : explorerUrl.addressPath || defaultAddressPath
31
48
 
32
- const resolveChain = (chain: Chain | number) =>
33
- Number.isFinite(chain) ? getChainById(chain as number) : (chain as Chain)
49
+ return {
50
+ url: sanitiseBaseUrl(url),
51
+ txPath,
52
+ addressPath,
53
+ }
54
+ }
34
55
 
35
56
  const getTransactionLink = ({
36
57
  txHash,
@@ -40,40 +61,18 @@ export const useExplorer = () => {
40
61
  if (!txHash && txLink) {
41
62
  return txLink
42
63
  }
43
- if (!chain) {
44
- const baseUrl = explorerUrls?.internal?.[0]
45
- ? sanitiseBaseUrl(explorerUrls?.internal[0])
46
- : lifiExplorerUrl
47
- return `${baseUrl}/tx/${txHash}`
48
- }
49
- const resolvedChain = resolveChain(chain)
50
- const txPathSegment = resolvedChain?.id === ChainId.SUI ? 'txblock' : 'tx'
51
- return `${resolvedChain ? getBaseUrl(resolvedChain) : lifiExplorerUrl}/${txPathSegment}/${txHash}`
52
- }
53
-
54
- const getAddressLink = (address: string, chain?: Chain | number) => {
55
- if (!chain) {
56
- const baseUrl = explorerUrls?.internal?.[0]
57
- ? sanitiseBaseUrl(explorerUrls?.internal[0])
58
- : lifiExplorerUrl
59
- return `${baseUrl}/address/${address}`
60
- }
61
64
 
62
- const resolvedChain = resolveChain(chain)
63
- return `${resolvedChain ? getBaseUrl(resolvedChain) : lifiExplorerUrl}/address/${address}`
65
+ const config = getExplorerConfig(chain)
66
+ return `${config.url}/${config.txPath}/${txHash}`
64
67
  }
65
68
 
66
- const getTokenAddressLink = (address: string, chain?: Chain | number) => {
67
- const link = getAddressLink(address, chain)
68
- if (chain === ChainId.SUI || (chain as EVMChain)?.id === ChainId.SUI) {
69
- return link.replace('address', 'coin')
70
- }
71
- return link
69
+ const getAddressLink = (address: string, chain?: Chain | number) => {
70
+ const config = getExplorerConfig(chain)
71
+ return `${config.url}/${config.addressPath}/${address}`
72
72
  }
73
73
 
74
74
  return {
75
75
  getTransactionLink,
76
76
  getAddressLink,
77
- getTokenAddressLink,
78
77
  }
79
78
  }
@@ -2,8 +2,10 @@ import type { RouteExtended } from '@lifi/sdk'
2
2
  import { useAccount } from '@lifi/wallet-management'
3
3
  import { keepPreviousData, useQuery } from '@tanstack/react-query'
4
4
  import { parseUnits } from 'viem'
5
+ import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
5
6
  import { useFieldValues } from '../stores/form/useFieldValues.js'
6
7
  import { isRouteDone } from '../stores/routes/utils.js'
8
+ import { getQueryKey } from '../utils/queries.js'
7
9
  import { useTokenAddressBalance } from './useTokenAddressBalance.js'
8
10
  import { getTokenBalancesWithRetry } from './useTokenBalance.js'
9
11
 
@@ -15,6 +17,7 @@ export const useFromTokenSufficiency = (route?: RouteExtended) => {
15
17
  'fromToken',
16
18
  'fromAmount'
17
19
  )
20
+ const { keyPrefix } = useWidgetConfig()
18
21
 
19
22
  let chainId = fromChainId
20
23
  let tokenAddress = fromTokenAddress
@@ -33,7 +36,7 @@ export const useFromTokenSufficiency = (route?: RouteExtended) => {
33
36
 
34
37
  const { data: insufficientFromToken, isLoading } = useQuery({
35
38
  queryKey: [
36
- 'from-token-sufficiency-check',
39
+ getQueryKey('from-token-sufficiency-check', keyPrefix),
37
40
  account.address,
38
41
  chainId,
39
42
  tokenAddress,
@@ -1,5 +1,7 @@
1
1
  import { type ChainId, getGasRecommendation } from '@lifi/sdk'
2
2
  import { useQuery } from '@tanstack/react-query'
3
+ import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
4
+ import { getQueryKey } from '../utils/queries.js'
3
5
  import { useAvailableChains } from './useAvailableChains.js'
4
6
 
5
7
  const refetchInterval = 60_000
@@ -10,6 +12,7 @@ export const useGasRecommendation = (
10
12
  fromToken?: string
11
13
  ) => {
12
14
  const { chains } = useAvailableChains()
15
+ const { keyPrefix } = useWidgetConfig()
13
16
 
14
17
  const checkRecommendationLiFuel =
15
18
  Boolean(toChainId) &&
@@ -21,7 +24,12 @@ export const useGasRecommendation = (
21
24
  Boolean(toChainId) && !fromChain && !fromToken && Boolean(chains?.length)
22
25
 
23
26
  return useQuery({
24
- queryKey: ['gas-recommendation', toChainId, fromChain, fromToken],
27
+ queryKey: [
28
+ getQueryKey('gas-recommendation', keyPrefix),
29
+ toChainId,
30
+ fromChain,
31
+ fromToken,
32
+ ],
25
33
  queryFn: async ({
26
34
  queryKey: [_, toChainId, fromChain, fromToken],
27
35
  signal,
@@ -1,7 +1,10 @@
1
- import type { EVMChain, RouteExtended, Token } from '@lifi/sdk'
2
- import { isRelayerStep } from '@lifi/sdk'
1
+ import type { EVMChain, RouteExtended, Token, TokenAmount } from '@lifi/sdk'
2
+ import { ChainType, isRelayerStep } from '@lifi/sdk'
3
3
  import { useAccount } from '@lifi/wallet-management'
4
4
  import { useQuery } from '@tanstack/react-query'
5
+ import { useMemo } from 'react'
6
+ import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
7
+ import { getQueryKey } from '../utils/queries.js'
5
8
  import { useAvailableChains } from './useAvailableChains.js'
6
9
  import { useIsContractAddress } from './useIsContractAddress.js'
7
10
  import { getTokenBalancesWithRetry } from './useTokenBalance.js'
@@ -19,30 +22,64 @@ const refetchInterval = 30_000
19
22
 
20
23
  export const useGasSufficiency = (route?: RouteExtended) => {
21
24
  const { getChainById } = useAvailableChains()
22
- const { account } = useAccount({
23
- chainType: getChainById(route?.fromChainId)?.chainType,
25
+ const { account: EVMAccount, accounts } = useAccount({
26
+ chainType: ChainType.EVM,
24
27
  })
28
+ const { keyPrefix } = useWidgetConfig()
29
+
30
+ const { relevantAccounts, relevantAccountsQueryKey } = useMemo(() => {
31
+ const chainTypes = route?.steps.reduce((acc, step) => {
32
+ const chainType = getChainById(step.action.fromChainId)?.chainType
33
+ if (chainType) {
34
+ acc.add(chainType)
35
+ }
36
+ return acc
37
+ }, new Set<ChainType>())
38
+
39
+ const relevantAccounts = accounts.filter(
40
+ (account) =>
41
+ account.isConnected &&
42
+ account.address &&
43
+ chainTypes?.has(account.chainType)
44
+ )
45
+ return {
46
+ relevantAccounts,
47
+ relevantAccountsQueryKey: relevantAccounts
48
+ .map((account) => account.address)
49
+ .join(','),
50
+ }
51
+ }, [accounts, route?.steps, getChainById])
25
52
 
26
53
  const { isContractAddress, isLoading: isContractAddressLoading } =
27
- useIsContractAddress(account.address, route?.fromChainId, account.chainType)
54
+ useIsContractAddress(
55
+ EVMAccount.address,
56
+ route?.fromChainId,
57
+ EVMAccount.chainType
58
+ )
28
59
 
29
60
  const { data: insufficientGas, isLoading } = useQuery({
30
61
  queryKey: [
31
- 'gas-sufficiency-check',
32
- account.address,
62
+ getQueryKey('gas-sufficiency-check', keyPrefix),
63
+ relevantAccountsQueryKey,
33
64
  route?.id,
34
65
  isContractAddress,
35
66
  ] as const,
36
- queryFn: async ({ queryKey: [, accountAddress] }) => {
67
+ queryFn: async () => {
37
68
  if (!route) {
38
69
  return
39
70
  }
40
71
 
41
- // If we have a relayer step with a permit (EIP-2612) for the from token, we don't need to check for gas sufficiency
42
- if (
43
- isRelayerStep(route.steps[0]) &&
44
- route.steps[0].typedData.some((t) => t.primaryType === 'Permit')
45
- ) {
72
+ // Filter out steps that are relayer steps or have primaryType 'Permit' or 'Order'
73
+ const filteredSteps = route.steps.filter(
74
+ (step) =>
75
+ !isRelayerStep(step) &&
76
+ !step.typedData?.some(
77
+ (t) => t.primaryType === 'Permit' || t.primaryType === 'Order'
78
+ )
79
+ )
80
+
81
+ // If all steps are filtered out, we don't need to check for gas sufficiency
82
+ if (!filteredSteps.length) {
46
83
  return
47
84
  }
48
85
 
@@ -51,7 +88,7 @@ export const useGasSufficiency = (route?: RouteExtended) => {
51
88
  .flatMap((step) => step.includedSteps)
52
89
  .some((includedStep) => includedStep.tool === 'gasZip')
53
90
 
54
- const gasCosts = route.steps
91
+ const gasCosts = filteredSteps
55
92
  .filter((step) => !step.execution || step.execution.status !== 'DONE')
56
93
  .reduce(
57
94
  (groupedGasCosts, step) => {
@@ -70,6 +107,7 @@ export const useGasSufficiency = (route?: RouteExtended) => {
70
107
  ? groupedGasCosts[token.chainId].gasAmount + gasCostAmount
71
108
  : gasCostAmount,
72
109
  token,
110
+ chain: getChainById(token.chainId),
73
111
  }
74
112
  }
75
113
  // Add fees paid in native tokens to gas sufficiency check (included: false)
@@ -88,11 +126,12 @@ export const useGasSufficiency = (route?: RouteExtended) => {
88
126
  ? groupedGasCosts[token.chainId].gasAmount + feeCostAmount
89
127
  : feeCostAmount,
90
128
  token,
91
- } as any
129
+ chain: getChainById(token.chainId),
130
+ }
92
131
  }
93
132
  return groupedGasCosts
94
133
  },
95
- {} as Record<number, GasSufficiency>
134
+ {} as Record<string, GasSufficiency>
96
135
  )
97
136
 
98
137
  // Check whether we are sending a native token
@@ -104,15 +143,30 @@ export const useGasSufficiency = (route?: RouteExtended) => {
104
143
  gasCosts[route.fromChainId]?.gasAmount + BigInt(route.fromAmount)
105
144
  }
106
145
 
107
- const tokenBalances = await getTokenBalancesWithRetry(
108
- accountAddress!,
109
- Object.values(gasCosts).map((item) => item.token)
146
+ const gasCostsValues = Object.values(gasCosts)
147
+
148
+ const balanceChecks = await Promise.allSettled(
149
+ relevantAccounts.map((account) => {
150
+ const relevantTokens = gasCostsValues
151
+ .filter((gasCost) => gasCost.chain?.chainType === account.chainType)
152
+ .map((item) => item.token)
153
+
154
+ return getTokenBalancesWithRetry(account.address!, relevantTokens)
155
+ })
110
156
  )
111
157
 
158
+ const tokenBalances = balanceChecks
159
+ .filter(
160
+ (result): result is PromiseFulfilledResult<TokenAmount[]> =>
161
+ result.status === 'fulfilled' && Boolean(result.value)
162
+ )
163
+ .flatMap((result) => result.value)
164
+
112
165
  if (!tokenBalances?.length) {
113
166
  return
114
167
  }
115
- ;[route.fromChainId, route.toChainId].forEach((chainId) => {
168
+
169
+ Object.keys(gasCosts).forEach((chainId) => {
116
170
  if (gasCosts[chainId]) {
117
171
  const gasTokenBalance =
118
172
  tokenBalances?.find(
@@ -135,7 +189,7 @@ export const useGasSufficiency = (route?: RouteExtended) => {
135
189
  ...gasCosts[chainId],
136
190
  insufficient,
137
191
  insufficientAmount,
138
- chain: insufficient ? getChainById(chainId) : undefined,
192
+ chain: insufficient ? getChainById(Number(chainId)) : undefined,
139
193
  }
140
194
  }
141
195
  })
@@ -150,7 +204,7 @@ export const useGasSufficiency = (route?: RouteExtended) => {
150
204
  enabled: Boolean(
151
205
  !isContractAddress &&
152
206
  !isContractAddressLoading &&
153
- account.address &&
207
+ relevantAccounts.length > 0 &&
154
208
  route
155
209
  ),
156
210
  refetchInterval,
@@ -1,16 +1,24 @@
1
1
  import { ChainType, isBatchingSupported } from '@lifi/sdk'
2
2
  import type { ExtendedChain } from '@lifi/sdk'
3
3
  import { useQuery } from '@tanstack/react-query'
4
+ import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
5
+ import { getQueryKey } from '../utils/queries.js'
4
6
 
5
7
  export function useIsBatchingSupported(
6
8
  chain?: ExtendedChain,
7
9
  address?: string
8
10
  ) {
11
+ const { keyPrefix } = useWidgetConfig()
12
+
9
13
  const enabled = Boolean(
10
14
  chain && chain.chainType === ChainType.EVM && !!address
11
15
  )
12
16
  const { data, isLoading } = useQuery({
13
- queryKey: ['isBatchingSupported', chain?.id, address],
17
+ queryKey: [
18
+ getQueryKey('isBatchingSupported', keyPrefix),
19
+ chain?.id,
20
+ address,
21
+ ],
14
22
  queryFn: () => {
15
23
  return isBatchingSupported({ chainId: chain!.id })
16
24
  },
@@ -22,7 +22,7 @@ export const useIsContractAddress = (
22
22
  query: {
23
23
  refetchInterval: 300_000,
24
24
  staleTime: 300_000,
25
- enabled: Boolean(chainType === ChainType.EVM && chainId),
25
+ enabled: chainType === ChainType.EVM && !!chainId && !!address,
26
26
  },
27
27
  })
28
28