0xtrails 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics.d.ts +1 -0
- package/dist/analytics.d.ts.map +1 -1
- package/dist/{ccip-D6ToCrWc.js → ccip-BbfANth7.js} +1 -1
- package/dist/chains.d.ts.map +1 -1
- package/dist/config.d.ts +1 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +2 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/gasless.d.ts +19 -7
- package/dist/gasless.d.ts.map +1 -1
- package/dist/{index-BqgeTLL8.js → index-WpIVoh3X.js} +27626 -26572
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -68
- package/dist/indexerClient.d.ts +10 -0
- package/dist/indexerClient.d.ts.map +1 -1
- package/dist/intentEntrypoint.d.ts +40 -14
- package/dist/intentEntrypoint.d.ts.map +1 -1
- package/dist/intents.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +11 -8
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/relayer.d.ts.map +1 -1
- package/dist/trails.d.ts.map +1 -1
- package/dist/trailsClient.d.ts.map +1 -1
- package/dist/trailsRouter.d.ts +22 -0
- package/dist/trailsRouter.d.ts.map +1 -0
- package/dist/transactions.d.ts +0 -1
- package/dist/transactions.d.ts.map +1 -1
- package/dist/widget/components/AccountSettings.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
- package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
- package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
- package/dist/widget/components/Earn.d.ts.map +1 -1
- package/dist/widget/components/FeeOption.d.ts +22 -0
- package/dist/widget/components/FeeOption.d.ts.map +1 -0
- package/dist/widget/components/FeeOptions.d.ts +13 -17
- package/dist/widget/components/FeeOptions.d.ts.map +1 -1
- package/dist/widget/components/Fund.d.ts.map +1 -1
- package/dist/widget/components/FundMethods.d.ts +1 -1
- package/dist/widget/components/FundMethods.d.ts.map +1 -1
- package/dist/widget/components/FundSendForm.d.ts.map +1 -1
- package/dist/widget/components/MeshConnectExchanges.d.ts +5 -2
- package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
- package/dist/widget/components/MeshConnectFlow.d.ts +2 -0
- package/dist/widget/components/MeshConnectFlow.d.ts.map +1 -1
- package/dist/widget/components/NativeGasOption.d.ts +12 -0
- package/dist/widget/components/NativeGasOption.d.ts.map +1 -0
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/PaySendForm.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/TokenSelector.d.ts.map +1 -1
- package/dist/widget/components/UserPreferences.d.ts.map +1 -1
- package/dist/widget/hooks/useBack.d.ts +2 -0
- package/dist/widget/hooks/useBack.d.ts.map +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
- package/dist/widget/hooks/useSelectedFeeToken.d.ts +32 -0
- package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +1 -0
- package/dist/widget/hooks/useSelectedMeshExchange.d.ts +14 -0
- package/dist/widget/hooks/useSelectedMeshExchange.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +8 -13
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/index.js +1 -1
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +29 -28
- package/src/analytics.ts +6 -0
- package/src/chains.ts +10 -0
- package/src/config.ts +25 -10
- package/src/constants.ts +11 -10
- package/src/gasless.ts +162 -109
- package/src/index.ts +1 -1
- package/src/indexerClient.ts +73 -1
- package/src/intentEntrypoint.ts +66 -101
- package/src/intents.ts +0 -2
- package/src/prepareSend.ts +1409 -887
- package/src/relayer.ts +4 -3
- package/src/trails.ts +1 -3
- package/src/trailsClient.ts +4 -1
- package/src/{balanceInjector.ts → trailsRouter.ts} +14 -14
- package/src/transactions.ts +4 -54
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/AccountSettings.tsx +7 -1
- package/src/widget/components/ClassicSwap.tsx +173 -175
- package/src/widget/components/ConfigDisplay.tsx +34 -1
- package/src/widget/components/ConnectWallet.tsx +168 -11
- package/src/widget/components/ConnectedWallets.tsx +184 -102
- package/src/widget/components/DebugToast.tsx +3 -3
- package/src/widget/components/Earn.tsx +4 -27
- package/src/widget/components/FeeOption.tsx +78 -0
- package/src/widget/components/FeeOptions.tsx +192 -127
- package/src/widget/components/Fund.tsx +18 -27
- package/src/widget/components/FundMethods.tsx +3 -3
- package/src/widget/components/FundSendForm.tsx +0 -33
- package/src/widget/components/MeshConnectExchanges.tsx +32 -3
- package/src/widget/components/MeshConnectFlow.tsx +23 -4
- package/src/widget/components/NativeGasOption.tsx +99 -0
- package/src/widget/components/Pay.tsx +36 -32
- package/src/widget/components/PaySendForm.tsx +0 -37
- package/src/widget/components/QuoteDetails.tsx +0 -29
- package/src/widget/components/TokenSelector.tsx +11 -0
- package/src/widget/components/TransferPendingVertical.tsx +1 -1
- package/src/widget/components/UserPreferences.tsx +3 -4
- package/src/widget/hooks/useBack.tsx +4 -0
- package/src/widget/hooks/useCurrentScreen.tsx +1 -0
- package/src/widget/hooks/useDefaultTokenSelection.tsx +3 -7
- package/src/widget/hooks/useSelectedFeeToken.tsx +299 -0
- package/src/widget/hooks/useSelectedMeshExchange.tsx +46 -0
- package/src/widget/hooks/useSendForm.ts +78 -23
- package/src/widget/widget.tsx +173 -111
- package/dist/balanceInjector.d.ts +0 -22
- package/dist/balanceInjector.d.ts.map +0 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type React from "react"
|
|
2
|
+
import { TokenImage } from "./TokenImage.js"
|
|
3
|
+
|
|
4
|
+
export type FeeOption = {
|
|
5
|
+
tokenAddress: string
|
|
6
|
+
tokenSymbol: string
|
|
7
|
+
tokenDecimals: number
|
|
8
|
+
amount: string
|
|
9
|
+
amountUSD: number
|
|
10
|
+
amountFormatted: string
|
|
11
|
+
amountUsdDisplay: string
|
|
12
|
+
tokenImageUrl?: string
|
|
13
|
+
notEnoughBalance?: boolean
|
|
14
|
+
chainId?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface FeeOptionProps {
|
|
18
|
+
option: FeeOption
|
|
19
|
+
isSelected: boolean
|
|
20
|
+
onClick: () => void
|
|
21
|
+
chainId?: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const FeeOption: React.FC<FeeOptionProps> = ({
|
|
25
|
+
option,
|
|
26
|
+
isSelected,
|
|
27
|
+
onClick,
|
|
28
|
+
chainId,
|
|
29
|
+
}) => {
|
|
30
|
+
return (
|
|
31
|
+
<button
|
|
32
|
+
type="button"
|
|
33
|
+
className={`w-full flex items-center justify-between p-1.5 trails-border-radius-input border transition-colors ${
|
|
34
|
+
option.notEnoughBalance
|
|
35
|
+
? "cursor-not-allowed opacity-50 bg-gray-100 dark:bg-gray-800 border-gray-300 dark:border-gray-600"
|
|
36
|
+
: isSelected
|
|
37
|
+
? "cursor-pointer trails-bg-primary/10 border-blue-500 bg-blue-50 dark:bg-blue-900/20"
|
|
38
|
+
: "cursor-pointer trails-bg-card trails-border-primary hover:trails-hover-bg"
|
|
39
|
+
}`}
|
|
40
|
+
onClick={option.notEnoughBalance ? undefined : onClick}
|
|
41
|
+
disabled={option.notEnoughBalance}
|
|
42
|
+
>
|
|
43
|
+
<div className="flex items-center space-x-2">
|
|
44
|
+
<TokenImage
|
|
45
|
+
imageUrl={option.tokenImageUrl}
|
|
46
|
+
symbol={option.tokenSymbol}
|
|
47
|
+
chainId={option.chainId || chainId}
|
|
48
|
+
size={16}
|
|
49
|
+
/>
|
|
50
|
+
<div className="text-left">
|
|
51
|
+
<div
|
|
52
|
+
className={`text-xs font-medium ${
|
|
53
|
+
option.notEnoughBalance
|
|
54
|
+
? "text-gray-400 dark:text-gray-500"
|
|
55
|
+
: "trails-text-primary"
|
|
56
|
+
}`}
|
|
57
|
+
>
|
|
58
|
+
{option.amountFormatted}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div className="text-right">
|
|
64
|
+
<div
|
|
65
|
+
className={`text-xs font-medium ${
|
|
66
|
+
option.notEnoughBalance
|
|
67
|
+
? "text-gray-400 dark:text-gray-500"
|
|
68
|
+
: "trails-text-primary"
|
|
69
|
+
}`}
|
|
70
|
+
>
|
|
71
|
+
{option.amountUsdDisplay}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</button>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default FeeOption
|
|
@@ -1,150 +1,215 @@
|
|
|
1
|
-
import { TokenImage } from "./TokenImage.js"
|
|
2
1
|
import { ChevronDown } from "lucide-react"
|
|
3
2
|
import type React from "react"
|
|
4
|
-
import {
|
|
5
|
-
import { formatRawAmount } from "../../tokenBalances.js"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
3
|
+
import { useRef, useState } from "react"
|
|
4
|
+
import { formatUsdAmountDisplay, formatRawAmount } from "../../tokenBalances.js"
|
|
5
|
+
import {
|
|
6
|
+
FeeOption,
|
|
7
|
+
type FeeOption as EnhancedFeeOptionType,
|
|
8
|
+
} from "./FeeOption.js"
|
|
9
|
+
import { logger } from "../../logger.js"
|
|
10
|
+
import { getChainInfo } from "../../chains.js"
|
|
11
|
+
import { TokenImage } from "./TokenImage.js"
|
|
12
|
+
import { getTokenImageUrl } from "../../tokens.js"
|
|
13
|
+
import { zeroAddress } from "viem"
|
|
14
|
+
|
|
15
|
+
// Original FeeOption type from useSendForm hook
|
|
16
|
+
type OriginalFeeOption = {
|
|
17
|
+
tokenAddress: string
|
|
18
|
+
tokenSymbol: string
|
|
19
|
+
tokenDecimals: number
|
|
20
|
+
amount: string
|
|
21
|
+
amountUSD: number
|
|
22
|
+
notEnoughBalance?: boolean
|
|
23
|
+
tokenImageUrl?: string
|
|
24
|
+
chainId?: number
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
interface FeeOptionsProps {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
feeOptions: OriginalFeeOption[]
|
|
29
|
+
selectedFeeToken: OriginalFeeOption | null
|
|
30
|
+
setSelectedFeeToken: (token: OriginalFeeOption | null) => void
|
|
31
|
+
chainId?: number
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
// Enable once we have a way to pay fees with other tokens
|
|
30
|
-
const DISABLED_FEE_OPTIONS = true
|
|
31
|
-
|
|
32
34
|
export const FeeOptions: React.FC<FeeOptionsProps> = ({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
feeOptions,
|
|
36
|
+
selectedFeeToken,
|
|
37
|
+
setSelectedFeeToken,
|
|
38
|
+
chainId,
|
|
36
39
|
}) => {
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
const dropdownRef = useRef<HTMLDivElement>(null)
|
|
40
|
-
|
|
41
|
-
const formattedFeeAmount = useMemo(() => {
|
|
42
|
-
if (!selectedOption) return "0.00"
|
|
43
|
-
return formatRawAmount(selectedOption.value, selectedOption.token.decimals)
|
|
44
|
-
}, [selectedOption])
|
|
45
|
-
|
|
46
|
-
// Calculate USD value of fee
|
|
47
|
-
const feeUsdValue = useMemo(() => {
|
|
48
|
-
if (!selectedOption) return ""
|
|
49
|
-
return "" // TODO
|
|
50
|
-
}, [selectedOption])
|
|
51
|
-
|
|
52
|
-
// Set first option as default if none selected
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
if (!selectedOption && options.length > 0) {
|
|
55
|
-
onSelect(options[0]!)
|
|
56
|
-
}
|
|
57
|
-
}, [selectedOption, options, onSelect])
|
|
40
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
41
|
+
const accordionRef = useRef<HTMLDivElement>(null)
|
|
58
42
|
|
|
59
|
-
if
|
|
43
|
+
// Don't render if no fee options available
|
|
44
|
+
if (!feeOptions || feeOptions.length === 0) {
|
|
60
45
|
return null
|
|
61
46
|
}
|
|
62
47
|
|
|
48
|
+
// Enhance ALL fee options with formatted values and image URLs (including native gas)
|
|
49
|
+
const enhancedFeeOptions: EnhancedFeeOptionType[] = feeOptions.map(
|
|
50
|
+
(option) => ({
|
|
51
|
+
...option,
|
|
52
|
+
amountFormatted: `${formatRawAmount(option.amount, option.tokenDecimals)} ${option.tokenSymbol}`,
|
|
53
|
+
amountUsdDisplay: formatUsdAmountDisplay(option.amountUSD),
|
|
54
|
+
tokenImageUrl: getTokenImageUrl({
|
|
55
|
+
chainId: option.chainId || chainId, // Use fee option's chain ID, fallback to prop
|
|
56
|
+
contractAddress: option.tokenAddress,
|
|
57
|
+
symbol: option.tokenSymbol,
|
|
58
|
+
}),
|
|
59
|
+
}),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
// Find native gas option (zero address) for header display
|
|
63
|
+
const nativeGasOption = enhancedFeeOptions.find(
|
|
64
|
+
(opt) => opt.tokenAddress.toLowerCase() === zeroAddress.toLowerCase(),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const handleFeeTokenSelect = (option: EnhancedFeeOptionType) => {
|
|
68
|
+
// Convert back to original format for the parent component
|
|
69
|
+
const originalOption: OriginalFeeOption = {
|
|
70
|
+
tokenAddress: option.tokenAddress,
|
|
71
|
+
tokenSymbol: option.tokenSymbol,
|
|
72
|
+
tokenDecimals: option.tokenDecimals,
|
|
73
|
+
amount: option.amount,
|
|
74
|
+
amountUSD: option.amountUSD,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// For native gas (zero address), set to null to trigger non-gasless flow
|
|
78
|
+
if (option.tokenAddress.toLowerCase() === zeroAddress.toLowerCase()) {
|
|
79
|
+
setSelectedFeeToken(null)
|
|
80
|
+
logger.console.log(
|
|
81
|
+
"[trails-sdk] [FEE-SELECT] Selected native gas fee option",
|
|
82
|
+
)
|
|
83
|
+
} else {
|
|
84
|
+
setSelectedFeeToken(originalOption)
|
|
85
|
+
logger.console.log(
|
|
86
|
+
"[trails-sdk] [FEE-SELECT] Selected ERC20 fee option:",
|
|
87
|
+
originalOption,
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setIsOpen(false)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Get display text and image for selected option
|
|
95
|
+
const getSelectedDisplayText = () => {
|
|
96
|
+
if (
|
|
97
|
+
selectedFeeToken === null ||
|
|
98
|
+
selectedFeeToken?.tokenAddress?.toLowerCase() ===
|
|
99
|
+
zeroAddress.toLowerCase()
|
|
100
|
+
) {
|
|
101
|
+
// Native gas selected - use native gas option data
|
|
102
|
+
const nativeSymbol =
|
|
103
|
+
nativeGasOption?.tokenSymbol ||
|
|
104
|
+
(chainId ? getNativeSymbol(chainId) : "ETH")
|
|
105
|
+
const nativeImageUrl = getTokenImageUrl({
|
|
106
|
+
chainId,
|
|
107
|
+
contractAddress: zeroAddress,
|
|
108
|
+
symbol: nativeSymbol,
|
|
109
|
+
})
|
|
110
|
+
const amountDisplay = nativeGasOption
|
|
111
|
+
? `${formatRawAmount(nativeGasOption.amount, nativeGasOption.tokenDecimals)} ${nativeGasOption.tokenSymbol}`
|
|
112
|
+
: ""
|
|
113
|
+
const usdDisplay = nativeGasOption
|
|
114
|
+
? formatUsdAmountDisplay(nativeGasOption.amountUSD)
|
|
115
|
+
: ""
|
|
116
|
+
return {
|
|
117
|
+
title: nativeSymbol,
|
|
118
|
+
amountDisplay,
|
|
119
|
+
usdDisplay,
|
|
120
|
+
symbol: nativeSymbol,
|
|
121
|
+
imageUrl: nativeImageUrl,
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
const tokenImageUrl = getTokenImageUrl({
|
|
125
|
+
chainId: selectedFeeToken.chainId || chainId, // Use fee token's chain ID, fallback to prop
|
|
126
|
+
contractAddress: selectedFeeToken.tokenAddress,
|
|
127
|
+
symbol: selectedFeeToken.tokenSymbol,
|
|
128
|
+
})
|
|
129
|
+
const amountDisplay = `${formatRawAmount(selectedFeeToken.amount, selectedFeeToken.tokenDecimals)} ${selectedFeeToken.tokenSymbol}`
|
|
130
|
+
const usdDisplay = formatUsdAmountDisplay(selectedFeeToken.amountUSD)
|
|
131
|
+
return {
|
|
132
|
+
title: selectedFeeToken.tokenSymbol,
|
|
133
|
+
amountDisplay,
|
|
134
|
+
usdDisplay,
|
|
135
|
+
symbol: selectedFeeToken.tokenSymbol,
|
|
136
|
+
imageUrl: tokenImageUrl,
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Helper function to get native symbol for a chain
|
|
142
|
+
const getNativeSymbol = (chainId: number) => {
|
|
143
|
+
const chainInfo = getChainInfo(chainId)
|
|
144
|
+
return chainInfo?.nativeCurrency.symbol || "ETH"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const selectedDisplay = getSelectedDisplayText()
|
|
148
|
+
|
|
63
149
|
return (
|
|
64
|
-
<div className="space-y-1" ref={
|
|
65
|
-
<div className="
|
|
66
|
-
<div className="
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
{selectedOption && (
|
|
70
|
-
<div className="text-xs text-gray-500 dark:text-gray-400">
|
|
71
|
-
Fee {formattedFeeAmount} {selectedOption.token.symbol}
|
|
72
|
-
<div>
|
|
73
|
-
{feeUsdValue && (
|
|
74
|
-
<span className="ml-1 text-xs text-gray-500 dark:text-gray-400">
|
|
75
|
-
≈ {feeUsdValue}
|
|
76
|
-
</span>
|
|
77
|
-
)}
|
|
78
|
-
</div>
|
|
150
|
+
<div className="space-y-1" ref={accordionRef}>
|
|
151
|
+
<div className="p-2">
|
|
152
|
+
<div className="flex justify-between items-center mb-2">
|
|
153
|
+
<div className="text-xs font-medium trails-text-primary">
|
|
154
|
+
Gas Fee Options
|
|
79
155
|
</div>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
156
|
+
<div className="text-xs trails-text-muted">
|
|
157
|
+
Choose how to pay for gas for transfer
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
{/* Accordion Header */}
|
|
83
162
|
<button
|
|
84
163
|
type="button"
|
|
85
|
-
onClick={() =>
|
|
86
|
-
className="w-
|
|
164
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
165
|
+
className="w-full flex items-center justify-between p-1.5 trails-border-radius-input border trails-border-primary hover:trails-hover-bg transition-colors cursor-pointer"
|
|
87
166
|
>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
167
|
+
<div className="flex items-center space-x-2">
|
|
168
|
+
<TokenImage
|
|
169
|
+
imageUrl={selectedDisplay.imageUrl}
|
|
170
|
+
symbol={selectedDisplay.symbol}
|
|
171
|
+
chainId={selectedFeeToken?.chainId || chainId}
|
|
172
|
+
size={16}
|
|
173
|
+
/>
|
|
174
|
+
<div className="text-left">
|
|
175
|
+
<div className="text-xs font-medium trails-text-primary">
|
|
176
|
+
{selectedDisplay.amountDisplay}
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
<div className="flex items-center space-x-2">
|
|
181
|
+
<div className="text-right">
|
|
182
|
+
<div className="text-xs font-medium trails-text-primary">
|
|
183
|
+
{selectedDisplay.usdDisplay}
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
<ChevronDown
|
|
187
|
+
className={`w-3 h-3 trails-text-muted transition-transform ${
|
|
188
|
+
isOpen ? "transform rotate-180" : ""
|
|
189
|
+
}`}
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
109
192
|
</button>
|
|
110
193
|
|
|
111
|
-
{
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
:
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
selectedOption?.token.symbol === option.token.symbol
|
|
131
|
-
? "bg-gray-200 dark:bg-gray-700"
|
|
132
|
-
: "bg-gray-100 dark:bg-gray-600"
|
|
133
|
-
}`}
|
|
134
|
-
>
|
|
135
|
-
<TokenImage
|
|
136
|
-
symbol={option.token.symbol}
|
|
137
|
-
imageUrl={option.token.logoURL}
|
|
138
|
-
size={16}
|
|
139
|
-
/>
|
|
140
|
-
</div>
|
|
141
|
-
<span className="ml-1.5">{option.token.symbol}</span>
|
|
142
|
-
{selectedOption?.token.symbol === option.token.symbol && (
|
|
143
|
-
<span className="ml-auto text-gray-900 dark:text-white">
|
|
144
|
-
•
|
|
145
|
-
</span>
|
|
146
|
-
)}
|
|
147
|
-
</button>
|
|
194
|
+
{/* Accordion Content */}
|
|
195
|
+
{isOpen && (
|
|
196
|
+
<div className="mt-2 space-y-1">
|
|
197
|
+
{/* All Fee Options (including native gas) */}
|
|
198
|
+
{enhancedFeeOptions.map((option, index) => (
|
|
199
|
+
<FeeOption
|
|
200
|
+
key={`${option.tokenAddress}-${index}`}
|
|
201
|
+
option={option}
|
|
202
|
+
isSelected={
|
|
203
|
+
option.tokenAddress.toLowerCase() ===
|
|
204
|
+
zeroAddress.toLowerCase()
|
|
205
|
+
? selectedFeeToken === null ||
|
|
206
|
+
selectedFeeToken?.tokenAddress?.toLowerCase() ===
|
|
207
|
+
zeroAddress.toLowerCase()
|
|
208
|
+
: selectedFeeToken?.tokenAddress === option.tokenAddress
|
|
209
|
+
}
|
|
210
|
+
onClick={() => handleFeeTokenSelect(option)}
|
|
211
|
+
chainId={option.chainId || chainId}
|
|
212
|
+
/>
|
|
148
213
|
))}
|
|
149
214
|
</div>
|
|
150
215
|
)}
|
|
@@ -21,6 +21,7 @@ import { useMode } from "../hooks/useMode.js"
|
|
|
21
21
|
import { useBalanceVisible } from "../hooks/useBalanceVisible.js"
|
|
22
22
|
import { useCurrentScreen } from "../hooks/useCurrentScreen.js"
|
|
23
23
|
import { useDefaultTokenSelection } from "../hooks/useDefaultTokenSelection.js" // Context version
|
|
24
|
+
import { useTargetAmount } from "../hooks/useTargetAmount.js"
|
|
24
25
|
import { TokenImage } from "./TokenImage.js"
|
|
25
26
|
import { Identicon } from "./Identicon.js"
|
|
26
27
|
import { TokenSelector } from "./TokenSelector.js"
|
|
@@ -29,8 +30,6 @@ import { ChainList } from "./ChainList.js"
|
|
|
29
30
|
import { QuoteDetails } from "./QuoteDetails.js"
|
|
30
31
|
import { ErrorDisplay } from "./ErrorDisplay.js"
|
|
31
32
|
import { getChainInfo } from "../../chains.js"
|
|
32
|
-
import { formatUsdAmountDisplay } from "../../tokenBalances.js"
|
|
33
|
-
import { MINIMUM_USD_AMOUNT_FOR_SWAP } from "../../constants.js"
|
|
34
33
|
import { truncateAddress } from "../../utils.js"
|
|
35
34
|
import type { PrepareSendQuote } from "../../prepareSend.js"
|
|
36
35
|
import type { SupportedToken } from "../../tokens.js"
|
|
@@ -108,6 +107,7 @@ export const Fund: React.FC<FundProps> = ({
|
|
|
108
107
|
const { selectedRecipient, setSelectedRecipient } = useSelectedRecipient()
|
|
109
108
|
const { amount: globalAmount, setAmount: setGlobalAmount } = useSwapAmount()
|
|
110
109
|
const { setCurrentScreen } = useCurrentScreen()
|
|
110
|
+
const { targetAmountUsd } = useTargetAmount()
|
|
111
111
|
|
|
112
112
|
// Use new default token selection hook
|
|
113
113
|
const {
|
|
@@ -639,6 +639,7 @@ export const Fund: React.FC<FundProps> = ({
|
|
|
639
639
|
/>
|
|
640
640
|
<TokenSelector
|
|
641
641
|
onTokenSelect={handleOriginTokenSelect}
|
|
642
|
+
targetAmountUsd={targetAmountUsd}
|
|
642
643
|
onError={onError}
|
|
643
644
|
fundMethod={fundMethod}
|
|
644
645
|
showContinueButton={false}
|
|
@@ -665,6 +666,9 @@ export const Fund: React.FC<FundProps> = ({
|
|
|
665
666
|
setShowOriginTokenSelector(false)
|
|
666
667
|
setShowOriginChainList(true)
|
|
667
668
|
}}
|
|
669
|
+
onNavigateToFundMethods={() => {
|
|
670
|
+
setCurrentScreen("fund-methods")
|
|
671
|
+
}}
|
|
668
672
|
/>
|
|
669
673
|
</div>
|
|
670
674
|
)
|
|
@@ -682,6 +686,7 @@ export const Fund: React.FC<FundProps> = ({
|
|
|
682
686
|
/>
|
|
683
687
|
<TokenSelector
|
|
684
688
|
onTokenSelect={handleDestinationTokenSelect}
|
|
689
|
+
targetAmountUsd={targetAmountUsd}
|
|
685
690
|
onError={onError}
|
|
686
691
|
fundMethod={fundMethod}
|
|
687
692
|
showContinueButton={false}
|
|
@@ -767,6 +772,13 @@ export const Fund: React.FC<FundProps> = ({
|
|
|
767
772
|
<div className="flex justify-between items-center mb-2">
|
|
768
773
|
<div className="text-sm font-medium trails-text-secondary text-left">
|
|
769
774
|
Deposit
|
|
775
|
+
{fundMethod === "qr-code" ? (
|
|
776
|
+
<span className="text-xs"> with QR Code</span>
|
|
777
|
+
) : fundMethod === "exchange" ? (
|
|
778
|
+
<span className="text-xs"> with Exchange</span>
|
|
779
|
+
) : (
|
|
780
|
+
""
|
|
781
|
+
)}
|
|
770
782
|
</div>
|
|
771
783
|
|
|
772
784
|
{/* Percentage Buttons */}
|
|
@@ -807,7 +819,10 @@ export const Fund: React.FC<FundProps> = ({
|
|
|
807
819
|
<div className="flex items-center space-x-2">
|
|
808
820
|
{/* Amount Input */}
|
|
809
821
|
<div className="flex-1">
|
|
810
|
-
<div
|
|
822
|
+
<div
|
|
823
|
+
className="flex items-center justify-start cursor-text"
|
|
824
|
+
onClick={() => inputRef.current?.focus()}
|
|
825
|
+
>
|
|
811
826
|
<div className="flex items-center">
|
|
812
827
|
<input
|
|
813
828
|
ref={inputRef}
|
|
@@ -1166,30 +1181,6 @@ export const Fund: React.FC<FundProps> = ({
|
|
|
1166
1181
|
</p>
|
|
1167
1182
|
</div>
|
|
1168
1183
|
</div>
|
|
1169
|
-
) : prepareSendQuote?.minimumNotMet ? (
|
|
1170
|
-
<div className="px-2 py-3 rounded-lg bg-amber-500/10 border border-solid border-amber-500/30">
|
|
1171
|
-
<div className="flex items-center space-x-2">
|
|
1172
|
-
<svg
|
|
1173
|
-
className="w-4 h-4 text-amber-500 flex-shrink-0"
|
|
1174
|
-
fill="none"
|
|
1175
|
-
stroke="currentColor"
|
|
1176
|
-
viewBox="0 0 24 24"
|
|
1177
|
-
aria-hidden="true"
|
|
1178
|
-
>
|
|
1179
|
-
<path
|
|
1180
|
-
strokeLinecap="round"
|
|
1181
|
-
strokeLinejoin="round"
|
|
1182
|
-
strokeWidth={2}
|
|
1183
|
-
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
|
|
1184
|
-
/>
|
|
1185
|
-
</svg>
|
|
1186
|
-
<p className="text-sm text-amber-600 dark:text-amber-400">
|
|
1187
|
-
Please enter an amount above{" "}
|
|
1188
|
-
{formatUsdAmountDisplay(MINIMUM_USD_AMOUNT_FOR_SWAP)} otherwise
|
|
1189
|
-
transfer may fail
|
|
1190
|
-
</p>
|
|
1191
|
-
</div>
|
|
1192
|
-
</div>
|
|
1193
1184
|
) : null}
|
|
1194
1185
|
|
|
1195
1186
|
{/* Fund Button */}
|
|
@@ -10,7 +10,7 @@ import { ScreenHeader } from "./ScreenHeader.js"
|
|
|
10
10
|
export interface FundMethodsProps {
|
|
11
11
|
onBack?: () => void
|
|
12
12
|
onSelectWalletConnect: () => void
|
|
13
|
-
|
|
13
|
+
onSelectExchangeList: () => void
|
|
14
14
|
onSelectConnectedAccount: () => void
|
|
15
15
|
onSelectQrCode: () => void
|
|
16
16
|
}
|
|
@@ -18,7 +18,7 @@ export interface FundMethodsProps {
|
|
|
18
18
|
const FundMethods: React.FC<FundMethodsProps> = ({
|
|
19
19
|
onBack,
|
|
20
20
|
onSelectWalletConnect,
|
|
21
|
-
|
|
21
|
+
onSelectExchangeList,
|
|
22
22
|
onSelectConnectedAccount,
|
|
23
23
|
onSelectQrCode,
|
|
24
24
|
}) => {
|
|
@@ -158,7 +158,7 @@ const FundMethods: React.FC<FundMethodsProps> = ({
|
|
|
158
158
|
|
|
159
159
|
<button
|
|
160
160
|
type="button"
|
|
161
|
-
onClick={
|
|
161
|
+
onClick={onSelectExchangeList}
|
|
162
162
|
className="w-full flex items-center justify-between cursor-pointer font-semibold py-4 px-6 trails-border-radius-large-button transition-all duration-200 trails-bg-secondary trails-hover-bg trails-text-primary"
|
|
163
163
|
>
|
|
164
164
|
<div className="flex items-center space-x-3 flex-1">
|
|
@@ -20,7 +20,6 @@ import { ErrorDisplay } from "./ErrorDisplay.js"
|
|
|
20
20
|
import { useMode } from "../hooks/useMode.js"
|
|
21
21
|
import { getExplorerUrlForAddress } from "../../explorer.js"
|
|
22
22
|
import { logger } from "../../logger.js"
|
|
23
|
-
import { FeeOptions } from "./FeeOptions.js"
|
|
24
23
|
import { useBalanceVisible } from "../hooks/useBalanceVisible.js"
|
|
25
24
|
|
|
26
25
|
interface FundSendFormProps {
|
|
@@ -138,9 +137,6 @@ export const FundSendForm: React.FC<FundSendFormProps> = ({
|
|
|
138
137
|
quoteErrorPrettified,
|
|
139
138
|
isSameTokenWithoutCustomCalldata,
|
|
140
139
|
destinationTokenAddress,
|
|
141
|
-
feeOptions,
|
|
142
|
-
selectedFeeToken,
|
|
143
|
-
setSelectedFeeToken,
|
|
144
140
|
isRecipientContract,
|
|
145
141
|
} = useSendForm({
|
|
146
142
|
account,
|
|
@@ -825,13 +821,6 @@ export const FundSendForm: React.FC<FundSendFormProps> = ({
|
|
|
825
821
|
chainId={selectedDestinationChain.id}
|
|
826
822
|
/> */}
|
|
827
823
|
|
|
828
|
-
{/* Fee Options */}
|
|
829
|
-
<FeeOptions
|
|
830
|
-
options={feeOptions}
|
|
831
|
-
selectedOption={selectedFeeToken ?? undefined}
|
|
832
|
-
onSelect={setSelectedFeeToken}
|
|
833
|
-
/>
|
|
834
|
-
|
|
835
824
|
{/* Warning Messages - Show only one at a time */}
|
|
836
825
|
{isSameTokenWithoutCustomCalldata ? (
|
|
837
826
|
<ErrorDisplay
|
|
@@ -867,28 +856,6 @@ export const FundSendForm: React.FC<FundSendFormProps> = ({
|
|
|
867
856
|
</p>
|
|
868
857
|
</div>
|
|
869
858
|
</div>
|
|
870
|
-
) : prepareSendQuote?.minimumNotMet ? (
|
|
871
|
-
<div className="px-2 py-3 rounded-lg bg-amber-500/10 border border-solid border-amber-500/30">
|
|
872
|
-
<div className="flex items-center space-x-2">
|
|
873
|
-
<svg
|
|
874
|
-
className="w-4 h-4 text-amber-500 flex-shrink-0"
|
|
875
|
-
fill="none"
|
|
876
|
-
stroke="currentColor"
|
|
877
|
-
viewBox="0 0 24 24"
|
|
878
|
-
aria-hidden="true"
|
|
879
|
-
>
|
|
880
|
-
<path
|
|
881
|
-
strokeLinecap="round"
|
|
882
|
-
strokeLinejoin="round"
|
|
883
|
-
strokeWidth={2}
|
|
884
|
-
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
|
|
885
|
-
/>
|
|
886
|
-
</svg>
|
|
887
|
-
<p className="text-sm text-amber-600 dark:text-amber-400">
|
|
888
|
-
Please enter an amount above $0.15 otherwise transfer may fail
|
|
889
|
-
</p>
|
|
890
|
-
</div>
|
|
891
|
-
</div>
|
|
892
859
|
) : null}
|
|
893
860
|
|
|
894
861
|
{/* Continue Button */}
|
|
@@ -11,16 +11,26 @@ import BinanceLogo from "../assets/Binance_Icon_Logo.svg"
|
|
|
11
11
|
import CoinbaseLogo from "../assets/Coinbase_Icon_Logo.svg"
|
|
12
12
|
import { ScreenHeader } from "./ScreenHeader.js"
|
|
13
13
|
import { logger } from "../../logger.js"
|
|
14
|
+
import { useSelectedMeshExchange } from "../hooks/useSelectedMeshExchange.js"
|
|
15
|
+
import { useMode } from "../hooks/useMode.js"
|
|
16
|
+
import { useCurrentScreen } from "../hooks/useCurrentScreen.js"
|
|
17
|
+
import type { Mode } from "../../mode.js"
|
|
18
|
+
import type { Screen } from "../hooks/useCurrentScreen.js"
|
|
14
19
|
|
|
15
20
|
export interface MeshConnectExchangesProps {
|
|
16
|
-
onBack
|
|
17
|
-
onSelectExchange
|
|
21
|
+
onBack?: () => void
|
|
22
|
+
onSelectExchange?: (integrationId: string, exchangeName: string) => void
|
|
23
|
+
getInitialScreenForMode?: (mode: Mode) => Screen
|
|
18
24
|
}
|
|
19
25
|
|
|
20
26
|
export const MeshConnectExchanges: React.FC<MeshConnectExchangesProps> = ({
|
|
21
27
|
onBack,
|
|
22
28
|
onSelectExchange,
|
|
29
|
+
getInitialScreenForMode,
|
|
23
30
|
}) => {
|
|
31
|
+
const { setSelectedExchange } = useSelectedMeshExchange()
|
|
32
|
+
const { mode } = useMode()
|
|
33
|
+
const { setCurrentScreen } = useCurrentScreen()
|
|
24
34
|
const [coinbaseId, setCoinbaseId] = useState<string | null>(null)
|
|
25
35
|
const [binanceId, setBinanceId] = useState<string | null>(null)
|
|
26
36
|
const [bitfinexId, setBitfinexId] = useState<string | null>(null)
|
|
@@ -73,7 +83,26 @@ export const MeshConnectExchanges: React.FC<MeshConnectExchangesProps> = ({
|
|
|
73
83
|
logger.console.log(
|
|
74
84
|
`[trails-sdk] Selected exchange: ${exchangeName} (${integrationId})`,
|
|
75
85
|
)
|
|
76
|
-
|
|
86
|
+
|
|
87
|
+
// If a callback is provided (MeshConnectFlow), use it
|
|
88
|
+
if (onSelectExchange) {
|
|
89
|
+
onSelectExchange(integrationId, exchangeName)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Otherwise, use the hook-based approach (main widget flow)
|
|
94
|
+
// Store the selected exchange
|
|
95
|
+
setSelectedExchange({ integrationId, exchangeName })
|
|
96
|
+
|
|
97
|
+
// Navigate to the initial screen for the current mode
|
|
98
|
+
if (getInitialScreenForMode) {
|
|
99
|
+
const currentMode = (mode || "pay") as Mode
|
|
100
|
+
const initialScreen = getInitialScreenForMode(currentMode)
|
|
101
|
+
logger.console.log(
|
|
102
|
+
`[trails-sdk] Navigating to initial screen for mode ${currentMode}: ${initialScreen}`,
|
|
103
|
+
)
|
|
104
|
+
setCurrentScreen(initialScreen)
|
|
105
|
+
}
|
|
77
106
|
}
|
|
78
107
|
|
|
79
108
|
if (loading) {
|