@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.
- package/README.md +26 -3
- package/dist/esm/components/AmountInputCard/AmountInputCardPair.js +5 -2
- package/dist/esm/components/AmountInputCard/AmountInputCardPair.js.map +1 -1
- package/dist/esm/components/SelectChainAndToken.js +5 -2
- package/dist/esm/components/SelectChainAndToken.js.map +1 -1
- package/dist/esm/components/SendToWallet/SendToWalletButton.js +5 -4
- package/dist/esm/components/SendToWallet/SendToWalletButton.js.map +1 -1
- package/dist/esm/components/TokenList/TokenDetailsSheet.js +5 -8
- package/dist/esm/components/TokenList/TokenDetailsSheet.js.map +1 -1
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.js +30 -2
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.js.map +1 -1
- package/dist/esm/components/TokenList/TokenListItem.js +36 -47
- package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
- package/dist/esm/components/TokenList/VirtualizedTokenList.d.ts.map +1 -1
- package/dist/esm/components/TokenList/VirtualizedTokenList.js +2 -2
- package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
- package/dist/esm/components/TokenList/tokenVerificationIcons.js +29 -0
- package/dist/esm/components/TokenList/tokenVerificationIcons.js.map +1 -0
- package/dist/esm/components/TokenList/useTokenSelect.d.ts.map +1 -1
- package/dist/esm/components/TokenList/useTokenSelect.js +10 -0
- package/dist/esm/components/TokenList/useTokenSelect.js.map +1 -1
- package/dist/esm/config/constants.js.map +1 -1
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/config/version.js.map +1 -1
- package/dist/esm/hooks/useAccountsBalancesData.js +5 -2
- package/dist/esm/hooks/useAccountsBalancesData.js.map +1 -1
- package/dist/esm/hooks/useAddressValidation.d.ts +3 -2
- package/dist/esm/hooks/useAddressValidation.d.ts.map +1 -1
- package/dist/esm/hooks/useAddressValidation.js +48 -21
- package/dist/esm/hooks/useAddressValidation.js.map +1 -1
- package/dist/esm/hooks/useGasRefuel.js +4 -1
- package/dist/esm/hooks/useGasRefuel.js.map +1 -1
- package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
- package/dist/esm/hooks/useRoutes.js +11 -6
- package/dist/esm/hooks/useRoutes.js.map +1 -1
- package/dist/esm/hooks/useToAddressAutoPopulate.js +6 -8
- package/dist/esm/hooks/useToAddressAutoPopulate.js.map +1 -1
- package/dist/esm/hooks/useToAddressRequirements.d.ts +3 -2
- package/dist/esm/hooks/useToAddressRequirements.d.ts.map +1 -1
- package/dist/esm/hooks/useToAddressRequirements.js +8 -7
- package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
- package/dist/esm/hooks/useToAddressReset.js +6 -3
- package/dist/esm/hooks/useToAddressReset.js.map +1 -1
- package/dist/esm/hooks/useTokenBalance.js +2 -1
- package/dist/esm/hooks/useTokenBalance.js.map +1 -1
- package/dist/esm/hooks/useTokens.js +1 -1
- package/dist/esm/i18n/bn.json +7 -2
- package/dist/esm/i18n/de.json +7 -2
- package/dist/esm/i18n/en.json +7 -2
- package/dist/esm/i18n/es.json +7 -2
- package/dist/esm/i18n/fr.json +7 -2
- package/dist/esm/i18n/hi.json +7 -2
- package/dist/esm/i18n/id.json +7 -2
- package/dist/esm/i18n/it.json +7 -2
- package/dist/esm/i18n/ja.json +7 -2
- package/dist/esm/i18n/ko.json +7 -2
- package/dist/esm/i18n/pl.json +7 -2
- package/dist/esm/i18n/pt.json +7 -2
- package/dist/esm/i18n/th.json +7 -2
- package/dist/esm/i18n/tr.json +7 -2
- package/dist/esm/i18n/uk.json +7 -2
- package/dist/esm/i18n/vi.json +7 -2
- package/dist/esm/i18n/zh.json +7 -2
- package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js +7 -4
- package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js.map +1 -1
- package/dist/esm/pages/SendToWallet/BookmarksPage.js +35 -31
- package/dist/esm/pages/SendToWallet/BookmarksPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js +4 -3
- package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js +43 -39
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js +32 -26
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToWalletPage.js +5 -10
- package/dist/esm/pages/SendToWallet/SendToWalletPage.js.map +1 -1
- package/dist/esm/providers/WalletProvider/WalletProvider.d.ts.map +1 -1
- package/dist/esm/providers/WalletProvider/WalletProvider.js +14 -2
- package/dist/esm/providers/WalletProvider/WalletProvider.js.map +1 -1
- package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts.map +1 -1
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js +10 -14
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
- package/dist/esm/stores/bookmarks/types.d.ts +3 -0
- package/dist/esm/stores/bookmarks/types.d.ts.map +1 -1
- package/dist/esm/stores/chains/ChainOrderStore.d.ts.map +1 -1
- package/dist/esm/stores/chains/ChainOrderStore.js +4 -1
- package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
- package/dist/esm/stores/form/FormStore.js +3 -2
- package/dist/esm/stores/form/FormStore.js.map +1 -1
- package/dist/esm/stores/form/URLSearchParamsBuilder.js +7 -3
- package/dist/esm/stores/form/URLSearchParamsBuilder.js.map +1 -1
- package/dist/esm/stores/form/getDefaultValuesFromQueryString.js +4 -1
- package/dist/esm/stores/form/getDefaultValuesFromQueryString.js.map +1 -1
- package/dist/esm/utils/chainType.js +32 -1
- package/dist/esm/utils/chainType.js.map +1 -1
- package/dist/esm/utils/customReceiver.js +6 -1
- package/dist/esm/utils/customReceiver.js.map +1 -1
- package/dist/esm/utils/token.js +13 -1
- package/dist/esm/utils/token.js.map +1 -1
- package/package.json +7 -7
- package/src/components/AmountInputCard/AmountInputCardPair.tsx +6 -2
- package/src/components/SelectChainAndToken.tsx +5 -1
- package/src/components/SendToWallet/SendToWalletButton.tsx +13 -8
- package/src/components/TokenList/TokenDetailsSheet.tsx +5 -9
- package/src/components/TokenList/TokenDetailsSheetContent.tsx +46 -4
- package/src/components/TokenList/TokenListItem.tsx +44 -68
- package/src/components/TokenList/VirtualizedTokenList.tsx +3 -6
- package/src/components/TokenList/tokenVerificationIcons.ts +15 -0
- package/src/components/TokenList/types.ts +3 -11
- package/src/components/TokenList/useTokenSelect.ts +15 -0
- package/src/config/constants.ts +3 -0
- package/src/config/version.ts +1 -1
- package/src/hooks/useAccountsBalancesData.ts +6 -2
- package/src/hooks/useAddressValidation.ts +87 -32
- package/src/hooks/useGasRefuel.ts +11 -1
- package/src/hooks/useRoutes.ts +23 -18
- package/src/hooks/useToAddressAutoPopulate.ts +13 -17
- package/src/hooks/useToAddressRequirements.ts +19 -4
- package/src/hooks/useToAddressReset.ts +13 -4
- package/src/hooks/useTokenBalance.ts +6 -1
- package/src/pages/SendToWallet/BookmarkAddressSheet.tsx +5 -1
- package/src/pages/SendToWallet/BookmarksPage.tsx +42 -52
- package/src/pages/SendToWallet/ConnectedWalletsPage.tsx +4 -14
- package/src/pages/SendToWallet/RecentWalletsPage.tsx +59 -66
- package/src/pages/SendToWallet/SendToConfiguredWalletPage.tsx +45 -50
- package/src/pages/SendToWallet/SendToWalletPage.tsx +4 -16
- package/src/providers/WalletProvider/WalletProvider.tsx +28 -3
- package/src/providers/WidgetProvider/WidgetProvider.tsx +20 -19
- package/src/stores/bookmarks/types.ts +3 -0
- package/src/stores/chains/ChainOrderStore.tsx +4 -1
- package/src/stores/form/FormStore.tsx +2 -0
- package/src/stores/form/URLSearchParamsBuilder.tsx +4 -2
- package/src/stores/form/getDefaultValuesFromQueryString.ts +19 -0
- package/src/utils/chainType.ts +73 -1
- package/src/utils/customReceiver.ts +36 -0
- package/src/utils/token.ts +48 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useChainTypeFromAddress } from '@lifi/widget-provider'
|
|
1
2
|
import ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'
|
|
2
3
|
import OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'
|
|
3
4
|
import { List, ListItemAvatar, ListItemText } from '@mui/material'
|
|
@@ -25,7 +26,8 @@ export const SendToConfiguredWalletPage = (): JSX.Element => {
|
|
|
25
26
|
const { t } = useTranslation()
|
|
26
27
|
const navigateBack = useNavigateBack()
|
|
27
28
|
const { toAddresses } = useWidgetConfig()
|
|
28
|
-
const {
|
|
29
|
+
const { isValidReceiver } = useToAddressRequirements()
|
|
30
|
+
const { getChainFromAddress } = useChainTypeFromAddress()
|
|
29
31
|
const { setSelectedBookmark } = useBookmarkActions()
|
|
30
32
|
const { setFieldValue } = useFieldActions()
|
|
31
33
|
const { getAddressLink } = useExplorer()
|
|
@@ -53,57 +55,50 @@ export const SendToConfiguredWalletPage = (): JSX.Element => {
|
|
|
53
55
|
disablePadding
|
|
54
56
|
sx={{ paddingTop: 1.5 }}
|
|
55
57
|
>
|
|
56
|
-
{toAddresses?.map((toAddress) =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
58
|
+
{toAddresses?.map((toAddress) => {
|
|
59
|
+
const disabled = !isValidReceiver(toAddress.address)
|
|
60
|
+
// A configured receiver has no chainId; its provider knows the chain.
|
|
61
|
+
const chainId =
|
|
62
|
+
getChainFromAddress(toAddress.address)?.chainId ??
|
|
63
|
+
defaultChainIdsByType[toAddress.chainType]
|
|
64
|
+
return (
|
|
65
|
+
<ListItem key={toAddress.address} sx={{ position: 'relative' }}>
|
|
66
|
+
<ListItemButton
|
|
67
|
+
disabled={disabled}
|
|
68
|
+
onClick={() => handleCuratedSelected(toAddress)}
|
|
69
|
+
>
|
|
70
|
+
<ListItemAvatar>
|
|
71
|
+
<AccountAvatar chainId={chainId} toAddress={toAddress} />
|
|
72
|
+
</ListItemAvatar>
|
|
73
|
+
<ListItemText
|
|
74
|
+
primary={toAddress.name || shortenAddress(toAddress.address)}
|
|
75
|
+
secondary={
|
|
76
|
+
toAddress.name
|
|
77
|
+
? shortenAddress(toAddress.address)
|
|
78
|
+
: undefined
|
|
79
|
+
}
|
|
69
80
|
/>
|
|
70
|
-
</
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
</ListItemButton>
|
|
82
|
+
<ContextMenu
|
|
83
|
+
disabled={disabled}
|
|
84
|
+
items={[
|
|
85
|
+
{
|
|
86
|
+
icon: <ContentCopyRounded />,
|
|
87
|
+
label: t('button.copyAddress'),
|
|
88
|
+
onClick: () =>
|
|
89
|
+
navigator.clipboard.writeText(toAddress.address),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
icon: <OpenInNewRounded />,
|
|
93
|
+
label: t('button.viewOnExplorer'),
|
|
94
|
+
onClick: () =>
|
|
95
|
+
openInNewTab(getAddressLink(toAddress.address, chainId)),
|
|
96
|
+
},
|
|
97
|
+
]}
|
|
76
98
|
/>
|
|
77
|
-
</
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
!!(
|
|
81
|
-
requiredToChainType &&
|
|
82
|
-
requiredToChainType !== toAddress.chainType
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
items={[
|
|
86
|
-
{
|
|
87
|
-
icon: <ContentCopyRounded />,
|
|
88
|
-
label: t('button.copyAddress'),
|
|
89
|
-
onClick: () =>
|
|
90
|
-
navigator.clipboard.writeText(toAddress.address),
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
icon: <OpenInNewRounded />,
|
|
94
|
-
label: t('button.viewOnExplorer'),
|
|
95
|
-
onClick: () =>
|
|
96
|
-
openInNewTab(
|
|
97
|
-
getAddressLink(
|
|
98
|
-
toAddress.address,
|
|
99
|
-
defaultChainIdsByType[toAddress.chainType]
|
|
100
|
-
)
|
|
101
|
-
),
|
|
102
|
-
},
|
|
103
|
-
]}
|
|
104
|
-
/>
|
|
105
|
-
</ListItem>
|
|
106
|
-
))}
|
|
99
|
+
</ListItem>
|
|
100
|
+
)
|
|
101
|
+
})}
|
|
107
102
|
</List>
|
|
108
103
|
</PageContainer>
|
|
109
104
|
)
|
|
@@ -12,13 +12,13 @@ import type { BottomSheetBase } from '../../components/BottomSheet/types.js'
|
|
|
12
12
|
import { ButtonTertiary } from '../../components/ButtonTertiary.js'
|
|
13
13
|
import { CardButton } from '../../components/Card/CardButton.js'
|
|
14
14
|
import { AccountDeployedMessage } from '../../components/Messages/AccountDeployedMessage.js'
|
|
15
|
+
import { addressInputMaxLength } from '../../config/constants.js'
|
|
15
16
|
import {
|
|
16
17
|
AddressType,
|
|
17
18
|
useAddressValidation,
|
|
18
19
|
} from '../../hooks/useAddressValidation.js'
|
|
19
20
|
import { useChain } from '../../hooks/useChain.js'
|
|
20
21
|
import { useHeader } from '../../hooks/useHeader.js'
|
|
21
|
-
import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'
|
|
22
22
|
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
|
|
23
23
|
import type { Bookmark } from '../../stores/bookmarks/types.js'
|
|
24
24
|
import { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'
|
|
@@ -57,7 +57,6 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
57
57
|
const [validatedWallet, setValidatedWallet] = useState<Bookmark>()
|
|
58
58
|
const [errorMessage, setErrorMessage] = useState('')
|
|
59
59
|
const { validateAddress, isValidating } = useAddressValidation()
|
|
60
|
-
const { requiredToChainType } = useToAddressRequirements()
|
|
61
60
|
const [toChainId] = useFieldValues('toChain')
|
|
62
61
|
const { chain: toChain } = useChain(toChainId)
|
|
63
62
|
const [isDoneButtonLoading, setIsDoneButtonLoading] = useState(false)
|
|
@@ -91,7 +90,6 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
91
90
|
setIsDoneButtonLoading(true)
|
|
92
91
|
const validationResult = await validateAddress({
|
|
93
92
|
value: inputAddressValue,
|
|
94
|
-
chainType: requiredToChainType,
|
|
95
93
|
chain: toChain,
|
|
96
94
|
})
|
|
97
95
|
setIsDoneButtonLoading(false)
|
|
@@ -100,18 +98,6 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
100
98
|
return
|
|
101
99
|
}
|
|
102
100
|
|
|
103
|
-
if (
|
|
104
|
-
requiredToChainType &&
|
|
105
|
-
requiredToChainType !== validationResult.chainType
|
|
106
|
-
) {
|
|
107
|
-
setErrorMessage(
|
|
108
|
-
t('error.title.walletChainTypeInvalid', {
|
|
109
|
-
chainName: toChain?.name,
|
|
110
|
-
})
|
|
111
|
-
)
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
|
|
115
101
|
setValidatedWallet({
|
|
116
102
|
name:
|
|
117
103
|
validationResult.addressType === AddressType.NameService
|
|
@@ -119,6 +105,7 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
119
105
|
: undefined,
|
|
120
106
|
address: validationResult.address,
|
|
121
107
|
chainType: validationResult.chainType,
|
|
108
|
+
chainId: validationResult.chainId,
|
|
122
109
|
})
|
|
123
110
|
confirmAddressSheetRef.current?.open()
|
|
124
111
|
}
|
|
@@ -155,6 +142,7 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
155
142
|
: undefined,
|
|
156
143
|
address: validationResult.address,
|
|
157
144
|
chainType: validationResult.chainType,
|
|
145
|
+
chainId: validationResult.chainId,
|
|
158
146
|
})
|
|
159
147
|
bookmarkAddressSheetRef.current?.open()
|
|
160
148
|
} else {
|
|
@@ -213,7 +201,7 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
213
201
|
placeholder={placeholder}
|
|
214
202
|
aria-label={placeholder}
|
|
215
203
|
maxRows={2}
|
|
216
|
-
inputProps={{ maxLength:
|
|
204
|
+
inputProps={{ maxLength: addressInputMaxLength }}
|
|
217
205
|
multiline
|
|
218
206
|
/>
|
|
219
207
|
{errorMessage ? (
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { WalletManagementProviders } from '@lifi/wallet-management'
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
useSDKProviders,
|
|
4
|
+
type WidgetProviderProps,
|
|
5
|
+
} from '@lifi/widget-provider'
|
|
3
6
|
import {
|
|
4
7
|
type FC,
|
|
5
8
|
type JSX,
|
|
@@ -10,8 +13,15 @@ import {
|
|
|
10
13
|
import { useTranslation } from 'react-i18next'
|
|
11
14
|
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
|
|
12
15
|
import { useInitializeSDKProviders } from '../../hooks/useInitializeSDKProviders.js'
|
|
16
|
+
import {
|
|
17
|
+
withoutUnservedToChain,
|
|
18
|
+
withServedDestinationChains,
|
|
19
|
+
} from '../../utils/chainType.js'
|
|
13
20
|
import { getConfigItemSets, isItemAllowedForSets } from '../../utils/item.js'
|
|
14
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
useWidgetConfig,
|
|
23
|
+
WidgetContext,
|
|
24
|
+
} from '../WidgetProvider/WidgetProvider.js'
|
|
15
25
|
import { useExternalWalletProvider } from './useExternalWalletProvider.js'
|
|
16
26
|
|
|
17
27
|
interface WalletProviderProps extends PropsWithChildren {
|
|
@@ -78,5 +88,20 @@ export const WalletProvider = ({
|
|
|
78
88
|
|
|
79
89
|
const SDKProviderInitializer: FC<PropsWithChildren> = ({ children }) => {
|
|
80
90
|
useInitializeSDKProviders()
|
|
81
|
-
|
|
91
|
+
const config = useWidgetConfig()
|
|
92
|
+
const sdkProviders = useSDKProviders()
|
|
93
|
+
// Only below the provider components is it known which chains are served.
|
|
94
|
+
// Keyed on `config.chains`: the config object is new on every render.
|
|
95
|
+
const chains = useMemo(
|
|
96
|
+
() => withServedDestinationChains(config.chains, sdkProviders),
|
|
97
|
+
[config.chains, sdkProviders]
|
|
98
|
+
)
|
|
99
|
+
const value = useMemo(
|
|
100
|
+
() =>
|
|
101
|
+
withoutUnservedToChain(
|
|
102
|
+
chains === config.chains ? config : { ...config, chains }
|
|
103
|
+
),
|
|
104
|
+
[config, chains]
|
|
105
|
+
)
|
|
106
|
+
return <WidgetContext value={value}>{children}</WidgetContext>
|
|
82
107
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { Context } from 'react'
|
|
2
2
|
import { createContext, use, useId, useMemo } from 'react'
|
|
3
3
|
import { useSettingsActions } from '../../stores/settings/useSettingsActions.js'
|
|
4
|
+
import {
|
|
5
|
+
isDestinationOnlyChain,
|
|
6
|
+
withDestinationOnlyChains,
|
|
7
|
+
} from '../../utils/chainType.js'
|
|
4
8
|
import {
|
|
5
9
|
defaultQueryScopeKey,
|
|
6
10
|
resolveQueryScopeKey,
|
|
@@ -29,33 +33,30 @@ export const WidgetProvider: React.FC<
|
|
|
29
33
|
throw new Error('Required property "integrator" is missing.')
|
|
30
34
|
}
|
|
31
35
|
|
|
36
|
+
// Keyed on the integrator's `chains`: the config object is new on every render.
|
|
37
|
+
const chains = useMemo(
|
|
38
|
+
() => withDestinationOnlyChains(widgetConfig.chains),
|
|
39
|
+
[widgetConfig.chains]
|
|
40
|
+
)
|
|
41
|
+
|
|
32
42
|
const value = useMemo((): WidgetContextProps => {
|
|
33
|
-
//
|
|
34
|
-
const
|
|
43
|
+
// Omitted, not undefined: an own undefined key resets the form field.
|
|
44
|
+
const { fromChain, fromToken, ...config } = widgetConfig
|
|
45
|
+
const value = {
|
|
46
|
+
...(isDestinationOnlyChain(fromChain) ? config : widgetConfig),
|
|
47
|
+
chains,
|
|
48
|
+
// Resolved once, so nothing below reads the deprecated keyPrefix.
|
|
35
49
|
storageScopeKey: resolveStorageScopeKey(widgetConfig),
|
|
36
50
|
queryScopeKey: resolveQueryScopeKey(widgetConfig),
|
|
37
|
-
|
|
51
|
+
elementId,
|
|
52
|
+
} as WidgetContextProps
|
|
38
53
|
try {
|
|
39
|
-
// Create widget configuration object
|
|
40
|
-
const value = {
|
|
41
|
-
...widgetConfig,
|
|
42
|
-
...scopeKeys,
|
|
43
|
-
elementId,
|
|
44
|
-
} as WidgetContextProps
|
|
45
|
-
|
|
46
54
|
// Set default settings for widget settings store
|
|
47
55
|
setDefaultSettings(value)
|
|
48
|
-
|
|
49
|
-
return value
|
|
50
56
|
} catch (e) {
|
|
51
57
|
console.warn(e)
|
|
52
|
-
return {
|
|
53
|
-
...widgetConfig,
|
|
54
|
-
...scopeKeys,
|
|
55
|
-
elementId,
|
|
56
|
-
integrator: widgetConfig.integrator,
|
|
57
|
-
}
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
return value
|
|
60
|
+
}, [elementId, widgetConfig, chains, setDefaultSettings])
|
|
60
61
|
return <WidgetContext value={value}>{children}</WidgetContext>
|
|
61
62
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { ChainId } from '@lifi/sdk'
|
|
1
2
|
import type { StoreApi, UseBoundStore } from 'zustand'
|
|
2
3
|
import type { ToAddress } from '../../types/widget.js'
|
|
3
4
|
|
|
4
5
|
export interface Bookmark extends ToAddress {
|
|
5
6
|
isConnectedAccount?: boolean
|
|
7
|
+
/** Display only: set when the ecosystem's default chain rejects the address. */
|
|
8
|
+
chainId?: ChainId
|
|
6
9
|
}
|
|
7
10
|
|
|
8
11
|
export interface BookmarkProps {
|
|
@@ -80,7 +80,10 @@ export function ChainOrderStoreProvider({
|
|
|
80
80
|
|
|
81
81
|
// Initialize the isAllNetworks with true if the tab is shown,
|
|
82
82
|
// there is no config chain value and no url chain value
|
|
83
|
-
const urlValues = getDefaultValuesFromQueryString({
|
|
83
|
+
const urlValues = getDefaultValuesFromQueryString({
|
|
84
|
+
buildUrl,
|
|
85
|
+
chains: chainsConfig,
|
|
86
|
+
})
|
|
84
87
|
const urlChainValue =
|
|
85
88
|
key === 'from' ? urlValues.fromChain : urlValues.toChain
|
|
86
89
|
const configChainValue =
|
|
@@ -45,6 +45,7 @@ export const FormStoreProvider: React.FC<FormStoreProviderProps> = ({
|
|
|
45
45
|
toAddress,
|
|
46
46
|
formUpdateKey,
|
|
47
47
|
buildUrl,
|
|
48
|
+
chains,
|
|
48
49
|
} = widgetConfig
|
|
49
50
|
|
|
50
51
|
const storeRef = useRef<FormStoreStore>(null)
|
|
@@ -113,6 +114,7 @@ export const FormStoreProvider: React.FC<FormStoreProviderProps> = ({
|
|
|
113
114
|
const queryDefaults = getDefaultValuesFromQueryString({
|
|
114
115
|
buildUrl,
|
|
115
116
|
includeToAddress: false,
|
|
117
|
+
chains,
|
|
116
118
|
})
|
|
117
119
|
storeRef.current = createFormStore(
|
|
118
120
|
initialiseDefaultValues({
|
|
@@ -34,16 +34,17 @@ export const URLSearchParamsBuilder = () => {
|
|
|
34
34
|
const values = useFieldValues(...formValueKeys, ...limitFormValueKeys)
|
|
35
35
|
const { setSelectedBookmark, addRecentWallet } = useBookmarkActions()
|
|
36
36
|
const { validateAddress } = useAddressValidation()
|
|
37
|
-
const { buildUrl, mode } = useWidgetConfig()
|
|
37
|
+
const { buildUrl, mode, chains } = useWidgetConfig()
|
|
38
38
|
// Using these methods as trying to use the touchedFields and values above
|
|
39
39
|
// often has a lag that can effect the widgets initialisation sequence
|
|
40
40
|
// and accidentally cause values to be wiped from the query string
|
|
41
41
|
const { getFieldValues, isTouched, setUserAndDefaultValues } =
|
|
42
42
|
useFieldActions()
|
|
43
43
|
|
|
44
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: the URL is read once, not again on each config render
|
|
44
45
|
useEffect(() => {
|
|
45
46
|
// get the initial values from the querystring
|
|
46
|
-
const formValues = getDefaultValuesFromQueryString({ buildUrl })
|
|
47
|
+
const formValues = getDefaultValuesFromQueryString({ buildUrl, chains })
|
|
47
48
|
const { toAddress, ...initialFormValues } = formValues
|
|
48
49
|
|
|
49
50
|
/**
|
|
@@ -62,6 +63,7 @@ export const URLSearchParamsBuilder = () => {
|
|
|
62
63
|
const bookmark = {
|
|
63
64
|
address: validationResult.address,
|
|
64
65
|
chainType: validationResult.chainType,
|
|
66
|
+
chainId: validationResult.chainId,
|
|
65
67
|
}
|
|
66
68
|
setUserAndDefaultValues({ toAddress })
|
|
67
69
|
setSelectedBookmark(bookmark)
|
|
@@ -1,14 +1,21 @@
|
|
|
1
|
+
import type { WidgetChains } from '../../types/widget.js'
|
|
2
|
+
import {
|
|
3
|
+
isDestinationOnlyChain,
|
|
4
|
+
isUnservedDestinationChain,
|
|
5
|
+
} from '../../utils/chainType.js'
|
|
1
6
|
import { formatInputAmount } from '../../utils/format.js'
|
|
2
7
|
import type { DefaultValues } from './types.js'
|
|
3
8
|
|
|
4
9
|
interface GetDefaultValuesFromQueryStringOptions {
|
|
5
10
|
includeToAddress?: boolean
|
|
6
11
|
buildUrl?: boolean
|
|
12
|
+
chains?: WidgetChains
|
|
7
13
|
}
|
|
8
14
|
|
|
9
15
|
export const getDefaultValuesFromQueryString = ({
|
|
10
16
|
buildUrl = false,
|
|
11
17
|
includeToAddress = true,
|
|
18
|
+
chains,
|
|
12
19
|
}: GetDefaultValuesFromQueryStringOptions): Partial<DefaultValues> => {
|
|
13
20
|
if (typeof window === 'undefined') {
|
|
14
21
|
return {}
|
|
@@ -22,6 +29,18 @@ export const getDefaultValuesFromQueryString = ({
|
|
|
22
29
|
new URLSearchParams(window.location.search)
|
|
23
30
|
)
|
|
24
31
|
|
|
32
|
+
// The loop below then drops their tokens too.
|
|
33
|
+
if (isDestinationOnlyChain(Number.parseInt(searchParams.fromChain, 10))) {
|
|
34
|
+
delete searchParams.fromChain
|
|
35
|
+
}
|
|
36
|
+
if (
|
|
37
|
+
isUnservedDestinationChain(
|
|
38
|
+
Number.parseInt(searchParams.toChain, 10),
|
|
39
|
+
chains
|
|
40
|
+
)
|
|
41
|
+
) {
|
|
42
|
+
delete searchParams.toChain
|
|
43
|
+
}
|
|
25
44
|
// Prevent using fromToken/toToken params if chain is not selected.
|
|
26
45
|
;(['from', 'to'] as const).forEach((key) => {
|
|
27
46
|
if (searchParams[`${key}Token`] && !searchParams[`${key}Chain`]) {
|
package/src/utils/chainType.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { ChainId, ChainType } from '@lifi/sdk'
|
|
1
|
+
import { ChainId, ChainType, type SDKProvider } from '@lifi/sdk'
|
|
2
|
+
import type { WidgetChains, WidgetConfig } from '../types/widget.js'
|
|
3
|
+
import { isItemAllowed } from './item.js'
|
|
2
4
|
|
|
3
5
|
export const defaultChainIdsByType: Record<ChainType, ChainId> = {
|
|
4
6
|
[ChainType.EVM]: ChainId.ETH,
|
|
@@ -8,3 +10,73 @@ export const defaultChainIdsByType: Record<ChainType, ChainId> = {
|
|
|
8
10
|
[ChainType.TVM]: ChainId.TRN,
|
|
9
11
|
[ChainType.STL]: ChainId.XLM,
|
|
10
12
|
}
|
|
13
|
+
|
|
14
|
+
// No wallet can sign on these chains yet, so a route may only deliver to them.
|
|
15
|
+
const destinationOnlyChainIds: ReadonlySet<number> = new Set<number>([
|
|
16
|
+
ChainId.ZEC,
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
export const isDestinationOnlyChain = (chainId?: number): boolean =>
|
|
20
|
+
chainId !== undefined && destinationOnlyChainIds.has(chainId)
|
|
21
|
+
|
|
22
|
+
export const withDestinationOnlyChains = (
|
|
23
|
+
chains: WidgetChains | undefined
|
|
24
|
+
): WidgetChains => {
|
|
25
|
+
const allow = chains?.from?.allow?.filter(
|
|
26
|
+
(chainId) => !isDestinationOnlyChain(chainId)
|
|
27
|
+
)
|
|
28
|
+
return {
|
|
29
|
+
...chains,
|
|
30
|
+
from: {
|
|
31
|
+
...chains?.from,
|
|
32
|
+
...(allow && { allow }),
|
|
33
|
+
deny: [...(chains?.from?.deny ?? []), ...destinationOnlyChainIds],
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// A destination-only chain takes a receiver only through a provider that lists it.
|
|
39
|
+
export const withServedDestinationChains = (
|
|
40
|
+
chains: WidgetChains | undefined,
|
|
41
|
+
providers: readonly Pick<SDKProvider, 'chainIds'>[]
|
|
42
|
+
): WidgetChains | undefined => {
|
|
43
|
+
const unserved = [...destinationOnlyChainIds].filter(
|
|
44
|
+
(chainId) =>
|
|
45
|
+
!providers.some((provider) =>
|
|
46
|
+
provider.chainIds?.some((id) => id === chainId)
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
if (!unserved.length) {
|
|
50
|
+
return chains
|
|
51
|
+
}
|
|
52
|
+
const allow = chains?.allow?.filter((chainId) => !unserved.includes(chainId))
|
|
53
|
+
return {
|
|
54
|
+
...chains,
|
|
55
|
+
...(allow && { allow }),
|
|
56
|
+
deny: [...(chains?.deny ?? []), ...unserved],
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The form keeps a preset chain that the chain list does not offer.
|
|
61
|
+
export const isUnservedDestinationChain = (
|
|
62
|
+
chainId: number | undefined,
|
|
63
|
+
chains: WidgetChains | undefined
|
|
64
|
+
): boolean => isDestinationOnlyChain(chainId) && !isItemAllowed(chainId, chains)
|
|
65
|
+
|
|
66
|
+
// Omitted, not undefined: an own undefined key resets the form field.
|
|
67
|
+
export const withoutUnservedToChain = <
|
|
68
|
+
T extends Pick<WidgetConfig, 'chains' | 'toChain' | 'toToken'>,
|
|
69
|
+
>(
|
|
70
|
+
config: T
|
|
71
|
+
): T => {
|
|
72
|
+
if (!isUnservedDestinationChain(config.toChain, config.chains)) {
|
|
73
|
+
return config
|
|
74
|
+
}
|
|
75
|
+
const { toChain: _toChain, toToken: _toToken, ...rest } = config
|
|
76
|
+
return rest as T
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const isDestinationAllowedAsSource = (
|
|
80
|
+
toChainId: number | undefined,
|
|
81
|
+
chains: WidgetChains | undefined
|
|
82
|
+
): boolean => !toChainId || isItemAllowed(toChainId, chains?.from)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ChainType } from '@lifi/sdk'
|
|
2
|
+
import type { ChainRef, IsAddressForChain } from '@lifi/widget-provider'
|
|
2
3
|
|
|
3
4
|
// Stellar routes always settle to the account that signs them.
|
|
4
5
|
export const isCustomReceiverUnsupported = (
|
|
@@ -29,3 +30,38 @@ export const isCustomReceiverBlocked = ({
|
|
|
29
30
|
}
|
|
30
31
|
return toAddress.toLowerCase() !== signerAddress?.toLowerCase()
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
// Without a receiver the API delivers to the signer when both chains share a
|
|
35
|
+
// chain type, and a Bitcoin signer cannot receive on ZEC.
|
|
36
|
+
export const isSignerInvalidReceiver = ({
|
|
37
|
+
signerAddress,
|
|
38
|
+
toAddress,
|
|
39
|
+
fromChain,
|
|
40
|
+
toChain,
|
|
41
|
+
isAddressForChain,
|
|
42
|
+
}: {
|
|
43
|
+
signerAddress: string | undefined
|
|
44
|
+
toAddress: string | undefined
|
|
45
|
+
fromChain: ChainRef | undefined
|
|
46
|
+
toChain: ChainRef | undefined
|
|
47
|
+
isAddressForChain: IsAddressForChain
|
|
48
|
+
}): boolean =>
|
|
49
|
+
Boolean(
|
|
50
|
+
signerAddress &&
|
|
51
|
+
!toAddress &&
|
|
52
|
+
fromChain &&
|
|
53
|
+
toChain &&
|
|
54
|
+
fromChain.chainType === toChain.chainType &&
|
|
55
|
+
!isAddressForChain(signerAddress, toChain)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
export const canQuoteWithToAddress = (
|
|
59
|
+
toAddress: string | undefined,
|
|
60
|
+
toChain: ChainRef | undefined,
|
|
61
|
+
isAddressForChain: IsAddressForChain
|
|
62
|
+
): boolean => {
|
|
63
|
+
if (!toAddress) {
|
|
64
|
+
return true
|
|
65
|
+
}
|
|
66
|
+
return toChain ? isAddressForChain(toAddress, toChain) : false
|
|
67
|
+
}
|
package/src/utils/token.ts
CHANGED
|
@@ -280,6 +280,54 @@ export const getTokenVerificationProvider = (
|
|
|
280
280
|
: undefined
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
+
export type TokenVerificationBadge =
|
|
284
|
+
| 'native'
|
|
285
|
+
| 'flagged'
|
|
286
|
+
| 'verified'
|
|
287
|
+
| 'unverifiedByProvider'
|
|
288
|
+
| 'unverified'
|
|
289
|
+
|
|
290
|
+
export const getTokenVerificationBadge = (
|
|
291
|
+
token: Pick<
|
|
292
|
+
TokenWithFlags,
|
|
293
|
+
'native' | 'listed' | 'verified' | 'verificationStatus'
|
|
294
|
+
>
|
|
295
|
+
): TokenVerificationBadge | undefined => {
|
|
296
|
+
// The provider calls the native-address convention a scam on some chains.
|
|
297
|
+
if (token.native) {
|
|
298
|
+
return 'native'
|
|
299
|
+
}
|
|
300
|
+
if (token.verificationStatus === 'flagged') {
|
|
301
|
+
return 'flagged'
|
|
302
|
+
}
|
|
303
|
+
if (token.verificationStatus === 'verified') {
|
|
304
|
+
return 'verified'
|
|
305
|
+
}
|
|
306
|
+
if (token.listed || token.verified) {
|
|
307
|
+
return undefined
|
|
308
|
+
}
|
|
309
|
+
return token.verificationStatus === 'unverified'
|
|
310
|
+
? 'unverifiedByProvider'
|
|
311
|
+
: 'unverified'
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export type TokenStatusTextKey =
|
|
315
|
+
| 'tokenMetric.statusVerified'
|
|
316
|
+
| 'tokenMetric.statusFlagged'
|
|
317
|
+
| 'tokenMetric.statusUnverified'
|
|
318
|
+
|
|
319
|
+
export const getTokenStatusTextKey = (
|
|
320
|
+
token: Pick<TokenWithFlags, 'native' | 'verificationStatus'>
|
|
321
|
+
): TokenStatusTextKey => {
|
|
322
|
+
if (token.verificationStatus === 'verified') {
|
|
323
|
+
return 'tokenMetric.statusVerified'
|
|
324
|
+
}
|
|
325
|
+
if (token.verificationStatus === 'flagged' && !token.native) {
|
|
326
|
+
return 'tokenMetric.statusFlagged'
|
|
327
|
+
}
|
|
328
|
+
return 'tokenMetric.statusUnverified'
|
|
329
|
+
}
|
|
330
|
+
|
|
283
331
|
/**
|
|
284
332
|
* Hyperliquid declares Arbitrum USDC as its native token, Lighter declares
|
|
285
333
|
* Ethereum USDC. Neither is a gas token you hold there. A chain-local
|