@lifi/widget 4.9.0 → 4.10.1

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.
Files changed (70) hide show
  1. package/README.md +28 -10
  2. package/dist/esm/components/TokenList/TokenDetailsSheet.js +5 -8
  3. package/dist/esm/components/TokenList/TokenDetailsSheet.js.map +1 -1
  4. package/dist/esm/components/TokenList/TokenDetailsSheetContent.js +30 -2
  5. package/dist/esm/components/TokenList/TokenDetailsSheetContent.js.map +1 -1
  6. package/dist/esm/components/TokenList/TokenListItem.js +36 -47
  7. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  8. package/dist/esm/components/TokenList/VirtualizedTokenList.d.ts.map +1 -1
  9. package/dist/esm/components/TokenList/VirtualizedTokenList.js +2 -2
  10. package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  11. package/dist/esm/components/TokenList/tokenVerificationIcons.js +29 -0
  12. package/dist/esm/components/TokenList/tokenVerificationIcons.js.map +1 -0
  13. package/dist/esm/config/version.d.ts +1 -1
  14. package/dist/esm/config/version.js +1 -1
  15. package/dist/esm/config/version.js.map +1 -1
  16. package/dist/esm/hooks/usePacedValue.js +1 -1
  17. package/dist/esm/hooks/usePacedValue.js.map +1 -1
  18. package/dist/esm/hooks/useRouteExecutionMessage.d.ts.map +1 -1
  19. package/dist/esm/hooks/useRouteExecutionMessage.js +4 -8
  20. package/dist/esm/hooks/useRouteExecutionMessage.js.map +1 -1
  21. package/dist/esm/i18n/bn.json +567 -266
  22. package/dist/esm/i18n/de.json +552 -251
  23. package/dist/esm/i18n/en.json +12 -62
  24. package/dist/esm/i18n/es.json +522 -221
  25. package/dist/esm/i18n/fr.json +541 -240
  26. package/dist/esm/i18n/hi.json +592 -291
  27. package/dist/esm/i18n/id.json +485 -184
  28. package/dist/esm/i18n/it.json +546 -245
  29. package/dist/esm/i18n/ja.json +512 -211
  30. package/dist/esm/i18n/ko.json +553 -252
  31. package/dist/esm/i18n/pl.json +598 -297
  32. package/dist/esm/i18n/pt.json +513 -212
  33. package/dist/esm/i18n/th.json +554 -253
  34. package/dist/esm/i18n/tr.json +538 -237
  35. package/dist/esm/i18n/uk.json +514 -213
  36. package/dist/esm/i18n/vi.json +550 -249
  37. package/dist/esm/i18n/zh.json +528 -227
  38. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js +70 -18
  39. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js.map +1 -1
  40. package/dist/esm/pages/TransactionPage/utils.d.ts +3 -0
  41. package/dist/esm/pages/TransactionPage/utils.d.ts.map +1 -1
  42. package/dist/esm/pages/TransactionPage/utils.js +27 -1
  43. package/dist/esm/pages/TransactionPage/utils.js.map +1 -1
  44. package/dist/esm/shared.d.ts +2 -2
  45. package/dist/esm/shared.js +2 -2
  46. package/dist/esm/stores/routes/utils.d.ts +1 -1
  47. package/dist/esm/stores/routes/utils.d.ts.map +1 -1
  48. package/dist/esm/stores/routes/utils.js +13 -1
  49. package/dist/esm/stores/routes/utils.js.map +1 -1
  50. package/dist/esm/utils/getErrorMessage.js +4 -0
  51. package/dist/esm/utils/getErrorMessage.js.map +1 -1
  52. package/dist/esm/utils/token.js +13 -1
  53. package/dist/esm/utils/token.js.map +1 -1
  54. package/package.json +4 -4
  55. package/src/components/TokenList/TokenDetailsSheet.tsx +5 -9
  56. package/src/components/TokenList/TokenDetailsSheetContent.tsx +46 -4
  57. package/src/components/TokenList/TokenListItem.tsx +44 -68
  58. package/src/components/TokenList/VirtualizedTokenList.tsx +3 -6
  59. package/src/components/TokenList/tokenVerificationIcons.ts +15 -0
  60. package/src/components/TokenList/types.ts +3 -11
  61. package/src/config/version.ts +1 -1
  62. package/src/hooks/usePacedValue.ts +1 -1
  63. package/src/hooks/useRouteExecutionMessage.ts +10 -13
  64. package/src/pages/TransactionPage/TransactionFailedButtons.test.tsx +497 -0
  65. package/src/pages/TransactionPage/TransactionFailedButtons.tsx +78 -9
  66. package/src/pages/TransactionPage/utils.ts +81 -0
  67. package/src/shared.ts +1 -0
  68. package/src/stores/routes/utils.ts +24 -1
  69. package/src/utils/getErrorMessage.ts +5 -0
  70. package/src/utils/token.ts +48 -0
@@ -1,14 +1,19 @@
1
1
  import { useWidgetEvents } from "../../hooks/useWidgetEvents.js";
2
2
  import "../../types/events.js";
3
3
  import { useWidgetConfig } from "../../providers/WidgetProvider/WidgetProvider.js";
4
+ import { navigationRoutes } from "../../utils/navigationRoutes.js";
5
+ import { useSwapOnly } from "../../hooks/useSwapOnly.js";
6
+ import { useBookmarkActions } from "../../stores/bookmarks/useBookmarkActions.js";
7
+ import { useFieldActions } from "../../stores/form/useFieldActions.js";
4
8
  import { getAccumulatedFeeCostsBreakdown } from "../../utils/fees.js";
5
9
  import { useNavigateBack } from "../../hooks/useNavigateBack.js";
6
10
  import { useAddressActivity } from "../../hooks/useAddressActivity.js";
7
11
  import { ConfirmToAddressSheet } from "./ConfirmToAddressSheet.js";
8
12
  import { StartTransactionButton } from "./StartTransactionButton.js";
9
- import { calculateValueLossPercentage, getRetryGates, getTokenValueLossThreshold, openNextGate } from "./utils.js";
13
+ import { calculateValueLossPercentage, canStartNewSwap, getNewSwapFormValues, getRetryGates, getTokenValueLossThreshold, isCallBundleNotFound, openNextGate } from "./utils.js";
10
14
  import { TokenValueBottomSheet } from "./TokenValueBottomSheet.js";
11
15
  import { useRef } from "react";
16
+ import { useNavigate } from "@tanstack/react-router";
12
17
  import { Box, Button } from "@mui/material";
13
18
  import { jsx, jsxs } from "react/jsx-runtime";
14
19
  import { useTranslation } from "react-i18next";
@@ -17,7 +22,11 @@ const TransactionFailedButtons = ({ route, restartRoute, deleteRoute }) => {
17
22
  const { t } = useTranslation();
18
23
  const emitter = useWidgetEvents();
19
24
  const navigateBack = useNavigateBack();
20
- const { mode, hiddenUI } = useWidgetConfig();
25
+ const navigate = useNavigate();
26
+ const { mode, hiddenUI, disabledUI, requiredUI } = useWidgetConfig();
27
+ const swapOnly = useSwapOnly();
28
+ const { setFieldValue, getFieldValues } = useFieldActions();
29
+ const { setSelectedBookmark, getSelectedBookmark } = useBookmarkActions();
21
30
  const tokenValueBottomSheetRef = useRef(null);
22
31
  const confirmToAddressSheetRef = useRef(null);
23
32
  const { toAddress, hasActivity, isLoading: isLoadingAddressActivity, isFetched: isActivityAddressFetched } = useAddressActivity(route.toChainId);
@@ -25,6 +34,36 @@ const TransactionFailedButtons = ({ route, restartRoute, deleteRoute }) => {
25
34
  navigateBack();
26
35
  deleteRoute();
27
36
  };
37
+ const callBundleNotFound = isCallBundleNotFound(route);
38
+ const showStartNewSwap = callBundleNotFound && canStartNewSwap({
39
+ route,
40
+ mode,
41
+ swapOnly
42
+ });
43
+ const showRetry = !callBundleNotFound;
44
+ const handleStartNewSwap = () => {
45
+ const [formReceiver] = getFieldValues("toAddress");
46
+ const keepReceiver = Boolean((disabledUI?.toAddress || hiddenUI?.toAddress) && formReceiver);
47
+ const values = getNewSwapFormValues(route, {
48
+ keepReceiver,
49
+ receiverRequired: requiredUI?.toAddress
50
+ });
51
+ const fieldNames = Object.keys(values);
52
+ const receiver = keepReceiver ? formReceiver : values.toAddress;
53
+ navigate({
54
+ to: navigationRoutes.home,
55
+ replace: true
56
+ }).then(() => {
57
+ for (const fieldName of fieldNames) setFieldValue(fieldName, values[fieldName], {
58
+ isDirty: true,
59
+ isTouched: true
60
+ });
61
+ if (getSelectedBookmark()?.address.toLowerCase() !== receiver?.toLowerCase()) setSelectedBookmark();
62
+ }).catch((error) => {
63
+ console.warn("Filling the new swap failed.", error);
64
+ });
65
+ deleteRoute();
66
+ };
28
67
  const retryGates = (() => {
29
68
  const { gasCostUSD, feeCostUSD } = getAccumulatedFeeCostsBreakdown(route);
30
69
  return getRetryGates({
@@ -71,22 +110,35 @@ const TransactionFailedButtons = ({ route, restartRoute, deleteRoute }) => {
71
110
  display: "flex",
72
111
  gap: 1.5
73
112
  },
74
- children: [/* @__PURE__ */ jsx(Box, {
75
- sx: { flex: 1 },
76
- children: /* @__PURE__ */ jsx(Button, {
77
- onClick: handleRemoveRoute,
78
- fullWidth: true,
79
- children: t("button.delete")
80
- })
81
- }), /* @__PURE__ */ jsx(Box, {
82
- sx: { flex: 1 },
83
- children: /* @__PURE__ */ jsx(StartTransactionButton, {
84
- text: t("button.tryAgain"),
85
- onClick: handleRetryClick,
86
- route,
87
- loading: isLoadingAddressActivity
88
- })
89
- })]
113
+ children: [
114
+ /* @__PURE__ */ jsx(Box, {
115
+ sx: { flex: 1 },
116
+ children: /* @__PURE__ */ jsx(Button, {
117
+ onClick: handleRemoveRoute,
118
+ fullWidth: true,
119
+ children: t("button.delete")
120
+ })
121
+ }),
122
+ showStartNewSwap ? /* @__PURE__ */ jsx(Box, {
123
+ sx: { flex: 1 },
124
+ children: /* @__PURE__ */ jsx(Button, {
125
+ variant: "contained",
126
+ color: "primary",
127
+ onClick: handleStartNewSwap,
128
+ fullWidth: true,
129
+ children: t("button.startNewSwap")
130
+ })
131
+ }) : null,
132
+ showRetry ? /* @__PURE__ */ jsx(Box, {
133
+ sx: { flex: 1 },
134
+ children: /* @__PURE__ */ jsx(StartTransactionButton, {
135
+ text: t("button.tryAgain"),
136
+ onClick: handleRetryClick,
137
+ route,
138
+ loading: isLoadingAddressActivity
139
+ })
140
+ }) : null
141
+ ]
90
142
  }),
91
143
  mode !== "custom" ? /* @__PURE__ */ jsx(TokenValueBottomSheet, {
92
144
  route,
@@ -1 +1 @@
1
- {"version":3,"file":"TransactionFailedButtons.js","names":[],"sources":["../../../../src/pages/TransactionPage/TransactionFailedButtons.tsx"],"sourcesContent":["import type { RouteExtended } from '@lifi/sdk'\nimport { Box, Button } from '@mui/material'\nimport { useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport type { BottomSheetBase } from '../../components/BottomSheet/types.js'\nimport { useAddressActivity } from '../../hooks/useAddressActivity.js'\nimport { useNavigateBack } from '../../hooks/useNavigateBack.js'\nimport { useWidgetEvents } from '../../hooks/useWidgetEvents.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { WidgetEvent } from '../../types/events.js'\nimport { getAccumulatedFeeCostsBreakdown } from '../../utils/fees.js'\nimport { ConfirmToAddressSheet } from './ConfirmToAddressSheet.js'\nimport { StartTransactionButton } from './StartTransactionButton.js'\nimport { TokenValueBottomSheet } from './TokenValueBottomSheet.js'\nimport type { RetryGate } from './utils.js'\nimport {\n calculateValueLossPercentage,\n getRetryGates,\n getTokenValueLossThreshold,\n openNextGate,\n} from './utils.js'\n\ninterface TransactionFailedButtonsProps {\n route: RouteExtended\n restartRoute: () => void\n deleteRoute: () => void\n}\n\nexport const TransactionFailedButtons: React.FC<\n TransactionFailedButtonsProps\n> = ({ route, restartRoute, deleteRoute }) => {\n const { t } = useTranslation()\n const emitter = useWidgetEvents()\n const navigateBack = useNavigateBack()\n const { mode, hiddenUI } = useWidgetConfig()\n\n const tokenValueBottomSheetRef = useRef<BottomSheetBase>(null)\n const confirmToAddressSheetRef = useRef<BottomSheetBase>(null)\n\n const {\n toAddress,\n hasActivity,\n isLoading: isLoadingAddressActivity,\n isFetched: isActivityAddressFetched,\n } = useAddressActivity(route.toChainId)\n\n const handleRemoveRoute = () => {\n navigateBack()\n deleteRoute()\n }\n\n const retryGates = (() => {\n const { gasCostUSD, feeCostUSD } = getAccumulatedFeeCostsBreakdown(route)\n return getRetryGates({\n toAddress,\n hasActivity,\n isLoadingAddressActivity,\n isActivityAddressFetched,\n confirmationHidden: Boolean(hiddenUI?.lowAddressActivityConfirmation),\n valueLossExceeded: getTokenValueLossThreshold(\n Number.parseFloat(route.fromAmountUSD),\n Number.parseFloat(route.toAmountUSD),\n gasCostUSD,\n feeCostUSD\n ),\n isCustomMode: mode === 'custom',\n })\n })()\n\n const openGate = (after?: RetryGate) =>\n openNextGate(\n retryGates,\n {\n address: () => confirmToAddressSheetRef.current?.open(),\n value: () => tokenValueBottomSheetRef.current?.open(),\n },\n handleRestartRoute,\n after\n )\n\n const handleRestartRoute = () => {\n if (tokenValueBottomSheetRef.current?.isOpen()) {\n const { gasCostUSD, feeCostUSD } = getAccumulatedFeeCostsBreakdown(route)\n const fromAmountUSD = Number.parseFloat(route.fromAmountUSD)\n const toAmountUSD = Number.parseFloat(route.toAmountUSD)\n emitter.emit(WidgetEvent.RouteHighValueLoss, {\n fromAmountUSD,\n toAmountUSD,\n gasCostUSD,\n feeCostUSD,\n valueLoss: calculateValueLossPercentage(\n fromAmountUSD,\n toAmountUSD,\n gasCostUSD,\n feeCostUSD\n ),\n })\n }\n tokenValueBottomSheetRef.current?.close()\n restartRoute()\n }\n\n const handleRetryClick = () => openGate()\n\n const handleConfirmToAddressContinue = () => openGate('address')\n\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>\n <Box sx={{ display: 'flex', gap: 1.5 }}>\n <Box sx={{ flex: 1 }}>\n <Button onClick={handleRemoveRoute} fullWidth>\n {t('button.delete')}\n </Button>\n </Box>\n <Box sx={{ flex: 1 }}>\n <StartTransactionButton\n text={t('button.tryAgain')}\n onClick={handleRetryClick}\n route={route}\n loading={isLoadingAddressActivity}\n />\n </Box>\n </Box>\n {mode !== 'custom' ? (\n <TokenValueBottomSheet\n route={route}\n ref={tokenValueBottomSheetRef}\n onContinue={handleRestartRoute}\n />\n ) : null}\n {!hiddenUI?.lowAddressActivityConfirmation ? (\n <ConfirmToAddressSheet\n ref={confirmToAddressSheetRef}\n onContinue={handleConfirmToAddressContinue}\n toAddress={toAddress!}\n toChainId={route.toChainId!}\n />\n ) : null}\n </Box>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;AA4BA,MAAa,4BAER,EAAE,OAAO,cAAc,kBAAkB;CAC5C,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,UAAU,gBAAgB;CAChC,MAAM,eAAe,gBAAgB;CACrC,MAAM,EAAE,MAAM,aAAa,gBAAgB;CAE3C,MAAM,2BAA2B,OAAwB,IAAI;CAC7D,MAAM,2BAA2B,OAAwB,IAAI;CAE7D,MAAM,EACJ,WACA,aACA,WAAW,0BACX,WAAW,6BACT,mBAAmB,MAAM,SAAS;CAEtC,MAAM,0BAA0B;EAC9B,aAAa;EACb,YAAY;CACd;CAEA,MAAM,oBAAoB;EACxB,MAAM,EAAE,YAAY,eAAe,gCAAgC,KAAK;EACxE,OAAO,cAAc;GACnB;GACA;GACA;GACA;GACA,oBAAoB,QAAQ,UAAU,8BAA8B;GACpE,mBAAmB,2BACjB,OAAO,WAAW,MAAM,aAAa,GACrC,OAAO,WAAW,MAAM,WAAW,GACnC,YACA,UACF;GACA,cAAc,SAAS;EACzB,CAAC;CACH,EAAA,CAAG;CAEH,MAAM,YAAY,UAChB,aACE,YACA;EACE,eAAe,yBAAyB,SAAS,KAAK;EACtD,aAAa,yBAAyB,SAAS,KAAK;CACtD,GACA,oBACA,KACF;CAEF,MAAM,2BAA2B;EAC/B,IAAI,yBAAyB,SAAS,OAAO,GAAG;GAC9C,MAAM,EAAE,YAAY,eAAe,gCAAgC,KAAK;GACxE,MAAM,gBAAgB,OAAO,WAAW,MAAM,aAAa;GAC3D,MAAM,cAAc,OAAO,WAAW,MAAM,WAAW;GACvD,QAAQ,KAAA,sBAAqC;IAC3C;IACA;IACA;IACA;IACA,WAAW,6BACT,eACA,aACA,YACA,UACF;GACF,CAAC;EACH;EACA,yBAAyB,SAAS,MAAM;EACxC,aAAa;CACf;CAEA,MAAM,yBAAyB,SAAS;CAExC,MAAM,uCAAuC,SAAS,SAAS;CAE/D,OACE,qBAAC,KAAD;EAAK,IAAI;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAI;EAA9D,UAAA;GACE,qBAAC,KAAD;IAAK,IAAI;KAAE,SAAS;KAAQ,KAAK;IAAI;IAArC,UAAA,CACE,oBAAC,KAAD;KAAK,IAAI,EAAE,MAAM,EAAE;KACjB,UAAA,oBAAC,QAAD;MAAQ,SAAS;MAAmB,WAAA;MACjC,UAAA,EAAE,eAAe;KACZ,CAAA;IACL,CAAA,GACL,oBAAC,KAAD;KAAK,IAAI,EAAE,MAAM,EAAE;KACjB,UAAA,oBAAC,wBAAD;MACE,MAAM,EAAE,iBAAiB;MACzB,SAAS;MACF;MACP,SAAS;KACV,CAAA;IACE,CAAA,CACF;;GACJ,SAAS,WACR,oBAAC,uBAAD;IACS;IACP,KAAK;IACL,YAAY;GACb,CAAA,IACC;GACH,CAAC,UAAU,iCACV,oBAAC,uBAAD;IACE,KAAK;IACL,YAAY;IACD;IACX,WAAW,MAAM;GAClB,CAAA,IACC;EACD;;AAET"}
1
+ {"version":3,"file":"TransactionFailedButtons.js","names":[],"sources":["../../../../src/pages/TransactionPage/TransactionFailedButtons.tsx"],"sourcesContent":["import type { RouteExtended } from '@lifi/sdk'\nimport { Box, Button } from '@mui/material'\nimport { useNavigate } from '@tanstack/react-router'\nimport { useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport type { BottomSheetBase } from '../../components/BottomSheet/types.js'\nimport { useAddressActivity } from '../../hooks/useAddressActivity.js'\nimport { useNavigateBack } from '../../hooks/useNavigateBack.js'\nimport { useSwapOnly } from '../../hooks/useSwapOnly.js'\nimport { useWidgetEvents } from '../../hooks/useWidgetEvents.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'\nimport { useFieldActions } from '../../stores/form/useFieldActions.js'\nimport { WidgetEvent } from '../../types/events.js'\nimport { getAccumulatedFeeCostsBreakdown } from '../../utils/fees.js'\nimport { navigationRoutes } from '../../utils/navigationRoutes.js'\nimport { ConfirmToAddressSheet } from './ConfirmToAddressSheet.js'\nimport { StartTransactionButton } from './StartTransactionButton.js'\nimport { TokenValueBottomSheet } from './TokenValueBottomSheet.js'\nimport type { RetryGate } from './utils.js'\nimport {\n calculateValueLossPercentage,\n canStartNewSwap,\n getNewSwapFormValues,\n getRetryGates,\n getTokenValueLossThreshold,\n isCallBundleNotFound,\n openNextGate,\n} from './utils.js'\n\ninterface TransactionFailedButtonsProps {\n route: RouteExtended\n restartRoute: () => void\n deleteRoute: () => void\n}\n\nexport const TransactionFailedButtons: React.FC<\n TransactionFailedButtonsProps\n> = ({ route, restartRoute, deleteRoute }) => {\n const { t } = useTranslation()\n const emitter = useWidgetEvents()\n const navigateBack = useNavigateBack()\n const navigate = useNavigate()\n const { mode, hiddenUI, disabledUI, requiredUI } = useWidgetConfig()\n const swapOnly = useSwapOnly()\n const { setFieldValue, getFieldValues } = useFieldActions()\n const { setSelectedBookmark, getSelectedBookmark } = useBookmarkActions()\n\n const tokenValueBottomSheetRef = useRef<BottomSheetBase>(null)\n const confirmToAddressSheetRef = useRef<BottomSheetBase>(null)\n\n const {\n toAddress,\n hasActivity,\n isLoading: isLoadingAddressActivity,\n isFetched: isActivityAddressFetched,\n } = useAddressActivity(route.toChainId)\n\n const handleRemoveRoute = () => {\n navigateBack()\n deleteRoute()\n }\n\n const callBundleNotFound = isCallBundleNotFound(route)\n const showStartNewSwap =\n callBundleNotFound && canStartNewSwap({ route, mode, swapOnly })\n // Try again would only wait for the same bundle again.\n const showRetry = !callBundleNotFound\n\n // Home, not back: after a reload, the route is opened from Activities.\n const handleStartNewSwap = () => {\n // An empty hidden or locked field still needs the route's receiver.\n const [formReceiver] = getFieldValues('toAddress')\n const keepReceiver = Boolean(\n (disabledUI?.toAddress || hiddenUI?.toAddress) && formReceiver\n )\n const values = getNewSwapFormValues(route, {\n keepReceiver,\n receiverRequired: requiredUI?.toAddress,\n })\n const fieldNames = Object.keys(values) as (keyof typeof values)[]\n const receiver = keepReceiver ? formReceiver : values.toAddress\n // Fill after navigate resolves: this page's unmount cleanup has run by then,\n // as TransactionFailedButtons.test.tsx checks.\n navigate({ to: navigationRoutes.home, replace: true })\n .then(() => {\n for (const fieldName of fieldNames) {\n setFieldValue(fieldName, values[fieldName], {\n isDirty: true,\n isTouched: true,\n })\n }\n // A bookmark name from an earlier receiver must not label this one.\n if (\n getSelectedBookmark()?.address.toLowerCase() !==\n receiver?.toLowerCase()\n ) {\n setSelectedBookmark()\n }\n })\n .catch((error) => {\n console.warn('Filling the new swap failed.', error)\n })\n deleteRoute()\n }\n\n const retryGates = (() => {\n const { gasCostUSD, feeCostUSD } = getAccumulatedFeeCostsBreakdown(route)\n return getRetryGates({\n toAddress,\n hasActivity,\n isLoadingAddressActivity,\n isActivityAddressFetched,\n confirmationHidden: Boolean(hiddenUI?.lowAddressActivityConfirmation),\n valueLossExceeded: getTokenValueLossThreshold(\n Number.parseFloat(route.fromAmountUSD),\n Number.parseFloat(route.toAmountUSD),\n gasCostUSD,\n feeCostUSD\n ),\n isCustomMode: mode === 'custom',\n })\n })()\n\n const openGate = (after?: RetryGate) =>\n openNextGate(\n retryGates,\n {\n address: () => confirmToAddressSheetRef.current?.open(),\n value: () => tokenValueBottomSheetRef.current?.open(),\n },\n handleRestartRoute,\n after\n )\n\n const handleRestartRoute = () => {\n if (tokenValueBottomSheetRef.current?.isOpen()) {\n const { gasCostUSD, feeCostUSD } = getAccumulatedFeeCostsBreakdown(route)\n const fromAmountUSD = Number.parseFloat(route.fromAmountUSD)\n const toAmountUSD = Number.parseFloat(route.toAmountUSD)\n emitter.emit(WidgetEvent.RouteHighValueLoss, {\n fromAmountUSD,\n toAmountUSD,\n gasCostUSD,\n feeCostUSD,\n valueLoss: calculateValueLossPercentage(\n fromAmountUSD,\n toAmountUSD,\n gasCostUSD,\n feeCostUSD\n ),\n })\n }\n tokenValueBottomSheetRef.current?.close()\n restartRoute()\n }\n\n const handleRetryClick = () => openGate()\n\n const handleConfirmToAddressContinue = () => openGate('address')\n\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>\n <Box sx={{ display: 'flex', gap: 1.5 }}>\n <Box sx={{ flex: 1 }}>\n <Button onClick={handleRemoveRoute} fullWidth>\n {t('button.delete')}\n </Button>\n </Box>\n {showStartNewSwap ? (\n <Box sx={{ flex: 1 }}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleStartNewSwap}\n fullWidth\n >\n {t('button.startNewSwap')}\n </Button>\n </Box>\n ) : null}\n {showRetry ? (\n <Box sx={{ flex: 1 }}>\n <StartTransactionButton\n text={t('button.tryAgain')}\n onClick={handleRetryClick}\n route={route}\n loading={isLoadingAddressActivity}\n />\n </Box>\n ) : null}\n </Box>\n {mode !== 'custom' ? (\n <TokenValueBottomSheet\n route={route}\n ref={tokenValueBottomSheetRef}\n onContinue={handleRestartRoute}\n />\n ) : null}\n {!hiddenUI?.lowAddressActivityConfirmation ? (\n <ConfirmToAddressSheet\n ref={confirmToAddressSheetRef}\n onContinue={handleConfirmToAddressContinue}\n toAddress={toAddress!}\n toChainId={route.toChainId!}\n />\n ) : null}\n </Box>\n )\n}\n"],"mappings":"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"}
@@ -1,5 +1,8 @@
1
+ import "../../types/widget.js";
2
+ import { RouteExtended } from "@lifi/sdk";
1
3
  //#region src/pages/TransactionPage/utils.d.ts
2
4
  export declare const calculateValueLossPercentage: (fromAmountUSD: number, toAmountUSD: number, gasCostUSD: number, feeCostUSD: number) => number;
3
5
  export declare const getTokenValueLossThreshold: (fromAmountUSD: number, toAmountUSD: number, gasCostUSD: number, feeCostUSD: number) => boolean;
6
+ export declare const isCallBundleNotFound: (route: RouteExtended) => boolean;
4
7
  //#endregion
5
8
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","names":[],"sources":["../../../../src/pages/TransactionPage/utils.ts"],"mappings":";qBAAa,+BACX,uBACA,qBACA,oBACA;qBAUW,6BACX,uBACA,qBACA,oBACA"}
1
+ {"version":3,"file":"utils.d.ts","names":[],"sources":["../../../../src/pages/TransactionPage/utils.ts"],"mappings":";;;qBAKa,+BACX,uBACA,qBACA,oBACA;qBAUW,6BACX,uBACA,qBACA,oBACA;qBA6FW,uBAAwB,OAAO"}
@@ -1,3 +1,6 @@
1
+ import { formatTokenAmount } from "../../utils/format.js";
2
+ import { getFailedStepAction } from "../../stores/routes/utils.js";
3
+ import { LiFiErrorCode } from "@lifi/sdk";
1
4
  //#region src/pages/TransactionPage/utils.ts
2
5
  const calculateValueLossPercentage = (fromAmountUSD, toAmountUSD, gasCostUSD, feeCostUSD) => {
3
6
  return Number.parseFloat(((toAmountUSD / (fromAmountUSD + gasCostUSD + feeCostUSD) - 1) * 100).toFixed(2));
@@ -28,7 +31,30 @@ const getStartGates = (input) => [
28
31
  ["address", needsAddressConfirmation(input)],
29
32
  ["value", input.valueLossExceeded && !input.isCustomMode]
30
33
  ];
34
+ const newSwapModes = [
35
+ void 0,
36
+ "default",
37
+ "split"
38
+ ];
39
+ const isCallBundleNotFound = (route) => getFailedStepAction(route)?.action.error?.code === LiFiErrorCode.CallBundleNotFound;
40
+ const canStartNewSwap = ({ route, mode, swapOnly }) => {
41
+ const failed = getFailedStepAction(route);
42
+ if (!failed || !isCallBundleNotFound(route)) return false;
43
+ return failed.step === route.steps[0] && route.steps.every((step) => step === failed.step || !step.execution?.actions?.length) && newSwapModes.includes(mode) && !(swapOnly && route.fromChainId !== route.toChainId);
44
+ };
45
+ const getNewSwapFormValues = (route, { keepReceiver = false, receiverRequired = false } = {}) => {
46
+ const receiver = route.toAddress || route.fromAddress;
47
+ const values = {
48
+ fromChain: route.fromChainId,
49
+ fromToken: route.fromToken.address,
50
+ fromAmount: formatTokenAmount(BigInt(route.fromAmount), route.fromToken.decimals),
51
+ toChain: route.toChainId,
52
+ toToken: route.toToken.address
53
+ };
54
+ if (!keepReceiver) values.toAddress = receiver && (receiverRequired || receiver.toLowerCase() !== route.fromAddress?.toLowerCase()) ? receiver : "";
55
+ return values;
56
+ };
31
57
  //#endregion
32
- export { calculateValueLossPercentage, getRetryGates, getStartGates, getTokenValueLossThreshold, nextGate, openNextGate };
58
+ export { calculateValueLossPercentage, canStartNewSwap, getNewSwapFormValues, getRetryGates, getStartGates, getTokenValueLossThreshold, isCallBundleNotFound, nextGate, openNextGate };
33
59
 
34
60
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../../../src/pages/TransactionPage/utils.ts"],"sourcesContent":["export const calculateValueLossPercentage = (\n fromAmountUSD: number,\n toAmountUSD: number,\n gasCostUSD: number,\n feeCostUSD: number\n): number => {\n return Number.parseFloat(\n (\n (toAmountUSD / (fromAmountUSD + gasCostUSD + feeCostUSD) - 1) *\n 100\n ).toFixed(2)\n )\n}\n\nexport const getTokenValueLossThreshold = (\n fromAmountUSD: number,\n toAmountUSD: number,\n gasCostUSD: number,\n feeCostUSD: number\n): boolean => {\n if (!fromAmountUSD || !toAmountUSD) {\n return false\n }\n return toAmountUSD / (fromAmountUSD + gasCostUSD + feeCostUSD) < 0.9\n}\n\ninterface AddressGateInput {\n toAddress: string | undefined\n hasActivity: boolean | undefined\n isLoadingAddressActivity: boolean\n isActivityAddressFetched: boolean\n confirmationHidden: boolean\n}\n\nconst needsAddressConfirmation = ({\n toAddress,\n hasActivity,\n isLoadingAddressActivity,\n isActivityAddressFetched,\n confirmationHidden,\n}: AddressGateInput): boolean =>\n Boolean(\n toAddress &&\n !hasActivity &&\n !isLoadingAddressActivity &&\n isActivityAddressFetched &&\n !confirmationHidden\n )\n\nexport type RetryGate = 'address' | 'value'\n\nexport const getRetryGates = (\n input: AddressGateInput & {\n valueLossExceeded: boolean\n isCustomMode: boolean\n }\n): readonly (readonly [gate: RetryGate, needed: boolean])[] => [\n ['address', needsAddressConfirmation(input)],\n ['value', input.valueLossExceeded && !input.isCustomMode],\n]\n\n/** Ordering lives here so no sheet decides what follows it — that is how gates came to be skipped. */\nexport const nextGate = <T extends string>(\n gates: readonly (readonly [gate: T, needed: boolean])[],\n after?: T\n): T | undefined => {\n if (!after) {\n return gates.find(([, needed]) => needed)?.[0]\n }\n const index = gates.findIndex(([gate]) => gate === after)\n if (index < 0) {\n return undefined\n }\n return gates.slice(index + 1).find(([, needed]) => needed)?.[0]\n}\n\nexport const openNextGate = <T extends string>(\n gates: readonly (readonly [gate: T, needed: boolean])[],\n open: Record<T, () => void>,\n done: () => void,\n after?: T\n): void => {\n const gate = nextGate(gates, after)\n if (gate) {\n open[gate]()\n return\n }\n done()\n}\n\nexport type StartGate = 'flagged' | 'address' | 'value'\n\nexport const getStartGates = (\n input: AddressGateInput & {\n flaggedTokenCount: number\n valueLossExceeded: boolean\n isCustomMode: boolean\n }\n): readonly (readonly [gate: StartGate, needed: boolean])[] => [\n ['flagged', input.flaggedTokenCount > 0],\n ['address', needsAddressConfirmation(input)],\n ['value', input.valueLossExceeded && !input.isCustomMode],\n]\n"],"mappings":";AAAA,MAAa,gCACX,eACA,aACA,YACA,eACW;CACX,OAAO,OAAO,aAET,eAAe,gBAAgB,aAAa,cAAc,KAC3D,IAAA,CACA,QAAQ,CAAC,CACb;AACF;AAEA,MAAa,8BACX,eACA,aACA,YACA,eACY;CACZ,IAAI,CAAC,iBAAiB,CAAC,aACrB,OAAO;CAET,OAAO,eAAe,gBAAgB,aAAa,cAAc;AACnE;AAUA,MAAM,4BAA4B,EAChC,WACA,aACA,0BACA,0BACA,yBAEA,QACE,aACE,CAAC,eACD,CAAC,4BACD,4BACA,CAAC,kBACL;AAIF,MAAa,iBACX,UAI6D,CAC7D,CAAC,WAAW,yBAAyB,KAAK,CAAC,GAC3C,CAAC,SAAS,MAAM,qBAAqB,CAAC,MAAM,YAAY,CAC1D;;AAGA,MAAa,YACX,OACA,UACkB;CAClB,IAAI,CAAC,OACH,OAAO,MAAM,MAAM,GAAG,YAAY,MAAM,CAAC,GAAG;CAE9C,MAAM,QAAQ,MAAM,WAAW,CAAC,UAAU,SAAS,KAAK;CACxD,IAAI,QAAQ,GACV;CAEF,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC,CAAC,MAAM,GAAG,YAAY,MAAM,CAAC,GAAG;AAC/D;AAEA,MAAa,gBACX,OACA,MACA,MACA,UACS;CACT,MAAM,OAAO,SAAS,OAAO,KAAK;CAClC,IAAI,MAAM;EACR,KAAK,KAAK,CAAC;EACX;CACF;CACA,KAAK;AACP;AAIA,MAAa,iBACX,UAK6D;CAC7D,CAAC,WAAW,MAAM,oBAAoB,CAAC;CACvC,CAAC,WAAW,yBAAyB,KAAK,CAAC;CAC3C,CAAC,SAAS,MAAM,qBAAqB,CAAC,MAAM,YAAY;AAC1D"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../../../src/pages/TransactionPage/utils.ts"],"sourcesContent":["import { LiFiErrorCode, type RouteExtended } from '@lifi/sdk'\nimport { getFailedStepAction } from '../../stores/routes/utils.js'\nimport type { WidgetMode } from '../../types/widget.js'\nimport { formatTokenAmount } from '../../utils/format.js'\n\nexport const calculateValueLossPercentage = (\n fromAmountUSD: number,\n toAmountUSD: number,\n gasCostUSD: number,\n feeCostUSD: number\n): number => {\n return Number.parseFloat(\n (\n (toAmountUSD / (fromAmountUSD + gasCostUSD + feeCostUSD) - 1) *\n 100\n ).toFixed(2)\n )\n}\n\nexport const getTokenValueLossThreshold = (\n fromAmountUSD: number,\n toAmountUSD: number,\n gasCostUSD: number,\n feeCostUSD: number\n): boolean => {\n if (!fromAmountUSD || !toAmountUSD) {\n return false\n }\n return toAmountUSD / (fromAmountUSD + gasCostUSD + feeCostUSD) < 0.9\n}\n\ninterface AddressGateInput {\n toAddress: string | undefined\n hasActivity: boolean | undefined\n isLoadingAddressActivity: boolean\n isActivityAddressFetched: boolean\n confirmationHidden: boolean\n}\n\nconst needsAddressConfirmation = ({\n toAddress,\n hasActivity,\n isLoadingAddressActivity,\n isActivityAddressFetched,\n confirmationHidden,\n}: AddressGateInput): boolean =>\n Boolean(\n toAddress &&\n !hasActivity &&\n !isLoadingAddressActivity &&\n isActivityAddressFetched &&\n !confirmationHidden\n )\n\nexport type RetryGate = 'address' | 'value'\n\nexport const getRetryGates = (\n input: AddressGateInput & {\n valueLossExceeded: boolean\n isCustomMode: boolean\n }\n): readonly (readonly [gate: RetryGate, needed: boolean])[] => [\n ['address', needsAddressConfirmation(input)],\n ['value', input.valueLossExceeded && !input.isCustomMode],\n]\n\n/** Ordering lives here so no sheet decides what follows it — that is how gates came to be skipped. */\nexport const nextGate = <T extends string>(\n gates: readonly (readonly [gate: T, needed: boolean])[],\n after?: T\n): T | undefined => {\n if (!after) {\n return gates.find(([, needed]) => needed)?.[0]\n }\n const index = gates.findIndex(([gate]) => gate === after)\n if (index < 0) {\n return undefined\n }\n return gates.slice(index + 1).find(([, needed]) => needed)?.[0]\n}\n\nexport const openNextGate = <T extends string>(\n gates: readonly (readonly [gate: T, needed: boolean])[],\n open: Record<T, () => void>,\n done: () => void,\n after?: T\n): void => {\n const gate = nextGate(gates, after)\n if (gate) {\n open[gate]()\n return\n }\n done()\n}\n\nexport type StartGate = 'flagged' | 'address' | 'value'\n\nexport const getStartGates = (\n input: AddressGateInput & {\n flaggedTokenCount: number\n valueLossExceeded: boolean\n isCustomMode: boolean\n }\n): readonly (readonly [gate: StartGate, needed: boolean])[] => [\n ['flagged', input.flaggedTokenCount > 0],\n ['address', needsAddressConfirmation(input)],\n ['value', input.valueLossExceeded && !input.isCustomMode],\n]\n\n// Only these modes show the plain from/to swap form.\nconst newSwapModes: readonly (WidgetMode | undefined)[] = [\n undefined,\n 'default',\n 'split',\n]\n\nexport const isCallBundleNotFound = (route: RouteExtended): boolean =>\n getFailedStepAction(route)?.action.error?.code ===\n LiFiErrorCode.CallBundleNotFound\n\n// A new swap repeats the whole route.\nexport const canStartNewSwap = ({\n route,\n mode,\n swapOnly,\n}: {\n route: RouteExtended\n mode: WidgetMode | undefined\n swapOnly: boolean\n}): boolean => {\n const failed = getFailedStepAction(route)\n if (!failed || !isCallBundleNotFound(route)) {\n return false\n }\n return (\n failed.step === route.steps[0] &&\n route.steps.every(\n (step) => step === failed.step || !step.execution?.actions?.length\n ) &&\n newSwapModes.includes(mode) &&\n // A swap-only form requests no bridges.\n !(swapOnly && route.fromChainId !== route.toChainId)\n )\n}\n\ninterface NewSwapFormValues {\n fromChain: number\n fromToken: string\n fromAmount: string\n toChain: number\n toToken: string\n toAddress?: string\n}\n\nexport const getNewSwapFormValues = (\n route: RouteExtended,\n {\n keepReceiver = false,\n receiverRequired = false,\n }: { keepReceiver?: boolean; receiverRequired?: boolean } = {}\n): NewSwapFormValues => {\n const receiver = route.toAddress || route.fromAddress\n const values: NewSwapFormValues = {\n fromChain: route.fromChainId,\n fromToken: route.fromToken.address,\n // The quote's amount, not a step amount trimmed to the balance: the new quote checks it.\n fromAmount: formatTokenAmount(\n BigInt(route.fromAmount),\n route.fromToken.decimals\n ),\n toChain: route.toChainId,\n toToken: route.toToken.address,\n }\n if (!keepReceiver) {\n // A required receiver stays, also when it is the sender.\n values.toAddress =\n receiver &&\n (receiverRequired ||\n receiver.toLowerCase() !== route.fromAddress?.toLowerCase())\n ? receiver\n : ''\n }\n return values\n}\n"],"mappings":";;;;AAKA,MAAa,gCACX,eACA,aACA,YACA,eACW;CACX,OAAO,OAAO,aAET,eAAe,gBAAgB,aAAa,cAAc,KAC3D,IAAA,CACA,QAAQ,CAAC,CACb;AACF;AAEA,MAAa,8BACX,eACA,aACA,YACA,eACY;CACZ,IAAI,CAAC,iBAAiB,CAAC,aACrB,OAAO;CAET,OAAO,eAAe,gBAAgB,aAAa,cAAc;AACnE;AAUA,MAAM,4BAA4B,EAChC,WACA,aACA,0BACA,0BACA,yBAEA,QACE,aACE,CAAC,eACD,CAAC,4BACD,4BACA,CAAC,kBACL;AAIF,MAAa,iBACX,UAI6D,CAC7D,CAAC,WAAW,yBAAyB,KAAK,CAAC,GAC3C,CAAC,SAAS,MAAM,qBAAqB,CAAC,MAAM,YAAY,CAC1D;;AAGA,MAAa,YACX,OACA,UACkB;CAClB,IAAI,CAAC,OACH,OAAO,MAAM,MAAM,GAAG,YAAY,MAAM,CAAC,GAAG;CAE9C,MAAM,QAAQ,MAAM,WAAW,CAAC,UAAU,SAAS,KAAK;CACxD,IAAI,QAAQ,GACV;CAEF,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC,CAAC,MAAM,GAAG,YAAY,MAAM,CAAC,GAAG;AAC/D;AAEA,MAAa,gBACX,OACA,MACA,MACA,UACS;CACT,MAAM,OAAO,SAAS,OAAO,KAAK;CAClC,IAAI,MAAM;EACR,KAAK,KAAK,CAAC;EACX;CACF;CACA,KAAK;AACP;AAIA,MAAa,iBACX,UAK6D;CAC7D,CAAC,WAAW,MAAM,oBAAoB,CAAC;CACvC,CAAC,WAAW,yBAAyB,KAAK,CAAC;CAC3C,CAAC,SAAS,MAAM,qBAAqB,CAAC,MAAM,YAAY;AAC1D;AAGA,MAAM,eAAoD;CACxD,KAAA;CACA;CACA;AACF;AAEA,MAAa,wBAAwB,UACnC,oBAAoB,KAAK,CAAC,EAAE,OAAO,OAAO,SAC1C,cAAc;AAGhB,MAAa,mBAAmB,EAC9B,OACA,MACA,eAKa;CACb,MAAM,SAAS,oBAAoB,KAAK;CACxC,IAAI,CAAC,UAAU,CAAC,qBAAqB,KAAK,GACxC,OAAO;CAET,OACE,OAAO,SAAS,MAAM,MAAM,MAC5B,MAAM,MAAM,OACT,SAAS,SAAS,OAAO,QAAQ,CAAC,KAAK,WAAW,SAAS,MAC9D,KACA,aAAa,SAAS,IAAI,KAE1B,EAAE,YAAY,MAAM,gBAAgB,MAAM;AAE9C;AAWA,MAAa,wBACX,OACA,EACE,eAAe,OACf,mBAAmB,UACuC,CAAC,MACvC;CACtB,MAAM,WAAW,MAAM,aAAa,MAAM;CAC1C,MAAM,SAA4B;EAChC,WAAW,MAAM;EACjB,WAAW,MAAM,UAAU;EAE3B,YAAY,kBACV,OAAO,MAAM,UAAU,GACvB,MAAM,UAAU,QAClB;EACA,SAAS,MAAM;EACf,SAAS,MAAM,QAAQ;CACzB;CACA,IAAI,CAAC,cAEH,OAAO,YACL,aACC,oBACC,SAAS,YAAY,MAAM,MAAM,aAAa,YAAY,KACxD,WACA;CAER,OAAO;AACT"}
@@ -85,7 +85,7 @@ import { RouteTracker } from "./pages/TransactionPage/RouteTracker.js";
85
85
  import { StartTransactionButton } from "./pages/TransactionPage/StartTransactionButton.js";
86
86
  import { TokenValueBottomSheet } from "./pages/TransactionPage/TokenValueBottomSheet.js";
87
87
  import { TransactionDoneButtons } from "./pages/TransactionPage/TransactionDoneButtons.js";
88
- import { calculateValueLossPercentage, getTokenValueLossThreshold } from "./pages/TransactionPage/utils.js";
88
+ import { calculateValueLossPercentage, getTokenValueLossThreshold, isCallBundleNotFound } from "./pages/TransactionPage/utils.js";
89
89
  import { I18nProvider } from "./providers/I18nProvider/I18nProvider.js";
90
90
  import { QueryClientProvider } from "./providers/QueryClientProvider.js";
91
91
  import { SDKClientProvider, useSDKClient } from "./providers/SDKClientProvider.js";
@@ -106,4 +106,4 @@ import { fitInputText } from "./utils/input.js";
106
106
  import { resolveStorageScopeKey } from "./utils/scopeKeys.js";
107
107
  import { isHttpUrl, openInNewTab } from "./utils/url.js";
108
108
  import { shortenAddress } from "./utils/wallet.js";
109
- export { ActionRow, AddressType, AmountInputEndAdornment, AvatarBadgedDefault, BaseTransactionButton, BookmarkStoreProvider, type BottomSheetBase, ButtonTertiary, Card, CardIconButton, CardTitle, ChainAvatar, ChainSelect, ConfirmToAddressSheet, ContactSupportButton, ContractComponent, DateLabelContainer, DateLabelText, DetailInfoIcon, DetailLabel, DetailLabelContainer, DetailRow, DetailValue, ElementId, ExchangeRateBottomSheet, type ExchangeRateBottomSheetBase, ExecutionDoneCard, type ExecutionRow, ExecutionStatusCard, ExternalLink, FeeBreakdownTooltip, FormControl, FormKeyHelper, type FormRef, type FormType, type FormTypeProps, I18nProvider, IconCircle, IconTypography, Input, InputCard, InputPriceButton, ListItemButton, MainWarningMessages, PageContainer, PoweredBy, ProgressToNextUpdate, QueryClientProvider, RouteCard, RouteCardSkeleton, RouteDetails, RouteExecutionStatus, RouteNotFoundCard, RouteTokens, RouteTracker, SDKClientProvider, SearchInput, SearchTokenInput, SelectChainPage, SendToWalletButton, SentToWalletRow, SettingsStoreProvider, Stack, StartTransactionButton, StatusIcon, StepActionsList, StoreProvider, Token, TokenAvatar, TokenNotFound, TokenRate, TokenValueBottomSheet, TransactionDoneButtons, VirtualizedTokenList, WalletProvider, WarningMessages, type WidgetConfig, WidgetEvent, WidgetProvider, type WidgetTheme, buildRouteFromTxHistory, calculateValueLossPercentage, createElementId, createTheme, fitInputText, formatDuration, formatInputAmount, formatTokenAmount, formatTokenPrice, getAccumulatedFeeCostsBreakdown, getPriceImpact, getSourceTxHash, getTokenValueLossThreshold, hasEnumFlag, isHttpUrl, isRouteActive, isRouteDone, isRouteFailed, navigationRoutes, openInNewTab, priceToTokenAmount, resolveStorageScopeKey, shortenAddress, usdDecimals, useAddressActivity, useAddressValidation, useAvailableChains, useChain, useChainOrderStore, useContactSupport, useDebouncedWatch, useExecutionRows, useExplorer, useFieldActions, useFieldValues, useHeader, useHeaderStore, useInputModeStore, useListHeight, useNavigateBack, useRouteExecution, useRouteExecutionMessage, useRouteExecutionStore, useRouteExecutionStoreContext, useRoutes, useSDKClient, useSetHeaderHeight, useSettingsStoreContext, useToAddressRequirements, useToken, useTokenAddressBalance, useTokenBalances, useTokenList, useTokenSelect, useTools, useWidgetConfig, useWidgetEvents };
109
+ export { ActionRow, AddressType, AmountInputEndAdornment, AvatarBadgedDefault, BaseTransactionButton, BookmarkStoreProvider, type BottomSheetBase, ButtonTertiary, Card, CardIconButton, CardTitle, ChainAvatar, ChainSelect, ConfirmToAddressSheet, ContactSupportButton, ContractComponent, DateLabelContainer, DateLabelText, DetailInfoIcon, DetailLabel, DetailLabelContainer, DetailRow, DetailValue, ElementId, ExchangeRateBottomSheet, type ExchangeRateBottomSheetBase, ExecutionDoneCard, type ExecutionRow, ExecutionStatusCard, ExternalLink, FeeBreakdownTooltip, FormControl, FormKeyHelper, type FormRef, type FormType, type FormTypeProps, I18nProvider, IconCircle, IconTypography, Input, InputCard, InputPriceButton, ListItemButton, MainWarningMessages, PageContainer, PoweredBy, ProgressToNextUpdate, QueryClientProvider, RouteCard, RouteCardSkeleton, RouteDetails, RouteExecutionStatus, RouteNotFoundCard, RouteTokens, RouteTracker, SDKClientProvider, SearchInput, SearchTokenInput, SelectChainPage, SendToWalletButton, SentToWalletRow, SettingsStoreProvider, Stack, StartTransactionButton, StatusIcon, StepActionsList, StoreProvider, Token, TokenAvatar, TokenNotFound, TokenRate, TokenValueBottomSheet, TransactionDoneButtons, VirtualizedTokenList, WalletProvider, WarningMessages, type WidgetConfig, WidgetEvent, WidgetProvider, type WidgetTheme, buildRouteFromTxHistory, calculateValueLossPercentage, createElementId, createTheme, fitInputText, formatDuration, formatInputAmount, formatTokenAmount, formatTokenPrice, getAccumulatedFeeCostsBreakdown, getPriceImpact, getSourceTxHash, getTokenValueLossThreshold, hasEnumFlag, isCallBundleNotFound, isHttpUrl, isRouteActive, isRouteDone, isRouteFailed, navigationRoutes, openInNewTab, priceToTokenAmount, resolveStorageScopeKey, shortenAddress, usdDecimals, useAddressActivity, useAddressValidation, useAvailableChains, useChain, useChainOrderStore, useContactSupport, useDebouncedWatch, useExecutionRows, useExplorer, useFieldActions, useFieldValues, useHeader, useHeaderStore, useInputModeStore, useListHeight, useNavigateBack, useRouteExecution, useRouteExecutionMessage, useRouteExecutionStore, useRouteExecutionStoreContext, useRoutes, useSDKClient, useSetHeaderHeight, useSettingsStoreContext, useToAddressRequirements, useToken, useTokenAddressBalance, useTokenBalances, useTokenList, useTokenSelect, useTools, useWidgetConfig, useWidgetEvents };
@@ -96,7 +96,7 @@ import { TransactionDoneButtons } from "./pages/TransactionPage/TransactionDoneB
96
96
  import { useAddressActivity } from "./hooks/useAddressActivity.js";
97
97
  import { ConfirmToAddressSheet } from "./pages/TransactionPage/ConfirmToAddressSheet.js";
98
98
  import { StartTransactionButton } from "./pages/TransactionPage/StartTransactionButton.js";
99
- import { calculateValueLossPercentage, getTokenValueLossThreshold } from "./pages/TransactionPage/utils.js";
99
+ import { calculateValueLossPercentage, getTokenValueLossThreshold, isCallBundleNotFound } from "./pages/TransactionPage/utils.js";
100
100
  import { TokenValueBottomSheet } from "./pages/TransactionPage/TokenValueBottomSheet.js";
101
101
  import { I18nProvider } from "./providers/I18nProvider/I18nProvider.js";
102
102
  import { QueryClientProvider } from "./providers/QueryClientProvider.js";
@@ -104,4 +104,4 @@ import { createTheme } from "./themes/createTheme.js";
104
104
  import { WalletProvider } from "./providers/WalletProvider/WalletProvider.js";
105
105
  import { StoreProvider } from "./stores/StoreProvider.js";
106
106
  import { useTokenList } from "./hooks/useTokenList.js";
107
- export { ActionRow, AddressType, AmountInputEndAdornment, AvatarBadgedDefault, BaseTransactionButton, BookmarkStoreProvider, ButtonTertiary, Card, CardIconButton, CardTitle, ChainAvatar, ChainSelect, ConfirmToAddressSheet, ContactSupportButton, ContractComponent, DateLabelContainer, DateLabelText, DetailInfoIcon, DetailLabel, DetailLabelContainer, DetailRow, DetailValue, ElementId, ExchangeRateBottomSheet, ExecutionDoneCard, ExecutionStatusCard, ExternalLink, FeeBreakdownTooltip, FormControl, FormKeyHelper, I18nProvider, IconCircle, IconTypography, Input, InputCard, InputPriceButton, ListItemButton, MainWarningMessages, PageContainer, PoweredBy, ProgressToNextUpdate, QueryClientProvider, RouteCard, RouteCardSkeleton, RouteDetails, RouteExecutionStatus, RouteNotFoundCard, RouteTokens, RouteTracker, SDKClientProvider, SearchInput, SearchTokenInput, SelectChainPage, SendToWalletButton, SentToWalletRow, SettingsStoreProvider, Stack, StartTransactionButton, StatusIcon, StepActionsList, StoreProvider, Token, TokenAvatar, TokenNotFound, TokenRate, TokenValueBottomSheet, TransactionDoneButtons, VirtualizedTokenList, WalletProvider, WarningMessages, WidgetEvent, WidgetProvider, buildRouteFromTxHistory, calculateValueLossPercentage, createElementId, createTheme, fitInputText, formatDuration, formatInputAmount, formatTokenAmount, formatTokenPrice, getAccumulatedFeeCostsBreakdown, getPriceImpact, getSourceTxHash, getTokenValueLossThreshold, hasEnumFlag, isHttpUrl, isRouteActive, isRouteDone, isRouteFailed, navigationRoutes, openInNewTab, priceToTokenAmount, resolveStorageScopeKey, shortenAddress, usdDecimals, useAddressActivity, useAddressValidation, useAvailableChains, useChain, useChainOrderStore, useContactSupport, useDebouncedWatch, useExecutionRows, useExplorer, useFieldActions, useFieldValues, useHeader, useHeaderStore, useInputModeStore, useListHeight, useNavigateBack, useRouteExecution, useRouteExecutionMessage, useRouteExecutionStore, useRouteExecutionStoreContext, useRoutes, useSDKClient, useSetHeaderHeight, useSettingsStoreContext, useToAddressRequirements, useToken, useTokenAddressBalance, useTokenBalances, useTokenList, useTokenSelect, useTools, useWidgetConfig, useWidgetEvents };
107
+ export { ActionRow, AddressType, AmountInputEndAdornment, AvatarBadgedDefault, BaseTransactionButton, BookmarkStoreProvider, ButtonTertiary, Card, CardIconButton, CardTitle, ChainAvatar, ChainSelect, ConfirmToAddressSheet, ContactSupportButton, ContractComponent, DateLabelContainer, DateLabelText, DetailInfoIcon, DetailLabel, DetailLabelContainer, DetailRow, DetailValue, ElementId, ExchangeRateBottomSheet, ExecutionDoneCard, ExecutionStatusCard, ExternalLink, FeeBreakdownTooltip, FormControl, FormKeyHelper, I18nProvider, IconCircle, IconTypography, Input, InputCard, InputPriceButton, ListItemButton, MainWarningMessages, PageContainer, PoweredBy, ProgressToNextUpdate, QueryClientProvider, RouteCard, RouteCardSkeleton, RouteDetails, RouteExecutionStatus, RouteNotFoundCard, RouteTokens, RouteTracker, SDKClientProvider, SearchInput, SearchTokenInput, SelectChainPage, SendToWalletButton, SentToWalletRow, SettingsStoreProvider, Stack, StartTransactionButton, StatusIcon, StepActionsList, StoreProvider, Token, TokenAvatar, TokenNotFound, TokenRate, TokenValueBottomSheet, TransactionDoneButtons, VirtualizedTokenList, WalletProvider, WarningMessages, WidgetEvent, WidgetProvider, buildRouteFromTxHistory, calculateValueLossPercentage, createElementId, createTheme, fitInputText, formatDuration, formatInputAmount, formatTokenAmount, formatTokenPrice, getAccumulatedFeeCostsBreakdown, getPriceImpact, getSourceTxHash, getTokenValueLossThreshold, hasEnumFlag, isCallBundleNotFound, isHttpUrl, isRouteActive, isRouteDone, isRouteFailed, navigationRoutes, openInNewTab, priceToTokenAmount, resolveStorageScopeKey, shortenAddress, usdDecimals, useAddressActivity, useAddressValidation, useAvailableChains, useChain, useChainOrderStore, useContactSupport, useDebouncedWatch, useExecutionRows, useExplorer, useFieldActions, useFieldValues, useHeader, useHeaderStore, useInputModeStore, useListHeight, useNavigateBack, useRouteExecution, useRouteExecutionMessage, useRouteExecutionStore, useRouteExecutionStoreContext, useRoutes, useSDKClient, useSetHeaderHeight, useSettingsStoreContext, useToAddressRequirements, useToken, useTokenAddressBalance, useTokenBalances, useTokenList, useTokenSelect, useTools, useWidgetConfig, useWidgetEvents };
@@ -1,4 +1,4 @@
1
- import { ExecutionAction, RouteExtended } from "@lifi/sdk";
1
+ import { ExecutionAction, LiFiStepExtended, RouteExtended } from "@lifi/sdk";
2
2
  //#region src/stores/routes/utils.d.ts
3
3
  export declare const isRouteDone: (route: RouteExtended) => boolean;
4
4
  export declare const isRouteFailed: (route: RouteExtended) => boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","names":[],"sources":["../../../../src/stores/routes/utils.ts"],"mappings":";;qBAGa,cAAe,OAAO;qBAgBtB,gBAAiB,OAAO;qBAIxB,gBAAiB,QAAQ;qBAgCzB,kBAAmB,QAAQ"}
1
+ {"version":3,"file":"utils.d.ts","names":[],"sources":["../../../../src/stores/routes/utils.ts"],"mappings":";;qBAQa,cAAe,OAAO;qBAgBtB,gBAAiB,OAAO;qBAsBxB,gBAAiB,QAAQ;qBAgCzB,kBAAmB,QAAQ"}
@@ -12,6 +12,18 @@ const isRouteRefunded = (route) => {
12
12
  const isRouteFailed = (route) => {
13
13
  return route.steps.some((step) => step.execution?.status === "FAILED");
14
14
  };
15
+ const getFailedStepAction = (route) => {
16
+ const step = route.steps.find((step) => step.execution?.status === "FAILED");
17
+ if (!step) return;
18
+ return {
19
+ step,
20
+ action: step.execution?.actions?.find((action) => action.status === "FAILED") || {
21
+ status: "FAILED",
22
+ type: "EXECUTION",
23
+ error: step.execution?.error
24
+ }
25
+ };
26
+ };
15
27
  const isRouteActive = (route) => {
16
28
  if (!route) return false;
17
29
  const isDone = isRouteDone(route);
@@ -30,6 +42,6 @@ const getSourceTxHash = (route) => {
30
42
  return sourceAction?.txHash || sourceAction?.taskId;
31
43
  };
32
44
  //#endregion
33
- export { getSourceTxHash, getUpdatedAction, isRouteActive, isRouteDone, isRouteFailed, isRoutePartiallyDone, isRouteRefunded };
45
+ export { getFailedStepAction, getSourceTxHash, getUpdatedAction, isRouteActive, isRouteDone, isRouteFailed, isRoutePartiallyDone, isRouteRefunded };
34
46
 
35
47
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../../../src/stores/routes/utils.ts"],"sourcesContent":["import type { ExecutionAction, RouteExtended } from '@lifi/sdk'\nimport microdiff from 'microdiff'\n\nexport const isRouteDone = (route: RouteExtended): boolean => {\n return route.steps.every((step) => step.execution?.status === 'DONE')\n}\n\nexport const isRoutePartiallyDone = (route: RouteExtended): boolean => {\n return route.steps.some((step) =>\n step.execution?.actions?.some((action) => action.substatus === 'PARTIAL')\n )\n}\n\nexport const isRouteRefunded = (route: RouteExtended): boolean => {\n return route.steps.some((step) =>\n step.execution?.actions?.some((action) => action.substatus === 'REFUNDED')\n )\n}\n\nexport const isRouteFailed = (route: RouteExtended): boolean => {\n return route.steps.some((step) => step.execution?.status === 'FAILED')\n}\n\nexport const isRouteActive = (route?: RouteExtended): boolean => {\n if (!route) {\n return false\n }\n const isDone = isRouteDone(route)\n const isFailed = isRouteFailed(route)\n const alreadyStarted = route.steps.some((step) => step.execution)\n return !isDone && !isFailed && alreadyStarted\n}\n\nexport const getUpdatedAction = (\n currentRoute: RouteExtended,\n updatedRoute: RouteExtended\n): ExecutionAction | undefined => {\n const actionDiff = microdiff(currentRoute, updatedRoute).find((diff) =>\n diff.path.includes('actions')\n )\n if (!actionDiff) {\n return undefined\n }\n // Find action index in the diff array so we can slice the complete action object\n // e.g. ['steps', 0, 'execution', 'actions', 0, 'message']\n const actionDiffIndex = actionDiff.path.indexOf('actions') + 2\n const actionPathSlice = actionDiff.path.slice(0, actionDiffIndex)\n // Reduce updated route using the diff path to get updated process\n const action = actionPathSlice.reduce(\n (obj, path) => obj[path],\n updatedRoute as any\n ) as ExecutionAction\n return action\n}\n\nexport const getSourceTxHash = (route?: RouteExtended): string | undefined => {\n const sourceAction = route?.steps[0].execution?.actions\n ?.filter(\n (action) => !['RESET_ALLOWANCE', 'SET_ALLOWANCE'].includes(action.type)\n )\n .find((action) => action.txHash || action.taskId)\n return sourceAction?.txHash || sourceAction?.taskId\n}\n"],"mappings":";;AAGA,MAAa,eAAe,UAAkC;CAC5D,OAAO,MAAM,MAAM,OAAO,SAAS,KAAK,WAAW,WAAW,MAAM;AACtE;AAEA,MAAa,wBAAwB,UAAkC;CACrE,OAAO,MAAM,MAAM,MAAM,SACvB,KAAK,WAAW,SAAS,MAAM,WAAW,OAAO,cAAc,SAAS,CAC1E;AACF;AAEA,MAAa,mBAAmB,UAAkC;CAChE,OAAO,MAAM,MAAM,MAAM,SACvB,KAAK,WAAW,SAAS,MAAM,WAAW,OAAO,cAAc,UAAU,CAC3E;AACF;AAEA,MAAa,iBAAiB,UAAkC;CAC9D,OAAO,MAAM,MAAM,MAAM,SAAS,KAAK,WAAW,WAAW,QAAQ;AACvE;AAEA,MAAa,iBAAiB,UAAmC;CAC/D,IAAI,CAAC,OACH,OAAO;CAET,MAAM,SAAS,YAAY,KAAK;CAChC,MAAM,WAAW,cAAc,KAAK;CACpC,MAAM,iBAAiB,MAAM,MAAM,MAAM,SAAS,KAAK,SAAS;CAChE,OAAO,CAAC,UAAU,CAAC,YAAY;AACjC;AAEA,MAAa,oBACX,cACA,iBACgC;CAChC,MAAM,aAAa,UAAU,cAAc,YAAY,CAAC,CAAC,MAAM,SAC7D,KAAK,KAAK,SAAS,SAAS,CAC9B;CACA,IAAI,CAAC,YACH;CAIF,MAAM,kBAAkB,WAAW,KAAK,QAAQ,SAAS,IAAI;CAO7D,OANwB,WAAW,KAAK,MAAM,GAAG,eAEpB,CAAC,CAAC,QAC5B,KAAK,SAAS,IAAI,OACnB,YAEU;AACd;AAEA,MAAa,mBAAmB,UAA8C;CAC5E,MAAM,eAAe,OAAO,MAAM,EAAE,CAAC,WAAW,SAC5C,QACC,WAAW,CAAC,CAAC,mBAAmB,eAAe,CAAC,CAAC,SAAS,OAAO,IAAI,CACxE,CAAC,CACA,MAAM,WAAW,OAAO,UAAU,OAAO,MAAM;CAClD,OAAO,cAAc,UAAU,cAAc;AAC/C"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../../../src/stores/routes/utils.ts"],"sourcesContent":["import type {\n ExecutionAction,\n ExecutionActionType,\n LiFiStepExtended,\n RouteExtended,\n} from '@lifi/sdk'\nimport microdiff from 'microdiff'\n\nexport const isRouteDone = (route: RouteExtended): boolean => {\n return route.steps.every((step) => step.execution?.status === 'DONE')\n}\n\nexport const isRoutePartiallyDone = (route: RouteExtended): boolean => {\n return route.steps.some((step) =>\n step.execution?.actions?.some((action) => action.substatus === 'PARTIAL')\n )\n}\n\nexport const isRouteRefunded = (route: RouteExtended): boolean => {\n return route.steps.some((step) =>\n step.execution?.actions?.some((action) => action.substatus === 'REFUNDED')\n )\n}\n\nexport const isRouteFailed = (route: RouteExtended): boolean => {\n return route.steps.some((step) => step.execution?.status === 'FAILED')\n}\n\n// A step can fail before any action, so its own error then stands in.\nexport const getFailedStepAction = (\n route: RouteExtended\n): { step: LiFiStepExtended; action: ExecutionAction } | undefined => {\n const step = route.steps.find((step) => step.execution?.status === 'FAILED')\n if (!step) {\n return undefined\n }\n const action = step.execution?.actions?.find(\n (action) => action.status === 'FAILED'\n ) || {\n status: 'FAILED',\n type: 'EXECUTION' as ExecutionActionType,\n error: step.execution?.error,\n }\n return { step, action }\n}\n\nexport const isRouteActive = (route?: RouteExtended): boolean => {\n if (!route) {\n return false\n }\n const isDone = isRouteDone(route)\n const isFailed = isRouteFailed(route)\n const alreadyStarted = route.steps.some((step) => step.execution)\n return !isDone && !isFailed && alreadyStarted\n}\n\nexport const getUpdatedAction = (\n currentRoute: RouteExtended,\n updatedRoute: RouteExtended\n): ExecutionAction | undefined => {\n const actionDiff = microdiff(currentRoute, updatedRoute).find((diff) =>\n diff.path.includes('actions')\n )\n if (!actionDiff) {\n return undefined\n }\n // Find action index in the diff array so we can slice the complete action object\n // e.g. ['steps', 0, 'execution', 'actions', 0, 'message']\n const actionDiffIndex = actionDiff.path.indexOf('actions') + 2\n const actionPathSlice = actionDiff.path.slice(0, actionDiffIndex)\n // Reduce updated route using the diff path to get updated process\n const action = actionPathSlice.reduce(\n (obj, path) => obj[path],\n updatedRoute as any\n ) as ExecutionAction\n return action\n}\n\nexport const getSourceTxHash = (route?: RouteExtended): string | undefined => {\n const sourceAction = route?.steps[0].execution?.actions\n ?.filter(\n (action) => !['RESET_ALLOWANCE', 'SET_ALLOWANCE'].includes(action.type)\n )\n .find((action) => action.txHash || action.taskId)\n return sourceAction?.txHash || sourceAction?.taskId\n}\n"],"mappings":";;AAQA,MAAa,eAAe,UAAkC;CAC5D,OAAO,MAAM,MAAM,OAAO,SAAS,KAAK,WAAW,WAAW,MAAM;AACtE;AAEA,MAAa,wBAAwB,UAAkC;CACrE,OAAO,MAAM,MAAM,MAAM,SACvB,KAAK,WAAW,SAAS,MAAM,WAAW,OAAO,cAAc,SAAS,CAC1E;AACF;AAEA,MAAa,mBAAmB,UAAkC;CAChE,OAAO,MAAM,MAAM,MAAM,SACvB,KAAK,WAAW,SAAS,MAAM,WAAW,OAAO,cAAc,UAAU,CAC3E;AACF;AAEA,MAAa,iBAAiB,UAAkC;CAC9D,OAAO,MAAM,MAAM,MAAM,SAAS,KAAK,WAAW,WAAW,QAAQ;AACvE;AAGA,MAAa,uBACX,UACoE;CACpE,MAAM,OAAO,MAAM,MAAM,MAAM,SAAS,KAAK,WAAW,WAAW,QAAQ;CAC3E,IAAI,CAAC,MACH;CASF,OAAO;EAAE;EAAM,QAPA,KAAK,WAAW,SAAS,MACrC,WAAW,OAAO,WAAW,QAChC,KAAK;GACH,QAAQ;GACR,MAAM;GACN,OAAO,KAAK,WAAW;EACzB;CACsB;AACxB;AAEA,MAAa,iBAAiB,UAAmC;CAC/D,IAAI,CAAC,OACH,OAAO;CAET,MAAM,SAAS,YAAY,KAAK;CAChC,MAAM,WAAW,cAAc,KAAK;CACpC,MAAM,iBAAiB,MAAM,MAAM,MAAM,SAAS,KAAK,SAAS;CAChE,OAAO,CAAC,UAAU,CAAC,YAAY;AACjC;AAEA,MAAa,oBACX,cACA,iBACgC;CAChC,MAAM,aAAa,UAAU,cAAc,YAAY,CAAC,CAAC,MAAM,SAC7D,KAAK,KAAK,SAAS,SAAS,CAC9B;CACA,IAAI,CAAC,YACH;CAIF,MAAM,kBAAkB,WAAW,KAAK,QAAQ,SAAS,IAAI;CAO7D,OANwB,WAAW,KAAK,MAAM,GAAG,eAEpB,CAAC,CAAC,QAC5B,KAAK,SAAS,IAAI,OACnB,YAEU;AACd;AAEA,MAAa,mBAAmB,UAA8C;CAC5E,MAAM,eAAe,OAAO,MAAM,EAAE,CAAC,WAAW,SAC5C,QACC,WAAW,CAAC,CAAC,mBAAmB,eAAe,CAAC,CAAC,SAAS,OAAO,IAAI,CACxE,CAAC,CACA,MAAM,WAAW,OAAO,UAAU,OAAO,MAAM;CAClD,OAAO,cAAc,UAAU,cAAc;AAC/C"}
@@ -99,6 +99,10 @@ function getErrorMessage(t, getChainById, step, failedAction) {
99
99
  chainName: getChainById(step.action.fromChainId)?.name ?? ""
100
100
  });
101
101
  break;
102
+ case LiFiErrorCode.CallBundleNotFound:
103
+ title = t("error.title.callBundleNotFound");
104
+ message = t("error.message.callBundleNotFound");
105
+ break;
102
106
  default:
103
107
  title = t("error.title.unknown");
104
108
  if (failedAction?.txHash) message = t("error.message.transactionFailed");
@@ -1 +1 @@
1
- {"version":3,"file":"getErrorMessage.js","names":[],"sources":["../../../src/utils/getErrorMessage.ts"],"sourcesContent":["import {\n type EVMChain,\n type ExecutionAction,\n LiFiErrorCode,\n type LiFiStepExtended,\n} from '@lifi/sdk'\nimport type { TFunction } from 'i18next'\nimport { formatTokenAmount, wrapLongWords } from '../utils/format.js'\n\nexport function getErrorMessage(\n t: TFunction,\n getChainById: (chainId: number) => EVMChain | undefined,\n step: LiFiStepExtended,\n failedAction?: ExecutionAction\n): { title: string; message: string } {\n const error = failedAction?.error\n if (!error) {\n return {\n title: t('error.title.unknown'),\n message: t('error.message.unknown'),\n }\n }\n\n const getDefaultErrorMessage = (key?: string) =>\n `${t((key as any) ?? 'error.message.transactionNotSent')} ${t(\n 'error.message.remainInYourWallet',\n {\n amount: formatTokenAmount(\n BigInt(step.action.fromAmount),\n step.action.fromToken.decimals\n ),\n tokenSymbol: step.action.fromToken.symbol,\n chainName: getChainById(step.action.fromChainId)?.name ?? '',\n }\n )}`\n\n let title = ''\n let message = ''\n switch (error.code) {\n case LiFiErrorCode.AllowanceRequired:\n title = t('error.title.allowanceRequired')\n message = t('error.message.allowanceRequired', {\n tokenSymbol: step.action.fromToken.symbol,\n })\n break\n case LiFiErrorCode.BalanceError:\n title = t('error.title.balanceIsTooLow')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.ChainSwitchError:\n title = t('error.title.chainSwitch')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.GasLimitError:\n title = t('error.title.gasLimitIsTooLow')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.InsufficientFunds:\n title = t('error.title.insufficientFunds')\n message = `${t(\n 'error.message.insufficientFunds'\n )} ${getDefaultErrorMessage()}`\n break\n case LiFiErrorCode.InsufficientGas:\n title = t('error.title.insufficientGas')\n message = `${t(\n 'error.message.insufficientFunds'\n )} ${getDefaultErrorMessage()}`\n break\n case LiFiErrorCode.SlippageError:\n title = t('error.title.slippageNotMet')\n message = t('error.message.slippageThreshold')\n break\n case LiFiErrorCode.TransactionFailed:\n title = t('error.title.transactionFailed')\n message = t('error.message.transactionFailed')\n break\n case LiFiErrorCode.TransactionExpired:\n title = t('error.title.transactionExpired')\n message = t('error.message.transactionExpired')\n break\n case LiFiErrorCode.TransactionSimulationFailed:\n title = t('error.title.transactionSimulationFailed')\n message = t('error.message.transactionSimulationFailed')\n break\n case LiFiErrorCode.WalletChangedDuringExecution:\n title = t('error.title.walletMismatch')\n message = t('error.message.walletChangedDuringExecution')\n break\n case LiFiErrorCode.RateLimitExceeded:\n title = t('error.title.rateLimitExceeded')\n message = t('error.message.rateLimitExceeded')\n break\n case LiFiErrorCode.ThirdPartyError:\n title = t('error.title.thirdPartyError')\n message = t('error.message.thirdPartyError')\n break\n case LiFiErrorCode.TransactionUnderpriced:\n title = t('error.title.transactionUnderpriced')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.TransactionUnprepared:\n title = t('error.title.transactionUnprepared')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.TransactionCanceled:\n title = t('error.title.transactionCanceled')\n message = getDefaultErrorMessage('error.message.transactionCanceled')\n break\n case LiFiErrorCode.TransactionRejected:\n title = t('error.title.transactionRejected')\n message = getDefaultErrorMessage('error.message.transactionRejected')\n break\n case LiFiErrorCode.TransactionConflict:\n title = t('error.title.transactionConflict')\n message = getDefaultErrorMessage('error.message.transactionConflict')\n break\n case LiFiErrorCode.ExchangeRateUpdateCanceled:\n title = t('error.title.exchangeRateUpdateCanceled')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.SignatureRejected:\n title = t('error.title.signatureRejected')\n message = t('error.message.signatureRejected', {\n amount: formatTokenAmount(\n BigInt(step.action.fromAmount),\n step.action.fromToken.decimals\n ),\n tokenSymbol: step.action.fromToken.symbol,\n chainName: getChainById(step.action.fromChainId)?.name ?? '',\n })\n break\n default:\n title = t('error.title.unknown')\n if (failedAction?.txHash) {\n message = t('error.message.transactionFailed')\n } else {\n message = error.message || t('error.message.unknown')\n }\n break\n }\n message = wrapLongWords(message)\n return { title, message }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"getErrorMessage.js","names":[],"sources":["../../../src/utils/getErrorMessage.ts"],"sourcesContent":["import {\n type EVMChain,\n type ExecutionAction,\n LiFiErrorCode,\n type LiFiStepExtended,\n} from '@lifi/sdk'\nimport type { TFunction } from 'i18next'\nimport { formatTokenAmount, wrapLongWords } from '../utils/format.js'\n\nexport function getErrorMessage(\n t: TFunction,\n getChainById: (chainId: number) => EVMChain | undefined,\n step: LiFiStepExtended,\n failedAction?: ExecutionAction\n): { title: string; message: string } {\n const error = failedAction?.error\n if (!error) {\n return {\n title: t('error.title.unknown'),\n message: t('error.message.unknown'),\n }\n }\n\n const getDefaultErrorMessage = (key?: string) =>\n `${t((key as any) ?? 'error.message.transactionNotSent')} ${t(\n 'error.message.remainInYourWallet',\n {\n amount: formatTokenAmount(\n BigInt(step.action.fromAmount),\n step.action.fromToken.decimals\n ),\n tokenSymbol: step.action.fromToken.symbol,\n chainName: getChainById(step.action.fromChainId)?.name ?? '',\n }\n )}`\n\n let title = ''\n let message = ''\n switch (error.code) {\n case LiFiErrorCode.AllowanceRequired:\n title = t('error.title.allowanceRequired')\n message = t('error.message.allowanceRequired', {\n tokenSymbol: step.action.fromToken.symbol,\n })\n break\n case LiFiErrorCode.BalanceError:\n title = t('error.title.balanceIsTooLow')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.ChainSwitchError:\n title = t('error.title.chainSwitch')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.GasLimitError:\n title = t('error.title.gasLimitIsTooLow')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.InsufficientFunds:\n title = t('error.title.insufficientFunds')\n message = `${t(\n 'error.message.insufficientFunds'\n )} ${getDefaultErrorMessage()}`\n break\n case LiFiErrorCode.InsufficientGas:\n title = t('error.title.insufficientGas')\n message = `${t(\n 'error.message.insufficientFunds'\n )} ${getDefaultErrorMessage()}`\n break\n case LiFiErrorCode.SlippageError:\n title = t('error.title.slippageNotMet')\n message = t('error.message.slippageThreshold')\n break\n case LiFiErrorCode.TransactionFailed:\n title = t('error.title.transactionFailed')\n message = t('error.message.transactionFailed')\n break\n case LiFiErrorCode.TransactionExpired:\n title = t('error.title.transactionExpired')\n message = t('error.message.transactionExpired')\n break\n case LiFiErrorCode.TransactionSimulationFailed:\n title = t('error.title.transactionSimulationFailed')\n message = t('error.message.transactionSimulationFailed')\n break\n case LiFiErrorCode.WalletChangedDuringExecution:\n title = t('error.title.walletMismatch')\n message = t('error.message.walletChangedDuringExecution')\n break\n case LiFiErrorCode.RateLimitExceeded:\n title = t('error.title.rateLimitExceeded')\n message = t('error.message.rateLimitExceeded')\n break\n case LiFiErrorCode.ThirdPartyError:\n title = t('error.title.thirdPartyError')\n message = t('error.message.thirdPartyError')\n break\n case LiFiErrorCode.TransactionUnderpriced:\n title = t('error.title.transactionUnderpriced')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.TransactionUnprepared:\n title = t('error.title.transactionUnprepared')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.TransactionCanceled:\n title = t('error.title.transactionCanceled')\n message = getDefaultErrorMessage('error.message.transactionCanceled')\n break\n case LiFiErrorCode.TransactionRejected:\n title = t('error.title.transactionRejected')\n message = getDefaultErrorMessage('error.message.transactionRejected')\n break\n case LiFiErrorCode.TransactionConflict:\n title = t('error.title.transactionConflict')\n message = getDefaultErrorMessage('error.message.transactionConflict')\n break\n case LiFiErrorCode.ExchangeRateUpdateCanceled:\n title = t('error.title.exchangeRateUpdateCanceled')\n message = getDefaultErrorMessage()\n break\n case LiFiErrorCode.SignatureRejected:\n title = t('error.title.signatureRejected')\n message = t('error.message.signatureRejected', {\n amount: formatTokenAmount(\n BigInt(step.action.fromAmount),\n step.action.fromToken.decimals\n ),\n tokenSymbol: step.action.fromToken.symbol,\n chainName: getChainById(step.action.fromChainId)?.name ?? '',\n })\n break\n // No \"remain in your wallet\" suffix: the funds may already be gone.\n case LiFiErrorCode.CallBundleNotFound:\n title = t('error.title.callBundleNotFound')\n message = t('error.message.callBundleNotFound')\n break\n default:\n title = t('error.title.unknown')\n if (failedAction?.txHash) {\n message = t('error.message.transactionFailed')\n } else {\n message = error.message || t('error.message.unknown')\n }\n break\n }\n message = wrapLongWords(message)\n return { title, message }\n}\n"],"mappings":"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"}
@@ -124,6 +124,18 @@ const getTokenVerificationProvider = (token) => {
124
124
  const provider = token.verificationStatusBreakdown?.find((entry) => entry.result === "verified")?.provider;
125
125
  return provider ? `${provider.charAt(0).toUpperCase()}${provider.slice(1)}` : void 0;
126
126
  };
127
+ const getTokenVerificationBadge = (token) => {
128
+ if (token.native) return "native";
129
+ if (token.verificationStatus === "flagged") return "flagged";
130
+ if (token.verificationStatus === "verified") return "verified";
131
+ if (token.listed || token.verified) return;
132
+ return token.verificationStatus === "unverified" ? "unverifiedByProvider" : "unverified";
133
+ };
134
+ const getTokenStatusTextKey = (token) => {
135
+ if (token.verificationStatus === "verified") return "tokenMetric.statusVerified";
136
+ if (token.verificationStatus === "flagged" && !token.native) return "tokenMetric.statusFlagged";
137
+ return "tokenMetric.statusUnverified";
138
+ };
127
139
  /**
128
140
  * Hyperliquid declares Arbitrum USDC as its native token, Lighter declares
129
141
  * Ethereum USDC. Neither is a gas token you hold there. A chain-local
@@ -132,6 +144,6 @@ const getTokenVerificationProvider = (token) => {
132
144
  const chainIdsWithoutNativeToken = /* @__PURE__ */ new Set([ChainId.HPL, ChainId.LTR]);
133
145
  const getNativeTokenAddresses = (chains) => new Map(chains?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id)).map((chain) => [chain.id, chain.nativeToken.address]));
134
146
  //#endregion
135
- export { filterAllowedTokens, getChainTokenAllowSets, getExecutionToToken, getNativeTokenAddresses, getTokenVerificationProvider, getVerifiedTokensSets, knownPriceUSD, mergeFallbackToken, mergeVerifiedWithSearchTokens, updateTokenInCache };
147
+ export { filterAllowedTokens, getChainTokenAllowSets, getExecutionToToken, getNativeTokenAddresses, getTokenStatusTextKey, getTokenVerificationBadge, getTokenVerificationProvider, getVerifiedTokensSets, knownPriceUSD, mergeFallbackToken, mergeVerifiedWithSearchTokens, updateTokenInCache };
136
148
 
137
149
  //# sourceMappingURL=token.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"token.js","names":[],"sources":["../../../src/utils/token.ts"],"sourcesContent":["import type {\n BaseToken,\n ExtendedChain,\n RouteExtended,\n StaticToken,\n Token,\n TokenAmount,\n TokenExtended,\n} from '@lifi/sdk'\nimport { ChainId } from '@lifi/sdk'\nimport type { FormType } from '../stores/form/types.js'\nimport type { TokensByChain, TokenWithFlags } from '../types/token.js'\nimport type {\n AllowDenySets,\n WidgetChains,\n WidgetTokens,\n} from '../types/widget.js'\nimport { getConfigItemSets, isFormItemAllowed } from './item.js'\n\n// Number(): plain-JS integrators may pass chainId as a string.\nexport const getChainTokenAllowSets = (\n configTokens: WidgetTokens | undefined,\n chainId: number,\n formType?: FormType\n): AllowDenySets | undefined =>\n getConfigItemSets(\n configTokens,\n (tokens: BaseToken[]) =>\n new Set(\n tokens\n .filter((t) => Number(t.chainId) === chainId)\n .map((t) => t.address.toLowerCase())\n ),\n formType\n )\n\n/**\n * Builds per-chain sets of lowercase token addresses from the\n * `tokens.verified` config allowlist.\n */\nexport const getVerifiedTokensSets = (\n configTokens?: WidgetTokens\n): Map<number, Set<string>> | undefined => {\n if (!configTokens?.verified?.length) {\n return undefined\n }\n const sets = new Map<number, Set<string>>()\n for (const token of configTokens.verified) {\n const chainId = Number(token.chainId)\n let addresses = sets.get(chainId)\n if (!addresses) {\n addresses = new Set()\n sets.set(chainId, addresses)\n }\n addresses.add(token.address.toLowerCase())\n }\n return sets\n}\n\n/**\n * Merges verified tokens with search tokens.\n * Verified tokens take priority - search tokens are only added if they don't already exist.\n */\nexport const mergeVerifiedWithSearchTokens = (\n verifiedTokens?: TokensByChain,\n searchTokens?: TokensByChain\n): TokensByChain | undefined => {\n if (!verifiedTokens) {\n return searchTokens\n }\n if (!searchTokens) {\n return verifiedTokens\n }\n\n const result = { ...verifiedTokens }\n\n for (const [chainId, tokens] of Object.entries(searchTokens)) {\n const chainIdNum = Number(chainId)\n const existingTokens = result[chainIdNum] || []\n const existingAddresses = new Set(\n existingTokens.map((t) => t.address.toLowerCase())\n )\n\n const newTokens = tokens.filter(\n (t) => !existingAddresses.has(t.address.toLowerCase())\n )\n\n if (newTokens.length) {\n result[chainIdNum] = [...existingTokens, ...newTokens]\n }\n }\n\n return result\n}\n\n/**\n * `priceUSD` is a required string, so an unknown price arrives as `'0'` or `''`\n * rather than as an absent key — the same reading as in `useLimitMarketRate`. A\n * known price must never lose to one, or a route that cannot price its output\n * blanks a value the cache already knew.\n */\nexport const knownPriceUSD = (fresher: string, cached: string): string =>\n Number(fresher) ? fresher : cached\n\n/**\n * Merges a cached token into a route token for display. The route wins on every\n * field it carries; the cache only fills gaps. Letting the cache win instead\n * swaps the quoted price for a stale feed price.\n */\nexport const mergeFallbackToken = (\n routeToken: TokenAmount,\n cachedToken: TokenExtended | undefined\n): TokenAmount =>\n cachedToken\n ? {\n ...cachedToken,\n ...routeToken,\n priceUSD: knownPriceUSD(routeToken.priceUSD, cachedToken.priceUSD),\n logoURI: routeToken.logoURI || cachedToken.logoURI,\n }\n : routeToken\n\n/**\n * Updates a token in the cache by chainId and address.\n * Returns a new cache object with the token updated, or the original if not found.\n * Addresses are compared case-insensitively: the token list and the route can\n * spell the same address differently. The incoming token only overwrites what it\n * actually knows, so an unpriced or logo-less route cannot blank the cache.\n */\nexport const updateTokenInCache = (\n data: TokensByChain | undefined,\n token: Token\n): TokensByChain | undefined => {\n if (!data) {\n return data\n }\n const chainTokens = data[token.chainId]\n if (!chainTokens) {\n return data\n }\n const address = token.address.toLowerCase()\n const index = chainTokens.findIndex(\n (t) => t.address.toLowerCase() === address\n )\n if (index < 0) {\n return data\n }\n return {\n ...data,\n [token.chainId]: chainTokens.map((t, i) =>\n i === index\n ? {\n ...t,\n ...token,\n // Keep the cached address: other readers hold it as the identity.\n address: t.address,\n priceUSD: knownPriceUSD(token.priceUSD, t.priceUSD),\n logoURI: token.logoURI || t.logoURI,\n }\n : t\n ),\n }\n}\n\nexport const filterAllowedTokens = (\n dataTokens: TokensByChain | undefined,\n configTokens?: WidgetTokens,\n chainsConfig?: WidgetChains,\n formType?: FormType,\n nativeTokenAddresses?: Map<number, string>\n): TokensByChain | undefined => {\n if (!dataTokens) {\n return\n }\n\n const includedTokens = configTokens?.include || []\n const allChainIds = Array.from(\n new Set([\n ...includedTokens.map((t) => t.chainId),\n ...Object.keys(dataTokens),\n ])\n ).map((chainId) => Number(chainId))\n\n const configChainIdsSet = getConfigItemSets(\n chainsConfig,\n (chainIds: number[]) => new Set(chainIds),\n formType\n )\n\n const allowedChainIds = configChainIdsSet\n ? allChainIds.filter((chainId) =>\n isFormItemAllowed(chainId, configChainIdsSet, formType)\n )\n : allChainIds\n\n const verifiedTokensSets = getVerifiedTokensSets(configTokens)\n\n const allowedTokensByChain: TokensByChain = {}\n for (const chainId of allowedChainIds) {\n const chainIncludedTokens = includedTokens.filter(\n (t) => Number(t.chainId) === chainId\n )\n const includedAddresses = new Set(\n chainIncludedTokens.map((t) => t.address.toLowerCase())\n )\n\n const allowedAddresses = getChainTokenAllowSets(\n configTokens,\n chainId,\n formType\n )\n\n const verifiedAddresses = verifiedTokensSets?.get(chainId)\n const nativeAddress = nativeTokenAddresses?.get(chainId)?.toLowerCase()\n\n const chainTokens: TokenWithFlags[] = [\n ...(dataTokens[chainId] ?? []),\n ...chainIncludedTokens,\n ]\n\n const filtered: TokenWithFlags[] = []\n const seenAddresses = new Set<string>()\n for (const token of chainTokens) {\n const address = token.address.toLowerCase()\n // Data tokens come first and win over include duplicates,\n // keeping their extended data\n if (seenAddresses.has(address)) {\n continue\n }\n seenAddresses.add(address)\n if (\n !isFormItemAllowed(token, allowedAddresses, formType, (t) =>\n t.address.toLowerCase()\n )\n ) {\n continue\n }\n // Include and verified-allowlist tokens are explicitly set by the\n // integrator, mark them as verified. Flags are only ever set to true,\n // so a token that earns none keeps its cached object.\n const markVerified =\n !token.verified &&\n (includedAddresses.has(address) || verifiedAddresses?.has(address))\n const markNative = !!nativeAddress && nativeAddress === address\n if (markVerified || markNative) {\n filtered.push({\n ...token,\n ...(markVerified ? { verified: true } : {}),\n ...(markNative ? { native: true } : {}),\n })\n } else {\n filtered.push(token)\n }\n }\n\n allowedTokensByChain[chainId] = filtered\n }\n\n return allowedTokensByChain\n}\n\nexport const getExecutionToToken = (route: RouteExtended): TokenAmount => ({\n ...(route.steps.at(-1)?.execution?.toToken ?? route.toToken),\n amount: BigInt(\n route.steps.at(-1)?.execution?.toAmount ??\n route.steps.at(-1)?.estimate.toAmount ??\n route.toAmount\n ),\n})\n\n/** Capitalized name of the first provider that verified the token. */\nexport const getTokenVerificationProvider = (\n token: Pick<StaticToken, 'verificationStatusBreakdown'>\n): string | undefined => {\n const provider = token.verificationStatusBreakdown?.find(\n (entry) => entry.result === 'verified'\n )?.provider\n return provider\n ? `${provider.charAt(0).toUpperCase()}${provider.slice(1)}`\n : undefined\n}\n\n/**\n * Hyperliquid declares Arbitrum USDC as its native token, Lighter declares\n * Ethereum USDC. Neither is a gas token you hold there. A chain-local\n * stablecoin gas token, as on Arc or Stable, is genuine and stays.\n */\nconst chainIdsWithoutNativeToken = new Set([ChainId.HPL, ChainId.LTR])\n\nexport const getNativeTokenAddresses = (\n chains?: ExtendedChain[]\n): Map<number, string> =>\n new Map(\n chains\n ?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id))\n .map((chain) => [chain.id, chain.nativeToken.address])\n )\n"],"mappings":"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"}
1
+ {"version":3,"file":"token.js","names":[],"sources":["../../../src/utils/token.ts"],"sourcesContent":["import type {\n BaseToken,\n ExtendedChain,\n RouteExtended,\n StaticToken,\n Token,\n TokenAmount,\n TokenExtended,\n} from '@lifi/sdk'\nimport { ChainId } from '@lifi/sdk'\nimport type { FormType } from '../stores/form/types.js'\nimport type { TokensByChain, TokenWithFlags } from '../types/token.js'\nimport type {\n AllowDenySets,\n WidgetChains,\n WidgetTokens,\n} from '../types/widget.js'\nimport { getConfigItemSets, isFormItemAllowed } from './item.js'\n\n// Number(): plain-JS integrators may pass chainId as a string.\nexport const getChainTokenAllowSets = (\n configTokens: WidgetTokens | undefined,\n chainId: number,\n formType?: FormType\n): AllowDenySets | undefined =>\n getConfigItemSets(\n configTokens,\n (tokens: BaseToken[]) =>\n new Set(\n tokens\n .filter((t) => Number(t.chainId) === chainId)\n .map((t) => t.address.toLowerCase())\n ),\n formType\n )\n\n/**\n * Builds per-chain sets of lowercase token addresses from the\n * `tokens.verified` config allowlist.\n */\nexport const getVerifiedTokensSets = (\n configTokens?: WidgetTokens\n): Map<number, Set<string>> | undefined => {\n if (!configTokens?.verified?.length) {\n return undefined\n }\n const sets = new Map<number, Set<string>>()\n for (const token of configTokens.verified) {\n const chainId = Number(token.chainId)\n let addresses = sets.get(chainId)\n if (!addresses) {\n addresses = new Set()\n sets.set(chainId, addresses)\n }\n addresses.add(token.address.toLowerCase())\n }\n return sets\n}\n\n/**\n * Merges verified tokens with search tokens.\n * Verified tokens take priority - search tokens are only added if they don't already exist.\n */\nexport const mergeVerifiedWithSearchTokens = (\n verifiedTokens?: TokensByChain,\n searchTokens?: TokensByChain\n): TokensByChain | undefined => {\n if (!verifiedTokens) {\n return searchTokens\n }\n if (!searchTokens) {\n return verifiedTokens\n }\n\n const result = { ...verifiedTokens }\n\n for (const [chainId, tokens] of Object.entries(searchTokens)) {\n const chainIdNum = Number(chainId)\n const existingTokens = result[chainIdNum] || []\n const existingAddresses = new Set(\n existingTokens.map((t) => t.address.toLowerCase())\n )\n\n const newTokens = tokens.filter(\n (t) => !existingAddresses.has(t.address.toLowerCase())\n )\n\n if (newTokens.length) {\n result[chainIdNum] = [...existingTokens, ...newTokens]\n }\n }\n\n return result\n}\n\n/**\n * `priceUSD` is a required string, so an unknown price arrives as `'0'` or `''`\n * rather than as an absent key — the same reading as in `useLimitMarketRate`. A\n * known price must never lose to one, or a route that cannot price its output\n * blanks a value the cache already knew.\n */\nexport const knownPriceUSD = (fresher: string, cached: string): string =>\n Number(fresher) ? fresher : cached\n\n/**\n * Merges a cached token into a route token for display. The route wins on every\n * field it carries; the cache only fills gaps. Letting the cache win instead\n * swaps the quoted price for a stale feed price.\n */\nexport const mergeFallbackToken = (\n routeToken: TokenAmount,\n cachedToken: TokenExtended | undefined\n): TokenAmount =>\n cachedToken\n ? {\n ...cachedToken,\n ...routeToken,\n priceUSD: knownPriceUSD(routeToken.priceUSD, cachedToken.priceUSD),\n logoURI: routeToken.logoURI || cachedToken.logoURI,\n }\n : routeToken\n\n/**\n * Updates a token in the cache by chainId and address.\n * Returns a new cache object with the token updated, or the original if not found.\n * Addresses are compared case-insensitively: the token list and the route can\n * spell the same address differently. The incoming token only overwrites what it\n * actually knows, so an unpriced or logo-less route cannot blank the cache.\n */\nexport const updateTokenInCache = (\n data: TokensByChain | undefined,\n token: Token\n): TokensByChain | undefined => {\n if (!data) {\n return data\n }\n const chainTokens = data[token.chainId]\n if (!chainTokens) {\n return data\n }\n const address = token.address.toLowerCase()\n const index = chainTokens.findIndex(\n (t) => t.address.toLowerCase() === address\n )\n if (index < 0) {\n return data\n }\n return {\n ...data,\n [token.chainId]: chainTokens.map((t, i) =>\n i === index\n ? {\n ...t,\n ...token,\n // Keep the cached address: other readers hold it as the identity.\n address: t.address,\n priceUSD: knownPriceUSD(token.priceUSD, t.priceUSD),\n logoURI: token.logoURI || t.logoURI,\n }\n : t\n ),\n }\n}\n\nexport const filterAllowedTokens = (\n dataTokens: TokensByChain | undefined,\n configTokens?: WidgetTokens,\n chainsConfig?: WidgetChains,\n formType?: FormType,\n nativeTokenAddresses?: Map<number, string>\n): TokensByChain | undefined => {\n if (!dataTokens) {\n return\n }\n\n const includedTokens = configTokens?.include || []\n const allChainIds = Array.from(\n new Set([\n ...includedTokens.map((t) => t.chainId),\n ...Object.keys(dataTokens),\n ])\n ).map((chainId) => Number(chainId))\n\n const configChainIdsSet = getConfigItemSets(\n chainsConfig,\n (chainIds: number[]) => new Set(chainIds),\n formType\n )\n\n const allowedChainIds = configChainIdsSet\n ? allChainIds.filter((chainId) =>\n isFormItemAllowed(chainId, configChainIdsSet, formType)\n )\n : allChainIds\n\n const verifiedTokensSets = getVerifiedTokensSets(configTokens)\n\n const allowedTokensByChain: TokensByChain = {}\n for (const chainId of allowedChainIds) {\n const chainIncludedTokens = includedTokens.filter(\n (t) => Number(t.chainId) === chainId\n )\n const includedAddresses = new Set(\n chainIncludedTokens.map((t) => t.address.toLowerCase())\n )\n\n const allowedAddresses = getChainTokenAllowSets(\n configTokens,\n chainId,\n formType\n )\n\n const verifiedAddresses = verifiedTokensSets?.get(chainId)\n const nativeAddress = nativeTokenAddresses?.get(chainId)?.toLowerCase()\n\n const chainTokens: TokenWithFlags[] = [\n ...(dataTokens[chainId] ?? []),\n ...chainIncludedTokens,\n ]\n\n const filtered: TokenWithFlags[] = []\n const seenAddresses = new Set<string>()\n for (const token of chainTokens) {\n const address = token.address.toLowerCase()\n // Data tokens come first and win over include duplicates,\n // keeping their extended data\n if (seenAddresses.has(address)) {\n continue\n }\n seenAddresses.add(address)\n if (\n !isFormItemAllowed(token, allowedAddresses, formType, (t) =>\n t.address.toLowerCase()\n )\n ) {\n continue\n }\n // Include and verified-allowlist tokens are explicitly set by the\n // integrator, mark them as verified. Flags are only ever set to true,\n // so a token that earns none keeps its cached object.\n const markVerified =\n !token.verified &&\n (includedAddresses.has(address) || verifiedAddresses?.has(address))\n const markNative = !!nativeAddress && nativeAddress === address\n if (markVerified || markNative) {\n filtered.push({\n ...token,\n ...(markVerified ? { verified: true } : {}),\n ...(markNative ? { native: true } : {}),\n })\n } else {\n filtered.push(token)\n }\n }\n\n allowedTokensByChain[chainId] = filtered\n }\n\n return allowedTokensByChain\n}\n\nexport const getExecutionToToken = (route: RouteExtended): TokenAmount => ({\n ...(route.steps.at(-1)?.execution?.toToken ?? route.toToken),\n amount: BigInt(\n route.steps.at(-1)?.execution?.toAmount ??\n route.steps.at(-1)?.estimate.toAmount ??\n route.toAmount\n ),\n})\n\n/** Capitalized name of the first provider that verified the token. */\nexport const getTokenVerificationProvider = (\n token: Pick<StaticToken, 'verificationStatusBreakdown'>\n): string | undefined => {\n const provider = token.verificationStatusBreakdown?.find(\n (entry) => entry.result === 'verified'\n )?.provider\n return provider\n ? `${provider.charAt(0).toUpperCase()}${provider.slice(1)}`\n : undefined\n}\n\nexport type TokenVerificationBadge =\n | 'native'\n | 'flagged'\n | 'verified'\n | 'unverifiedByProvider'\n | 'unverified'\n\nexport const getTokenVerificationBadge = (\n token: Pick<\n TokenWithFlags,\n 'native' | 'listed' | 'verified' | 'verificationStatus'\n >\n): TokenVerificationBadge | undefined => {\n // The provider calls the native-address convention a scam on some chains.\n if (token.native) {\n return 'native'\n }\n if (token.verificationStatus === 'flagged') {\n return 'flagged'\n }\n if (token.verificationStatus === 'verified') {\n return 'verified'\n }\n if (token.listed || token.verified) {\n return undefined\n }\n return token.verificationStatus === 'unverified'\n ? 'unverifiedByProvider'\n : 'unverified'\n}\n\nexport type TokenStatusTextKey =\n | 'tokenMetric.statusVerified'\n | 'tokenMetric.statusFlagged'\n | 'tokenMetric.statusUnverified'\n\nexport const getTokenStatusTextKey = (\n token: Pick<TokenWithFlags, 'native' | 'verificationStatus'>\n): TokenStatusTextKey => {\n if (token.verificationStatus === 'verified') {\n return 'tokenMetric.statusVerified'\n }\n if (token.verificationStatus === 'flagged' && !token.native) {\n return 'tokenMetric.statusFlagged'\n }\n return 'tokenMetric.statusUnverified'\n}\n\n/**\n * Hyperliquid declares Arbitrum USDC as its native token, Lighter declares\n * Ethereum USDC. Neither is a gas token you hold there. A chain-local\n * stablecoin gas token, as on Arc or Stable, is genuine and stays.\n */\nconst chainIdsWithoutNativeToken = new Set([ChainId.HPL, ChainId.LTR])\n\nexport const getNativeTokenAddresses = (\n chains?: ExtendedChain[]\n): Map<number, string> =>\n new Map(\n chains\n ?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id))\n .map((chain) => [chain.id, chain.nativeToken.address])\n )\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/widget",
3
- "version": "4.9.0",
3
+ "version": "4.10.1",
4
4
  "description": "LI.FI Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.",
5
5
  "type": "module",
6
6
  "main": "./dist/esm/index.js",
@@ -57,9 +57,9 @@
57
57
  "dependencies": {
58
58
  "@emotion/react": "^11.14.0",
59
59
  "@emotion/styled": "^11.14.1",
60
- "@lifi/sdk": "^4.11.0",
61
- "@lifi/wallet-management": "4.3.2",
62
- "@lifi/widget-provider": "4.6.0",
60
+ "@lifi/sdk": "^4.12.0",
61
+ "@lifi/wallet-management": "4.3.3",
62
+ "@lifi/widget-provider": "4.6.1",
63
63
  "@mui/icons-material": "^9.4.0",
64
64
  "@mui/material": "^9.4.0",
65
65
  "@mui/system": "^9.4.0",
@@ -1,5 +1,6 @@
1
1
  import type { JSX, Ref } from 'react'
2
2
  import { useImperativeHandle, useRef, useState } from 'react'
3
+ import type { TokenAmount } from '../../types/token.js'
3
4
  import { BottomSheet } from '../BottomSheet/BottomSheet.js'
4
5
  import type { BottomSheetBase } from '../BottomSheet/types.js'
5
6
  import { TokenDetailsSheetContent } from './TokenDetailsSheetContent.js'
@@ -11,20 +12,16 @@ export const TokenDetailsSheet = ({
11
12
  ref?: Ref<TokenDetailsSheetBase>
12
13
  }): JSX.Element => {
13
14
  const bottomSheetRef = useRef<BottomSheetBase>(null)
14
- const [tokenAddress, setTokenAddress] = useState<string | undefined>(
15
- undefined
16
- )
17
- const [chainId, setChainId] = useState<number | undefined>(undefined)
15
+ const [listToken, setListToken] = useState<TokenAmount | undefined>(undefined)
18
16
  const [withoutContractAddress, setWithoutContractAddress] = useState(false)
19
17
 
20
18
  useImperativeHandle(
21
19
  ref,
22
20
  () => ({
23
21
  isOpen: () => bottomSheetRef.current?.isOpen(),
24
- open: (address: string, noContractAddress: boolean, chainId: number) => {
25
- setTokenAddress(address)
22
+ open: (token: TokenAmount, noContractAddress: boolean) => {
23
+ setListToken(token)
26
24
  setWithoutContractAddress(noContractAddress)
27
- setChainId(chainId)
28
25
  bottomSheetRef.current?.open()
29
26
  },
30
27
  close: () => {
@@ -38,9 +35,8 @@ export const TokenDetailsSheet = ({
38
35
  <BottomSheet ref={bottomSheetRef} keepMounted>
39
36
  <TokenDetailsSheetContent
40
37
  onClose={() => bottomSheetRef.current?.close()}
41
- tokenAddress={tokenAddress}
38
+ listToken={listToken}
42
39
  withoutContractAddress={withoutContractAddress}
43
- chainId={chainId}
44
40
  />
45
41
  </BottomSheet>
46
42
  )