@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,3 +1,8 @@
1
+ import { LiFiErrorCode, type RouteExtended } from '@lifi/sdk'
2
+ import { getFailedStepAction } from '../../stores/routes/utils.js'
3
+ import type { WidgetMode } from '../../types/widget.js'
4
+ import { formatTokenAmount } from '../../utils/format.js'
5
+
1
6
  export const calculateValueLossPercentage = (
2
7
  fromAmountUSD: number,
3
8
  toAmountUSD: number,
@@ -101,3 +106,79 @@ export const getStartGates = (
101
106
  ['address', needsAddressConfirmation(input)],
102
107
  ['value', input.valueLossExceeded && !input.isCustomMode],
103
108
  ]
109
+
110
+ // Only these modes show the plain from/to swap form.
111
+ const newSwapModes: readonly (WidgetMode | undefined)[] = [
112
+ undefined,
113
+ 'default',
114
+ 'split',
115
+ ]
116
+
117
+ export const isCallBundleNotFound = (route: RouteExtended): boolean =>
118
+ getFailedStepAction(route)?.action.error?.code ===
119
+ LiFiErrorCode.CallBundleNotFound
120
+
121
+ // A new swap repeats the whole route.
122
+ export const canStartNewSwap = ({
123
+ route,
124
+ mode,
125
+ swapOnly,
126
+ }: {
127
+ route: RouteExtended
128
+ mode: WidgetMode | undefined
129
+ swapOnly: boolean
130
+ }): boolean => {
131
+ const failed = getFailedStepAction(route)
132
+ if (!failed || !isCallBundleNotFound(route)) {
133
+ return false
134
+ }
135
+ return (
136
+ failed.step === route.steps[0] &&
137
+ route.steps.every(
138
+ (step) => step === failed.step || !step.execution?.actions?.length
139
+ ) &&
140
+ newSwapModes.includes(mode) &&
141
+ // A swap-only form requests no bridges.
142
+ !(swapOnly && route.fromChainId !== route.toChainId)
143
+ )
144
+ }
145
+
146
+ interface NewSwapFormValues {
147
+ fromChain: number
148
+ fromToken: string
149
+ fromAmount: string
150
+ toChain: number
151
+ toToken: string
152
+ toAddress?: string
153
+ }
154
+
155
+ export const getNewSwapFormValues = (
156
+ route: RouteExtended,
157
+ {
158
+ keepReceiver = false,
159
+ receiverRequired = false,
160
+ }: { keepReceiver?: boolean; receiverRequired?: boolean } = {}
161
+ ): NewSwapFormValues => {
162
+ const receiver = route.toAddress || route.fromAddress
163
+ const values: NewSwapFormValues = {
164
+ fromChain: route.fromChainId,
165
+ fromToken: route.fromToken.address,
166
+ // The quote's amount, not a step amount trimmed to the balance: the new quote checks it.
167
+ fromAmount: formatTokenAmount(
168
+ BigInt(route.fromAmount),
169
+ route.fromToken.decimals
170
+ ),
171
+ toChain: route.toChainId,
172
+ toToken: route.toToken.address,
173
+ }
174
+ if (!keepReceiver) {
175
+ // A required receiver stays, also when it is the sender.
176
+ values.toAddress =
177
+ receiver &&
178
+ (receiverRequired ||
179
+ receiver.toLowerCase() !== route.fromAddress?.toLowerCase())
180
+ ? receiver
181
+ : ''
182
+ }
183
+ return values
184
+ }
package/src/shared.ts CHANGED
@@ -114,6 +114,7 @@ export { TransactionDoneButtons } from './pages/TransactionPage/TransactionDoneB
114
114
  export {
115
115
  calculateValueLossPercentage,
116
116
  getTokenValueLossThreshold,
117
+ isCallBundleNotFound,
117
118
  } from './pages/TransactionPage/utils.js'
118
119
 
119
120
  // ── providers ────────────────────────────────────────────────────────────────
@@ -1,4 +1,9 @@
1
- import type { ExecutionAction, RouteExtended } from '@lifi/sdk'
1
+ import type {
2
+ ExecutionAction,
3
+ ExecutionActionType,
4
+ LiFiStepExtended,
5
+ RouteExtended,
6
+ } from '@lifi/sdk'
2
7
  import microdiff from 'microdiff'
3
8
 
4
9
  export const isRouteDone = (route: RouteExtended): boolean => {
@@ -21,6 +26,24 @@ export const isRouteFailed = (route: RouteExtended): boolean => {
21
26
  return route.steps.some((step) => step.execution?.status === 'FAILED')
22
27
  }
23
28
 
29
+ // A step can fail before any action, so its own error then stands in.
30
+ export const getFailedStepAction = (
31
+ route: RouteExtended
32
+ ): { step: LiFiStepExtended; action: ExecutionAction } | undefined => {
33
+ const step = route.steps.find((step) => step.execution?.status === 'FAILED')
34
+ if (!step) {
35
+ return undefined
36
+ }
37
+ const action = step.execution?.actions?.find(
38
+ (action) => action.status === 'FAILED'
39
+ ) || {
40
+ status: 'FAILED',
41
+ type: 'EXECUTION' as ExecutionActionType,
42
+ error: step.execution?.error,
43
+ }
44
+ return { step, action }
45
+ }
46
+
24
47
  export const isRouteActive = (route?: RouteExtended): boolean => {
25
48
  if (!route) {
26
49
  return false
@@ -130,6 +130,11 @@ export function getErrorMessage(
130
130
  chainName: getChainById(step.action.fromChainId)?.name ?? '',
131
131
  })
132
132
  break
133
+ // No "remain in your wallet" suffix: the funds may already be gone.
134
+ case LiFiErrorCode.CallBundleNotFound:
135
+ title = t('error.title.callBundleNotFound')
136
+ message = t('error.message.callBundleNotFound')
137
+ break
133
138
  default:
134
139
  title = t('error.title.unknown')
135
140
  if (failedAction?.txHash) {
@@ -280,6 +280,54 @@ export const getTokenVerificationProvider = (
280
280
  : undefined
281
281
  }
282
282
 
283
+ export type TokenVerificationBadge =
284
+ | 'native'
285
+ | 'flagged'
286
+ | 'verified'
287
+ | 'unverifiedByProvider'
288
+ | 'unverified'
289
+
290
+ export const getTokenVerificationBadge = (
291
+ token: Pick<
292
+ TokenWithFlags,
293
+ 'native' | 'listed' | 'verified' | 'verificationStatus'
294
+ >
295
+ ): TokenVerificationBadge | undefined => {
296
+ // The provider calls the native-address convention a scam on some chains.
297
+ if (token.native) {
298
+ return 'native'
299
+ }
300
+ if (token.verificationStatus === 'flagged') {
301
+ return 'flagged'
302
+ }
303
+ if (token.verificationStatus === 'verified') {
304
+ return 'verified'
305
+ }
306
+ if (token.listed || token.verified) {
307
+ return undefined
308
+ }
309
+ return token.verificationStatus === 'unverified'
310
+ ? 'unverifiedByProvider'
311
+ : 'unverified'
312
+ }
313
+
314
+ export type TokenStatusTextKey =
315
+ | 'tokenMetric.statusVerified'
316
+ | 'tokenMetric.statusFlagged'
317
+ | 'tokenMetric.statusUnverified'
318
+
319
+ export const getTokenStatusTextKey = (
320
+ token: Pick<TokenWithFlags, 'native' | 'verificationStatus'>
321
+ ): TokenStatusTextKey => {
322
+ if (token.verificationStatus === 'verified') {
323
+ return 'tokenMetric.statusVerified'
324
+ }
325
+ if (token.verificationStatus === 'flagged' && !token.native) {
326
+ return 'tokenMetric.statusFlagged'
327
+ }
328
+ return 'tokenMetric.statusUnverified'
329
+ }
330
+
283
331
  /**
284
332
  * Hyperliquid declares Arbitrum USDC as its native token, Lighter declares
285
333
  * Ethereum USDC. Neither is a gas token you hold there. A chain-local