@lifi/widget 4.7.1 → 4.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/dist/esm/components/AmountInput/AmountInput.style.d.ts.map +1 -1
- package/dist/esm/components/AmountInputCard/AmountInputCardPair.js +5 -2
- package/dist/esm/components/AmountInputCard/AmountInputCardPair.js.map +1 -1
- package/dist/esm/components/Card/Card.d.ts.map +1 -1
- package/dist/esm/components/Card/CardIconButton.d.ts.map +1 -1
- package/dist/esm/components/ContractComponent/ItemPrice.d.ts.map +1 -1
- package/dist/esm/components/ListItem/ListItemButton.d.ts.map +1 -1
- package/dist/esm/components/ListItemButton.d.ts.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/Token/Token.d.ts.map +1 -1
- package/dist/esm/components/TokenList/types.d.ts.map +1 -1
- 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/useAvailableChains.d.ts.map +1 -1
- package/dist/esm/hooks/useAvailableChains.js +21 -38
- package/dist/esm/hooks/useAvailableChains.js.map +1 -1
- package/dist/esm/hooks/useFromTokenSufficiency.js +3 -3
- package/dist/esm/hooks/useFromTokenSufficiency.js.map +1 -1
- package/dist/esm/hooks/useGasRecommendation.js +3 -3
- package/dist/esm/hooks/useGasRecommendation.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/useGasSufficiency.js +3 -3
- package/dist/esm/hooks/useGasSufficiency.js.map +1 -1
- package/dist/esm/hooks/useIsBatchingSupported.js +2 -2
- package/dist/esm/hooks/useIsBatchingSupported.js.map +1 -1
- package/dist/esm/hooks/useRouteExecution.js +6 -6
- package/dist/esm/hooks/useRouteExecution.js.map +1 -1
- package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
- package/dist/esm/hooks/useRoutes.js +18 -13
- 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 +6 -5
- package/dist/esm/hooks/useTokenBalance.js.map +1 -1
- package/dist/esm/hooks/useTokenBalancesQueries.js +3 -3
- package/dist/esm/hooks/useTokenBalancesQueries.js.map +1 -1
- package/dist/esm/hooks/useTokenSearch.js +3 -3
- package/dist/esm/hooks/useTokenSearch.js.map +1 -1
- package/dist/esm/hooks/useTokens.js +15 -15
- package/dist/esm/hooks/useTokens.js.map +1 -1
- package/dist/esm/hooks/useTools.d.ts.map +1 -1
- package/dist/esm/hooks/useTools.js +26 -27
- package/dist/esm/hooks/useTools.js.map +1 -1
- package/dist/esm/hooks/useTransactionDetails.js +2 -2
- package/dist/esm/hooks/useTransactionDetails.js.map +1 -1
- package/dist/esm/hooks/useTransactionHistory.js +3 -3
- package/dist/esm/hooks/useTransactionHistory.js.map +1 -1
- package/dist/esm/i18n/bn.json +2 -1
- package/dist/esm/i18n/de.json +2 -1
- package/dist/esm/i18n/en.json +2 -1
- package/dist/esm/i18n/es.json +2 -1
- package/dist/esm/i18n/fr.json +2 -1
- package/dist/esm/i18n/hi.json +2 -1
- package/dist/esm/i18n/id.json +2 -1
- package/dist/esm/i18n/it.json +2 -1
- package/dist/esm/i18n/ja.json +2 -1
- package/dist/esm/i18n/ko.json +2 -1
- package/dist/esm/i18n/pl.json +2 -1
- package/dist/esm/i18n/pt.json +2 -1
- package/dist/esm/i18n/th.json +2 -1
- package/dist/esm/i18n/tr.json +2 -1
- package/dist/esm/i18n/uk.json +2 -1
- package/dist/esm/i18n/vi.json +2 -1
- package/dist/esm/i18n/zh.json +2 -1
- package/dist/esm/index.d.ts +5 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/pages/RoutesPage/RoutesPage.style.d.ts.map +1 -1
- 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/I18nProvider/types.d.ts.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 +15 -11
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
- package/dist/esm/providers/WidgetProvider/types.d.ts +2 -0
- package/dist/esm/providers/WidgetProvider/types.d.ts.map +1 -1
- package/dist/esm/queries/getChains.d.ts +38 -0
- package/dist/esm/queries/getChains.d.ts.map +1 -0
- package/dist/esm/queries/getChains.js +51 -0
- package/dist/esm/queries/getChains.js.map +1 -0
- package/dist/esm/queries/getTools.d.ts +26 -0
- package/dist/esm/queries/getTools.d.ts.map +1 -0
- package/dist/esm/queries/getTools.js +30 -0
- package/dist/esm/queries/getTools.js.map +1 -0
- package/dist/esm/queries/index.d.ts +4 -0
- package/dist/esm/queries/index.js +3 -0
- package/dist/esm/queries/types.d.ts +25 -0
- package/dist/esm/queries/types.d.ts.map +1 -0
- package/dist/esm/shared.d.ts +2 -1
- package/dist/esm/shared.js +2 -1
- package/dist/esm/stores/StoreProvider.d.ts.map +1 -1
- package/dist/esm/stores/StoreProvider.js +16 -17
- package/dist/esm/stores/StoreProvider.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/stores/header/useHeaderStore.d.ts +1 -1
- package/dist/esm/stores/header/useHeaderStore.d.ts.map +1 -1
- package/dist/esm/stores/header/useHeaderStore.js.map +1 -1
- package/dist/esm/themes/createTheme.d.ts.map +1 -1
- package/dist/esm/types/events.d.ts.map +1 -1
- package/dist/esm/types/token.d.ts.map +1 -1
- package/dist/esm/types/widget.d.ts +30 -0
- package/dist/esm/types/widget.d.ts.map +1 -1
- package/dist/esm/types/widget.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/queries.js.map +1 -1
- package/dist/esm/utils/scopeKeys.d.ts +6 -0
- package/dist/esm/utils/scopeKeys.d.ts.map +1 -0
- package/dist/esm/utils/scopeKeys.js +8 -0
- package/dist/esm/utils/scopeKeys.js.map +1 -0
- package/package.json +15 -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/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/useAvailableChains.test.tsx +170 -0
- package/src/hooks/useAvailableChains.ts +29 -47
- package/src/hooks/useFromTokenSufficiency.ts +2 -2
- package/src/hooks/useGasRecommendation.ts +2 -2
- package/src/hooks/useGasRefuel.ts +11 -1
- package/src/hooks/useGasSufficiency.ts +2 -2
- package/src/hooks/useIsBatchingSupported.ts +2 -2
- package/src/hooks/useRouteExecution.ts +5 -5
- package/src/hooks/useRoutes.ts +28 -23
- 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 +10 -5
- package/src/hooks/useTokenBalancesQueries.ts +3 -3
- package/src/hooks/useTokenSearch.ts +7 -3
- package/src/hooks/useTokens.ts +13 -14
- package/src/hooks/useTools.sharedEntry.test.tsx +155 -0
- package/src/hooks/useTools.ts +42 -37
- package/src/hooks/useTransactionDetails.ts +3 -3
- package/src/hooks/useTransactionHistory.ts +2 -2
- package/src/index.ts +2 -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.test.tsx +73 -0
- package/src/providers/WidgetProvider/WidgetProvider.tsx +28 -14
- package/src/providers/WidgetProvider/types.ts +2 -0
- package/src/queries/getChains.ts +93 -0
- package/src/queries/getTools.ts +62 -0
- package/src/queries/index.ts +30 -0
- package/src/queries/sharedCache.test.tsx +223 -0
- package/src/queries/types.ts +38 -0
- package/src/shared.ts +1 -0
- package/src/stores/StoreProvider.test.tsx +107 -0
- package/src/stores/StoreProvider.tsx +8 -6
- 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/stores/header/useHeaderStore.tsx +3 -4
- package/src/types/widget.ts +30 -0
- package/src/utils/chainType.ts +73 -1
- package/src/utils/customReceiver.ts +36 -0
- package/src/utils/queries.ts +1 -0
- package/src/utils/scopeKeys.ts +18 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
QueryFunction,
|
|
3
|
+
QueryKey,
|
|
4
|
+
UseQueryOptions,
|
|
5
|
+
} from '@tanstack/react-query'
|
|
6
|
+
|
|
7
|
+
export type ScopeKeyParameter = {
|
|
8
|
+
/**
|
|
9
|
+
* Scopes the entry inside a shared `QueryClient`, like wagmi's `scopeKey`.
|
|
10
|
+
* Callers that pass the same value share the cached data; a widget passes
|
|
11
|
+
* its `queryScopeKey`.
|
|
12
|
+
*/
|
|
13
|
+
scopeKey?: string | undefined
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Behaviour the caller owns — `staleTime`, `refetchInterval`, `select`,
|
|
18
|
+
* `enabled`, … It is spread into the options and never becomes part of the
|
|
19
|
+
* key, so callers with different behaviour still share one entry.
|
|
20
|
+
*/
|
|
21
|
+
export type QueryParameter<queryFnData, data, queryKey extends QueryKey> = {
|
|
22
|
+
query?:
|
|
23
|
+
| Omit<
|
|
24
|
+
UseQueryOptions<queryFnData, Error, data, queryKey>,
|
|
25
|
+
'queryKey' | 'queryFn'
|
|
26
|
+
>
|
|
27
|
+
| undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Options ready for `useQuery`, `fetchQuery` or `prefetchQuery`. */
|
|
31
|
+
export type FactoryQueryOptions<
|
|
32
|
+
queryFnData,
|
|
33
|
+
data,
|
|
34
|
+
queryKey extends QueryKey,
|
|
35
|
+
> = UseQueryOptions<queryFnData, Error, data, queryKey> & {
|
|
36
|
+
queryKey: queryKey
|
|
37
|
+
queryFn: QueryFunction<queryFnData, queryKey>
|
|
38
|
+
}
|
package/src/shared.ts
CHANGED
|
@@ -188,5 +188,6 @@ export {
|
|
|
188
188
|
export { getPriceImpact } from './utils/getPriceImpact.js'
|
|
189
189
|
export { fitInputText } from './utils/input.js'
|
|
190
190
|
export { navigationRoutes } from './utils/navigationRoutes.js'
|
|
191
|
+
export { resolveStorageScopeKey } from './utils/scopeKeys.js'
|
|
191
192
|
export { isHttpUrl, openInNewTab } from './utils/url.js'
|
|
192
193
|
export { shortenAddress } from './utils/wallet.js'
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/** @vitest-environment happy-dom */
|
|
2
|
+
|
|
3
|
+
import { act } from 'react'
|
|
4
|
+
import { createRoot, type Root } from 'react-dom/client'
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
6
|
+
import type { WidgetConfig } from '../types/widget.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The persisted stores are named from the resolved storage scope. An
|
|
10
|
+
* integration that only set the deprecated keyPrefix must keep the store
|
|
11
|
+
* names it always had, or its users lose their saved state.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { recorded, recorder } = vi.hoisted(() => {
|
|
15
|
+
const recorded = { namePrefix: {} as Record<string, string | undefined> }
|
|
16
|
+
const recorder = (name: string) => ({
|
|
17
|
+
[name]: ({
|
|
18
|
+
children,
|
|
19
|
+
namePrefix,
|
|
20
|
+
}: {
|
|
21
|
+
children?: unknown
|
|
22
|
+
namePrefix?: string
|
|
23
|
+
}) => {
|
|
24
|
+
recorded.namePrefix[name] = namePrefix
|
|
25
|
+
return children
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
return { recorded, recorder }
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
vi.mock('./bookmarks/BookmarkStore.js', () => recorder('BookmarkStoreProvider'))
|
|
32
|
+
vi.mock('./chains/ChainOrderStore.js', () =>
|
|
33
|
+
recorder('ChainOrderStoreProvider')
|
|
34
|
+
)
|
|
35
|
+
vi.mock('./form/FormStore.js', () => recorder('FormStoreProvider'))
|
|
36
|
+
vi.mock('./header/useHeaderStore.js', () => recorder('HeaderStoreProvider'))
|
|
37
|
+
vi.mock('./navigationTabs/useNavigationTabsStore.js', () =>
|
|
38
|
+
recorder('NavigationTabsStoreProvider')
|
|
39
|
+
)
|
|
40
|
+
vi.mock('./pinnedTokens/PinnedTokensStore.js', () =>
|
|
41
|
+
recorder('PinnedTokensStoreProvider')
|
|
42
|
+
)
|
|
43
|
+
vi.mock('./recentTokens/RecentTokensStore.js', () =>
|
|
44
|
+
recorder('RecentTokensStoreProvider')
|
|
45
|
+
)
|
|
46
|
+
vi.mock('./routes/RouteExecutionStore.js', () =>
|
|
47
|
+
recorder('RouteExecutionStoreProvider')
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
import { StoreProvider } from './StoreProvider.js'
|
|
51
|
+
|
|
52
|
+
const PERSISTED = [
|
|
53
|
+
'BookmarkStoreProvider',
|
|
54
|
+
'PinnedTokensStoreProvider',
|
|
55
|
+
'RecentTokensStoreProvider',
|
|
56
|
+
'ChainOrderStoreProvider',
|
|
57
|
+
'RouteExecutionStoreProvider',
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
let root: Root
|
|
61
|
+
|
|
62
|
+
const render = async (config: WidgetConfig) => {
|
|
63
|
+
await act(async () => {
|
|
64
|
+
root.render(<StoreProvider config={config}>{null}</StoreProvider>)
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe('StoreProvider storage scope', () => {
|
|
69
|
+
beforeEach(() => {
|
|
70
|
+
recorded.namePrefix = {}
|
|
71
|
+
const container = document.createElement('div')
|
|
72
|
+
document.body.appendChild(container)
|
|
73
|
+
root = createRoot(container)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
afterEach(async () => {
|
|
77
|
+
await act(async () => root.unmount())
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('names the persisted stores from keyPrefix when only that is set', async () => {
|
|
81
|
+
await render({ integrator: 'test', keyPrefix: 'legacy' })
|
|
82
|
+
|
|
83
|
+
for (const store of PERSISTED) {
|
|
84
|
+
expect(recorded.namePrefix[store]).toBe('legacy')
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('names the persisted stores from storageScopeKey over keyPrefix', async () => {
|
|
89
|
+
await render({
|
|
90
|
+
integrator: 'test',
|
|
91
|
+
keyPrefix: 'legacy',
|
|
92
|
+
storageScopeKey: 'store',
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
for (const store of PERSISTED) {
|
|
96
|
+
expect(recorded.namePrefix[store]).toBe('store')
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('keeps the historical names when nothing is set', async () => {
|
|
101
|
+
await render({ integrator: 'test' })
|
|
102
|
+
|
|
103
|
+
for (const store of PERSISTED) {
|
|
104
|
+
expect(recorded.namePrefix[store]).toBeUndefined()
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PropsWithChildren } from 'react'
|
|
2
2
|
import type { WidgetConfigProps } from '../types/widget.js'
|
|
3
|
+
import { resolveStorageScopeKey } from '../utils/scopeKeys.js'
|
|
3
4
|
import { BookmarkStoreProvider } from './bookmarks/BookmarkStore.js'
|
|
4
5
|
import { ChainOrderStoreProvider } from './chains/ChainOrderStore.js'
|
|
5
6
|
import { FormStoreProvider } from './form/FormStore.js'
|
|
@@ -14,15 +15,16 @@ export const StoreProvider: React.FC<PropsWithChildren<WidgetConfigProps>> = ({
|
|
|
14
15
|
config,
|
|
15
16
|
formRef,
|
|
16
17
|
}) => {
|
|
18
|
+
const storageScopeKey = resolveStorageScopeKey(config)
|
|
17
19
|
return (
|
|
18
20
|
<NavigationTabsStoreProvider config={config}>
|
|
19
|
-
<HeaderStoreProvider
|
|
20
|
-
<BookmarkStoreProvider namePrefix={
|
|
21
|
-
<PinnedTokensStoreProvider namePrefix={
|
|
22
|
-
<RecentTokensStoreProvider namePrefix={
|
|
21
|
+
<HeaderStoreProvider>
|
|
22
|
+
<BookmarkStoreProvider namePrefix={storageScopeKey}>
|
|
23
|
+
<PinnedTokensStoreProvider namePrefix={storageScopeKey}>
|
|
24
|
+
<RecentTokensStoreProvider namePrefix={storageScopeKey}>
|
|
23
25
|
<FormStoreProvider formRef={formRef}>
|
|
24
|
-
<ChainOrderStoreProvider namePrefix={
|
|
25
|
-
<RouteExecutionStoreProvider namePrefix={
|
|
26
|
+
<ChainOrderStoreProvider namePrefix={storageScopeKey}>
|
|
27
|
+
<RouteExecutionStoreProvider namePrefix={storageScopeKey}>
|
|
26
28
|
{children}
|
|
27
29
|
</RouteExecutionStoreProvider>
|
|
28
30
|
</ChainOrderStoreProvider>
|
|
@@ -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`]) {
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
+
import type { PropsWithChildren } from 'react'
|
|
1
2
|
import { createContext, use, useRef } from 'react'
|
|
2
3
|
import { create } from 'zustand'
|
|
3
4
|
import { useShallow } from 'zustand/shallow'
|
|
4
|
-
import type { PersistStoreProviderProps } from '../types.js'
|
|
5
5
|
import type { HeaderState, HeaderStore } from './types.js'
|
|
6
6
|
|
|
7
7
|
const HeaderStoreContext = createContext<HeaderStore | null>(null)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}: PersistStoreProviderProps): any {
|
|
9
|
+
// Not persisted, so it takes no storage scope.
|
|
10
|
+
export function HeaderStoreProvider({ children }: PropsWithChildren): any {
|
|
12
11
|
const storeRef = useRef<HeaderStore>(null)
|
|
13
12
|
if (!storeRef.current) {
|
|
14
13
|
storeRef.current = createHeaderStore()
|
package/src/types/widget.ts
CHANGED
|
@@ -410,7 +410,37 @@ export interface WidgetConfig {
|
|
|
410
410
|
sdkConfig?: WidgetSDKConfig
|
|
411
411
|
|
|
412
412
|
buildUrl?: boolean
|
|
413
|
+
/**
|
|
414
|
+
* @deprecated Use `storageScopeKey` for persisted state and `queryScopeKey`
|
|
415
|
+
* for the query cache. Still honoured as the fallback for both.
|
|
416
|
+
*/
|
|
413
417
|
keyPrefix?: string
|
|
418
|
+
/**
|
|
419
|
+
* Namespaces the widget's persisted state in `localStorage`: bookmarks,
|
|
420
|
+
* pinned tokens, recent tokens, chain order and route history, as
|
|
421
|
+
* `keyPrefix` does. Use a different value per widget instance that should
|
|
422
|
+
* keep its own state. Settings keep one shared name.
|
|
423
|
+
*
|
|
424
|
+
* It also scopes the widget's other queries (routes, tokens, balances, …),
|
|
425
|
+
* so instances with different values keep their data apart.
|
|
426
|
+
*
|
|
427
|
+
* Falls back to `keyPrefix`, then to the historical `li.fi-*` names.
|
|
428
|
+
*/
|
|
429
|
+
storageScopeKey?: string
|
|
430
|
+
/**
|
|
431
|
+
* Scopes the widget's chains and tools queries inside the host's react-query
|
|
432
|
+
* `QueryClient`. Widget instances - and a host app using the
|
|
433
|
+
* `@lifi/widget/queries` factories - that pass the same value
|
|
434
|
+
* share those entries. The other widget queries are scoped by
|
|
435
|
+
* `storageScopeKey`.
|
|
436
|
+
*
|
|
437
|
+
* The keys start with a plain `'chains'` / `'tools'`, as in wagmi, so a host
|
|
438
|
+
* that matches on those roots (for example `setQueriesData({ queryKey:
|
|
439
|
+
* ['chains'] })`) also reaches the widget's entries.
|
|
440
|
+
*
|
|
441
|
+
* Falls back to `keyPrefix`, then to `'li.fi'`.
|
|
442
|
+
*/
|
|
443
|
+
queryScopeKey?: string
|
|
414
444
|
|
|
415
445
|
bridges?: AllowDeny<string>
|
|
416
446
|
exchanges?: AllowDeny<string>
|
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/queries.ts
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { WidgetConfig } from '../types/widget.js'
|
|
2
|
+
|
|
3
|
+
// Non-empty, so an unconfigured widget never shares a host's unscoped entries.
|
|
4
|
+
export const defaultQueryScopeKey = 'li.fi'
|
|
5
|
+
|
|
6
|
+
type ScopeKeyConfig = Pick<
|
|
7
|
+
WidgetConfig,
|
|
8
|
+
'storageScopeKey' | 'queryScopeKey' | 'keyPrefix'
|
|
9
|
+
>
|
|
10
|
+
|
|
11
|
+
// `undefined` keeps the historical `li.fi-*` store names.
|
|
12
|
+
export const resolveStorageScopeKey = (
|
|
13
|
+
config: Partial<ScopeKeyConfig> | undefined
|
|
14
|
+
): string | undefined => config?.storageScopeKey ?? config?.keyPrefix
|
|
15
|
+
|
|
16
|
+
export const resolveQueryScopeKey = (
|
|
17
|
+
config: Partial<ScopeKeyConfig> | undefined
|
|
18
|
+
): string => config?.queryScopeKey ?? config?.keyPrefix ?? defaultQueryScopeKey
|