@lifi/widget 4.10.2 → 4.10.3
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/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/useTransactionHistory.js +3 -1
- package/dist/esm/hooks/useTransactionHistory.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js +4 -1
- package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js.map +1 -1
- package/dist/esm/utils/converters.d.ts +2 -2
- package/dist/esm/utils/converters.d.ts.map +1 -1
- package/dist/esm/utils/converters.js +22 -14
- package/dist/esm/utils/converters.js.map +1 -1
- package/dist/esm/utils/format.d.ts +14 -0
- package/dist/esm/utils/format.d.ts.map +1 -1
- package/dist/esm/utils/format.js +25 -1
- package/dist/esm/utils/format.js.map +1 -1
- package/package.json +7 -7
- package/src/config/version.ts +1 -1
- package/src/hooks/useTransactionHistory.ts +4 -1
- package/src/pages/TransactionDetailsPage/TransactionDetailsPage.tsx +8 -2
- package/src/utils/converters.ts +51 -30
- package/src/utils/format.ts +31 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","names":[],"sources":["../../../src/config/version.ts"],"sourcesContent":["export const name = '@lifi/widget'\nexport const version = '4.10.
|
|
1
|
+
{"version":3,"file":"version.js","names":[],"sources":["../../../src/config/version.ts"],"sourcesContent":["export const name = '@lifi/widget'\nexport const version = '4.10.3'\n"],"mappings":";AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useWidgetConfig } from "../providers/WidgetProvider/WidgetProvider.js";
|
|
2
2
|
import { useSDKClient } from "../providers/SDKClientProvider.js";
|
|
3
|
+
import { useAvailableChains } from "./useAvailableChains.js";
|
|
3
4
|
import { getSourceTxHash } from "../stores/routes/utils.js";
|
|
4
5
|
import { useRouteExecutionStoreContext } from "../stores/routes/RouteExecutionStore.js";
|
|
5
6
|
import { getQueryKey } from "../utils/queries.js";
|
|
@@ -16,6 +17,7 @@ const useTransactionHistory = () => {
|
|
|
16
17
|
const { storageScopeKey } = useWidgetConfig();
|
|
17
18
|
const sdkClient = useSDKClient();
|
|
18
19
|
const { tools } = useTools();
|
|
20
|
+
const { getChainById } = useAvailableChains();
|
|
19
21
|
const { data: routeExecutions, isLoading } = useQueries({
|
|
20
22
|
queries: accounts.map((account) => ({
|
|
21
23
|
queryKey: [getQueryKey("transaction-history", storageScopeKey), account.address],
|
|
@@ -41,7 +43,7 @@ const useTransactionHistory = () => {
|
|
|
41
43
|
});
|
|
42
44
|
return {
|
|
43
45
|
data: Array.from(uniqueTransactions.values()).flatMap((transaction) => {
|
|
44
|
-
const routeExecution = buildRouteFromTxHistory(transaction, tools);
|
|
46
|
+
const routeExecution = buildRouteFromTxHistory(transaction, tools, getChainById(transaction.sending.chainId));
|
|
45
47
|
return routeExecution ? [routeExecution] : [];
|
|
46
48
|
}),
|
|
47
49
|
isLoading: results.some((result) => result.isLoading)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTransactionHistory.js","names":[],"sources":["../../../src/hooks/useTransactionHistory.ts"],"sourcesContent":["import type { FullStatusData, StatusResponse } from '@lifi/sdk'\nimport { getTransactionHistory } from '@lifi/sdk'\nimport { useAccount } from '@lifi/wallet-management'\nimport type { QueryFunction } from '@tanstack/react-query'\nimport { useQueries } from '@tanstack/react-query'\nimport { useEffect, useMemo } from 'react'\nimport { useSDKClient } from '../providers/SDKClientProvider.js'\nimport { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'\nimport { useRouteExecutionStoreContext } from '../stores/routes/RouteExecutionStore.js'\nimport type {\n RouteExecution,\n RouteExecutionState,\n} from '../stores/routes/types.js'\nimport { getSourceTxHash } from '../stores/routes/utils.js'\nimport { buildRouteFromTxHistory } from '../utils/converters.js'\nimport { getQueryKey } from '../utils/queries.js'\nimport { useTools } from './useTools.js'\n\nexport const useTransactionHistory = (): {\n data: RouteExecution[]\n isLoading: boolean\n} => {\n const store = useRouteExecutionStoreContext()\n const { accounts } = useAccount()\n const { storageScopeKey } = useWidgetConfig()\n const sdkClient = useSDKClient()\n const { tools } = useTools()\n\n const { data: routeExecutions, isLoading } = useQueries({\n queries: accounts.map((account) => ({\n queryKey: [\n getQueryKey('transaction-history', storageScopeKey),\n account.address,\n ],\n queryFn: (async ({ queryKey: [, accountAddress], signal }) => {\n if (!accountAddress) {\n return []\n }\n const date = new Date()\n date.setFullYear(date.getFullYear() - 10)\n\n const response = await getTransactionHistory(\n sdkClient,\n {\n wallet: accountAddress,\n fromTimestamp: Math.floor(date.getTime() / 1000),\n toTimestamp: Math.floor(Date.now() / 1000),\n },\n { signal }\n )\n\n return response.transfers\n }) as QueryFunction<StatusResponse[], (string | undefined)[], never>,\n refetchInterval: 300_000,\n enabled: Boolean(account.address),\n })),\n combine: (results) => {\n const uniqueTransactions = new Map<string, StatusResponse>()\n results.forEach((result) => {\n if (result.data) {\n result.data.forEach((transaction) => {\n if (\n (transaction as FullStatusData)?.transactionId &&\n (transaction as FullStatusData)?.receiving?.chainId &&\n transaction?.sending.chainId\n ) {\n uniqueTransactions.set(\n (transaction as FullStatusData).transactionId,\n transaction\n )\n }\n })\n }\n })\n // Convert raw API transactions into RouteExecution objects.\n const data = Array.from(uniqueTransactions.values()).flatMap(\n (transaction) => {\n const routeExecution = buildRouteFromTxHistory(\n transaction as FullStatusData,\n tools\n )\n return routeExecution ? [routeExecution] : []\n }\n )\n return {\n data,\n isLoading: results.some((result) => result.isLoading),\n }\n },\n })\n\n // Stable Set — only recreated when data actually changes (TanStack Query structural sharing).\n const apiTxHashes = useMemo(\n () => new Set(routeExecutions.map((r) => getSourceTxHash(r.route))),\n [routeExecutions]\n )\n\n // Remove local store routes already indexed by the API to avoid duplicates.\n // Must be in an effect — combine runs during render, so store mutations there\n // would trigger \"Cannot update a component while rendering\" in concurrent mode.\n useEffect(() => {\n if (!apiTxHashes.size) {\n return\n }\n const { routes, deleteRoute } = store.getState() as RouteExecutionState\n for (const [id, routeExecution] of Object.entries(routes)) {\n const txHash = getSourceTxHash(\n (routeExecution as RouteExecution | undefined)?.route\n )\n if (txHash && apiTxHashes.has(txHash)) {\n deleteRoute(id)\n }\n }\n }, [apiTxHashes, store])\n\n return {\n data: routeExecutions,\n isLoading,\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"useTransactionHistory.js","names":[],"sources":["../../../src/hooks/useTransactionHistory.ts"],"sourcesContent":["import type { FullStatusData, StatusResponse } from '@lifi/sdk'\nimport { getTransactionHistory } from '@lifi/sdk'\nimport { useAccount } from '@lifi/wallet-management'\nimport type { QueryFunction } from '@tanstack/react-query'\nimport { useQueries } from '@tanstack/react-query'\nimport { useEffect, useMemo } from 'react'\nimport { useSDKClient } from '../providers/SDKClientProvider.js'\nimport { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'\nimport { useRouteExecutionStoreContext } from '../stores/routes/RouteExecutionStore.js'\nimport type {\n RouteExecution,\n RouteExecutionState,\n} from '../stores/routes/types.js'\nimport { getSourceTxHash } from '../stores/routes/utils.js'\nimport { buildRouteFromTxHistory } from '../utils/converters.js'\nimport { getQueryKey } from '../utils/queries.js'\nimport { useAvailableChains } from './useAvailableChains.js'\nimport { useTools } from './useTools.js'\n\nexport const useTransactionHistory = (): {\n data: RouteExecution[]\n isLoading: boolean\n} => {\n const store = useRouteExecutionStoreContext()\n const { accounts } = useAccount()\n const { storageScopeKey } = useWidgetConfig()\n const sdkClient = useSDKClient()\n const { tools } = useTools()\n const { getChainById } = useAvailableChains()\n\n const { data: routeExecutions, isLoading } = useQueries({\n queries: accounts.map((account) => ({\n queryKey: [\n getQueryKey('transaction-history', storageScopeKey),\n account.address,\n ],\n queryFn: (async ({ queryKey: [, accountAddress], signal }) => {\n if (!accountAddress) {\n return []\n }\n const date = new Date()\n date.setFullYear(date.getFullYear() - 10)\n\n const response = await getTransactionHistory(\n sdkClient,\n {\n wallet: accountAddress,\n fromTimestamp: Math.floor(date.getTime() / 1000),\n toTimestamp: Math.floor(Date.now() / 1000),\n },\n { signal }\n )\n\n return response.transfers\n }) as QueryFunction<StatusResponse[], (string | undefined)[], never>,\n refetchInterval: 300_000,\n enabled: Boolean(account.address),\n })),\n combine: (results) => {\n const uniqueTransactions = new Map<string, StatusResponse>()\n results.forEach((result) => {\n if (result.data) {\n result.data.forEach((transaction) => {\n if (\n (transaction as FullStatusData)?.transactionId &&\n (transaction as FullStatusData)?.receiving?.chainId &&\n transaction?.sending.chainId\n ) {\n uniqueTransactions.set(\n (transaction as FullStatusData).transactionId,\n transaction\n )\n }\n })\n }\n })\n // Convert raw API transactions into RouteExecution objects.\n const data = Array.from(uniqueTransactions.values()).flatMap(\n (transaction) => {\n const routeExecution = buildRouteFromTxHistory(\n transaction as FullStatusData,\n tools,\n getChainById(transaction.sending.chainId)\n )\n return routeExecution ? [routeExecution] : []\n }\n )\n return {\n data,\n isLoading: results.some((result) => result.isLoading),\n }\n },\n })\n\n // Stable Set — only recreated when data actually changes (TanStack Query structural sharing).\n const apiTxHashes = useMemo(\n () => new Set(routeExecutions.map((r) => getSourceTxHash(r.route))),\n [routeExecutions]\n )\n\n // Remove local store routes already indexed by the API to avoid duplicates.\n // Must be in an effect — combine runs during render, so store mutations there\n // would trigger \"Cannot update a component while rendering\" in concurrent mode.\n useEffect(() => {\n if (!apiTxHashes.size) {\n return\n }\n const { routes, deleteRoute } = store.getState() as RouteExecutionState\n for (const [id, routeExecution] of Object.entries(routes)) {\n const txHash = getSourceTxHash(\n (routeExecution as RouteExecution | undefined)?.route\n )\n if (txHash && apiTxHashes.has(txHash)) {\n deleteRoute(id)\n }\n }\n }, [apiTxHashes, store])\n\n return {\n data: routeExecutions,\n isLoading,\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAmBA,MAAa,8BAGR;CACH,MAAM,QAAQ,8BAA8B;CAC5C,MAAM,EAAE,aAAa,WAAW;CAChC,MAAM,EAAE,oBAAoB,gBAAgB;CAC5C,MAAM,YAAY,aAAa;CAC/B,MAAM,EAAE,UAAU,SAAS;CAC3B,MAAM,EAAE,iBAAiB,mBAAmB;CAE5C,MAAM,EAAE,MAAM,iBAAiB,cAAc,WAAW;EACtD,SAAS,SAAS,KAAK,aAAa;GAClC,UAAU,CACR,YAAY,uBAAuB,eAAe,GAClD,QAAQ,OACV;GACA,UAAU,OAAO,EAAE,UAAU,GAAG,iBAAiB,aAAa;IAC5D,IAAI,CAAC,gBACH,OAAO,CAAC;IAEV,MAAM,uBAAO,IAAI,KAAK;IACtB,KAAK,YAAY,KAAK,YAAY,IAAI,EAAE;IAYxC,QAAO,MAVgB,sBACrB,WACA;KACE,QAAQ;KACR,eAAe,KAAK,MAAM,KAAK,QAAQ,IAAI,GAAI;KAC/C,aAAa,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;IAC3C,GACA,EAAE,OAAO,CACX,EAAA,CAEgB;GAClB;GACA,iBAAiB;GACjB,SAAS,QAAQ,QAAQ,OAAO;EAClC,EAAE;EACF,UAAU,YAAY;GACpB,MAAM,qCAAqB,IAAI,IAA4B;GAC3D,QAAQ,SAAS,WAAW;IAC1B,IAAI,OAAO,MACT,OAAO,KAAK,SAAS,gBAAgB;KACnC,IACG,aAAgC,iBAChC,aAAgC,WAAW,WAC5C,aAAa,QAAQ,SAErB,mBAAmB,IAChB,YAA+B,eAChC,WACF;IAEJ,CAAC;GAEL,CAAC;GAYD,OAAO;IACL,MAXW,MAAM,KAAK,mBAAmB,OAAO,CAAC,CAAC,CAAC,SAClD,gBAAgB;KACf,MAAM,iBAAiB,wBACrB,aACA,OACA,aAAa,YAAY,QAAQ,OAAO,CAC1C;KACA,OAAO,iBAAiB,CAAC,cAAc,IAAI,CAAC;IAC9C,CAGG;IACH,WAAW,QAAQ,MAAM,WAAW,OAAO,SAAS;GACtD;EACF;CACF,CAAC;CAGD,MAAM,cAAc,cACZ,IAAI,IAAI,gBAAgB,KAAK,MAAM,gBAAgB,EAAE,KAAK,CAAC,CAAC,GAClE,CAAC,eAAe,CAClB;CAKA,gBAAgB;EACd,IAAI,CAAC,YAAY,MACf;EAEF,MAAM,EAAE,QAAQ,gBAAgB,MAAM,SAAS;EAC/C,KAAK,MAAM,CAAC,IAAI,mBAAmB,OAAO,QAAQ,MAAM,GAAG;GACzD,MAAM,SAAS,gBACZ,gBAA+C,KAClD;GACA,IAAI,UAAU,YAAY,IAAI,MAAM,GAClC,YAAY,EAAE;EAElB;CACF,GAAG,CAAC,aAAa,KAAK,CAAC;CAEvB,OAAO;EACL,MAAM;EACN;CACF;AACF"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -30,9 +30,9 @@ import { watermelonLightTheme } from "./themes/watermelonLight.js";
|
|
|
30
30
|
import { windows95Theme } from "./themes/windows95.js";
|
|
31
31
|
import { compactNumberFormatter } from "./utils/compactNumberFormatter.js";
|
|
32
32
|
import { currencyExtendedFormatter } from "./utils/currencyExtendedFormatter.js";
|
|
33
|
-
import { formatDuration, formatInputAmount, formatSlippage, formatTokenAmount, formatTokenPrice, priceToTokenAmount, usdDecimals, wrapLongWords } from "./utils/format.js";
|
|
33
|
+
import { formatDuration, formatInputAmount, formatSlippage, formatTokenAmount, formatTokenPrice, parseAmountToBigInt, priceToTokenAmount, usdDecimals, wrapLongWords } from "./utils/format.js";
|
|
34
34
|
import { getPriceImpact } from "./utils/getPriceImpact.js";
|
|
35
35
|
import { percentFormatter } from "./utils/percentFormatter.js";
|
|
36
36
|
import { ChainId, ChainType, CoinKey } from "@lifi/sdk";
|
|
37
37
|
export type * from "@lifi/sdk";
|
|
38
|
-
export { AllowDeny, AllowDenyItems, AllowDenySet, AllowDenySetItem, AllowDenySets, Appearance, Bookmark, BookmarkActions, BookmarkProps, BookmarkState, BookmarkStore, CalculateFeeParams, ChainId, ChainPinned, ChainTokenSelected, ChainType, CoinKey, ContactSupport, CustomMode, DefaultFieldValues, DefaultUI, DefaultValues, DisabledUIConfig, ExpansionType, ExplorerUrl, ExtractValueType, FieldNames, FieldValues, FormActions, FormFieldArray, FormFieldChanged, FormFieldNames, FormFieldOptions, FormKeyHelper, FormProps, FormRef, FormRefProps, FormState, FormStoreStore, FormType, FormTypeProps, FormValueControl, FormValues, FormValuesState, GenericFormValue, type GetChainsOptions, type GetChainsQueryFnData, type GetChainsQueryKey, type GetChainsQueryKeyParameters, type GetChainsQueryOptions, type GetToolsOptions, type GetToolsQueryFnData, type GetToolsQueryKey, type GetToolsQueryKeyParameters, type GetToolsQueryOptions, HiddenUIConfig, InternalNavigationTabKey, InternalWidgetMode, ItemPrice, ItemPriceProps, App as LiFiWidget, LowAddressActivityConfirmed, ModeOptions, NFT, NFTBase, NFTBaseProps, NFTOwner, NFTProps, NavigationProps, NavigationTabChanged, NavigationTabKey, PoweredByType, type QueryParameter, RequiredUIConfig, ResetOptions, RouteExecutionUpdate, RouteHighValueLossUpdate, RouteLabel, RouteLabelRule, RouteSelected, type ScopeKeyParameter, SetFieldValueFunction, SetOptions, SettingUpdated, SplitMode, SplitModeOptions, SplitNavigationTabKey, ToAddress, type TokenAmount, TokenSearch, TouchedFields, ValidationActions, ValidationProps, WidgetChains, WidgetConfig, WidgetConfigPartialProps, WidgetConfigProps, WidgetContractTool, type WidgetDrawer, WidgetDrawerProps, WidgetEvent, type WidgetEventEmitter, WidgetEvents, WidgetFeeConfig, WidgetLanguages, WidgetMode, WidgetProps, WidgetProvider, WidgetSDKConfig, WidgetSkeleton, WidgetTheme, WidgetThemeComponents, WidgetTokens, WidgetVariant, WidgetWalletConfig, azureLightTheme, compactNumberFormatter, createTheme, currencyExtendedFormatter, defaultMaxHeight, formatDuration, formatInputAmount, formatSlippage, formatTokenAmount, formatTokenPrice, getChainsQueryKey, getChainsQueryOptions, getPriceImpact, getToolsQueryKey, getToolsQueryOptions, jumperTheme, name, navigationRoutes, palette, paletteDark, paletteLight, percentFormatter, priceToTokenAmount, supportedChainTypes, usdDecimals, useFieldActions, useFieldValues, useWidgetChains, useWidgetEvents, version, watermelonLightTheme, widgetEvents, windows95Theme, wrapLongWords };
|
|
38
|
+
export { AllowDeny, AllowDenyItems, AllowDenySet, AllowDenySetItem, AllowDenySets, Appearance, Bookmark, BookmarkActions, BookmarkProps, BookmarkState, BookmarkStore, CalculateFeeParams, ChainId, ChainPinned, ChainTokenSelected, ChainType, CoinKey, ContactSupport, CustomMode, DefaultFieldValues, DefaultUI, DefaultValues, DisabledUIConfig, ExpansionType, ExplorerUrl, ExtractValueType, FieldNames, FieldValues, FormActions, FormFieldArray, FormFieldChanged, FormFieldNames, FormFieldOptions, FormKeyHelper, FormProps, FormRef, FormRefProps, FormState, FormStoreStore, FormType, FormTypeProps, FormValueControl, FormValues, FormValuesState, GenericFormValue, type GetChainsOptions, type GetChainsQueryFnData, type GetChainsQueryKey, type GetChainsQueryKeyParameters, type GetChainsQueryOptions, type GetToolsOptions, type GetToolsQueryFnData, type GetToolsQueryKey, type GetToolsQueryKeyParameters, type GetToolsQueryOptions, HiddenUIConfig, InternalNavigationTabKey, InternalWidgetMode, ItemPrice, ItemPriceProps, App as LiFiWidget, LowAddressActivityConfirmed, ModeOptions, NFT, NFTBase, NFTBaseProps, NFTOwner, NFTProps, NavigationProps, NavigationTabChanged, NavigationTabKey, PoweredByType, type QueryParameter, RequiredUIConfig, ResetOptions, RouteExecutionUpdate, RouteHighValueLossUpdate, RouteLabel, RouteLabelRule, RouteSelected, type ScopeKeyParameter, SetFieldValueFunction, SetOptions, SettingUpdated, SplitMode, SplitModeOptions, SplitNavigationTabKey, ToAddress, type TokenAmount, TokenSearch, TouchedFields, ValidationActions, ValidationProps, WidgetChains, WidgetConfig, WidgetConfigPartialProps, WidgetConfigProps, WidgetContractTool, type WidgetDrawer, WidgetDrawerProps, WidgetEvent, type WidgetEventEmitter, WidgetEvents, WidgetFeeConfig, WidgetLanguages, WidgetMode, WidgetProps, WidgetProvider, WidgetSDKConfig, WidgetSkeleton, WidgetTheme, WidgetThemeComponents, WidgetTokens, WidgetVariant, WidgetWalletConfig, azureLightTheme, compactNumberFormatter, createTheme, currencyExtendedFormatter, defaultMaxHeight, formatDuration, formatInputAmount, formatSlippage, formatTokenAmount, formatTokenPrice, getChainsQueryKey, getChainsQueryOptions, getPriceImpact, getToolsQueryKey, getToolsQueryOptions, jumperTheme, name, navigationRoutes, palette, paletteDark, paletteLight, parseAmountToBigInt, percentFormatter, priceToTokenAmount, supportedChainTypes, usdDecimals, useFieldActions, useFieldValues, useWidgetChains, useWidgetEvents, version, watermelonLightTheme, widgetEvents, windows95Theme, wrapLongWords };
|
package/dist/esm/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { navigationRoutes } from "./utils/navigationRoutes.js";
|
|
|
6
6
|
import { name, version } from "./config/version.js";
|
|
7
7
|
import { getChainsQueryKey, getChainsQueryOptions, supportedChainTypes } from "./queries/getChains.js";
|
|
8
8
|
import { useFieldActions } from "./stores/form/useFieldActions.js";
|
|
9
|
-
import { formatDuration, formatInputAmount, formatSlippage, formatTokenAmount, formatTokenPrice, priceToTokenAmount, usdDecimals, wrapLongWords } from "./utils/format.js";
|
|
9
|
+
import { formatDuration, formatInputAmount, formatSlippage, formatTokenAmount, formatTokenPrice, parseAmountToBigInt, priceToTokenAmount, usdDecimals, wrapLongWords } from "./utils/format.js";
|
|
10
10
|
import { FormKeyHelper } from "./stores/form/types.js";
|
|
11
11
|
import { useFieldValues } from "./stores/form/useFieldValues.js";
|
|
12
12
|
import { getPriceImpact } from "./utils/getPriceImpact.js";
|
|
@@ -27,4 +27,4 @@ import { jumperTheme } from "./themes/jumper.js";
|
|
|
27
27
|
import { watermelonLightTheme } from "./themes/watermelonLight.js";
|
|
28
28
|
import { windows95Theme } from "./themes/windows95.js";
|
|
29
29
|
import { ChainId, ChainType, CoinKey } from "@lifi/sdk";
|
|
30
|
-
export { ChainId, ChainType, CoinKey, ExpansionType, FormKeyHelper, ItemPrice, App as LiFiWidget, NFT, NFTBase, WidgetEvent, WidgetSkeleton, azureLightTheme, compactNumberFormatter, createTheme, currencyExtendedFormatter, defaultMaxHeight, formatDuration, formatInputAmount, formatSlippage, formatTokenAmount, formatTokenPrice, getChainsQueryKey, getChainsQueryOptions, getPriceImpact, getToolsQueryKey, getToolsQueryOptions, jumperTheme, name, navigationRoutes, palette, paletteDark, paletteLight, percentFormatter, priceToTokenAmount, supportedChainTypes, usdDecimals, useFieldActions, useFieldValues, useWidgetChains, useWidgetEvents, version, watermelonLightTheme, widgetEvents, windows95Theme, wrapLongWords };
|
|
30
|
+
export { ChainId, ChainType, CoinKey, ExpansionType, FormKeyHelper, ItemPrice, App as LiFiWidget, NFT, NFTBase, WidgetEvent, WidgetSkeleton, azureLightTheme, compactNumberFormatter, createTheme, currencyExtendedFormatter, defaultMaxHeight, formatDuration, formatInputAmount, formatSlippage, formatTokenAmount, formatTokenPrice, getChainsQueryKey, getChainsQueryOptions, getPriceImpact, getToolsQueryKey, getToolsQueryOptions, jumperTheme, name, navigationRoutes, palette, paletteDark, paletteLight, parseAmountToBigInt, percentFormatter, priceToTokenAmount, supportedChainTypes, usdDecimals, useFieldActions, useFieldValues, useWidgetChains, useWidgetEvents, version, watermelonLightTheme, widgetEvents, windows95Theme, wrapLongWords };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useWidgetConfig } from "../../providers/WidgetProvider/WidgetProvider.js";
|
|
2
2
|
import "../../config/constants.js";
|
|
3
3
|
import { navigationRoutes } from "../../utils/navigationRoutes.js";
|
|
4
|
+
import { useAvailableChains } from "../../hooks/useAvailableChains.js";
|
|
4
5
|
import { PageContainer } from "../../components/PageContainer.js";
|
|
5
6
|
import { getSourceTxHash } from "../../stores/routes/utils.js";
|
|
6
7
|
import { useRouteExecutionStore } from "../../stores/routes/RouteExecutionStore.js";
|
|
@@ -26,6 +27,7 @@ const TransactionDetailsPage = () => {
|
|
|
26
27
|
const { mode, modeOptions, contractSecondaryComponent, explorerUrls } = useWidgetConfig();
|
|
27
28
|
const { search } = useLocation();
|
|
28
29
|
const { tools } = useTools();
|
|
30
|
+
const { getChainById } = useAvailableChains();
|
|
29
31
|
const { getTransactionLink } = useExplorer();
|
|
30
32
|
const storedRouteExecution = useRouteExecutionStore((store) => search?.routeId ? store.routes[search.routeId] : void 0);
|
|
31
33
|
const { transaction, isLoading } = useTransactionDetails(!storedRouteExecution && search?.transactionHash);
|
|
@@ -34,8 +36,9 @@ const TransactionDetailsPage = () => {
|
|
|
34
36
|
const routeExecution = useMemo(() => {
|
|
35
37
|
if (storedRouteExecution) return storedRouteExecution;
|
|
36
38
|
if (isLoading) return;
|
|
37
|
-
if (transaction) return buildRouteFromTxHistory(transaction, tools);
|
|
39
|
+
if (transaction) return buildRouteFromTxHistory(transaction, tools, getChainById(transaction.sending.chainId));
|
|
38
40
|
}, [
|
|
41
|
+
getChainById,
|
|
39
42
|
isLoading,
|
|
40
43
|
storedRouteExecution,
|
|
41
44
|
tools,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransactionDetailsPage.js","names":[],"sources":["../../../../src/pages/TransactionDetailsPage/TransactionDetailsPage.tsx"],"sourcesContent":["import type { FullStatusData } from '@lifi/sdk'\nimport { useLocation, useNavigate } from '@tanstack/react-router'\nimport { useEffect, useMemo } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { ContractComponent } from '../../components/ContractComponent/ContractComponent.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { TransactionCard } from '../../components/TransactionCard/TransactionCard.js'\nimport { internalExplorerUrl } from '../../config/constants.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useTools } from '../../hooks/useTools.js'\nimport { useTransactionDetails } from '../../hooks/useTransactionDetails.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { useRouteExecutionStore } from '../../stores/routes/RouteExecutionStore.js'\nimport { getSourceTxHash } from '../../stores/routes/utils.js'\nimport { buildRouteFromTxHistory } from '../../utils/converters.js'\nimport { navigationRoutes } from '../../utils/navigationRoutes.js'\nimport { isHttpUrl } from '../../utils/url.js'\nimport { ReceiptsCard } from './ReceiptsCard.js'\nimport { TransactionDetailsSkeleton } from './TransactionDetailsSkeleton.js'\nimport { TransferIdCard } from './TransferIdCard.js'\n\nexport const TransactionDetailsPage: React.FC = () => {\n const { t } = useTranslation()\n const navigate = useNavigate()\n const { mode, modeOptions, contractSecondaryComponent, explorerUrls } =\n useWidgetConfig()\n const { search }: any = useLocation()\n const { tools } = useTools()\n const { getTransactionLink } = useExplorer()\n const storedRouteExecution = useRouteExecutionStore((store) =>\n search?.routeId ? store.routes[search.routeId] : undefined\n )\n const { transaction, isLoading } = useTransactionDetails(\n !storedRouteExecution && search?.transactionHash\n )\n\n const title =\n mode === 'custom'\n ? t(`header.${modeOptions?.custom?.type ?? 'checkout'}Details`)\n : t('header.transactionDetails')\n useHeader(title)\n\n const routeExecution = useMemo(() => {\n if (storedRouteExecution) {\n return storedRouteExecution\n }\n if (isLoading) {\n return\n }\n if (transaction) {\n return buildRouteFromTxHistory(transaction as FullStatusData
|
|
1
|
+
{"version":3,"file":"TransactionDetailsPage.js","names":[],"sources":["../../../../src/pages/TransactionDetailsPage/TransactionDetailsPage.tsx"],"sourcesContent":["import type { FullStatusData } from '@lifi/sdk'\nimport { useLocation, useNavigate } from '@tanstack/react-router'\nimport { useEffect, useMemo } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { ContractComponent } from '../../components/ContractComponent/ContractComponent.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { TransactionCard } from '../../components/TransactionCard/TransactionCard.js'\nimport { internalExplorerUrl } from '../../config/constants.js'\nimport { useAvailableChains } from '../../hooks/useAvailableChains.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useTools } from '../../hooks/useTools.js'\nimport { useTransactionDetails } from '../../hooks/useTransactionDetails.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { useRouteExecutionStore } from '../../stores/routes/RouteExecutionStore.js'\nimport { getSourceTxHash } from '../../stores/routes/utils.js'\nimport { buildRouteFromTxHistory } from '../../utils/converters.js'\nimport { navigationRoutes } from '../../utils/navigationRoutes.js'\nimport { isHttpUrl } from '../../utils/url.js'\nimport { ReceiptsCard } from './ReceiptsCard.js'\nimport { TransactionDetailsSkeleton } from './TransactionDetailsSkeleton.js'\nimport { TransferIdCard } from './TransferIdCard.js'\n\nexport const TransactionDetailsPage: React.FC = () => {\n const { t } = useTranslation()\n const navigate = useNavigate()\n const { mode, modeOptions, contractSecondaryComponent, explorerUrls } =\n useWidgetConfig()\n const { search }: any = useLocation()\n const { tools } = useTools()\n const { getChainById } = useAvailableChains()\n const { getTransactionLink } = useExplorer()\n const storedRouteExecution = useRouteExecutionStore((store) =>\n search?.routeId ? store.routes[search.routeId] : undefined\n )\n const { transaction, isLoading } = useTransactionDetails(\n !storedRouteExecution && search?.transactionHash\n )\n\n const title =\n mode === 'custom'\n ? t(`header.${modeOptions?.custom?.type ?? 'checkout'}Details`)\n : t('header.transactionDetails')\n useHeader(title)\n\n const routeExecution = useMemo(() => {\n if (storedRouteExecution) {\n return storedRouteExecution\n }\n if (isLoading) {\n return\n }\n if (transaction) {\n return buildRouteFromTxHistory(\n transaction as FullStatusData,\n tools,\n getChainById(transaction.sending.chainId)\n )\n }\n }, [getChainById, isLoading, storedRouteExecution, tools, transaction])\n\n useEffect(() => {\n if (!isLoading && !routeExecution) {\n navigate({ to: navigationRoutes.home })\n }\n }, [isLoading, navigate, routeExecution])\n\n const explorerUrl = explorerUrls?.internal?.[0]\n const url = typeof explorerUrl === 'string' ? explorerUrl : explorerUrl?.url\n\n const sourceTxHash = getSourceTxHash(routeExecution?.route)\n let supportId = sourceTxHash ?? routeExecution?.route.id ?? ''\n\n const internalTxLink =\n routeExecution?.route?.steps?.at(-1)?.execution?.internalTxLink\n const externalTxLink =\n routeExecution?.route?.steps?.at(-1)?.execution?.externalTxLink\n\n const txLink =\n [\n url ? internalTxLink?.replace(internalExplorerUrl, url) : internalTxLink,\n externalTxLink,\n ].find(isHttpUrl) ?? getTransactionLink({ txHash: supportId })\n\n if (process.env.NODE_ENV === 'development') {\n supportId += `_${routeExecution?.route.id}`\n }\n\n const startedAt = new Date(\n routeExecution?.route.steps[0].execution?.startedAt ?? 0\n )\n\n if (isLoading && !storedRouteExecution) {\n return <TransactionDetailsSkeleton />\n }\n\n if (!routeExecution?.route) {\n return null\n }\n\n return (\n <PageContainer\n topGutters\n bottomGutters\n sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}\n >\n <TransactionCard route={routeExecution.route} date={startedAt} />\n {mode === 'custom' && contractSecondaryComponent ? (\n <ContractComponent>{contractSecondaryComponent}</ContractComponent>\n ) : null}\n <ReceiptsCard route={routeExecution.route} />\n {supportId ? (\n <TransferIdCard transferId={supportId} txLink={txLink} />\n ) : null}\n </PageContainer>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,+BAAyC;CACpD,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,WAAW,YAAY;CAC7B,MAAM,EAAE,MAAM,aAAa,4BAA4B,iBACrD,gBAAgB;CAClB,MAAM,EAAE,WAAgB,YAAY;CACpC,MAAM,EAAE,UAAU,SAAS;CAC3B,MAAM,EAAE,iBAAiB,mBAAmB;CAC5C,MAAM,EAAE,uBAAuB,YAAY;CAC3C,MAAM,uBAAuB,wBAAwB,UACnD,QAAQ,UAAU,MAAM,OAAO,OAAO,WAAW,KAAA,CACnD;CACA,MAAM,EAAE,aAAa,cAAc,sBACjC,CAAC,wBAAwB,QAAQ,eACnC;CAEA,MAAM,QACJ,SAAS,WACL,EAAE,UAAU,aAAa,QAAQ,QAAQ,WAAW,QAAQ,IAC5D,EAAE,2BAA2B;CACnC,UAAU,KAAK;CAEf,MAAM,iBAAiB,cAAc;EACnC,IAAI,sBACF,OAAO;EAET,IAAI,WACF;EAEF,IAAI,aACF,OAAO,wBACL,aACA,OACA,aAAa,YAAY,QAAQ,OAAO,CAC1C;CAEJ,GAAG;EAAC;EAAc;EAAW;EAAsB;EAAO;CAAW,CAAC;CAEtE,gBAAgB;EACd,IAAI,CAAC,aAAa,CAAC,gBACjB,SAAS,EAAE,IAAI,iBAAiB,KAAK,CAAC;CAE1C,GAAG;EAAC;EAAW;EAAU;CAAc,CAAC;CAExC,MAAM,cAAc,cAAc,WAAW;CAC7C,MAAM,MAAM,OAAO,gBAAgB,WAAW,cAAc,aAAa;CAGzE,IAAI,YADiB,gBAAgB,gBAAgB,KAC1B,KAAK,gBAAgB,MAAM,MAAM;CAE5D,MAAM,iBACJ,gBAAgB,OAAO,OAAO,GAAG,EAAE,CAAC,EAAE,WAAW;CACnD,MAAM,iBACJ,gBAAgB,OAAO,OAAO,GAAG,EAAE,CAAC,EAAE,WAAW;CAEnD,MAAM,SACJ,CACE,MAAM,gBAAgB,QAAA,sBAA6B,GAAG,IAAI,gBAC1D,cACF,CAAC,CAAC,KAAK,SAAS,KAAK,mBAAmB,EAAE,QAAQ,UAAU,CAAC;CAE/D,IAAI,QAAQ,IAAI,aAAa,eAC3B,aAAa,IAAI,gBAAgB,MAAM;CAGzC,MAAM,YAAY,IAAI,KACpB,gBAAgB,MAAM,MAAM,EAAE,CAAC,WAAW,aAAa,CACzD;CAEA,IAAI,aAAa,CAAC,sBAChB,OAAO,oBAAC,4BAAD,CAA6B,CAAA;CAGtC,IAAI,CAAC,gBAAgB,OACnB,OAAO;CAGT,OACE,qBAAC,eAAD;EACE,YAAA;EACA,eAAA;EACA,IAAI;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAE;EAHzD,UAAA;GAKE,oBAAC,iBAAD;IAAiB,OAAO,eAAe;IAAO,MAAM;GAAY,CAAA;GAC/D,SAAS,YAAY,6BACpB,oBAAC,mBAAD,EAAA,UAAoB,2BAA8C,CAAA,IAChE;GACJ,oBAAC,cAAD,EAAc,OAAO,eAAe,MAAQ,CAAA;GAC3C,YACC,oBAAC,gBAAD;IAAgB,YAAY;IAAmB;GAAS,CAAA,IACtD;EACS;;AAEnB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RouteExecution } from "../stores/routes/types.js";
|
|
2
|
-
import { FullStatusData, ToolsResponse } from "@lifi/sdk";
|
|
2
|
+
import { ExtendedChain, FullStatusData, ToolsResponse } from "@lifi/sdk";
|
|
3
3
|
//#region src/utils/converters.d.ts
|
|
4
|
-
export declare const buildRouteFromTxHistory: (tx: FullStatusData, tools?: ToolsResponse) => RouteExecution | undefined;
|
|
4
|
+
export declare const buildRouteFromTxHistory: (tx: FullStatusData, tools?: ToolsResponse, fromChain?: ExtendedChain) => RouteExecution | undefined;
|
|
5
5
|
//#endregion
|
|
6
6
|
//# sourceMappingURL=converters.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"converters.d.ts","names":[],"sources":["../../../src/utils/converters.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"converters.d.ts","names":[],"sources":["../../../src/utils/converters.ts"],"mappings":";;;qBAgHa,0BACX,IAAI,gBACJ,QAAQ,eACR,YAAY,kBACX"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { formatTokenPrice } from "./format.js";
|
|
1
|
+
import { formatTokenPrice, parseAmountToBigInt } from "./format.js";
|
|
2
2
|
//#region src/utils/converters.ts
|
|
3
3
|
const buildActionsFromTxHistory = (tx) => {
|
|
4
4
|
const sending = tx.sending;
|
|
@@ -34,7 +34,26 @@ const buildActionsFromTxHistory = (tx) => {
|
|
|
34
34
|
txLink: receiving.txLink
|
|
35
35
|
}];
|
|
36
36
|
};
|
|
37
|
-
const
|
|
37
|
+
const isValidFeeCost = (feeCost) => Boolean(feeCost.token) && parseAmountToBigInt(feeCost.amount) > 0n;
|
|
38
|
+
const buildFeeCostsFromTxHistory = (tx, fromChain) => {
|
|
39
|
+
const feeCosts = tx.feeCosts?.filter(isValidFeeCost);
|
|
40
|
+
if (feeCosts?.some((feeCost) => feeCost.included === false)) return feeCosts;
|
|
41
|
+
if (fromChain?.nonStandardNativeDecimals) return;
|
|
42
|
+
const sending = tx.sending;
|
|
43
|
+
const sendingValue = parseAmountToBigInt(sending.value);
|
|
44
|
+
const sendingFeeAmount = sending.gasToken.address === sending.token?.address && sending.amount ? sendingValue - parseAmountToBigInt(sending.amount) : sendingValue;
|
|
45
|
+
if (sendingFeeAmount <= 0n) return;
|
|
46
|
+
return [{
|
|
47
|
+
amount: sendingFeeAmount.toString(),
|
|
48
|
+
amountUSD: formatTokenPrice(sendingFeeAmount, sending.gasToken.priceUSD, sending.gasToken.decimals).toFixed(2),
|
|
49
|
+
token: sending.gasToken,
|
|
50
|
+
included: false,
|
|
51
|
+
description: "",
|
|
52
|
+
name: "",
|
|
53
|
+
percentage: ""
|
|
54
|
+
}];
|
|
55
|
+
};
|
|
56
|
+
const buildRouteFromTxHistory = (tx, tools, fromChain) => {
|
|
38
57
|
const sending = tx.sending;
|
|
39
58
|
const receiving = tx.receiving;
|
|
40
59
|
if (!sending.token?.chainId || !receiving.token?.chainId) return;
|
|
@@ -56,18 +75,7 @@ const buildRouteFromTxHistory = (tx, tools) => {
|
|
|
56
75
|
...receiving.token,
|
|
57
76
|
amount: BigInt(receiving.amount ?? 0)
|
|
58
77
|
};
|
|
59
|
-
const
|
|
60
|
-
const sendingFeeAmount = sending.gasToken.address === sending.token.address && sending.amount ? sendingValue - BigInt(sending.amount) : sendingValue;
|
|
61
|
-
const sendingFeeAmountUsd = sending.gasToken.priceUSD && sendingFeeAmount ? formatTokenPrice(sendingFeeAmount, sending.gasToken.priceUSD, sending.gasToken.decimals) : 0;
|
|
62
|
-
const feeCosts = sendingFeeAmount ? [{
|
|
63
|
-
amount: sendingFeeAmount.toString(),
|
|
64
|
-
amountUSD: sendingFeeAmountUsd.toFixed(2),
|
|
65
|
-
token: sending.gasToken,
|
|
66
|
-
included: false,
|
|
67
|
-
description: "",
|
|
68
|
-
name: "",
|
|
69
|
-
percentage: ""
|
|
70
|
-
}] : void 0;
|
|
78
|
+
const feeCosts = buildFeeCostsFromTxHistory(tx, fromChain);
|
|
71
79
|
return {
|
|
72
80
|
status: 1,
|
|
73
81
|
route: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"converters.js","names":[],"sources":["../../../src/utils/converters.ts"],"sourcesContent":["import type {\n ExecutionAction,\n ExecutionActionStatus,\n ExtendedTransactionInfo,\n FeeCost,\n FullStatusData,\n Substatus,\n TokenAmount,\n ToolsResponse,\n} from '@lifi/sdk'\nimport type { RouteExecution } from '../stores/routes/types.js'\nimport { formatTokenPrice } from './format.js'\n\nconst buildActionsFromTxHistory = (tx: FullStatusData): ExecutionAction[] => {\n const sending = tx.sending as ExtendedTransactionInfo\n const receiving = tx.receiving as ExtendedTransactionInfo\n\n if (!sending.token?.chainId || !receiving.token?.chainId) {\n return []\n }\n\n const actionStatus: ExecutionActionStatus =\n tx.status === 'DONE' ? 'DONE' : 'FAILED'\n const substatus: Substatus =\n actionStatus === 'FAILED' ? 'UNKNOWN_ERROR' : 'COMPLETED'\n\n if (sending.chainId === receiving.chainId) {\n return [\n {\n type: 'SWAP', // operations on same chain will be swaps\n message: '',\n status: actionStatus,\n chainId: sending.chainId,\n txHash: sending.txHash,\n txLink: sending.txLink,\n substatus,\n substatusMessage: '',\n },\n ]\n }\n\n const actions: ExecutionAction[] = [\n {\n type: 'CROSS_CHAIN', // first step of bridging, ignoring the approvals\n message: '',\n status: actionStatus, // can be FAILED\n chainId: sending.chainId,\n txHash: sending.txHash,\n txLink: sending.txLink,\n },\n {\n type: 'RECEIVING_CHAIN', // final step of bridging, post swaps\n message: '',\n status: actionStatus,\n substatus,\n substatusMessage: '',\n chainId: receiving.chainId,\n txHash: receiving.txHash,\n txLink: receiving.txLink,\n },\n ]\n\n return actions\n}\n\nexport const buildRouteFromTxHistory = (\n tx: FullStatusData,\n tools?: ToolsResponse\n): RouteExecution | undefined => {\n const sending = tx.sending as ExtendedTransactionInfo\n const receiving = tx.receiving as ExtendedTransactionInfo\n\n if (!sending.token?.chainId || !receiving.token?.chainId) {\n return\n }\n\n let usedTool = tx.sending.includedSteps?.find(\n (step) => step.toolDetails.key === tx.tool\n )?.toolDetails\n\n if (!usedTool) {\n const selectedBridge = tools?.bridges.find(\n (bridge) => bridge.key === tx.tool\n )\n\n const selectedExchange = tools?.exchanges.find(\n (exchange) => exchange.key === tx.tool\n )\n usedTool = {\n key: tx.tool,\n name: selectedBridge?.name ?? selectedExchange?.name ?? tx.tool,\n logoURI: selectedBridge?.logoURI ?? selectedExchange?.logoURI ?? '',\n }\n }\n\n const fromToken: TokenAmount = {\n ...sending.token,\n amount: BigInt(sending.amount ?? 0),\n }\n\n const toToken: TokenAmount = {\n ...receiving.token,\n amount: BigInt(receiving.amount ?? 0),\n }\n\n const sendingValue = sending.value ? BigInt(sending.value) : 0n\n const sendingFeeAmount =\n sending.gasToken.address === sending.token.address && sending.amount\n ? sendingValue - BigInt(sending.amount)\n : sendingValue\n const sendingFeeAmountUsd =\n sending.gasToken.priceUSD && sendingFeeAmount\n ? formatTokenPrice(\n sendingFeeAmount,\n sending.gasToken.priceUSD,\n sending.gasToken.decimals\n )\n : 0\n\n const feeCosts: FeeCost[] | undefined = sendingFeeAmount\n ? [\n {\n amount: sendingFeeAmount.toString(),\n amountUSD: sendingFeeAmountUsd.toFixed(2),\n token: sending.gasToken,\n included: false,\n // Not used\n description: '',\n name: '',\n percentage: '',\n },\n ]\n : undefined\n\n const routeExecution: RouteExecution = {\n status: 1,\n route: {\n id: (tx as FullStatusData).transactionId,\n fromAddress: (tx as FullStatusData).fromAddress,\n toAddress: (tx as FullStatusData).toAddress,\n fromChainId: sending.chainId,\n fromAmount: sending.amount ?? '',\n fromAmountUSD: sending.amountUSD ?? '',\n toAmount: receiving.amount ?? '',\n toAmountMin: receiving.amount ?? '',\n toAmountUSD: receiving.amountUSD ?? '',\n toChainId: receiving.chainId,\n fromToken,\n toToken,\n gasCostUSD: sending.gasAmountUSD,\n steps: [\n {\n id: (tx as FullStatusData).transactionId,\n type: 'lifi',\n tool: tx.tool,\n toolDetails: usedTool,\n action: {\n fromToken: sending.token,\n fromAmount: sending.amount ?? '',\n fromChainId: sending.chainId,\n fromAddress: (tx as FullStatusData).fromAddress,\n toToken: receiving.token,\n toChainId: receiving.chainId,\n toAddress: (tx as FullStatusData).toAddress,\n slippage: 0,\n },\n estimate: {\n tool: tx.tool,\n approvalAddress: '',\n fromAmount: sending.amount ?? '',\n fromAmountUSD: sending.amountUSD ?? '',\n toAmountMin: receiving.amount ?? '',\n toAmount: receiving.amount ?? '',\n toAmountUSD: receiving.amountUSD ?? '',\n executionDuration: 0,\n },\n includedSteps: [\n {\n id: (tx as FullStatusData).transactionId,\n type: sending.chainId === receiving.chainId ? 'swap' : 'cross',\n action: {\n fromChainId: sending.chainId,\n fromAmount: sending.amount ?? '',\n fromToken: sending.token,\n toChainId: receiving.chainId,\n toToken: receiving.token,\n slippage: 0,\n fromAddress: (tx as FullStatusData).fromAddress,\n toAddress: (tx as FullStatusData).toAddress,\n },\n estimate: {\n tool: tx.tool,\n fromAmount: sending.amount ?? '',\n toAmount: receiving.amount ?? '',\n toAmountMin: receiving.amount ?? '',\n approvalAddress: '',\n executionDuration: 0,\n },\n tool: tx.tool,\n toolDetails: usedTool,\n },\n ],\n integrator: tx.metadata?.integrator ?? '',\n execution: {\n status: 'DONE', // can be FAILED\n startedAt: sending.timestamp\n ? sending.timestamp * 1000\n : Date.now(),\n actions: buildActionsFromTxHistory(tx),\n fromAmount: sending.amount,\n toAmount: receiving.amount,\n toToken: receiving.token,\n internalTxLink: tx.lifiExplorerLink,\n externalTxLink: tx.bridgeExplorerLink,\n gasCosts: [\n {\n amount: sending.gasAmount,\n amountUSD: sending.gasAmountUSD,\n token: sending.gasToken,\n estimate: '0',\n limit: '0',\n price: '0',\n type: 'SEND',\n },\n ],\n feeCosts,\n },\n },\n ],\n insurance: {\n state: 'NOT_INSURABLE',\n feeAmountUsd: '0',\n },\n },\n }\n\n return routeExecution\n}\n"],"mappings":";;AAaA,MAAM,6BAA6B,OAA0C;CAC3E,MAAM,UAAU,GAAG;CACnB,MAAM,YAAY,GAAG;CAErB,IAAI,CAAC,QAAQ,OAAO,WAAW,CAAC,UAAU,OAAO,SAC/C,OAAO,CAAC;CAGV,MAAM,eACJ,GAAG,WAAW,SAAS,SAAS;CAClC,MAAM,YACJ,iBAAiB,WAAW,kBAAkB;CAEhD,IAAI,QAAQ,YAAY,UAAU,SAChC,OAAO,CACL;EACE,MAAM;EACN,SAAS;EACT,QAAQ;EACR,SAAS,QAAQ;EACjB,QAAQ,QAAQ;EAChB,QAAQ,QAAQ;EAChB;EACA,kBAAkB;CACpB,CACF;CAwBF,OAAO,CApBL;EACE,MAAM;EACN,SAAS;EACT,QAAQ;EACR,SAAS,QAAQ;EACjB,QAAQ,QAAQ;EAChB,QAAQ,QAAQ;CAClB,GACA;EACE,MAAM;EACN,SAAS;EACT,QAAQ;EACR;EACA,kBAAkB;EAClB,SAAS,UAAU;EACnB,QAAQ,UAAU;EAClB,QAAQ,UAAU;CACpB,CAGW;AACf;AAEA,MAAa,2BACX,IACA,UAC+B;CAC/B,MAAM,UAAU,GAAG;CACnB,MAAM,YAAY,GAAG;CAErB,IAAI,CAAC,QAAQ,OAAO,WAAW,CAAC,UAAU,OAAO,SAC/C;CAGF,IAAI,WAAW,GAAG,QAAQ,eAAe,MACtC,SAAS,KAAK,YAAY,QAAQ,GAAG,IACxC,CAAC,EAAE;CAEH,IAAI,CAAC,UAAU;EACb,MAAM,iBAAiB,OAAO,QAAQ,MACnC,WAAW,OAAO,QAAQ,GAAG,IAChC;EAEA,MAAM,mBAAmB,OAAO,UAAU,MACvC,aAAa,SAAS,QAAQ,GAAG,IACpC;EACA,WAAW;GACT,KAAK,GAAG;GACR,MAAM,gBAAgB,QAAQ,kBAAkB,QAAQ,GAAG;GAC3D,SAAS,gBAAgB,WAAW,kBAAkB,WAAW;EACnE;CACF;CAEA,MAAM,YAAyB;EAC7B,GAAG,QAAQ;EACX,QAAQ,OAAO,QAAQ,UAAU,CAAC;CACpC;CAEA,MAAM,UAAuB;EAC3B,GAAG,UAAU;EACb,QAAQ,OAAO,UAAU,UAAU,CAAC;CACtC;CAEA,MAAM,eAAe,QAAQ,QAAQ,OAAO,QAAQ,KAAK,IAAI;CAC7D,MAAM,mBACJ,QAAQ,SAAS,YAAY,QAAQ,MAAM,WAAW,QAAQ,SAC1D,eAAe,OAAO,QAAQ,MAAM,IACpC;CACN,MAAM,sBACJ,QAAQ,SAAS,YAAY,mBACzB,iBACE,kBACA,QAAQ,SAAS,UACjB,QAAQ,SAAS,QACnB,IACA;CAEN,MAAM,WAAkC,mBACpC,CACE;EACE,QAAQ,iBAAiB,SAAS;EAClC,WAAW,oBAAoB,QAAQ,CAAC;EACxC,OAAO,QAAQ;EACf,UAAU;EAEV,aAAa;EACb,MAAM;EACN,YAAY;CACd,CACF,IACA,KAAA;CAwGJ,OAAO;EArGL,QAAQ;EACR,OAAO;GACL,IAAK,GAAsB;GAC3B,aAAc,GAAsB;GACpC,WAAY,GAAsB;GAClC,aAAa,QAAQ;GACrB,YAAY,QAAQ,UAAU;GAC9B,eAAe,QAAQ,aAAa;GACpC,UAAU,UAAU,UAAU;GAC9B,aAAa,UAAU,UAAU;GACjC,aAAa,UAAU,aAAa;GACpC,WAAW,UAAU;GACrB;GACA;GACA,YAAY,QAAQ;GACpB,OAAO,CACL;IACE,IAAK,GAAsB;IAC3B,MAAM;IACN,MAAM,GAAG;IACT,aAAa;IACb,QAAQ;KACN,WAAW,QAAQ;KACnB,YAAY,QAAQ,UAAU;KAC9B,aAAa,QAAQ;KACrB,aAAc,GAAsB;KACpC,SAAS,UAAU;KACnB,WAAW,UAAU;KACrB,WAAY,GAAsB;KAClC,UAAU;IACZ;IACA,UAAU;KACR,MAAM,GAAG;KACT,iBAAiB;KACjB,YAAY,QAAQ,UAAU;KAC9B,eAAe,QAAQ,aAAa;KACpC,aAAa,UAAU,UAAU;KACjC,UAAU,UAAU,UAAU;KAC9B,aAAa,UAAU,aAAa;KACpC,mBAAmB;IACrB;IACA,eAAe,CACb;KACE,IAAK,GAAsB;KAC3B,MAAM,QAAQ,YAAY,UAAU,UAAU,SAAS;KACvD,QAAQ;MACN,aAAa,QAAQ;MACrB,YAAY,QAAQ,UAAU;MAC9B,WAAW,QAAQ;MACnB,WAAW,UAAU;MACrB,SAAS,UAAU;MACnB,UAAU;MACV,aAAc,GAAsB;MACpC,WAAY,GAAsB;KACpC;KACA,UAAU;MACR,MAAM,GAAG;MACT,YAAY,QAAQ,UAAU;MAC9B,UAAU,UAAU,UAAU;MAC9B,aAAa,UAAU,UAAU;MACjC,iBAAiB;MACjB,mBAAmB;KACrB;KACA,MAAM,GAAG;KACT,aAAa;IACf,CACF;IACA,YAAY,GAAG,UAAU,cAAc;IACvC,WAAW;KACT,QAAQ;KACR,WAAW,QAAQ,YACf,QAAQ,YAAY,MACpB,KAAK,IAAI;KACb,SAAS,0BAA0B,EAAE;KACrC,YAAY,QAAQ;KACpB,UAAU,UAAU;KACpB,SAAS,UAAU;KACnB,gBAAgB,GAAG;KACnB,gBAAgB,GAAG;KACnB,UAAU,CACR;MACE,QAAQ,QAAQ;MAChB,WAAW,QAAQ;MACnB,OAAO,QAAQ;MACf,UAAU;MACV,OAAO;MACP,OAAO;MACP,MAAM;KACR,CACF;KACA;IACF;GACF,CACF;GACA,WAAW;IACT,OAAO;IACP,cAAc;GAChB;EACF;CAGkB;AACtB"}
|
|
1
|
+
{"version":3,"file":"converters.js","names":[],"sources":["../../../src/utils/converters.ts"],"sourcesContent":["import type {\n ExecutionAction,\n ExecutionActionStatus,\n ExtendedChain,\n ExtendedTransactionInfo,\n FeeCost,\n FullStatusData,\n Substatus,\n TokenAmount,\n ToolsResponse,\n} from '@lifi/sdk'\nimport type { RouteExecution } from '../stores/routes/types.js'\nimport { formatTokenPrice, parseAmountToBigInt } from './format.js'\n\nconst buildActionsFromTxHistory = (tx: FullStatusData): ExecutionAction[] => {\n const sending = tx.sending as ExtendedTransactionInfo\n const receiving = tx.receiving as ExtendedTransactionInfo\n\n if (!sending.token?.chainId || !receiving.token?.chainId) {\n return []\n }\n\n const actionStatus: ExecutionActionStatus =\n tx.status === 'DONE' ? 'DONE' : 'FAILED'\n const substatus: Substatus =\n actionStatus === 'FAILED' ? 'UNKNOWN_ERROR' : 'COMPLETED'\n\n if (sending.chainId === receiving.chainId) {\n return [\n {\n type: 'SWAP', // operations on same chain will be swaps\n message: '',\n status: actionStatus,\n chainId: sending.chainId,\n txHash: sending.txHash,\n txLink: sending.txLink,\n substatus,\n substatusMessage: '',\n },\n ]\n }\n\n const actions: ExecutionAction[] = [\n {\n type: 'CROSS_CHAIN', // first step of bridging, ignoring the approvals\n message: '',\n status: actionStatus, // can be FAILED\n chainId: sending.chainId,\n txHash: sending.txHash,\n txLink: sending.txLink,\n },\n {\n type: 'RECEIVING_CHAIN', // final step of bridging, post swaps\n message: '',\n status: actionStatus,\n substatus,\n substatusMessage: '',\n chainId: receiving.chainId,\n txHash: receiving.txHash,\n txLink: receiving.txLink,\n },\n ]\n\n return actions\n}\n\nconst isValidFeeCost = (feeCost: FeeCost): boolean =>\n Boolean(feeCost.token) && parseAmountToBigInt(feeCost.amount) > 0n\n\nconst buildFeeCostsFromTxHistory = (\n tx: FullStatusData,\n fromChain?: ExtendedChain\n): FeeCost[] | undefined => {\n const feeCosts = tx.feeCosts?.filter(isValidFeeCost)\n if (feeCosts?.some((feeCost) => feeCost.included === false)) {\n return feeCosts\n }\n\n // These chains report value in native decimals, so it holds the sent amount\n if (fromChain?.nonStandardNativeDecimals) {\n return\n }\n\n const sending = tx.sending as ExtendedTransactionInfo\n const sendingValue = parseAmountToBigInt(sending.value)\n const sendingFeeAmount =\n sending.gasToken.address === sending.token?.address && sending.amount\n ? sendingValue - parseAmountToBigInt(sending.amount)\n : sendingValue\n\n if (sendingFeeAmount <= 0n) {\n return\n }\n\n return [\n {\n amount: sendingFeeAmount.toString(),\n amountUSD: formatTokenPrice(\n sendingFeeAmount,\n sending.gasToken.priceUSD,\n sending.gasToken.decimals\n ).toFixed(2),\n token: sending.gasToken,\n included: false,\n // Not used\n description: '',\n name: '',\n percentage: '',\n },\n ]\n}\n\nexport const buildRouteFromTxHistory = (\n tx: FullStatusData,\n tools?: ToolsResponse,\n fromChain?: ExtendedChain\n): RouteExecution | undefined => {\n const sending = tx.sending as ExtendedTransactionInfo\n const receiving = tx.receiving as ExtendedTransactionInfo\n\n if (!sending.token?.chainId || !receiving.token?.chainId) {\n return\n }\n\n let usedTool = tx.sending.includedSteps?.find(\n (step) => step.toolDetails.key === tx.tool\n )?.toolDetails\n\n if (!usedTool) {\n const selectedBridge = tools?.bridges.find(\n (bridge) => bridge.key === tx.tool\n )\n\n const selectedExchange = tools?.exchanges.find(\n (exchange) => exchange.key === tx.tool\n )\n usedTool = {\n key: tx.tool,\n name: selectedBridge?.name ?? selectedExchange?.name ?? tx.tool,\n logoURI: selectedBridge?.logoURI ?? selectedExchange?.logoURI ?? '',\n }\n }\n\n const fromToken: TokenAmount = {\n ...sending.token,\n amount: BigInt(sending.amount ?? 0),\n }\n\n const toToken: TokenAmount = {\n ...receiving.token,\n amount: BigInt(receiving.amount ?? 0),\n }\n\n const feeCosts = buildFeeCostsFromTxHistory(tx, fromChain)\n\n const routeExecution: RouteExecution = {\n status: 1,\n route: {\n id: (tx as FullStatusData).transactionId,\n fromAddress: (tx as FullStatusData).fromAddress,\n toAddress: (tx as FullStatusData).toAddress,\n fromChainId: sending.chainId,\n fromAmount: sending.amount ?? '',\n fromAmountUSD: sending.amountUSD ?? '',\n toAmount: receiving.amount ?? '',\n toAmountMin: receiving.amount ?? '',\n toAmountUSD: receiving.amountUSD ?? '',\n toChainId: receiving.chainId,\n fromToken,\n toToken,\n gasCostUSD: sending.gasAmountUSD,\n steps: [\n {\n id: (tx as FullStatusData).transactionId,\n type: 'lifi',\n tool: tx.tool,\n toolDetails: usedTool,\n action: {\n fromToken: sending.token,\n fromAmount: sending.amount ?? '',\n fromChainId: sending.chainId,\n fromAddress: (tx as FullStatusData).fromAddress,\n toToken: receiving.token,\n toChainId: receiving.chainId,\n toAddress: (tx as FullStatusData).toAddress,\n slippage: 0,\n },\n estimate: {\n tool: tx.tool,\n approvalAddress: '',\n fromAmount: sending.amount ?? '',\n fromAmountUSD: sending.amountUSD ?? '',\n toAmountMin: receiving.amount ?? '',\n toAmount: receiving.amount ?? '',\n toAmountUSD: receiving.amountUSD ?? '',\n executionDuration: 0,\n },\n includedSteps: [\n {\n id: (tx as FullStatusData).transactionId,\n type: sending.chainId === receiving.chainId ? 'swap' : 'cross',\n action: {\n fromChainId: sending.chainId,\n fromAmount: sending.amount ?? '',\n fromToken: sending.token,\n toChainId: receiving.chainId,\n toToken: receiving.token,\n slippage: 0,\n fromAddress: (tx as FullStatusData).fromAddress,\n toAddress: (tx as FullStatusData).toAddress,\n },\n estimate: {\n tool: tx.tool,\n fromAmount: sending.amount ?? '',\n toAmount: receiving.amount ?? '',\n toAmountMin: receiving.amount ?? '',\n approvalAddress: '',\n executionDuration: 0,\n },\n tool: tx.tool,\n toolDetails: usedTool,\n },\n ],\n integrator: tx.metadata?.integrator ?? '',\n execution: {\n status: 'DONE', // can be FAILED\n startedAt: sending.timestamp\n ? sending.timestamp * 1000\n : Date.now(),\n actions: buildActionsFromTxHistory(tx),\n fromAmount: sending.amount,\n toAmount: receiving.amount,\n toToken: receiving.token,\n internalTxLink: tx.lifiExplorerLink,\n externalTxLink: tx.bridgeExplorerLink,\n gasCosts: [\n {\n amount: sending.gasAmount,\n amountUSD: sending.gasAmountUSD,\n token: sending.gasToken,\n estimate: '0',\n limit: '0',\n price: '0',\n type: 'SEND',\n },\n ],\n feeCosts,\n },\n },\n ],\n insurance: {\n state: 'NOT_INSURABLE',\n feeAmountUsd: '0',\n },\n },\n }\n\n return routeExecution\n}\n"],"mappings":";;AAcA,MAAM,6BAA6B,OAA0C;CAC3E,MAAM,UAAU,GAAG;CACnB,MAAM,YAAY,GAAG;CAErB,IAAI,CAAC,QAAQ,OAAO,WAAW,CAAC,UAAU,OAAO,SAC/C,OAAO,CAAC;CAGV,MAAM,eACJ,GAAG,WAAW,SAAS,SAAS;CAClC,MAAM,YACJ,iBAAiB,WAAW,kBAAkB;CAEhD,IAAI,QAAQ,YAAY,UAAU,SAChC,OAAO,CACL;EACE,MAAM;EACN,SAAS;EACT,QAAQ;EACR,SAAS,QAAQ;EACjB,QAAQ,QAAQ;EAChB,QAAQ,QAAQ;EAChB;EACA,kBAAkB;CACpB,CACF;CAwBF,OAAO,CApBL;EACE,MAAM;EACN,SAAS;EACT,QAAQ;EACR,SAAS,QAAQ;EACjB,QAAQ,QAAQ;EAChB,QAAQ,QAAQ;CAClB,GACA;EACE,MAAM;EACN,SAAS;EACT,QAAQ;EACR;EACA,kBAAkB;EAClB,SAAS,UAAU;EACnB,QAAQ,UAAU;EAClB,QAAQ,UAAU;CACpB,CAGW;AACf;AAEA,MAAM,kBAAkB,YACtB,QAAQ,QAAQ,KAAK,KAAK,oBAAoB,QAAQ,MAAM,IAAI;AAElE,MAAM,8BACJ,IACA,cAC0B;CAC1B,MAAM,WAAW,GAAG,UAAU,OAAO,cAAc;CACnD,IAAI,UAAU,MAAM,YAAY,QAAQ,aAAa,KAAK,GACxD,OAAO;CAIT,IAAI,WAAW,2BACb;CAGF,MAAM,UAAU,GAAG;CACnB,MAAM,eAAe,oBAAoB,QAAQ,KAAK;CACtD,MAAM,mBACJ,QAAQ,SAAS,YAAY,QAAQ,OAAO,WAAW,QAAQ,SAC3D,eAAe,oBAAoB,QAAQ,MAAM,IACjD;CAEN,IAAI,oBAAoB,IACtB;CAGF,OAAO,CACL;EACE,QAAQ,iBAAiB,SAAS;EAClC,WAAW,iBACT,kBACA,QAAQ,SAAS,UACjB,QAAQ,SAAS,QACnB,CAAC,CAAC,QAAQ,CAAC;EACX,OAAO,QAAQ;EACf,UAAU;EAEV,aAAa;EACb,MAAM;EACN,YAAY;CACd,CACF;AACF;AAEA,MAAa,2BACX,IACA,OACA,cAC+B;CAC/B,MAAM,UAAU,GAAG;CACnB,MAAM,YAAY,GAAG;CAErB,IAAI,CAAC,QAAQ,OAAO,WAAW,CAAC,UAAU,OAAO,SAC/C;CAGF,IAAI,WAAW,GAAG,QAAQ,eAAe,MACtC,SAAS,KAAK,YAAY,QAAQ,GAAG,IACxC,CAAC,EAAE;CAEH,IAAI,CAAC,UAAU;EACb,MAAM,iBAAiB,OAAO,QAAQ,MACnC,WAAW,OAAO,QAAQ,GAAG,IAChC;EAEA,MAAM,mBAAmB,OAAO,UAAU,MACvC,aAAa,SAAS,QAAQ,GAAG,IACpC;EACA,WAAW;GACT,KAAK,GAAG;GACR,MAAM,gBAAgB,QAAQ,kBAAkB,QAAQ,GAAG;GAC3D,SAAS,gBAAgB,WAAW,kBAAkB,WAAW;EACnE;CACF;CAEA,MAAM,YAAyB;EAC7B,GAAG,QAAQ;EACX,QAAQ,OAAO,QAAQ,UAAU,CAAC;CACpC;CAEA,MAAM,UAAuB;EAC3B,GAAG,UAAU;EACb,QAAQ,OAAO,UAAU,UAAU,CAAC;CACtC;CAEA,MAAM,WAAW,2BAA2B,IAAI,SAAS;CAwGzD,OAAO;EArGL,QAAQ;EACR,OAAO;GACL,IAAK,GAAsB;GAC3B,aAAc,GAAsB;GACpC,WAAY,GAAsB;GAClC,aAAa,QAAQ;GACrB,YAAY,QAAQ,UAAU;GAC9B,eAAe,QAAQ,aAAa;GACpC,UAAU,UAAU,UAAU;GAC9B,aAAa,UAAU,UAAU;GACjC,aAAa,UAAU,aAAa;GACpC,WAAW,UAAU;GACrB;GACA;GACA,YAAY,QAAQ;GACpB,OAAO,CACL;IACE,IAAK,GAAsB;IAC3B,MAAM;IACN,MAAM,GAAG;IACT,aAAa;IACb,QAAQ;KACN,WAAW,QAAQ;KACnB,YAAY,QAAQ,UAAU;KAC9B,aAAa,QAAQ;KACrB,aAAc,GAAsB;KACpC,SAAS,UAAU;KACnB,WAAW,UAAU;KACrB,WAAY,GAAsB;KAClC,UAAU;IACZ;IACA,UAAU;KACR,MAAM,GAAG;KACT,iBAAiB;KACjB,YAAY,QAAQ,UAAU;KAC9B,eAAe,QAAQ,aAAa;KACpC,aAAa,UAAU,UAAU;KACjC,UAAU,UAAU,UAAU;KAC9B,aAAa,UAAU,aAAa;KACpC,mBAAmB;IACrB;IACA,eAAe,CACb;KACE,IAAK,GAAsB;KAC3B,MAAM,QAAQ,YAAY,UAAU,UAAU,SAAS;KACvD,QAAQ;MACN,aAAa,QAAQ;MACrB,YAAY,QAAQ,UAAU;MAC9B,WAAW,QAAQ;MACnB,WAAW,UAAU;MACrB,SAAS,UAAU;MACnB,UAAU;MACV,aAAc,GAAsB;MACpC,WAAY,GAAsB;KACpC;KACA,UAAU;MACR,MAAM,GAAG;MACT,YAAY,QAAQ,UAAU;MAC9B,UAAU,UAAU,UAAU;MAC9B,aAAa,UAAU,UAAU;MACjC,iBAAiB;MACjB,mBAAmB;KACrB;KACA,MAAM,GAAG;KACT,aAAa;IACf,CACF;IACA,YAAY,GAAG,UAAU,cAAc;IACvC,WAAW;KACT,QAAQ;KACR,WAAW,QAAQ,YACf,QAAQ,YAAY,MACpB,KAAK,IAAI;KACb,SAAS,0BAA0B,EAAE;KACrC,YAAY,QAAQ;KACpB,UAAU,UAAU;KACpB,SAAS,UAAU;KACnB,gBAAgB,GAAG;KACnB,gBAAgB,GAAG;KACnB,UAAU,CACR;MACE,QAAQ,QAAQ;MAChB,WAAW,QAAQ;MACnB,OAAO,QAAQ;MACf,UAAU;MACV,OAAO;MACP,OAAO;MACP,MAAM;KACR,CACF;KACA;IACF;GACF,CACF;GACA,WAAW;IACT,OAAO;IACP,cAAc;GAChB;EACF;CAGkB;AACtB"}
|
|
@@ -17,6 +17,20 @@ export declare function formatSlippage(slippage?: string, defaultValue?: string,
|
|
|
17
17
|
* @returns The formatted amount string, or empty string for invalid input.
|
|
18
18
|
*/
|
|
19
19
|
export declare function formatInputAmount(amount: string, decimals?: number | null, returnInitial?: boolean): string;
|
|
20
|
+
/**
|
|
21
|
+
* Convert an amount string denominated in a token's smallest unit to a bigint.
|
|
22
|
+
*
|
|
23
|
+
* Never route these amounts through `Number`: values above `Number.MAX_SAFE_INTEGER`
|
|
24
|
+
* lose precision, and from 1e21 upwards `Number#toFixed` returns exponential
|
|
25
|
+
* notation, which `BigInt` rejects outright.
|
|
26
|
+
*
|
|
27
|
+
* A smallest unit is indivisible, so a fractional amount carries no meaning. Should a
|
|
28
|
+
* provider report one anyway, the fraction is truncated textually — discarding nothing
|
|
29
|
+
* real, and without letting `BigInt` throw on it.
|
|
30
|
+
* @param amount amount in the token's smallest unit.
|
|
31
|
+
* @returns the amount as a bigint, or 0n when it is missing or not a plain decimal.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseAmountToBigInt(amount?: string): bigint;
|
|
20
34
|
export declare function formatTokenPrice(amount?: string | bigint, price?: string, decimals?: number): number;
|
|
21
35
|
/**
|
|
22
36
|
* Convert price value to token amount
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","names":[],"sources":["../../../src/utils/format.ts"],"mappings":";qBAEa;;;;;;wBAOG,kBACd,4BACA;wBAWc,eACd,mBACA,uBACA;;;;;;;;;;wBAiCc,kBACd,gBACA,0BACA;
|
|
1
|
+
{"version":3,"file":"format.d.ts","names":[],"sources":["../../../src/utils/format.ts"],"mappings":";qBAEa;;;;;;wBAOG,kBACd,4BACA;wBAWc,eACd,mBACA,uBACA;;;;;;;;;;wBAiCc,kBACd,gBACA,0BACA;;;;;;;;;;;;;;wBAqEc,oBAAoB;wBAepB,iBACd,0BACA,gBACA;;;;wBA+Bc,mBACd,oBACA;wBA2Bc,eACd,iBACA,gBACA,cAAa,KAAK;wBAaJ,cAAc"}
|
package/dist/esm/utils/format.js
CHANGED
|
@@ -49,6 +49,30 @@ function formatInputAmount(amount, decimals = null, returnInitial = false) {
|
|
|
49
49
|
fraction = fraction.replace(/(0+)$/, "");
|
|
50
50
|
return `${integer || (fraction ? "0" : "")}${fraction ? `.${fraction}` : ""}`;
|
|
51
51
|
}
|
|
52
|
+
/** Matches a plain decimal number, i.e. no exponent and no other characters. */
|
|
53
|
+
const plainDecimalRegExp = /^-?\d+(?:\.\d+)?$/;
|
|
54
|
+
/**
|
|
55
|
+
* Convert an amount string denominated in a token's smallest unit to a bigint.
|
|
56
|
+
*
|
|
57
|
+
* Never route these amounts through `Number`: values above `Number.MAX_SAFE_INTEGER`
|
|
58
|
+
* lose precision, and from 1e21 upwards `Number#toFixed` returns exponential
|
|
59
|
+
* notation, which `BigInt` rejects outright.
|
|
60
|
+
*
|
|
61
|
+
* A smallest unit is indivisible, so a fractional amount carries no meaning. Should a
|
|
62
|
+
* provider report one anyway, the fraction is truncated textually — discarding nothing
|
|
63
|
+
* real, and without letting `BigInt` throw on it.
|
|
64
|
+
* @param amount amount in the token's smallest unit.
|
|
65
|
+
* @returns the amount as a bigint, or 0n when it is missing or not a plain decimal.
|
|
66
|
+
*/
|
|
67
|
+
function parseAmountToBigInt(amount) {
|
|
68
|
+
const trimmedAmount = amount?.trim();
|
|
69
|
+
if (!trimmedAmount) return 0n;
|
|
70
|
+
if (!plainDecimalRegExp.test(trimmedAmount)) {
|
|
71
|
+
console.warn("Unparseable token amount:", amount);
|
|
72
|
+
return 0n;
|
|
73
|
+
}
|
|
74
|
+
return BigInt(trimmedAmount.split(".")[0]);
|
|
75
|
+
}
|
|
52
76
|
function formatTokenPrice(amount, price, decimals) {
|
|
53
77
|
if (!amount || !price) return 0;
|
|
54
78
|
const formattedAmount = typeof amount === "bigint" && decimals !== void 0 ? formatUnits(amount, decimals) : amount.toString();
|
|
@@ -95,6 +119,6 @@ function wrapLongWords(text) {
|
|
|
95
119
|
return text.replace(/\S{32,}/g, (word) => `${word.slice(0, 8)}...${word.slice(-4)}`);
|
|
96
120
|
}
|
|
97
121
|
//#endregion
|
|
98
|
-
export { formatDuration, formatInputAmount, formatSlippage, formatTokenAmount, formatTokenPrice, priceToTokenAmount, usdDecimals, wrapLongWords };
|
|
122
|
+
export { formatDuration, formatInputAmount, formatSlippage, formatTokenAmount, formatTokenPrice, parseAmountToBigInt, priceToTokenAmount, usdDecimals, wrapLongWords };
|
|
99
123
|
|
|
100
124
|
//# sourceMappingURL=format.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","names":[],"sources":["../../../src/utils/format.ts"],"sourcesContent":["import { formatUnits } from '@lifi/sdk'\n\nexport const usdDecimals = 2\n\n/**\n * Format token amount to at least 6 decimals.\n * @param amount amount to format.\n * @returns formatted amount.\n */\nexport function formatTokenAmount(\n amount: bigint | undefined,\n decimals: number\n): string {\n const formattedAmount = amount ? formatUnits(amount, decimals) : '0'\n const parsedAmount = Number.parseFloat(formattedAmount)\n if (!parsedAmount || Number.isNaN(Number(formattedAmount))) {\n return '0'\n }\n\n return formattedAmount\n}\n\nexport function formatSlippage(\n slippage = '',\n defaultValue = '',\n returnInitial = false\n): string {\n if (!slippage) {\n return slippage\n }\n const parsedSlippage = Number.parseFloat(slippage)\n if (Number.isNaN(Number(slippage)) && !Number.isNaN(parsedSlippage)) {\n return parsedSlippage.toString()\n }\n if (Number.isNaN(parsedSlippage)) {\n return defaultValue\n }\n if (parsedSlippage >= 100) {\n return '100'\n }\n if (parsedSlippage < 0) {\n return Math.abs(parsedSlippage).toString()\n }\n if (returnInitial) {\n return slippage\n }\n return parsedSlippage.toString()\n}\n\n/**\n * Formats a user input amount string, normalizing it and optionally limiting decimal places.\n * @param amount - The amount string to format (e.g., '123.45', '1,23', '0..')\n * @param decimals - Maximum number of decimal places to allow. If null, no limit is applied.\n * @param returnInitial - If true, preserves the input format during typing (e.g., keeps trailing dots,\n * preserves leading zeros, keeps negative signs). If false (default), cleans up the\n * format on blur (removes leading/trailing zeros, removes negative signs).\n * @returns The formatted amount string, or empty string for invalid input.\n */\nexport function formatInputAmount(\n amount: string,\n decimals: number | null = null,\n returnInitial: boolean = false\n): string {\n if (!amount) {\n return amount\n }\n\n // Replace commas with dots\n let formattedAmount = amount.trim().replaceAll(',', '.')\n\n // Keep only the first dot, remove all subsequent dots\n const dotIndex = formattedAmount.indexOf('.')\n if (dotIndex !== -1) {\n formattedAmount = `${formattedAmount.slice(0, dotIndex + 1)}${formattedAmount.slice(dotIndex + 1).replaceAll('.', '')}`\n }\n\n // If the amount starts with a dot, prepend 0\n if (\n (!returnInitial && formattedAmount.startsWith('.')) ||\n formattedAmount === '.'\n ) {\n formattedAmount = `0${formattedAmount}`\n }\n\n // Parse the valid part of the amount\n const parsedAmount = Number.parseFloat(formattedAmount)\n if (Number.isNaN(Number(formattedAmount)) && !Number.isNaN(parsedAmount)) {\n formattedAmount = parsedAmount.toString()\n }\n if (Number.isNaN(Math.abs(Number(formattedAmount)))) {\n return ''\n }\n\n // Split and limit decimals\n let [integer, fraction = ''] = formattedAmount.split('.')\n if (decimals !== null && fraction.length > decimals) {\n fraction = fraction.slice(0, decimals)\n }\n\n if (returnInitial) {\n if (!fraction) {\n return formattedAmount\n }\n return `${integer}${fraction ? `.${fraction}` : ''}`\n }\n\n // Remove leading zeros and minus sign\n integer = integer.replace(/^0+|-/, '')\n // Remove trailing zeros\n fraction = fraction.replace(/(0+)$/, '')\n\n return `${integer || (fraction ? '0' : '')}${fraction ? `.${fraction}` : ''}`\n}\n\nexport function formatTokenPrice(\n amount?: string | bigint,\n price?: string,\n decimals?: number\n): number {\n if (!amount || !price) {\n return 0\n }\n\n const formattedAmount =\n typeof amount === 'bigint' && decimals !== undefined\n ? formatUnits(amount, decimals)\n : amount.toString()\n\n if (Number.isNaN(Number(formattedAmount)) || Number.isNaN(Number(price))) {\n return 0\n }\n return Number.parseFloat(formattedAmount) * Number.parseFloat(price)\n}\n\n/**\n * Formatter for price value to token amount\n */\nconst formatter = new Intl.NumberFormat('en', {\n notation: 'standard',\n roundingPriority: 'morePrecision',\n maximumSignificantDigits: 20,\n maximumFractionDigits: 20,\n useGrouping: false,\n})\n\n/**\n * Convert price value to token amount\n */\nexport function priceToTokenAmount(\n priceValue: string,\n priceUSD?: string\n): string {\n if (!priceValue || !priceUSD) {\n return '0'\n }\n\n const numericPriceValue = Number.parseFloat(priceValue)\n const numericPrice = Number.parseFloat(priceUSD)\n\n if (\n Number.isNaN(numericPriceValue) ||\n Number.isNaN(numericPrice) ||\n !numericPrice\n ) {\n return '0'\n }\n\n return formatter.format(numericPriceValue / numericPrice)\n}\n\nconst units = [\n ['day', 86400],\n ['hour', 3600],\n ['minute', 60],\n ['second', 1],\n] as const\n\nexport function formatDuration(\n seconds: number,\n locale: string,\n unitDisplay: Intl.NumberFormatOptions['unitDisplay'] = 'narrow'\n): string {\n const match = units.find(([, v]) => seconds >= v)\n const amount = match ? Math.floor(seconds / match[1]) : 0\n const unit = match?.[0] ?? 'second'\n\n return amount.toLocaleString(locale, {\n style: 'unit',\n unit,\n unitDisplay,\n })\n}\n\nexport function wrapLongWords(text: string): string {\n return text.replace(\n /\\S{32,}/g,\n (word) => `${word.slice(0, 8)}...${word.slice(-4)}`\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"format.js","names":[],"sources":["../../../src/utils/format.ts"],"sourcesContent":["import { formatUnits } from '@lifi/sdk'\n\nexport const usdDecimals = 2\n\n/**\n * Format token amount to at least 6 decimals.\n * @param amount amount to format.\n * @returns formatted amount.\n */\nexport function formatTokenAmount(\n amount: bigint | undefined,\n decimals: number\n): string {\n const formattedAmount = amount ? formatUnits(amount, decimals) : '0'\n const parsedAmount = Number.parseFloat(formattedAmount)\n if (!parsedAmount || Number.isNaN(Number(formattedAmount))) {\n return '0'\n }\n\n return formattedAmount\n}\n\nexport function formatSlippage(\n slippage = '',\n defaultValue = '',\n returnInitial = false\n): string {\n if (!slippage) {\n return slippage\n }\n const parsedSlippage = Number.parseFloat(slippage)\n if (Number.isNaN(Number(slippage)) && !Number.isNaN(parsedSlippage)) {\n return parsedSlippage.toString()\n }\n if (Number.isNaN(parsedSlippage)) {\n return defaultValue\n }\n if (parsedSlippage >= 100) {\n return '100'\n }\n if (parsedSlippage < 0) {\n return Math.abs(parsedSlippage).toString()\n }\n if (returnInitial) {\n return slippage\n }\n return parsedSlippage.toString()\n}\n\n/**\n * Formats a user input amount string, normalizing it and optionally limiting decimal places.\n * @param amount - The amount string to format (e.g., '123.45', '1,23', '0..')\n * @param decimals - Maximum number of decimal places to allow. If null, no limit is applied.\n * @param returnInitial - If true, preserves the input format during typing (e.g., keeps trailing dots,\n * preserves leading zeros, keeps negative signs). If false (default), cleans up the\n * format on blur (removes leading/trailing zeros, removes negative signs).\n * @returns The formatted amount string, or empty string for invalid input.\n */\nexport function formatInputAmount(\n amount: string,\n decimals: number | null = null,\n returnInitial: boolean = false\n): string {\n if (!amount) {\n return amount\n }\n\n // Replace commas with dots\n let formattedAmount = amount.trim().replaceAll(',', '.')\n\n // Keep only the first dot, remove all subsequent dots\n const dotIndex = formattedAmount.indexOf('.')\n if (dotIndex !== -1) {\n formattedAmount = `${formattedAmount.slice(0, dotIndex + 1)}${formattedAmount.slice(dotIndex + 1).replaceAll('.', '')}`\n }\n\n // If the amount starts with a dot, prepend 0\n if (\n (!returnInitial && formattedAmount.startsWith('.')) ||\n formattedAmount === '.'\n ) {\n formattedAmount = `0${formattedAmount}`\n }\n\n // Parse the valid part of the amount\n const parsedAmount = Number.parseFloat(formattedAmount)\n if (Number.isNaN(Number(formattedAmount)) && !Number.isNaN(parsedAmount)) {\n formattedAmount = parsedAmount.toString()\n }\n if (Number.isNaN(Math.abs(Number(formattedAmount)))) {\n return ''\n }\n\n // Split and limit decimals\n let [integer, fraction = ''] = formattedAmount.split('.')\n if (decimals !== null && fraction.length > decimals) {\n fraction = fraction.slice(0, decimals)\n }\n\n if (returnInitial) {\n if (!fraction) {\n return formattedAmount\n }\n return `${integer}${fraction ? `.${fraction}` : ''}`\n }\n\n // Remove leading zeros and minus sign\n integer = integer.replace(/^0+|-/, '')\n // Remove trailing zeros\n fraction = fraction.replace(/(0+)$/, '')\n\n return `${integer || (fraction ? '0' : '')}${fraction ? `.${fraction}` : ''}`\n}\n\n/** Matches a plain decimal number, i.e. no exponent and no other characters. */\nconst plainDecimalRegExp = /^-?\\d+(?:\\.\\d+)?$/\n\n/**\n * Convert an amount string denominated in a token's smallest unit to a bigint.\n *\n * Never route these amounts through `Number`: values above `Number.MAX_SAFE_INTEGER`\n * lose precision, and from 1e21 upwards `Number#toFixed` returns exponential\n * notation, which `BigInt` rejects outright.\n *\n * A smallest unit is indivisible, so a fractional amount carries no meaning. Should a\n * provider report one anyway, the fraction is truncated textually — discarding nothing\n * real, and without letting `BigInt` throw on it.\n * @param amount amount in the token's smallest unit.\n * @returns the amount as a bigint, or 0n when it is missing or not a plain decimal.\n */\nexport function parseAmountToBigInt(amount?: string): bigint {\n const trimmedAmount = amount?.trim()\n if (!trimmedAmount) {\n return 0n\n }\n if (!plainDecimalRegExp.test(trimmedAmount)) {\n // Falling back to 0n keeps the widget rendering, but it under-reports gas and\n // fee totals — surface it rather than letting a bad amount vanish silently\n console.warn('Unparseable token amount:', amount)\n return 0n\n }\n // Smallest units are indivisible, so drop any fractional part\n return BigInt(trimmedAmount.split('.')[0])\n}\n\nexport function formatTokenPrice(\n amount?: string | bigint,\n price?: string,\n decimals?: number\n): number {\n if (!amount || !price) {\n return 0\n }\n\n const formattedAmount =\n typeof amount === 'bigint' && decimals !== undefined\n ? formatUnits(amount, decimals)\n : amount.toString()\n\n if (Number.isNaN(Number(formattedAmount)) || Number.isNaN(Number(price))) {\n return 0\n }\n return Number.parseFloat(formattedAmount) * Number.parseFloat(price)\n}\n\n/**\n * Formatter for price value to token amount\n */\nconst formatter = new Intl.NumberFormat('en', {\n notation: 'standard',\n roundingPriority: 'morePrecision',\n maximumSignificantDigits: 20,\n maximumFractionDigits: 20,\n useGrouping: false,\n})\n\n/**\n * Convert price value to token amount\n */\nexport function priceToTokenAmount(\n priceValue: string,\n priceUSD?: string\n): string {\n if (!priceValue || !priceUSD) {\n return '0'\n }\n\n const numericPriceValue = Number.parseFloat(priceValue)\n const numericPrice = Number.parseFloat(priceUSD)\n\n if (\n Number.isNaN(numericPriceValue) ||\n Number.isNaN(numericPrice) ||\n !numericPrice\n ) {\n return '0'\n }\n\n return formatter.format(numericPriceValue / numericPrice)\n}\n\nconst units = [\n ['day', 86400],\n ['hour', 3600],\n ['minute', 60],\n ['second', 1],\n] as const\n\nexport function formatDuration(\n seconds: number,\n locale: string,\n unitDisplay: Intl.NumberFormatOptions['unitDisplay'] = 'narrow'\n): string {\n const match = units.find(([, v]) => seconds >= v)\n const amount = match ? Math.floor(seconds / match[1]) : 0\n const unit = match?.[0] ?? 'second'\n\n return amount.toLocaleString(locale, {\n style: 'unit',\n unit,\n unitDisplay,\n })\n}\n\nexport function wrapLongWords(text: string): string {\n return text.replace(\n /\\S{32,}/g,\n (word) => `${word.slice(0, 8)}...${word.slice(-4)}`\n )\n}\n"],"mappings":";;AAEA,MAAa,cAAc;;;;;;AAO3B,SAAgB,kBACd,QACA,UACQ;CACR,MAAM,kBAAkB,SAAS,YAAY,QAAQ,QAAQ,IAAI;CAEjE,IAAI,CADiB,OAAO,WAAW,eACvB,KAAK,OAAO,MAAM,OAAO,eAAe,CAAC,GACvD,OAAO;CAGT,OAAO;AACT;AAEA,SAAgB,eACd,WAAW,IACX,eAAe,IACf,gBAAgB,OACR;CACR,IAAI,CAAC,UACH,OAAO;CAET,MAAM,iBAAiB,OAAO,WAAW,QAAQ;CACjD,IAAI,OAAO,MAAM,OAAO,QAAQ,CAAC,KAAK,CAAC,OAAO,MAAM,cAAc,GAChE,OAAO,eAAe,SAAS;CAEjC,IAAI,OAAO,MAAM,cAAc,GAC7B,OAAO;CAET,IAAI,kBAAkB,KACpB,OAAO;CAET,IAAI,iBAAiB,GACnB,OAAO,KAAK,IAAI,cAAc,CAAC,CAAC,SAAS;CAE3C,IAAI,eACF,OAAO;CAET,OAAO,eAAe,SAAS;AACjC;;;;;;;;;;AAWA,SAAgB,kBACd,QACA,WAA0B,MAC1B,gBAAyB,OACjB;CACR,IAAI,CAAC,QACH,OAAO;CAIT,IAAI,kBAAkB,OAAO,KAAK,CAAC,CAAC,WAAW,KAAK,GAAG;CAGvD,MAAM,WAAW,gBAAgB,QAAQ,GAAG;CAC5C,IAAI,aAAa,IACf,kBAAkB,GAAG,gBAAgB,MAAM,GAAG,WAAW,CAAC,IAAI,gBAAgB,MAAM,WAAW,CAAC,CAAC,CAAC,WAAW,KAAK,EAAE;CAItH,IACG,CAAC,iBAAiB,gBAAgB,WAAW,GAAG,KACjD,oBAAoB,KAEpB,kBAAkB,IAAI;CAIxB,MAAM,eAAe,OAAO,WAAW,eAAe;CACtD,IAAI,OAAO,MAAM,OAAO,eAAe,CAAC,KAAK,CAAC,OAAO,MAAM,YAAY,GACrE,kBAAkB,aAAa,SAAS;CAE1C,IAAI,OAAO,MAAM,KAAK,IAAI,OAAO,eAAe,CAAC,CAAC,GAChD,OAAO;CAIT,IAAI,CAAC,SAAS,WAAW,MAAM,gBAAgB,MAAM,GAAG;CACxD,IAAI,aAAa,QAAQ,SAAS,SAAS,UACzC,WAAW,SAAS,MAAM,GAAG,QAAQ;CAGvC,IAAI,eAAe;EACjB,IAAI,CAAC,UACH,OAAO;EAET,OAAO,GAAG,UAAU,WAAW,IAAI,aAAa;CAClD;CAGA,UAAU,QAAQ,QAAQ,SAAS,EAAE;CAErC,WAAW,SAAS,QAAQ,SAAS,EAAE;CAEvC,OAAO,GAAG,YAAY,WAAW,MAAM,MAAM,WAAW,IAAI,aAAa;AAC3E;;AAGA,MAAM,qBAAqB;;;;;;;;;;;;;;AAe3B,SAAgB,oBAAoB,QAAyB;CAC3D,MAAM,gBAAgB,QAAQ,KAAK;CACnC,IAAI,CAAC,eACH,OAAO;CAET,IAAI,CAAC,mBAAmB,KAAK,aAAa,GAAG;EAG3C,QAAQ,KAAK,6BAA6B,MAAM;EAChD,OAAO;CACT;CAEA,OAAO,OAAO,cAAc,MAAM,GAAG,CAAC,CAAC,EAAE;AAC3C;AAEA,SAAgB,iBACd,QACA,OACA,UACQ;CACR,IAAI,CAAC,UAAU,CAAC,OACd,OAAO;CAGT,MAAM,kBACJ,OAAO,WAAW,YAAY,aAAa,KAAA,IACvC,YAAY,QAAQ,QAAQ,IAC5B,OAAO,SAAS;CAEtB,IAAI,OAAO,MAAM,OAAO,eAAe,CAAC,KAAK,OAAO,MAAM,OAAO,KAAK,CAAC,GACrE,OAAO;CAET,OAAO,OAAO,WAAW,eAAe,IAAI,OAAO,WAAW,KAAK;AACrE;;;;AAKA,MAAM,YAAY,IAAI,KAAK,aAAa,MAAM;CAC5C,UAAU;CACV,kBAAkB;CAClB,0BAA0B;CAC1B,uBAAuB;CACvB,aAAa;AACf,CAAC;;;;AAKD,SAAgB,mBACd,YACA,UACQ;CACR,IAAI,CAAC,cAAc,CAAC,UAClB,OAAO;CAGT,MAAM,oBAAoB,OAAO,WAAW,UAAU;CACtD,MAAM,eAAe,OAAO,WAAW,QAAQ;CAE/C,IACE,OAAO,MAAM,iBAAiB,KAC9B,OAAO,MAAM,YAAY,KACzB,CAAC,cAED,OAAO;CAGT,OAAO,UAAU,OAAO,oBAAoB,YAAY;AAC1D;AAEA,MAAM,QAAQ;CACZ,CAAC,OAAO,KAAK;CACb,CAAC,QAAQ,IAAI;CACb,CAAC,UAAU,EAAE;CACb,CAAC,UAAU,CAAC;AACd;AAEA,SAAgB,eACd,SACA,QACA,cAAuD,UAC/C;CACR,MAAM,QAAQ,MAAM,MAAM,GAAG,OAAO,WAAW,CAAC;CAChD,MAAM,SAAS,QAAQ,KAAK,MAAM,UAAU,MAAM,EAAE,IAAI;CACxD,MAAM,OAAO,QAAQ,MAAM;CAE3B,OAAO,OAAO,eAAe,QAAQ;EACnC,OAAO;EACP;EACA;CACF,CAAC;AACH;AAEA,SAAgB,cAAc,MAAsB;CAClD,OAAO,KAAK,QACV,aACC,SAAS,GAAG,KAAK,MAAM,GAAG,CAAC,EAAE,KAAK,KAAK,MAAM,EAAE,GAClD;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifi/widget",
|
|
3
|
-
"version": "4.10.
|
|
3
|
+
"version": "4.10.3",
|
|
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",
|
|
@@ -58,18 +58,18 @@
|
|
|
58
58
|
"@emotion/react": "^11.14.0",
|
|
59
59
|
"@emotion/styled": "^11.14.1",
|
|
60
60
|
"@lifi/sdk": "^4.12.1",
|
|
61
|
-
"@lifi/wallet-management": "4.3.
|
|
61
|
+
"@lifi/wallet-management": "4.3.5",
|
|
62
62
|
"@lifi/widget-provider": "4.6.2",
|
|
63
63
|
"@mui/icons-material": "^9.5.0",
|
|
64
64
|
"@mui/material": "^9.5.0",
|
|
65
65
|
"@mui/system": "^9.5.0",
|
|
66
|
-
"@tanstack/react-router": "^1.170.
|
|
67
|
-
"@tanstack/react-virtual": "^3.14.
|
|
66
|
+
"@tanstack/react-router": "^1.170.45",
|
|
67
|
+
"@tanstack/react-virtual": "^3.14.21",
|
|
68
68
|
"eventemitter3": "^5.0.4",
|
|
69
|
-
"i18next": "^26.
|
|
69
|
+
"i18next": "^26.5.2",
|
|
70
70
|
"microdiff": "^1.6.0",
|
|
71
|
-
"motion": "^14.
|
|
72
|
-
"react-i18next": "^17.0
|
|
71
|
+
"motion": "^14.4.0",
|
|
72
|
+
"react-i18next": "^17.1.0",
|
|
73
73
|
"react-intersection-observer": "^11.0.1",
|
|
74
74
|
"react-transition-group": "^4.4.5",
|
|
75
75
|
"zustand": "^5.0.15"
|
package/src/config/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@lifi/widget'
|
|
2
|
-
export const version = '4.10.
|
|
2
|
+
export const version = '4.10.3'
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
import { getSourceTxHash } from '../stores/routes/utils.js'
|
|
15
15
|
import { buildRouteFromTxHistory } from '../utils/converters.js'
|
|
16
16
|
import { getQueryKey } from '../utils/queries.js'
|
|
17
|
+
import { useAvailableChains } from './useAvailableChains.js'
|
|
17
18
|
import { useTools } from './useTools.js'
|
|
18
19
|
|
|
19
20
|
export const useTransactionHistory = (): {
|
|
@@ -25,6 +26,7 @@ export const useTransactionHistory = (): {
|
|
|
25
26
|
const { storageScopeKey } = useWidgetConfig()
|
|
26
27
|
const sdkClient = useSDKClient()
|
|
27
28
|
const { tools } = useTools()
|
|
29
|
+
const { getChainById } = useAvailableChains()
|
|
28
30
|
|
|
29
31
|
const { data: routeExecutions, isLoading } = useQueries({
|
|
30
32
|
queries: accounts.map((account) => ({
|
|
@@ -77,7 +79,8 @@ export const useTransactionHistory = (): {
|
|
|
77
79
|
(transaction) => {
|
|
78
80
|
const routeExecution = buildRouteFromTxHistory(
|
|
79
81
|
transaction as FullStatusData,
|
|
80
|
-
tools
|
|
82
|
+
tools,
|
|
83
|
+
getChainById(transaction.sending.chainId)
|
|
81
84
|
)
|
|
82
85
|
return routeExecution ? [routeExecution] : []
|
|
83
86
|
}
|
|
@@ -6,6 +6,7 @@ import { ContractComponent } from '../../components/ContractComponent/ContractCo
|
|
|
6
6
|
import { PageContainer } from '../../components/PageContainer.js'
|
|
7
7
|
import { TransactionCard } from '../../components/TransactionCard/TransactionCard.js'
|
|
8
8
|
import { internalExplorerUrl } from '../../config/constants.js'
|
|
9
|
+
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
|
|
9
10
|
import { useExplorer } from '../../hooks/useExplorer.js'
|
|
10
11
|
import { useHeader } from '../../hooks/useHeader.js'
|
|
11
12
|
import { useTools } from '../../hooks/useTools.js'
|
|
@@ -27,6 +28,7 @@ export const TransactionDetailsPage: React.FC = () => {
|
|
|
27
28
|
useWidgetConfig()
|
|
28
29
|
const { search }: any = useLocation()
|
|
29
30
|
const { tools } = useTools()
|
|
31
|
+
const { getChainById } = useAvailableChains()
|
|
30
32
|
const { getTransactionLink } = useExplorer()
|
|
31
33
|
const storedRouteExecution = useRouteExecutionStore((store) =>
|
|
32
34
|
search?.routeId ? store.routes[search.routeId] : undefined
|
|
@@ -49,9 +51,13 @@ export const TransactionDetailsPage: React.FC = () => {
|
|
|
49
51
|
return
|
|
50
52
|
}
|
|
51
53
|
if (transaction) {
|
|
52
|
-
return buildRouteFromTxHistory(
|
|
54
|
+
return buildRouteFromTxHistory(
|
|
55
|
+
transaction as FullStatusData,
|
|
56
|
+
tools,
|
|
57
|
+
getChainById(transaction.sending.chainId)
|
|
58
|
+
)
|
|
53
59
|
}
|
|
54
|
-
}, [isLoading, storedRouteExecution, tools, transaction])
|
|
60
|
+
}, [getChainById, isLoading, storedRouteExecution, tools, transaction])
|
|
55
61
|
|
|
56
62
|
useEffect(() => {
|
|
57
63
|
if (!isLoading && !routeExecution) {
|
package/src/utils/converters.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ExecutionAction,
|
|
3
3
|
ExecutionActionStatus,
|
|
4
|
+
ExtendedChain,
|
|
4
5
|
ExtendedTransactionInfo,
|
|
5
6
|
FeeCost,
|
|
6
7
|
FullStatusData,
|
|
@@ -9,7 +10,7 @@ import type {
|
|
|
9
10
|
ToolsResponse,
|
|
10
11
|
} from '@lifi/sdk'
|
|
11
12
|
import type { RouteExecution } from '../stores/routes/types.js'
|
|
12
|
-
import { formatTokenPrice } from './format.js'
|
|
13
|
+
import { formatTokenPrice, parseAmountToBigInt } from './format.js'
|
|
13
14
|
|
|
14
15
|
const buildActionsFromTxHistory = (tx: FullStatusData): ExecutionAction[] => {
|
|
15
16
|
const sending = tx.sending as ExtendedTransactionInfo
|
|
@@ -63,9 +64,56 @@ const buildActionsFromTxHistory = (tx: FullStatusData): ExecutionAction[] => {
|
|
|
63
64
|
return actions
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
const isValidFeeCost = (feeCost: FeeCost): boolean =>
|
|
68
|
+
Boolean(feeCost.token) && parseAmountToBigInt(feeCost.amount) > 0n
|
|
69
|
+
|
|
70
|
+
const buildFeeCostsFromTxHistory = (
|
|
71
|
+
tx: FullStatusData,
|
|
72
|
+
fromChain?: ExtendedChain
|
|
73
|
+
): FeeCost[] | undefined => {
|
|
74
|
+
const feeCosts = tx.feeCosts?.filter(isValidFeeCost)
|
|
75
|
+
if (feeCosts?.some((feeCost) => feeCost.included === false)) {
|
|
76
|
+
return feeCosts
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// These chains report value in native decimals, so it holds the sent amount
|
|
80
|
+
if (fromChain?.nonStandardNativeDecimals) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const sending = tx.sending as ExtendedTransactionInfo
|
|
85
|
+
const sendingValue = parseAmountToBigInt(sending.value)
|
|
86
|
+
const sendingFeeAmount =
|
|
87
|
+
sending.gasToken.address === sending.token?.address && sending.amount
|
|
88
|
+
? sendingValue - parseAmountToBigInt(sending.amount)
|
|
89
|
+
: sendingValue
|
|
90
|
+
|
|
91
|
+
if (sendingFeeAmount <= 0n) {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return [
|
|
96
|
+
{
|
|
97
|
+
amount: sendingFeeAmount.toString(),
|
|
98
|
+
amountUSD: formatTokenPrice(
|
|
99
|
+
sendingFeeAmount,
|
|
100
|
+
sending.gasToken.priceUSD,
|
|
101
|
+
sending.gasToken.decimals
|
|
102
|
+
).toFixed(2),
|
|
103
|
+
token: sending.gasToken,
|
|
104
|
+
included: false,
|
|
105
|
+
// Not used
|
|
106
|
+
description: '',
|
|
107
|
+
name: '',
|
|
108
|
+
percentage: '',
|
|
109
|
+
},
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
|
|
66
113
|
export const buildRouteFromTxHistory = (
|
|
67
114
|
tx: FullStatusData,
|
|
68
|
-
tools?: ToolsResponse
|
|
115
|
+
tools?: ToolsResponse,
|
|
116
|
+
fromChain?: ExtendedChain
|
|
69
117
|
): RouteExecution | undefined => {
|
|
70
118
|
const sending = tx.sending as ExtendedTransactionInfo
|
|
71
119
|
const receiving = tx.receiving as ExtendedTransactionInfo
|
|
@@ -103,34 +151,7 @@ export const buildRouteFromTxHistory = (
|
|
|
103
151
|
amount: BigInt(receiving.amount ?? 0),
|
|
104
152
|
}
|
|
105
153
|
|
|
106
|
-
const
|
|
107
|
-
const sendingFeeAmount =
|
|
108
|
-
sending.gasToken.address === sending.token.address && sending.amount
|
|
109
|
-
? sendingValue - BigInt(sending.amount)
|
|
110
|
-
: sendingValue
|
|
111
|
-
const sendingFeeAmountUsd =
|
|
112
|
-
sending.gasToken.priceUSD && sendingFeeAmount
|
|
113
|
-
? formatTokenPrice(
|
|
114
|
-
sendingFeeAmount,
|
|
115
|
-
sending.gasToken.priceUSD,
|
|
116
|
-
sending.gasToken.decimals
|
|
117
|
-
)
|
|
118
|
-
: 0
|
|
119
|
-
|
|
120
|
-
const feeCosts: FeeCost[] | undefined = sendingFeeAmount
|
|
121
|
-
? [
|
|
122
|
-
{
|
|
123
|
-
amount: sendingFeeAmount.toString(),
|
|
124
|
-
amountUSD: sendingFeeAmountUsd.toFixed(2),
|
|
125
|
-
token: sending.gasToken,
|
|
126
|
-
included: false,
|
|
127
|
-
// Not used
|
|
128
|
-
description: '',
|
|
129
|
-
name: '',
|
|
130
|
-
percentage: '',
|
|
131
|
-
},
|
|
132
|
-
]
|
|
133
|
-
: undefined
|
|
154
|
+
const feeCosts = buildFeeCostsFromTxHistory(tx, fromChain)
|
|
134
155
|
|
|
135
156
|
const routeExecution: RouteExecution = {
|
|
136
157
|
status: 1,
|
package/src/utils/format.ts
CHANGED
|
@@ -112,6 +112,37 @@ export function formatInputAmount(
|
|
|
112
112
|
return `${integer || (fraction ? '0' : '')}${fraction ? `.${fraction}` : ''}`
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
/** Matches a plain decimal number, i.e. no exponent and no other characters. */
|
|
116
|
+
const plainDecimalRegExp = /^-?\d+(?:\.\d+)?$/
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Convert an amount string denominated in a token's smallest unit to a bigint.
|
|
120
|
+
*
|
|
121
|
+
* Never route these amounts through `Number`: values above `Number.MAX_SAFE_INTEGER`
|
|
122
|
+
* lose precision, and from 1e21 upwards `Number#toFixed` returns exponential
|
|
123
|
+
* notation, which `BigInt` rejects outright.
|
|
124
|
+
*
|
|
125
|
+
* A smallest unit is indivisible, so a fractional amount carries no meaning. Should a
|
|
126
|
+
* provider report one anyway, the fraction is truncated textually — discarding nothing
|
|
127
|
+
* real, and without letting `BigInt` throw on it.
|
|
128
|
+
* @param amount amount in the token's smallest unit.
|
|
129
|
+
* @returns the amount as a bigint, or 0n when it is missing or not a plain decimal.
|
|
130
|
+
*/
|
|
131
|
+
export function parseAmountToBigInt(amount?: string): bigint {
|
|
132
|
+
const trimmedAmount = amount?.trim()
|
|
133
|
+
if (!trimmedAmount) {
|
|
134
|
+
return 0n
|
|
135
|
+
}
|
|
136
|
+
if (!plainDecimalRegExp.test(trimmedAmount)) {
|
|
137
|
+
// Falling back to 0n keeps the widget rendering, but it under-reports gas and
|
|
138
|
+
// fee totals — surface it rather than letting a bad amount vanish silently
|
|
139
|
+
console.warn('Unparseable token amount:', amount)
|
|
140
|
+
return 0n
|
|
141
|
+
}
|
|
142
|
+
// Smallest units are indivisible, so drop any fractional part
|
|
143
|
+
return BigInt(trimmedAmount.split('.')[0])
|
|
144
|
+
}
|
|
145
|
+
|
|
115
146
|
export function formatTokenPrice(
|
|
116
147
|
amount?: string | bigint,
|
|
117
148
|
price?: string,
|