@lifi/widget 3.8.2 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/esm/components/Header/WalletHeader.js +9 -3
- package/dist/esm/components/Header/WalletHeader.js.map +1 -1
- package/dist/esm/components/ReverseTokensButton/ReverseTokensButton.js +16 -1
- package/dist/esm/components/ReverseTokensButton/ReverseTokensButton.js.map +1 -1
- package/dist/esm/components/TokenList/TokenList.js +6 -4
- package/dist/esm/components/TokenList/TokenList.js.map +1 -1
- package/dist/esm/components/TokenList/TokenListItem.js +1 -1
- package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
- package/dist/esm/components/TokenList/types.d.ts +2 -2
- package/dist/esm/components/TokenList/useTokenSelect.d.ts +4 -0
- package/dist/esm/components/TokenList/useTokenSelect.js +19 -10
- package/dist/esm/components/TokenList/useTokenSelect.js.map +1 -1
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/hooks/useAvailableChains.d.ts +2 -1
- package/dist/esm/hooks/useAvailableChains.js.map +1 -1
- package/dist/esm/hooks/useChain.d.ts +1 -0
- package/dist/esm/hooks/useChain.js +1 -0
- package/dist/esm/hooks/useChain.js.map +1 -1
- package/dist/esm/hooks/useChains.d.ts +1 -1
- package/dist/esm/hooks/useGasSufficiency.d.ts +1 -1
- package/dist/esm/hooks/useGasSufficiency.js +2 -12
- package/dist/esm/hooks/useGasSufficiency.js.map +1 -1
- package/dist/esm/hooks/useIsContractAddress.d.ts +2 -0
- package/dist/esm/hooks/useIsContractAddress.js +16 -0
- package/dist/esm/hooks/useIsContractAddress.js.map +1 -0
- package/dist/esm/hooks/useToAddressAutoPopulate.d.ts +12 -0
- package/dist/esm/hooks/useToAddressAutoPopulate.js +68 -0
- package/dist/esm/hooks/useToAddressAutoPopulate.js.map +1 -0
- package/dist/esm/hooks/useToAddressRequirements.d.ts +1 -1
- package/dist/esm/hooks/useToAddressRequirements.js +9 -1
- package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
- package/dist/esm/hooks/useToAddressReset.js +15 -8
- package/dist/esm/hooks/useToAddressReset.js.map +1 -1
- package/dist/esm/pages/SendToWallet/BookmarksPage.js +1 -0
- package/dist/esm/pages/SendToWallet/BookmarksPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/ConfirmAddressSheet.js +1 -0
- package/dist/esm/pages/SendToWallet/ConfirmAddressSheet.js.map +1 -1
- package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js +1 -0
- package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js +1 -0
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js +4 -1
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js.map +1 -1
- package/dist/esm/stores/form/createFormStore.js +1 -0
- package/dist/esm/stores/form/createFormStore.js.map +1 -1
- package/dist/esm/stores/form/types.d.ts +1 -0
- package/dist/esm/stores/form/types.js.map +1 -1
- package/dist/esm/stores/form/useFieldActions.d.ts +1 -0
- package/dist/esm/stores/form/useFieldActions.js +1 -0
- package/dist/esm/stores/form/useFieldActions.js.map +1 -1
- package/package.json +8 -8
- package/src/components/Header/WalletHeader.tsx +14 -5
- package/src/components/ReverseTokensButton/ReverseTokensButton.tsx +25 -6
- package/src/components/TokenList/TokenList.tsx +7 -4
- package/src/components/TokenList/TokenListItem.tsx +1 -1
- package/src/components/TokenList/types.ts +2 -2
- package/src/components/TokenList/useTokenSelect.ts +39 -16
- package/src/config/version.ts +1 -1
- package/src/hooks/useAvailableChains.ts +6 -1
- package/src/hooks/useChain.ts +1 -0
- package/src/hooks/useGasSufficiency.ts +7 -19
- package/src/hooks/useIsContractAddress.ts +21 -0
- package/src/hooks/useToAddressAutoPopulate.ts +95 -0
- package/src/hooks/useToAddressRequirements.ts +13 -2
- package/src/hooks/useToAddressReset.ts +15 -8
- package/src/pages/SendToWallet/BookmarksPage.tsx +1 -0
- package/src/pages/SendToWallet/ConfirmAddressSheet.tsx +1 -0
- package/src/pages/SendToWallet/ConnectedWalletsPage.tsx +1 -0
- package/src/pages/SendToWallet/RecentWalletsPage.tsx +1 -0
- package/src/pages/SendToWallet/SendToConfiguredWalletPage.tsx +4 -1
- package/src/stores/form/createFormStore.ts +2 -0
- package/src/stores/form/types.ts +1 -0
- package/src/stores/form/useFieldActions.ts +1 -0
- package/src/stores/header/useHeaderStore.tsx +1 -1
|
@@ -5,6 +5,11 @@ import { useCallback } from 'react'
|
|
|
5
5
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
6
6
|
import { isItemAllowed } from '../utils/item.js'
|
|
7
7
|
|
|
8
|
+
export type GetChainById = (
|
|
9
|
+
chainId?: number,
|
|
10
|
+
chains?: ExtendedChain[]
|
|
11
|
+
) => ExtendedChain | undefined
|
|
12
|
+
|
|
8
13
|
const supportedChainTypes = [ChainType.EVM, ChainType.SVM, ChainType.UTXO]
|
|
9
14
|
|
|
10
15
|
export const useAvailableChains = (chainTypes?: ChainType[]) => {
|
|
@@ -35,7 +40,7 @@ export const useAvailableChains = (chainTypes?: ChainType[]) => {
|
|
|
35
40
|
staleTime: 300_000,
|
|
36
41
|
})
|
|
37
42
|
|
|
38
|
-
const getChainById = useCallback(
|
|
43
|
+
const getChainById: GetChainById = useCallback(
|
|
39
44
|
(chainId?: number, chains: ExtendedChain[] | undefined = data) => {
|
|
40
45
|
if (!chainId) {
|
|
41
46
|
return
|
package/src/hooks/useChain.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ChainType,
|
|
3
|
-
type EVMChain,
|
|
4
|
-
type RouteExtended,
|
|
5
|
-
type Token,
|
|
6
|
-
} from '@lifi/sdk'
|
|
1
|
+
import type { EVMChain, RouteExtended, Token } from '@lifi/sdk'
|
|
7
2
|
import { useAccount } from '@lifi/wallet-management'
|
|
8
3
|
import { useQuery } from '@tanstack/react-query'
|
|
9
|
-
import type { Address } from 'viem'
|
|
10
|
-
import { type Connector, useBytecode } from 'wagmi'
|
|
11
4
|
import { useAvailableChains } from './useAvailableChains.js'
|
|
5
|
+
import { useIsContractAddress } from './useIsContractAddress.js'
|
|
12
6
|
import { getTokenBalancesWithRetry } from './useTokenBalance.js'
|
|
13
7
|
|
|
14
8
|
export interface GasSufficiency {
|
|
@@ -27,18 +21,12 @@ export const useGasSufficiency = (route?: RouteExtended) => {
|
|
|
27
21
|
const { account } = useAccount({
|
|
28
22
|
chainType: getChainById(route?.fromChainId)?.chainType,
|
|
29
23
|
})
|
|
30
|
-
const { data: contractCode } = useBytecode({
|
|
31
|
-
address:
|
|
32
|
-
account.chainType === ChainType.EVM
|
|
33
|
-
? (account.address as Address)
|
|
34
|
-
: undefined,
|
|
35
|
-
query: {
|
|
36
|
-
refetchInterval: 300_000,
|
|
37
|
-
staleTime: 300_000,
|
|
38
|
-
},
|
|
39
|
-
})
|
|
40
24
|
|
|
41
|
-
const isContractAddress =
|
|
25
|
+
const isContractAddress = useIsContractAddress(
|
|
26
|
+
account.address,
|
|
27
|
+
route?.fromChainId,
|
|
28
|
+
account.chainType
|
|
29
|
+
)
|
|
42
30
|
|
|
43
31
|
const { data: insufficientGas, isLoading } = useQuery({
|
|
44
32
|
queryKey: ['gas-sufficiency-check', account.address, route?.id],
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ChainType } from '@lifi/sdk'
|
|
2
|
+
import type { Address } from 'viem'
|
|
3
|
+
import { useBytecode } from 'wagmi'
|
|
4
|
+
|
|
5
|
+
export const useIsContractAddress = (
|
|
6
|
+
address?: string,
|
|
7
|
+
chainId?: number,
|
|
8
|
+
chainType?: ChainType
|
|
9
|
+
) => {
|
|
10
|
+
const { data: contractCode } = useBytecode({
|
|
11
|
+
address: address as Address,
|
|
12
|
+
chainId: chainId,
|
|
13
|
+
query: {
|
|
14
|
+
refetchInterval: 300_000,
|
|
15
|
+
staleTime: 300_000,
|
|
16
|
+
enabled: Boolean(chainType === ChainType.EVM && chainId),
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
const isContractAddress = !!contractCode
|
|
20
|
+
return isContractAddress
|
|
21
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { useAccount } from '@lifi/wallet-management'
|
|
2
|
+
import { useCallback } from 'react'
|
|
3
|
+
import { useBookmarkActions } from '../stores/bookmarks/useBookmarkActions.js'
|
|
4
|
+
import type { FormType } from '../stores/form/types.js'
|
|
5
|
+
import { useFieldActions } from '../stores/form/useFieldActions.js'
|
|
6
|
+
import { useSendToWalletActions } from '../stores/settings/useSendToWalletStore.js'
|
|
7
|
+
import { getChainTypeFromAddress } from '../utils/chainType.js'
|
|
8
|
+
import { useAvailableChains } from './useAvailableChains.js'
|
|
9
|
+
|
|
10
|
+
export type UpdateToAddressArgs = {
|
|
11
|
+
formType: FormType
|
|
12
|
+
selectedToAddress?: string
|
|
13
|
+
selectedChainId?: number
|
|
14
|
+
selectedOppositeTokenAddress?: string
|
|
15
|
+
selectedOppositeChainId?: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Automatically populates toAddress field if bridging across ecosystems and compatible wallet is connected
|
|
20
|
+
*/
|
|
21
|
+
export const useToAddressAutoPopulate = () => {
|
|
22
|
+
const { setFieldValue } = useFieldActions()
|
|
23
|
+
const { setSendToWallet } = useSendToWalletActions()
|
|
24
|
+
const { setSelectedBookmark } = useBookmarkActions()
|
|
25
|
+
const { getChainById } = useAvailableChains()
|
|
26
|
+
const { accounts } = useAccount()
|
|
27
|
+
|
|
28
|
+
return useCallback(
|
|
29
|
+
({
|
|
30
|
+
formType,
|
|
31
|
+
selectedToAddress,
|
|
32
|
+
selectedChainId,
|
|
33
|
+
selectedOppositeTokenAddress,
|
|
34
|
+
selectedOppositeChainId,
|
|
35
|
+
}: UpdateToAddressArgs) => {
|
|
36
|
+
if (
|
|
37
|
+
!selectedOppositeTokenAddress ||
|
|
38
|
+
!selectedOppositeChainId ||
|
|
39
|
+
!selectedChainId ||
|
|
40
|
+
!accounts?.length
|
|
41
|
+
) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
const selectedChain = getChainById?.(selectedChainId)
|
|
45
|
+
const selectedOppositeChain = getChainById?.(selectedOppositeChainId)
|
|
46
|
+
// Proceed if both chains are defined and of different ecosystem types (indicating cross-ecosystem bridging)
|
|
47
|
+
if (
|
|
48
|
+
!selectedChain ||
|
|
49
|
+
!selectedOppositeChain ||
|
|
50
|
+
selectedChain.chainType === selectedOppositeChain.chainType
|
|
51
|
+
) {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
// Identify the destination chain type based on the bridge direction ('from' or 'to')
|
|
55
|
+
const destinationChainType =
|
|
56
|
+
formType === 'from'
|
|
57
|
+
? selectedOppositeChain.chainType
|
|
58
|
+
: selectedChain.chainType
|
|
59
|
+
// If toAddress is already selected, verify that it matches the destination chain type
|
|
60
|
+
if (selectedToAddress) {
|
|
61
|
+
const selectedToAddressChainType =
|
|
62
|
+
getChainTypeFromAddress(selectedToAddress)
|
|
63
|
+
if (destinationChainType === selectedToAddressChainType) {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Find connected account compatible with the destination chain type
|
|
68
|
+
const destinationAccount = accounts?.find(
|
|
69
|
+
(account) => account.chainType === destinationChainType
|
|
70
|
+
)
|
|
71
|
+
// If a compatible destination account is found, set toAddress as if selecting it from the "Send to Wallet" connected wallets page
|
|
72
|
+
if (destinationAccount?.address) {
|
|
73
|
+
setFieldValue('toAddress', destinationAccount.address, {
|
|
74
|
+
isDirty: false,
|
|
75
|
+
isTouched: true,
|
|
76
|
+
})
|
|
77
|
+
setSelectedBookmark({
|
|
78
|
+
name: destinationAccount.connector?.name,
|
|
79
|
+
address: destinationAccount.address,
|
|
80
|
+
chainType: destinationAccount.chainType,
|
|
81
|
+
isConnectedAccount: true,
|
|
82
|
+
})
|
|
83
|
+
setSendToWallet(true)
|
|
84
|
+
return destinationAccount.address
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
[
|
|
88
|
+
accounts,
|
|
89
|
+
getChainById,
|
|
90
|
+
setFieldValue,
|
|
91
|
+
setSelectedBookmark,
|
|
92
|
+
setSendToWallet,
|
|
93
|
+
]
|
|
94
|
+
)
|
|
95
|
+
}
|
|
@@ -1,20 +1,31 @@
|
|
|
1
|
+
import { useAccount } from '@lifi/wallet-management'
|
|
1
2
|
import { useChain } from '../hooks/useChain.js'
|
|
2
3
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
3
4
|
import { useFieldValues } from '../stores/form/useFieldValues.js'
|
|
4
5
|
import { RequiredUI } from '../types/widget.js'
|
|
6
|
+
import { useIsContractAddress } from './useIsContractAddress.js'
|
|
5
7
|
|
|
6
8
|
export const useToAddressRequirements = () => {
|
|
7
9
|
const { requiredUI } = useWidgetConfig()
|
|
8
10
|
const [fromChainId, toChainId] = useFieldValues('fromChain', 'toChain')
|
|
9
|
-
|
|
10
11
|
const { chain: fromChain } = useChain(fromChainId)
|
|
11
12
|
const { chain: toChain } = useChain(toChainId)
|
|
13
|
+
const { account } = useAccount({
|
|
14
|
+
chainType: fromChain?.chainType,
|
|
15
|
+
})
|
|
16
|
+
const isFromContractAddress = useIsContractAddress(
|
|
17
|
+
account.address,
|
|
18
|
+
fromChainId,
|
|
19
|
+
account.chainType
|
|
20
|
+
)
|
|
12
21
|
|
|
13
22
|
const isDifferentChainType =
|
|
14
23
|
fromChain && toChain && fromChain.chainType !== toChain.chainType
|
|
15
24
|
|
|
16
25
|
const requiredToAddress =
|
|
17
|
-
requiredUI?.includes(RequiredUI.ToAddress) ||
|
|
26
|
+
requiredUI?.includes(RequiredUI.ToAddress) ||
|
|
27
|
+
isDifferentChainType ||
|
|
28
|
+
isFromContractAddress
|
|
18
29
|
|
|
19
30
|
return {
|
|
20
31
|
requiredToAddress,
|
|
@@ -3,13 +3,15 @@ import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
|
3
3
|
import { useBookmarkActions } from '../stores/bookmarks/useBookmarkActions.js'
|
|
4
4
|
import { useBookmarks } from '../stores/bookmarks/useBookmarks.js'
|
|
5
5
|
import { useFieldActions } from '../stores/form/useFieldActions.js'
|
|
6
|
+
import { useSendToWalletActions } from '../stores/settings/useSendToWalletStore.js'
|
|
6
7
|
import { RequiredUI } from '../types/widget.js'
|
|
7
8
|
|
|
8
9
|
export const useToAddressReset = () => {
|
|
9
10
|
const { requiredUI } = useWidgetConfig()
|
|
10
|
-
const { setFieldValue } = useFieldActions()
|
|
11
|
+
const { setFieldValue, isDirty } = useFieldActions()
|
|
11
12
|
const { selectedBookmark } = useBookmarks()
|
|
12
13
|
const { setSelectedBookmark } = useBookmarkActions()
|
|
14
|
+
const { setSendToWallet } = useSendToWalletActions()
|
|
13
15
|
|
|
14
16
|
const tryResetToAddress = (toChain: ExtendedChain) => {
|
|
15
17
|
const requiredToAddress = requiredUI?.includes(RequiredUI.ToAddress)
|
|
@@ -20,15 +22,20 @@ export const useToAddressReset = () => {
|
|
|
20
22
|
const shouldResetToAddress =
|
|
21
23
|
!requiredToAddress && !bookmarkSatisfiesToChainType
|
|
22
24
|
|
|
23
|
-
// toAddress field is required
|
|
24
|
-
//
|
|
25
|
-
// We
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
// but it keeps toAddress value set for the previous chain pair.
|
|
25
|
+
// The toAddress field is required and always visible when bridging between
|
|
26
|
+
// different ecosystems (fromChain and toChain have different chain types).
|
|
27
|
+
// We reset toAddress on each chain change if it's no longer required, ensuring that
|
|
28
|
+
// switching chain types doesn't leave a previously set toAddress value when
|
|
29
|
+
// the "Send to Wallet" field is hidden.
|
|
29
30
|
if (shouldResetToAddress) {
|
|
30
|
-
setFieldValue('toAddress', '')
|
|
31
|
+
setFieldValue('toAddress', '', { isTouched: true })
|
|
31
32
|
setSelectedBookmark()
|
|
33
|
+
// If toAddress was auto-filled (e.g., when making cross-ecosystem bridging and compatible destination wallet was connected)
|
|
34
|
+
// and not manually edited by the user, we need to hide "Send to Wallet".
|
|
35
|
+
const isToAddressDirty = isDirty('toAddress')
|
|
36
|
+
if (!isToAddressDirty) {
|
|
37
|
+
setSendToWallet(false)
|
|
38
|
+
}
|
|
32
39
|
}
|
|
33
40
|
}
|
|
34
41
|
|
|
@@ -43,7 +43,10 @@ export const SendToConfiguredWalletPage = () => {
|
|
|
43
43
|
|
|
44
44
|
const handleCuratedSelected = (toAddress: ToAddress) => {
|
|
45
45
|
setSelectedBookmark(toAddress)
|
|
46
|
-
setFieldValue('toAddress', toAddress.address, {
|
|
46
|
+
setFieldValue('toAddress', toAddress.address, {
|
|
47
|
+
isTouched: true,
|
|
48
|
+
isDirty: true,
|
|
49
|
+
})
|
|
47
50
|
navigateBack()
|
|
48
51
|
}
|
|
49
52
|
|
|
@@ -102,6 +102,8 @@ export const createFormStore = (defaultValues?: DefaultValues) =>
|
|
|
102
102
|
},
|
|
103
103
|
isTouched: (fieldName: FormFieldNames) =>
|
|
104
104
|
!!get().userValues[fieldName]?.isTouched,
|
|
105
|
+
isDirty: (fieldName: FormFieldNames) =>
|
|
106
|
+
!!get().userValues[fieldName]?.isDirty,
|
|
105
107
|
setAsTouched: (fieldName: FormFieldNames) => {
|
|
106
108
|
const userValues = {
|
|
107
109
|
...get().userValues,
|
package/src/stores/form/types.ts
CHANGED
|
@@ -70,6 +70,7 @@ export interface FormActions {
|
|
|
70
70
|
setDefaultValues: (formValues: DefaultValues) => void
|
|
71
71
|
setUserAndDefaultValues: (formValues: Partial<DefaultValues>) => void
|
|
72
72
|
isTouched: (fieldName: FormFieldNames) => boolean
|
|
73
|
+
isDirty: (fieldName: FormFieldNames) => boolean
|
|
73
74
|
setAsTouched: (fieldName: FormFieldNames) => void
|
|
74
75
|
resetField: (fieldName: FormFieldNames, resetOptions?: ResetOptions) => void
|
|
75
76
|
setFieldValue: (
|
|
@@ -18,6 +18,7 @@ export const useFieldActions = () => {
|
|
|
18
18
|
(store) => ({
|
|
19
19
|
getFieldValues: store.getFieldValues,
|
|
20
20
|
isTouched: store.isTouched,
|
|
21
|
+
isDirty: store.isDirty,
|
|
21
22
|
resetField: store.resetField,
|
|
22
23
|
setAsTouched: store.setAsTouched,
|
|
23
24
|
setDefaultValues: store.setDefaultValues,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createContext, useContext, useRef } from 'react'
|
|
2
2
|
import { shallow } from 'zustand/shallow'
|
|
3
3
|
import { createWithEqualityFn } from 'zustand/traditional'
|
|
4
|
-
import type {
|
|
4
|
+
import type { PersistStoreProviderProps } from '../types.js'
|
|
5
5
|
import type { HeaderState, HeaderStore } from './types.js'
|
|
6
6
|
|
|
7
7
|
export const HeaderStoreContext = createContext<HeaderStore | null>(null)
|