@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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChainOrderStore.js","names":[],"sources":["../../../../src/stores/chains/ChainOrderStore.tsx"],"sourcesContent":["import { createContext, type JSX, use, useEffect, useRef } from 'react'\nimport type { StoreApi, UseBoundStore } from 'zustand'\nimport { useShallow } from 'zustand/shallow'\nimport { useChains } from '../../hooks/useChains.js'\nimport { useSwapOnly } from '../../hooks/useSwapOnly.js'\nimport { useExternalWalletProvider } from '../../providers/WalletProvider/useExternalWalletProvider.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { getConfigItemSets, isItemAllowedForSets } from '../../utils/item.js'\nimport { getDefaultValuesFromQueryString } from '../form/getDefaultValuesFromQueryString.js'\nimport type { FormType } from '../form/types.js'\nimport { useFieldActions } from '../form/useFieldActions.js'\nimport type { PersistStoreProviderProps } from '../types.js'\nimport { createChainOrderStore } from './createChainOrderStore.js'\nimport type { ChainOrderState } from './types.js'\n\ntype ChainOrderStore = UseBoundStore<StoreApi<ChainOrderState>>\n\nconst ChainOrderStoreContext = createContext<ChainOrderStore | null>(null)\n\nexport function ChainOrderStoreProvider({\n children,\n ...props\n}: PersistStoreProviderProps): JSX.Element {\n const {\n chains: chainsConfig,\n hiddenUI,\n fromChain: fromChainConfig,\n toChain: toChainConfig,\n buildUrl,\n } = useWidgetConfig()\n const storeRef = useRef<ChainOrderStore>(null)\n const { chains } = useChains()\n const { setFieldValue, getFieldValues, isTouched } = useFieldActions()\n const swapOnly = useSwapOnly()\n const { variant } = useWidgetConfig()\n const { externalChainTypes, useExternalWalletProvidersOnly } =\n useExternalWalletProvider()\n\n if (!storeRef.current) {\n storeRef.current = createChainOrderStore({\n ...props,\n })\n }\n\n useEffect(() => {\n if (chains) {\n ;(['from', 'to'] as FormType[]).forEach((key) => {\n const configChainIds = chainsConfig?.[key]\n const isFromKey = key === 'from'\n\n // Convert configChainIds to Sets for O(1) lookup\n const configChainIdsSet = getConfigItemSets(\n configChainIds,\n (chainIds) => new Set(chainIds)\n )\n const filteredChains = chains.filter((chain) => {\n const passesChainsConfigFilter = configChainIdsSet\n ? isItemAllowedForSets(chain.id, configChainIdsSet)\n : true\n // If the integrator uses external wallet management and has not opted in for partial wallet management,\n // restrict the displayed chains to those compatible with external wallet management.\n // This ensures users only see chains for which they can sign transactions.\n const passesWalletConfigFilter = isFromKey\n ? !useExternalWalletProvidersOnly ||\n externalChainTypes.includes(chain.chainType)\n : true\n return passesChainsConfigFilter && passesWalletConfigFilter\n })\n\n const chainOrder = storeRef.current?.getState().initializeChains(\n filteredChains.map((chain) => chain.id),\n key\n )\n\n const isSwapTo = swapOnly && key === 'to'\n\n // Show \"All networks\" button if there are multiple networks\n const showAllNetworks =\n filteredChains.length > 1 && !hiddenUI?.allNetworks && !isSwapTo\n\n // Initialize the isAllNetworks with true if the tab is shown,\n // there is no config chain value and no url chain value\n const urlValues = getDefaultValuesFromQueryString({
|
|
1
|
+
{"version":3,"file":"ChainOrderStore.js","names":[],"sources":["../../../../src/stores/chains/ChainOrderStore.tsx"],"sourcesContent":["import { createContext, type JSX, use, useEffect, useRef } from 'react'\nimport type { StoreApi, UseBoundStore } from 'zustand'\nimport { useShallow } from 'zustand/shallow'\nimport { useChains } from '../../hooks/useChains.js'\nimport { useSwapOnly } from '../../hooks/useSwapOnly.js'\nimport { useExternalWalletProvider } from '../../providers/WalletProvider/useExternalWalletProvider.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { getConfigItemSets, isItemAllowedForSets } from '../../utils/item.js'\nimport { getDefaultValuesFromQueryString } from '../form/getDefaultValuesFromQueryString.js'\nimport type { FormType } from '../form/types.js'\nimport { useFieldActions } from '../form/useFieldActions.js'\nimport type { PersistStoreProviderProps } from '../types.js'\nimport { createChainOrderStore } from './createChainOrderStore.js'\nimport type { ChainOrderState } from './types.js'\n\ntype ChainOrderStore = UseBoundStore<StoreApi<ChainOrderState>>\n\nconst ChainOrderStoreContext = createContext<ChainOrderStore | null>(null)\n\nexport function ChainOrderStoreProvider({\n children,\n ...props\n}: PersistStoreProviderProps): JSX.Element {\n const {\n chains: chainsConfig,\n hiddenUI,\n fromChain: fromChainConfig,\n toChain: toChainConfig,\n buildUrl,\n } = useWidgetConfig()\n const storeRef = useRef<ChainOrderStore>(null)\n const { chains } = useChains()\n const { setFieldValue, getFieldValues, isTouched } = useFieldActions()\n const swapOnly = useSwapOnly()\n const { variant } = useWidgetConfig()\n const { externalChainTypes, useExternalWalletProvidersOnly } =\n useExternalWalletProvider()\n\n if (!storeRef.current) {\n storeRef.current = createChainOrderStore({\n ...props,\n })\n }\n\n useEffect(() => {\n if (chains) {\n ;(['from', 'to'] as FormType[]).forEach((key) => {\n const configChainIds = chainsConfig?.[key]\n const isFromKey = key === 'from'\n\n // Convert configChainIds to Sets for O(1) lookup\n const configChainIdsSet = getConfigItemSets(\n configChainIds,\n (chainIds) => new Set(chainIds)\n )\n const filteredChains = chains.filter((chain) => {\n const passesChainsConfigFilter = configChainIdsSet\n ? isItemAllowedForSets(chain.id, configChainIdsSet)\n : true\n // If the integrator uses external wallet management and has not opted in for partial wallet management,\n // restrict the displayed chains to those compatible with external wallet management.\n // This ensures users only see chains for which they can sign transactions.\n const passesWalletConfigFilter = isFromKey\n ? !useExternalWalletProvidersOnly ||\n externalChainTypes.includes(chain.chainType)\n : true\n return passesChainsConfigFilter && passesWalletConfigFilter\n })\n\n const chainOrder = storeRef.current?.getState().initializeChains(\n filteredChains.map((chain) => chain.id),\n key\n )\n\n const isSwapTo = swapOnly && key === 'to'\n\n // Show \"All networks\" button if there are multiple networks\n const showAllNetworks =\n filteredChains.length > 1 && !hiddenUI?.allNetworks && !isSwapTo\n\n // Initialize the isAllNetworks with true if the tab is shown,\n // there is no config chain value and no url chain value\n const urlValues = getDefaultValuesFromQueryString({\n buildUrl,\n chains: chainsConfig,\n })\n const urlChainValue =\n key === 'from' ? urlValues.fromChain : urlValues.toChain\n const configChainValue =\n key === 'from' ? fromChainConfig : toChainConfig\n\n // A touched, empty chain field was cleared deliberately — \"All\n // networks\". It has to outrank the two values above, because both\n // still name the chain the page was opened on at that moment: the\n // query string is rewritten a commit later, and an integrator that\n // seeds its config from the URL never rewrites it.\n const [chainValue] = getFieldValues(`${key}Chain`)\n const clearedForAllNetworks = !chainValue && isTouched(`${key}Chain`)\n\n const initialIsAllNetworks =\n showAllNetworks &&\n (clearedForAllNetworks || (!configChainValue && !urlChainValue))\n storeRef.current?.getState().setIsAllNetworks(initialIsAllNetworks, key)\n storeRef.current?.getState().setShowAllNetworks(showAllNetworks, key)\n\n // Copying an empty source would alternate with the fallback below.\n if (isSwapTo) {\n const [fromChainValue] = getFieldValues('fromChain')\n if (fromChainValue) {\n if (fromChainValue !== chainValue) {\n setFieldValue('toChain', fromChainValue)\n }\n return\n }\n }\n\n // When \"All Networks\" is active, don't auto-select a chain from the\n // persisted chain order. This prevents stale cross-ecosystem selections\n // (e.g. EVM from + Solana to) from triggering the \"destination wallet\n // address required\" message after a page refresh.\n if (initialIsAllNetworks) {\n return\n }\n\n // With no source to follow, drop a destination the list no longer offers.\n const keepsChain =\n chainValue &&\n (!isSwapTo || filteredChains.some((chain) => chain.id === chainValue))\n if (keepsChain) {\n return\n }\n\n const firstAllowedPinnedChain = storeRef.current\n ?.getState()\n .pinnedChains?.find((chainId) =>\n filteredChains.some((chain) => chain.id === chainId)\n )\n if (\n variant === 'wide' &&\n !hiddenUI?.chainSidebar &&\n firstAllowedPinnedChain\n ) {\n setFieldValue(`${key}Chain`, firstAllowedPinnedChain)\n } else if (chainOrder?.length) {\n setFieldValue(`${key}Chain`, chainOrder[0])\n }\n })\n }\n }, [\n chains,\n chainsConfig,\n externalChainTypes,\n getFieldValues,\n isTouched,\n setFieldValue,\n useExternalWalletProvidersOnly,\n variant,\n hiddenUI,\n swapOnly,\n fromChainConfig,\n toChainConfig,\n buildUrl,\n ])\n\n return (\n <ChainOrderStoreContext value={storeRef.current}>\n {children}\n </ChainOrderStoreContext>\n )\n}\n\nfunction useChainOrderStoreContext() {\n const useStore = use(ChainOrderStoreContext)\n if (!useStore) {\n throw new Error(\n `You forgot to wrap your component in <${ChainOrderStoreProvider.name}>.`\n )\n }\n return useStore\n}\n\nexport function useChainOrderStore<T>(\n selector: (state: ChainOrderState) => T\n): T {\n const useStore = useChainOrderStoreContext()\n return useStore(useShallow(selector))\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAM,yBAAyB,cAAsC,IAAI;AAEzE,SAAgB,wBAAwB,EACtC,UACA,GAAG,SACsC;CACzC,MAAM,EACJ,QAAQ,cACR,UACA,WAAW,iBACX,SAAS,eACT,aACE,gBAAgB;CACpB,MAAM,WAAW,OAAwB,IAAI;CAC7C,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,EAAE,eAAe,gBAAgB,cAAc,gBAAgB;CACrE,MAAM,WAAW,YAAY;CAC7B,MAAM,EAAE,YAAY,gBAAgB;CACpC,MAAM,EAAE,oBAAoB,mCAC1B,0BAA0B;CAE5B,IAAI,CAAC,SAAS,SACZ,SAAS,UAAU,sBAAsB,EACvC,GAAG,MACL,CAAC;CAGH,gBAAgB;EACd,IAAI,QACD,CAAE,QAAQ,IAAI,CAAC,CAAgB,SAAS,QAAQ;GAC/C,MAAM,iBAAiB,eAAe;GACtC,MAAM,YAAY,QAAQ;GAG1B,MAAM,oBAAoB,kBACxB,iBACC,aAAa,IAAI,IAAI,QAAQ,CAChC;GACA,MAAM,iBAAiB,OAAO,QAAQ,UAAU;IAC9C,MAAM,2BAA2B,oBAC7B,qBAAqB,MAAM,IAAI,iBAAiB,IAChD;IAIJ,MAAM,2BAA2B,YAC7B,CAAC,kCACD,mBAAmB,SAAS,MAAM,SAAS,IAC3C;IACJ,OAAO,4BAA4B;GACrC,CAAC;GAED,MAAM,aAAa,SAAS,SAAS,SAAS,CAAC,CAAC,iBAC9C,eAAe,KAAK,UAAU,MAAM,EAAE,GACtC,GACF;GAEA,MAAM,WAAW,YAAY,QAAQ;GAGrC,MAAM,kBACJ,eAAe,SAAS,KAAK,CAAC,UAAU,eAAe,CAAC;GAI1D,MAAM,YAAY,gCAAgC;IAChD;IACA,QAAQ;GACV,CAAC;GACD,MAAM,gBACJ,QAAQ,SAAS,UAAU,YAAY,UAAU;GACnD,MAAM,mBACJ,QAAQ,SAAS,kBAAkB;GAOrC,MAAM,CAAC,cAAc,eAAe,GAAG,IAAI,MAAM;GACjD,MAAM,wBAAwB,CAAC,cAAc,UAAU,GAAG,IAAI,MAAM;GAEpE,MAAM,uBACJ,oBACC,yBAA0B,CAAC,oBAAoB,CAAC;GACnD,SAAS,SAAS,SAAS,CAAC,CAAC,iBAAiB,sBAAsB,GAAG;GACvE,SAAS,SAAS,SAAS,CAAC,CAAC,mBAAmB,iBAAiB,GAAG;GAGpE,IAAI,UAAU;IACZ,MAAM,CAAC,kBAAkB,eAAe,WAAW;IACnD,IAAI,gBAAgB;KAClB,IAAI,mBAAmB,YACrB,cAAc,WAAW,cAAc;KAEzC;IACF;GACF;GAMA,IAAI,sBACF;GAOF,IAFE,eACC,CAAC,YAAY,eAAe,MAAM,UAAU,MAAM,OAAO,UAAU,IAEpE;GAGF,MAAM,0BAA0B,SAAS,SACrC,SAAS,CAAC,CACX,cAAc,MAAM,YACnB,eAAe,MAAM,UAAU,MAAM,OAAO,OAAO,CACrD;GACF,IACE,YAAY,UACZ,CAAC,UAAU,gBACX,yBAEA,cAAc,GAAG,IAAI,QAAQ,uBAAuB;QAC/C,IAAI,YAAY,QACrB,cAAc,GAAG,IAAI,QAAQ,WAAW,EAAE;EAE9C,CAAC;CAEL,GAAG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,OACE,oBAAC,wBAAD;EAAwB,OAAO,SAAS;EACrC;CACqB,CAAA;AAE5B;AAEA,SAAS,4BAA4B;CACnC,MAAM,WAAW,IAAI,sBAAsB;CAC3C,IAAI,CAAC,UACH,MAAM,IAAI,MACR,yCAAyC,wBAAwB,KAAK,GACxE;CAEF,OAAO;AACT;AAEA,SAAgB,mBACd,UACG;CAEH,OADiB,0BACH,CAAC,CAAC,WAAW,QAAQ,CAAC;AACtC"}
|
|
@@ -16,7 +16,7 @@ const initialiseDefaultValues = (defaultValues) => ({
|
|
|
16
16
|
});
|
|
17
17
|
const FormStoreProvider = ({ children, formRef }) => {
|
|
18
18
|
const widgetConfig = useWidgetConfig();
|
|
19
|
-
const { fromChain, fromToken, toChain, toToken, fromAmount, toAmount, toAddress, formUpdateKey, buildUrl } = widgetConfig;
|
|
19
|
+
const { fromChain, fromToken, toChain, toToken, fromAmount, toAmount, toAddress, formUpdateKey, buildUrl, chains } = widgetConfig;
|
|
20
20
|
const storeRef = useRef(null);
|
|
21
21
|
const configHasFromChain = Object.hasOwn(widgetConfig, "fromChain");
|
|
22
22
|
const configHasFromToken = Object.hasOwn(widgetConfig, "fromToken");
|
|
@@ -53,7 +53,8 @@ const FormStoreProvider = ({ children, formRef }) => {
|
|
|
53
53
|
if (!storeRef.current) {
|
|
54
54
|
const queryDefaults = getDefaultValuesFromQueryString({
|
|
55
55
|
buildUrl,
|
|
56
|
-
includeToAddress: false
|
|
56
|
+
includeToAddress: false,
|
|
57
|
+
chains
|
|
57
58
|
});
|
|
58
59
|
storeRef.current = createFormStore(initialiseDefaultValues({
|
|
59
60
|
...reactiveFormValues,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormStore.js","names":[],"sources":["../../../../src/stores/form/FormStore.tsx"],"sourcesContent":["import type { PropsWithChildren } from 'react'\nimport { useMemo, useRef } from 'react'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport type { FormRef } from '../../types/widget.js'\nimport { createFormStore, formDefaultValues } from './createFormStore.js'\nimport { FormStoreContext } from './FormStoreContext.js'\nimport { FormUpdater } from './FormUpdater.js'\nimport { getDefaultValuesFromQueryString } from './getDefaultValuesFromQueryString.js'\nimport type { DefaultValues, FormStoreStore } from './types.js'\nimport { useFormRef } from './useFormRef.js'\n\n// decorates and initialise the form date for use in the form store\nconst initialiseDefaultValues = (defaultValues: Partial<DefaultValues>) => ({\n ...formDefaultValues,\n ...defaultValues,\n fromAmount:\n (typeof defaultValues.fromAmount === 'number'\n ? (defaultValues.fromAmount as number)?.toPrecision?.()\n : defaultValues.fromAmount) || formDefaultValues.fromAmount,\n toAmount:\n (typeof defaultValues.toAmount === 'number'\n ? (defaultValues.toAmount as number)?.toPrecision?.()\n : defaultValues.toAmount) || formDefaultValues.toAmount,\n // Prevent setting address when the field is hidden\n toAddress: defaultValues.toAddress || formDefaultValues.toAddress,\n})\n\ninterface FormStoreProviderProps extends PropsWithChildren {\n formRef?: FormRef\n}\n\nexport const FormStoreProvider: React.FC<FormStoreProviderProps> = ({\n children,\n formRef,\n}) => {\n const widgetConfig = useWidgetConfig()\n\n const {\n fromChain,\n fromToken,\n toChain,\n toToken,\n fromAmount,\n toAmount,\n toAddress,\n formUpdateKey,\n buildUrl,\n } = widgetConfig\n\n const storeRef = useRef<FormStoreStore>(null)\n\n const configHasFromChain = Object.hasOwn(widgetConfig, 'fromChain')\n const configHasFromToken = Object.hasOwn(widgetConfig, 'fromToken')\n const configHasFromAmount = Object.hasOwn(widgetConfig, 'fromAmount')\n const configHasToAmount = Object.hasOwn(widgetConfig, 'toAmount')\n const configHasToAddress = Object.hasOwn(widgetConfig, 'toAddress')\n const configHasToChain = Object.hasOwn(widgetConfig, 'toChain')\n const configHasToToken = Object.hasOwn(widgetConfig, 'toToken')\n\n // We use the presence/absence of a property to decide if the form values in state need to be updated\n // We only build and set a property on the memoized form values here if they are included in the\n // config - undefined is considered a valid value that will reset that form field\n // biome-ignore lint/correctness/useExhaustiveDependencies: formUpdateKey is needed here.\n const reactiveFormValues = useMemo(\n () => ({\n ...(configHasFromChain ? { fromChain } : undefined),\n ...(configHasFromToken ? { fromToken } : undefined),\n ...(configHasFromAmount\n ? {\n fromAmount:\n (typeof fromAmount === 'number'\n ? fromAmount?.toPrecision()\n : fromAmount) || formDefaultValues.fromAmount,\n }\n : undefined),\n ...(configHasToAmount\n ? {\n toAmount:\n (typeof toAmount === 'number'\n ? toAmount?.toPrecision()\n : toAmount) || formDefaultValues.toAmount,\n }\n : undefined),\n ...(configHasToChain ? { toChain } : undefined),\n ...(configHasToToken ? { toToken } : undefined),\n ...(configHasToAddress\n ? {\n toAddress: toAddress?.address || formDefaultValues.toAddress,\n }\n : undefined),\n }),\n [\n fromAmount,\n toAmount,\n fromChain,\n fromToken,\n toAddress,\n toChain,\n toToken,\n // formUpdateKey should be a randomly assigned unique key. It can be used to force updates for the form field values\n formUpdateKey,\n configHasFromChain,\n configHasFromToken,\n configHasFromAmount,\n configHasToAmount,\n configHasToAddress,\n configHasToChain,\n configHasToToken,\n ]\n )\n\n if (!storeRef.current) {\n const queryDefaults = getDefaultValuesFromQueryString({\n buildUrl,\n includeToAddress: false,\n })\n storeRef.current = createFormStore(\n initialiseDefaultValues({\n ...reactiveFormValues,\n ...queryDefaults,\n })\n )\n }\n\n useFormRef(storeRef.current, formRef)\n\n return (\n <FormStoreContext value={storeRef.current}>\n {children}\n <FormUpdater reactiveFormValues={reactiveFormValues} />\n </FormStoreContext>\n )\n}\n"],"mappings":";;;;;;;;;AAYA,MAAM,2BAA2B,mBAA2C;CAC1E,GAAG;CACH,GAAG;CACH,aACG,OAAO,cAAc,eAAe,WAChC,cAAc,YAAuB,cAAc,IACpD,cAAc,eAAe,kBAAkB;CACrD,WACG,OAAO,cAAc,aAAa,WAC9B,cAAc,UAAqB,cAAc,IAClD,cAAc,aAAa,kBAAkB;CAEnD,WAAW,cAAc,aAAa,kBAAkB;AAC1D;AAMA,MAAa,qBAAuD,EAClE,UACA,cACI;CACJ,MAAM,eAAe,gBAAgB;CAErC,MAAM,EACJ,WACA,WACA,SACA,SACA,YACA,UACA,WACA,eACA,
|
|
1
|
+
{"version":3,"file":"FormStore.js","names":[],"sources":["../../../../src/stores/form/FormStore.tsx"],"sourcesContent":["import type { PropsWithChildren } from 'react'\nimport { useMemo, useRef } from 'react'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport type { FormRef } from '../../types/widget.js'\nimport { createFormStore, formDefaultValues } from './createFormStore.js'\nimport { FormStoreContext } from './FormStoreContext.js'\nimport { FormUpdater } from './FormUpdater.js'\nimport { getDefaultValuesFromQueryString } from './getDefaultValuesFromQueryString.js'\nimport type { DefaultValues, FormStoreStore } from './types.js'\nimport { useFormRef } from './useFormRef.js'\n\n// decorates and initialise the form date for use in the form store\nconst initialiseDefaultValues = (defaultValues: Partial<DefaultValues>) => ({\n ...formDefaultValues,\n ...defaultValues,\n fromAmount:\n (typeof defaultValues.fromAmount === 'number'\n ? (defaultValues.fromAmount as number)?.toPrecision?.()\n : defaultValues.fromAmount) || formDefaultValues.fromAmount,\n toAmount:\n (typeof defaultValues.toAmount === 'number'\n ? (defaultValues.toAmount as number)?.toPrecision?.()\n : defaultValues.toAmount) || formDefaultValues.toAmount,\n // Prevent setting address when the field is hidden\n toAddress: defaultValues.toAddress || formDefaultValues.toAddress,\n})\n\ninterface FormStoreProviderProps extends PropsWithChildren {\n formRef?: FormRef\n}\n\nexport const FormStoreProvider: React.FC<FormStoreProviderProps> = ({\n children,\n formRef,\n}) => {\n const widgetConfig = useWidgetConfig()\n\n const {\n fromChain,\n fromToken,\n toChain,\n toToken,\n fromAmount,\n toAmount,\n toAddress,\n formUpdateKey,\n buildUrl,\n chains,\n } = widgetConfig\n\n const storeRef = useRef<FormStoreStore>(null)\n\n const configHasFromChain = Object.hasOwn(widgetConfig, 'fromChain')\n const configHasFromToken = Object.hasOwn(widgetConfig, 'fromToken')\n const configHasFromAmount = Object.hasOwn(widgetConfig, 'fromAmount')\n const configHasToAmount = Object.hasOwn(widgetConfig, 'toAmount')\n const configHasToAddress = Object.hasOwn(widgetConfig, 'toAddress')\n const configHasToChain = Object.hasOwn(widgetConfig, 'toChain')\n const configHasToToken = Object.hasOwn(widgetConfig, 'toToken')\n\n // We use the presence/absence of a property to decide if the form values in state need to be updated\n // We only build and set a property on the memoized form values here if they are included in the\n // config - undefined is considered a valid value that will reset that form field\n // biome-ignore lint/correctness/useExhaustiveDependencies: formUpdateKey is needed here.\n const reactiveFormValues = useMemo(\n () => ({\n ...(configHasFromChain ? { fromChain } : undefined),\n ...(configHasFromToken ? { fromToken } : undefined),\n ...(configHasFromAmount\n ? {\n fromAmount:\n (typeof fromAmount === 'number'\n ? fromAmount?.toPrecision()\n : fromAmount) || formDefaultValues.fromAmount,\n }\n : undefined),\n ...(configHasToAmount\n ? {\n toAmount:\n (typeof toAmount === 'number'\n ? toAmount?.toPrecision()\n : toAmount) || formDefaultValues.toAmount,\n }\n : undefined),\n ...(configHasToChain ? { toChain } : undefined),\n ...(configHasToToken ? { toToken } : undefined),\n ...(configHasToAddress\n ? {\n toAddress: toAddress?.address || formDefaultValues.toAddress,\n }\n : undefined),\n }),\n [\n fromAmount,\n toAmount,\n fromChain,\n fromToken,\n toAddress,\n toChain,\n toToken,\n // formUpdateKey should be a randomly assigned unique key. It can be used to force updates for the form field values\n formUpdateKey,\n configHasFromChain,\n configHasFromToken,\n configHasFromAmount,\n configHasToAmount,\n configHasToAddress,\n configHasToChain,\n configHasToToken,\n ]\n )\n\n if (!storeRef.current) {\n const queryDefaults = getDefaultValuesFromQueryString({\n buildUrl,\n includeToAddress: false,\n chains,\n })\n storeRef.current = createFormStore(\n initialiseDefaultValues({\n ...reactiveFormValues,\n ...queryDefaults,\n })\n )\n }\n\n useFormRef(storeRef.current, formRef)\n\n return (\n <FormStoreContext value={storeRef.current}>\n {children}\n <FormUpdater reactiveFormValues={reactiveFormValues} />\n </FormStoreContext>\n )\n}\n"],"mappings":";;;;;;;;;AAYA,MAAM,2BAA2B,mBAA2C;CAC1E,GAAG;CACH,GAAG;CACH,aACG,OAAO,cAAc,eAAe,WAChC,cAAc,YAAuB,cAAc,IACpD,cAAc,eAAe,kBAAkB;CACrD,WACG,OAAO,cAAc,aAAa,WAC9B,cAAc,UAAqB,cAAc,IAClD,cAAc,aAAa,kBAAkB;CAEnD,WAAW,cAAc,aAAa,kBAAkB;AAC1D;AAMA,MAAa,qBAAuD,EAClE,UACA,cACI;CACJ,MAAM,eAAe,gBAAgB;CAErC,MAAM,EACJ,WACA,WACA,SACA,SACA,YACA,UACA,WACA,eACA,UACA,WACE;CAEJ,MAAM,WAAW,OAAuB,IAAI;CAE5C,MAAM,qBAAqB,OAAO,OAAO,cAAc,WAAW;CAClE,MAAM,qBAAqB,OAAO,OAAO,cAAc,WAAW;CAClE,MAAM,sBAAsB,OAAO,OAAO,cAAc,YAAY;CACpE,MAAM,oBAAoB,OAAO,OAAO,cAAc,UAAU;CAChE,MAAM,qBAAqB,OAAO,OAAO,cAAc,WAAW;CAClE,MAAM,mBAAmB,OAAO,OAAO,cAAc,SAAS;CAC9D,MAAM,mBAAmB,OAAO,OAAO,cAAc,SAAS;CAM9D,MAAM,qBAAqB,eAClB;EACL,GAAI,qBAAqB,EAAE,UAAU,IAAI,KAAA;EACzC,GAAI,qBAAqB,EAAE,UAAU,IAAI,KAAA;EACzC,GAAI,sBACA,EACE,aACG,OAAO,eAAe,WACnB,YAAY,YAAY,IACxB,eAAe,kBAAkB,WACzC,IACA,KAAA;EACJ,GAAI,oBACA,EACE,WACG,OAAO,aAAa,WACjB,UAAU,YAAY,IACtB,aAAa,kBAAkB,SACvC,IACA,KAAA;EACJ,GAAI,mBAAmB,EAAE,QAAQ,IAAI,KAAA;EACrC,GAAI,mBAAmB,EAAE,QAAQ,IAAI,KAAA;EACrC,GAAI,qBACA,EACE,WAAW,WAAW,WAAW,kBAAkB,UACrD,IACA,KAAA;CACN,IACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;CAEA,IAAI,CAAC,SAAS,SAAS;EACrB,MAAM,gBAAgB,gCAAgC;GACpD;GACA,kBAAkB;GAClB;EACF,CAAC;EACD,SAAS,UAAU,gBACjB,wBAAwB;GACtB,GAAG;GACH,GAAG;EACL,CAAC,CACH;CACF;CAEA,WAAW,SAAS,SAAS,OAAO;CAEpC,OACE,qBAAC,kBAAD;EAAkB,OAAO,SAAS;EAAlC,UAAA,CACG,UACD,oBAAC,aAAD,EAAiC,mBAAqB,CAAA,CACtC;;AAEtB"}
|
|
@@ -27,10 +27,13 @@ const URLSearchParamsBuilder = () => {
|
|
|
27
27
|
const values = useFieldValues(...formValueKeys, ...limitFormValueKeys);
|
|
28
28
|
const { setSelectedBookmark, addRecentWallet } = useBookmarkActions();
|
|
29
29
|
const { validateAddress } = useAddressValidation();
|
|
30
|
-
const { buildUrl, mode } = useWidgetConfig();
|
|
30
|
+
const { buildUrl, mode, chains } = useWidgetConfig();
|
|
31
31
|
const { getFieldValues, isTouched, setUserAndDefaultValues } = useFieldActions();
|
|
32
32
|
useEffect(() => {
|
|
33
|
-
const { toAddress, ...initialFormValues } = getDefaultValuesFromQueryString({
|
|
33
|
+
const { toAddress, ...initialFormValues } = getDefaultValuesFromQueryString({
|
|
34
|
+
buildUrl,
|
|
35
|
+
chains
|
|
36
|
+
});
|
|
34
37
|
/**
|
|
35
38
|
* When URL builder is enabled and user opens a page with toAddress parameter,
|
|
36
39
|
* validate the address and set it up as a bookmark. This allows direct linking
|
|
@@ -43,7 +46,8 @@ const URLSearchParamsBuilder = () => {
|
|
|
43
46
|
if (validationResult.isValid && toAddress) {
|
|
44
47
|
const bookmark = {
|
|
45
48
|
address: validationResult.address,
|
|
46
|
-
chainType: validationResult.chainType
|
|
49
|
+
chainType: validationResult.chainType,
|
|
50
|
+
chainId: validationResult.chainId
|
|
47
51
|
};
|
|
48
52
|
setUserAndDefaultValues({ toAddress });
|
|
49
53
|
setSelectedBookmark(bookmark);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"URLSearchParamsBuilder.js","names":[],"sources":["../../../../src/stores/form/URLSearchParamsBuilder.tsx"],"sourcesContent":["import { useLocation } from '@tanstack/react-router'\nimport { useEffect } from 'react'\nimport { useAddressValidation } from '../../hooks/useAddressValidation.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { useBookmarkActions } from '../bookmarks/useBookmarkActions.js'\nimport type { FormFieldNames } from '../form/types.js'\nimport { useFieldActions } from '../form/useFieldActions.js'\nimport { useFieldValues } from '../form/useFieldValues.js'\nimport { useTouchedFields } from '../form/useTouchedFields.js'\nimport { getDefaultValuesFromQueryString } from './getDefaultValuesFromQueryString.js'\n\nconst formValueKeys: FormFieldNames[] = [\n 'fromAmount',\n 'fromChain',\n 'fromToken',\n 'toAddress',\n 'toChain',\n 'toToken',\n]\n\n// Limit-order params are only mirrored to the URL while the limit tab is\n// active, and stripped when the user leaves it. Unlike the base keys these are\n// presence-gated (not touched-gated) so the URL fully reproduces the limit\n// order — including the boolean/number fields, which are never \"falsy-empty\".\nconst limitFormValueKeys: FormFieldNames[] = [\n 'toAmount',\n 'validUntil',\n 'partiallyFillable',\n]\n\nexport const URLSearchParamsBuilder = () => {\n const { pathname } = useLocation()\n const touchedFields = useTouchedFields()\n const values = useFieldValues(...formValueKeys, ...limitFormValueKeys)\n const { setSelectedBookmark, addRecentWallet } = useBookmarkActions()\n const { validateAddress } = useAddressValidation()\n const { buildUrl, mode } = useWidgetConfig()\n // Using these methods as trying to use the touchedFields and values above\n // often has a lag that can effect the widgets initialisation sequence\n // and accidentally cause values to be wiped from the query string\n const { getFieldValues, isTouched, setUserAndDefaultValues } =\n useFieldActions()\n\n useEffect(() => {\n // get the initial values from the querystring\n const formValues = getDefaultValuesFromQueryString({ buildUrl })\n const { toAddress, ...initialFormValues } = formValues\n\n /**\n * When URL builder is enabled and user opens a page with toAddress parameter,\n * validate the address and set it up as a bookmark. This allows direct linking\n * to the widget with a pre-filled destination address that will be treated the\n * same way as a manually entered and validated address.\n */\n const initializeFromAddress = async () => {\n if (toAddress) {\n try {\n const validationResult = await validateAddress({\n value: toAddress,\n })\n if (validationResult.isValid && toAddress) {\n const bookmark = {\n address: validationResult.address,\n chainType: validationResult.chainType,\n }\n setUserAndDefaultValues({ toAddress })\n setSelectedBookmark(bookmark)\n addRecentWallet(bookmark)\n }\n } catch (_) {\n // Address validation failed\n }\n }\n }\n\n setUserAndDefaultValues(initialFormValues)\n initializeFromAddress()\n }, [\n setUserAndDefaultValues,\n validateAddress,\n setSelectedBookmark,\n addRecentWallet,\n buildUrl,\n ])\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: run only when pathname changes\n useEffect(() => {\n // set the values on the querysting\n const url = new URL(window.location as any)\n formValueKeys.forEach((key, _index) => {\n const value = getFieldValues(key)[0]\n if (isTouched(key) && value) {\n url.searchParams.set(key, value.toString())\n } else if (url.searchParams.has(key) && !value) {\n url.searchParams.delete(key)\n }\n })\n // Limit params live in the URL only while the limit tab is active.\n limitFormValueKeys.forEach((key) => {\n const value = getFieldValues(key)[0]\n // Booleans are always \"set\" (false is meaningful); strings/numbers use\n // truthiness so an empty toAmount is treated as absent.\n const hasValue = typeof value === 'boolean' ? true : Boolean(value)\n if (mode === 'limit' && hasValue) {\n url.searchParams.set(key, String(value))\n } else {\n url.searchParams.delete(key)\n }\n })\n url.searchParams.sort()\n window.history.replaceState(window.history.state, '', url)\n }, [pathname, touchedFields, values, isTouched, getFieldValues, mode])\n\n return null\n}\n"],"mappings":";;;;;;;;;;AAWA,MAAM,gBAAkC;CACtC;CACA;CACA;CACA;CACA;CACA;AACF;AAMA,MAAM,qBAAuC;CAC3C;CACA;CACA;AACF;AAEA,MAAa,+BAA+B;CAC1C,MAAM,EAAE,aAAa,YAAY;CACjC,MAAM,gBAAgB,iBAAiB;CACvC,MAAM,SAAS,eAAe,GAAG,eAAe,GAAG,kBAAkB;CACrE,MAAM,EAAE,qBAAqB,oBAAoB,mBAAmB;CACpE,MAAM,EAAE,oBAAoB,qBAAqB;CACjD,MAAM,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"URLSearchParamsBuilder.js","names":[],"sources":["../../../../src/stores/form/URLSearchParamsBuilder.tsx"],"sourcesContent":["import { useLocation } from '@tanstack/react-router'\nimport { useEffect } from 'react'\nimport { useAddressValidation } from '../../hooks/useAddressValidation.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { useBookmarkActions } from '../bookmarks/useBookmarkActions.js'\nimport type { FormFieldNames } from '../form/types.js'\nimport { useFieldActions } from '../form/useFieldActions.js'\nimport { useFieldValues } from '../form/useFieldValues.js'\nimport { useTouchedFields } from '../form/useTouchedFields.js'\nimport { getDefaultValuesFromQueryString } from './getDefaultValuesFromQueryString.js'\n\nconst formValueKeys: FormFieldNames[] = [\n 'fromAmount',\n 'fromChain',\n 'fromToken',\n 'toAddress',\n 'toChain',\n 'toToken',\n]\n\n// Limit-order params are only mirrored to the URL while the limit tab is\n// active, and stripped when the user leaves it. Unlike the base keys these are\n// presence-gated (not touched-gated) so the URL fully reproduces the limit\n// order — including the boolean/number fields, which are never \"falsy-empty\".\nconst limitFormValueKeys: FormFieldNames[] = [\n 'toAmount',\n 'validUntil',\n 'partiallyFillable',\n]\n\nexport const URLSearchParamsBuilder = () => {\n const { pathname } = useLocation()\n const touchedFields = useTouchedFields()\n const values = useFieldValues(...formValueKeys, ...limitFormValueKeys)\n const { setSelectedBookmark, addRecentWallet } = useBookmarkActions()\n const { validateAddress } = useAddressValidation()\n const { buildUrl, mode, chains } = useWidgetConfig()\n // Using these methods as trying to use the touchedFields and values above\n // often has a lag that can effect the widgets initialisation sequence\n // and accidentally cause values to be wiped from the query string\n const { getFieldValues, isTouched, setUserAndDefaultValues } =\n useFieldActions()\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: the URL is read once, not again on each config render\n useEffect(() => {\n // get the initial values from the querystring\n const formValues = getDefaultValuesFromQueryString({ buildUrl, chains })\n const { toAddress, ...initialFormValues } = formValues\n\n /**\n * When URL builder is enabled and user opens a page with toAddress parameter,\n * validate the address and set it up as a bookmark. This allows direct linking\n * to the widget with a pre-filled destination address that will be treated the\n * same way as a manually entered and validated address.\n */\n const initializeFromAddress = async () => {\n if (toAddress) {\n try {\n const validationResult = await validateAddress({\n value: toAddress,\n })\n if (validationResult.isValid && toAddress) {\n const bookmark = {\n address: validationResult.address,\n chainType: validationResult.chainType,\n chainId: validationResult.chainId,\n }\n setUserAndDefaultValues({ toAddress })\n setSelectedBookmark(bookmark)\n addRecentWallet(bookmark)\n }\n } catch (_) {\n // Address validation failed\n }\n }\n }\n\n setUserAndDefaultValues(initialFormValues)\n initializeFromAddress()\n }, [\n setUserAndDefaultValues,\n validateAddress,\n setSelectedBookmark,\n addRecentWallet,\n buildUrl,\n ])\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: run only when pathname changes\n useEffect(() => {\n // set the values on the querysting\n const url = new URL(window.location as any)\n formValueKeys.forEach((key, _index) => {\n const value = getFieldValues(key)[0]\n if (isTouched(key) && value) {\n url.searchParams.set(key, value.toString())\n } else if (url.searchParams.has(key) && !value) {\n url.searchParams.delete(key)\n }\n })\n // Limit params live in the URL only while the limit tab is active.\n limitFormValueKeys.forEach((key) => {\n const value = getFieldValues(key)[0]\n // Booleans are always \"set\" (false is meaningful); strings/numbers use\n // truthiness so an empty toAmount is treated as absent.\n const hasValue = typeof value === 'boolean' ? true : Boolean(value)\n if (mode === 'limit' && hasValue) {\n url.searchParams.set(key, String(value))\n } else {\n url.searchParams.delete(key)\n }\n })\n url.searchParams.sort()\n window.history.replaceState(window.history.state, '', url)\n }, [pathname, touchedFields, values, isTouched, getFieldValues, mode])\n\n return null\n}\n"],"mappings":";;;;;;;;;;AAWA,MAAM,gBAAkC;CACtC;CACA;CACA;CACA;CACA;CACA;AACF;AAMA,MAAM,qBAAuC;CAC3C;CACA;CACA;AACF;AAEA,MAAa,+BAA+B;CAC1C,MAAM,EAAE,aAAa,YAAY;CACjC,MAAM,gBAAgB,iBAAiB;CACvC,MAAM,SAAS,eAAe,GAAG,eAAe,GAAG,kBAAkB;CACrE,MAAM,EAAE,qBAAqB,oBAAoB,mBAAmB;CACpE,MAAM,EAAE,oBAAoB,qBAAqB;CACjD,MAAM,EAAE,UAAU,MAAM,WAAW,gBAAgB;CAInD,MAAM,EAAE,gBAAgB,WAAW,4BACjC,gBAAgB;CAGlB,gBAAgB;EAGd,MAAM,EAAE,WAAW,GAAG,sBADH,gCAAgC;GAAE;GAAU;EAAO,CACjB;;;;;;;EAQrD,MAAM,wBAAwB,YAAY;GACxC,IAAI,WACF,IAAI;IACF,MAAM,mBAAmB,MAAM,gBAAgB,EAC7C,OAAO,UACT,CAAC;IACD,IAAI,iBAAiB,WAAW,WAAW;KACzC,MAAM,WAAW;MACf,SAAS,iBAAiB;MAC1B,WAAW,iBAAiB;MAC5B,SAAS,iBAAiB;KAC5B;KACA,wBAAwB,EAAE,UAAU,CAAC;KACrC,oBAAoB,QAAQ;KAC5B,gBAAgB,QAAQ;IAC1B;GACF,SAAS,GAAG,CAEZ;EAEJ;EAEA,wBAAwB,iBAAiB;EACzC,sBAAsB;CACxB,GAAG;EACD;EACA;EACA;EACA;EACA;CACF,CAAC;CAGD,gBAAgB;EAEd,MAAM,MAAM,IAAI,IAAI,OAAO,QAAe;EAC1C,cAAc,SAAS,KAAK,WAAW;GACrC,MAAM,QAAQ,eAAe,GAAG,CAAC,CAAC;GAClC,IAAI,UAAU,GAAG,KAAK,OACpB,IAAI,aAAa,IAAI,KAAK,MAAM,SAAS,CAAC;QACrC,IAAI,IAAI,aAAa,IAAI,GAAG,KAAK,CAAC,OACvC,IAAI,aAAa,OAAO,GAAG;EAE/B,CAAC;EAED,mBAAmB,SAAS,QAAQ;GAClC,MAAM,QAAQ,eAAe,GAAG,CAAC,CAAC;GAIlC,IAAI,SAAS,YADI,OAAO,UAAU,YAAY,OAAO,QAAQ,KAAK,IAEhE,IAAI,aAAa,IAAI,KAAK,OAAO,KAAK,CAAC;QAEvC,IAAI,aAAa,OAAO,GAAG;EAE/B,CAAC;EACD,IAAI,aAAa,KAAK;EACtB,OAAO,QAAQ,aAAa,OAAO,QAAQ,OAAO,IAAI,GAAG;CAC3D,GAAG;EAAC;EAAU;EAAe;EAAQ;EAAW;EAAgB;CAAI,CAAC;CAErE,OAAO;AACT"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { isDestinationOnlyChain, isUnservedDestinationChain } from "../../utils/chainType.js";
|
|
1
2
|
import { formatInputAmount } from "../../utils/format.js";
|
|
2
3
|
//#region src/stores/form/getDefaultValuesFromQueryString.ts
|
|
3
|
-
const getDefaultValuesFromQueryString = ({ buildUrl = false, includeToAddress = true }) => {
|
|
4
|
+
const getDefaultValuesFromQueryString = ({ buildUrl = false, includeToAddress = true, chains }) => {
|
|
4
5
|
if (typeof window === "undefined") return {};
|
|
5
6
|
if (!buildUrl) return {};
|
|
6
7
|
const searchParams = Object.fromEntries(new URLSearchParams(window.location.search));
|
|
8
|
+
if (isDestinationOnlyChain(Number.parseInt(searchParams.fromChain, 10))) delete searchParams.fromChain;
|
|
9
|
+
if (isUnservedDestinationChain(Number.parseInt(searchParams.toChain, 10), chains)) delete searchParams.toChain;
|
|
7
10
|
["from", "to"].forEach((key) => {
|
|
8
11
|
if (searchParams[`${key}Token`] && !searchParams[`${key}Chain`]) delete searchParams[`${key}Token`];
|
|
9
12
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getDefaultValuesFromQueryString.js","names":[],"sources":["../../../../src/stores/form/getDefaultValuesFromQueryString.ts"],"sourcesContent":["import { formatInputAmount } from '../../utils/format.js'\nimport type { DefaultValues } from './types.js'\n\ninterface GetDefaultValuesFromQueryStringOptions {\n includeToAddress?: boolean\n buildUrl?: boolean\n}\n\nexport const getDefaultValuesFromQueryString = ({\n buildUrl = false,\n includeToAddress = true,\n}: GetDefaultValuesFromQueryStringOptions): Partial<DefaultValues> => {\n if (typeof window === 'undefined') {\n return {}\n }\n\n if (!buildUrl) {\n return {}\n }\n\n const searchParams = Object.fromEntries(\n new URLSearchParams(window.location.search)\n )\n\n // Prevent using fromToken/toToken params if chain is not selected.\n ;(['from', 'to'] as const).forEach((key) => {\n if (searchParams[`${key}Token`] && !searchParams[`${key}Chain`]) {\n delete searchParams[`${key}Token`]\n }\n })\n\n return {\n ...(Number.isFinite(Number.parseInt(searchParams.fromChain, 10))\n ? { fromChain: Number.parseInt(searchParams.fromChain, 10) }\n : {}),\n ...(Number.isFinite(Number.parseInt(searchParams.toChain, 10))\n ? { toChain: Number.parseInt(searchParams.toChain, 10) }\n : {}),\n ...(searchParams.fromToken ? { fromToken: searchParams.fromToken } : {}),\n ...(searchParams.toToken ? { toToken: searchParams.toToken } : {}),\n ...(Number.isFinite(Number.parseFloat(searchParams.fromAmount))\n ? { fromAmount: formatInputAmount(searchParams.fromAmount) }\n : {}),\n ...(Number.isFinite(Number.parseFloat(searchParams.toAmount))\n ? { toAmount: formatInputAmount(searchParams.toAmount) }\n : {}),\n // Limit-order params (written only while on the limit tab).\n ...(Number.isFinite(Number.parseInt(searchParams.validUntil, 10))\n ? { validUntil: Number.parseInt(searchParams.validUntil, 10) }\n : {}),\n ...(searchParams.partiallyFillable === 'true' ||\n searchParams.partiallyFillable === 'false'\n ? { partiallyFillable: searchParams.partiallyFillable === 'true' }\n : {}),\n ...(includeToAddress && searchParams.toAddress\n ? { toAddress: searchParams.toAddress }\n : {}),\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"getDefaultValuesFromQueryString.js","names":[],"sources":["../../../../src/stores/form/getDefaultValuesFromQueryString.ts"],"sourcesContent":["import type { WidgetChains } from '../../types/widget.js'\nimport {\n isDestinationOnlyChain,\n isUnservedDestinationChain,\n} from '../../utils/chainType.js'\nimport { formatInputAmount } from '../../utils/format.js'\nimport type { DefaultValues } from './types.js'\n\ninterface GetDefaultValuesFromQueryStringOptions {\n includeToAddress?: boolean\n buildUrl?: boolean\n chains?: WidgetChains\n}\n\nexport const getDefaultValuesFromQueryString = ({\n buildUrl = false,\n includeToAddress = true,\n chains,\n}: GetDefaultValuesFromQueryStringOptions): Partial<DefaultValues> => {\n if (typeof window === 'undefined') {\n return {}\n }\n\n if (!buildUrl) {\n return {}\n }\n\n const searchParams = Object.fromEntries(\n new URLSearchParams(window.location.search)\n )\n\n // The loop below then drops their tokens too.\n if (isDestinationOnlyChain(Number.parseInt(searchParams.fromChain, 10))) {\n delete searchParams.fromChain\n }\n if (\n isUnservedDestinationChain(\n Number.parseInt(searchParams.toChain, 10),\n chains\n )\n ) {\n delete searchParams.toChain\n }\n // Prevent using fromToken/toToken params if chain is not selected.\n ;(['from', 'to'] as const).forEach((key) => {\n if (searchParams[`${key}Token`] && !searchParams[`${key}Chain`]) {\n delete searchParams[`${key}Token`]\n }\n })\n\n return {\n ...(Number.isFinite(Number.parseInt(searchParams.fromChain, 10))\n ? { fromChain: Number.parseInt(searchParams.fromChain, 10) }\n : {}),\n ...(Number.isFinite(Number.parseInt(searchParams.toChain, 10))\n ? { toChain: Number.parseInt(searchParams.toChain, 10) }\n : {}),\n ...(searchParams.fromToken ? { fromToken: searchParams.fromToken } : {}),\n ...(searchParams.toToken ? { toToken: searchParams.toToken } : {}),\n ...(Number.isFinite(Number.parseFloat(searchParams.fromAmount))\n ? { fromAmount: formatInputAmount(searchParams.fromAmount) }\n : {}),\n ...(Number.isFinite(Number.parseFloat(searchParams.toAmount))\n ? { toAmount: formatInputAmount(searchParams.toAmount) }\n : {}),\n // Limit-order params (written only while on the limit tab).\n ...(Number.isFinite(Number.parseInt(searchParams.validUntil, 10))\n ? { validUntil: Number.parseInt(searchParams.validUntil, 10) }\n : {}),\n ...(searchParams.partiallyFillable === 'true' ||\n searchParams.partiallyFillable === 'false'\n ? { partiallyFillable: searchParams.partiallyFillable === 'true' }\n : {}),\n ...(includeToAddress && searchParams.toAddress\n ? { toAddress: searchParams.toAddress }\n : {}),\n }\n}\n"],"mappings":";;;AAcA,MAAa,mCAAmC,EAC9C,WAAW,OACX,mBAAmB,MACnB,aACoE;CACpE,IAAI,OAAO,WAAW,aACpB,OAAO,CAAC;CAGV,IAAI,CAAC,UACH,OAAO,CAAC;CAGV,MAAM,eAAe,OAAO,YAC1B,IAAI,gBAAgB,OAAO,SAAS,MAAM,CAC5C;CAGA,IAAI,uBAAuB,OAAO,SAAS,aAAa,WAAW,EAAE,CAAC,GACpE,OAAO,aAAa;CAEtB,IACE,2BACE,OAAO,SAAS,aAAa,SAAS,EAAE,GACxC,MACF,GAEA,OAAO,aAAa;CAGrB,CAAE,QAAQ,IAAI,CAAC,CAAW,SAAS,QAAQ;EAC1C,IAAI,aAAa,GAAG,IAAI,WAAW,CAAC,aAAa,GAAG,IAAI,SACtD,OAAO,aAAa,GAAG,IAAI;CAE/B,CAAC;CAED,OAAO;EACL,GAAI,OAAO,SAAS,OAAO,SAAS,aAAa,WAAW,EAAE,CAAC,IAC3D,EAAE,WAAW,OAAO,SAAS,aAAa,WAAW,EAAE,EAAE,IACzD,CAAC;EACL,GAAI,OAAO,SAAS,OAAO,SAAS,aAAa,SAAS,EAAE,CAAC,IACzD,EAAE,SAAS,OAAO,SAAS,aAAa,SAAS,EAAE,EAAE,IACrD,CAAC;EACL,GAAI,aAAa,YAAY,EAAE,WAAW,aAAa,UAAU,IAAI,CAAC;EACtE,GAAI,aAAa,UAAU,EAAE,SAAS,aAAa,QAAQ,IAAI,CAAC;EAChE,GAAI,OAAO,SAAS,OAAO,WAAW,aAAa,UAAU,CAAC,IAC1D,EAAE,YAAY,kBAAkB,aAAa,UAAU,EAAE,IACzD,CAAC;EACL,GAAI,OAAO,SAAS,OAAO,WAAW,aAAa,QAAQ,CAAC,IACxD,EAAE,UAAU,kBAAkB,aAAa,QAAQ,EAAE,IACrD,CAAC;EAEL,GAAI,OAAO,SAAS,OAAO,SAAS,aAAa,YAAY,EAAE,CAAC,IAC5D,EAAE,YAAY,OAAO,SAAS,aAAa,YAAY,EAAE,EAAE,IAC3D,CAAC;EACL,GAAI,aAAa,sBAAsB,UACvC,aAAa,sBAAsB,UAC/B,EAAE,mBAAmB,aAAa,sBAAsB,OAAO,IAC/D,CAAC;EACL,GAAI,oBAAoB,aAAa,YACjC,EAAE,WAAW,aAAa,UAAU,IACpC,CAAC;CACP;AACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isItemAllowed } from "./item.js";
|
|
1
2
|
import { ChainId, ChainType } from "@lifi/sdk";
|
|
2
3
|
//#region src/utils/chainType.ts
|
|
3
4
|
const defaultChainIdsByType = {
|
|
@@ -8,7 +9,37 @@ const defaultChainIdsByType = {
|
|
|
8
9
|
[ChainType.TVM]: ChainId.TRN,
|
|
9
10
|
[ChainType.STL]: ChainId.XLM
|
|
10
11
|
};
|
|
12
|
+
const destinationOnlyChainIds = /* @__PURE__ */ new Set([ChainId.ZEC]);
|
|
13
|
+
const isDestinationOnlyChain = (chainId) => chainId !== void 0 && destinationOnlyChainIds.has(chainId);
|
|
14
|
+
const withDestinationOnlyChains = (chains) => {
|
|
15
|
+
const allow = chains?.from?.allow?.filter((chainId) => !isDestinationOnlyChain(chainId));
|
|
16
|
+
return {
|
|
17
|
+
...chains,
|
|
18
|
+
from: {
|
|
19
|
+
...chains?.from,
|
|
20
|
+
...allow && { allow },
|
|
21
|
+
deny: [...chains?.from?.deny ?? [], ...destinationOnlyChainIds]
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const withServedDestinationChains = (chains, providers) => {
|
|
26
|
+
const unserved = [...destinationOnlyChainIds].filter((chainId) => !providers.some((provider) => provider.chainIds?.some((id) => id === chainId)));
|
|
27
|
+
if (!unserved.length) return chains;
|
|
28
|
+
const allow = chains?.allow?.filter((chainId) => !unserved.includes(chainId));
|
|
29
|
+
return {
|
|
30
|
+
...chains,
|
|
31
|
+
...allow && { allow },
|
|
32
|
+
deny: [...chains?.deny ?? [], ...unserved]
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
const isUnservedDestinationChain = (chainId, chains) => isDestinationOnlyChain(chainId) && !isItemAllowed(chainId, chains);
|
|
36
|
+
const withoutUnservedToChain = (config) => {
|
|
37
|
+
if (!isUnservedDestinationChain(config.toChain, config.chains)) return config;
|
|
38
|
+
const { toChain: _toChain, toToken: _toToken, ...rest } = config;
|
|
39
|
+
return rest;
|
|
40
|
+
};
|
|
41
|
+
const isDestinationAllowedAsSource = (toChainId, chains) => !toChainId || isItemAllowed(toChainId, chains?.from);
|
|
11
42
|
//#endregion
|
|
12
|
-
export { defaultChainIdsByType };
|
|
43
|
+
export { defaultChainIdsByType, isDestinationAllowedAsSource, isDestinationOnlyChain, isUnservedDestinationChain, withDestinationOnlyChains, withServedDestinationChains, withoutUnservedToChain };
|
|
13
44
|
|
|
14
45
|
//# sourceMappingURL=chainType.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chainType.js","names":[],"sources":["../../../src/utils/chainType.ts"],"sourcesContent":["import { ChainId, ChainType } from '@lifi/sdk'\n\nexport const defaultChainIdsByType: Record<ChainType, ChainId> = {\n [ChainType.EVM]: ChainId.ETH,\n [ChainType.SVM]: ChainId.SOL,\n [ChainType.UTXO]: ChainId.BTC,\n [ChainType.MVM]: ChainId.SUI,\n [ChainType.TVM]: ChainId.TRN,\n [ChainType.STL]: ChainId.XLM,\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"chainType.js","names":[],"sources":["../../../src/utils/chainType.ts"],"sourcesContent":["import { ChainId, ChainType, type SDKProvider } from '@lifi/sdk'\nimport type { WidgetChains, WidgetConfig } from '../types/widget.js'\nimport { isItemAllowed } from './item.js'\n\nexport const defaultChainIdsByType: Record<ChainType, ChainId> = {\n [ChainType.EVM]: ChainId.ETH,\n [ChainType.SVM]: ChainId.SOL,\n [ChainType.UTXO]: ChainId.BTC,\n [ChainType.MVM]: ChainId.SUI,\n [ChainType.TVM]: ChainId.TRN,\n [ChainType.STL]: ChainId.XLM,\n}\n\n// No wallet can sign on these chains yet, so a route may only deliver to them.\nconst destinationOnlyChainIds: ReadonlySet<number> = new Set<number>([\n ChainId.ZEC,\n])\n\nexport const isDestinationOnlyChain = (chainId?: number): boolean =>\n chainId !== undefined && destinationOnlyChainIds.has(chainId)\n\nexport const withDestinationOnlyChains = (\n chains: WidgetChains | undefined\n): WidgetChains => {\n const allow = chains?.from?.allow?.filter(\n (chainId) => !isDestinationOnlyChain(chainId)\n )\n return {\n ...chains,\n from: {\n ...chains?.from,\n ...(allow && { allow }),\n deny: [...(chains?.from?.deny ?? []), ...destinationOnlyChainIds],\n },\n }\n}\n\n// A destination-only chain takes a receiver only through a provider that lists it.\nexport const withServedDestinationChains = (\n chains: WidgetChains | undefined,\n providers: readonly Pick<SDKProvider, 'chainIds'>[]\n): WidgetChains | undefined => {\n const unserved = [...destinationOnlyChainIds].filter(\n (chainId) =>\n !providers.some((provider) =>\n provider.chainIds?.some((id) => id === chainId)\n )\n )\n if (!unserved.length) {\n return chains\n }\n const allow = chains?.allow?.filter((chainId) => !unserved.includes(chainId))\n return {\n ...chains,\n ...(allow && { allow }),\n deny: [...(chains?.deny ?? []), ...unserved],\n }\n}\n\n// The form keeps a preset chain that the chain list does not offer.\nexport const isUnservedDestinationChain = (\n chainId: number | undefined,\n chains: WidgetChains | undefined\n): boolean => isDestinationOnlyChain(chainId) && !isItemAllowed(chainId, chains)\n\n// Omitted, not undefined: an own undefined key resets the form field.\nexport const withoutUnservedToChain = <\n T extends Pick<WidgetConfig, 'chains' | 'toChain' | 'toToken'>,\n>(\n config: T\n): T => {\n if (!isUnservedDestinationChain(config.toChain, config.chains)) {\n return config\n }\n const { toChain: _toChain, toToken: _toToken, ...rest } = config\n return rest as T\n}\n\nexport const isDestinationAllowedAsSource = (\n toChainId: number | undefined,\n chains: WidgetChains | undefined\n): boolean => !toChainId || isItemAllowed(toChainId, chains?.from)\n"],"mappings":";;;AAIA,MAAa,wBAAoD;EAC9D,UAAU,MAAM,QAAQ;EACxB,UAAU,MAAM,QAAQ;EACxB,UAAU,OAAO,QAAQ;EACzB,UAAU,MAAM,QAAQ;EACxB,UAAU,MAAM,QAAQ;EACxB,UAAU,MAAM,QAAQ;AAC3B;AAGA,MAAM,0CAA+C,IAAI,IAAY,CACnE,QAAQ,GACV,CAAC;AAED,MAAa,0BAA0B,YACrC,YAAY,KAAA,KAAa,wBAAwB,IAAI,OAAO;AAE9D,MAAa,6BACX,WACiB;CACjB,MAAM,QAAQ,QAAQ,MAAM,OAAO,QAChC,YAAY,CAAC,uBAAuB,OAAO,CAC9C;CACA,OAAO;EACL,GAAG;EACH,MAAM;GACJ,GAAG,QAAQ;GACX,GAAI,SAAS,EAAE,MAAM;GACrB,MAAM,CAAC,GAAI,QAAQ,MAAM,QAAQ,CAAC,GAAI,GAAG,uBAAuB;EAClE;CACF;AACF;AAGA,MAAa,+BACX,QACA,cAC6B;CAC7B,MAAM,WAAW,CAAC,GAAG,uBAAuB,CAAC,CAAC,QAC3C,YACC,CAAC,UAAU,MAAM,aACf,SAAS,UAAU,MAAM,OAAO,OAAO,OAAO,CAChD,CACJ;CACA,IAAI,CAAC,SAAS,QACZ,OAAO;CAET,MAAM,QAAQ,QAAQ,OAAO,QAAQ,YAAY,CAAC,SAAS,SAAS,OAAO,CAAC;CAC5E,OAAO;EACL,GAAG;EACH,GAAI,SAAS,EAAE,MAAM;EACrB,MAAM,CAAC,GAAI,QAAQ,QAAQ,CAAC,GAAI,GAAG,QAAQ;CAC7C;AACF;AAGA,MAAa,8BACX,SACA,WACY,uBAAuB,OAAO,KAAK,CAAC,cAAc,SAAS,MAAM;AAG/E,MAAa,0BAGX,WACM;CACN,IAAI,CAAC,2BAA2B,OAAO,SAAS,OAAO,MAAM,GAC3D,OAAO;CAET,MAAM,EAAE,SAAS,UAAU,SAAS,UAAU,GAAG,SAAS;CAC1D,OAAO;AACT;AAEA,MAAa,gCACX,WACA,WACY,CAAC,aAAa,cAAc,WAAW,QAAQ,IAAI"}
|
|
@@ -6,7 +6,12 @@ const isCustomReceiverBlocked = ({ fromChainType, toChainType, toAddress, signer
|
|
|
6
6
|
if (!toAddress) return Boolean(receiverRequired);
|
|
7
7
|
return toAddress.toLowerCase() !== signerAddress?.toLowerCase();
|
|
8
8
|
};
|
|
9
|
+
const isSignerInvalidReceiver = ({ signerAddress, toAddress, fromChain, toChain, isAddressForChain }) => Boolean(signerAddress && !toAddress && fromChain && toChain && fromChain.chainType === toChain.chainType && !isAddressForChain(signerAddress, toChain));
|
|
10
|
+
const canQuoteWithToAddress = (toAddress, toChain, isAddressForChain) => {
|
|
11
|
+
if (!toAddress) return true;
|
|
12
|
+
return toChain ? isAddressForChain(toAddress, toChain) : false;
|
|
13
|
+
};
|
|
9
14
|
//#endregion
|
|
10
|
-
export { isCustomReceiverBlocked, isCustomReceiverUnsupported };
|
|
15
|
+
export { canQuoteWithToAddress, isCustomReceiverBlocked, isCustomReceiverUnsupported, isSignerInvalidReceiver };
|
|
11
16
|
|
|
12
17
|
//# sourceMappingURL=customReceiver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"customReceiver.js","names":[],"sources":["../../../src/utils/customReceiver.ts"],"sourcesContent":["import { ChainType } from '@lifi/sdk'\n\n// Stellar routes always settle to the account that signs them.\nexport const isCustomReceiverUnsupported = (\n fromChainType?: ChainType,\n toChainType?: ChainType\n): boolean => fromChainType === ChainType.STL && toChainType === ChainType.STL\n\nexport interface CustomReceiverBlockedArgs {\n fromChainType?: ChainType\n toChainType?: ChainType\n toAddress?: string\n signerAddress?: string\n receiverRequired?: boolean\n}\n\nexport const isCustomReceiverBlocked = ({\n fromChainType,\n toChainType,\n toAddress,\n signerAddress,\n receiverRequired,\n}: CustomReceiverBlockedArgs): boolean => {\n if (!isCustomReceiverUnsupported(fromChainType, toChainType)) {\n return false\n }\n if (!toAddress) {\n return Boolean(receiverRequired)\n }\n return toAddress.toLowerCase() !== signerAddress?.toLowerCase()\n}\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"customReceiver.js","names":[],"sources":["../../../src/utils/customReceiver.ts"],"sourcesContent":["import { ChainType } from '@lifi/sdk'\nimport type { ChainRef, IsAddressForChain } from '@lifi/widget-provider'\n\n// Stellar routes always settle to the account that signs them.\nexport const isCustomReceiverUnsupported = (\n fromChainType?: ChainType,\n toChainType?: ChainType\n): boolean => fromChainType === ChainType.STL && toChainType === ChainType.STL\n\nexport interface CustomReceiverBlockedArgs {\n fromChainType?: ChainType\n toChainType?: ChainType\n toAddress?: string\n signerAddress?: string\n receiverRequired?: boolean\n}\n\nexport const isCustomReceiverBlocked = ({\n fromChainType,\n toChainType,\n toAddress,\n signerAddress,\n receiverRequired,\n}: CustomReceiverBlockedArgs): boolean => {\n if (!isCustomReceiverUnsupported(fromChainType, toChainType)) {\n return false\n }\n if (!toAddress) {\n return Boolean(receiverRequired)\n }\n return toAddress.toLowerCase() !== signerAddress?.toLowerCase()\n}\n\n// Without a receiver the API delivers to the signer when both chains share a\n// chain type, and a Bitcoin signer cannot receive on ZEC.\nexport const isSignerInvalidReceiver = ({\n signerAddress,\n toAddress,\n fromChain,\n toChain,\n isAddressForChain,\n}: {\n signerAddress: string | undefined\n toAddress: string | undefined\n fromChain: ChainRef | undefined\n toChain: ChainRef | undefined\n isAddressForChain: IsAddressForChain\n}): boolean =>\n Boolean(\n signerAddress &&\n !toAddress &&\n fromChain &&\n toChain &&\n fromChain.chainType === toChain.chainType &&\n !isAddressForChain(signerAddress, toChain)\n )\n\nexport const canQuoteWithToAddress = (\n toAddress: string | undefined,\n toChain: ChainRef | undefined,\n isAddressForChain: IsAddressForChain\n): boolean => {\n if (!toAddress) {\n return true\n }\n return toChain ? isAddressForChain(toAddress, toChain) : false\n}\n"],"mappings":";;AAIA,MAAa,+BACX,eACA,gBACY,kBAAkB,UAAU,OAAO,gBAAgB,UAAU;AAU3E,MAAa,2BAA2B,EACtC,eACA,aACA,WACA,eACA,uBACwC;CACxC,IAAI,CAAC,4BAA4B,eAAe,WAAW,GACzD,OAAO;CAET,IAAI,CAAC,WACH,OAAO,QAAQ,gBAAgB;CAEjC,OAAO,UAAU,YAAY,MAAM,eAAe,YAAY;AAChE;AAIA,MAAa,2BAA2B,EACtC,eACA,WACA,WACA,SACA,wBAQA,QACE,iBACE,CAAC,aACD,aACA,WACA,UAAU,cAAc,QAAQ,aAChC,CAAC,kBAAkB,eAAe,OAAO,CAC7C;AAEF,MAAa,yBACX,WACA,SACA,sBACY;CACZ,IAAI,CAAC,WACH,OAAO;CAET,OAAO,UAAU,kBAAkB,WAAW,OAAO,IAAI;AAC3D"}
|
package/dist/esm/utils/token.js
CHANGED
|
@@ -124,6 +124,18 @@ const getTokenVerificationProvider = (token) => {
|
|
|
124
124
|
const provider = token.verificationStatusBreakdown?.find((entry) => entry.result === "verified")?.provider;
|
|
125
125
|
return provider ? `${provider.charAt(0).toUpperCase()}${provider.slice(1)}` : void 0;
|
|
126
126
|
};
|
|
127
|
+
const getTokenVerificationBadge = (token) => {
|
|
128
|
+
if (token.native) return "native";
|
|
129
|
+
if (token.verificationStatus === "flagged") return "flagged";
|
|
130
|
+
if (token.verificationStatus === "verified") return "verified";
|
|
131
|
+
if (token.listed || token.verified) return;
|
|
132
|
+
return token.verificationStatus === "unverified" ? "unverifiedByProvider" : "unverified";
|
|
133
|
+
};
|
|
134
|
+
const getTokenStatusTextKey = (token) => {
|
|
135
|
+
if (token.verificationStatus === "verified") return "tokenMetric.statusVerified";
|
|
136
|
+
if (token.verificationStatus === "flagged" && !token.native) return "tokenMetric.statusFlagged";
|
|
137
|
+
return "tokenMetric.statusUnverified";
|
|
138
|
+
};
|
|
127
139
|
/**
|
|
128
140
|
* Hyperliquid declares Arbitrum USDC as its native token, Lighter declares
|
|
129
141
|
* Ethereum USDC. Neither is a gas token you hold there. A chain-local
|
|
@@ -132,6 +144,6 @@ const getTokenVerificationProvider = (token) => {
|
|
|
132
144
|
const chainIdsWithoutNativeToken = /* @__PURE__ */ new Set([ChainId.HPL, ChainId.LTR]);
|
|
133
145
|
const getNativeTokenAddresses = (chains) => new Map(chains?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id)).map((chain) => [chain.id, chain.nativeToken.address]));
|
|
134
146
|
//#endregion
|
|
135
|
-
export { filterAllowedTokens, getChainTokenAllowSets, getExecutionToToken, getNativeTokenAddresses, getTokenVerificationProvider, getVerifiedTokensSets, knownPriceUSD, mergeFallbackToken, mergeVerifiedWithSearchTokens, updateTokenInCache };
|
|
147
|
+
export { filterAllowedTokens, getChainTokenAllowSets, getExecutionToToken, getNativeTokenAddresses, getTokenStatusTextKey, getTokenVerificationBadge, getTokenVerificationProvider, getVerifiedTokensSets, knownPriceUSD, mergeFallbackToken, mergeVerifiedWithSearchTokens, updateTokenInCache };
|
|
136
148
|
|
|
137
149
|
//# sourceMappingURL=token.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token.js","names":[],"sources":["../../../src/utils/token.ts"],"sourcesContent":["import type {\n BaseToken,\n ExtendedChain,\n RouteExtended,\n StaticToken,\n Token,\n TokenAmount,\n TokenExtended,\n} from '@lifi/sdk'\nimport { ChainId } from '@lifi/sdk'\nimport type { FormType } from '../stores/form/types.js'\nimport type { TokensByChain, TokenWithFlags } from '../types/token.js'\nimport type {\n AllowDenySets,\n WidgetChains,\n WidgetTokens,\n} from '../types/widget.js'\nimport { getConfigItemSets, isFormItemAllowed } from './item.js'\n\n// Number(): plain-JS integrators may pass chainId as a string.\nexport const getChainTokenAllowSets = (\n configTokens: WidgetTokens | undefined,\n chainId: number,\n formType?: FormType\n): AllowDenySets | undefined =>\n getConfigItemSets(\n configTokens,\n (tokens: BaseToken[]) =>\n new Set(\n tokens\n .filter((t) => Number(t.chainId) === chainId)\n .map((t) => t.address.toLowerCase())\n ),\n formType\n )\n\n/**\n * Builds per-chain sets of lowercase token addresses from the\n * `tokens.verified` config allowlist.\n */\nexport const getVerifiedTokensSets = (\n configTokens?: WidgetTokens\n): Map<number, Set<string>> | undefined => {\n if (!configTokens?.verified?.length) {\n return undefined\n }\n const sets = new Map<number, Set<string>>()\n for (const token of configTokens.verified) {\n const chainId = Number(token.chainId)\n let addresses = sets.get(chainId)\n if (!addresses) {\n addresses = new Set()\n sets.set(chainId, addresses)\n }\n addresses.add(token.address.toLowerCase())\n }\n return sets\n}\n\n/**\n * Merges verified tokens with search tokens.\n * Verified tokens take priority - search tokens are only added if they don't already exist.\n */\nexport const mergeVerifiedWithSearchTokens = (\n verifiedTokens?: TokensByChain,\n searchTokens?: TokensByChain\n): TokensByChain | undefined => {\n if (!verifiedTokens) {\n return searchTokens\n }\n if (!searchTokens) {\n return verifiedTokens\n }\n\n const result = { ...verifiedTokens }\n\n for (const [chainId, tokens] of Object.entries(searchTokens)) {\n const chainIdNum = Number(chainId)\n const existingTokens = result[chainIdNum] || []\n const existingAddresses = new Set(\n existingTokens.map((t) => t.address.toLowerCase())\n )\n\n const newTokens = tokens.filter(\n (t) => !existingAddresses.has(t.address.toLowerCase())\n )\n\n if (newTokens.length) {\n result[chainIdNum] = [...existingTokens, ...newTokens]\n }\n }\n\n return result\n}\n\n/**\n * `priceUSD` is a required string, so an unknown price arrives as `'0'` or `''`\n * rather than as an absent key — the same reading as in `useLimitMarketRate`. A\n * known price must never lose to one, or a route that cannot price its output\n * blanks a value the cache already knew.\n */\nexport const knownPriceUSD = (fresher: string, cached: string): string =>\n Number(fresher) ? fresher : cached\n\n/**\n * Merges a cached token into a route token for display. The route wins on every\n * field it carries; the cache only fills gaps. Letting the cache win instead\n * swaps the quoted price for a stale feed price.\n */\nexport const mergeFallbackToken = (\n routeToken: TokenAmount,\n cachedToken: TokenExtended | undefined\n): TokenAmount =>\n cachedToken\n ? {\n ...cachedToken,\n ...routeToken,\n priceUSD: knownPriceUSD(routeToken.priceUSD, cachedToken.priceUSD),\n logoURI: routeToken.logoURI || cachedToken.logoURI,\n }\n : routeToken\n\n/**\n * Updates a token in the cache by chainId and address.\n * Returns a new cache object with the token updated, or the original if not found.\n * Addresses are compared case-insensitively: the token list and the route can\n * spell the same address differently. The incoming token only overwrites what it\n * actually knows, so an unpriced or logo-less route cannot blank the cache.\n */\nexport const updateTokenInCache = (\n data: TokensByChain | undefined,\n token: Token\n): TokensByChain | undefined => {\n if (!data) {\n return data\n }\n const chainTokens = data[token.chainId]\n if (!chainTokens) {\n return data\n }\n const address = token.address.toLowerCase()\n const index = chainTokens.findIndex(\n (t) => t.address.toLowerCase() === address\n )\n if (index < 0) {\n return data\n }\n return {\n ...data,\n [token.chainId]: chainTokens.map((t, i) =>\n i === index\n ? {\n ...t,\n ...token,\n // Keep the cached address: other readers hold it as the identity.\n address: t.address,\n priceUSD: knownPriceUSD(token.priceUSD, t.priceUSD),\n logoURI: token.logoURI || t.logoURI,\n }\n : t\n ),\n }\n}\n\nexport const filterAllowedTokens = (\n dataTokens: TokensByChain | undefined,\n configTokens?: WidgetTokens,\n chainsConfig?: WidgetChains,\n formType?: FormType,\n nativeTokenAddresses?: Map<number, string>\n): TokensByChain | undefined => {\n if (!dataTokens) {\n return\n }\n\n const includedTokens = configTokens?.include || []\n const allChainIds = Array.from(\n new Set([\n ...includedTokens.map((t) => t.chainId),\n ...Object.keys(dataTokens),\n ])\n ).map((chainId) => Number(chainId))\n\n const configChainIdsSet = getConfigItemSets(\n chainsConfig,\n (chainIds: number[]) => new Set(chainIds),\n formType\n )\n\n const allowedChainIds = configChainIdsSet\n ? allChainIds.filter((chainId) =>\n isFormItemAllowed(chainId, configChainIdsSet, formType)\n )\n : allChainIds\n\n const verifiedTokensSets = getVerifiedTokensSets(configTokens)\n\n const allowedTokensByChain: TokensByChain = {}\n for (const chainId of allowedChainIds) {\n const chainIncludedTokens = includedTokens.filter(\n (t) => Number(t.chainId) === chainId\n )\n const includedAddresses = new Set(\n chainIncludedTokens.map((t) => t.address.toLowerCase())\n )\n\n const allowedAddresses = getChainTokenAllowSets(\n configTokens,\n chainId,\n formType\n )\n\n const verifiedAddresses = verifiedTokensSets?.get(chainId)\n const nativeAddress = nativeTokenAddresses?.get(chainId)?.toLowerCase()\n\n const chainTokens: TokenWithFlags[] = [\n ...(dataTokens[chainId] ?? []),\n ...chainIncludedTokens,\n ]\n\n const filtered: TokenWithFlags[] = []\n const seenAddresses = new Set<string>()\n for (const token of chainTokens) {\n const address = token.address.toLowerCase()\n // Data tokens come first and win over include duplicates,\n // keeping their extended data\n if (seenAddresses.has(address)) {\n continue\n }\n seenAddresses.add(address)\n if (\n !isFormItemAllowed(token, allowedAddresses, formType, (t) =>\n t.address.toLowerCase()\n )\n ) {\n continue\n }\n // Include and verified-allowlist tokens are explicitly set by the\n // integrator, mark them as verified. Flags are only ever set to true,\n // so a token that earns none keeps its cached object.\n const markVerified =\n !token.verified &&\n (includedAddresses.has(address) || verifiedAddresses?.has(address))\n const markNative = !!nativeAddress && nativeAddress === address\n if (markVerified || markNative) {\n filtered.push({\n ...token,\n ...(markVerified ? { verified: true } : {}),\n ...(markNative ? { native: true } : {}),\n })\n } else {\n filtered.push(token)\n }\n }\n\n allowedTokensByChain[chainId] = filtered\n }\n\n return allowedTokensByChain\n}\n\nexport const getExecutionToToken = (route: RouteExtended): TokenAmount => ({\n ...(route.steps.at(-1)?.execution?.toToken ?? route.toToken),\n amount: BigInt(\n route.steps.at(-1)?.execution?.toAmount ??\n route.steps.at(-1)?.estimate.toAmount ??\n route.toAmount\n ),\n})\n\n/** Capitalized name of the first provider that verified the token. */\nexport const getTokenVerificationProvider = (\n token: Pick<StaticToken, 'verificationStatusBreakdown'>\n): string | undefined => {\n const provider = token.verificationStatusBreakdown?.find(\n (entry) => entry.result === 'verified'\n )?.provider\n return provider\n ? `${provider.charAt(0).toUpperCase()}${provider.slice(1)}`\n : undefined\n}\n\n/**\n * Hyperliquid declares Arbitrum USDC as its native token, Lighter declares\n * Ethereum USDC. Neither is a gas token you hold there. A chain-local\n * stablecoin gas token, as on Arc or Stable, is genuine and stays.\n */\nconst chainIdsWithoutNativeToken = new Set([ChainId.HPL, ChainId.LTR])\n\nexport const getNativeTokenAddresses = (\n chains?: ExtendedChain[]\n): Map<number, string> =>\n new Map(\n chains\n ?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id))\n .map((chain) => [chain.id, chain.nativeToken.address])\n )\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"token.js","names":[],"sources":["../../../src/utils/token.ts"],"sourcesContent":["import type {\n BaseToken,\n ExtendedChain,\n RouteExtended,\n StaticToken,\n Token,\n TokenAmount,\n TokenExtended,\n} from '@lifi/sdk'\nimport { ChainId } from '@lifi/sdk'\nimport type { FormType } from '../stores/form/types.js'\nimport type { TokensByChain, TokenWithFlags } from '../types/token.js'\nimport type {\n AllowDenySets,\n WidgetChains,\n WidgetTokens,\n} from '../types/widget.js'\nimport { getConfigItemSets, isFormItemAllowed } from './item.js'\n\n// Number(): plain-JS integrators may pass chainId as a string.\nexport const getChainTokenAllowSets = (\n configTokens: WidgetTokens | undefined,\n chainId: number,\n formType?: FormType\n): AllowDenySets | undefined =>\n getConfigItemSets(\n configTokens,\n (tokens: BaseToken[]) =>\n new Set(\n tokens\n .filter((t) => Number(t.chainId) === chainId)\n .map((t) => t.address.toLowerCase())\n ),\n formType\n )\n\n/**\n * Builds per-chain sets of lowercase token addresses from the\n * `tokens.verified` config allowlist.\n */\nexport const getVerifiedTokensSets = (\n configTokens?: WidgetTokens\n): Map<number, Set<string>> | undefined => {\n if (!configTokens?.verified?.length) {\n return undefined\n }\n const sets = new Map<number, Set<string>>()\n for (const token of configTokens.verified) {\n const chainId = Number(token.chainId)\n let addresses = sets.get(chainId)\n if (!addresses) {\n addresses = new Set()\n sets.set(chainId, addresses)\n }\n addresses.add(token.address.toLowerCase())\n }\n return sets\n}\n\n/**\n * Merges verified tokens with search tokens.\n * Verified tokens take priority - search tokens are only added if they don't already exist.\n */\nexport const mergeVerifiedWithSearchTokens = (\n verifiedTokens?: TokensByChain,\n searchTokens?: TokensByChain\n): TokensByChain | undefined => {\n if (!verifiedTokens) {\n return searchTokens\n }\n if (!searchTokens) {\n return verifiedTokens\n }\n\n const result = { ...verifiedTokens }\n\n for (const [chainId, tokens] of Object.entries(searchTokens)) {\n const chainIdNum = Number(chainId)\n const existingTokens = result[chainIdNum] || []\n const existingAddresses = new Set(\n existingTokens.map((t) => t.address.toLowerCase())\n )\n\n const newTokens = tokens.filter(\n (t) => !existingAddresses.has(t.address.toLowerCase())\n )\n\n if (newTokens.length) {\n result[chainIdNum] = [...existingTokens, ...newTokens]\n }\n }\n\n return result\n}\n\n/**\n * `priceUSD` is a required string, so an unknown price arrives as `'0'` or `''`\n * rather than as an absent key — the same reading as in `useLimitMarketRate`. A\n * known price must never lose to one, or a route that cannot price its output\n * blanks a value the cache already knew.\n */\nexport const knownPriceUSD = (fresher: string, cached: string): string =>\n Number(fresher) ? fresher : cached\n\n/**\n * Merges a cached token into a route token for display. The route wins on every\n * field it carries; the cache only fills gaps. Letting the cache win instead\n * swaps the quoted price for a stale feed price.\n */\nexport const mergeFallbackToken = (\n routeToken: TokenAmount,\n cachedToken: TokenExtended | undefined\n): TokenAmount =>\n cachedToken\n ? {\n ...cachedToken,\n ...routeToken,\n priceUSD: knownPriceUSD(routeToken.priceUSD, cachedToken.priceUSD),\n logoURI: routeToken.logoURI || cachedToken.logoURI,\n }\n : routeToken\n\n/**\n * Updates a token in the cache by chainId and address.\n * Returns a new cache object with the token updated, or the original if not found.\n * Addresses are compared case-insensitively: the token list and the route can\n * spell the same address differently. The incoming token only overwrites what it\n * actually knows, so an unpriced or logo-less route cannot blank the cache.\n */\nexport const updateTokenInCache = (\n data: TokensByChain | undefined,\n token: Token\n): TokensByChain | undefined => {\n if (!data) {\n return data\n }\n const chainTokens = data[token.chainId]\n if (!chainTokens) {\n return data\n }\n const address = token.address.toLowerCase()\n const index = chainTokens.findIndex(\n (t) => t.address.toLowerCase() === address\n )\n if (index < 0) {\n return data\n }\n return {\n ...data,\n [token.chainId]: chainTokens.map((t, i) =>\n i === index\n ? {\n ...t,\n ...token,\n // Keep the cached address: other readers hold it as the identity.\n address: t.address,\n priceUSD: knownPriceUSD(token.priceUSD, t.priceUSD),\n logoURI: token.logoURI || t.logoURI,\n }\n : t\n ),\n }\n}\n\nexport const filterAllowedTokens = (\n dataTokens: TokensByChain | undefined,\n configTokens?: WidgetTokens,\n chainsConfig?: WidgetChains,\n formType?: FormType,\n nativeTokenAddresses?: Map<number, string>\n): TokensByChain | undefined => {\n if (!dataTokens) {\n return\n }\n\n const includedTokens = configTokens?.include || []\n const allChainIds = Array.from(\n new Set([\n ...includedTokens.map((t) => t.chainId),\n ...Object.keys(dataTokens),\n ])\n ).map((chainId) => Number(chainId))\n\n const configChainIdsSet = getConfigItemSets(\n chainsConfig,\n (chainIds: number[]) => new Set(chainIds),\n formType\n )\n\n const allowedChainIds = configChainIdsSet\n ? allChainIds.filter((chainId) =>\n isFormItemAllowed(chainId, configChainIdsSet, formType)\n )\n : allChainIds\n\n const verifiedTokensSets = getVerifiedTokensSets(configTokens)\n\n const allowedTokensByChain: TokensByChain = {}\n for (const chainId of allowedChainIds) {\n const chainIncludedTokens = includedTokens.filter(\n (t) => Number(t.chainId) === chainId\n )\n const includedAddresses = new Set(\n chainIncludedTokens.map((t) => t.address.toLowerCase())\n )\n\n const allowedAddresses = getChainTokenAllowSets(\n configTokens,\n chainId,\n formType\n )\n\n const verifiedAddresses = verifiedTokensSets?.get(chainId)\n const nativeAddress = nativeTokenAddresses?.get(chainId)?.toLowerCase()\n\n const chainTokens: TokenWithFlags[] = [\n ...(dataTokens[chainId] ?? []),\n ...chainIncludedTokens,\n ]\n\n const filtered: TokenWithFlags[] = []\n const seenAddresses = new Set<string>()\n for (const token of chainTokens) {\n const address = token.address.toLowerCase()\n // Data tokens come first and win over include duplicates,\n // keeping their extended data\n if (seenAddresses.has(address)) {\n continue\n }\n seenAddresses.add(address)\n if (\n !isFormItemAllowed(token, allowedAddresses, formType, (t) =>\n t.address.toLowerCase()\n )\n ) {\n continue\n }\n // Include and verified-allowlist tokens are explicitly set by the\n // integrator, mark them as verified. Flags are only ever set to true,\n // so a token that earns none keeps its cached object.\n const markVerified =\n !token.verified &&\n (includedAddresses.has(address) || verifiedAddresses?.has(address))\n const markNative = !!nativeAddress && nativeAddress === address\n if (markVerified || markNative) {\n filtered.push({\n ...token,\n ...(markVerified ? { verified: true } : {}),\n ...(markNative ? { native: true } : {}),\n })\n } else {\n filtered.push(token)\n }\n }\n\n allowedTokensByChain[chainId] = filtered\n }\n\n return allowedTokensByChain\n}\n\nexport const getExecutionToToken = (route: RouteExtended): TokenAmount => ({\n ...(route.steps.at(-1)?.execution?.toToken ?? route.toToken),\n amount: BigInt(\n route.steps.at(-1)?.execution?.toAmount ??\n route.steps.at(-1)?.estimate.toAmount ??\n route.toAmount\n ),\n})\n\n/** Capitalized name of the first provider that verified the token. */\nexport const getTokenVerificationProvider = (\n token: Pick<StaticToken, 'verificationStatusBreakdown'>\n): string | undefined => {\n const provider = token.verificationStatusBreakdown?.find(\n (entry) => entry.result === 'verified'\n )?.provider\n return provider\n ? `${provider.charAt(0).toUpperCase()}${provider.slice(1)}`\n : undefined\n}\n\nexport type TokenVerificationBadge =\n | 'native'\n | 'flagged'\n | 'verified'\n | 'unverifiedByProvider'\n | 'unverified'\n\nexport const getTokenVerificationBadge = (\n token: Pick<\n TokenWithFlags,\n 'native' | 'listed' | 'verified' | 'verificationStatus'\n >\n): TokenVerificationBadge | undefined => {\n // The provider calls the native-address convention a scam on some chains.\n if (token.native) {\n return 'native'\n }\n if (token.verificationStatus === 'flagged') {\n return 'flagged'\n }\n if (token.verificationStatus === 'verified') {\n return 'verified'\n }\n if (token.listed || token.verified) {\n return undefined\n }\n return token.verificationStatus === 'unverified'\n ? 'unverifiedByProvider'\n : 'unverified'\n}\n\nexport type TokenStatusTextKey =\n | 'tokenMetric.statusVerified'\n | 'tokenMetric.statusFlagged'\n | 'tokenMetric.statusUnverified'\n\nexport const getTokenStatusTextKey = (\n token: Pick<TokenWithFlags, 'native' | 'verificationStatus'>\n): TokenStatusTextKey => {\n if (token.verificationStatus === 'verified') {\n return 'tokenMetric.statusVerified'\n }\n if (token.verificationStatus === 'flagged' && !token.native) {\n return 'tokenMetric.statusFlagged'\n }\n return 'tokenMetric.statusUnverified'\n}\n\n/**\n * Hyperliquid declares Arbitrum USDC as its native token, Lighter declares\n * Ethereum USDC. Neither is a gas token you hold there. A chain-local\n * stablecoin gas token, as on Arc or Stable, is genuine and stays.\n */\nconst chainIdsWithoutNativeToken = new Set([ChainId.HPL, ChainId.LTR])\n\nexport const getNativeTokenAddresses = (\n chains?: ExtendedChain[]\n): Map<number, string> =>\n new Map(\n chains\n ?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id))\n .map((chain) => [chain.id, chain.nativeToken.address])\n )\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifi/widget",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
4
4
|
"description": "LI.FI Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/esm/index.js",
|
|
@@ -57,19 +57,19 @@
|
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@emotion/react": "^11.14.0",
|
|
59
59
|
"@emotion/styled": "^11.14.1",
|
|
60
|
-
"@lifi/sdk": "^4.
|
|
61
|
-
"@lifi/wallet-management": "4.3.
|
|
62
|
-
"@lifi/widget-provider": "4.
|
|
60
|
+
"@lifi/sdk": "^4.11.0",
|
|
61
|
+
"@lifi/wallet-management": "4.3.2",
|
|
62
|
+
"@lifi/widget-provider": "4.6.0",
|
|
63
63
|
"@mui/icons-material": "^9.4.0",
|
|
64
64
|
"@mui/material": "^9.4.0",
|
|
65
65
|
"@mui/system": "^9.4.0",
|
|
66
|
-
"@tanstack/react-router": "^1.170.
|
|
66
|
+
"@tanstack/react-router": "^1.170.41",
|
|
67
67
|
"@tanstack/react-virtual": "^3.14.13",
|
|
68
68
|
"eventemitter3": "^5.0.4",
|
|
69
69
|
"i18next": "^26.4.2",
|
|
70
70
|
"microdiff": "^1.6.0",
|
|
71
|
-
"motion": "^
|
|
72
|
-
"react-i18next": "^17.0.
|
|
71
|
+
"motion": "^14.0.0",
|
|
72
|
+
"react-i18next": "^17.0.16",
|
|
73
73
|
"react-intersection-observer": "^11.0.1",
|
|
74
74
|
"react-transition-group": "^4.4.5",
|
|
75
75
|
"zustand": "^5.0.15"
|
|
@@ -2,6 +2,8 @@ import type { BoxProps } from '@mui/material'
|
|
|
2
2
|
import type React from 'react'
|
|
3
3
|
import type { JSX } from 'react'
|
|
4
4
|
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
|
|
5
|
+
import { useFieldValues } from '../../stores/form/useFieldValues.js'
|
|
6
|
+
import { isDestinationAllowedAsSource } from '../../utils/chainType.js'
|
|
5
7
|
import { LimitPriceCard } from '../LimitPriceCard/LimitPriceCard.js'
|
|
6
8
|
import { SwapButton } from '../SwapButton/SwapButton.js'
|
|
7
9
|
import { CardContainer } from './AmountInputCard.style.js'
|
|
@@ -11,8 +13,10 @@ import { SendAmountCard } from './SendAmountCard.js'
|
|
|
11
13
|
export const AmountInputCardPair: React.FC<BoxProps> = (props): JSX.Element => {
|
|
12
14
|
// Refuel's destination is fixed to native gas, so reversing from/to is
|
|
13
15
|
// meaningless — hide the swap button (and the notch mask it sits in).
|
|
14
|
-
const { mode } = useWidgetConfig()
|
|
15
|
-
const
|
|
16
|
+
const { mode, chains } = useWidgetConfig()
|
|
17
|
+
const [toChainId] = useFieldValues('toChain')
|
|
18
|
+
const showSwapButton =
|
|
19
|
+
mode !== 'refuel' && isDestinationAllowedAsSource(toChainId, chains)
|
|
16
20
|
|
|
17
21
|
return (
|
|
18
22
|
<CardContainer {...props}>
|
|
@@ -3,13 +3,17 @@ import { Box } from '@mui/material'
|
|
|
3
3
|
import { ReverseTokensButton } from '../components/ReverseTokensButton/ReverseTokensButton.js'
|
|
4
4
|
import { SelectTokenButton } from '../components/SelectTokenButton/SelectTokenButton.js'
|
|
5
5
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
6
|
+
import { useFieldValues } from '../stores/form/useFieldValues.js'
|
|
7
|
+
import { isDestinationAllowedAsSource } from '../utils/chainType.js'
|
|
6
8
|
import { ReverseTokensButtonEmpty } from './ReverseTokensButton/ReverseTokensButton.style.js'
|
|
7
9
|
|
|
8
10
|
export const SelectChainAndToken: React.FC<BoxProps> = (props) => {
|
|
9
|
-
const { disabledUI, hiddenUI, mode } = useWidgetConfig()
|
|
11
|
+
const { disabledUI, hiddenUI, mode, chains } = useWidgetConfig()
|
|
12
|
+
const [toChainId] = useFieldValues('toChain')
|
|
10
13
|
|
|
11
14
|
const hiddenReverse =
|
|
12
15
|
mode === 'refuel' ||
|
|
16
|
+
!isDestinationAllowedAsSource(toChainId, chains) ||
|
|
13
17
|
disabledUI?.fromToken ||
|
|
14
18
|
disabledUI?.toToken ||
|
|
15
19
|
hiddenUI?.fromToken ||
|
|
@@ -50,7 +50,7 @@ export const SendToWalletButton: React.FC<
|
|
|
50
50
|
const { selectedBookmark } = useBookmarks()
|
|
51
51
|
const { setSelectedBookmark } = useBookmarkActions()
|
|
52
52
|
const { accounts } = useAccount()
|
|
53
|
-
const {
|
|
53
|
+
const { getChainFromAddress } = useChainTypeFromAddress()
|
|
54
54
|
const { requiredToAddress } = useToAddressRequirements()
|
|
55
55
|
const disabledToAddress = disabledUI?.toAddress
|
|
56
56
|
const hiddenToAddress = hiddenUI?.toAddress
|
|
@@ -65,11 +65,16 @@ export const SendToWalletButton: React.FC<
|
|
|
65
65
|
(account) => account.address === toAddressFieldValue
|
|
66
66
|
)
|
|
67
67
|
|
|
68
|
+
// A bookmark from the config or the URL may carry no chainId.
|
|
69
|
+
const detectedChain =
|
|
70
|
+
!matchingConnectedAccount &&
|
|
71
|
+
selectedBookmark?.chainId === undefined &&
|
|
72
|
+
toAddressFieldValue
|
|
73
|
+
? getChainFromAddress(toAddressFieldValue)
|
|
74
|
+
: undefined
|
|
75
|
+
|
|
68
76
|
const chainType = !matchingConnectedAccount
|
|
69
|
-
? selectedBookmark?.chainType ||
|
|
70
|
-
(toAddressFieldValue
|
|
71
|
-
? getChainTypeFromAddress(toAddressFieldValue)
|
|
72
|
-
: undefined)
|
|
77
|
+
? selectedBookmark?.chainType || detectedChain?.chainType
|
|
73
78
|
: undefined
|
|
74
79
|
|
|
75
80
|
const chainId =
|
|
@@ -77,9 +82,9 @@ export const SendToWalletButton: React.FC<
|
|
|
77
82
|
? toChainId
|
|
78
83
|
: matchingConnectedAccount
|
|
79
84
|
? matchingConnectedAccount.chainId
|
|
80
|
-
:
|
|
81
|
-
|
|
82
|
-
: undefined
|
|
85
|
+
: (selectedBookmark?.chainId ??
|
|
86
|
+
detectedChain?.chainId ??
|
|
87
|
+
(chainType ? defaultChainIdsByType[chainType] : undefined))
|
|
83
88
|
|
|
84
89
|
const isConnectedAccount =
|
|
85
90
|
selectedBookmark?.isConnectedAccount &&
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { JSX, Ref } from 'react'
|
|
2
2
|
import { useImperativeHandle, useRef, useState } from 'react'
|
|
3
|
+
import type { TokenAmount } from '../../types/token.js'
|
|
3
4
|
import { BottomSheet } from '../BottomSheet/BottomSheet.js'
|
|
4
5
|
import type { BottomSheetBase } from '../BottomSheet/types.js'
|
|
5
6
|
import { TokenDetailsSheetContent } from './TokenDetailsSheetContent.js'
|
|
@@ -11,20 +12,16 @@ export const TokenDetailsSheet = ({
|
|
|
11
12
|
ref?: Ref<TokenDetailsSheetBase>
|
|
12
13
|
}): JSX.Element => {
|
|
13
14
|
const bottomSheetRef = useRef<BottomSheetBase>(null)
|
|
14
|
-
const [
|
|
15
|
-
undefined
|
|
16
|
-
)
|
|
17
|
-
const [chainId, setChainId] = useState<number | undefined>(undefined)
|
|
15
|
+
const [listToken, setListToken] = useState<TokenAmount | undefined>(undefined)
|
|
18
16
|
const [withoutContractAddress, setWithoutContractAddress] = useState(false)
|
|
19
17
|
|
|
20
18
|
useImperativeHandle(
|
|
21
19
|
ref,
|
|
22
20
|
() => ({
|
|
23
21
|
isOpen: () => bottomSheetRef.current?.isOpen(),
|
|
24
|
-
open: (
|
|
25
|
-
|
|
22
|
+
open: (token: TokenAmount, noContractAddress: boolean) => {
|
|
23
|
+
setListToken(token)
|
|
26
24
|
setWithoutContractAddress(noContractAddress)
|
|
27
|
-
setChainId(chainId)
|
|
28
25
|
bottomSheetRef.current?.open()
|
|
29
26
|
},
|
|
30
27
|
close: () => {
|
|
@@ -38,9 +35,8 @@ export const TokenDetailsSheet = ({
|
|
|
38
35
|
<BottomSheet ref={bottomSheetRef} keepMounted>
|
|
39
36
|
<TokenDetailsSheetContent
|
|
40
37
|
onClose={() => bottomSheetRef.current?.close()}
|
|
41
|
-
|
|
38
|
+
listToken={listToken}
|
|
42
39
|
withoutContractAddress={withoutContractAddress}
|
|
43
|
-
chainId={chainId}
|
|
44
40
|
/>
|
|
45
41
|
</BottomSheet>
|
|
46
42
|
)
|