@lifi/widget 4.9.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/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/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/i18n/bn.json +5 -1
- package/dist/esm/i18n/de.json +5 -1
- package/dist/esm/i18n/en.json +5 -1
- package/dist/esm/i18n/es.json +5 -1
- package/dist/esm/i18n/fr.json +5 -1
- package/dist/esm/i18n/hi.json +5 -1
- package/dist/esm/i18n/id.json +5 -1
- package/dist/esm/i18n/it.json +5 -1
- package/dist/esm/i18n/ja.json +5 -1
- package/dist/esm/i18n/ko.json +5 -1
- package/dist/esm/i18n/pl.json +5 -1
- package/dist/esm/i18n/pt.json +5 -1
- package/dist/esm/i18n/th.json +5 -1
- package/dist/esm/i18n/tr.json +5 -1
- package/dist/esm/i18n/uk.json +5 -1
- package/dist/esm/i18n/vi.json +5 -1
- package/dist/esm/i18n/zh.json +5 -1
- package/dist/esm/utils/token.js +13 -1
- package/dist/esm/utils/token.js.map +1 -1
- package/package.json +1 -1
- 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/config/version.ts +1 -1
- package/src/utils/token.ts +48 -0
package/dist/esm/i18n/bn.json
CHANGED
package/dist/esm/i18n/de.json
CHANGED
package/dist/esm/i18n/en.json
CHANGED
|
@@ -410,7 +410,11 @@
|
|
|
410
410
|
"currentPrice": "Current price",
|
|
411
411
|
"contractAddress": "Contract address",
|
|
412
412
|
"marketCap": "Market cap",
|
|
413
|
-
"volume24h": "Volume 24h"
|
|
413
|
+
"volume24h": "Volume 24h",
|
|
414
|
+
"status": "Status",
|
|
415
|
+
"statusFlagged": "Flagged as malicious by Hypernative. Trading this token can result in a total loss of funds.",
|
|
416
|
+
"statusUnverified": "Unverified by Hypernative",
|
|
417
|
+
"statusVerified": "Verified by Hypernative. False positives can occur, and we accept no liability for these alerts. Always do your own research before taking action."
|
|
414
418
|
},
|
|
415
419
|
"tags": {
|
|
416
420
|
"connected": "Connected",
|
package/dist/esm/i18n/es.json
CHANGED
package/dist/esm/i18n/fr.json
CHANGED
package/dist/esm/i18n/hi.json
CHANGED
package/dist/esm/i18n/id.json
CHANGED
package/dist/esm/i18n/it.json
CHANGED
package/dist/esm/i18n/ja.json
CHANGED
package/dist/esm/i18n/ko.json
CHANGED
package/dist/esm/i18n/pl.json
CHANGED
package/dist/esm/i18n/pt.json
CHANGED
|
@@ -364,6 +364,10 @@
|
|
|
364
364
|
"currentPrice": "Preço atual",
|
|
365
365
|
"contractAddress": "Endereço do Contrato",
|
|
366
366
|
"marketCap": "Capitalização de mercado",
|
|
367
|
-
"volume24h": "Volume 24h"
|
|
367
|
+
"volume24h": "Volume 24h",
|
|
368
|
+
"status": "",
|
|
369
|
+
"statusFlagged": "",
|
|
370
|
+
"statusUnverified": "",
|
|
371
|
+
"statusVerified": ""
|
|
368
372
|
}
|
|
369
373
|
}
|
package/dist/esm/i18n/th.json
CHANGED
package/dist/esm/i18n/tr.json
CHANGED
package/dist/esm/i18n/uk.json
CHANGED
|
@@ -364,6 +364,10 @@
|
|
|
364
364
|
"currentPrice": "Поточна ціна",
|
|
365
365
|
"contractAddress": "Адреса контракту",
|
|
366
366
|
"marketCap": "Ринкова капіталізація",
|
|
367
|
-
"volume24h": "Обсяг 24г"
|
|
367
|
+
"volume24h": "Обсяг 24г",
|
|
368
|
+
"status": "",
|
|
369
|
+
"statusFlagged": "",
|
|
370
|
+
"statusUnverified": "",
|
|
371
|
+
"statusVerified": ""
|
|
368
372
|
}
|
|
369
373
|
}
|
package/dist/esm/i18n/vi.json
CHANGED
package/dist/esm/i18n/zh.json
CHANGED
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",
|
|
@@ -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
|
)
|
|
@@ -8,6 +8,11 @@ import { useTranslation } from 'react-i18next'
|
|
|
8
8
|
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
|
|
9
9
|
import { useExplorer } from '../../hooks/useExplorer.js'
|
|
10
10
|
import { useToken } from '../../hooks/useToken.js'
|
|
11
|
+
import type { TokenAmount } from '../../types/token.js'
|
|
12
|
+
import {
|
|
13
|
+
getTokenStatusTextKey,
|
|
14
|
+
getTokenVerificationBadge,
|
|
15
|
+
} from '../../utils/token.js'
|
|
11
16
|
import { shortenAddress } from '../../utils/wallet.js'
|
|
12
17
|
import { TokenAvatar } from '../Avatar/TokenAvatar.js'
|
|
13
18
|
import { CardIconButton } from '../Card/CardIconButton.js'
|
|
@@ -17,10 +22,10 @@ import {
|
|
|
17
22
|
TokenDetailsSheetContainer,
|
|
18
23
|
TokenDetailsSheetHeader,
|
|
19
24
|
} from './TokenDetailsSheetContent.style.js'
|
|
25
|
+
import { tokenVerificationIcons } from './tokenVerificationIcons.js'
|
|
20
26
|
|
|
21
27
|
interface TokenDetailsSheetContentProps {
|
|
22
|
-
|
|
23
|
-
chainId: number | undefined
|
|
28
|
+
listToken: TokenAmount | undefined
|
|
24
29
|
withoutContractAddress: boolean
|
|
25
30
|
onClose: () => void
|
|
26
31
|
}
|
|
@@ -28,8 +33,7 @@ interface TokenDetailsSheetContentProps {
|
|
|
28
33
|
const noDataLabel = '-'
|
|
29
34
|
|
|
30
35
|
export const TokenDetailsSheetContent = ({
|
|
31
|
-
|
|
32
|
-
chainId,
|
|
36
|
+
listToken,
|
|
33
37
|
withoutContractAddress,
|
|
34
38
|
onClose,
|
|
35
39
|
}: TokenDetailsSheetContentProps): JSX.Element => {
|
|
@@ -37,9 +41,22 @@ export const TokenDetailsSheetContent = ({
|
|
|
37
41
|
const { getAddressLink } = useExplorer()
|
|
38
42
|
const { getChainById } = useAvailableChains()
|
|
39
43
|
|
|
44
|
+
const tokenAddress = listToken?.address
|
|
45
|
+
const chainId = listToken?.chainId
|
|
40
46
|
const { token, isLoading } = useToken(chainId, tokenAddress, true)
|
|
41
47
|
const chain = useMemo(() => getChainById(chainId), [chainId, getChainById])
|
|
42
48
|
|
|
49
|
+
// The row token, not the fetched one: only the list sets the flags.
|
|
50
|
+
const statusBadge = listToken
|
|
51
|
+
? getTokenVerificationBadge(listToken)
|
|
52
|
+
: undefined
|
|
53
|
+
const statusIcon = statusBadge
|
|
54
|
+
? tokenVerificationIcons[statusBadge]
|
|
55
|
+
: undefined
|
|
56
|
+
const statusText = listToken
|
|
57
|
+
? t(getTokenStatusTextKey(listToken))
|
|
58
|
+
: noDataLabel
|
|
59
|
+
|
|
43
60
|
const copyContractAddress = async (e: React.MouseEvent) => {
|
|
44
61
|
e.stopPropagation()
|
|
45
62
|
try {
|
|
@@ -215,6 +232,31 @@ export const TokenDetailsSheetContent = ({
|
|
|
215
232
|
: noDataLabel}
|
|
216
233
|
</Typography>
|
|
217
234
|
</MetricWithSkeleton>
|
|
235
|
+
<MetricContainer>
|
|
236
|
+
<Label>{t('tokenMetric.status')}</Label>
|
|
237
|
+
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1 }}>
|
|
238
|
+
{statusIcon ? (
|
|
239
|
+
<statusIcon.Icon
|
|
240
|
+
sx={{
|
|
241
|
+
flexShrink: 0,
|
|
242
|
+
fontSize: 20,
|
|
243
|
+
color: statusIcon.color,
|
|
244
|
+
}}
|
|
245
|
+
/>
|
|
246
|
+
) : null}
|
|
247
|
+
<Typography
|
|
248
|
+
sx={{
|
|
249
|
+
minWidth: 0,
|
|
250
|
+
fontWeight: 500,
|
|
251
|
+
fontSize: '14px',
|
|
252
|
+
lineHeight: '20px',
|
|
253
|
+
color: 'text.primary',
|
|
254
|
+
}}
|
|
255
|
+
>
|
|
256
|
+
{statusText}
|
|
257
|
+
</Typography>
|
|
258
|
+
</Box>
|
|
259
|
+
</MetricContainer>
|
|
218
260
|
</TokenDetailsSheetContainer>
|
|
219
261
|
)
|
|
220
262
|
}
|