@lifi/widget 4.5.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/AppDrawer.js +2 -1
- package/dist/esm/AppDrawer.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInput.js +13 -7
- package/dist/esm/components/AmountInput/AmountInput.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +2 -13
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
- package/dist/esm/components/AmountInputCard/PercentageChips.js +2 -13
- package/dist/esm/components/AmountInputCard/PercentageChips.js.map +1 -1
- package/dist/esm/components/AmountInputCard/PercentageChips.style.js +1 -1
- package/dist/esm/components/AmountInputCard/PercentageChips.style.js.map +1 -1
- package/dist/esm/components/AmountInputCard/SendAmountCard.js +13 -6
- package/dist/esm/components/AmountInputCard/SendAmountCard.js.map +1 -1
- package/dist/esm/components/Chains/AllChainsAvatar.js +7 -1
- package/dist/esm/components/Chains/AllChainsAvatar.js.map +1 -1
- package/dist/esm/components/Dialog/Dialog.js +2 -1
- package/dist/esm/components/Dialog/Dialog.js.map +1 -1
- package/dist/esm/components/Expansion/Expansion.js +4 -3
- package/dist/esm/components/Expansion/Expansion.js.map +1 -1
- package/dist/esm/components/Header/SettingsButton.js +2 -1
- package/dist/esm/components/Header/SettingsButton.js.map +1 -1
- package/dist/esm/components/IconCircle/IconCircle.js +2 -1
- package/dist/esm/components/IconCircle/IconCircle.js.map +1 -1
- package/dist/esm/components/Messages/UnsupportedToAddressMessage.js +27 -0
- package/dist/esm/components/Messages/UnsupportedToAddressMessage.js.map +1 -0
- package/dist/esm/components/Messages/WarningMessages.d.ts.map +1 -1
- package/dist/esm/components/Messages/WarningMessages.js +2 -0
- package/dist/esm/components/Messages/WarningMessages.js.map +1 -1
- package/dist/esm/components/Messages/useMessageQueue.js +6 -1
- package/dist/esm/components/Messages/useMessageQueue.js.map +1 -1
- package/dist/esm/components/RouteCard/RouteDetails.js +2 -1
- package/dist/esm/components/RouteCard/RouteDetails.js.map +1 -1
- package/dist/esm/components/Routes/LimitOrderRoutes.js +2 -2
- package/dist/esm/components/Routes/LimitOrderRoutes.js.map +1 -1
- package/dist/esm/components/Routes/RoutesContent.js +2 -2
- package/dist/esm/components/Routes/RoutesContent.js.map +1 -1
- package/dist/esm/components/SendToWallet/SendToWalletExpandButton.js +2 -2
- package/dist/esm/components/SendToWallet/SendToWalletExpandButton.js.map +1 -1
- package/dist/esm/components/Skeleton/WidgetSkeleton.js +2 -1
- package/dist/esm/components/Skeleton/WidgetSkeleton.js.map +1 -1
- package/dist/esm/components/Step/StepActions.js +18 -16
- package/dist/esm/components/Step/StepActions.js.map +1 -1
- package/dist/esm/components/StepActions/StepActionRow.js +2 -1
- package/dist/esm/components/StepActions/StepActionRow.js.map +1 -1
- package/dist/esm/components/Switch.js +1 -1
- package/dist/esm/components/Switch.js.map +1 -1
- package/dist/esm/components/Tabs/Tabs.style.js +4 -2
- package/dist/esm/components/Tabs/Tabs.style.js.map +1 -1
- package/dist/esm/components/TokenList/TokenListItem.js +46 -8
- package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
- package/dist/esm/components/TokenList/VirtualizedTokenList.js +22 -9
- package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
- package/dist/esm/components/TokenRate/TokenRate.js +2 -1
- package/dist/esm/components/TokenRate/TokenRate.js.map +1 -1
- package/dist/esm/components/TransactionCard/ActiveTransactionCard.js +22 -21
- package/dist/esm/components/TransactionCard/ActiveTransactionCard.js.map +1 -1
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/config/version.js.map +1 -1
- package/dist/esm/hooks/timer/time.js +2 -2
- package/dist/esm/hooks/timer/time.js.map +1 -1
- package/dist/esm/hooks/useAvailableChains.d.ts.map +1 -1
- package/dist/esm/hooks/useAvailableChains.js +2 -1
- package/dist/esm/hooks/useAvailableChains.js.map +1 -1
- package/dist/esm/hooks/useExplorer.d.ts.map +1 -1
- package/dist/esm/hooks/useExplorer.js +4 -0
- package/dist/esm/hooks/useExplorer.js.map +1 -1
- package/dist/esm/hooks/useFlaggedTokenGuard.js +28 -0
- package/dist/esm/hooks/useFlaggedTokenGuard.js.map +1 -0
- package/dist/esm/hooks/useGasSufficiency.js +3 -1
- package/dist/esm/hooks/useGasSufficiency.js.map +1 -1
- package/dist/esm/hooks/useMaxSendAmount.js +15 -0
- package/dist/esm/hooks/useMaxSendAmount.js.map +1 -0
- package/dist/esm/hooks/usePacedRouteExecution.js +3 -2
- package/dist/esm/hooks/usePacedRouteExecution.js.map +1 -1
- package/dist/esm/hooks/useRouteExecutionMessage.js +3 -2
- package/dist/esm/hooks/useRouteExecutionMessage.js.map +1 -1
- package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
- package/dist/esm/hooks/useRoutes.js +3 -1
- package/dist/esm/hooks/useRoutes.js.map +1 -1
- package/dist/esm/hooks/useToAddressRequirements.d.ts +2 -0
- package/dist/esm/hooks/useToAddressRequirements.d.ts.map +1 -1
- package/dist/esm/hooks/useToAddressRequirements.js +7 -1
- package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
- package/dist/esm/hooks/useTokenBalances.d.ts.map +1 -1
- package/dist/esm/hooks/useTokenBalances.js +7 -2
- package/dist/esm/hooks/useTokenBalances.js.map +1 -1
- package/dist/esm/hooks/useTokenBalancesQueries.js +29 -28
- package/dist/esm/hooks/useTokenBalancesQueries.js.map +1 -1
- package/dist/esm/hooks/useTokenSearch.js +3 -5
- package/dist/esm/hooks/useTokenSearch.js.map +1 -1
- package/dist/esm/hooks/useTokens.js +14 -10
- package/dist/esm/hooks/useTokens.js.map +1 -1
- package/dist/esm/hooks/useWideVariant.js +2 -1
- package/dist/esm/hooks/useWideVariant.js.map +1 -1
- package/dist/esm/i18n/bn.json +3 -1
- package/dist/esm/i18n/de.json +3 -1
- package/dist/esm/i18n/en.json +12 -3
- package/dist/esm/i18n/es.json +3 -1
- package/dist/esm/i18n/fr.json +3 -1
- package/dist/esm/i18n/hi.json +3 -1
- package/dist/esm/i18n/id.json +3 -1
- package/dist/esm/i18n/it.json +3 -1
- package/dist/esm/i18n/ja.json +3 -1
- package/dist/esm/i18n/ko.json +3 -1
- package/dist/esm/i18n/pl.json +3 -1
- package/dist/esm/i18n/pt.json +3 -1
- package/dist/esm/i18n/th.json +3 -1
- package/dist/esm/i18n/tr.json +3 -1
- package/dist/esm/i18n/uk.json +3 -1
- package/dist/esm/i18n/vi.json +3 -1
- package/dist/esm/i18n/zh.json +3 -1
- package/dist/esm/pages/MainPage/MainPage.js +2 -1
- package/dist/esm/pages/MainPage/MainPage.js.map +1 -1
- package/dist/esm/pages/MainPage/ReviewButton.js +2 -2
- package/dist/esm/pages/MainPage/ReviewButton.js.map +1 -1
- package/dist/esm/pages/RoutesPage/RoutesPage.js +2 -2
- package/dist/esm/pages/RoutesPage/RoutesPage.js.map +1 -1
- package/dist/esm/pages/SelectChainPage/SelectChainPage.js +13 -12
- package/dist/esm/pages/SelectChainPage/SelectChainPage.js.map +1 -1
- package/dist/esm/pages/SelectEnabledToolsPage.js +8 -5
- package/dist/esm/pages/SelectEnabledToolsPage.js.map +1 -1
- package/dist/esm/pages/SelectTokenPage/SelectTokenPage.js +2 -1
- package/dist/esm/pages/SelectTokenPage/SelectTokenPage.js.map +1 -1
- package/dist/esm/pages/SettingsPage/SmallBalanceFilterSettings.js +2 -1
- package/dist/esm/pages/SettingsPage/SmallBalanceFilterSettings.js.map +1 -1
- package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.js +2 -1
- package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.js.map +1 -1
- package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js +2 -1
- package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js.map +1 -1
- package/dist/esm/pages/TransactionPage/StartTransactionButton.js +4 -3
- package/dist/esm/pages/TransactionPage/StartTransactionButton.js.map +1 -1
- package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js +64 -0
- package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js.map +1 -0
- package/dist/esm/pages/TransactionPage/TransactionExecutionContent.js +19 -16
- package/dist/esm/pages/TransactionPage/TransactionExecutionContent.js.map +1 -1
- package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js +3 -1
- package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js.map +1 -1
- package/dist/esm/pages/TransactionPage/TransactionReview.js +24 -2
- package/dist/esm/pages/TransactionPage/TransactionReview.js.map +1 -1
- package/dist/esm/providers/I18nProvider/I18nProvider.js +37 -36
- package/dist/esm/providers/I18nProvider/I18nProvider.js.map +1 -1
- package/dist/esm/providers/ThemeProvider/ThemeProvider.js +2 -1
- package/dist/esm/providers/ThemeProvider/ThemeProvider.js.map +1 -1
- package/dist/esm/providers/WalletProvider/WalletProvider.js +10 -9
- package/dist/esm/providers/WalletProvider/WalletProvider.js.map +1 -1
- package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js +7 -3
- package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js.map +1 -1
- package/dist/esm/stores/routes/useRouteExecutionIndicator.js +4 -3
- package/dist/esm/stores/routes/useRouteExecutionIndicator.js.map +1 -1
- package/dist/esm/types/token.d.ts +4 -1
- package/dist/esm/types/token.d.ts.map +1 -1
- package/dist/esm/utils/compactNumberFormatter.js +1 -3
- package/dist/esm/utils/compactNumberFormatter.js.map +1 -1
- package/dist/esm/utils/customReceiver.js +7 -0
- package/dist/esm/utils/customReceiver.js.map +1 -0
- package/dist/esm/utils/gas.js +24 -0
- package/dist/esm/utils/gas.js.map +1 -0
- package/dist/esm/utils/getErrorMessage.js +0 -1
- package/dist/esm/utils/getErrorMessage.js.map +1 -1
- package/dist/esm/utils/limitOrder.js +1 -1
- package/dist/esm/utils/limitOrder.js.map +1 -1
- package/dist/esm/utils/token.js +21 -4
- package/dist/esm/utils/token.js.map +1 -1
- package/dist/esm/utils/tokenList.js +38 -21
- package/dist/esm/utils/tokenList.js.map +1 -1
- package/package.json +15 -15
- package/src/components/AmountInput/AmountInputEndAdornment.tsx +2 -21
- package/src/components/AmountInputCard/PercentageChips.tsx +2 -18
- package/src/components/Chains/AllChainsAvatar.tsx +7 -1
- package/src/components/Messages/UnsupportedToAddressMessage.tsx +24 -0
- package/src/components/Messages/WarningMessages.tsx +3 -0
- package/src/components/Messages/useMessageQueue.ts +9 -0
- package/src/components/Routes/LimitOrderRoutes.tsx +5 -2
- package/src/components/Routes/RoutesContent.tsx +5 -2
- package/src/components/SendToWallet/SendToWalletExpandButton.tsx +2 -1
- package/src/components/TokenList/TokenListItem.tsx +68 -9
- package/src/components/TokenList/VirtualizedTokenList.tsx +33 -9
- package/src/components/TokenList/types.ts +4 -0
- package/src/config/version.ts +1 -1
- package/src/hooks/useAvailableChains.ts +1 -0
- package/src/hooks/useExplorer.ts +2 -0
- package/src/hooks/useFlaggedTokenGuard.ts +33 -0
- package/src/hooks/useGasSufficiency.ts +9 -1
- package/src/hooks/useMaxSendAmount.ts +21 -0
- package/src/hooks/useRoutes.ts +7 -0
- package/src/hooks/useToAddressRequirements.ts +17 -1
- package/src/hooks/useTokenBalances.ts +18 -2
- package/src/hooks/useTokenSearch.ts +1 -5
- package/src/hooks/useTokens.ts +28 -13
- package/src/pages/MainPage/ReviewButton.tsx +6 -2
- package/src/pages/RoutesPage/RoutesPage.tsx +5 -2
- package/src/pages/TransactionPage/TokenVerificationBottomSheet.tsx +93 -0
- package/src/pages/TransactionPage/TransactionReview.tsx +28 -1
- package/src/providers/WalletProvider/useExternalWalletProvider.ts +9 -1
- package/src/types/token.ts +6 -3
- package/src/utils/customReceiver.ts +7 -0
- package/src/utils/gas.ts +41 -0
- package/src/utils/token.ts +53 -9
- package/src/utils/tokenList.ts +28 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limitOrder.js","names":[],"sources":["../../../src/utils/limitOrder.ts"],"sourcesContent":["import { formatInputAmount } from './format.js'\n\n/** Default order validity: 1 day, expressed as a duration in seconds. */\nexport const DEFAULT_VALID_UNTIL_SECONDS: number = 24 * 60 * 60\n\n/**\n * High-precision formatter for derived limit-order values. Mirrors the\n * approach used by `priceToTokenAmount` — these are display-level derivations;\n * the route request re-parses amounts via `parseUnits`.\n */\nconst preciseFormatter = new Intl.NumberFormat('en', {\n notation: 'standard',\n roundingPriority: 'morePrecision',\n maximumSignificantDigits: 20,\n maximumFractionDigits: 20,\n useGrouping: false,\n})\n\nfunction parsePositive(value?: string): number | undefined {\n if (!value) {\n return undefined\n }\n const parsed = Number.parseFloat(value)\n if (Number.isNaN(parsed) || parsed <= 0) {\n return undefined\n }\n return parsed\n}\n\n/**\n * Derive the receive amount: `receiveAmount = sendAmount × limitPrice`.\n * Returns an empty string when either input is missing or invalid.\n */\nexport function deriveReceiveAmount(\n sendAmount?: string,\n limitPrice?: string,\n toDecimals?: number\n): string {\n const send = parsePositive(sendAmount)\n const price = parsePositive(limitPrice)\n if (send === undefined || price === undefined) {\n return ''\n }\n return formatInputAmount(\n preciseFormatter.format(send * price),\n toDecimals ?? null\n )\n}\n\n/**\n * Derive the limit price: `limitPrice = receiveAmount ÷ sendAmount`.\n * Returns an empty string when either input is missing or invalid.\n */\nexport function deriveLimitPrice(\n sendAmount?: string,\n receiveAmount?: string\n): string {\n const send = parsePositive(sendAmount)\n const receive = parsePositive(receiveAmount)\n if (send === undefined || receive === undefined) {\n return ''\n }\n return preciseFormatter.format(receive / send)\n}\n\n/** Invert a price for display flipping: `1 ÷ price`. */\nexport function invertPrice(price?: string): string {\n const parsed = parsePositive(price)\n if (parsed === undefined) {\n return ''\n }\n return preciseFormatter.format(1 / parsed)\n}\n\n/**\n * Apply a percentage offset to a price, e.g. `percent = 2` → `price × 1.02`.\n * Used by the quick-set buttons (+2%, +5%).\n */\nexport function applyPriceOffset(\n price: string | undefined,\n percent: number\n): string {\n const parsed = parsePositive(price)\n if (parsed === undefined) {\n return ''\n }\n return preciseFormatter.format(parsed * (1 + percent / 100))\n}\n"],"mappings":";;;AAGA,MAAa,8BAAsC
|
|
1
|
+
{"version":3,"file":"limitOrder.js","names":[],"sources":["../../../src/utils/limitOrder.ts"],"sourcesContent":["import { formatInputAmount } from './format.js'\n\n/** Default order validity: 1 day, expressed as a duration in seconds. */\nexport const DEFAULT_VALID_UNTIL_SECONDS: number = 24 * 60 * 60\n\n/**\n * High-precision formatter for derived limit-order values. Mirrors the\n * approach used by `priceToTokenAmount` — these are display-level derivations;\n * the route request re-parses amounts via `parseUnits`.\n */\nconst preciseFormatter = new Intl.NumberFormat('en', {\n notation: 'standard',\n roundingPriority: 'morePrecision',\n maximumSignificantDigits: 20,\n maximumFractionDigits: 20,\n useGrouping: false,\n})\n\nfunction parsePositive(value?: string): number | undefined {\n if (!value) {\n return undefined\n }\n const parsed = Number.parseFloat(value)\n if (Number.isNaN(parsed) || parsed <= 0) {\n return undefined\n }\n return parsed\n}\n\n/**\n * Derive the receive amount: `receiveAmount = sendAmount × limitPrice`.\n * Returns an empty string when either input is missing or invalid.\n */\nexport function deriveReceiveAmount(\n sendAmount?: string,\n limitPrice?: string,\n toDecimals?: number\n): string {\n const send = parsePositive(sendAmount)\n const price = parsePositive(limitPrice)\n if (send === undefined || price === undefined) {\n return ''\n }\n return formatInputAmount(\n preciseFormatter.format(send * price),\n toDecimals ?? null\n )\n}\n\n/**\n * Derive the limit price: `limitPrice = receiveAmount ÷ sendAmount`.\n * Returns an empty string when either input is missing or invalid.\n */\nexport function deriveLimitPrice(\n sendAmount?: string,\n receiveAmount?: string\n): string {\n const send = parsePositive(sendAmount)\n const receive = parsePositive(receiveAmount)\n if (send === undefined || receive === undefined) {\n return ''\n }\n return preciseFormatter.format(receive / send)\n}\n\n/** Invert a price for display flipping: `1 ÷ price`. */\nexport function invertPrice(price?: string): string {\n const parsed = parsePositive(price)\n if (parsed === undefined) {\n return ''\n }\n return preciseFormatter.format(1 / parsed)\n}\n\n/**\n * Apply a percentage offset to a price, e.g. `percent = 2` → `price × 1.02`.\n * Used by the quick-set buttons (+2%, +5%).\n */\nexport function applyPriceOffset(\n price: string | undefined,\n percent: number\n): string {\n const parsed = parsePositive(price)\n if (parsed === undefined) {\n return ''\n }\n return preciseFormatter.format(parsed * (1 + percent / 100))\n}\n"],"mappings":";;;AAGA,MAAa,8BAAsC;;;;;;AAOnD,MAAM,mBAAmB,IAAI,KAAK,aAAa,MAAM;CACnD,UAAU;CACV,kBAAkB;CAClB,0BAA0B;CAC1B,uBAAuB;CACvB,aAAa;AACf,CAAC;AAED,SAAS,cAAc,OAAoC;CACzD,IAAI,CAAC,OACH;CAEF,MAAM,SAAS,OAAO,WAAW,KAAK;CACtC,IAAI,OAAO,MAAM,MAAM,KAAK,UAAU,GACpC;CAEF,OAAO;AACT;;;;;AAMA,SAAgB,oBACd,YACA,YACA,YACQ;CACR,MAAM,OAAO,cAAc,UAAU;CACrC,MAAM,QAAQ,cAAc,UAAU;CACtC,IAAI,SAAS,KAAA,KAAa,UAAU,KAAA,GAClC,OAAO;CAET,OAAO,kBACL,iBAAiB,OAAO,OAAO,KAAK,GACpC,cAAc,IAChB;AACF;;;;;AAMA,SAAgB,iBACd,YACA,eACQ;CACR,MAAM,OAAO,cAAc,UAAU;CACrC,MAAM,UAAU,cAAc,aAAa;CAC3C,IAAI,SAAS,KAAA,KAAa,YAAY,KAAA,GACpC,OAAO;CAET,OAAO,iBAAiB,OAAO,UAAU,IAAI;AAC/C;;AAGA,SAAgB,YAAY,OAAwB;CAClD,MAAM,SAAS,cAAc,KAAK;CAClC,IAAI,WAAW,KAAA,GACb,OAAO;CAET,OAAO,iBAAiB,OAAO,IAAI,MAAM;AAC3C;;;;;AAMA,SAAgB,iBACd,OACA,SACQ;CACR,MAAM,SAAS,cAAc,KAAK;CAClC,IAAI,WAAW,KAAA,GACb,OAAO;CAET,OAAO,iBAAiB,OAAO,UAAU,IAAI,UAAU,IAAI;AAC7D"}
|
package/dist/esm/utils/token.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getConfigItemSets, isFormItemAllowed } from "./item.js";
|
|
2
|
+
import { ChainId } from "@lifi/sdk";
|
|
2
3
|
//#region src/utils/token.ts
|
|
3
4
|
/**
|
|
4
5
|
* Builds per-chain sets of lowercase token addresses from the
|
|
@@ -53,7 +54,7 @@ const updateTokenInCache = (data, token) => {
|
|
|
53
54
|
} : t)
|
|
54
55
|
};
|
|
55
56
|
};
|
|
56
|
-
const filterAllowedTokens = (dataTokens, configTokens, chainsConfig, formType) => {
|
|
57
|
+
const filterAllowedTokens = (dataTokens, configTokens, chainsConfig, formType, nativeTokenAddresses) => {
|
|
57
58
|
if (!dataTokens) return;
|
|
58
59
|
const includedTokens = configTokens?.include || [];
|
|
59
60
|
const allChainIds = Array.from(/* @__PURE__ */ new Set([...includedTokens.map((t) => t.chainId), ...Object.keys(dataTokens)])).map((chainId) => Number(chainId));
|
|
@@ -66,6 +67,7 @@ const filterAllowedTokens = (dataTokens, configTokens, chainsConfig, formType) =
|
|
|
66
67
|
const includedAddresses = new Set(chainIncludedTokens.map((t) => t.address.toLowerCase()));
|
|
67
68
|
const allowedAddresses = getConfigItemSets(configTokens, (tokens) => new Set(tokens.filter((t) => Number(t.chainId) === chainId).map((t) => t.address.toLowerCase())), formType);
|
|
68
69
|
const verifiedAddresses = verifiedTokensSets?.get(chainId);
|
|
70
|
+
const nativeAddress = nativeTokenAddresses?.get(chainId)?.toLowerCase();
|
|
69
71
|
const chainTokens = [...dataTokens[chainId] ?? [], ...chainIncludedTokens];
|
|
70
72
|
const filtered = [];
|
|
71
73
|
const seenAddresses = /* @__PURE__ */ new Set();
|
|
@@ -74,9 +76,12 @@ const filterAllowedTokens = (dataTokens, configTokens, chainsConfig, formType) =
|
|
|
74
76
|
if (seenAddresses.has(address)) continue;
|
|
75
77
|
seenAddresses.add(address);
|
|
76
78
|
if (!isFormItemAllowed(token, allowedAddresses, formType, (t) => t.address.toLowerCase())) continue;
|
|
77
|
-
|
|
79
|
+
const markVerified = !token.verified && (includedAddresses.has(address) || verifiedAddresses?.has(address));
|
|
80
|
+
const markNative = !!nativeAddress && nativeAddress === address;
|
|
81
|
+
if (markVerified || markNative) filtered.push({
|
|
78
82
|
...token,
|
|
79
|
-
verified: true
|
|
83
|
+
...markVerified ? { verified: true } : {},
|
|
84
|
+
...markNative ? { native: true } : {}
|
|
80
85
|
});
|
|
81
86
|
else filtered.push(token);
|
|
82
87
|
}
|
|
@@ -88,7 +93,19 @@ const getExecutionToToken = (route) => ({
|
|
|
88
93
|
...route.steps.at(-1)?.execution?.toToken ?? route.toToken,
|
|
89
94
|
amount: BigInt(route.steps.at(-1)?.execution?.toAmount ?? route.steps.at(-1)?.estimate.toAmount ?? route.toAmount)
|
|
90
95
|
});
|
|
96
|
+
/** Capitalized name of the first provider that verified the token. */
|
|
97
|
+
const getTokenVerificationProvider = (token) => {
|
|
98
|
+
const provider = token.verificationStatusBreakdown?.find((entry) => entry.result === "verified")?.provider;
|
|
99
|
+
return provider ? `${provider.charAt(0).toUpperCase()}${provider.slice(1)}` : void 0;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Hyperliquid declares Arbitrum USDC as its native token, Lighter declares
|
|
103
|
+
* Ethereum USDC. Neither is a gas token you hold there. A chain-local
|
|
104
|
+
* stablecoin gas token, as on Arc or Stable, is genuine and stays.
|
|
105
|
+
*/
|
|
106
|
+
const chainIdsWithoutNativeToken = /* @__PURE__ */ new Set([ChainId.HPL, ChainId.LTR]);
|
|
107
|
+
const getNativeTokenAddresses = (chains) => new Map(chains?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id)).map((chain) => [chain.id, chain.nativeToken.address]));
|
|
91
108
|
//#endregion
|
|
92
|
-
export { filterAllowedTokens, getExecutionToToken, getVerifiedTokensSets, mergeVerifiedWithSearchTokens, updateTokenInCache };
|
|
109
|
+
export { filterAllowedTokens, getExecutionToToken, getNativeTokenAddresses, getTokenVerificationProvider, getVerifiedTokensSets, mergeVerifiedWithSearchTokens, updateTokenInCache };
|
|
93
110
|
|
|
94
111
|
//# sourceMappingURL=token.js.map
|
|
@@ -1 +1 @@
|
|
|
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"}
|
|
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} 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 { 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 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 = 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 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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"}
|
|
@@ -3,30 +3,32 @@ import { formatUnits } from "@lifi/sdk";
|
|
|
3
3
|
const sortByBalances = (a, b) => Number.parseFloat(formatUnits(b.amount ?? 0n, b.decimals)) * Number.parseFloat(b.priceUSD ?? "0") - Number.parseFloat(formatUnits(a.amount ?? 0n, a.decimals)) * Number.parseFloat(a.priceUSD ?? "0");
|
|
4
4
|
const sortByVolume = (a, b) => (b.volumeUSD24H ?? 0) - (a.volumeUSD24H ?? 0);
|
|
5
5
|
const processTokenList = (withoutBalances, noCategories, configTokens, selectedChainId, tokens, tokensWithBalances, isPinnedToken) => {
|
|
6
|
-
if (withoutBalances)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
if (withoutBalances) {
|
|
7
|
+
if (noCategories) {
|
|
8
|
+
const sortedTokens = [...tokens ?? []].sort(sortByVolume);
|
|
9
|
+
if (isPinnedToken) {
|
|
10
|
+
const pinned = [];
|
|
11
|
+
const notPinned = [];
|
|
12
|
+
for (const token of sortedTokens) if (isPinnedToken(token.chainId, token.address)) {
|
|
13
|
+
const pinnedToken = {
|
|
14
|
+
...token,
|
|
15
|
+
pinned: true
|
|
16
|
+
};
|
|
17
|
+
pinned.push(pinnedToken);
|
|
18
|
+
} else notPinned.push(token);
|
|
19
|
+
return {
|
|
20
|
+
processedTokens: [...pinned, ...notPinned],
|
|
21
|
+
withCategories: false,
|
|
22
|
+
withPinnedTokens: !!pinned.length
|
|
15
23
|
};
|
|
16
|
-
|
|
17
|
-
} else notPinned.push(token);
|
|
24
|
+
}
|
|
18
25
|
return {
|
|
19
|
-
processedTokens:
|
|
26
|
+
processedTokens: sortedTokens,
|
|
20
27
|
withCategories: false,
|
|
21
|
-
withPinnedTokens:
|
|
28
|
+
withPinnedTokens: false
|
|
22
29
|
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
processedTokens: sortedTokens,
|
|
26
|
-
withCategories: false,
|
|
27
|
-
withPinnedTokens: false
|
|
28
|
-
};
|
|
29
|
-
} else return processedTypedTokens(tokens ?? [], [], selectedChainId, configTokens, isPinnedToken);
|
|
30
|
+
} else return processedTypedTokens(tokens ?? [], [], selectedChainId, configTokens, isPinnedToken);
|
|
31
|
+
}
|
|
30
32
|
const sortedTokensWithBalances = [...tokensWithBalances ?? []].sort(sortByBalances);
|
|
31
33
|
const tokensWithBalancesSet = new Set(sortedTokensWithBalances.map((token) => `${token.chainId}-${token.address.toLowerCase()}`));
|
|
32
34
|
const tokensWithoutBalances = tokens?.filter((token) => {
|
|
@@ -138,7 +140,22 @@ const isSearchMatch = (token, search) => {
|
|
|
138
140
|
const searchLowerCase = search.toLowerCase();
|
|
139
141
|
return token.name?.toLowerCase().includes(searchLowerCase) || token.symbol?.replaceAll("₮", "T").toLowerCase().includes(searchLowerCase) || token.address?.toLowerCase().includes(searchLowerCase);
|
|
140
142
|
};
|
|
143
|
+
/**
|
|
144
|
+
* Moves the chain's native token to the top of its own chain's list, unless an
|
|
145
|
+
* external list already places it. Returns the array unchanged when nothing
|
|
146
|
+
* moves, so a memo downstream keeps its reference.
|
|
147
|
+
*/
|
|
148
|
+
const hoistNativeToken = (tokens, selectedChainId) => {
|
|
149
|
+
if (!selectedChainId) return tokens;
|
|
150
|
+
const index = tokens.findIndex((token) => token.native && token.chainId === selectedChainId && !token.featured && !token.popular && !token.pinned && !token.verified);
|
|
151
|
+
if (index < 1) return tokens;
|
|
152
|
+
return [
|
|
153
|
+
tokens[index],
|
|
154
|
+
...tokens.slice(0, index),
|
|
155
|
+
...tokens.slice(index + 1)
|
|
156
|
+
];
|
|
157
|
+
};
|
|
141
158
|
//#endregion
|
|
142
|
-
export { isSearchMatch, processTokenList };
|
|
159
|
+
export { hoistNativeToken, isSearchMatch, processTokenList };
|
|
143
160
|
|
|
144
161
|
//# sourceMappingURL=tokenList.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenList.js","names":[],"sources":["../../../src/utils/tokenList.ts"],"sourcesContent":["import { formatUnits, type TokenExtended } from '@lifi/sdk'\nimport type { TokenAmount, TokenAmountExtended } from '../types/token.js'\nimport type { WidgetTokens } from '../types/widget.js'\n\nconst sortByBalances = (a: TokenAmount, b: TokenAmount) =>\n Number.parseFloat(formatUnits(b.amount ?? 0n, b.decimals)) *\n Number.parseFloat(b.priceUSD ?? '0') -\n Number.parseFloat(formatUnits(a.amount ?? 0n, a.decimals)) *\n Number.parseFloat(a.priceUSD ?? '0')\n\nconst sortByVolume = (a: TokenExtended, b: TokenExtended) =>\n (b.volumeUSD24H ?? 0) - (a.volumeUSD24H ?? 0)\n\nexport const processTokenList = (\n withoutBalances: boolean,\n noCategories: boolean,\n configTokens?: WidgetTokens,\n selectedChainId?: number,\n tokens?: TokenExtended[],\n tokensWithBalances?: TokenAmount[],\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n): {\n processedTokens: TokenAmount[]\n withCategories: boolean\n withPinnedTokens: boolean\n} => {\n if (withoutBalances) {\n if (noCategories) {\n const sortedTokens = [...(tokens ?? [])].sort(sortByVolume)\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinned: TokenAmount[] = []\n const notPinned: TokenAmount[] = []\n for (const token of sortedTokens) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinned.push(pinnedToken)\n } else {\n notPinned.push(token)\n }\n }\n return {\n processedTokens: [...pinned, ...notPinned],\n withCategories: false,\n withPinnedTokens: !!pinned.length,\n }\n }\n return {\n processedTokens: sortedTokens,\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokens ?? [],\n [],\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n }\n\n const sortedTokensWithBalances = [...(tokensWithBalances ?? [])].sort(\n sortByBalances\n )\n\n const tokensWithBalancesSet = new Set(\n sortedTokensWithBalances.map(\n (token) => `${token.chainId}-${token.address.toLowerCase()}`\n )\n )\n const tokensWithoutBalances =\n tokens\n ?.filter((token) => {\n const tokenKey = `${token.chainId}-${token.address.toLowerCase()}`\n return !tokensWithBalancesSet.has(tokenKey)\n })\n .sort(sortByVolume) ?? []\n\n if (noCategories) {\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinnedWithBalances: TokenAmount[] = []\n const notPinnedWithBalances: TokenAmount[] = []\n const pinnedWithoutBalances: TokenAmount[] = []\n const notPinnedWithoutBalances: TokenAmount[] = []\n\n for (const token of sortedTokensWithBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithBalances.push(pinnedToken)\n } else {\n notPinnedWithBalances.push(token)\n }\n }\n\n for (const token of tokensWithoutBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithoutBalances.push(pinnedToken)\n } else {\n notPinnedWithoutBalances.push(token)\n }\n }\n\n return {\n processedTokens: [\n ...pinnedWithBalances,\n ...pinnedWithoutBalances,\n ...notPinnedWithBalances,\n ...notPinnedWithoutBalances,\n ],\n withCategories: false,\n withPinnedTokens:\n !!pinnedWithBalances.length || !!pinnedWithoutBalances.length,\n }\n }\n return {\n processedTokens: [...sortedTokensWithBalances, ...tokensWithoutBalances],\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokensWithoutBalances,\n sortedTokensWithBalances,\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n}\n\n// NB: only for non-all-networks\nconst processedTypedTokens = (\n tokens: TokenAmount[],\n tokensWithBalances: TokenAmount[],\n selectedChainId?: number,\n configTokens?: WidgetTokens,\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n) => {\n const filteredTokensMap = new Map(\n tokens.map((token) => [token.address, token])\n )\n\n const pinnedTokens: TokenAmount[] = []\n const featuredTokensFromConfig: TokenAmount[] = []\n const popularTokensFromConfig: TokenAmount[] = []\n\n ;(['popular', 'featured'] as const).forEach((tokenType) => {\n const typedTokens = configTokens?.[tokenType]?.filter(\n (token) => token.chainId === selectedChainId\n )\n\n typedTokens?.forEach((token) => {\n const tokenAmount = { ...token } as TokenAmount\n tokenAmount[tokenType] = true\n // Config tokens are explicitly set by integrator, mark as verified\n tokenAmount.verified = true\n\n const match = filteredTokensMap.get(token.address)\n if (match?.priceUSD) {\n tokenAmount.priceUSD = match.priceUSD\n }\n if (!token.logoURI && match?.logoURI) {\n tokenAmount.logoURI = match.logoURI\n }\n\n if (tokenType === 'popular') {\n popularTokensFromConfig.push(tokenAmount)\n } else {\n featuredTokensFromConfig.push(tokenAmount)\n }\n\n // Additionally add to pinned tokens if it is pinned\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n if (isPinned) {\n const pinnedToken = { ...tokenAmount, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n }\n })\n })\n\n // Filter out config-added tokens from main list\n const configTokenAddresses = new Set(\n [...popularTokensFromConfig, ...featuredTokensFromConfig].map((t) =>\n t.address.toLowerCase()\n )\n )\n\n const remainingTokens = tokens.filter(\n (token) => !configTokenAddresses.has(token.address.toLowerCase())\n )\n\n const otherTokens: TokenAmount[] = []\n\n // Separate pinned tokens and categorize remaining tokens\n for (const token of remainingTokens) {\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n\n if (isPinned) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n } else if (token.featured) {\n featuredTokensFromConfig.push(token)\n } else if (token.popular) {\n popularTokensFromConfig.push(token)\n } else {\n otherTokens.push(token)\n }\n }\n\n // Also check tokens with balances for pinned status\n const pinnedTokensWithBalances: TokenAmount[] = []\n const nonPinnedTokensWithBalances: TokenAmount[] = []\n\n if (isPinnedToken && selectedChainId) {\n for (const token of tokensWithBalances) {\n if (isPinnedToken(selectedChainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokensWithBalances.push(pinnedToken)\n } else {\n nonPinnedTokensWithBalances.push(token)\n }\n }\n } else {\n nonPinnedTokensWithBalances.push(...tokensWithBalances)\n }\n\n const sortedPinnedTokens = [\n ...pinnedTokens,\n ...pinnedTokensWithBalances,\n ].sort(sortByVolume)\n const sortedFeaturedTokens = [...featuredTokensFromConfig].sort(sortByVolume)\n const sortedPopularTokens = [...popularTokensFromConfig].sort(sortByVolume)\n const sortedOtherTokens = [...otherTokens].sort(sortByVolume)\n\n return {\n processedTokens: [\n ...sortedPinnedTokens,\n ...sortedFeaturedTokens,\n ...nonPinnedTokensWithBalances,\n ...sortedPopularTokens,\n ...sortedOtherTokens,\n ],\n withCategories:\n !!featuredTokensFromConfig.length || !!popularTokensFromConfig.length,\n withPinnedTokens: !!sortedPinnedTokens.length,\n }\n}\n\nexport const isSearchMatch = (\n token: TokenExtended | TokenAmountExtended,\n search?: string\n): boolean => {\n if (!search) {\n return true\n }\n\n const searchLowerCase = search.toLowerCase()\n return (\n token.name?.toLowerCase().includes(searchLowerCase) ||\n token.symbol\n ?.replaceAll('₮', 'T')\n .toLowerCase()\n .includes(searchLowerCase) ||\n token.address?.toLowerCase().includes(searchLowerCase)\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tokenList.js","names":[],"sources":["../../../src/utils/tokenList.ts"],"sourcesContent":["import { formatUnits, type TokenExtended } from '@lifi/sdk'\nimport type { TokenAmount, TokenAmountExtended } from '../types/token.js'\nimport type { WidgetTokens } from '../types/widget.js'\n\nconst sortByBalances = (a: TokenAmount, b: TokenAmount) =>\n Number.parseFloat(formatUnits(b.amount ?? 0n, b.decimals)) *\n Number.parseFloat(b.priceUSD ?? '0') -\n Number.parseFloat(formatUnits(a.amount ?? 0n, a.decimals)) *\n Number.parseFloat(a.priceUSD ?? '0')\n\nconst sortByVolume = (a: TokenExtended, b: TokenExtended) =>\n (b.volumeUSD24H ?? 0) - (a.volumeUSD24H ?? 0)\n\nexport const processTokenList = (\n withoutBalances: boolean,\n noCategories: boolean,\n configTokens?: WidgetTokens,\n selectedChainId?: number,\n tokens?: TokenExtended[],\n tokensWithBalances?: TokenAmount[],\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n): {\n processedTokens: TokenAmount[]\n withCategories: boolean\n withPinnedTokens: boolean\n} => {\n if (withoutBalances) {\n if (noCategories) {\n const sortedTokens = [...(tokens ?? [])].sort(sortByVolume)\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinned: TokenAmount[] = []\n const notPinned: TokenAmount[] = []\n for (const token of sortedTokens) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinned.push(pinnedToken)\n } else {\n notPinned.push(token)\n }\n }\n return {\n processedTokens: [...pinned, ...notPinned],\n withCategories: false,\n withPinnedTokens: !!pinned.length,\n }\n }\n return {\n processedTokens: sortedTokens,\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokens ?? [],\n [],\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n }\n\n const sortedTokensWithBalances = [...(tokensWithBalances ?? [])].sort(\n sortByBalances\n )\n\n const tokensWithBalancesSet = new Set(\n sortedTokensWithBalances.map(\n (token) => `${token.chainId}-${token.address.toLowerCase()}`\n )\n )\n const tokensWithoutBalances =\n tokens\n ?.filter((token) => {\n const tokenKey = `${token.chainId}-${token.address.toLowerCase()}`\n return !tokensWithBalancesSet.has(tokenKey)\n })\n .sort(sortByVolume) ?? []\n\n if (noCategories) {\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinnedWithBalances: TokenAmount[] = []\n const notPinnedWithBalances: TokenAmount[] = []\n const pinnedWithoutBalances: TokenAmount[] = []\n const notPinnedWithoutBalances: TokenAmount[] = []\n\n for (const token of sortedTokensWithBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithBalances.push(pinnedToken)\n } else {\n notPinnedWithBalances.push(token)\n }\n }\n\n for (const token of tokensWithoutBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithoutBalances.push(pinnedToken)\n } else {\n notPinnedWithoutBalances.push(token)\n }\n }\n\n return {\n processedTokens: [\n ...pinnedWithBalances,\n ...pinnedWithoutBalances,\n ...notPinnedWithBalances,\n ...notPinnedWithoutBalances,\n ],\n withCategories: false,\n withPinnedTokens:\n !!pinnedWithBalances.length || !!pinnedWithoutBalances.length,\n }\n }\n return {\n processedTokens: [...sortedTokensWithBalances, ...tokensWithoutBalances],\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokensWithoutBalances,\n sortedTokensWithBalances,\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n}\n\n// NB: only for non-all-networks\nconst processedTypedTokens = (\n tokens: TokenAmount[],\n tokensWithBalances: TokenAmount[],\n selectedChainId?: number,\n configTokens?: WidgetTokens,\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n) => {\n const filteredTokensMap = new Map(\n tokens.map((token) => [token.address, token])\n )\n\n const pinnedTokens: TokenAmount[] = []\n const featuredTokensFromConfig: TokenAmount[] = []\n const popularTokensFromConfig: TokenAmount[] = []\n\n ;(['popular', 'featured'] as const).forEach((tokenType) => {\n const typedTokens = configTokens?.[tokenType]?.filter(\n (token) => token.chainId === selectedChainId\n )\n\n typedTokens?.forEach((token) => {\n const tokenAmount = { ...token } as TokenAmount\n tokenAmount[tokenType] = true\n // Config tokens are explicitly set by integrator, mark as verified\n tokenAmount.verified = true\n\n const match = filteredTokensMap.get(token.address)\n if (match?.priceUSD) {\n tokenAmount.priceUSD = match.priceUSD\n }\n if (!token.logoURI && match?.logoURI) {\n tokenAmount.logoURI = match.logoURI\n }\n\n if (tokenType === 'popular') {\n popularTokensFromConfig.push(tokenAmount)\n } else {\n featuredTokensFromConfig.push(tokenAmount)\n }\n\n // Additionally add to pinned tokens if it is pinned\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n if (isPinned) {\n const pinnedToken = { ...tokenAmount, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n }\n })\n })\n\n // Filter out config-added tokens from main list\n const configTokenAddresses = new Set(\n [...popularTokensFromConfig, ...featuredTokensFromConfig].map((t) =>\n t.address.toLowerCase()\n )\n )\n\n const remainingTokens = tokens.filter(\n (token) => !configTokenAddresses.has(token.address.toLowerCase())\n )\n\n const otherTokens: TokenAmount[] = []\n\n // Separate pinned tokens and categorize remaining tokens\n for (const token of remainingTokens) {\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n\n if (isPinned) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n } else if (token.featured) {\n featuredTokensFromConfig.push(token)\n } else if (token.popular) {\n popularTokensFromConfig.push(token)\n } else {\n otherTokens.push(token)\n }\n }\n\n // Also check tokens with balances for pinned status\n const pinnedTokensWithBalances: TokenAmount[] = []\n const nonPinnedTokensWithBalances: TokenAmount[] = []\n\n if (isPinnedToken && selectedChainId) {\n for (const token of tokensWithBalances) {\n if (isPinnedToken(selectedChainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokensWithBalances.push(pinnedToken)\n } else {\n nonPinnedTokensWithBalances.push(token)\n }\n }\n } else {\n nonPinnedTokensWithBalances.push(...tokensWithBalances)\n }\n\n const sortedPinnedTokens = [\n ...pinnedTokens,\n ...pinnedTokensWithBalances,\n ].sort(sortByVolume)\n const sortedFeaturedTokens = [...featuredTokensFromConfig].sort(sortByVolume)\n const sortedPopularTokens = [...popularTokensFromConfig].sort(sortByVolume)\n const sortedOtherTokens = [...otherTokens].sort(sortByVolume)\n\n return {\n processedTokens: [\n ...sortedPinnedTokens,\n ...sortedFeaturedTokens,\n ...nonPinnedTokensWithBalances,\n ...sortedPopularTokens,\n ...sortedOtherTokens,\n ],\n withCategories:\n !!featuredTokensFromConfig.length || !!popularTokensFromConfig.length,\n withPinnedTokens: !!sortedPinnedTokens.length,\n }\n}\n\nexport const isSearchMatch = (\n token: TokenExtended | TokenAmountExtended,\n search?: string\n): boolean => {\n if (!search) {\n return true\n }\n\n const searchLowerCase = search.toLowerCase()\n return (\n token.name?.toLowerCase().includes(searchLowerCase) ||\n token.symbol\n ?.replaceAll('₮', 'T')\n .toLowerCase()\n .includes(searchLowerCase) ||\n token.address?.toLowerCase().includes(searchLowerCase)\n )\n}\n\n/**\n * Moves the chain's native token to the top of its own chain's list, unless an\n * external list already places it. Returns the array unchanged when nothing\n * moves, so a memo downstream keeps its reference.\n */\nexport const hoistNativeToken = (\n tokens: TokenAmount[],\n selectedChainId?: number\n): TokenAmount[] => {\n if (!selectedChainId) {\n return tokens\n }\n const index = tokens.findIndex(\n (token) =>\n token.native &&\n token.chainId === selectedChainId &&\n !token.featured &&\n !token.popular &&\n !token.pinned &&\n !token.verified\n )\n // -1 is no native token, 0 is already first\n if (index < 1) {\n return tokens\n }\n return [tokens[index], ...tokens.slice(0, index), ...tokens.slice(index + 1)]\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.6.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",
|
|
@@ -52,22 +52,22 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@emotion/react": "^11.14.0",
|
|
54
54
|
"@emotion/styled": "^11.14.1",
|
|
55
|
-
"@lifi/sdk": "^4.
|
|
56
|
-
"@
|
|
57
|
-
"@
|
|
58
|
-
"@mui/
|
|
59
|
-
"@
|
|
60
|
-
"@
|
|
55
|
+
"@lifi/sdk": "^4.6.1",
|
|
56
|
+
"@lifi/wallet-management": "4.2.0",
|
|
57
|
+
"@lifi/widget-provider": "4.4.0",
|
|
58
|
+
"@mui/icons-material": "^9.4.0",
|
|
59
|
+
"@mui/material": "^9.4.0",
|
|
60
|
+
"@mui/system": "^9.4.0",
|
|
61
|
+
"@tanstack/react-router": "^1.170.32",
|
|
62
|
+
"@tanstack/react-virtual": "^3.14.10",
|
|
61
63
|
"eventemitter3": "^5.0.4",
|
|
62
|
-
"i18next": "^26.
|
|
63
|
-
"microdiff": "^1.
|
|
64
|
-
"motion": "^
|
|
65
|
-
"react-i18next": "^17.0.
|
|
66
|
-
"react-intersection-observer": "^
|
|
64
|
+
"i18next": "^26.4.1",
|
|
65
|
+
"microdiff": "^1.6.0",
|
|
66
|
+
"motion": "^13.2.0",
|
|
67
|
+
"react-i18next": "^17.0.13",
|
|
68
|
+
"react-intersection-observer": "^11.0.1",
|
|
67
69
|
"react-transition-group": "^4.4.5",
|
|
68
|
-
"zustand": "^5.0.
|
|
69
|
-
"@lifi/widget-provider": "4.3.1",
|
|
70
|
-
"@lifi/wallet-management": "4.1.3"
|
|
70
|
+
"zustand": "^5.0.15"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"@tanstack/react-query": ">=5.90.0",
|
|
@@ -4,7 +4,7 @@ import type React from 'react'
|
|
|
4
4
|
import { type JSX, memo } from 'react'
|
|
5
5
|
import { useTranslation } from 'react-i18next'
|
|
6
6
|
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
|
|
7
|
-
import {
|
|
7
|
+
import { useMaxSendAmount } from '../../hooks/useMaxSendAmount.js'
|
|
8
8
|
import { useTokenAddressBalance } from '../../hooks/useTokenAddressBalance.js'
|
|
9
9
|
import type { FormTypeProps } from '../../stores/form/types.js'
|
|
10
10
|
import { FormKeyHelper } from '../../stores/form/types.js'
|
|
@@ -29,28 +29,10 @@ export const AmountInputEndAdornment: React.NamedExoticComponent<FormTypeProps>
|
|
|
29
29
|
const chain = getChainById(chainId)
|
|
30
30
|
const { account } = useAccount({ chainType: chain?.chainType })
|
|
31
31
|
|
|
32
|
-
// We get gas recommendations for the source chain to make sure that after pressing the Max button
|
|
33
|
-
// the user will have enough funds remaining to cover gas costs
|
|
34
|
-
const { data } = useGasRecommendation(chainId)
|
|
35
|
-
|
|
36
32
|
const { token } = useTokenAddressBalance(chainId, tokenAddress)
|
|
37
|
-
|
|
38
|
-
const getMaxAmount = () => {
|
|
39
|
-
if (!token?.amount) {
|
|
40
|
-
return 0n
|
|
41
|
-
}
|
|
42
|
-
let maxAmount = token.amount
|
|
43
|
-
if (chain?.nativeToken.address === tokenAddress && data?.recommended) {
|
|
44
|
-
const recommendedAmount = BigInt(data.recommended.amount)
|
|
45
|
-
if (token.amount > recommendedAmount) {
|
|
46
|
-
maxAmount = token.amount - recommendedAmount
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return maxAmount
|
|
50
|
-
}
|
|
33
|
+
const maxAmount = useMaxSendAmount(chainId, tokenAddress)
|
|
51
34
|
|
|
52
35
|
const handlePercentage = (percentage: number) => {
|
|
53
|
-
const maxAmount = getMaxAmount()
|
|
54
36
|
if (maxAmount && token?.decimals) {
|
|
55
37
|
const percentageAmount = (maxAmount * BigInt(percentage)) / 100n
|
|
56
38
|
setFieldValue(
|
|
@@ -64,7 +46,6 @@ export const AmountInputEndAdornment: React.NamedExoticComponent<FormTypeProps>
|
|
|
64
46
|
}
|
|
65
47
|
|
|
66
48
|
const handleMax = () => {
|
|
67
|
-
const maxAmount = getMaxAmount()
|
|
68
49
|
if (maxAmount && token?.decimals) {
|
|
69
50
|
setFieldValue(
|
|
70
51
|
FormKeyHelper.getAmountKey(formType),
|
|
@@ -3,8 +3,8 @@ import { useAccount } from '@lifi/wallet-management'
|
|
|
3
3
|
import { type JSX, memo } from 'react'
|
|
4
4
|
import { useTranslation } from 'react-i18next'
|
|
5
5
|
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
|
|
6
|
-
import { useGasRecommendation } from '../../hooks/useGasRecommendation.js'
|
|
7
6
|
import { useLinkedLimitFields } from '../../hooks/useLinkedLimitFields.js'
|
|
7
|
+
import { useMaxSendAmount } from '../../hooks/useMaxSendAmount.js'
|
|
8
8
|
import { useTokenAddressBalance } from '../../hooks/useTokenAddressBalance.js'
|
|
9
9
|
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
|
|
10
10
|
import type { FormTypeProps } from '../../stores/form/types.js'
|
|
@@ -33,22 +33,8 @@ export const PercentageChips: React.NamedExoticComponent<FormTypeProps> = memo(
|
|
|
33
33
|
const chain = getChainById(chainId)
|
|
34
34
|
const { account } = useAccount({ chainType: chain?.chainType })
|
|
35
35
|
|
|
36
|
-
const { data } = useGasRecommendation(chainId)
|
|
37
36
|
const { token } = useTokenAddressBalance(chainId, tokenAddress)
|
|
38
|
-
|
|
39
|
-
const getMaxAmount = (): bigint => {
|
|
40
|
-
if (!token?.amount) {
|
|
41
|
-
return 0n
|
|
42
|
-
}
|
|
43
|
-
let maxAmount = token.amount
|
|
44
|
-
if (chain?.nativeToken.address === tokenAddress && data?.recommended) {
|
|
45
|
-
const recommendedAmount = BigInt(data.recommended.amount)
|
|
46
|
-
if (token.amount > recommendedAmount) {
|
|
47
|
-
maxAmount = token.amount - recommendedAmount
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return maxAmount
|
|
51
|
-
}
|
|
37
|
+
const maxAmount = useMaxSendAmount(chainId, tokenAddress)
|
|
52
38
|
|
|
53
39
|
// In limit mode the send amount must flow through the linked-field
|
|
54
40
|
// derivation so the receive amount recomputes; otherwise it is a plain
|
|
@@ -62,7 +48,6 @@ export const PercentageChips: React.NamedExoticComponent<FormTypeProps> = memo(
|
|
|
62
48
|
}
|
|
63
49
|
|
|
64
50
|
const handlePercentage = (percentage: number): void => {
|
|
65
|
-
const maxAmount = getMaxAmount()
|
|
66
51
|
if (maxAmount && token?.decimals) {
|
|
67
52
|
const percentageAmount = (maxAmount * BigInt(percentage)) / 100n
|
|
68
53
|
applyAmount(formatUnits(percentageAmount, token.decimals))
|
|
@@ -70,7 +55,6 @@ export const PercentageChips: React.NamedExoticComponent<FormTypeProps> = memo(
|
|
|
70
55
|
}
|
|
71
56
|
|
|
72
57
|
const handleMax = (): void => {
|
|
73
|
-
const maxAmount = getMaxAmount()
|
|
74
58
|
if (maxAmount && token?.decimals) {
|
|
75
59
|
applyAmount(formatUnits(maxAmount, token.decimals))
|
|
76
60
|
}
|
|
@@ -44,6 +44,12 @@ const chainTypeIcons = [
|
|
|
44
44
|
icon: 'https://lifinance.github.io/types/src/assets/icons/chains/tron.svg',
|
|
45
45
|
defaultChainId: ChainId.TRN,
|
|
46
46
|
},
|
|
47
|
+
{
|
|
48
|
+
name: 'Stellar',
|
|
49
|
+
chainType: ChainType.STL,
|
|
50
|
+
icon: 'https://lifinance.github.io/types/src/assets/icons/chains/stellar.svg',
|
|
51
|
+
defaultChainId: ChainId.XLM,
|
|
52
|
+
},
|
|
47
53
|
]
|
|
48
54
|
|
|
49
55
|
const maxChainAvatarsCount = 4
|
|
@@ -93,7 +99,7 @@ export const AllChainsAvatar: React.NamedExoticComponent<AllChainsAvatarProps> =
|
|
|
93
99
|
}
|
|
94
100
|
)
|
|
95
101
|
|
|
96
|
-
if (existingChainTypeIcons.length
|
|
102
|
+
if (existingChainTypeIcons.length >= maxChainAvatarsCount) {
|
|
97
103
|
return existingChainTypeIcons
|
|
98
104
|
}
|
|
99
105
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import WarningRounded from '@mui/icons-material/WarningRounded'
|
|
2
|
+
import type { BoxProps } from '@mui/material'
|
|
3
|
+
import { Typography } from '@mui/material'
|
|
4
|
+
import { useTranslation } from 'react-i18next'
|
|
5
|
+
import { AlertMessage } from './AlertMessage.js'
|
|
6
|
+
|
|
7
|
+
export const UnsupportedToAddressMessage: React.FC<BoxProps> = ({
|
|
8
|
+
...props
|
|
9
|
+
}) => {
|
|
10
|
+
const { t } = useTranslation()
|
|
11
|
+
return (
|
|
12
|
+
<AlertMessage
|
|
13
|
+
title={
|
|
14
|
+
<Typography variant="body2" sx={{ px: 1, color: 'text.primary' }}>
|
|
15
|
+
{t('info.message.unsupportedToAddressMessage')}
|
|
16
|
+
</Typography>
|
|
17
|
+
}
|
|
18
|
+
icon={<WarningRounded />}
|
|
19
|
+
severity="warning"
|
|
20
|
+
multiline
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -7,6 +7,7 @@ import { FundsSufficiencyMessage } from './FundsSufficiencyMessage.js'
|
|
|
7
7
|
import { GasSufficiencyMessage } from './GasSufficiencyMessage.js'
|
|
8
8
|
import { MinFromAmountUSDMessage } from './MinFromAmountUSDMessage.js'
|
|
9
9
|
import { MissingRouteRequiredAccountMessage } from './MissingRouteRequiredAccountMessage.js'
|
|
10
|
+
import { UnsupportedToAddressMessage } from './UnsupportedToAddressMessage.js'
|
|
10
11
|
import { useMessageQueue } from './useMessageQueue.js'
|
|
11
12
|
|
|
12
13
|
type WarningMessagesProps = BoxProps & {
|
|
@@ -51,6 +52,8 @@ export const WarningMessages: React.FC<WarningMessagesProps> = ({
|
|
|
51
52
|
return <AccountNotDeployedMessage {...props} />
|
|
52
53
|
case 'ACCOUNT_DEPLOYED':
|
|
53
54
|
return <AccountDeployedMessage {...props} />
|
|
55
|
+
case 'UNSUPPORTED_TO_ADDRESS':
|
|
56
|
+
return <UnsupportedToAddressMessage {...props} />
|
|
54
57
|
default:
|
|
55
58
|
return null
|
|
56
59
|
}
|
|
@@ -22,6 +22,7 @@ export const useMessageQueue = (
|
|
|
22
22
|
} => {
|
|
23
23
|
const {
|
|
24
24
|
requiredToAddress,
|
|
25
|
+
unsupportedReceiverBlocking,
|
|
25
26
|
accountNotDeployedAtDestination,
|
|
26
27
|
accountDeployedAtDestination,
|
|
27
28
|
isLoading: isToAddressRequirementsLoading,
|
|
@@ -37,6 +38,13 @@ export const useMessageQueue = (
|
|
|
37
38
|
const messageQueue = useMemo(() => {
|
|
38
39
|
const queue: QueuedMessage[] = []
|
|
39
40
|
|
|
41
|
+
if (unsupportedReceiverBlocking) {
|
|
42
|
+
queue.push({
|
|
43
|
+
id: 'UNSUPPORTED_TO_ADDRESS',
|
|
44
|
+
priority: 7,
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
40
48
|
if (missingChain) {
|
|
41
49
|
queue.push({
|
|
42
50
|
id: 'MISSING_ROUTE_REQUIRED_ACCOUNT',
|
|
@@ -94,6 +102,7 @@ export const useMessageQueue = (
|
|
|
94
102
|
accountDeployedAtDestination,
|
|
95
103
|
accountNotDeployedAtDestination,
|
|
96
104
|
requiredToAddress,
|
|
105
|
+
unsupportedReceiverBlocking,
|
|
97
106
|
missingChain,
|
|
98
107
|
missingAccountAddress,
|
|
99
108
|
belowMinFromAmountUSD,
|
|
@@ -17,7 +17,8 @@ export const LimitOrderRoutes: React.FC<CardProps> = (props) => {
|
|
|
17
17
|
'selectedRouteId'
|
|
18
18
|
)
|
|
19
19
|
const { setFieldValue } = useFieldActions()
|
|
20
|
-
const { requiredToAddress } =
|
|
20
|
+
const { requiredToAddress, unsupportedReceiverBlocking } =
|
|
21
|
+
useToAddressRequirements()
|
|
21
22
|
|
|
22
23
|
const currentRoute = routes?.[0]
|
|
23
24
|
|
|
@@ -25,7 +26,9 @@ export const LimitOrderRoutes: React.FC<CardProps> = (props) => {
|
|
|
25
26
|
return null
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
const toAddressUnsatisfied =
|
|
29
|
+
const toAddressUnsatisfied =
|
|
30
|
+
currentRoute &&
|
|
31
|
+
(unsupportedReceiverBlocking || (requiredToAddress && !toAddress))
|
|
29
32
|
const allowInteraction = !toAddressUnsatisfied
|
|
30
33
|
|
|
31
34
|
const activeRouteId =
|
|
@@ -43,12 +43,15 @@ export const RoutesContent: React.NamedExoticComponent<RoutesContentProps> =
|
|
|
43
43
|
|
|
44
44
|
const { account } = useAccount({ chainType: fromChain?.chainType })
|
|
45
45
|
const [toAddress] = useFieldValues('toAddress')
|
|
46
|
-
const { requiredToAddress } =
|
|
46
|
+
const { requiredToAddress, unsupportedReceiverBlocking } =
|
|
47
|
+
useToAddressRequirements()
|
|
47
48
|
|
|
48
49
|
const currentRoute = routes?.[0]
|
|
49
50
|
|
|
50
51
|
const routeNotFound = !currentRoute && !isLoading && !isFetching
|
|
51
|
-
const toAddressUnsatisfied =
|
|
52
|
+
const toAddressUnsatisfied =
|
|
53
|
+
currentRoute &&
|
|
54
|
+
(unsupportedReceiverBlocking || (requiredToAddress && !toAddress))
|
|
52
55
|
const allowInteraction = account.isConnected && !toAddressUnsatisfied
|
|
53
56
|
|
|
54
57
|
const title =
|
|
@@ -16,12 +16,13 @@ export const SendToWalletExpandButton = (): JSX.Element => {
|
|
|
16
16
|
const { toAddresses, disabledUI, hiddenUI } = useWidgetConfig()
|
|
17
17
|
const hiddenToAddress = hiddenUI?.toAddress
|
|
18
18
|
const disabledToAddress = disabledUI?.toAddress
|
|
19
|
-
const { requiredToAddress } = useToAddressRequirements()
|
|
19
|
+
const { requiredToAddress, unsupportedToAddress } = useToAddressRequirements()
|
|
20
20
|
const [toAddressValue] = useFieldValues('toAddress')
|
|
21
21
|
|
|
22
22
|
const visible =
|
|
23
23
|
!hiddenToAddress &&
|
|
24
24
|
!disabledToAddress &&
|
|
25
|
+
!unsupportedToAddress &&
|
|
25
26
|
!toAddressValue &&
|
|
26
27
|
!requiredToAddress
|
|
27
28
|
|