@lifi/widget 4.2.0 → 4.3.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.
@@ -1,6 +1,6 @@
1
1
  //#region src/config/version.d.ts
2
2
  declare const name = "@lifi/widget";
3
- declare const version = "4.2.0";
3
+ declare const version = "4.3.0";
4
4
  //#endregion
5
5
  export { name, version };
6
6
  //# sourceMappingURL=version.d.ts.map
@@ -1,6 +1,6 @@
1
1
  //#region src/config/version.ts
2
2
  const name = "@lifi/widget";
3
- const version = "4.2.0";
3
+ const version = "4.3.0";
4
4
  //#endregion
5
5
  export { name, version };
6
6
 
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","names":[],"sources":["../../../src/config/version.ts"],"sourcesContent":["export const name = '@lifi/widget'\nexport const version = '4.2.0'\n"],"mappings":";AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
1
+ {"version":3,"file":"version.js","names":[],"sources":["../../../src/config/version.ts"],"sourcesContent":["export const name = '@lifi/widget'\nexport const version = '4.3.0'\n"],"mappings":";AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
@@ -1,6 +1,7 @@
1
1
  import { getConfigItemSets, isFormItemAllowed } from "../utils/item.js";
2
2
  import { useWidgetConfig } from "../providers/WidgetProvider/WidgetProvider.js";
3
3
  import { useSDKClient } from "../providers/SDKClientProvider.js";
4
+ import { getVerifiedTokensSets } from "../utils/token.js";
4
5
  import { isSupportedToken } from "../utils/tokenList.js";
5
6
  import { ChainType, getWalletBalances } from "@lifi/sdk";
6
7
  import { useMemo } from "react";
@@ -28,10 +29,12 @@ const useFilteredTokensByBalance = (accountsWithTokens, formType) => {
28
29
  for (const [address, { tokens }] of Object.entries(accountsWithTokens)) result[address] = tokens;
29
30
  return result;
30
31
  }
32
+ const verifiedTokensSets = getVerifiedTokensSets(configTokens);
31
33
  for (const [address, { tokens }] of Object.entries(accountsWithTokens)) {
32
34
  result[address] = {};
33
35
  for (const [chainIdStr, chainTokens] of Object.entries(tokens)) {
34
36
  const chainId = Number(chainIdStr);
37
+ const verifiedAddresses = verifiedTokensSets?.get(chainId);
35
38
  const balances = existingBalances?.[chainId];
36
39
  if (!balances?.length) {
37
40
  if (chainTokens.length) result[address][chainId] = chainTokens;
@@ -49,7 +52,7 @@ const useFilteredTokensByBalance = (accountsWithTokens, formType) => {
49
52
  return !chainTokenSet.has(balanceKey) && isSupportedToken(balance) && isFormItemAllowed(balance, allowedAddressesConfig, formType, (t) => t.address.toLowerCase());
50
53
  }).map((token) => ({
51
54
  ...token,
52
- verified: false
55
+ verified: verifiedAddresses?.has(token.address.toLowerCase()) ?? false
53
56
  }));
54
57
  const allTokens = [...filteredTokens, ...additionalTokens];
55
58
  if (allTokens.length) result[address][chainId] = allTokens;
@@ -1 +1 @@
1
- {"version":3,"file":"useFilteredByTokenBalances.js","names":[],"sources":["../../../src/hooks/useFilteredByTokenBalances.ts"],"sourcesContent":["import {\n type BaseToken,\n ChainType,\n getWalletBalances,\n type TokenExtended,\n type WalletTokenExtended,\n} from '@lifi/sdk'\nimport { useQuery } from '@tanstack/react-query'\nimport { useMemo } from 'react'\nimport { useSDKClient } from '../providers/SDKClientProvider.js'\nimport { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'\nimport type { FormType } from '../stores/form/types.js'\nimport { getConfigItemSets, isFormItemAllowed } from '../utils/item.js'\nimport { isSupportedToken } from '../utils/tokenList.js'\n\nexport const useFilteredTokensByBalance = (\n accountsWithTokens?: Record<\n string,\n { chainType: ChainType; tokens: Record<number, TokenExtended[]> }\n >,\n formType?: FormType\n): {\n data: Record<string, Record<number, TokenExtended[]>> | undefined\n isLoading: boolean\n} => {\n const { tokens: configTokens } = useWidgetConfig()\n const sdkClient = useSDKClient()\n\n const evmAddress = useMemo(() => {\n const evmAccount = Object.entries(accountsWithTokens ?? {}).find(\n ([_, { chainType }]) => chainType === ChainType.EVM\n )\n return evmAccount?.[0]\n }, [accountsWithTokens])\n\n const { data: existingBalances, isLoading } = useQuery({\n queryKey: ['existing-evm-balances', evmAddress],\n queryFn: () => getWalletBalances(sdkClient, evmAddress ?? ''),\n enabled: !!evmAddress,\n refetchInterval: 30_000, // 30 seconds\n staleTime: 30_000, // 30 seconds\n retry: false,\n })\n\n const accountsWithFilteredTokens = useMemo(() => {\n if (!accountsWithTokens) {\n return undefined\n }\n\n // Early return if no existing balances - return all tokens\n const result: Record<string, Record<number, TokenExtended[]>> = {}\n if (!existingBalances) {\n for (const [address, { tokens }] of Object.entries(accountsWithTokens)) {\n result[address] = tokens\n }\n return result\n }\n\n for (const [address, { tokens }] of Object.entries(accountsWithTokens)) {\n result[address] = {}\n\n for (const [chainIdStr, chainTokens] of Object.entries(tokens)) {\n const chainId = Number(chainIdStr)\n // Get balances for this specific chain\n const balances = existingBalances?.[chainId]\n // If no balances, RPC all tokens of the chain\n if (!balances?.length) {\n if (chainTokens.length) {\n result[address][chainId] = chainTokens\n }\n continue\n }\n\n // Optimize token matching with Set for O(1) lookup\n const balanceSet = new Set(\n balances.map((balance: WalletTokenExtended) =>\n balance.address.toLowerCase()\n )\n )\n\n // Get tokens that are in chainTokens and have balances\n const filteredTokens = chainTokens.filter((token) => {\n const tokenKey = token.address.toLowerCase()\n return balanceSet.has(tokenKey)\n })\n\n // Get tokens that are in balances but not in chainTokens\n const chainTokenSet = new Set(\n chainTokens.map((token) => token.address.toLowerCase())\n )\n\n // Get allowed addresses from config tokens\n const allowedAddressesConfig = 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 const additionalTokens = balances\n .filter((balance: WalletTokenExtended) => {\n const balanceKey = balance.address.toLowerCase()\n return (\n !chainTokenSet.has(balanceKey) &&\n isSupportedToken(balance) &&\n isFormItemAllowed(\n balance,\n allowedAddressesConfig,\n formType,\n (t) => t.address.toLowerCase()\n )\n )\n })\n // Mark tokens from wallet balances as unverified\n .map((token: WalletTokenExtended) => ({\n ...token,\n verified: false,\n })) as TokenExtended[]\n\n // Combine both sets of tokens - convert WalletTokenExtended to TokenAmount\n const allTokens = [\n ...filteredTokens,\n ...additionalTokens,\n ] as TokenExtended[]\n\n if (allTokens.length) {\n result[address][chainId] = allTokens\n }\n }\n }\n\n return result\n }, [accountsWithTokens, existingBalances, configTokens, formType])\n\n return { data: accountsWithFilteredTokens, isLoading }\n}\n"],"mappings":";;;;;;;;AAeA,MAAa,8BACX,oBAIA,aAIG;CACH,MAAM,EAAE,QAAQ,iBAAiB,gBAAgB;CACjD,MAAM,YAAY,aAAa;CAE/B,MAAM,aAAa,cAAc;EAI/B,OAHmB,OAAO,QAAQ,sBAAsB,CAAC,CAAC,CAAC,CAAC,MACzD,CAAC,GAAG,EAAE,iBAAiB,cAAc,UAAU,GAElC,CAAC,GAAG;CACtB,GAAG,CAAC,kBAAkB,CAAC;CAEvB,MAAM,EAAE,MAAM,kBAAkB,cAAc,SAAS;EACrD,UAAU,CAAC,yBAAyB,UAAU;EAC9C,eAAe,kBAAkB,WAAW,cAAc,EAAE;EAC5D,SAAS,CAAC,CAAC;EACX,iBAAiB;EACjB,WAAW;EACX,OAAO;CACT,CAAC;CAgGD,OAAO;EAAE,MA9F0B,cAAc;GAC/C,IAAI,CAAC,oBACH;GAIF,MAAM,SAA0D,CAAC;GACjE,IAAI,CAAC,kBAAkB;IACrB,KAAK,MAAM,CAAC,SAAS,EAAE,aAAa,OAAO,QAAQ,kBAAkB,GACnE,OAAO,WAAW;IAEpB,OAAO;GACT;GAEA,KAAK,MAAM,CAAC,SAAS,EAAE,aAAa,OAAO,QAAQ,kBAAkB,GAAG;IACtE,OAAO,WAAW,CAAC;IAEnB,KAAK,MAAM,CAAC,YAAY,gBAAgB,OAAO,QAAQ,MAAM,GAAG;KAC9D,MAAM,UAAU,OAAO,UAAU;KAEjC,MAAM,WAAW,mBAAmB;KAEpC,IAAI,CAAC,UAAU,QAAQ;MACrB,IAAI,YAAY,QACd,OAAO,QAAQ,CAAC,WAAW;MAE7B;KACF;KAGA,MAAM,aAAa,IAAI,IACrB,SAAS,KAAK,YACZ,QAAQ,QAAQ,YAAY,CAC9B,CACF;KAGA,MAAM,iBAAiB,YAAY,QAAQ,UAAU;MACnD,MAAM,WAAW,MAAM,QAAQ,YAAY;MAC3C,OAAO,WAAW,IAAI,QAAQ;KAChC,CAAC;KAGD,MAAM,gBAAgB,IAAI,IACxB,YAAY,KAAK,UAAU,MAAM,QAAQ,YAAY,CAAC,CACxD;KAGA,MAAM,yBAAyB,kBAC7B,eACC,WACC,IAAI,IACF,OACG,QAAQ,MAAM,OAAO,EAAE,OAAO,MAAM,OAAO,CAAC,CAC5C,KAAK,MAAM,EAAE,QAAQ,YAAY,CAAC,CACvC,GACF,QACF;KAEA,MAAM,mBAAmB,SACtB,QAAQ,YAAiC;MACxC,MAAM,aAAa,QAAQ,QAAQ,YAAY;MAC/C,OACE,CAAC,cAAc,IAAI,UAAU,KAC7B,iBAAiB,OAAO,KACxB,kBACE,SACA,wBACA,WACC,MAAM,EAAE,QAAQ,YAAY,CAC/B;KAEJ,CAAC,CAAC,CAED,KAAK,WAAgC;MACpC,GAAG;MACH,UAAU;KACZ,EAAE;KAGJ,MAAM,YAAY,CAChB,GAAG,gBACH,GAAG,gBACL;KAEA,IAAI,UAAU,QACZ,OAAO,QAAQ,CAAC,WAAW;IAE/B;GACF;GAEA,OAAO;EACT,GAAG;GAAC;GAAoB;GAAkB;GAAc;EAAQ,CAExB;EAAG;CAAU;AACvD"}
1
+ {"version":3,"file":"useFilteredByTokenBalances.js","names":[],"sources":["../../../src/hooks/useFilteredByTokenBalances.ts"],"sourcesContent":["import {\n type BaseToken,\n ChainType,\n getWalletBalances,\n type TokenExtended,\n type WalletTokenExtended,\n} from '@lifi/sdk'\nimport { useQuery } from '@tanstack/react-query'\nimport { useMemo } from 'react'\nimport { useSDKClient } from '../providers/SDKClientProvider.js'\nimport { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'\nimport type { FormType } from '../stores/form/types.js'\nimport { getConfigItemSets, isFormItemAllowed } from '../utils/item.js'\nimport { getVerifiedTokensSets } from '../utils/token.js'\nimport { isSupportedToken } from '../utils/tokenList.js'\n\nexport const useFilteredTokensByBalance = (\n accountsWithTokens?: Record<\n string,\n { chainType: ChainType; tokens: Record<number, TokenExtended[]> }\n >,\n formType?: FormType\n): {\n data: Record<string, Record<number, TokenExtended[]>> | undefined\n isLoading: boolean\n} => {\n const { tokens: configTokens } = useWidgetConfig()\n const sdkClient = useSDKClient()\n\n const evmAddress = useMemo(() => {\n const evmAccount = Object.entries(accountsWithTokens ?? {}).find(\n ([_, { chainType }]) => chainType === ChainType.EVM\n )\n return evmAccount?.[0]\n }, [accountsWithTokens])\n\n const { data: existingBalances, isLoading } = useQuery({\n queryKey: ['existing-evm-balances', evmAddress],\n queryFn: () => getWalletBalances(sdkClient, evmAddress ?? ''),\n enabled: !!evmAddress,\n refetchInterval: 30_000, // 30 seconds\n staleTime: 30_000, // 30 seconds\n retry: false,\n })\n\n const accountsWithFilteredTokens = useMemo(() => {\n if (!accountsWithTokens) {\n return undefined\n }\n\n // Early return if no existing balances - return all tokens\n const result: Record<string, Record<number, TokenExtended[]>> = {}\n if (!existingBalances) {\n for (const [address, { tokens }] of Object.entries(accountsWithTokens)) {\n result[address] = tokens\n }\n return result\n }\n\n const verifiedTokensSets = getVerifiedTokensSets(configTokens)\n\n for (const [address, { tokens }] of Object.entries(accountsWithTokens)) {\n result[address] = {}\n\n for (const [chainIdStr, chainTokens] of Object.entries(tokens)) {\n const chainId = Number(chainIdStr)\n const verifiedAddresses = verifiedTokensSets?.get(chainId)\n // Get balances for this specific chain\n const balances = existingBalances?.[chainId]\n // If no balances, RPC all tokens of the chain\n if (!balances?.length) {\n if (chainTokens.length) {\n result[address][chainId] = chainTokens\n }\n continue\n }\n\n // Optimize token matching with Set for O(1) lookup\n const balanceSet = new Set(\n balances.map((balance: WalletTokenExtended) =>\n balance.address.toLowerCase()\n )\n )\n\n // Get tokens that are in chainTokens and have balances\n const filteredTokens = chainTokens.filter((token) => {\n const tokenKey = token.address.toLowerCase()\n return balanceSet.has(tokenKey)\n })\n\n // Get tokens that are in balances but not in chainTokens\n const chainTokenSet = new Set(\n chainTokens.map((token) => token.address.toLowerCase())\n )\n\n // Get allowed addresses from config tokens\n const allowedAddressesConfig = 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 const additionalTokens = balances\n .filter((balance: WalletTokenExtended) => {\n const balanceKey = balance.address.toLowerCase()\n return (\n !chainTokenSet.has(balanceKey) &&\n isSupportedToken(balance) &&\n isFormItemAllowed(\n balance,\n allowedAddressesConfig,\n formType,\n (t) => t.address.toLowerCase()\n )\n )\n })\n // Mark tokens from wallet balances as unverified unless allowlisted\n .map((token: WalletTokenExtended) => ({\n ...token,\n verified:\n verifiedAddresses?.has(token.address.toLowerCase()) ?? false,\n })) as TokenExtended[]\n\n // Combine both sets of tokens - convert WalletTokenExtended to TokenAmount\n const allTokens = [\n ...filteredTokens,\n ...additionalTokens,\n ] as TokenExtended[]\n\n if (allTokens.length) {\n result[address][chainId] = allTokens\n }\n }\n }\n\n return result\n }, [accountsWithTokens, existingBalances, configTokens, formType])\n\n return { data: accountsWithFilteredTokens, isLoading }\n}\n"],"mappings":";;;;;;;;;AAgBA,MAAa,8BACX,oBAIA,aAIG;CACH,MAAM,EAAE,QAAQ,iBAAiB,gBAAgB;CACjD,MAAM,YAAY,aAAa;CAE/B,MAAM,aAAa,cAAc;EAI/B,OAHmB,OAAO,QAAQ,sBAAsB,CAAC,CAAC,CAAC,CAAC,MACzD,CAAC,GAAG,EAAE,iBAAiB,cAAc,UAAU,GAElC,CAAC,GAAG;CACtB,GAAG,CAAC,kBAAkB,CAAC;CAEvB,MAAM,EAAE,MAAM,kBAAkB,cAAc,SAAS;EACrD,UAAU,CAAC,yBAAyB,UAAU;EAC9C,eAAe,kBAAkB,WAAW,cAAc,EAAE;EAC5D,SAAS,CAAC,CAAC;EACX,iBAAiB;EACjB,WAAW;EACX,OAAO;CACT,CAAC;CAoGD,OAAO;EAAE,MAlG0B,cAAc;GAC/C,IAAI,CAAC,oBACH;GAIF,MAAM,SAA0D,CAAC;GACjE,IAAI,CAAC,kBAAkB;IACrB,KAAK,MAAM,CAAC,SAAS,EAAE,aAAa,OAAO,QAAQ,kBAAkB,GACnE,OAAO,WAAW;IAEpB,OAAO;GACT;GAEA,MAAM,qBAAqB,sBAAsB,YAAY;GAE7D,KAAK,MAAM,CAAC,SAAS,EAAE,aAAa,OAAO,QAAQ,kBAAkB,GAAG;IACtE,OAAO,WAAW,CAAC;IAEnB,KAAK,MAAM,CAAC,YAAY,gBAAgB,OAAO,QAAQ,MAAM,GAAG;KAC9D,MAAM,UAAU,OAAO,UAAU;KACjC,MAAM,oBAAoB,oBAAoB,IAAI,OAAO;KAEzD,MAAM,WAAW,mBAAmB;KAEpC,IAAI,CAAC,UAAU,QAAQ;MACrB,IAAI,YAAY,QACd,OAAO,QAAQ,CAAC,WAAW;MAE7B;KACF;KAGA,MAAM,aAAa,IAAI,IACrB,SAAS,KAAK,YACZ,QAAQ,QAAQ,YAAY,CAC9B,CACF;KAGA,MAAM,iBAAiB,YAAY,QAAQ,UAAU;MACnD,MAAM,WAAW,MAAM,QAAQ,YAAY;MAC3C,OAAO,WAAW,IAAI,QAAQ;KAChC,CAAC;KAGD,MAAM,gBAAgB,IAAI,IACxB,YAAY,KAAK,UAAU,MAAM,QAAQ,YAAY,CAAC,CACxD;KAGA,MAAM,yBAAyB,kBAC7B,eACC,WACC,IAAI,IACF,OACG,QAAQ,MAAM,OAAO,EAAE,OAAO,MAAM,OAAO,CAAC,CAC5C,KAAK,MAAM,EAAE,QAAQ,YAAY,CAAC,CACvC,GACF,QACF;KAEA,MAAM,mBAAmB,SACtB,QAAQ,YAAiC;MACxC,MAAM,aAAa,QAAQ,QAAQ,YAAY;MAC/C,OACE,CAAC,cAAc,IAAI,UAAU,KAC7B,iBAAiB,OAAO,KACxB,kBACE,SACA,wBACA,WACC,MAAM,EAAE,QAAQ,YAAY,CAC/B;KAEJ,CAAC,CAAC,CAED,KAAK,WAAgC;MACpC,GAAG;MACH,UACE,mBAAmB,IAAI,MAAM,QAAQ,YAAY,CAAC,KAAK;KAC3D,EAAE;KAGJ,MAAM,YAAY,CAChB,GAAG,gBACH,GAAG,gBACL;KAEA,IAAI,UAAU,QACZ,OAAO,QAAQ,CAAC,WAAW;IAE/B;GACF;GAEA,OAAO;EACT,GAAG;GAAC;GAAoB;GAAkB;GAAc;EAAQ,CAExB;EAAG;CAAU;AACvD"}
@@ -220,6 +220,7 @@ type WidgetTokens = {
220
220
  featured?: StaticToken[];
221
221
  include?: Token[];
222
222
  popular?: StaticToken[];
223
+ verified?: BaseToken[];
223
224
  } & AllowDenyItems<BaseToken>;
224
225
  type WidgetLanguages = {
225
226
  default?: LanguageKey;
@@ -1 +1 @@
1
- {"version":3,"file":"widget.d.ts","names":[],"sources":["../../../src/types/widget.ts"],"mappings":"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"}
1
+ {"version":3,"file":"widget.d.ts","names":[],"sources":["../../../src/types/widget.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"widget.js","names":[],"sources":["../../../src/types/widget.ts"],"sourcesContent":["import type {\n BaseToken,\n ChainType,\n ContractCall,\n ExecutionOptions,\n ExtendedChain,\n Order,\n Route,\n RouteOptions,\n SDKConfig,\n StaticToken,\n Token,\n} from '@lifi/sdk'\nimport type { WalletMenuOpenArgs } from '@lifi/wallet-management'\nimport type { WidgetProviderProps } from '@lifi/widget-provider'\nimport type {\n Components,\n PaletteMode,\n PaletteOptions,\n Shape,\n SxProps,\n Theme,\n} from '@mui/material'\nimport type { TypographyVariantsOptions } from '@mui/material/styles'\nimport type {\n CSSProperties,\n PropsWithChildren,\n ReactNode,\n RefObject,\n} from 'react'\nimport type {\n LanguageKey,\n LanguageResources,\n} from '../providers/I18nProvider/types.js'\nimport type { DefaultFieldValues } from '../stores/form/types.js'\n\nexport type WidgetVariant = 'compact' | 'wide' | 'drawer'\n\n/** @internal Not part of the public API. */\nexport type InternalWidgetMode = 'limit'\nexport type WidgetMode =\n | 'default'\n | 'split'\n | 'custom'\n | 'refuel'\n | InternalWidgetMode\nexport type SplitMode = 'bridge' | 'swap'\nexport type SplitModeOptions = {\n defaultTab: SplitMode\n}\nexport type CustomMode = 'checkout' | 'deposit'\nexport interface ModeOptions {\n /**\n * Configure split mode behavior:\n * - 'bridge' | 'swap': Single mode without tabs\n * - { defaultTab: 'bridge' | 'swap' }: Tabs mode with configurable default tab\n */\n split?: SplitMode | SplitModeOptions\n custom?: { type: CustomMode }\n}\n\n/**\n * Identity of a header navigation tab. Each key maps to a widget variant, mode and\n * (optionally) mode options; labels are resolved from the key.\n */\nexport type SplitNavigationTabKey = SplitMode\n/** @internal Not part of the public API. */\nexport type InternalNavigationTabKey =\n | 'default'\n | 'private'\n | 'refuel'\n | 'swap-advanced'\n | 'bridge-advanced'\n | 'limit'\nexport type NavigationTabKey = InternalNavigationTabKey | SplitNavigationTabKey\n\nexport type Appearance = PaletteMode | 'system'\nexport interface NavigationProps {\n /**\n * If given, uses a negative margin to counteract the padding on sides for navigation elements like icon buttons.\n * @default true\n */\n edge?: boolean\n}\nexport type WidgetThemeComponents = Partial<\n Pick<\n Components<Theme>,\n | 'MuiAppBar'\n | 'MuiAvatar'\n | 'MuiButton'\n | 'MuiCard'\n | 'MuiDrawer'\n | 'MuiIconButton'\n | 'MuiInputCard'\n | 'MuiNavigationTabs'\n | 'MuiNavigationTab'\n | 'MuiTabs'\n | 'MuiCheckbox'\n >\n>\n\nexport type WidgetTheme = {\n colorSchemes?: {\n light?: {\n palette: PaletteOptions\n }\n dark?: {\n palette: PaletteOptions\n }\n }\n shape?: Partial<Shape>\n typography?: TypographyVariantsOptions\n components?: WidgetThemeComponents\n container?: CSSProperties\n routesContainer?: CSSProperties\n chainSidebarContainer?: CSSProperties\n header?: CSSProperties\n navigation?: NavigationProps\n}\n\nexport interface DisabledUIConfig {\n fromAmount?: boolean\n fromToken?: boolean\n toAddress?: boolean\n toToken?: boolean\n}\n\nexport interface HiddenUIConfig {\n addressBookConnectedWallets?: boolean\n allNetworks?: boolean\n appearance?: boolean\n bridgesSettings?: boolean\n chainSelect?: boolean\n /**\n * Hide the chain sidebar in the `wide` variant. No effect in other variants.\n * @default false\n */\n chainSidebar?: boolean\n contactSupport?: boolean\n /**\n * Hide the drawer close button. Only meaningful when `variant === 'drawer'`.\n * @default false\n */\n drawerCloseButton?: boolean\n fromToken?: boolean\n gasRefuelMessage?: boolean\n hideSmallBalances?: boolean\n history?: boolean\n insufficientGasMessage?: boolean\n integratorStepDetails?: boolean\n language?: boolean\n /**\n * Hide the warning shown when sending to an address with low historical\n * on-chain activity (low transaction count or contract-like behavior).\n * @default false\n */\n lowAddressActivityConfirmation?: boolean\n poweredBy?: boolean\n reverseTokensButton?: boolean\n routeCardPriceImpact?: boolean\n routeTokenDescription?: boolean\n searchTokenInput?: boolean\n toAddress?: boolean\n toToken?: boolean\n walletMenu?: boolean\n}\n\nexport interface RequiredUIConfig {\n accountDeployedMessage?: boolean\n toAddress?: boolean\n}\n\nexport type DefaultUI = {\n transactionDetailsExpanded?: boolean\n navigationHeaderTitleNoWrap?: boolean\n /**\n * Layout for the main form.\n * - `'default'`: the chain/token selectors plus single amount input.\n * - `'cards'`: the redesigned card-based layout — stacked Send/Receive amount\n * cards (`AmountInputCardPair`) with inline token pill selectors and a swap\n * button.\n * @default 'default'\n */\n layout?: 'default' | 'cards'\n}\n\nexport interface WidgetWalletConfig {\n walletEcosystemsOrder?: Record<string, ChainType[]>\n onConnect?(args?: WalletMenuOpenArgs): void\n /**\n * Determines whether the widget should provide partial wallet management functionality.\n *\n * In partial mode, external wallet management will be used for \"opt-out\" providers,\n * while the internal management is applied for any remaining providers that do not opt out.\n * This allows a flexible balance between the integrator's custom wallet menu and the widget's native wallet menu.\n * @default false\n */\n usePartialWalletManagement?: boolean\n /**\n * This option forces the widget to always use internal wallet management ignoring external wallet management contexts\n * @default false\n */\n forceInternalWalletManagement?: boolean\n}\n\nexport interface WidgetSDKConfig\n extends Omit<\n SDKConfig,\n | 'apiKey'\n | 'disableVersionCheck'\n | 'integrator'\n | 'providers'\n | 'routeOptions'\n | 'widgetVersion'\n > {\n routeOptions?: Omit<\n RouteOptions,\n 'bridges' | 'exchanges' | 'fee' | 'referrer' | 'order' | 'slippage'\n >\n executionOptions?: Pick<ExecutionOptions, 'updateTransactionRequestHook'>\n}\n\nexport interface WidgetContractTool {\n name: string\n logoURI: string\n}\n\nexport interface CalculateFeeParams {\n fromChain: ExtendedChain\n toChain: ExtendedChain\n fromToken: Token\n toToken: Token\n fromAddress?: string\n toAddress?: string\n fromAmount?: bigint\n toAmount?: bigint\n slippage?: number\n}\n\nexport interface WidgetFeeConfig {\n name?: string\n logoURI?: string\n fee?: number\n /**\n * Whether to show the fee percentage in the fee details.\n * @default false\n */\n showFeePercentage?: boolean\n /**\n * Whether to show a tooltip with the fee details. Requires `name` or `feeTooltipComponent` to be set.\n * @default false\n */\n showFeeTooltip?: boolean\n /**\n * Custom tooltip component to show with the fee details.\n */\n feeTooltipComponent?: ReactNode\n /**\n * Function to calculate fees before fetching quotes.\n * If provided, this function will be used instead of the `fee` parameter.\n * Only one of `fee` or `calculateFee` should be used.\n *\n * @param params Object containing the fee calculation parameters\n * @returns A promise that resolves to the calculated fee as a number (e.g., 0.03 represents a 3% fee)\n */\n calculateFee?(params: CalculateFeeParams): Promise<number | undefined>\n}\n\nexport interface ToAddress {\n name?: string\n address: string\n chainType: ChainType\n logoURI?: string\n}\n\nexport interface AllowDeny<T> {\n allow?: T[]\n deny?: T[]\n}\n\nexport type AllowDenySetItem = string | number\nexport interface AllowDenySet {\n allow?: Set<AllowDenySetItem>\n deny?: Set<AllowDenySetItem>\n}\n\nexport type AllowDenySets = {\n from?: AllowDenySet\n to?: AllowDenySet\n} & AllowDenySet\n\nexport type AllowDenyItems<T> = {\n from?: AllowDeny<T>\n to?: AllowDeny<T>\n} & AllowDeny<T>\n\nexport type WidgetChains = {\n types?: AllowDeny<ChainType>\n} & AllowDenyItems<number>\n\nexport type WidgetTokens = {\n featured?: StaticToken[]\n include?: Token[]\n popular?: StaticToken[]\n} & AllowDenyItems<BaseToken>\n\nexport type WidgetLanguages = {\n default?: LanguageKey\n} & AllowDeny<LanguageKey>\n\nexport type PoweredByType = 'default' | 'jumper'\n\nexport interface RouteLabel {\n text: string\n sx?: SxProps<Theme>\n}\n\nexport interface RouteLabelRule {\n label: RouteLabel\n // Matching criteria\n bridges?: AllowDeny<string>\n exchanges?: AllowDeny<string>\n fromChainId?: number[]\n toChainId?: number[]\n fromTokenAddress?: string[]\n toTokenAddress?: string[]\n /**\n * Custom match function evaluated against the route. When provided, its\n * result is AND'd with the other matching criteria — all specified\n * conditions must pass for the label to apply. Use this for matching logic\n * that the built-in fields cannot express (gas costs, step count, route\n * tags, etc.).\n */\n match?: (route: Route) => boolean\n}\n\nexport type ExplorerUrl =\n | string\n | {\n url: string\n txPath?: string\n addressPath?: string\n }\n\nexport type WidgetProvider = (\n props: PropsWithChildren<WidgetProviderProps>\n) => ReactNode\n\nexport interface WidgetConfig {\n fromChain?: number\n toChain?: number\n fromToken?: string\n toToken?: string\n toAddress?: ToAddress\n toAddresses?: ToAddress[]\n fromAmount?: number | string\n minFromAmountUSD?: number\n toAmount?: number | string\n formUpdateKey?: string\n\n providers?: WidgetProvider[]\n\n contractCalls?: ContractCall[]\n contractComponent?: ReactNode\n contractSecondaryComponent?: ReactNode\n contractCompactComponent?: ReactNode\n contractTool?: WidgetContractTool\n integrator: string\n apiKey?: string\n feeConfig?: WidgetFeeConfig\n referrer?: string\n\n routePriority?: Order\n slippage?: number\n\n variant?: WidgetVariant\n mode?: WidgetMode\n modeOptions?: ModeOptions\n /**\n * Header navigation tabs, in order. When set, the widget renders a tab bar\n * and the active tab drives the displayed flow. Each entry is a tab key.\n *\n * @internal Not part of the public API.\n */\n _navigationTabs?: NavigationTabKey[]\n\n appearance?: Appearance\n theme?: WidgetTheme\n\n disabledUI?: DisabledUIConfig\n hiddenUI?: HiddenUIConfig\n requiredUI?: RequiredUIConfig\n defaultUI?: DefaultUI\n /**\n * When true, shows only the recommended route and hides the route\n * selector UI. Distinct from `routePriority`, which sets the SDK\n * ranking order across multiple displayed routes.\n * @default false\n */\n showSingleRoute?: boolean\n useRelayerRoutes?: boolean\n\n walletConfig?: WidgetWalletConfig\n sdkConfig?: WidgetSDKConfig\n\n buildUrl?: boolean\n keyPrefix?: string\n\n bridges?: AllowDeny<string>\n exchanges?: AllowDeny<string>\n chains?: WidgetChains\n tokens?: WidgetTokens\n languages?: WidgetLanguages\n languageResources?: LanguageResources\n explorerUrls?: Record<number, ExplorerUrl[]> &\n Partial<Record<'internal', ExplorerUrl[]>>\n poweredBy?: PoweredByType\n\n /**\n * Custom labels/badges to show on routes based on specified rules\n */\n routeLabels?: RouteLabelRule[]\n}\n\nexport interface FormFieldOptions {\n setUrlSearchParam: boolean\n}\n\nexport interface FieldValues\n extends Omit<DefaultFieldValues, 'fromAmount' | 'toAmount' | 'toAddress'> {\n fromAmount?: number | string\n toAmount?: number | string\n toAddress?: ToAddress | string\n}\n\nexport type FieldNames = keyof FieldValues\n\nexport type SetFieldValueFunction = <K extends FieldNames>(\n key: K,\n value: FieldValues[K],\n options?: FormFieldOptions\n) => void\n\nexport type FormState = {\n setFieldValue: SetFieldValueFunction\n}\n\nexport type FormRef = RefObject<FormState | null>\n\nexport interface FormRefProps {\n formRef?: FormRef\n}\n\nexport interface WidgetConfigProps extends FormRefProps {\n config: WidgetConfig\n}\n\nexport interface WidgetConfigPartialProps {\n config?: Partial<WidgetConfig>\n}\n\nexport type WidgetProps = WidgetDrawerProps &\n WidgetConfig &\n WidgetConfigPartialProps &\n FormRefProps\n\nexport interface WidgetDrawerProps extends WidgetConfigPartialProps {\n elementRef?: RefObject<HTMLDivElement>\n open?: boolean\n /**\n * Make sure to make the onClose callback stable (e.g. using useCallback) to avoid causing re-renders of the entire widget\n */\n onClose?(): void\n}\n\nexport enum ExpansionType {\n Routes = 'routes',\n FromChain = 'fromChain',\n ToChain = 'toChain',\n}\n"],"mappings":";AA2dA,IAAY,gBAAL,yBAAA,eAAA;CACL,cAAA,YAAA;CACA,cAAA,eAAA;CACA,cAAA,aAAA;;AACF,EAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"widget.js","names":[],"sources":["../../../src/types/widget.ts"],"sourcesContent":["import type {\n BaseToken,\n ChainType,\n ContractCall,\n ExecutionOptions,\n ExtendedChain,\n Order,\n Route,\n RouteOptions,\n SDKConfig,\n StaticToken,\n Token,\n} from '@lifi/sdk'\nimport type { WalletMenuOpenArgs } from '@lifi/wallet-management'\nimport type { WidgetProviderProps } from '@lifi/widget-provider'\nimport type {\n Components,\n PaletteMode,\n PaletteOptions,\n Shape,\n SxProps,\n Theme,\n} from '@mui/material'\nimport type { TypographyVariantsOptions } from '@mui/material/styles'\nimport type {\n CSSProperties,\n PropsWithChildren,\n ReactNode,\n RefObject,\n} from 'react'\nimport type {\n LanguageKey,\n LanguageResources,\n} from '../providers/I18nProvider/types.js'\nimport type { DefaultFieldValues } from '../stores/form/types.js'\n\nexport type WidgetVariant = 'compact' | 'wide' | 'drawer'\n\n/** @internal Not part of the public API. */\nexport type InternalWidgetMode = 'limit'\nexport type WidgetMode =\n | 'default'\n | 'split'\n | 'custom'\n | 'refuel'\n | InternalWidgetMode\nexport type SplitMode = 'bridge' | 'swap'\nexport type SplitModeOptions = {\n defaultTab: SplitMode\n}\nexport type CustomMode = 'checkout' | 'deposit'\nexport interface ModeOptions {\n /**\n * Configure split mode behavior:\n * - 'bridge' | 'swap': Single mode without tabs\n * - { defaultTab: 'bridge' | 'swap' }: Tabs mode with configurable default tab\n */\n split?: SplitMode | SplitModeOptions\n custom?: { type: CustomMode }\n}\n\n/**\n * Identity of a header navigation tab. Each key maps to a widget variant, mode and\n * (optionally) mode options; labels are resolved from the key.\n */\nexport type SplitNavigationTabKey = SplitMode\n/** @internal Not part of the public API. */\nexport type InternalNavigationTabKey =\n | 'default'\n | 'private'\n | 'refuel'\n | 'swap-advanced'\n | 'bridge-advanced'\n | 'limit'\nexport type NavigationTabKey = InternalNavigationTabKey | SplitNavigationTabKey\n\nexport type Appearance = PaletteMode | 'system'\nexport interface NavigationProps {\n /**\n * If given, uses a negative margin to counteract the padding on sides for navigation elements like icon buttons.\n * @default true\n */\n edge?: boolean\n}\nexport type WidgetThemeComponents = Partial<\n Pick<\n Components<Theme>,\n | 'MuiAppBar'\n | 'MuiAvatar'\n | 'MuiButton'\n | 'MuiCard'\n | 'MuiDrawer'\n | 'MuiIconButton'\n | 'MuiInputCard'\n | 'MuiNavigationTabs'\n | 'MuiNavigationTab'\n | 'MuiTabs'\n | 'MuiCheckbox'\n >\n>\n\nexport type WidgetTheme = {\n colorSchemes?: {\n light?: {\n palette: PaletteOptions\n }\n dark?: {\n palette: PaletteOptions\n }\n }\n shape?: Partial<Shape>\n typography?: TypographyVariantsOptions\n components?: WidgetThemeComponents\n container?: CSSProperties\n routesContainer?: CSSProperties\n chainSidebarContainer?: CSSProperties\n header?: CSSProperties\n navigation?: NavigationProps\n}\n\nexport interface DisabledUIConfig {\n fromAmount?: boolean\n fromToken?: boolean\n toAddress?: boolean\n toToken?: boolean\n}\n\nexport interface HiddenUIConfig {\n addressBookConnectedWallets?: boolean\n allNetworks?: boolean\n appearance?: boolean\n bridgesSettings?: boolean\n chainSelect?: boolean\n /**\n * Hide the chain sidebar in the `wide` variant. No effect in other variants.\n * @default false\n */\n chainSidebar?: boolean\n contactSupport?: boolean\n /**\n * Hide the drawer close button. Only meaningful when `variant === 'drawer'`.\n * @default false\n */\n drawerCloseButton?: boolean\n fromToken?: boolean\n gasRefuelMessage?: boolean\n hideSmallBalances?: boolean\n history?: boolean\n insufficientGasMessage?: boolean\n integratorStepDetails?: boolean\n language?: boolean\n /**\n * Hide the warning shown when sending to an address with low historical\n * on-chain activity (low transaction count or contract-like behavior).\n * @default false\n */\n lowAddressActivityConfirmation?: boolean\n poweredBy?: boolean\n reverseTokensButton?: boolean\n routeCardPriceImpact?: boolean\n routeTokenDescription?: boolean\n searchTokenInput?: boolean\n toAddress?: boolean\n toToken?: boolean\n walletMenu?: boolean\n}\n\nexport interface RequiredUIConfig {\n accountDeployedMessage?: boolean\n toAddress?: boolean\n}\n\nexport type DefaultUI = {\n transactionDetailsExpanded?: boolean\n navigationHeaderTitleNoWrap?: boolean\n /**\n * Layout for the main form.\n * - `'default'`: the chain/token selectors plus single amount input.\n * - `'cards'`: the redesigned card-based layout — stacked Send/Receive amount\n * cards (`AmountInputCardPair`) with inline token pill selectors and a swap\n * button.\n * @default 'default'\n */\n layout?: 'default' | 'cards'\n}\n\nexport interface WidgetWalletConfig {\n walletEcosystemsOrder?: Record<string, ChainType[]>\n onConnect?(args?: WalletMenuOpenArgs): void\n /**\n * Determines whether the widget should provide partial wallet management functionality.\n *\n * In partial mode, external wallet management will be used for \"opt-out\" providers,\n * while the internal management is applied for any remaining providers that do not opt out.\n * This allows a flexible balance between the integrator's custom wallet menu and the widget's native wallet menu.\n * @default false\n */\n usePartialWalletManagement?: boolean\n /**\n * This option forces the widget to always use internal wallet management ignoring external wallet management contexts\n * @default false\n */\n forceInternalWalletManagement?: boolean\n}\n\nexport interface WidgetSDKConfig\n extends Omit<\n SDKConfig,\n | 'apiKey'\n | 'disableVersionCheck'\n | 'integrator'\n | 'providers'\n | 'routeOptions'\n | 'widgetVersion'\n > {\n routeOptions?: Omit<\n RouteOptions,\n 'bridges' | 'exchanges' | 'fee' | 'referrer' | 'order' | 'slippage'\n >\n executionOptions?: Pick<ExecutionOptions, 'updateTransactionRequestHook'>\n}\n\nexport interface WidgetContractTool {\n name: string\n logoURI: string\n}\n\nexport interface CalculateFeeParams {\n fromChain: ExtendedChain\n toChain: ExtendedChain\n fromToken: Token\n toToken: Token\n fromAddress?: string\n toAddress?: string\n fromAmount?: bigint\n toAmount?: bigint\n slippage?: number\n}\n\nexport interface WidgetFeeConfig {\n name?: string\n logoURI?: string\n fee?: number\n /**\n * Whether to show the fee percentage in the fee details.\n * @default false\n */\n showFeePercentage?: boolean\n /**\n * Whether to show a tooltip with the fee details. Requires `name` or `feeTooltipComponent` to be set.\n * @default false\n */\n showFeeTooltip?: boolean\n /**\n * Custom tooltip component to show with the fee details.\n */\n feeTooltipComponent?: ReactNode\n /**\n * Function to calculate fees before fetching quotes.\n * If provided, this function will be used instead of the `fee` parameter.\n * Only one of `fee` or `calculateFee` should be used.\n *\n * @param params Object containing the fee calculation parameters\n * @returns A promise that resolves to the calculated fee as a number (e.g., 0.03 represents a 3% fee)\n */\n calculateFee?(params: CalculateFeeParams): Promise<number | undefined>\n}\n\nexport interface ToAddress {\n name?: string\n address: string\n chainType: ChainType\n logoURI?: string\n}\n\nexport interface AllowDeny<T> {\n allow?: T[]\n deny?: T[]\n}\n\nexport type AllowDenySetItem = string | number\nexport interface AllowDenySet {\n allow?: Set<AllowDenySetItem>\n deny?: Set<AllowDenySetItem>\n}\n\nexport type AllowDenySets = {\n from?: AllowDenySet\n to?: AllowDenySet\n} & AllowDenySet\n\nexport type AllowDenyItems<T> = {\n from?: AllowDeny<T>\n to?: AllowDeny<T>\n} & AllowDeny<T>\n\nexport type WidgetChains = {\n types?: AllowDeny<ChainType>\n} & AllowDenyItems<number>\n\nexport type WidgetTokens = {\n featured?: StaticToken[]\n include?: Token[]\n popular?: StaticToken[]\n verified?: BaseToken[]\n} & AllowDenyItems<BaseToken>\n\nexport type WidgetLanguages = {\n default?: LanguageKey\n} & AllowDeny<LanguageKey>\n\nexport type PoweredByType = 'default' | 'jumper'\n\nexport interface RouteLabel {\n text: string\n sx?: SxProps<Theme>\n}\n\nexport interface RouteLabelRule {\n label: RouteLabel\n // Matching criteria\n bridges?: AllowDeny<string>\n exchanges?: AllowDeny<string>\n fromChainId?: number[]\n toChainId?: number[]\n fromTokenAddress?: string[]\n toTokenAddress?: string[]\n /**\n * Custom match function evaluated against the route. When provided, its\n * result is AND'd with the other matching criteria — all specified\n * conditions must pass for the label to apply. Use this for matching logic\n * that the built-in fields cannot express (gas costs, step count, route\n * tags, etc.).\n */\n match?: (route: Route) => boolean\n}\n\nexport type ExplorerUrl =\n | string\n | {\n url: string\n txPath?: string\n addressPath?: string\n }\n\nexport type WidgetProvider = (\n props: PropsWithChildren<WidgetProviderProps>\n) => ReactNode\n\nexport interface WidgetConfig {\n fromChain?: number\n toChain?: number\n fromToken?: string\n toToken?: string\n toAddress?: ToAddress\n toAddresses?: ToAddress[]\n fromAmount?: number | string\n minFromAmountUSD?: number\n toAmount?: number | string\n formUpdateKey?: string\n\n providers?: WidgetProvider[]\n\n contractCalls?: ContractCall[]\n contractComponent?: ReactNode\n contractSecondaryComponent?: ReactNode\n contractCompactComponent?: ReactNode\n contractTool?: WidgetContractTool\n integrator: string\n apiKey?: string\n feeConfig?: WidgetFeeConfig\n referrer?: string\n\n routePriority?: Order\n slippage?: number\n\n variant?: WidgetVariant\n mode?: WidgetMode\n modeOptions?: ModeOptions\n /**\n * Header navigation tabs, in order. When set, the widget renders a tab bar\n * and the active tab drives the displayed flow. Each entry is a tab key.\n *\n * @internal Not part of the public API.\n */\n _navigationTabs?: NavigationTabKey[]\n\n appearance?: Appearance\n theme?: WidgetTheme\n\n disabledUI?: DisabledUIConfig\n hiddenUI?: HiddenUIConfig\n requiredUI?: RequiredUIConfig\n defaultUI?: DefaultUI\n /**\n * When true, shows only the recommended route and hides the route\n * selector UI. Distinct from `routePriority`, which sets the SDK\n * ranking order across multiple displayed routes.\n * @default false\n */\n showSingleRoute?: boolean\n useRelayerRoutes?: boolean\n\n walletConfig?: WidgetWalletConfig\n sdkConfig?: WidgetSDKConfig\n\n buildUrl?: boolean\n keyPrefix?: string\n\n bridges?: AllowDeny<string>\n exchanges?: AllowDeny<string>\n chains?: WidgetChains\n tokens?: WidgetTokens\n languages?: WidgetLanguages\n languageResources?: LanguageResources\n explorerUrls?: Record<number, ExplorerUrl[]> &\n Partial<Record<'internal', ExplorerUrl[]>>\n poweredBy?: PoweredByType\n\n /**\n * Custom labels/badges to show on routes based on specified rules\n */\n routeLabels?: RouteLabelRule[]\n}\n\nexport interface FormFieldOptions {\n setUrlSearchParam: boolean\n}\n\nexport interface FieldValues\n extends Omit<DefaultFieldValues, 'fromAmount' | 'toAmount' | 'toAddress'> {\n fromAmount?: number | string\n toAmount?: number | string\n toAddress?: ToAddress | string\n}\n\nexport type FieldNames = keyof FieldValues\n\nexport type SetFieldValueFunction = <K extends FieldNames>(\n key: K,\n value: FieldValues[K],\n options?: FormFieldOptions\n) => void\n\nexport type FormState = {\n setFieldValue: SetFieldValueFunction\n}\n\nexport type FormRef = RefObject<FormState | null>\n\nexport interface FormRefProps {\n formRef?: FormRef\n}\n\nexport interface WidgetConfigProps extends FormRefProps {\n config: WidgetConfig\n}\n\nexport interface WidgetConfigPartialProps {\n config?: Partial<WidgetConfig>\n}\n\nexport type WidgetProps = WidgetDrawerProps &\n WidgetConfig &\n WidgetConfigPartialProps &\n FormRefProps\n\nexport interface WidgetDrawerProps extends WidgetConfigPartialProps {\n elementRef?: RefObject<HTMLDivElement>\n open?: boolean\n /**\n * Make sure to make the onClose callback stable (e.g. using useCallback) to avoid causing re-renders of the entire widget\n */\n onClose?(): void\n}\n\nexport enum ExpansionType {\n Routes = 'routes',\n FromChain = 'fromChain',\n ToChain = 'toChain',\n}\n"],"mappings":";AA4dA,IAAY,gBAAL,yBAAA,eAAA;CACL,cAAA,YAAA;CACA,cAAA,eAAA;CACA,cAAA,aAAA;;AACF,EAAA,CAAA,CAAA"}
@@ -1,6 +1,24 @@
1
1
  import { getConfigItemSets, isFormItemAllowed } from "./item.js";
2
2
  //#region src/utils/token.ts
3
3
  /**
4
+ * Builds per-chain sets of lowercase token addresses from the
5
+ * `tokens.verified` config allowlist.
6
+ */
7
+ const getVerifiedTokensSets = (configTokens) => {
8
+ if (!configTokens?.verified?.length) return;
9
+ const sets = /* @__PURE__ */ new Map();
10
+ for (const token of configTokens.verified) {
11
+ const chainId = Number(token.chainId);
12
+ let addresses = sets.get(chainId);
13
+ if (!addresses) {
14
+ addresses = /* @__PURE__ */ new Set();
15
+ sets.set(chainId, addresses);
16
+ }
17
+ addresses.add(token.address.toLowerCase());
18
+ }
19
+ return sets;
20
+ };
21
+ /**
4
22
  * Merges verified tokens with search tokens.
5
23
  * Verified tokens take priority - search tokens are only added if they don't already exist.
6
24
  */
@@ -41,11 +59,28 @@ const filterAllowedTokens = (dataTokens, configTokens, chainsConfig, formType) =
41
59
  const allChainIds = Array.from(/* @__PURE__ */ new Set([...includedTokens.map((t) => t.chainId), ...Object.keys(dataTokens)])).map((chainId) => Number(chainId));
42
60
  const configChainIdsSet = getConfigItemSets(chainsConfig, (chainIds) => new Set(chainIds), formType);
43
61
  const allowedChainIds = configChainIdsSet ? allChainIds.filter((chainId) => isFormItemAllowed(chainId, configChainIdsSet, formType)) : allChainIds;
62
+ const verifiedTokensSets = getVerifiedTokensSets(configTokens);
44
63
  const allowedTokensByChain = {};
45
64
  for (const chainId of allowedChainIds) {
46
- const chainTokens = [...dataTokens[chainId], ...includedTokens.filter((t) => Number(t.chainId) === chainId)];
65
+ const chainIncludedTokens = includedTokens.filter((t) => Number(t.chainId) === chainId);
66
+ const includedAddresses = new Set(chainIncludedTokens.map((t) => t.address.toLowerCase()));
47
67
  const allowedAddresses = getConfigItemSets(configTokens, (tokens) => new Set(tokens.filter((t) => Number(t.chainId) === chainId).map((t) => t.address.toLowerCase())), formType);
48
- allowedTokensByChain[chainId] = chainTokens.filter((token) => isFormItemAllowed(token, allowedAddresses, formType, (t) => t.address.toLowerCase()));
68
+ const verifiedAddresses = verifiedTokensSets?.get(chainId);
69
+ const chainTokens = [...dataTokens[chainId] ?? [], ...chainIncludedTokens];
70
+ const filtered = [];
71
+ const seenAddresses = /* @__PURE__ */ new Set();
72
+ for (const token of chainTokens) {
73
+ const address = token.address.toLowerCase();
74
+ if (seenAddresses.has(address)) continue;
75
+ seenAddresses.add(address);
76
+ if (!isFormItemAllowed(token, allowedAddresses, formType, (t) => t.address.toLowerCase())) continue;
77
+ if (!token.verified && (includedAddresses.has(address) || verifiedAddresses?.has(address))) filtered.push({
78
+ ...token,
79
+ verified: true
80
+ });
81
+ else filtered.push(token);
82
+ }
83
+ allowedTokensByChain[chainId] = filtered;
49
84
  }
50
85
  return allowedTokensByChain;
51
86
  };
@@ -54,6 +89,6 @@ const getExecutionToToken = (route) => ({
54
89
  amount: BigInt(route.steps.at(-1)?.execution?.toAmount ?? route.steps.at(-1)?.estimate.toAmount ?? route.toAmount)
55
90
  });
56
91
  //#endregion
57
- export { filterAllowedTokens, getExecutionToToken, mergeVerifiedWithSearchTokens, updateTokenInCache };
92
+ export { filterAllowedTokens, getExecutionToToken, getVerifiedTokensSets, mergeVerifiedWithSearchTokens, updateTokenInCache };
58
93
 
59
94
  //# sourceMappingURL=token.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"token.js","names":[],"sources":["../../../src/utils/token.ts"],"sourcesContent":["import type {\n BaseToken,\n RouteExtended,\n Token,\n TokenAmount,\n TokenExtended,\n} from '@lifi/sdk'\nimport type { FormType } from '../stores/form/types.js'\nimport type { TokensByChain } from '../types/token.js'\nimport type { WidgetChains, WidgetTokens } from '../types/widget.js'\nimport { getConfigItemSets, isFormItemAllowed } from './item.js'\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 * 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 */\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 index = chainTokens.findIndex((t) => t.address === token.address)\n if (index < 0) {\n return data\n }\n return {\n ...data,\n [token.chainId]: chainTokens.map((t, i) =>\n i === index ? { ...t, ...token } : t\n ),\n }\n}\n\nexport const filterAllowedTokens = (\n dataTokens: { [chainId: number]: TokenExtended[] } | undefined,\n configTokens?: WidgetTokens,\n chainsConfig?: WidgetChains,\n formType?: FormType\n): { [chainId: number]: TokenExtended[] } | 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 allowedTokensByChain: { [chainId: number]: TokenExtended[] } = {}\n for (const chainId of allowedChainIds) {\n const chainTokens = [\n ...dataTokens[chainId],\n ...includedTokens.filter((t) => Number(t.chainId) === chainId),\n ]\n\n const allowedAddresses = 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 const filtered = chainTokens.filter((token) =>\n isFormItemAllowed(token, allowedAddresses, formType, (t) =>\n t.address.toLowerCase()\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"],"mappings":";;;;;;AAgBA,MAAa,iCACX,gBACA,iBAC8B;CAC9B,IAAI,CAAC,gBACH,OAAO;CAET,IAAI,CAAC,cACH,OAAO;CAGT,MAAM,SAAS,EAAE,GAAG,eAAe;CAEnC,KAAK,MAAM,CAAC,SAAS,WAAW,OAAO,QAAQ,YAAY,GAAG;EAC5D,MAAM,aAAa,OAAO,OAAO;EACjC,MAAM,iBAAiB,OAAO,eAAe,CAAC;EAC9C,MAAM,oBAAoB,IAAI,IAC5B,eAAe,KAAK,MAAM,EAAE,QAAQ,YAAY,CAAC,CACnD;EAEA,MAAM,YAAY,OAAO,QACtB,MAAM,CAAC,kBAAkB,IAAI,EAAE,QAAQ,YAAY,CAAC,CACvD;EAEA,IAAI,UAAU,QACZ,OAAO,cAAc,CAAC,GAAG,gBAAgB,GAAG,SAAS;CAEzD;CAEA,OAAO;AACT;;;;;AAMA,MAAa,sBACX,MACA,UAC8B;CAC9B,IAAI,CAAC,MACH,OAAO;CAET,MAAM,cAAc,KAAK,MAAM;CAC/B,IAAI,CAAC,aACH,OAAO;CAET,MAAM,QAAQ,YAAY,WAAW,MAAM,EAAE,YAAY,MAAM,OAAO;CACtE,IAAI,QAAQ,GACV,OAAO;CAET,OAAO;EACL,GAAG;GACF,MAAM,UAAU,YAAY,KAAK,GAAG,MACnC,MAAM,QAAQ;GAAE,GAAG;GAAG,GAAG;EAAM,IAAI,CACrC;CACF;AACF;AAEA,MAAa,uBACX,YACA,cACA,cACA,aACuD;CACvD,IAAI,CAAC,YACH;CAGF,MAAM,iBAAiB,cAAc,WAAW,CAAC;CACjD,MAAM,cAAc,MAAM,qBACxB,IAAI,IAAI,CACN,GAAG,eAAe,KAAK,MAAM,EAAE,OAAO,GACtC,GAAG,OAAO,KAAK,UAAU,CAC3B,CAAC,CACH,CAAC,CAAC,KAAK,YAAY,OAAO,OAAO,CAAC;CAElC,MAAM,oBAAoB,kBACxB,eACC,aAAuB,IAAI,IAAI,QAAQ,GACxC,QACF;CAEA,MAAM,kBAAkB,oBACpB,YAAY,QAAQ,YAClB,kBAAkB,SAAS,mBAAmB,QAAQ,CACxD,IACA;CAEJ,MAAM,uBAA+D,CAAC;CACtE,KAAK,MAAM,WAAW,iBAAiB;EACrC,MAAM,cAAc,CAClB,GAAG,WAAW,UACd,GAAG,eAAe,QAAQ,MAAM,OAAO,EAAE,OAAO,MAAM,OAAO,CAC/D;EAEA,MAAM,mBAAmB,kBACvB,eACC,WACC,IAAI,IACF,OACG,QAAQ,MAAM,OAAO,EAAE,OAAO,MAAM,OAAO,CAAC,CAC5C,KAAK,MAAM,EAAE,QAAQ,YAAY,CAAC,CACvC,GACF,QACF;EAQA,qBAAqB,WANJ,YAAY,QAAQ,UACnC,kBAAkB,OAAO,kBAAkB,WAAW,MACpD,EAAE,QAAQ,YAAY,CACxB,CAGqC;CACzC;CAEA,OAAO;AACT;AAEA,MAAa,uBAAuB,WAAuC;CACzE,GAAI,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,WAAW,WAAW,MAAM;CACpD,QAAQ,OACN,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,WAAW,YAC7B,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,SAAS,YAC7B,MAAM,QACV;AACF"}
1
+ {"version":3,"file":"token.js","names":[],"sources":["../../../src/utils/token.ts"],"sourcesContent":["import type { BaseToken, RouteExtended, Token, TokenAmount } from '@lifi/sdk'\nimport type { FormType } from '../stores/form/types.js'\nimport type { TokensByChain, TokenWithVerified } from '../types/token.js'\nimport type { WidgetChains, WidgetTokens } from '../types/widget.js'\nimport { getConfigItemSets, isFormItemAllowed } from './item.js'\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 * 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 */\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 index = chainTokens.findIndex((t) => t.address === token.address)\n if (index < 0) {\n return data\n }\n return {\n ...data,\n [token.chainId]: chainTokens.map((t, i) =>\n i === index ? { ...t, ...token } : t\n ),\n }\n}\n\nexport const filterAllowedTokens = (\n dataTokens: TokensByChain | undefined,\n configTokens?: WidgetTokens,\n chainsConfig?: WidgetChains,\n formType?: FormType\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 = 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 const verifiedAddresses = verifiedTokensSets?.get(chainId)\n\n const chainTokens: TokenWithVerified[] = [\n ...(dataTokens[chainId] ?? []),\n ...chainIncludedTokens,\n ]\n\n const filtered: TokenWithVerified[] = []\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\n if (\n !token.verified &&\n (includedAddresses.has(address) || verifiedAddresses?.has(address))\n ) {\n filtered.push({ ...token, verified: true })\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"],"mappings":";;;;;;AAUA,MAAa,yBACX,iBACyC;CACzC,IAAI,CAAC,cAAc,UAAU,QAC3B;CAEF,MAAM,uBAAO,IAAI,IAAyB;CAC1C,KAAK,MAAM,SAAS,aAAa,UAAU;EACzC,MAAM,UAAU,OAAO,MAAM,OAAO;EACpC,IAAI,YAAY,KAAK,IAAI,OAAO;EAChC,IAAI,CAAC,WAAW;GACd,4BAAY,IAAI,IAAI;GACpB,KAAK,IAAI,SAAS,SAAS;EAC7B;EACA,UAAU,IAAI,MAAM,QAAQ,YAAY,CAAC;CAC3C;CACA,OAAO;AACT;;;;;AAMA,MAAa,iCACX,gBACA,iBAC8B;CAC9B,IAAI,CAAC,gBACH,OAAO;CAET,IAAI,CAAC,cACH,OAAO;CAGT,MAAM,SAAS,EAAE,GAAG,eAAe;CAEnC,KAAK,MAAM,CAAC,SAAS,WAAW,OAAO,QAAQ,YAAY,GAAG;EAC5D,MAAM,aAAa,OAAO,OAAO;EACjC,MAAM,iBAAiB,OAAO,eAAe,CAAC;EAC9C,MAAM,oBAAoB,IAAI,IAC5B,eAAe,KAAK,MAAM,EAAE,QAAQ,YAAY,CAAC,CACnD;EAEA,MAAM,YAAY,OAAO,QACtB,MAAM,CAAC,kBAAkB,IAAI,EAAE,QAAQ,YAAY,CAAC,CACvD;EAEA,IAAI,UAAU,QACZ,OAAO,cAAc,CAAC,GAAG,gBAAgB,GAAG,SAAS;CAEzD;CAEA,OAAO;AACT;;;;;AAMA,MAAa,sBACX,MACA,UAC8B;CAC9B,IAAI,CAAC,MACH,OAAO;CAET,MAAM,cAAc,KAAK,MAAM;CAC/B,IAAI,CAAC,aACH,OAAO;CAET,MAAM,QAAQ,YAAY,WAAW,MAAM,EAAE,YAAY,MAAM,OAAO;CACtE,IAAI,QAAQ,GACV,OAAO;CAET,OAAO;EACL,GAAG;GACF,MAAM,UAAU,YAAY,KAAK,GAAG,MACnC,MAAM,QAAQ;GAAE,GAAG;GAAG,GAAG;EAAM,IAAI,CACrC;CACF;AACF;AAEA,MAAa,uBACX,YACA,cACA,cACA,aAC8B;CAC9B,IAAI,CAAC,YACH;CAGF,MAAM,iBAAiB,cAAc,WAAW,CAAC;CACjD,MAAM,cAAc,MAAM,qBACxB,IAAI,IAAI,CACN,GAAG,eAAe,KAAK,MAAM,EAAE,OAAO,GACtC,GAAG,OAAO,KAAK,UAAU,CAC3B,CAAC,CACH,CAAC,CAAC,KAAK,YAAY,OAAO,OAAO,CAAC;CAElC,MAAM,oBAAoB,kBACxB,eACC,aAAuB,IAAI,IAAI,QAAQ,GACxC,QACF;CAEA,MAAM,kBAAkB,oBACpB,YAAY,QAAQ,YAClB,kBAAkB,SAAS,mBAAmB,QAAQ,CACxD,IACA;CAEJ,MAAM,qBAAqB,sBAAsB,YAAY;CAE7D,MAAM,uBAAsC,CAAC;CAC7C,KAAK,MAAM,WAAW,iBAAiB;EACrC,MAAM,sBAAsB,eAAe,QACxC,MAAM,OAAO,EAAE,OAAO,MAAM,OAC/B;EACA,MAAM,oBAAoB,IAAI,IAC5B,oBAAoB,KAAK,MAAM,EAAE,QAAQ,YAAY,CAAC,CACxD;EAEA,MAAM,mBAAmB,kBACvB,eACC,WACC,IAAI,IACF,OACG,QAAQ,MAAM,OAAO,EAAE,OAAO,MAAM,OAAO,CAAC,CAC5C,KAAK,MAAM,EAAE,QAAQ,YAAY,CAAC,CACvC,GACF,QACF;EAEA,MAAM,oBAAoB,oBAAoB,IAAI,OAAO;EAEzD,MAAM,cAAmC,CACvC,GAAI,WAAW,YAAY,CAAC,GAC5B,GAAG,mBACL;EAEA,MAAM,WAAgC,CAAC;EACvC,MAAM,gCAAgB,IAAI,IAAY;EACtC,KAAK,MAAM,SAAS,aAAa;GAC/B,MAAM,UAAU,MAAM,QAAQ,YAAY;GAG1C,IAAI,cAAc,IAAI,OAAO,GAC3B;GAEF,cAAc,IAAI,OAAO;GACzB,IACE,CAAC,kBAAkB,OAAO,kBAAkB,WAAW,MACrD,EAAE,QAAQ,YAAY,CACxB,GAEA;GAIF,IACE,CAAC,MAAM,aACN,kBAAkB,IAAI,OAAO,KAAK,mBAAmB,IAAI,OAAO,IAEjE,SAAS,KAAK;IAAE,GAAG;IAAO,UAAU;GAAK,CAAC;QAE1C,SAAS,KAAK,KAAK;EAEvB;EAEA,qBAAqB,WAAW;CAClC;CAEA,OAAO;AACT;AAEA,MAAa,uBAAuB,WAAuC;CACzE,GAAI,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,WAAW,WAAW,MAAM;CACpD,QAAQ,OACN,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,WAAW,YAC7B,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,SAAS,YAC7B,MAAM,QACV;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/widget",
3
- "version": "4.2.0",
3
+ "version": "4.3.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",
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget'
2
- export const version = '4.2.0'
2
+ export const version = '4.3.0'
@@ -11,6 +11,7 @@ import { useSDKClient } from '../providers/SDKClientProvider.js'
11
11
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
12
12
  import type { FormType } from '../stores/form/types.js'
13
13
  import { getConfigItemSets, isFormItemAllowed } from '../utils/item.js'
14
+ import { getVerifiedTokensSets } from '../utils/token.js'
14
15
  import { isSupportedToken } from '../utils/tokenList.js'
15
16
 
16
17
  export const useFilteredTokensByBalance = (
@@ -56,11 +57,14 @@ export const useFilteredTokensByBalance = (
56
57
  return result
57
58
  }
58
59
 
60
+ const verifiedTokensSets = getVerifiedTokensSets(configTokens)
61
+
59
62
  for (const [address, { tokens }] of Object.entries(accountsWithTokens)) {
60
63
  result[address] = {}
61
64
 
62
65
  for (const [chainIdStr, chainTokens] of Object.entries(tokens)) {
63
66
  const chainId = Number(chainIdStr)
67
+ const verifiedAddresses = verifiedTokensSets?.get(chainId)
64
68
  // Get balances for this specific chain
65
69
  const balances = existingBalances?.[chainId]
66
70
  // If no balances, RPC all tokens of the chain
@@ -115,10 +119,11 @@ export const useFilteredTokensByBalance = (
115
119
  )
116
120
  )
117
121
  })
118
- // Mark tokens from wallet balances as unverified
122
+ // Mark tokens from wallet balances as unverified unless allowlisted
119
123
  .map((token: WalletTokenExtended) => ({
120
124
  ...token,
121
- verified: false,
125
+ verified:
126
+ verifiedAddresses?.has(token.address.toLowerCase()) ?? false,
122
127
  })) as TokenExtended[]
123
128
 
124
129
  // Combine both sets of tokens - convert WalletTokenExtended to TokenAmount
@@ -302,6 +302,7 @@ export type WidgetTokens = {
302
302
  featured?: StaticToken[]
303
303
  include?: Token[]
304
304
  popular?: StaticToken[]
305
+ verified?: BaseToken[]
305
306
  } & AllowDenyItems<BaseToken>
306
307
 
307
308
  export type WidgetLanguages = {
@@ -1,15 +1,32 @@
1
- import type {
2
- BaseToken,
3
- RouteExtended,
4
- Token,
5
- TokenAmount,
6
- TokenExtended,
7
- } from '@lifi/sdk'
1
+ import type { BaseToken, RouteExtended, Token, TokenAmount } from '@lifi/sdk'
8
2
  import type { FormType } from '../stores/form/types.js'
9
- import type { TokensByChain } from '../types/token.js'
3
+ import type { TokensByChain, TokenWithVerified } from '../types/token.js'
10
4
  import type { WidgetChains, WidgetTokens } from '../types/widget.js'
11
5
  import { getConfigItemSets, isFormItemAllowed } from './item.js'
12
6
 
7
+ /**
8
+ * Builds per-chain sets of lowercase token addresses from the
9
+ * `tokens.verified` config allowlist.
10
+ */
11
+ export const getVerifiedTokensSets = (
12
+ configTokens?: WidgetTokens
13
+ ): Map<number, Set<string>> | undefined => {
14
+ if (!configTokens?.verified?.length) {
15
+ return undefined
16
+ }
17
+ const sets = new Map<number, Set<string>>()
18
+ for (const token of configTokens.verified) {
19
+ const chainId = Number(token.chainId)
20
+ let addresses = sets.get(chainId)
21
+ if (!addresses) {
22
+ addresses = new Set()
23
+ sets.set(chainId, addresses)
24
+ }
25
+ addresses.add(token.address.toLowerCase())
26
+ }
27
+ return sets
28
+ }
29
+
13
30
  /**
14
31
  * Merges verified tokens with search tokens.
15
32
  * Verified tokens take priority - search tokens are only added if they don't already exist.
@@ -74,11 +91,11 @@ export const updateTokenInCache = (
74
91
  }
75
92
 
76
93
  export const filterAllowedTokens = (
77
- dataTokens: { [chainId: number]: TokenExtended[] } | undefined,
94
+ dataTokens: TokensByChain | undefined,
78
95
  configTokens?: WidgetTokens,
79
96
  chainsConfig?: WidgetChains,
80
97
  formType?: FormType
81
- ): { [chainId: number]: TokenExtended[] } | undefined => {
98
+ ): TokensByChain | undefined => {
82
99
  if (!dataTokens) {
83
100
  return
84
101
  }
@@ -103,12 +120,16 @@ export const filterAllowedTokens = (
103
120
  )
104
121
  : allChainIds
105
122
 
106
- const allowedTokensByChain: { [chainId: number]: TokenExtended[] } = {}
123
+ const verifiedTokensSets = getVerifiedTokensSets(configTokens)
124
+
125
+ const allowedTokensByChain: TokensByChain = {}
107
126
  for (const chainId of allowedChainIds) {
108
- const chainTokens = [
109
- ...dataTokens[chainId],
110
- ...includedTokens.filter((t) => Number(t.chainId) === chainId),
111
- ]
127
+ const chainIncludedTokens = includedTokens.filter(
128
+ (t) => Number(t.chainId) === chainId
129
+ )
130
+ const includedAddresses = new Set(
131
+ chainIncludedTokens.map((t) => t.address.toLowerCase())
132
+ )
112
133
 
113
134
  const allowedAddresses = getConfigItemSets(
114
135
  configTokens,
@@ -121,11 +142,41 @@ export const filterAllowedTokens = (
121
142
  formType
122
143
  )
123
144
 
124
- const filtered = chainTokens.filter((token) =>
125
- isFormItemAllowed(token, allowedAddresses, formType, (t) =>
126
- t.address.toLowerCase()
127
- )
128
- )
145
+ const verifiedAddresses = verifiedTokensSets?.get(chainId)
146
+
147
+ const chainTokens: TokenWithVerified[] = [
148
+ ...(dataTokens[chainId] ?? []),
149
+ ...chainIncludedTokens,
150
+ ]
151
+
152
+ const filtered: TokenWithVerified[] = []
153
+ const seenAddresses = new Set<string>()
154
+ for (const token of chainTokens) {
155
+ const address = token.address.toLowerCase()
156
+ // Data tokens come first and win over include duplicates,
157
+ // keeping their extended data
158
+ if (seenAddresses.has(address)) {
159
+ continue
160
+ }
161
+ seenAddresses.add(address)
162
+ if (
163
+ !isFormItemAllowed(token, allowedAddresses, formType, (t) =>
164
+ t.address.toLowerCase()
165
+ )
166
+ ) {
167
+ continue
168
+ }
169
+ // Include and verified-allowlist tokens are explicitly set by the
170
+ // integrator, mark them as verified
171
+ if (
172
+ !token.verified &&
173
+ (includedAddresses.has(address) || verifiedAddresses?.has(address))
174
+ ) {
175
+ filtered.push({ ...token, verified: true })
176
+ } else {
177
+ filtered.push(token)
178
+ }
179
+ }
129
180
 
130
181
  allowedTokensByChain[chainId] = filtered
131
182
  }