0xtrails 0.2.1 → 0.2.4

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 (87) hide show
  1. package/dist/aave.d.ts.map +1 -1
  2. package/dist/{ccip-BbfANth7.js → ccip-BlV1Mry3.js} +1 -1
  3. package/dist/chains.d.ts +5 -1
  4. package/dist/chains.d.ts.map +1 -1
  5. package/dist/constants.d.ts +4 -4
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/{index-WpIVoh3X.js → index-BNWCIGfQ.js} +49015 -46131
  8. package/dist/index.d.ts +4 -3
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2 -2
  11. package/dist/intentEntrypoint.d.ts +0 -8
  12. package/dist/intentEntrypoint.d.ts.map +1 -1
  13. package/dist/metaTxnMonitor.d.ts +5 -4
  14. package/dist/metaTxnMonitor.d.ts.map +1 -1
  15. package/dist/metaTxns.d.ts +3 -3
  16. package/dist/metaTxns.d.ts.map +1 -1
  17. package/dist/prepareSend.d.ts +3 -3
  18. package/dist/prepareSend.d.ts.map +1 -1
  19. package/dist/relayer.d.ts +10 -7
  20. package/dist/relayer.d.ts.map +1 -1
  21. package/dist/sequenceWallet.d.ts +3 -2
  22. package/dist/sequenceWallet.d.ts.map +1 -1
  23. package/dist/tokenBalances.d.ts +7 -0
  24. package/dist/tokenBalances.d.ts.map +1 -1
  25. package/dist/tokens.d.ts +2 -1
  26. package/dist/tokens.d.ts.map +1 -1
  27. package/dist/trails.d.ts +2 -2
  28. package/dist/trails.d.ts.map +1 -1
  29. package/dist/widget/components/AccountActionsDropdown.d.ts.map +1 -1
  30. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  31. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  32. package/dist/widget/components/EarnPools.d.ts.map +1 -1
  33. package/dist/widget/components/Fund.d.ts +1 -0
  34. package/dist/widget/components/Fund.d.ts.map +1 -1
  35. package/dist/widget/components/Pay.d.ts +1 -0
  36. package/dist/widget/components/Pay.d.ts.map +1 -1
  37. package/dist/widget/components/Recipients.d.ts.map +1 -1
  38. package/dist/widget/components/RefundWarning.d.ts +1 -0
  39. package/dist/widget/components/RefundWarning.d.ts.map +1 -1
  40. package/dist/widget/hooks/useBack.d.ts +5 -0
  41. package/dist/widget/hooks/useBack.d.ts.map +1 -1
  42. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  43. package/dist/widget/hooks/useInitialRedirect.d.ts +7 -0
  44. package/dist/widget/hooks/useInitialRedirect.d.ts.map +1 -0
  45. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +1 -1
  46. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  47. package/dist/widget/index.js +1 -1
  48. package/dist/widget/widget.d.ts.map +1 -1
  49. package/package.json +18 -17
  50. package/src/aave.ts +90 -74
  51. package/src/chains.ts +23 -3
  52. package/src/constants.ts +10 -17
  53. package/src/error.ts +1 -1
  54. package/src/index.ts +8 -3
  55. package/src/intentEntrypoint.ts +0 -15
  56. package/src/metaTxnMonitor.ts +28 -22
  57. package/src/metaTxns.ts +5 -3
  58. package/src/prepareSend.ts +217 -286
  59. package/src/relayer.ts +15 -16
  60. package/src/sequenceWallet.ts +7 -3
  61. package/src/tokenBalances.ts +55 -1
  62. package/src/tokens.ts +10 -0
  63. package/src/trails.ts +2 -2
  64. package/src/widget/compiled.css +1 -1
  65. package/src/widget/components/AccountActionsDropdown.tsx +6 -2
  66. package/src/widget/components/AccountIntentTransactionHistory.tsx +1 -1
  67. package/src/widget/components/AccountSettings.tsx +5 -4
  68. package/src/widget/components/ChainFilterDropdown.tsx +1 -1
  69. package/src/widget/components/ChainList.tsx +1 -1
  70. package/src/widget/components/ConnectWallet.tsx +6 -2
  71. package/src/widget/components/EarnPools.tsx +2 -1
  72. package/src/widget/components/Fund.tsx +50 -27
  73. package/src/widget/components/Pay.tsx +24 -1
  74. package/src/widget/components/Receive.tsx +1 -1
  75. package/src/widget/components/Recipients.tsx +4 -2
  76. package/src/widget/components/RefundWarning.tsx +5 -1
  77. package/src/widget/components/SwapSettings.tsx +9 -9
  78. package/src/widget/components/TokenSelector.tsx +1 -1
  79. package/src/widget/components/WalletList.tsx +3 -3
  80. package/src/widget/hooks/useBack.tsx +111 -9
  81. package/src/widget/hooks/useDefaultTokenSelection.tsx +5 -1
  82. package/src/widget/hooks/useInitialRedirect.tsx +70 -0
  83. package/src/widget/hooks/useSelectedFeeToken.tsx +10 -16
  84. package/src/widget/hooks/useSendForm.ts +10 -10
  85. package/src/widget/hooks/useTokenList.ts +11 -2
  86. package/src/widget/widget.tsx +85 -106
  87. /package/dist/{style.css → 0xtrails.css} +0 -0
@@ -1,6 +1,6 @@
1
1
  import type { TokenPrice } from "@0xsequence/trails-api"
2
2
  import type React from "react"
3
- import { useCallback, useEffect, useMemo, useState } from "react"
3
+ import { useCallback, useEffect, useMemo, useState, useRef } from "react"
4
4
  import {
5
5
  type Account,
6
6
  getAddress,
@@ -441,6 +441,12 @@ export function useSendForm({
441
441
  allSupportedTokens: true, // Get all tokens for balance checking
442
442
  })
443
443
 
444
+ // Use ref to store latest filteredTokensFormatted without triggering re-renders
445
+ const filteredTokensFormattedRef = useRef(filteredTokensFormatted)
446
+ useEffect(() => {
447
+ filteredTokensFormattedRef.current = filteredTokensFormatted
448
+ }, [filteredTokensFormatted])
449
+
444
450
  const destTokenAddress = useTokenAddress({
445
451
  chainId: selectedDestinationChain?.id,
446
452
  tokenSymbol: selectedDestToken?.symbol,
@@ -945,7 +951,7 @@ export function useSendForm({
945
951
  return formatAmountDisplay(quotedDestinationAmount || "0")
946
952
  }, [quotedDestinationAmount])
947
953
 
948
- // Set raw fee options in the hook whenever prepareSendResult or tokens change
954
+ // Set raw fee options in the hook whenever prepareSendResult changes
949
955
  useEffect(() => {
950
956
  const apiFeeOptions = prepareSendResult?.feeOptions?.feeOptions ?? []
951
957
  logger.console.log(
@@ -961,15 +967,9 @@ export function useSendForm({
961
967
  setRawFeeOptions(
962
968
  apiFeeOptions,
963
969
  selectedToken?.chainId,
964
- filteredTokensFormatted,
970
+ filteredTokensFormattedRef.current,
965
971
  )
966
- // eslint-disable-next-line react-hooks/exhaustive-deps
967
- }, [
968
- prepareSendResult,
969
- selectedToken?.chainId,
970
- filteredTokensFormatted,
971
- setRawFeeOptions,
972
- ])
972
+ }, [prepareSendResult, selectedToken?.chainId, setRawFeeOptions])
973
973
 
974
974
  const processSend = useCallback(async () => {
975
975
  try {
@@ -316,7 +316,16 @@ export function useTokenList({
316
316
  const imageUrl = token.imageUrl
317
317
  const decimals = isNative ? 18 : token.contractInfo?.decimals
318
318
  if (!decimals) {
319
- throw new Error("Decimals not found")
319
+ logger.console.warn(
320
+ "[trails-sdk] [useTokenList] Missing decimals for token:",
321
+ {
322
+ token: token.contractInfo,
323
+ chainId: token.chainId,
324
+ isNative,
325
+ },
326
+ )
327
+ onError("Decimals not found [useTokenList]")
328
+ return
320
329
  }
321
330
 
322
331
  let formattedToken: Token
@@ -396,7 +405,7 @@ export function useTokenList({
396
405
  return sortedTokens
397
406
  }
398
407
 
399
- const query = searchQuery.toLowerCase().trim()
408
+ const query = searchQuery.trim().toLowerCase()
400
409
  const queryParts = query.split(/\s+/).filter((part) => part.length > 0)
401
410
 
402
411
  const matchingTokens = sortedTokens.filter(
@@ -23,6 +23,7 @@ import {
23
23
  createStorage,
24
24
  useAccount,
25
25
  useConnect,
26
+ useConnections,
26
27
  useDisconnect,
27
28
  WagmiContext,
28
29
  WagmiProvider,
@@ -137,6 +138,7 @@ import {
137
138
  type Screen,
138
139
  } from "./hooks/useCurrentScreen.js"
139
140
  import { BackProvider, useBack } from "./hooks/useBack.js"
141
+ import { useInitialRedirect } from "./hooks/useInitialRedirect.js"
140
142
  import { DefaultTokenSelectionProvider } from "./hooks/useDefaultTokenSelection.js"
141
143
  import {
142
144
  SelectedMeshExchangeProvider,
@@ -440,6 +442,17 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
440
442
  hideDisconnect,
441
443
  } = useWidgetProps()
442
444
  const { address, isConnected, chainId, connector } = useAccount()
445
+
446
+ // Helper function to detect if the current connector is a Sequence wallet
447
+ const isSequenceWallet = useMemo(() => {
448
+ if (!connector) return false
449
+ const connectorName = connector.name?.toLowerCase() || ""
450
+ const connectorId = connector.id?.toLowerCase() || ""
451
+ return (
452
+ connectorName.includes("sequence") || connectorId.includes("sequence")
453
+ )
454
+ }, [connector])
455
+ const connections = useConnections()
443
456
  const { disconnectAsync } = useDisconnect()
444
457
  const { recentTokens, addRecentToken } = useRecentTokens(address)
445
458
  const { wallets: allWallets } = useWallets()
@@ -448,12 +461,14 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
448
461
  const [isModalOpen, setIsModalOpen] = useState(false)
449
462
  const [currentMode, setCurrentMode] = useState<Mode>(mode)
450
463
  const { currentScreen, setCurrentScreen } = useCurrentScreen()
451
- const {
452
- goBack: handleBack,
453
- clearHistory,
454
- isNavigatingBack,
455
- getHistory,
456
- } = useBack()
464
+ const { goBack, clearHistory, isNavigatingBack, setCurrentScreenWithBack } =
465
+ useBack()
466
+
467
+ // Wrapper function that clears errors when going back
468
+ const handleBack = () => {
469
+ setError(null)
470
+ goBack?.()
471
+ }
457
472
  const [previousAddress, setPreviousAddress] = useState<string | undefined>(
458
473
  address,
459
474
  )
@@ -514,6 +529,13 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
514
529
  }
515
530
  }, [])
516
531
 
532
+ // Use the simple initial redirect hook
533
+ const { hasConnectedBefore } = useInitialRedirect(
534
+ isConnected,
535
+ currentMode,
536
+ getInitialScreenForMode,
537
+ )
538
+
517
539
  // Set proper initial screen based on connection state and mode
518
540
  useEffect(() => {
519
541
  if (isInitialScreenSet) {
@@ -521,8 +543,14 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
521
543
  }
522
544
 
523
545
  let properInitialScreen: Screen = "connect"
546
+
524
547
  if (isConnected) {
548
+ // For initial load, always go to the appropriate mode screen
525
549
  properInitialScreen = getInitialScreenForMode(currentMode)
550
+ logger.console.log(
551
+ "[trails-sdk] Initial load with connected wallet, going to mode screen:",
552
+ properInitialScreen,
553
+ )
526
554
  } else if (currentMode === "receive") {
527
555
  properInitialScreen = "receive"
528
556
  }
@@ -797,76 +825,14 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
797
825
  isConnected,
798
826
  })
799
827
 
800
- // redirect to tokens screen only once after initial connection
801
- const [alreadyRedirectedToTokens, setAlreadyRedirectedToTokens] =
802
- useState(false)
803
-
804
- // Update screen based on connection state and mode
828
+ // Simple post-connection navigation - let the hook handle the logic
805
829
  useEffect(() => {
806
830
  if (isNavigatingBack) {
807
831
  return
808
832
  }
809
- if (isConnected) {
810
- if (
811
- currentScreen === "connect" ||
812
- currentScreen === "wallet-list" ||
813
- currentScreen === "wallet-connection-pending"
814
- ) {
815
- if (!alreadyRedirectedToTokens) {
816
- setAlreadyRedirectedToTokens(true)
817
- const history = getHistory()
818
- logger.console.log(
819
- "[trails-sdk] Post-connection navigation triggered",
820
- {
821
- currentScreen,
822
- alreadyRedirectedToTokens,
823
- isConnected,
824
- currentMode,
825
- history,
826
- },
827
- )
828
-
829
- // Check if we came from account-settings -> wallet-list pattern
830
- // Look for this pattern in the last 4-5 screens to handle any intermediate screens
831
- const historyLength = history.length
832
- logger.console.log("[trails-sdk] Full navigation history:", history)
833
-
834
- // Iterate from older to newer screens to find the pattern
835
- let foundAccountSettings = false
836
- let foundWalletListAfterAccountSettings = false
837
-
838
- for (
839
- let i = Math.max(0, historyLength - 5);
840
- i < historyLength - 1;
841
- i++
842
- ) {
843
- if (history[i] === "account-settings") {
844
- foundAccountSettings = true
845
- }
846
- if (history[i] === "wallet-list" && foundAccountSettings) {
847
- foundWalletListAfterAccountSettings = true
848
- break // Found the pattern, no need to continue
849
- }
850
- }
851
-
852
- if (foundAccountSettings && foundWalletListAfterAccountSettings) {
853
- logger.console.log(
854
- "[trails-sdk] Came from account-settings -> wallet-list flow, returning to account-settings",
855
- )
856
- setCurrentScreen("account-settings")
857
- return
858
- }
859
833
 
860
- console.log(
861
- "[trails-sdk] Using default navigation for mode:",
862
- currentMode,
863
- )
864
-
865
- const initialScreen = getInitialScreenForMode(currentMode)
866
- setCurrentScreen(initialScreen)
867
- }
868
- }
869
- } else {
834
+ // Handle disconnection
835
+ if (!isConnected) {
870
836
  if (
871
837
  currentScreen !== "connect" &&
872
838
  currentScreen !== "wallet-connect" &&
@@ -879,25 +845,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
879
845
  }, 0)
880
846
  }
881
847
  }
882
- }, [
883
- isConnected,
884
- currentScreen,
885
- alreadyRedirectedToTokens,
886
- currentMode,
887
- isNavigatingBack,
888
- setCurrentScreen,
889
- getHistory,
890
- getInitialScreenForMode,
891
- ])
892
-
893
- useEffect(() => {
894
- if (
895
- currentScreen === "wallet-connection-pending" &&
896
- alreadyRedirectedToTokens
897
- ) {
898
- setAlreadyRedirectedToTokens(false)
899
- }
900
- }, [currentScreen, alreadyRedirectedToTokens])
848
+ }, [isConnected, currentScreen, isNavigatingBack, setCurrentScreen])
901
849
 
902
850
  // Auto-detect mode changes and switch screens accordingly
903
851
  useEffect(() => {
@@ -1003,13 +951,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1003
951
  setError(null)
1004
952
  setIsConnecting(true)
1005
953
 
1006
- // Handle special case for wallet-list screen
1007
- if (walletId === "wallet-list") {
1008
- setCurrentScreen("wallet-list")
1009
- setIsConnecting(false)
1010
- return
1011
- }
1012
-
1013
954
  const config = allWallets.find((w) => w.id === walletId)
1014
955
  if (!config) {
1015
956
  setError(`No configuration found for wallet: ${walletId}`)
@@ -1031,6 +972,26 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1031
972
  )
1032
973
  // Set the last clicked wallet after successful connection
1033
974
  saveLastClickedWallet(walletId)
975
+
976
+ // Check if this is the first wallet connection
977
+ if (!hasConnectedBefore) {
978
+ // First connection - go to initial screen for the mode
979
+ const initialScreen = getInitialScreenForMode(currentMode)
980
+ logger.console.log(
981
+ "[trails-sdk] First wallet connection, going to initial screen:",
982
+ initialScreen,
983
+ )
984
+ setCurrentScreen(initialScreen)
985
+ } else {
986
+ // Subsequent connection - go to account-settings
987
+ logger.console.log(
988
+ "[trails-sdk] Subsequent wallet connection, going to account-settings",
989
+ )
990
+ setCurrentScreenWithBack(
991
+ "account-settings",
992
+ getInitialScreenForMode(currentMode),
993
+ )
994
+ }
1034
995
  } else if (config.connector === walletConnectConnector) {
1035
996
  // Store the current connector as previous before switching to WalletConnect
1036
997
  if (connector && connector.name !== "WalletConnect") {
@@ -1055,8 +1016,31 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1055
1016
  setError(null)
1056
1017
 
1057
1018
  try {
1019
+ // Disconnect all connected wallets
1020
+ logger.console.log(
1021
+ "[trails-sdk] Disconnecting all connected wallets:",
1022
+ connections.length,
1023
+ )
1024
+
1025
+ // Disconnect each connection
1026
+ for (const connection of connections) {
1027
+ try {
1028
+ await connection.connector.disconnect()
1029
+ logger.console.log(
1030
+ "[trails-sdk] Disconnected wallet:",
1031
+ connection.connector.name,
1032
+ )
1033
+ } catch (error) {
1034
+ logger.console.error(
1035
+ "[trails-sdk] Failed to disconnect wallet:",
1036
+ connection.connector.name,
1037
+ error,
1038
+ )
1039
+ }
1040
+ }
1041
+
1042
+ // Also call the main disconnect to ensure cleanup
1058
1043
  await disconnectAsync()
1059
- setAlreadyRedirectedToTokens(false)
1060
1044
  trackWalletDisconnected()
1061
1045
  } catch (error) {
1062
1046
  logger.console.error("[trails-sdk] Failed to disconnect:", error)
@@ -1180,7 +1164,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1180
1164
  setTransactionStates([])
1181
1165
  setPrepareSendQuote(null)
1182
1166
  setTotalCompletionSeconds(null)
1183
- setAlreadyRedirectedToTokens(false)
1184
1167
  clearHistory()
1185
1168
  }, [
1186
1169
  setDestinationTxHash,
@@ -1587,7 +1570,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1587
1570
  <Pay
1588
1571
  selectedToken={selectedToken}
1589
1572
  onSend={handleOnSend}
1590
- onBack={handleBack}
1591
1573
  onWaitingForWalletConfirm={handleWaitingForWalletConfirm}
1592
1574
  onConfirm={() => setCurrentScreen("pending")}
1593
1575
  onComplete={handleTransferComplete}
@@ -1612,6 +1594,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1612
1594
  selectedPool ? generatedCalldata : toCalldata || undefined
1613
1595
  }
1614
1596
  walletClient={walletClient}
1597
+ isSequenceWallet={isSequenceWallet}
1615
1598
  onTransactionStateChange={handleTransactionStateChange}
1616
1599
  onError={handleSendError}
1617
1600
  paymasterUrls={paymasterUrls}
@@ -1660,7 +1643,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1660
1643
  return walletClient?.account ? (
1661
1644
  <Fund
1662
1645
  onSend={handleOnSend}
1663
- onBack={handleBack}
1664
1646
  onWaitingForWalletConfirm={handleWaitingForWalletConfirm}
1665
1647
  onConfirm={() => setCurrentScreen("pending")}
1666
1648
  onComplete={handleTransferComplete}
@@ -1671,6 +1653,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1671
1653
  toToken={toToken || undefined}
1672
1654
  toCalldata={toCalldata || undefined}
1673
1655
  walletClient={walletClient}
1656
+ isSequenceWallet={isSequenceWallet}
1674
1657
  onTransactionStateChange={handleTransactionStateChange}
1675
1658
  onError={handleSendError}
1676
1659
  paymasterUrls={paymasterUrls}
@@ -1766,8 +1749,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1766
1749
  logger.console.log(
1767
1750
  "[trails-sdk] Regular wallet selected, going to wallet-connection-pending",
1768
1751
  )
1769
- // Reset the redirect flag to prevent immediate navigation
1770
- setAlreadyRedirectedToTokens(false)
1752
+ // Don't reset the redirect flag - user has already been through initial flow
1771
1753
  setTimeout(() => {
1772
1754
  setCurrentScreen("wallet-connection-pending")
1773
1755
  }, 100)
@@ -1788,7 +1770,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1788
1770
  case "earn":
1789
1771
  return walletClient?.account ? (
1790
1772
  <Earn
1791
- onBack={handleBack}
1792
1773
  onContinue={() => setCurrentScreen("send-form")}
1793
1774
  account={walletClient.account}
1794
1775
  walletClient={walletClient}
@@ -1829,7 +1810,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1829
1810
  return walletClient?.account ? (
1830
1811
  <Swap
1831
1812
  onSend={handleOnSend}
1832
- onBack={handleBack}
1833
1813
  onWaitingForWalletConfirm={handleWaitingForWalletConfirm}
1834
1814
  onConfirm={() => setCurrentScreen("pending")}
1835
1815
  onComplete={handleTransferComplete}
@@ -1864,7 +1844,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1864
1844
  return (
1865
1845
  <Receive
1866
1846
  accountAddress={toAddress || ""}
1867
- onBack={handleBack}
1868
1847
  isConnected={isConnected}
1869
1848
  onConnectWallet={() => setCurrentScreen("connect")}
1870
1849
  onPay={() => setCurrentScreen("send-form")}
File without changes