@lifi/widget 3.17.1 → 3.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/esm/components/Card/CardLabel.d.ts +1 -1
  3. package/dist/esm/components/Card/CardLabel.js +14 -3
  4. package/dist/esm/components/Card/CardLabel.js.map +1 -1
  5. package/dist/esm/components/FeeBreakdownTooltip.d.ts +1 -0
  6. package/dist/esm/components/FeeBreakdownTooltip.js +3 -3
  7. package/dist/esm/components/FeeBreakdownTooltip.js.map +1 -1
  8. package/dist/esm/components/Messages/WarningMessages.d.ts +1 -0
  9. package/dist/esm/components/Messages/WarningMessages.js +4 -4
  10. package/dist/esm/components/Messages/WarningMessages.js.map +1 -1
  11. package/dist/esm/components/Messages/useMessageQueue.d.ts +2 -2
  12. package/dist/esm/components/Messages/useMessageQueue.js +5 -4
  13. package/dist/esm/components/Messages/useMessageQueue.js.map +1 -1
  14. package/dist/esm/components/RouteCard/RouteCard.js +11 -5
  15. package/dist/esm/components/RouteCard/RouteCard.js.map +1 -1
  16. package/dist/esm/components/RouteCard/RouteCardEssentials.js +9 -5
  17. package/dist/esm/components/RouteCard/RouteCardEssentials.js.map +1 -1
  18. package/dist/esm/components/Routes/RoutesExpanded.js +1 -4
  19. package/dist/esm/components/Routes/RoutesExpanded.js.map +1 -1
  20. package/dist/esm/components/Step/StepProcess.js +2 -1
  21. package/dist/esm/components/Step/StepProcess.js.map +1 -1
  22. package/dist/esm/components/StepActions/StepActions.js +20 -15
  23. package/dist/esm/components/StepActions/StepActions.js.map +1 -1
  24. package/dist/esm/components/StepActions/types.d.ts +1 -0
  25. package/dist/esm/components/Timer/RouteTimer.js +11 -2
  26. package/dist/esm/components/Timer/RouteTimer.js.map +1 -1
  27. package/dist/esm/components/Timer/StepTimer.js +11 -2
  28. package/dist/esm/components/Timer/StepTimer.js.map +1 -1
  29. package/dist/esm/components/Token/Token.js +1 -1
  30. package/dist/esm/components/Token/Token.js.map +1 -1
  31. package/dist/esm/components/TransactionDetails.js +20 -14
  32. package/dist/esm/components/TransactionDetails.js.map +1 -1
  33. package/dist/esm/config/version.d.ts +1 -1
  34. package/dist/esm/config/version.js +1 -1
  35. package/dist/esm/hooks/useGasRefuel.js +2 -2
  36. package/dist/esm/hooks/useGasRefuel.js.map +1 -1
  37. package/dist/esm/hooks/useGasSufficiency.js +17 -3
  38. package/dist/esm/hooks/useGasSufficiency.js.map +1 -1
  39. package/dist/esm/hooks/useIsBatchingSupported.d.ts +5 -0
  40. package/dist/esm/hooks/useIsBatchingSupported.js +19 -0
  41. package/dist/esm/hooks/useIsBatchingSupported.js.map +1 -0
  42. package/dist/esm/hooks/useProcessMessage.js +22 -6
  43. package/dist/esm/hooks/useProcessMessage.js.map +1 -1
  44. package/dist/esm/hooks/useRoutes.d.ts +3 -3
  45. package/dist/esm/hooks/useRoutes.js +119 -70
  46. package/dist/esm/hooks/useRoutes.js.map +1 -1
  47. package/dist/esm/i18n/en.json +29 -17
  48. package/dist/esm/pages/MainPage/ReviewButton.js +1 -4
  49. package/dist/esm/pages/MainPage/ReviewButton.js.map +1 -1
  50. package/dist/esm/pages/RoutesPage/RoutesPage.js +1 -4
  51. package/dist/esm/pages/RoutesPage/RoutesPage.js.map +1 -1
  52. package/dist/esm/pages/TransactionPage/StartTransactionButton.js +1 -1
  53. package/dist/esm/pages/TransactionPage/StartTransactionButton.js.map +1 -1
  54. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js +6 -2
  55. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js.map +1 -1
  56. package/dist/esm/pages/TransactionPage/TransactionPage.js +1 -1
  57. package/dist/esm/pages/TransactionPage/TransactionPage.js.map +1 -1
  58. package/dist/esm/providers/WalletProvider/SDKProviders.js +2 -15
  59. package/dist/esm/providers/WalletProvider/SDKProviders.js.map +1 -1
  60. package/dist/esm/types/widget.d.ts +3 -1
  61. package/dist/esm/types/widget.js +1 -0
  62. package/dist/esm/types/widget.js.map +1 -1
  63. package/dist/esm/utils/timer.d.ts +7 -0
  64. package/dist/esm/utils/timer.js +16 -0
  65. package/dist/esm/utils/timer.js.map +1 -0
  66. package/package.json +10 -10
  67. package/package.json.tmp +90 -0
  68. package/src/components/Card/CardLabel.tsx +29 -4
  69. package/src/components/FeeBreakdownTooltip.tsx +5 -2
  70. package/src/components/Messages/WarningMessages.tsx +5 -3
  71. package/src/components/Messages/useMessageQueue.ts +5 -4
  72. package/src/components/RouteCard/RouteCard.tsx +23 -8
  73. package/src/components/RouteCard/RouteCardEssentials.tsx +13 -5
  74. package/src/components/Routes/RoutesExpanded.tsx +1 -5
  75. package/src/components/Step/StepProcess.tsx +2 -1
  76. package/src/components/StepActions/StepActions.tsx +18 -6
  77. package/src/components/StepActions/types.ts +1 -0
  78. package/src/components/Timer/RouteTimer.tsx +13 -2
  79. package/src/components/Timer/StepTimer.tsx +15 -5
  80. package/src/components/Token/Token.tsx +1 -1
  81. package/src/components/TransactionDetails.tsx +51 -23
  82. package/src/config/version.ts +1 -1
  83. package/src/hooks/useGasRefuel.ts +2 -2
  84. package/src/hooks/useGasSufficiency.ts +23 -7
  85. package/src/hooks/useIsBatchingSupported.ts +24 -0
  86. package/src/hooks/useProcessMessage.ts +26 -5
  87. package/src/hooks/useRoutes.ts +148 -78
  88. package/src/i18n/en.json +29 -17
  89. package/src/pages/MainPage/ReviewButton.tsx +1 -6
  90. package/src/pages/RoutesPage/RoutesPage.tsx +2 -5
  91. package/src/pages/TransactionPage/StartTransactionButton.tsx +1 -1
  92. package/src/pages/TransactionPage/TokenValueBottomSheet.tsx +9 -2
  93. package/src/pages/TransactionPage/TransactionPage.tsx +1 -1
  94. package/src/providers/WalletProvider/SDKProviders.tsx +2 -16
  95. package/src/types/widget.ts +2 -0
  96. package/src/utils/timer.ts +28 -0
  97. package/dist/esm/components/Messages/GasMessage.d.ts +0 -7
  98. package/dist/esm/components/Messages/GasMessage.js +0 -13
  99. package/dist/esm/components/Messages/GasMessage.js.map +0 -1
  100. package/dist/esm/providers/WalletProvider/getSafeMultisigConfig.d.ts +0 -8
  101. package/dist/esm/providers/WalletProvider/getSafeMultisigConfig.js +0 -95
  102. package/dist/esm/providers/WalletProvider/getSafeMultisigConfig.js.map +0 -1
  103. package/src/components/Messages/GasMessage.tsx +0 -35
  104. package/src/providers/WalletProvider/getSafeMultisigConfig.ts +0 -144
@@ -1,5 +1,13 @@
1
- import type { Route, RoutesResponse, Token } from '@lifi/sdk'
2
- import { LiFiErrorCode, getContractCallsQuote, getRoutes } from '@lifi/sdk'
1
+ import type { Route, Token } from '@lifi/sdk'
2
+ import {
3
+ ChainType,
4
+ LiFiErrorCode,
5
+ convertQuoteToRoute,
6
+ getContractCallsQuote,
7
+ getRelayerQuote,
8
+ getRoutes,
9
+ isRelayerStep,
10
+ } from '@lifi/sdk'
3
11
  import { useAccount } from '@lifi/wallet-management'
4
12
  import { useQuery, useQueryClient } from '@tanstack/react-query'
5
13
  import { parseUnits } from 'viem'
@@ -13,7 +21,7 @@ import { getChainTypeFromAddress } from '../utils/chainType.js'
13
21
  import { useChain } from './useChain.js'
14
22
  import { useDebouncedWatch } from './useDebouncedWatch.js'
15
23
  import { useGasRefuel } from './useGasRefuel.js'
16
- import { useIsCompatibleDestinationAccount } from './useIsCompatibleDestinationAccount.js'
24
+ import { useIsBatchingSupported } from './useIsBatchingSupported.js'
17
25
  import { useSwapOnly } from './useSwapOnly.js'
18
26
  import { useToken } from './useToken.js'
19
27
  import { useWidgetEvents } from './useWidgetEvents.js'
@@ -33,6 +41,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
33
41
  exchanges,
34
42
  fee,
35
43
  feeConfig,
44
+ useRelayerRoutes,
36
45
  } = useWidgetConfig()
37
46
  const setExecutableRoute = useSetExecutableRoute()
38
47
  const queryClient = useQueryClient()
@@ -77,12 +86,12 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
77
86
  const { token: toToken } = useToken(toChainId, toTokenAddress)
78
87
  const { chain: fromChain } = useChain(fromChainId)
79
88
  const { chain: toChain } = useChain(toChainId)
80
- const { isCompatibleDestinationAccount } =
81
- useIsCompatibleDestinationAccount(observableRoute)
82
89
  const { enabled: enabledRefuel, fromAmount: gasRecommendationFromAmount } =
83
90
  useGasRefuel()
84
91
 
85
92
  const { account } = useAccount({ chainType: fromChain?.chainType })
93
+ const { isBatchingSupported, isBatchingSupportedLoading } =
94
+ useIsBatchingSupported(fromChain, account.address)
86
95
 
87
96
  const hasAmount = Number(fromTokenAmount) > 0 || Number(toTokenAmount) > 0
88
97
 
@@ -110,29 +119,28 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
110
119
  exchanges?.allow?.length || exchanges?.deny?.length
111
120
  ? enabledExchanges
112
121
  : undefined
113
- const allowSwitchChain = isCompatibleDestinationAccount
114
- ? sdkConfig?.routeOptions?.allowSwitchChain
115
- : false
122
+ const allowSwitchChain = sdkConfig?.routeOptions?.allowSwitchChain
116
123
 
117
124
  const isEnabled =
118
- Boolean(Number(fromChainId)) &&
119
- Boolean(Number(toChainId)) &&
125
+ Boolean(Number(fromChain?.id)) &&
126
+ Boolean(Number(toChain?.id)) &&
120
127
  Boolean(fromToken?.address) &&
121
128
  Boolean(toToken?.address) &&
122
129
  !Number.isNaN(slippage) &&
123
130
  hasAmount &&
124
131
  isToAddressSatisfied &&
125
- contractCallQuoteEnabled
132
+ contractCallQuoteEnabled &&
133
+ !isBatchingSupportedLoading
126
134
 
127
135
  // Some values should be strictly typed and isEnabled ensures that
128
136
  const queryKey = [
129
137
  'routes',
130
138
  account.address,
131
- fromChainId as number,
139
+ fromChain?.id as number,
132
140
  fromToken?.address as string,
133
141
  fromTokenAmount,
134
142
  toWalletAddress,
135
- toChainId as number,
143
+ toChain?.id as number,
136
144
  toToken?.address as string,
137
145
  toTokenAmount,
138
146
  contractCalls,
@@ -148,6 +156,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
148
156
  enabledRefuel && enabledAutoRefuel,
149
157
  gasRecommendationFromAmount,
150
158
  feeConfig?.fee || fee,
159
+ !!isBatchingSupported,
151
160
  observableRoute?.id,
152
161
  ] as const
153
162
 
@@ -178,6 +187,8 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
178
187
  enabledRefuel,
179
188
  gasRecommendationFromAmount,
180
189
  fee,
190
+ isBatchingSupported,
191
+ // _observableRouteId must be the last element in the query key
181
192
  _observableRouteId,
182
193
  ],
183
194
  signal,
@@ -266,27 +277,9 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
266
277
  contractCallQuote.toolDetails = toolDetails
267
278
  }
268
279
 
269
- const route: Route = {
270
- id: crypto.randomUUID(),
271
- fromChainId: contractCallQuote.action.fromChainId,
272
- fromAmountUSD: contractCallQuote.estimate.fromAmountUSD || '',
273
- fromAmount: contractCallQuote.action.fromAmount,
274
- fromToken: contractCallQuote.action.fromToken,
275
- fromAddress: contractCallQuote.action.fromAddress,
276
- toChainId: contractCallQuote.action.toChainId,
277
- toAmountUSD: contractCallQuote.estimate.toAmountUSD || '',
278
- toAmount: contractCallQuote.estimate.toAmount,
279
- toAmountMin: contractCallQuote.estimate.toAmountMin,
280
- toToken: toToken!,
281
- toAddress:
282
- contractCallQuote.action.toAddress ||
283
- contractCallQuote.action.fromAddress,
284
- gasCostUSD: contractCallQuote.estimate.gasCosts?.[0].amountUSD,
285
- steps: [contractCallQuote],
286
- insurance: { state: 'NOT_INSURABLE', feeAmountUsd: '0' },
287
- }
280
+ const route: Route = convertQuoteToRoute(contractCallQuote)
288
281
 
289
- return { routes: [route] } as RoutesResponse
282
+ return [route]
290
283
  }
291
284
 
292
285
  // Prevent sending a request for the same chain token combinations.
@@ -294,50 +287,116 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
294
287
  return
295
288
  }
296
289
 
297
- const data = await getRoutes(
298
- {
299
- fromAddress,
300
- fromAmount: fromAmount.toString(),
301
- fromChainId,
302
- fromTokenAddress,
303
- toAddress,
304
- toChainId,
305
- toTokenAddress,
306
- fromAmountForGas:
307
- enabledRefuel && gasRecommendationFromAmount
308
- ? gasRecommendationFromAmount
309
- : undefined,
310
- options: {
311
- allowSwitchChain:
312
- subvariant === 'refuel' ? false : allowSwitchChain,
313
- bridges:
314
- allowBridges?.length || disabledBridges.length
315
- ? {
316
- allow: allowBridges,
317
- deny: disabledBridges.length
318
- ? disabledBridges
290
+ const isObservableRelayerRoute =
291
+ observableRoute?.steps?.some(isRelayerStep)
292
+
293
+ const shouldUseMainRoutes =
294
+ !observableRoute || !isObservableRelayerRoute
295
+ const shouldUseRelayerQuote =
296
+ fromAddress &&
297
+ fromChain?.chainType === ChainType.EVM &&
298
+ fromChain.permit2 &&
299
+ fromChain.permit2Proxy &&
300
+ fromChain.relayerSupported &&
301
+ fromChain.nativeToken.address !== fromTokenAddress &&
302
+ useRelayerRoutes &&
303
+ !isBatchingSupported &&
304
+ (!observableRoute || isObservableRelayerRoute)
305
+
306
+ const [routesResult, relayerRouteResult] = await Promise.all([
307
+ shouldUseMainRoutes
308
+ ? getRoutes(
309
+ {
310
+ fromAddress,
311
+ fromAmount: fromAmount.toString(),
312
+ fromChainId,
313
+ fromTokenAddress,
314
+ toAddress,
315
+ toChainId,
316
+ toTokenAddress,
317
+ fromAmountForGas:
318
+ enabledRefuel && gasRecommendationFromAmount
319
+ ? gasRecommendationFromAmount
320
+ : undefined,
321
+ options: {
322
+ allowSwitchChain:
323
+ subvariant === 'refuel' ? false : allowSwitchChain,
324
+ bridges:
325
+ allowBridges?.length || disabledBridges.length
326
+ ? {
327
+ allow: allowBridges,
328
+ deny: disabledBridges.length
329
+ ? disabledBridges
330
+ : undefined,
331
+ }
319
332
  : undefined,
320
- }
321
- : undefined,
322
- exchanges:
323
- allowExchanges?.length || disabledExchanges.length
324
- ? {
325
- allow: allowExchanges,
326
- deny: disabledExchanges.length
327
- ? disabledExchanges
333
+ exchanges:
334
+ allowExchanges?.length || disabledExchanges.length
335
+ ? {
336
+ allow: allowExchanges,
337
+ deny: disabledExchanges.length
338
+ ? disabledExchanges
339
+ : undefined,
340
+ }
328
341
  : undefined,
329
- }
330
- : undefined,
331
- order: routePriority,
332
- slippage: formattedSlippage,
333
- fee: calculatedFee || fee,
334
- },
335
- },
336
- { signal }
337
- )
338
- if (data.routes[0] && fromAddress) {
342
+ order: routePriority,
343
+ slippage: formattedSlippage,
344
+ fee: calculatedFee || fee,
345
+ },
346
+ },
347
+ { signal }
348
+ )
349
+ : Promise.resolve(null),
350
+ shouldUseRelayerQuote
351
+ ? getRelayerQuote(
352
+ {
353
+ fromAddress,
354
+ fromAmount: fromAmount.toString(),
355
+ fromChain: fromChainId,
356
+ fromToken: fromTokenAddress,
357
+ toAddress,
358
+ toChain: toChainId,
359
+ toToken: toTokenAddress,
360
+ fromAmountForGas:
361
+ enabledRefuel && gasRecommendationFromAmount
362
+ ? gasRecommendationFromAmount
363
+ : undefined,
364
+ order: routePriority,
365
+ slippage: formattedSlippage,
366
+ fee: calculatedFee || fee,
367
+ ...(allowBridges?.length || disabledBridges.length
368
+ ? {
369
+ allowBridges: allowBridges,
370
+ denyBridges: disabledBridges.length
371
+ ? disabledBridges
372
+ : undefined,
373
+ }
374
+ : undefined),
375
+ ...(allowExchanges?.length || disabledExchanges.length
376
+ ? {
377
+ allowExchanges: allowExchanges,
378
+ denyExchanges: disabledExchanges.length
379
+ ? disabledExchanges
380
+ : undefined,
381
+ }
382
+ : undefined),
383
+ },
384
+ { signal }
385
+ )
386
+ .then((response) => {
387
+ const quote = {
388
+ ...response.quote,
389
+ permits: response.permits,
390
+ }
391
+ return convertQuoteToRoute(quote)
392
+ })
393
+ .catch(() => null)
394
+ : Promise.resolve(null),
395
+ ])
396
+
397
+ if (routesResult?.routes[0] && fromAddress) {
339
398
  // Update local tokens cache to keep priceUSD in sync
340
- const { fromToken, toToken } = data.routes[0]
399
+ const { fromToken, toToken } = routesResult.routes[0]
341
400
  ;[fromToken, toToken].forEach((token) => {
342
401
  queryClient.setQueriesData<Token[]>(
343
402
  { queryKey: ['token-balances', fromAddress, token.chainId] },
@@ -357,8 +416,16 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
357
416
  )
358
417
  })
359
418
  }
360
- emitter.emit(WidgetEvent.AvailableRoutes, data.routes)
361
- return data
419
+
420
+ const routes = routesResult?.routes ?? []
421
+
422
+ // Add relayer route if available
423
+ if (relayerRouteResult) {
424
+ routes.splice(1, 0, relayerRouteResult)
425
+ }
426
+
427
+ emitter.emit(WidgetEvent.AvailableRoutes, routes)
428
+ return routes
362
429
  },
363
430
  enabled: isEnabled,
364
431
  staleTime: refetchTime,
@@ -369,7 +436,10 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
369
436
  )
370
437
  },
371
438
  retry(failureCount, error: any) {
372
- if (failureCount >= 5) {
439
+ if (process.env.NODE_ENV === 'development') {
440
+ console.warn('Route query failed:', { failureCount, error })
441
+ }
442
+ if (failureCount >= 3) {
373
443
  return false
374
444
  }
375
445
  if (error?.code === LiFiErrorCode.NotFound) {
@@ -390,7 +460,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
390
460
  }
391
461
 
392
462
  return {
393
- routes: data?.routes,
463
+ routes: data,
394
464
  isLoading: isEnabled && isLoading,
395
465
  isFetching,
396
466
  isFetched,
package/src/i18n/en.json CHANGED
@@ -102,7 +102,7 @@
102
102
  "success": {
103
103
  "message": {
104
104
  "exchangePartiallySuccessful": "We've tried to complete the transaction, but {{tool}} couldn't proceed due to either slippage settings or lack of liquidity for the {{tokenSymbol}} token.",
105
- "exchangeSuccessful": "There are now {{amount, number(maximumFractionDigits: 9)}} {{tokenSymbol}} in {{walletAddress}} wallet on {{chainName}} chain.",
105
+ "exchangeSuccessful": "There are now {{amount, numberExt}} {{tokenSymbol}} in {{walletAddress}} wallet on {{chainName}} chain.",
106
106
  "checkoutSuccessful": "You now own {{assetName}} in {{walletAddress}} wallet on {{chainName}} chain."
107
107
  },
108
108
  "title": {
@@ -117,8 +117,7 @@
117
117
  },
118
118
  "warning": {
119
119
  "message": {
120
- "accountNotDeployedMessage": "Smart contract account is not deployed on the destination chain. Sending funds to a non-existent contract would result in permanent loss.",
121
- "noAddressActivity": "This address has never been used on this network. Please verify you're sending to the correct address to prevent potential loss of funds.",
120
+ "accountNotDeployedMessage": "Smart contract account doesn't exist on the destination chain. Sending funds to a non-existent smart contract account can result in permanent loss.",
122
121
  "lowAddressActivity": "This address has low activity on {{chainName}} network. Please verify you're sending to the correct address and network to prevent potential loss of funds.",
123
122
  "deleteActiveTransactions": "Active transactions are only stored locally and can't be recovered if you delete them.",
124
123
  "deleteTransactionHistory": "Transaction history is only stored locally and can't be recovered if you delete it.",
@@ -146,12 +145,13 @@
146
145
  "message": {
147
146
  "allowanceRequired": "Transfer amount for {{tokenSymbol}} exceeds your current allowance. Please increase your allowance and try again.",
148
147
  "insufficientFunds": "You don't have enough gas to cover the cost of the transaction.",
149
- "remainInYourWallet": "{{amount, number(maximumFractionDigits: 9)}} {{tokenSymbol}} on {{chainName}} remain in your wallet.",
150
- "signatureRejected": "Your signature is required to complete the transaction. {{amount, number(maximumFractionDigits: 9)}} {{tokenSymbol}} on {{chainName}} remain in your wallet.",
148
+ "remainInYourWallet": "{{amount, numberExt}} {{tokenSymbol}} on {{chainName}} remain in your wallet.",
149
+ "signatureRejected": "Your signature is required to complete the transaction. {{amount, numberExt}} {{tokenSymbol}} on {{chainName}} remain in your wallet.",
151
150
  "slippageThreshold": "The slippage is larger than the defined threshold. Please request a new quote.",
152
151
  "transactionCanceled": "Transaction was canceled.",
153
152
  "transactionConflict": "Transaction couldn't be processed because it's conflicting with another pending transaction using the same funds. Please wait for the pending transaction to be confirmed or check your transaction history before trying again.",
154
153
  "transactionExpired": "The block height has exceeded the maximum allowed limit or blockhash is no longer recent enough.",
154
+ "transactionRejected": "The transaction was rejected due to an internal wallet error. Please try again or request a new quote.",
155
155
  "transactionFailed": "Please check the block explorer for more information.",
156
156
  "transactionNotSent": "Transaction was not sent.",
157
157
  "transactionSimulationFailed": "The transaction could not be executed in simulation.",
@@ -173,6 +173,7 @@
173
173
  "transactionConflict": "Transaction conflict",
174
174
  "transactionExpired": "Transaction expired",
175
175
  "transactionFailed": "Transaction failed",
176
+ "transactionRejected": "Transaction rejected",
176
177
  "transactionSimulationFailed": "Transaction simulation failed",
177
178
  "transactionUnderpriced": "Transaction is underpriced",
178
179
  "transactionUnprepared": "Unable to prepare transaction",
@@ -186,13 +187,14 @@
186
187
  },
187
188
  "tooltip": {
188
189
  "deselectAll": "Deselect all",
189
- "estimatedTime": "Estimated time to complete the swap or bridge transaction, excluding chain switching and token approval.",
190
+ "estimatedTime": "Time to complete the swap or bridge transaction, excluding chain switching and token approval.",
190
191
  "feeCollection": "The fee is applied to selected token pairs and ensures the best experience with {{tool}}.",
191
192
  "minReceived": "The estimated minimum amount may change until the swapping/bridging transaction is signed. For 2-step transfers, this applies until the second step transaction is signed.",
192
193
  "notFound": {
193
194
  "text": "We couldn't find this page.",
194
195
  "title": "404"
195
196
  },
197
+ "relayerService": "We handle the gas, so you can transfer assets without holding native tokens. Network costs are included in the transfer.",
196
198
  "numberOfSteps": "Each exchange step can contain 1-2 transactions that require a signature.",
197
199
  "priceImpact": "The estimated value difference between the source and destination tokens.",
198
200
  "progressToNextUpdate": "Quotes will update in {{value}} seconds. <0/> Click here to update now.",
@@ -209,7 +211,9 @@
209
211
  "featuredTokens": "Featured tokens",
210
212
  "fees": {
211
213
  "defaultIntegrator": "Integrator fee",
214
+ "relayerService": "Gasless service",
212
215
  "estimated": "estimated costs",
216
+ "free": "Free",
213
217
  "integrator": "{{tool}} fee",
214
218
  "network": "Network cost",
215
219
  "paid": "paid costs",
@@ -226,9 +230,9 @@
226
230
  "priceImpact": "Price impact",
227
231
  "process": {
228
232
  "bridge": {
229
- "actionRequired": "Please sign the transaction",
233
+ "actionRequired": "Sign bridge transaction",
230
234
  "done": "Bridge transaction confirmed",
231
- "pending": "Waiting for bridge transaction",
235
+ "pending": "Bridge transaction pending",
232
236
  "started": "Preparing bridge transaction"
233
237
  },
234
238
  "checkout": {
@@ -244,19 +248,26 @@
244
248
  "refunded": "Bridge transaction refunded"
245
249
  },
246
250
  "swap": {
247
- "actionRequired": "Please sign the transaction",
251
+ "actionRequired": "Sign swap transaction",
248
252
  "done": "Swap completed",
249
- "pending": "Waiting for swap transaction",
253
+ "pending": "Swap transaction pending",
250
254
  "started": "Preparing swap transaction"
251
255
  },
252
256
  "switchChain": {
253
- "actionRequired": "Chain switch required",
254
- "done": "Chain switched successfully"
257
+ "actionRequired": "Confirm chain switch",
258
+ "done": "Chain switched"
259
+ },
260
+ "permit": {
261
+ "actionRequired": "Sign permit message",
262
+ "done": "Permit message signed",
263
+ "pending": "Waiting for permit message",
264
+ "started": "Preparing transaction"
255
265
  },
256
266
  "tokenAllowance": {
257
- "done": "Token allowance approved",
258
- "pending": "Waiting for token allowance",
259
- "started": "Setting token allowance"
267
+ "actionRequired": "Approve {{tokenSymbol}} spending",
268
+ "done": "{{tokenSymbol}} spending approved",
269
+ "pending": "{{tokenSymbol}} approval pending",
270
+ "started": "Preparing approval transaction"
260
271
  }
261
272
  },
262
273
  "quotedAmount": "Quoted amount",
@@ -284,11 +295,12 @@
284
295
  "transferId": "Transfer ID",
285
296
  "tags": {
286
297
  "cheapest": "Best Return",
287
- "fastest": "Fastest"
298
+ "fastest": "Fastest",
299
+ "gasless": "Gasless"
288
300
  },
289
301
  "to": "To",
290
302
  "tokenOnChain": "{{tokenSymbol}} on {{chainName}}",
291
- "tokenOnChainAmount": "{{amount, number(maximumFractionDigits: 9)}} {{tokenSymbol}} on {{chainName}}",
303
+ "tokenOnChainAmount": "{{amount, numberExt}} {{tokenSymbol}} on {{chainName}}",
292
304
  "tokenSearch": "Search by token name or address",
293
305
  "valueLoss": "Value loss",
294
306
  "searchChains": "Search by chain name",
@@ -1,7 +1,6 @@
1
1
  import { useTranslation } from 'react-i18next'
2
2
  import { useNavigate } from 'react-router-dom'
3
3
  import { BaseTransactionButton } from '../../components/BaseTransactionButton/BaseTransactionButton.js'
4
- import { useIsCompatibleDestinationAccount } from '../../hooks/useIsCompatibleDestinationAccount.js'
5
4
  import { useRoutes } from '../../hooks/useRoutes.js'
6
5
  import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'
7
6
  import { useWidgetEvents } from '../../hooks/useWidgetEvents.js'
@@ -17,7 +16,6 @@ export const ReviewButton: React.FC = () => {
17
16
  const { subvariant, subvariantOptions } = useWidgetConfig()
18
17
  const splitState = useSplitSubvariantStore((state) => state.state)
19
18
  const { toAddress, requiredToAddress } = useToAddressRequirements()
20
- const { isCompatibleDestinationAccount } = useIsCompatibleDestinationAccount()
21
19
  const { routes, setReviewableRoute } = useRoutes()
22
20
 
23
21
  const currentRoute = routes?.[0]
@@ -74,10 +72,7 @@ export const ReviewButton: React.FC = () => {
74
72
  <BaseTransactionButton
75
73
  text={getButtonText()}
76
74
  onClick={handleClick}
77
- disabled={
78
- (currentRoute && requiredToAddress && !toAddress) ||
79
- !isCompatibleDestinationAccount
80
- }
75
+ disabled={currentRoute && requiredToAddress && !toAddress}
81
76
  />
82
77
  )
83
78
  }
@@ -8,7 +8,6 @@ import { RouteCard } from '../../components/RouteCard/RouteCard.js'
8
8
  import { RouteCardSkeleton } from '../../components/RouteCard/RouteCardSkeleton.js'
9
9
  import { RouteNotFoundCard } from '../../components/RouteCard/RouteNotFoundCard.js'
10
10
  import { useHeader } from '../../hooks/useHeader.js'
11
- import { useIsCompatibleDestinationAccount } from '../../hooks/useIsCompatibleDestinationAccount.js'
12
11
  import { useNavigateBack } from '../../hooks/useNavigateBack.js'
13
12
  import { useRoutes } from '../../hooks/useRoutes.js'
14
13
  import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'
@@ -35,7 +34,6 @@ export const RoutesPage: React.FC<BoxProps> = () => {
35
34
  const { account } = useAccount({ chainType: fromChain?.chainType })
36
35
  const [toAddress] = useFieldValues('toAddress')
37
36
  const { requiredToAddress } = useToAddressRequirements()
38
- const { isCompatibleDestinationAccount } = useIsCompatibleDestinationAccount()
39
37
 
40
38
  const headerAction = useMemo(
41
39
  () => (
@@ -66,9 +64,8 @@ export const RoutesPage: React.FC<BoxProps> = () => {
66
64
 
67
65
  const routeNotFound = !routes?.length && !isLoading && !isFetching
68
66
 
69
- const toAddressUnsatisfied =
70
- (routes?.[0] && requiredToAddress && !toAddress) ||
71
- !isCompatibleDestinationAccount
67
+ const toAddressUnsatisfied = routes?.[0] && requiredToAddress && !toAddress
68
+
72
69
  const allowInteraction = account.isConnected && !toAddressUnsatisfied
73
70
 
74
71
  return (
@@ -8,7 +8,7 @@ export const StartTransactionButton: React.FC<StartTransactionButtonProps> = ({
8
8
  text,
9
9
  loading,
10
10
  }) => {
11
- const { hasMessages, isLoading } = useMessageQueue(route)
11
+ const { hasMessages, isLoading } = useMessageQueue(route, true)
12
12
 
13
13
  return (
14
14
  <BaseTransactionButton
@@ -1,4 +1,5 @@
1
1
  import type { Route } from '@lifi/sdk'
2
+ import { isRelayerStep } from '@lifi/sdk'
2
3
  import { WarningRounded } from '@mui/icons-material'
3
4
  import { Box, Button, Typography } from '@mui/material'
4
5
  import type { MutableRefObject } from 'react'
@@ -50,6 +51,7 @@ const TokenValueBottomSheetContent: React.FC<TokenValueBottomSheetProps> = ({
50
51
  getAccumulatedFeeCostsBreakdown(route)
51
52
  const fromAmountUSD = Number.parseFloat(route.fromAmountUSD)
52
53
  const toAmountUSD = Number.parseFloat(route.toAmountUSD)
54
+ const hasRelayerSupport = route.steps.some(isRelayerStep)
53
55
  return (
54
56
  <Box
55
57
  ref={ref}
@@ -102,13 +104,18 @@ const TokenValueBottomSheetContent: React.FC<TokenValueBottomSheetProps> = ({
102
104
  }}
103
105
  >
104
106
  <Typography>{t('main.fees.network')}</Typography>
105
- <FeeBreakdownTooltip gasCosts={gasCosts}>
107
+ <FeeBreakdownTooltip
108
+ gasCosts={gasCosts}
109
+ relayerSupport={hasRelayerSupport}
110
+ >
106
111
  <Typography
107
112
  sx={{
108
113
  fontWeight: 600,
109
114
  }}
110
115
  >
111
- {t('format.currency', { value: gasCostUSD })}
116
+ {hasRelayerSupport
117
+ ? t('main.fees.free')
118
+ : t('format.currency', { value: gasCostUSD })}
112
119
  </Typography>
113
120
  </FeeBreakdownTooltip>
114
121
  </Box>
@@ -207,7 +207,7 @@ export const TransactionPage: React.FC = () => {
207
207
  {status === RouteExecutionStatus.Idle ||
208
208
  status === RouteExecutionStatus.Failed ? (
209
209
  <>
210
- <WarningMessages mt={2} route={route} />
210
+ <WarningMessages mt={2} route={route} allowInteraction />
211
211
  <Box
212
212
  sx={{
213
213
  mt: 2,
@@ -5,20 +5,18 @@ import { ChainType, EVM, Solana, UTXO, config } from '@lifi/sdk'
5
5
  import type { SignerWalletAdapter } from '@solana/wallet-adapter-base'
6
6
  import { useWallet } from '@solana/wallet-adapter-react'
7
7
  import { useEffect } from 'react'
8
- import { useAccount, useConfig as useWagmiConfig } from 'wagmi'
8
+ import { useConfig as useWagmiConfig } from 'wagmi'
9
9
  import {
10
10
  getConnectorClient as getWagmiConnectorClient,
11
11
  switchChain,
12
12
  } from 'wagmi/actions'
13
13
  import { useWidgetConfig } from '../WidgetProvider/WidgetProvider.js'
14
- import { getSafeMultisigConfig } from './getSafeMultisigConfig.js'
15
14
 
16
15
  export const SDKProviders = () => {
17
16
  const { sdkConfig } = useWidgetConfig()
18
17
  const { wallet } = useWallet()
19
18
  const wagmiConfig = useWagmiConfig()
20
19
  const bigmiConfig = useBigmiConfig()
21
- const account = useAccount({ config: wagmiConfig })
22
20
 
23
21
  useEffect(() => {
24
22
  // Configure SDK Providers
@@ -33,11 +31,6 @@ export const SDKProviders = () => {
33
31
  (provider) => provider.type === ChainType.UTXO
34
32
  )
35
33
  if (!hasConfiguredEVMProvider) {
36
- // TODO: refactor this in favor of EIP-5792: Wallet Call API
37
- const multisig =
38
- account.connector?.id === 'safe'
39
- ? getSafeMultisigConfig(account.connector)
40
- : undefined
41
34
  providers.push(
42
35
  EVM({
43
36
  getWalletClient: () => getWagmiConnectorClient(wagmiConfig),
@@ -45,7 +38,6 @@ export const SDKProviders = () => {
45
38
  const chain = await switchChain(wagmiConfig, { chainId })
46
39
  return getWagmiConnectorClient(wagmiConfig, { chainId: chain.id })
47
40
  },
48
- multisig,
49
41
  })
50
42
  )
51
43
  }
@@ -69,13 +61,7 @@ export const SDKProviders = () => {
69
61
  providers.push(...sdkConfig.providers)
70
62
  }
71
63
  config.setProviders(providers)
72
- }, [
73
- account.connector,
74
- bigmiConfig,
75
- sdkConfig?.providers,
76
- wagmiConfig,
77
- wallet?.adapter,
78
- ])
64
+ }, [bigmiConfig, sdkConfig?.providers, wagmiConfig, wallet?.adapter])
79
65
 
80
66
  return null
81
67
  }
@@ -95,6 +95,7 @@ export enum HiddenUI {
95
95
  WalletMenu = 'walletMenu',
96
96
  IntegratorStepDetails = 'integratorStepDetails',
97
97
  ReverseTokensButton = 'reverseTokensButton',
98
+ RouteTokenDescription = 'routeTokenDescription',
98
99
  }
99
100
  export type HiddenUIType = `${HiddenUI}`
100
101
 
@@ -245,6 +246,7 @@ export interface WidgetConfig {
245
246
  hiddenUI?: HiddenUIType[]
246
247
  requiredUI?: RequiredUIType[]
247
248
  useRecommendedRoute?: boolean
249
+ useRelayerRoutes?: boolean
248
250
 
249
251
  walletConfig?: WidgetWalletConfig
250
252
  sdkConfig?: WidgetSDKConfig