0xtrails 0.2.4 → 0.2.5

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 (161) hide show
  1. package/dist/aave.d.ts +8 -0
  2. package/dist/aave.d.ts.map +1 -1
  3. package/dist/{ccip-BlV1Mry3.js → ccip-CXlshvBY.js} +1 -1
  4. package/dist/config.d.ts +1 -1
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/constants.d.ts +1 -0
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/error.d.ts +1 -0
  9. package/dist/error.d.ts.map +1 -1
  10. package/dist/estimate.d.ts +52 -0
  11. package/dist/estimate.d.ts.map +1 -1
  12. package/dist/{index-BNWCIGfQ.js → index-_QuyGrjU.js} +72332 -72246
  13. package/dist/index.js +2 -2
  14. package/dist/intents.d.ts +40 -0
  15. package/dist/intents.d.ts.map +1 -1
  16. package/dist/metaTxnMonitor.d.ts +3 -3
  17. package/dist/metaTxnMonitor.d.ts.map +1 -1
  18. package/dist/metaTxns.d.ts +3 -3
  19. package/dist/metaTxns.d.ts.map +1 -1
  20. package/dist/morpho.d.ts +8 -0
  21. package/dist/morpho.d.ts.map +1 -1
  22. package/dist/prepareSend.d.ts +16 -6
  23. package/dist/prepareSend.d.ts.map +1 -1
  24. package/dist/queryParams.d.ts.map +1 -1
  25. package/dist/relayer.d.ts +6 -6
  26. package/dist/relayer.d.ts.map +1 -1
  27. package/dist/sequenceWallet.d.ts +2 -2
  28. package/dist/sequenceWallet.d.ts.map +1 -1
  29. package/dist/tokens.d.ts.map +1 -1
  30. package/dist/wallets.d.ts.map +1 -1
  31. package/dist/widget/components/AccountActionsDropdown.d.ts.map +1 -1
  32. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  33. package/dist/widget/components/ClassicSwap.d.ts +2 -0
  34. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  35. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  36. package/dist/widget/components/ConnectedWallets.d.ts +4 -0
  37. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  38. package/dist/widget/components/Earn.d.ts.map +1 -1
  39. package/dist/widget/components/Fund.d.ts.map +1 -1
  40. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  41. package/dist/widget/components/{FundSendForm.d.ts → FundSwap.d.ts} +11 -5
  42. package/dist/widget/components/FundSwap.d.ts.map +1 -0
  43. package/dist/widget/components/FundingMethodSelectorButton.d.ts +4 -0
  44. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -0
  45. package/dist/widget/components/Modal.d.ts.map +1 -1
  46. package/dist/widget/components/Pay.d.ts.map +1 -1
  47. package/dist/widget/components/PercentageMaxButtons.d.ts +12 -0
  48. package/dist/widget/components/PercentageMaxButtons.d.ts.map +1 -0
  49. package/dist/widget/components/{PaySendForm.d.ts → PoolDeposit.d.ts} +11 -34
  50. package/dist/widget/components/PoolDeposit.d.ts.map +1 -0
  51. package/dist/widget/components/{SimpleSwap.d.ts → PoolWithdraw.d.ts} +16 -8
  52. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -0
  53. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  54. package/dist/widget/components/Receive.d.ts.map +1 -1
  55. package/dist/widget/components/RecipientSelectorButton.d.ts +4 -0
  56. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -0
  57. package/dist/widget/components/Recipients.d.ts.map +1 -1
  58. package/dist/widget/components/RequiredPropsError.d.ts +8 -0
  59. package/dist/widget/components/RequiredPropsError.d.ts.map +1 -0
  60. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  61. package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
  62. package/dist/widget/components/Swap.d.ts +1 -0
  63. package/dist/widget/components/Swap.d.ts.map +1 -1
  64. package/dist/widget/components/SwapSettings.d.ts.map +1 -1
  65. package/dist/widget/components/TokenImage.d.ts +1 -0
  66. package/dist/widget/components/TokenImage.d.ts.map +1 -1
  67. package/dist/widget/components/TokenList.d.ts.map +1 -1
  68. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  69. package/dist/widget/components/TokenSelectorButton.d.ts +16 -0
  70. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -0
  71. package/dist/widget/components/UserPreferences.d.ts.map +1 -1
  72. package/dist/widget/components/WaasFeeOptions.d.ts +8 -0
  73. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -0
  74. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  75. package/dist/widget/components/WalletList.d.ts.map +1 -1
  76. package/dist/widget/css/compiled.css +2 -0
  77. package/dist/widget/css/index.css +554 -0
  78. package/dist/widget/hooks/useBack.d.ts +1 -0
  79. package/dist/widget/hooks/useBack.d.ts.map +1 -1
  80. package/dist/widget/hooks/useCheckout.d.ts +1 -1
  81. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  82. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  83. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  84. package/dist/widget/hooks/useDefaultTokenSelection.d.ts +3 -3
  85. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  86. package/dist/widget/hooks/usePayMessage.d.ts.map +1 -1
  87. package/dist/widget/hooks/useSelectedFundMethod.d.ts +12 -0
  88. package/dist/widget/hooks/useSelectedFundMethod.d.ts.map +1 -0
  89. package/dist/widget/hooks/useSelectedRecipient.d.ts.map +1 -1
  90. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  91. package/dist/widget/index.js +1 -1
  92. package/dist/widget/widget.d.ts +4 -4
  93. package/dist/widget/widget.d.ts.map +1 -1
  94. package/package.json +18 -12
  95. package/src/aave.ts +32 -0
  96. package/src/config.ts +12 -4
  97. package/src/constants.ts +2 -0
  98. package/src/error.ts +19 -1
  99. package/src/estimate.ts +416 -5
  100. package/src/intents.ts +161 -11
  101. package/src/metaTxnMonitor.ts +3 -3
  102. package/src/metaTxns.ts +3 -5
  103. package/src/morpho.ts +32 -0
  104. package/src/prepareSend.ts +503 -166
  105. package/src/queryParams.ts +2 -1
  106. package/src/relayer.ts +11 -11
  107. package/src/sequenceWallet.ts +2 -2
  108. package/src/tokens.ts +7 -1
  109. package/src/wallets.ts +8 -0
  110. package/src/widget/compiled.css +2 -2
  111. package/src/widget/components/AccountActionsDropdown.tsx +3 -13
  112. package/src/widget/components/AccountSettings.tsx +6 -24
  113. package/src/widget/components/ClassicSwap.tsx +111 -155
  114. package/src/widget/components/ConnectWallet.tsx +4 -37
  115. package/src/widget/components/ConnectedWallets.tsx +113 -58
  116. package/src/widget/components/Earn.tsx +73 -589
  117. package/src/widget/components/Fund.tsx +31 -82
  118. package/src/widget/components/FundMethods.tsx +82 -159
  119. package/src/widget/components/FundSwap.tsx +52 -0
  120. package/src/widget/components/FundingMethodSelectorButton.tsx +60 -0
  121. package/src/widget/components/Modal.tsx +6 -2
  122. package/src/widget/components/Pay.tsx +183 -208
  123. package/src/widget/components/PercentageMaxButtons.tsx +77 -0
  124. package/src/widget/components/PoolDeposit.tsx +593 -0
  125. package/src/widget/components/PoolWithdraw.tsx +903 -0
  126. package/src/widget/components/QuoteDetails.tsx +22 -8
  127. package/src/widget/components/Receive.tsx +0 -2
  128. package/src/widget/components/RecipientSelectorButton.tsx +42 -0
  129. package/src/widget/components/Recipients.tsx +62 -156
  130. package/src/widget/components/RequiredPropsError.tsx +33 -0
  131. package/src/widget/components/ScreenHeader.tsx +5 -1
  132. package/src/widget/components/SlippageToleranceSettings.tsx +2 -1
  133. package/src/widget/components/Swap.tsx +2 -43
  134. package/src/widget/components/SwapSettings.tsx +2 -14
  135. package/src/widget/components/TokenImage.tsx +21 -4
  136. package/src/widget/components/TokenList.tsx +0 -1
  137. package/src/widget/components/TokenSelector.tsx +1 -0
  138. package/src/widget/components/TokenSelectorButton.tsx +75 -0
  139. package/src/widget/components/UserPreferences.tsx +6 -24
  140. package/src/widget/components/WaasFeeOptions.tsx +331 -0
  141. package/src/widget/components/WalletConfirmation.tsx +55 -3
  142. package/src/widget/components/WalletList.tsx +4 -2
  143. package/src/widget/hooks/useBack.tsx +2 -0
  144. package/src/widget/hooks/useCheckout.ts +36 -20
  145. package/src/widget/hooks/useCurrentScreen.tsx +1 -0
  146. package/src/widget/hooks/useDefaultTokenSelection.tsx +104 -28
  147. package/src/widget/hooks/usePayMessage.tsx +86 -11
  148. package/src/widget/hooks/useSelectedFundMethod.tsx +41 -0
  149. package/src/widget/hooks/useSelectedRecipient.tsx +10 -0
  150. package/src/widget/hooks/useSendForm.ts +24 -2
  151. package/src/widget/index.css +27 -0
  152. package/src/widget/widget.tsx +169 -111
  153. package/dist/widget/components/FundSendForm.d.ts.map +0 -1
  154. package/dist/widget/components/PaySendForm.d.ts.map +0 -1
  155. package/dist/widget/components/SimpleSwap.d.ts.map +0 -1
  156. package/dist/widget/hooks/useSwapSettings.d.ts +0 -16
  157. package/dist/widget/hooks/useSwapSettings.d.ts.map +0 -1
  158. package/src/widget/components/FundSendForm.tsx +0 -903
  159. package/src/widget/components/PaySendForm.tsx +0 -869
  160. package/src/widget/components/SimpleSwap.tsx +0 -983
  161. package/src/widget/hooks/useSwapSettings.tsx +0 -100
@@ -1,19 +1,28 @@
1
1
  import React, { useMemo } from "react"
2
2
  import { ChainImage } from "./ChainImage.js"
3
3
  import { getCommonTokenImageUrl } from "../../tokens.js"
4
+ import { logger } from "../../logger.js"
4
5
 
5
6
  function normalizeImageUrl(
6
7
  imageUrl?: string | null,
7
8
  symbol?: string | null,
8
9
  chainId?: number | null,
10
+ contractAddress?: string | null,
9
11
  ) {
10
12
  if (symbol === "ETH") {
11
13
  return "https://assets.sequence.info/images/tokens/large/1/0x0000000000000000000000000000000000000000.webp"
12
14
  }
15
+ const commonImageUrl = getCommonTokenImageUrl({
16
+ symbol,
17
+ chainId,
18
+ contractAddress,
19
+ })
20
+ if (commonImageUrl) {
21
+ return commonImageUrl
22
+ }
23
+
13
24
  if (imageUrl) {
14
25
  return imageUrl.replace("/small/", "/large/")
15
- } else if (symbol) {
16
- return getCommonTokenImageUrl({ symbol, chainId })
17
26
  }
18
27
 
19
28
  return null
@@ -23,6 +32,7 @@ interface TokenImageProps {
23
32
  imageUrl?: string | null
24
33
  symbol?: string | null
25
34
  chainId?: number | null
35
+ contractAddress?: string | null
26
36
  size?: number
27
37
  }
28
38
 
@@ -30,12 +40,19 @@ export const TokenImage: React.FC<TokenImageProps> = ({
30
40
  imageUrl,
31
41
  symbol,
32
42
  chainId,
43
+ contractAddress,
33
44
  size = 24,
34
45
  }) => {
35
46
  const [imageError, setImageError] = React.useState(false)
36
47
  const effectiveImageUrl = useMemo(() => {
37
- return normalizeImageUrl(imageUrl, symbol, chainId)
38
- }, [imageUrl, symbol, chainId])
48
+ logger.console.log("[TokenImage] normalizeImageUrl", {
49
+ imageUrl,
50
+ symbol,
51
+ chainId,
52
+ contractAddress,
53
+ })
54
+ return normalizeImageUrl(imageUrl, symbol, chainId, contractAddress)
55
+ }, [imageUrl, symbol, chainId, contractAddress])
39
56
 
40
57
  const displaySymbol = symbol?.[0]?.toUpperCase() || "?"
41
58
 
@@ -72,7 +72,6 @@ export const TokenList: React.FC<TokenListProps> = ({
72
72
  <div className="space-y-2">
73
73
  <ScreenHeader
74
74
  onBack={mode !== "fund" ? onBack : undefined}
75
- showAccountActions={true}
76
75
  headerContent={
77
76
  mode === "fund"
78
77
  ? "Fund with any token"
@@ -295,6 +295,7 @@ export const TokenSelector: React.FC<TokenSelectorProps> = ({
295
295
  symbol={symbol}
296
296
  imageUrl={imageUrl}
297
297
  chainId={chainId}
298
+ contractAddress={contractAddress}
298
299
  size={32}
299
300
  />
300
301
  ) : (
@@ -0,0 +1,75 @@
1
+ import { ChevronRight } from "lucide-react"
2
+ import type React from "react"
3
+ import { useMemo } from "react"
4
+ import { TokenImage } from "./TokenImage.js"
5
+ import { getChainInfo } from "../../chains.js"
6
+
7
+ interface TokenSelectorButtonProps {
8
+ token?: {
9
+ symbol: string
10
+ imageUrl?: string
11
+ contractAddress?: string
12
+ chainId?: number | null
13
+ } | null
14
+ chainId?: number | null
15
+ onSelect: () => void
16
+ className?: string
17
+ unselectable?: boolean
18
+ }
19
+
20
+ export const TokenSelectorButton: React.FC<TokenSelectorButtonProps> = ({
21
+ token,
22
+ chainId,
23
+ onSelect,
24
+ className = "flex items-center space-x-2 trails-border-radius-input px-2.5 py-1.5 border transition-colors",
25
+ unselectable = false,
26
+ }) => {
27
+ const displayChainId = token?.chainId || chainId
28
+
29
+ const chainInfo = useMemo(() => {
30
+ return displayChainId ? getChainInfo(displayChainId) : null
31
+ }, [displayChainId])
32
+
33
+ return (
34
+ <button
35
+ type="button"
36
+ onClick={unselectable ? undefined : onSelect}
37
+ disabled={unselectable}
38
+ className={`${className} ${
39
+ token
40
+ ? "trails-bg-card hover:trails-hover-bg trails-border-primary"
41
+ : "bg-blue-500 hover:bg-blue-600 border-blue-500 text-white"
42
+ } ${unselectable ? "cursor-default" : "cursor-pointer"}`}
43
+ >
44
+ {token ? (
45
+ <>
46
+ <TokenImage
47
+ symbol={token.symbol}
48
+ imageUrl={token.imageUrl}
49
+ chainId={displayChainId}
50
+ contractAddress={token.contractAddress}
51
+ size={28}
52
+ />
53
+ <div className="flex flex-col items-start">
54
+ <span className="font-bold trails-text-primary text-sm">
55
+ {token.symbol}
56
+ </span>
57
+ {chainInfo && (
58
+ <span className="text-xs trails-text-muted">
59
+ {chainInfo.name}
60
+ </span>
61
+ )}
62
+ </div>
63
+ {!unselectable && (
64
+ <ChevronRight className="w-3.5 h-3.5 trails-text-muted" />
65
+ )}
66
+ </>
67
+ ) : (
68
+ <>
69
+ <span className="font-medium text-sm text-white">Select Token</span>
70
+ {!unselectable && <ChevronRight className="w-3.5 h-3.5 text-white" />}
71
+ </>
72
+ )}
73
+ </button>
74
+ )
75
+ }
@@ -1,10 +1,10 @@
1
1
  import { ChevronDown, RotateCcw } from "lucide-react"
2
2
  import type React from "react"
3
3
  import { useBalanceVisible } from "../hooks/useBalanceVisible.js"
4
- import { useSwapSettings } from "../hooks/useSwapSettings.js"
5
4
  import { useThemePreference } from "../hooks/useTheme.js"
6
5
  import { useWidgetProps } from "../hooks/useWidgetProps.js"
7
6
  import { ScreenHeader } from "./ScreenHeader.js"
7
+ import { SlippageToleranceSettings } from "./SlippageToleranceSettings.js"
8
8
  import { logger } from "../../logger.js"
9
9
 
10
10
  interface UserPreferencesProps {
@@ -14,7 +14,6 @@ interface UserPreferencesProps {
14
14
  export const UserPreferences: React.FC<UserPreferencesProps> = ({ onBack }) => {
15
15
  const { isBalanceVisible, toggleBalanceVisible, resetBalanceVisible } =
16
16
  useBalanceVisible()
17
- const { resetSwapSettings } = useSwapSettings()
18
17
  const { selectedTheme, setSelectedTheme, resetThemePreference } =
19
18
  useThemePreference()
20
19
  const { customCss, theme: widgetTheme } = useWidgetProps()
@@ -31,7 +30,6 @@ export const UserPreferences: React.FC<UserPreferencesProps> = ({ onBack }) => {
31
30
  try {
32
31
  // Reset all preferences using their respective hook methods
33
32
  resetBalanceVisible()
34
- resetSwapSettings()
35
33
  resetThemePreference()
36
34
 
37
35
  logger.console.log(
@@ -51,7 +49,6 @@ export const UserPreferences: React.FC<UserPreferencesProps> = ({ onBack }) => {
51
49
  headerContent="User Preferences"
52
50
  headerContentAlign="left"
53
51
  onBack={onBack}
54
- showAccountActions={false}
55
52
  />
56
53
 
57
54
  <div className="space-y-6">
@@ -75,26 +72,6 @@ export const UserPreferences: React.FC<UserPreferencesProps> = ({ onBack }) => {
75
72
  </button>
76
73
  </div>
77
74
 
78
- {/* Simple Swap UI Setting */}
79
- {/* <div className="flex items-center justify-between">
80
- <span className="text-sm font-medium text-gray-900 dark:text-gray-100">
81
- Simple Swap UI
82
- </span>
83
- <button
84
- type="button"
85
- onClick={toggleSimpleSwapMode}
86
- className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
87
- isSimpleSwapMode ? "bg-blue-600" : "bg-gray-200 dark:bg-gray-700"
88
- }`}
89
- >
90
- <span
91
- className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
92
- isSimpleSwapMode ? "translate-x-6" : "translate-x-1"
93
- }`}
94
- />
95
- </button>
96
- </div> */}
97
-
98
75
  {/* Theme Setting */}
99
76
  <div className="flex items-center justify-between">
100
77
  <span
@@ -136,6 +113,11 @@ export const UserPreferences: React.FC<UserPreferencesProps> = ({ onBack }) => {
136
113
  </div>
137
114
  </div>
138
115
 
116
+ {/* Slippage Tolerance Settings */}
117
+ <div className="pt-2">
118
+ <SlippageToleranceSettings />
119
+ </div>
120
+
139
121
  {/* Reset Preferences Button */}
140
122
  <div className="pt-4 dark:border-gray-700">
141
123
  <button
@@ -0,0 +1,331 @@
1
+ import { useEffect, useState } from "react"
2
+ import { useWaasFeeOptions } from "@0xsequence/connect"
3
+ import { TokenImage } from "./TokenImage.js"
4
+ import { ChevronDown, ChevronUp } from "lucide-react"
5
+ import { formatUnits } from "viem"
6
+
7
+ // Define types based on the @0xsequence/connect documentation
8
+ interface FeeToken {
9
+ symbol: string
10
+ contractAddress?: string
11
+ name?: string
12
+ decimals?: number
13
+ imageUrl?: string
14
+ }
15
+
16
+ interface FeeOption {
17
+ token: FeeToken
18
+ amount?: string
19
+ amountUSD?: number
20
+ }
21
+
22
+ interface FeeOptionExtended extends FeeOption {
23
+ balance: string
24
+ balanceFormatted: string
25
+ hasEnoughBalanceForFee: boolean
26
+ }
27
+
28
+ interface WaasFeeOptionsProps {
29
+ chainId?: number
30
+ setIsFeeOptionConfirmed: (isFeeOptionConfirmed: boolean) => void
31
+ onFeeOptionsLoaded?: () => void
32
+ }
33
+
34
+ export const WaasFeeOptions: React.FC<WaasFeeOptionsProps> = ({
35
+ chainId,
36
+ setIsFeeOptionConfirmed,
37
+ onFeeOptionsLoaded,
38
+ }) => {
39
+ const [
40
+ pendingFeeOptionConfirmation,
41
+ confirmPendingFeeOption,
42
+ rejectPendingFeeOption,
43
+ ] = useWaasFeeOptions({ chainIdOverride: chainId })
44
+
45
+ const [selectedFeeOptionTokenName, setSelectedFeeOptionTokenName] =
46
+ useState<string>()
47
+ const [isExpanded, setIsExpanded] = useState(false)
48
+ const [isProcessing, setIsProcessing] = useState(false)
49
+ const [isLoading, setIsLoading] = useState(false)
50
+
51
+ // Debug logging
52
+ useEffect(() => {
53
+ console.log("[trails-sdk] WaasFeeOptions component mounted/updated:", {
54
+ chainId,
55
+ pendingFeeOptionConfirmation: !!pendingFeeOptionConfirmation,
56
+ optionsCount: pendingFeeOptionConfirmation?.options?.length || 0,
57
+ hasOptions: !!pendingFeeOptionConfirmation?.options,
58
+ confirmationId: pendingFeeOptionConfirmation?.id,
59
+ isLoading,
60
+ isProcessing,
61
+ })
62
+ }, [chainId, pendingFeeOptionConfirmation, isLoading, isProcessing])
63
+
64
+ // Log when component renders but has no pending confirmation
65
+ useEffect(() => {
66
+ if (!pendingFeeOptionConfirmation) {
67
+ console.log(
68
+ "[trails-sdk] WaasFeeOptions: No pending fee confirmation - this is normal until a transaction requires fee payment",
69
+ )
70
+ }
71
+ }, [pendingFeeOptionConfirmation])
72
+
73
+ // Manage loading state
74
+ useEffect(() => {
75
+ if (
76
+ pendingFeeOptionConfirmation &&
77
+ pendingFeeOptionConfirmation.options?.length > 0
78
+ ) {
79
+ setIsLoading(false)
80
+ } else if (
81
+ pendingFeeOptionConfirmation &&
82
+ (!pendingFeeOptionConfirmation.options ||
83
+ pendingFeeOptionConfirmation.options.length === 0)
84
+ ) {
85
+ setIsLoading(true)
86
+ }
87
+ }, [pendingFeeOptionConfirmation])
88
+
89
+ // Initialize with first option when fee options become available
90
+ useEffect(() => {
91
+ if (pendingFeeOptionConfirmation) {
92
+ console.log(
93
+ "[trails-sdk] Pending fee options: ",
94
+ pendingFeeOptionConfirmation.options,
95
+ )
96
+
97
+ // Notify parent that fee options are loaded
98
+ if (
99
+ onFeeOptionsLoaded &&
100
+ pendingFeeOptionConfirmation.options?.length > 0
101
+ ) {
102
+ onFeeOptionsLoaded()
103
+ }
104
+
105
+ // Select the first fee option by default
106
+ if (pendingFeeOptionConfirmation.options.length > 0) {
107
+ const firstOption = pendingFeeOptionConfirmation.options.filter(
108
+ (option: any) => option.hasEnoughBalanceForFee,
109
+ )[0]
110
+ if (firstOption?.token?.symbol) {
111
+ setSelectedFeeOptionTokenName(firstOption.token.symbol)
112
+ }
113
+ }
114
+ }
115
+ }, [pendingFeeOptionConfirmation, onFeeOptionsLoaded])
116
+
117
+ // Handle fee option selection and confirmation
118
+ const handleConfirmFee = async (tokenAddress: string | null) => {
119
+ if (pendingFeeOptionConfirmation && !isProcessing) {
120
+ setIsProcessing(true)
121
+ try {
122
+ confirmPendingFeeOption(pendingFeeOptionConfirmation.id, tokenAddress)
123
+ setIsFeeOptionConfirmed(true)
124
+ } finally {
125
+ setIsProcessing(false)
126
+ }
127
+ }
128
+ }
129
+
130
+ // Handle fee option rejection
131
+ const handleRejectFee = async () => {
132
+ if (pendingFeeOptionConfirmation && !isProcessing) {
133
+ setIsProcessing(true)
134
+ try {
135
+ rejectPendingFeeOption(pendingFeeOptionConfirmation.id)
136
+ } finally {
137
+ setIsProcessing(false)
138
+ }
139
+ }
140
+ }
141
+
142
+ // Don't render if no pending fee confirmation
143
+ if (!pendingFeeOptionConfirmation) {
144
+ return null
145
+ }
146
+
147
+ // Show loading when we have confirmation but no options yet
148
+ if (isLoading) {
149
+ return (
150
+ <div className="flex items-center justify-center py-8">
151
+ <div className="flex flex-col items-center space-y-3">
152
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-trails-primary"></div>
153
+ <p className="text-sm trails-text-muted">Loading fee options...</p>
154
+ </div>
155
+ </div>
156
+ )
157
+ }
158
+
159
+ const selectedOption = pendingFeeOptionConfirmation.options
160
+ .filter((option: any) => option.hasEnoughBalanceForFee)
161
+ .find((option) => option.token.symbol === selectedFeeOptionTokenName) as
162
+ | FeeOptionExtended
163
+ | undefined
164
+
165
+ return (
166
+ <div className="space-y-2">
167
+ {/* Header */}
168
+ <div className="flex items-center justify-between">
169
+ <div className="text-sm font-medium trails-text-primary">
170
+ Select Fee Payment Token
171
+ </div>
172
+ <button
173
+ type="button"
174
+ onClick={() => setIsExpanded(!isExpanded)}
175
+ className="flex items-center space-x-1 text-xs trails-text-muted hover:trails-text-primary transition-colors cursor-pointer"
176
+ >
177
+ <span>{isExpanded ? "Hide" : "Show"} options</span>
178
+ {isExpanded ? (
179
+ <ChevronUp className="w-3 h-3" />
180
+ ) : (
181
+ <ChevronDown className="w-3 h-3" />
182
+ )}
183
+ </button>
184
+ </div>
185
+
186
+ {/* Selected Option Display */}
187
+ {selectedOption?.hasEnoughBalanceForFee && (
188
+ <div className="trails-bg-secondary trails-border-radius-container p-3">
189
+ <div className="flex items-center justify-between">
190
+ <div className="flex items-center space-x-2">
191
+ <TokenImage
192
+ symbol={selectedOption.token.symbol}
193
+ imageUrl={(selectedOption.token as any).logoURL}
194
+ chainId={chainId}
195
+ size={20}
196
+ />
197
+ <div className="text-left">
198
+ <div className="font-medium trails-text-primary text-sm">
199
+ {selectedOption.token.symbol}
200
+ </div>
201
+ <div className="text-xs trails-text-muted">
202
+ {selectedOption.token.contractAddress
203
+ ? `${selectedOption.token.contractAddress.slice(0, 6)}...${selectedOption.token.contractAddress.slice(-4)}`
204
+ : "Native Token"}
205
+ </div>
206
+ </div>
207
+ </div>
208
+ {/* Display balance info if available */}
209
+ {selectedOption && "balanceFormatted" in selectedOption && (
210
+ <div className="text-right">
211
+ <div className="text-xs trails-text-muted">
212
+ Balance: {String(selectedOption.balanceFormatted)}
213
+ </div>
214
+ {!selectedOption.hasEnoughBalanceForFee && (
215
+ <div className="text-xs text-red-500">
216
+ Insufficient balance
217
+ </div>
218
+ )}
219
+ </div>
220
+ )}
221
+ </div>
222
+ </div>
223
+ )}
224
+
225
+ {/* Expanded Options */}
226
+ {isExpanded && (
227
+ <div className="space-y-2">
228
+ {pendingFeeOptionConfirmation.options
229
+ .filter((option: any) => option.hasEnoughBalanceForFee)
230
+ .map((option) => (
231
+ <label
232
+ key={option.token.symbol || option.token.contractAddress}
233
+ className={`flex items-center space-x-3 p-3 rounded-lg border cursor-pointer transition-colors ${
234
+ selectedFeeOptionTokenName === option.token.symbol
235
+ ? "trails-bg-primary/10 border-trails-primary"
236
+ : "trails-bg-secondary hover:trails-hover-bg border-transparent"
237
+ }`}
238
+ >
239
+ <input
240
+ type="radio"
241
+ name="feeOption"
242
+ checked={selectedFeeOptionTokenName === option.token.symbol}
243
+ onChange={() =>
244
+ setSelectedFeeOptionTokenName(option.token.symbol)
245
+ }
246
+ className="w-4 h-4 text-trails-primary focus:ring-trails-primary"
247
+ />
248
+ <div className="flex items-center space-x-2 flex-1">
249
+ <TokenImage
250
+ symbol={option.token.symbol}
251
+ imageUrl={(option.token as any).logoURL}
252
+ chainId={chainId}
253
+ size={20}
254
+ />
255
+ <div className="flex-1 text-left">
256
+ <div className="font-medium trails-text-primary text-sm">
257
+ {option.token.symbol}
258
+ </div>
259
+ <div className="text-xs trails-text-muted">
260
+ {option.token.contractAddress
261
+ ? `${option.token.contractAddress.slice(0, 6)}...${option.token.contractAddress.slice(-4)}`
262
+ : "Native Token"}
263
+ </div>
264
+ </div>
265
+ </div>
266
+ {/* Display balance info if available */}
267
+ {"balanceFormatted" in option && (
268
+ <div className="text-right">
269
+ <div className="text-xs trails-text-muted">
270
+ Balance: {String(option.balanceFormatted)}
271
+ </div>
272
+ <div className="text-xs trails-text-muted">
273
+ Cost:{" "}
274
+ {String(
275
+ formatUnits(
276
+ BigInt(option.value),
277
+ option.token.decimals || 18,
278
+ ),
279
+ )}
280
+ </div>
281
+ {!("hasEnoughBalanceForFee" in option) ||
282
+ !option.hasEnoughBalanceForFee ? (
283
+ <div className="text-xs text-red-500">Insufficient</div>
284
+ ) : null}
285
+ </div>
286
+ )}
287
+ </label>
288
+ ))}
289
+ </div>
290
+ )}
291
+
292
+ {/* Action Buttons */}
293
+ <div className="flex space-x-3">
294
+ <button
295
+ type="button"
296
+ onClick={() =>
297
+ handleConfirmFee(selectedOption?.token.contractAddress || null)
298
+ }
299
+ disabled={!selectedOption || isProcessing}
300
+ className="flex-1 py-3 px-6 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-300 disabled:text-gray-500 text-white font-semibold rounded-lg transition-all duration-200 cursor-pointer disabled:cursor-not-allowed shadow-sm hover:shadow-md disabled:shadow-none flex items-center justify-center"
301
+ >
302
+ {isProcessing ? (
303
+ <>
304
+ <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
305
+ Processing...
306
+ </>
307
+ ) : (
308
+ "Confirm Fee"
309
+ )}
310
+ </button>
311
+ <button
312
+ type="button"
313
+ onClick={handleRejectFee}
314
+ disabled={isProcessing}
315
+ className="px-6 py-3 bg-gray-100 hover:bg-gray-200 disabled:bg-gray-50 text-gray-700 font-semibold rounded-lg transition-all duration-200 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50 flex items-center justify-center border border-gray-300"
316
+ >
317
+ {isProcessing ? (
318
+ <>
319
+ <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-gray-500 mr-2"></div>
320
+ Processing...
321
+ </>
322
+ ) : (
323
+ "Cancel"
324
+ )}
325
+ </button>
326
+ </div>
327
+ </div>
328
+ )
329
+ }
330
+
331
+ export default WaasFeeOptions
@@ -1,9 +1,11 @@
1
1
  import { TokenImage } from "./TokenImage.js"
2
2
  import { ChevronLeft } from "lucide-react"
3
3
  import type React from "react"
4
- import { useEffect, useState } from "react"
4
+ import { useEffect, useMemo, useState } from "react"
5
5
  import type { PrepareSendQuote } from "../../prepareSend.js"
6
6
  import { QuoteDetails } from "./QuoteDetails.js"
7
+ import { WaasFeeOptions } from "./WaasFeeOptions.js"
8
+ import { useAccount } from "wagmi"
7
9
 
8
10
  interface WalletConfirmationProps {
9
11
  onBack?: () => void
@@ -21,6 +23,9 @@ export const WalletConfirmation: React.FC<WalletConfirmationProps> = ({
21
23
  }) => {
22
24
  const [showContent, setShowContent] = useState(false)
23
25
  const [showTimeoutWarning, setShowTimeoutWarning] = useState(false)
26
+ const [isFeeOptionConfirmed, setIsFeeOptionConfirmed] = useState(false)
27
+ const [areFeeOptionsLoaded, setAreFeeOptionsLoaded] = useState(false)
28
+ const { connector } = useAccount()
24
29
 
25
30
  useEffect(() => {
26
31
  setShowContent(true)
@@ -39,6 +44,25 @@ export const WalletConfirmation: React.FC<WalletConfirmationProps> = ({
39
44
  return () => clearTimeout(timer)
40
45
  }, [retryEnabled])
41
46
 
47
+ const isSequenceWaas = useMemo(() => {
48
+ if (!connector) return false
49
+ const connectorName = connector.name?.toLowerCase() || ""
50
+ const connectorId = connector.id?.toLowerCase() || ""
51
+ const isSequenceWaas =
52
+ connectorName.includes("waas") ||
53
+ connectorId.includes("waas") ||
54
+ connectorId === "sequence-waas"
55
+
56
+ console.log("[trails-sdk] Sequence WaaS detection:", {
57
+ connectorName,
58
+ connectorId,
59
+ isSequenceWaas,
60
+ connector: connector.name,
61
+ })
62
+
63
+ return isSequenceWaas
64
+ }, [connector])
65
+
42
66
  return (
43
67
  <div className="space-y-6">
44
68
  <div className="flex items-center relative">
@@ -67,6 +91,7 @@ export const WalletConfirmation: React.FC<WalletConfirmationProps> = ({
67
91
  imageUrl={quote?.originToken.imageUrl}
68
92
  symbol={quote?.originToken.symbol}
69
93
  chainId={quote?.originChain.id}
94
+ contractAddress={quote?.originToken.contractAddress}
70
95
  size={64}
71
96
  />
72
97
  </div>
@@ -76,12 +101,39 @@ export const WalletConfirmation: React.FC<WalletConfirmationProps> = ({
76
101
  className={`mb-2 transition-all duration-500 ease-out ${showContent ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}
77
102
  >
78
103
  <h2 className="text-xl font-bold text-gray-900 dark:text-white">
79
- {retryEnabled ? "Try again" : "Waiting for wallet…"}
104
+ {retryEnabled
105
+ ? "Try again"
106
+ : !isSequenceWaas
107
+ ? "Waiting for wallet…"
108
+ : isFeeOptionConfirmed
109
+ ? "Waiting for wallet…"
110
+ : areFeeOptionsLoaded
111
+ ? "Waiting for fee selection…"
112
+ : "Waiting for fee options…"}
80
113
  </h2>
81
114
  <p className="mt-2 text-sm text-gray-600 dark:text-gray-300">
82
- Please approve the request in your wallet
115
+ {!isSequenceWaas
116
+ ? "Please approve the request in your wallet"
117
+ : isFeeOptionConfirmed
118
+ ? "Please approve the request in your wallet"
119
+ : ""}
83
120
  </p>
84
121
  </div>
122
+
123
+ {isSequenceWaas && (
124
+ <div
125
+ className={`mb-2 mt-4 transition-all duration-500 ease-out ${showContent ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}
126
+ >
127
+ <WaasFeeOptions
128
+ chainId={quote?.originChain.id}
129
+ setIsFeeOptionConfirmed={setIsFeeOptionConfirmed}
130
+ onFeeOptionsLoaded={() => {
131
+ console.log("[trails-sdk] Fee options loaded callback called")
132
+ setAreFeeOptionsLoaded(true)
133
+ }}
134
+ />
135
+ </div>
136
+ )}
85
137
  </div>
86
138
 
87
139
  {/* Timeout Warning */}
@@ -21,7 +21,9 @@ export const WalletList: React.FC<WalletListProps> = ({
21
21
 
22
22
  const prefilteredWalletOptions = useMemo(() => {
23
23
  return walletOptions
24
- .filter((wallet) => wallet.id !== "injected")
24
+ .filter(
25
+ (wallet) => wallet.id !== "injected" && wallet.id !== "sequence-waas",
26
+ )
25
27
  .sort((a, b) => {
26
28
  const aIndex = topShownWallets.indexOf(a.id)
27
29
  const bIndex = topShownWallets.indexOf(b.id)
@@ -61,7 +63,7 @@ export const WalletList: React.FC<WalletListProps> = ({
61
63
  return (
62
64
  <div className="space-y-6">
63
65
  <ScreenHeader
64
- headerContent="More Wallets"
66
+ headerContent="Connect a Wallet"
65
67
  headerContentAlign="left"
66
68
  onBack={onBack}
67
69
  />
@@ -20,6 +20,7 @@ interface BackContextType {
20
20
  setAccountSettingsSource: (screen: Screen) => void
21
21
  getAccountSettingsSource: () => Screen | null
22
22
  setCurrentScreenWithBack: (screen: Screen, backTo?: Screen) => void
23
+ getPreviousScreen: () => Screen | null
23
24
  }
24
25
 
25
26
  const BackContext = createContext<BackContextType | null>(null)
@@ -194,6 +195,7 @@ export function BackProvider({ children }: BackProviderProps) {
194
195
  setAccountSettingsSource: () => {}, // Deprecated, kept for compatibility
195
196
  getAccountSettingsSource: () => null, // Deprecated, kept for compatibility
196
197
  setCurrentScreenWithBack,
198
+ getPreviousScreen,
197
199
  }
198
200
 
199
201
  return <BackContext.Provider value={value}>{children}</BackContext.Provider>