0xtrails 0.2.5 → 0.2.6

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 (116) hide show
  1. package/dist/abortController.d.ts +8 -0
  2. package/dist/abortController.d.ts.map +1 -0
  3. package/dist/{ccip-CXlshvBY.js → ccip-Xjh9d1gb.js} +7 -7
  4. package/dist/constants.d.ts +2 -0
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/fees.d.ts +19 -0
  7. package/dist/fees.d.ts.map +1 -0
  8. package/dist/{index-_QuyGrjU.js → index-BnhdZ8Ho.js} +34769 -34247
  9. package/dist/index.js +726 -520
  10. package/dist/prepareSend.d.ts +11 -77
  11. package/dist/prepareSend.d.ts.map +1 -1
  12. package/dist/transactions.d.ts +4 -2
  13. package/dist/transactions.d.ts.map +1 -1
  14. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts +4 -0
  15. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts.map +1 -0
  16. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  17. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  18. package/dist/widget/components/ClassicSwap.d.ts +2 -2
  19. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  20. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  21. package/dist/widget/components/DynamicInputStyles.d.ts +18 -0
  22. package/dist/widget/components/DynamicInputStyles.d.ts.map +1 -0
  23. package/dist/widget/components/Earn.d.ts +2 -2
  24. package/dist/widget/components/Earn.d.ts.map +1 -1
  25. package/dist/widget/components/ErrorAnimationIcon.d.ts +2 -0
  26. package/dist/widget/components/ErrorAnimationIcon.d.ts.map +1 -0
  27. package/dist/widget/components/FeeBreakdown.d.ts +9 -0
  28. package/dist/widget/components/FeeBreakdown.d.ts.map +1 -0
  29. package/dist/widget/components/Fund.d.ts +2 -2
  30. package/dist/widget/components/Fund.d.ts.map +1 -1
  31. package/dist/widget/components/FundSwap.d.ts +2 -2
  32. package/dist/widget/components/FundSwap.d.ts.map +1 -1
  33. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -1
  34. package/dist/widget/components/Identicon.d.ts.map +1 -1
  35. package/dist/widget/components/MeshConnectExchanges.d.ts +0 -3
  36. package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
  37. package/dist/widget/components/Modal.d.ts.map +1 -1
  38. package/dist/widget/components/Pay.d.ts +2 -2
  39. package/dist/widget/components/Pay.d.ts.map +1 -1
  40. package/dist/widget/components/PoolDeposit.d.ts +3 -2
  41. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  42. package/dist/widget/components/PoolWithdraw.d.ts +3 -2
  43. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
  44. package/dist/widget/components/QuoteDetails.d.ts +1 -0
  45. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  46. package/dist/widget/components/Receipt.d.ts.map +1 -1
  47. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -1
  48. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  49. package/dist/widget/components/Swap.d.ts +2 -2
  50. package/dist/widget/components/Swap.d.ts.map +1 -1
  51. package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
  52. package/dist/widget/components/TokenDisplayNonSelectable.d.ts +11 -0
  53. package/dist/widget/components/TokenDisplayNonSelectable.d.ts.map +1 -0
  54. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  55. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -1
  56. package/dist/widget/components/Tooltip.d.ts +9 -0
  57. package/dist/widget/components/Tooltip.d.ts.map +1 -0
  58. package/dist/widget/components/WaasFeeOptions.d.ts +1 -0
  59. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
  60. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  61. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  62. package/dist/widget/css/compiled.css +1 -1
  63. package/dist/widget/hooks/useQuote.d.ts +83 -0
  64. package/dist/widget/hooks/useQuote.d.ts.map +1 -0
  65. package/dist/widget/hooks/useSendForm.d.ts +2 -2
  66. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  67. package/dist/widget/index.js +2 -2
  68. package/dist/widget/widget.d.ts +5 -0
  69. package/dist/widget/widget.d.ts.map +1 -1
  70. package/package.json +2 -2
  71. package/src/abortController.ts +35 -0
  72. package/src/constants.ts +3 -0
  73. package/src/fees.ts +199 -0
  74. package/src/prepareSend.ts +225 -398
  75. package/src/trails.ts +3 -3
  76. package/src/transactions.ts +62 -18
  77. package/src/widget/compiled.css +1 -1
  78. package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +22 -0
  79. package/src/widget/components/AccountSettings.tsx +48 -36
  80. package/src/widget/components/ChainFilterDropdown.tsx +24 -3
  81. package/src/widget/components/ClassicSwap.tsx +24 -62
  82. package/src/widget/components/ConnectWallet.tsx +4 -1
  83. package/src/widget/components/ConnectedWallets.tsx +21 -21
  84. package/src/widget/components/DynamicInputStyles.tsx +76 -0
  85. package/src/widget/components/Earn.tsx +34 -29
  86. package/src/widget/components/ErrorAnimationIcon.tsx +130 -0
  87. package/src/widget/components/FeeBreakdown.tsx +155 -0
  88. package/src/widget/components/Fund.tsx +10 -26
  89. package/src/widget/components/FundSwap.tsx +2 -2
  90. package/src/widget/components/FundingMethodSelectorButton.tsx +24 -14
  91. package/src/widget/components/Identicon.tsx +164 -95
  92. package/src/widget/components/MeshConnectExchanges.tsx +2 -15
  93. package/src/widget/components/Modal.tsx +0 -12
  94. package/src/widget/components/Pay.tsx +65 -63
  95. package/src/widget/components/PoolDeposit.tsx +206 -230
  96. package/src/widget/components/PoolWithdraw.tsx +219 -238
  97. package/src/widget/components/PriceImpactWarning.tsx +1 -1
  98. package/src/widget/components/QuoteDetails.tsx +25 -8
  99. package/src/widget/components/Receipt.tsx +16 -2
  100. package/src/widget/components/RecipientSelectorButton.tsx +7 -5
  101. package/src/widget/components/Recipients.tsx +1 -1
  102. package/src/widget/components/ScreenHeader.tsx +60 -36
  103. package/src/widget/components/Swap.tsx +2 -2
  104. package/src/widget/components/ThemeProvider.tsx +2 -1
  105. package/src/widget/components/TokenDisplayNonSelectable.tsx +40 -0
  106. package/src/widget/components/TokenImage.tsx +1 -1
  107. package/src/widget/components/TokenSelector.tsx +62 -53
  108. package/src/widget/components/TokenSelectorButton.tsx +38 -15
  109. package/src/widget/components/Tooltip.tsx +51 -0
  110. package/src/widget/components/TransferPendingVertical.tsx +1 -1
  111. package/src/widget/components/WaasFeeOptions.tsx +124 -5
  112. package/src/widget/components/WalletConfirmation.tsx +23 -13
  113. package/src/widget/components/WalletConnect.tsx +93 -29
  114. package/src/widget/hooks/useQuote.ts +413 -0
  115. package/src/widget/hooks/useSendForm.ts +8 -4
  116. package/src/widget/widget.tsx +175 -190
@@ -4,6 +4,7 @@ import { AaveClient, AaveProvider } from "@aave/react"
4
4
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
5
5
  import { AnimatePresence, motion } from "motion/react"
6
6
  import React, {
7
+ createContext,
7
8
  forwardRef,
8
9
  StrictMode,
9
10
  useCallback,
@@ -152,9 +153,42 @@ import {
152
153
  SelectedFundMethodProvider,
153
154
  useSelectedFundMethod,
154
155
  } from "./hooks/useSelectedFundMethod.js"
156
+ import { DEFAULT_MODE } from "../constants.js"
155
157
 
156
158
  export const aaveClient = AaveClient.create()
157
159
 
160
+ // Modal Context
161
+ interface ModalContextType {
162
+ closeModal: () => void
163
+ isModalOpen: boolean
164
+ }
165
+
166
+ const ModalContext = createContext<ModalContextType | null>(null)
167
+
168
+ // Modal Provider Component
169
+ const ModalProvider: React.FC<{
170
+ children: React.ReactNode
171
+ closeModal: () => void
172
+ isModalOpen: boolean
173
+ }> = ({ children, closeModal, isModalOpen }) => (
174
+ <ModalContext.Provider value={{ closeModal, isModalOpen }}>
175
+ {children}
176
+ </ModalContext.Provider>
177
+ )
178
+
179
+ // useModal Hook
180
+ export const useModal = (): ModalContextType => {
181
+ const context = useContext(ModalContext)
182
+ if (!context) {
183
+ // Return a no-op function when not in modal context (e.g., renderInline)
184
+ return {
185
+ closeModal: () => {},
186
+ isModalOpen: false,
187
+ }
188
+ }
189
+ return context
190
+ }
191
+
158
192
  // Validate toToken - must be "ETH", "USDC", or a valid hex address
159
193
  const isValidToToToken = (toToken: string | null | undefined) => {
160
194
  if (toToken === null || toToken === undefined || toToken === "") {
@@ -252,7 +286,11 @@ const useWalletManager = (
252
286
  if (!connector) {
253
287
  return
254
288
  }
255
- const activeProvider = await connector.getProvider?.()
289
+ if (typeof connector?.getProvider !== "function") {
290
+ return
291
+ }
292
+
293
+ const activeProvider = await connector.getProvider()
256
294
 
257
295
  if (activeProvider && address && chainId) {
258
296
  const chain = getChainInfo(chainId)
@@ -428,7 +466,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
428
466
  toCalldata,
429
467
  children,
430
468
  renderInline = false,
431
- mode = "pay",
469
+ mode = DEFAULT_MODE,
432
470
  onOriginConfirmation,
433
471
  onDestinationConfirmation,
434
472
  onCheckoutStart,
@@ -448,7 +486,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
448
486
  decoupleWagmi,
449
487
  hideDisconnect,
450
488
  } = useWidgetProps()
451
- const { address, isConnected, chainId, connector } = useAccount()
489
+ const { address, chainId, connector } = useAccount()
452
490
  // const { setOpenConnectModal } = useOpenConnectModal()
453
491
 
454
492
  // Helper function to detect if the current connector is a Sequence wallet
@@ -470,6 +508,9 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
470
508
  }, [connector])
471
509
  const connections = useConnections()
472
510
  const { disconnectAsync } = useDisconnect()
511
+
512
+ // Check if there are any connected accounts across all connectors
513
+ const isConnected = connections.length > 0
473
514
  const { recentTokens, addRecentToken } = useRecentTokens(address)
474
515
  const { wallets: allWallets } = useWallets()
475
516
  const { selectedToken, setSelectedToken } = useSelectedToken()
@@ -477,13 +518,8 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
477
518
  const [isModalOpen, setIsModalOpen] = useState(false)
478
519
  const [currentMode, setCurrentMode] = useState<Mode>(mode)
479
520
  const { currentScreen, setCurrentScreen } = useCurrentScreen()
480
- const {
481
- goBack,
482
- clearHistory,
483
- isNavigatingBack,
484
- setCurrentScreenWithBack,
485
- getPreviousScreen,
486
- } = useBack()
521
+ const { goBack, clearHistory, setCurrentScreenWithBack, getPreviousScreen } =
522
+ useBack()
487
523
 
488
524
  // Wrapper function that clears errors when going back
489
525
  const handleBack = () => {
@@ -532,7 +568,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
532
568
  number | null
533
569
  >(null)
534
570
  const { connectAsync } = useConnect()
535
- const [isInitialScreenSet, setIsInitialScreenSet] = useState(false)
536
571
 
537
572
  const getInitialScreenForMode = useCallback((mode: Mode) => {
538
573
  if (mode === "swap") {
@@ -546,7 +581,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
546
581
  } else if (mode === "pay") {
547
582
  return "send-form"
548
583
  } else {
549
- return "account-settings"
584
+ return "send-form"
550
585
  }
551
586
  }, [])
552
587
 
@@ -556,55 +591,27 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
556
591
 
557
592
  // If connected, navigate to the appropriate screen for the new mode
558
593
  if (isConnected) {
559
- const newScreen = getInitialScreenForMode(mode)
560
- setCurrentScreen(newScreen)
594
+ setCurrentScreen("home")
561
595
  }
562
- }, [mode, isConnected, setCurrentScreen, getInitialScreenForMode])
596
+ }, [mode, isConnected, setCurrentScreen])
563
597
 
564
- // Use the simple initial redirect hook
565
- const { hasConnectedBefore } = useInitialRedirect(
566
- isConnected,
567
- currentMode,
568
- getInitialScreenForMode,
569
- )
598
+ const goHome = useCallback(() => {
599
+ const initialScreen = getInitialScreenForMode(currentMode)
600
+ setCurrentScreen(initialScreen)
601
+ }, [currentMode, setCurrentScreen, getInitialScreenForMode])
570
602
 
571
- // Set proper initial screen based on connection state and mode
572
603
  useEffect(() => {
573
- if (isInitialScreenSet) {
574
- return
604
+ if (currentScreen === "home") {
605
+ goHome()
575
606
  }
607
+ }, [currentScreen, goHome])
576
608
 
577
- let properInitialScreen: Screen = "connect"
578
-
579
- if (isConnected) {
580
- // For initial load, always go to the appropriate mode screen
581
- properInitialScreen = getInitialScreenForMode(currentMode)
582
- logger.console.log(
583
- "[trails-sdk] Initial load with connected wallet, going to mode screen:",
584
- properInitialScreen,
585
- )
586
- } else if (currentMode === "receive") {
587
- properInitialScreen = "receive"
588
- }
589
-
590
- // Only update if the current screen is still the default
591
- if (currentScreen === "connect" || currentScreen === "receive") {
592
- logger.console.log(
593
- "[trails-sdk] setting currentScreen to",
594
- properInitialScreen,
595
- )
596
- setCurrentScreen(properInitialScreen)
597
- }
598
-
599
- setIsInitialScreenSet(true)
600
- }, [
609
+ // Use the simple initial redirect hook
610
+ const { hasConnectedBefore } = useInitialRedirect(
601
611
  isConnected,
602
612
  currentMode,
603
- currentScreen,
604
- setCurrentScreen,
605
- isInitialScreenSet,
606
613
  getInitialScreenForMode,
607
- ])
614
+ )
608
615
 
609
616
  const defaultButtonText = useMemo(() => {
610
617
  if (currentMode === "fund") {
@@ -632,11 +639,27 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
632
639
  const isValidToToken = isValidToToToken(toToken)
633
640
  const isValidToAddress = toAddress ? isAddress(toAddress) : true
634
641
 
642
+ // Validate paymasterUrls - each url must be a valid URL
643
+ let isValidPaymasterUrls = true
644
+ let invalidPaymasterUrl = ""
645
+ if (paymasterUrls && Array.isArray(paymasterUrls)) {
646
+ for (const paymaster of paymasterUrls) {
647
+ try {
648
+ new URL(paymaster.url)
649
+ } catch {
650
+ isValidPaymasterUrls = false
651
+ invalidPaymasterUrl = paymaster.url
652
+ break
653
+ }
654
+ }
655
+ }
656
+
635
657
  if (
636
658
  isValidToAmount &&
637
659
  isValidToChainId &&
638
660
  isValidToToken &&
639
- isValidToAddress
661
+ isValidToAddress &&
662
+ isValidPaymasterUrls
640
663
  ) {
641
664
  setError(null)
642
665
  return
@@ -680,6 +703,17 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
680
703
  toAddress,
681
704
  )
682
705
  setError("Invalid toAddress: must be a valid hex address")
706
+ return
707
+ }
708
+
709
+ // Validate paymasterUrls
710
+ if (!isValidPaymasterUrls) {
711
+ logger.console.error(
712
+ "[trails-sdk] Invalid paymasterUrls prop: url must be a valid URL. Received:",
713
+ invalidPaymasterUrl,
714
+ )
715
+ setError("Invalid paymasterUrls: url must be a valid URL")
716
+ return
683
717
  }
684
718
 
685
719
  if (currentMode === "fund") {
@@ -706,7 +740,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
706
740
  return
707
741
  }
708
742
  }
709
- }, [toAmount, toChainId, toToken, currentMode, toAddress])
743
+ }, [toAmount, toChainId, toToken, currentMode, toAddress, paymasterUrls])
710
744
 
711
745
  const walletClient = useWalletManager(address, chainId, connector)
712
746
 
@@ -857,28 +891,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
857
891
  isConnected,
858
892
  })
859
893
 
860
- // Simple post-connection navigation - let the hook handle the logic
861
- useEffect(() => {
862
- if (isNavigatingBack) {
863
- return
864
- }
865
-
866
- // Handle disconnection
867
- if (!isConnected) {
868
- if (
869
- currentScreen !== "connect" &&
870
- currentScreen !== "wallet-connect" &&
871
- currentScreen !== "wallet-list" &&
872
- currentScreen !== "wallet-connection-pending" &&
873
- currentScreen !== "receive"
874
- ) {
875
- setTimeout(() => {
876
- setCurrentScreen("connect")
877
- }, 0)
878
- }
879
- }
880
- }, [isConnected, currentScreen, isNavigatingBack, setCurrentScreen])
881
-
882
894
  // Auto-detect mode changes and switch screens accordingly
883
895
  useEffect(() => {
884
896
  if (
@@ -1017,21 +1029,14 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1017
1029
  // Check if this is the first wallet connection
1018
1030
  if (!hasConnectedBefore) {
1019
1031
  // First connection - go to initial screen for the mode
1020
- const initialScreen = getInitialScreenForMode(currentMode)
1021
- logger.console.log(
1022
- "[trails-sdk] First wallet connection, going to initial screen:",
1023
- initialScreen,
1024
- )
1025
- setCurrentScreen(initialScreen)
1032
+ logger.console.log("[trails-sdk] First wallet connection, going home")
1033
+ goHome()
1026
1034
  } else {
1027
- // Subsequent connection - go to account-settings
1035
+ // Subsequent connection - go to home which will route to the appropriate screen
1028
1036
  logger.console.log(
1029
- "[trails-sdk] Subsequent wallet connection, going to account-settings",
1030
- )
1031
- setCurrentScreenWithBack(
1032
- "account-settings",
1033
- getInitialScreenForMode(currentMode),
1037
+ "[trails-sdk] Subsequent wallet connection, going to home",
1034
1038
  )
1039
+ goHome()
1035
1040
  }
1036
1041
  } else if (config.connector === walletConnectConnector) {
1037
1042
  // Store the current connector as previous before switching to WalletConnect
@@ -1191,7 +1196,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1191
1196
  const resetState = useCallback(() => {
1192
1197
  setSelectedFundMethod("wallet")
1193
1198
  // Reset to appropriate screen based on mode
1194
- setCurrentScreen(currentMode === "receive" ? "receive" : "connect")
1199
+ setCurrentScreen("home")
1195
1200
  setSelectedToken(null)
1196
1201
  setSelectedPool(null)
1197
1202
  setSelectedWalletId(null)
@@ -1215,7 +1220,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1215
1220
  setOriginChainId,
1216
1221
  setSelectedToken,
1217
1222
  setSelectedPool,
1218
- currentMode,
1219
1223
  setCurrentScreen,
1220
1224
  clearHistory,
1221
1225
  ])
@@ -1588,12 +1592,11 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1588
1592
  onSelectExchangeList={handleSelectExchangeList}
1589
1593
  onSelectConnectedAccount={() => {
1590
1594
  setSelectedFundMethod("connected-account")
1591
- const initialScreen = getInitialScreenForMode(currentMode)
1592
- setCurrentScreen(initialScreen)
1595
+ setCurrentScreen("home")
1593
1596
  }}
1594
1597
  onSelectQrCode={() => {
1595
1598
  setSelectedFundMethod("qr-code")
1596
- setCurrentScreen(getInitialScreenForMode(currentMode))
1599
+ setCurrentScreen("home")
1597
1600
  }}
1598
1601
  />
1599
1602
  )
@@ -1613,14 +1616,14 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1613
1616
  />
1614
1617
  )
1615
1618
  case "send-form":
1616
- return walletClient?.account ? (
1619
+ return (
1617
1620
  <Pay
1618
1621
  selectedToken={selectedToken}
1619
1622
  onSend={handleOnSend}
1620
1623
  onWaitingForWalletConfirm={handleWaitingForWalletConfirm}
1621
1624
  onConfirm={() => setCurrentScreen("pending")}
1622
1625
  onComplete={handleTransferComplete}
1623
- account={walletClient.account}
1626
+ account={walletClient?.account}
1624
1627
  toRecipient={
1625
1628
  selectedPool
1626
1629
  ? selectedPool.depositAddress
@@ -1640,7 +1643,7 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1640
1643
  toCalldata={
1641
1644
  selectedPool ? generatedCalldata : toCalldata || undefined
1642
1645
  }
1643
- walletClient={walletClient}
1646
+ walletClient={walletClient ?? undefined}
1644
1647
  isSequenceWallet={isSequenceWallet}
1645
1648
  onTransactionStateChange={handleTransactionStateChange}
1646
1649
  onError={handleSendError}
@@ -1681,26 +1684,22 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1681
1684
  onRecentTokenSelect={handleRecentTokenSelect}
1682
1685
  onTrackToken={handleTrackToken}
1683
1686
  />
1684
- ) : (
1685
- <div className="text-center p-4 rounded-lg text-gray-600 bg-gray-50 dark:text-gray-300 dark:bg-gray-800">
1686
- Please connect wallet
1687
- </div>
1688
1687
  )
1689
1688
  case "fund-form":
1690
- return walletClient?.account ? (
1689
+ return (
1691
1690
  <Fund
1692
1691
  selectedToken={selectedToken}
1693
1692
  onSend={handleOnSend}
1694
1693
  onWaitingForWalletConfirm={handleWaitingForWalletConfirm}
1695
1694
  onConfirm={() => setCurrentScreen("pending")}
1696
1695
  onComplete={handleTransferComplete}
1697
- account={walletClient.account}
1696
+ account={walletClient?.account}
1698
1697
  toAmount={toAmount || undefined}
1699
1698
  toRecipient={toAddress || undefined}
1700
1699
  toChainId={toChainId ? Number(toChainId) : undefined}
1701
1700
  toToken={toToken || undefined}
1702
1701
  toCalldata={toCalldata || undefined}
1703
- walletClient={walletClient}
1702
+ walletClient={walletClient ?? undefined}
1704
1703
  isSequenceWallet={isSequenceWallet}
1705
1704
  onTransactionStateChange={handleTransactionStateChange}
1706
1705
  onError={handleSendError}
@@ -1715,10 +1714,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1715
1714
  onRecentTokenSelect={handleRecentTokenSelect}
1716
1715
  onTrackToken={handleTrackToken}
1717
1716
  />
1718
- ) : (
1719
- <div className="text-center p-4 rounded-lg text-gray-600 bg-gray-50 dark:text-gray-300 dark:bg-gray-800">
1720
- Please connect wallet
1721
- </div>
1722
1717
  )
1723
1718
  case "wallet-confirmation":
1724
1719
  return (
@@ -1821,11 +1816,11 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1821
1816
  />
1822
1817
  )
1823
1818
  case "earn":
1824
- return walletClient?.account ? (
1819
+ return (
1825
1820
  <Earn
1826
1821
  onContinue={() => setCurrentScreen("send-form")}
1827
- account={walletClient.account}
1828
- walletClient={walletClient}
1822
+ account={walletClient?.account}
1823
+ walletClient={walletClient ?? undefined}
1829
1824
  onTransactionStateChange={handleTransactionStateChange}
1830
1825
  onError={handleSendError}
1831
1826
  onWaitingForWalletConfirm={handleWaitingForWalletConfirm}
@@ -1843,10 +1838,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1843
1838
  onRecentTokenSelect={handleRecentTokenSelect}
1844
1839
  onTrackToken={handleTrackToken}
1845
1840
  />
1846
- ) : (
1847
- <div className="text-center p-4 rounded-lg text-gray-600 bg-gray-50 dark:text-gray-300 dark:bg-gray-800">
1848
- Please connect wallet
1849
- </div>
1850
1841
  )
1851
1842
  case "earn-pools":
1852
1843
  return (
@@ -1860,20 +1851,20 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1860
1851
  />
1861
1852
  )
1862
1853
  case "swap":
1863
- return walletClient?.account ? (
1854
+ return (
1864
1855
  <Swap
1865
1856
  onSend={handleOnSend}
1866
1857
  onWaitingForWalletConfirm={handleWaitingForWalletConfirm}
1867
1858
  onConfirm={() => setCurrentScreen("pending")}
1868
1859
  onComplete={handleTransferComplete}
1869
1860
  selectedToken={selectedToken}
1870
- account={walletClient.account}
1861
+ account={walletClient?.account}
1871
1862
  toRecipient={toAddress || undefined}
1872
1863
  toAmount={toAmount || undefined}
1873
1864
  toChainId={toChainId ? Number(toChainId) : undefined}
1874
1865
  toToken={toToken || undefined}
1875
1866
  toCalldata={toCalldata || undefined}
1876
- walletClient={walletClient}
1867
+ walletClient={walletClient ?? undefined}
1877
1868
  isSequenceWallet={isSequenceWallet}
1878
1869
  onTransactionStateChange={handleTransactionStateChange}
1879
1870
  onError={handleSendError}
@@ -1889,10 +1880,6 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1889
1880
  onRecentTokenSelect={handleRecentTokenSelect}
1890
1881
  onTrackToken={handleTrackToken}
1891
1882
  />
1892
- ) : (
1893
- <div className="text-center p-4 rounded-lg text-gray-600 bg-gray-50 dark:text-gray-300 dark:bg-gray-800">
1894
- Please connect wallet
1895
- </div>
1896
1883
  )
1897
1884
  case "receive":
1898
1885
  return (
@@ -1928,6 +1915,10 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1928
1915
  />
1929
1916
  )
1930
1917
  case "account-settings":
1918
+ if (!isConnected) {
1919
+ setCurrentScreen("connect")
1920
+ return
1921
+ }
1931
1922
  return <AccountSettings onBack={handleBack} />
1932
1923
  case "user-preferences":
1933
1924
  return <UserPreferences onBack={handleBack} />
@@ -1943,16 +1934,9 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1943
1934
  />
1944
1935
  )
1945
1936
  case "mesh-connect-exchanges":
1946
- return (
1947
- <MeshConnectExchanges
1948
- onBack={handleBack}
1949
- getInitialScreenForMode={getInitialScreenForMode}
1950
- />
1951
- )
1937
+ return <MeshConnectExchanges onBack={handleBack} />
1952
1938
  case "home": {
1953
- // Redirect to the appropriate screen for the current mode
1954
- const homeScreen = getInitialScreenForMode(currentMode)
1955
- setCurrentScreen(homeScreen)
1939
+ // Navigation handled by useEffect above, otherwise it would cause a loop
1956
1940
  return null
1957
1941
  }
1958
1942
  default:
@@ -1962,68 +1946,70 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1962
1946
 
1963
1947
  const renderScreen = () => {
1964
1948
  return (
1965
- <ModeProvider
1966
- value={{
1967
- mode: currentMode,
1968
- }}
1969
- >
1970
- <PriceImpactWarningProvider
1949
+ <ModalProvider closeModal={handleCloseModal} isModalOpen={isModalOpen}>
1950
+ <ModeProvider
1971
1951
  value={{
1972
- thresholdBps: priceImpactWarningThresholdBps,
1973
- warningMessage: priceImpactWarningMessage,
1974
- fallbackBridgeUrl: priceImpactFallbackBridgeUrl,
1952
+ mode: currentMode,
1975
1953
  }}
1976
1954
  >
1977
- <motion.div
1978
- initial={{ opacity: 0, scale: 0.95 }}
1979
- animate={{ opacity: 1, scale: 1 }}
1980
- exit={{ opacity: 0, scale: 0.95 }}
1981
- transition={{
1982
- type: "spring",
1983
- stiffness: 200,
1984
- damping: 30,
1985
- mass: 1,
1955
+ <PriceImpactWarningProvider
1956
+ value={{
1957
+ thresholdBps: priceImpactWarningThresholdBps,
1958
+ warningMessage: priceImpactWarningMessage,
1959
+ fallbackBridgeUrl: priceImpactFallbackBridgeUrl,
1986
1960
  }}
1987
- className="flex flex-col min-h-[400px] shadow-xl p-4 sm:p-6 relative w-full sm:w-[400px] mx-auto custom-scrollbar trails-bg-primary trails-text-primary trails-font trails-border-radius-widget trails-widget-border"
1988
- layout
1989
- layoutId="modal-container"
1990
- onClick={(e) => e.stopPropagation()}
1991
1961
  >
1992
- <AnimatePresence mode="wait">
1993
- <motion.div
1994
- key={currentScreen}
1995
- initial={{ opacity: 0, x: 20 }}
1996
- animate={{ opacity: 1, x: 0 }}
1997
- exit={{ opacity: 0, x: -20 }}
1998
- transition={{
1999
- type: "spring",
2000
- stiffness: 500,
2001
- damping: 30,
2002
- mass: 0.6,
2003
- }}
2004
- className="flex-1 flex flex-col w-full"
2005
- layout
2006
- >
2007
- {renderScreenContent()}
2008
- {/* Error Display */}
2009
- {error && (
2010
- <div className="mt-2">
2011
- <ErrorDisplay
2012
- errorPrettified={getPrettifiedErrorMessage(
2013
- error,
2014
- "An error occured",
2015
- )}
2016
- error={error}
2017
- severity="error"
2018
- />
2019
- </div>
2020
- )}
2021
- </motion.div>
2022
- </AnimatePresence>
2023
- <Footer onDebugScreenSelect={handleDebugScreenSelect} />
2024
- </motion.div>
2025
- </PriceImpactWarningProvider>
2026
- </ModeProvider>
1962
+ <motion.div
1963
+ initial={{ opacity: 0, scale: 0.95 }}
1964
+ animate={{ opacity: 1, scale: 1 }}
1965
+ exit={{ opacity: 0, scale: 0.95 }}
1966
+ transition={{
1967
+ type: "spring",
1968
+ stiffness: 200,
1969
+ damping: 30,
1970
+ mass: 1,
1971
+ }}
1972
+ className="flex flex-col min-h-[400px] shadow-xl p-4 sm:p-6 relative w-full sm:w-[400px] mx-auto custom-scrollbar trails-bg-primary trails-text-primary trails-font trails-border-radius-widget trails-widget-border"
1973
+ layout
1974
+ layoutId="modal-container"
1975
+ onClick={(e) => e.stopPropagation()}
1976
+ >
1977
+ <AnimatePresence mode="wait">
1978
+ <motion.div
1979
+ key={currentScreen}
1980
+ initial={{ opacity: 0, x: 20 }}
1981
+ animate={{ opacity: 1, x: 0 }}
1982
+ exit={{ opacity: 0, x: -20 }}
1983
+ transition={{
1984
+ type: "spring",
1985
+ stiffness: 500,
1986
+ damping: 30,
1987
+ mass: 0.6,
1988
+ }}
1989
+ className="flex-1 flex flex-col w-full"
1990
+ layout
1991
+ >
1992
+ {renderScreenContent()}
1993
+ {/* Error Display */}
1994
+ {error && (
1995
+ <div className="mt-2">
1996
+ <ErrorDisplay
1997
+ errorPrettified={getPrettifiedErrorMessage(
1998
+ error,
1999
+ "An error occured",
2000
+ )}
2001
+ error={error}
2002
+ severity="error"
2003
+ />
2004
+ </div>
2005
+ )}
2006
+ </motion.div>
2007
+ </AnimatePresence>
2008
+ <Footer onDebugScreenSelect={handleDebugScreenSelect} />
2009
+ </motion.div>
2010
+ </PriceImpactWarningProvider>
2011
+ </ModeProvider>
2012
+ </ModalProvider>
2027
2013
  )
2028
2014
  }
2029
2015
 
@@ -2069,8 +2055,7 @@ const WidgetInner = forwardRef<TrailsWidgetRef, TrailsWidgetProps>(
2069
2055
  // Calculate initial screen based on mode and connection state
2070
2056
  // Note: We can't use useAccount here as it needs to be inside WagmiProvider
2071
2057
  // So we'll use a default and let the WidgetContent handle the logic
2072
- const initialScreen: Screen =
2073
- props.mode === "receive" ? "receive" : "connect"
2058
+ const initialScreen: Screen = "home"
2074
2059
 
2075
2060
  return (
2076
2061
  <WidgetPropsProvider props={props}>
@@ -2160,7 +2145,7 @@ export const TrailsWidget = forwardRef<TrailsWidgetRef, TrailsWidgetProps>(
2160
2145
  logger.console.log("props.slippageTolerance", props.slippageTolerance)
2161
2146
  setSlippageTolerance(String(props.slippageTolerance))
2162
2147
  }
2163
- if (typeof props.debug === "boolean" && props.debug === getDebug()) {
2148
+ if (typeof props.debug === "boolean" && props.debug !== getDebug()) {
2164
2149
  logger.console.log("props.debug", props.debug)
2165
2150
  setDebug(props.debug)
2166
2151
  }