0xtrails 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics.d.ts +8 -3
- package/dist/analytics.d.ts.map +1 -1
- package/dist/{ccip-DhEkQ6QC.js → ccip-Dw5AN7oU.js} +1 -1
- package/dist/cctp.d.ts +0 -149
- package/dist/cctp.d.ts.map +1 -1
- package/dist/chains.d.ts +28 -3
- package/dist/chains.d.ts.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/contractUtils.d.ts.map +1 -1
- package/dist/estimate.d.ts.map +1 -1
- package/dist/fees.d.ts.map +1 -1
- package/dist/gasless.d.ts +12 -0
- package/dist/gasless.d.ts.map +1 -1
- package/dist/{index-MhD2DA7_.js → index-BtVUTbEZ.js} +30984 -38945
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +108 -107
- package/dist/indexerClient.d.ts +2 -2
- package/dist/intents.d.ts +0 -17
- package/dist/intents.d.ts.map +1 -1
- package/dist/mutations.d.ts.map +1 -1
- package/dist/paymasterSend.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +1 -1
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/sendUserOp.d.ts +0 -18
- package/dist/sendUserOp.d.ts.map +1 -1
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/tokens.d.ts +10 -8
- package/dist/tokens.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +4 -5
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +4 -5
- package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/standardDeposit.d.ts +2 -2
- package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/execution/transactionState.d.ts +2 -2
- package/dist/transactionIntent/execution/transactionState.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/crossChain.d.ts +4 -4
- package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/index.d.ts +0 -1
- package/dist/transactionIntent/handlers/index.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +4 -34
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
- package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts +2 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
- package/dist/transactionIntent/types.d.ts +6 -19
- package/dist/transactionIntent/types.d.ts.map +1 -1
- package/dist/transactionIntent/utils/index.d.ts +0 -1
- package/dist/transactionIntent/utils/index.d.ts.map +1 -1
- package/dist/transactions.d.ts +2 -20
- package/dist/transactions.d.ts.map +1 -1
- package/dist/utils.d.ts +8 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/walletUtils.d.ts +21 -0
- package/dist/walletUtils.d.ts.map +1 -0
- package/dist/wallets.d.ts +33 -240
- package/dist/wallets.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/FeeOption.d.ts +8 -13
- package/dist/widget/components/FeeOption.d.ts.map +1 -1
- package/dist/widget/components/FeeOptions.d.ts +11 -5
- package/dist/widget/components/FeeOptions.d.ts.map +1 -1
- package/dist/widget/components/NativeGasOption.d.ts.map +1 -1
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
- package/dist/widget/components/QRCodeDeposit.d.ts +5 -0
- package/dist/widget/components/QRCodeDeposit.d.ts.map +1 -1
- package/dist/widget/components/QRCodeWalletSelect.d.ts +13 -0
- package/dist/widget/components/QRCodeWalletSelect.d.ts.map +1 -0
- package/dist/widget/components/QrCode.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/Receipt.d.ts.map +1 -1
- package/dist/widget/components/ScreenHeader.d.ts +1 -1
- package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
- package/dist/widget/components/Toast.d.ts.map +1 -1
- package/dist/widget/components/TokenImage.d.ts.map +1 -1
- package/dist/widget/css/compiled.css +1 -1
- package/dist/widget/hooks/useCheckout.d.ts +15 -1
- package/dist/widget/hooks/useCheckout.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/useDebugScreens.d.ts +1 -1
- package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
- package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts +7 -0
- package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts.map +1 -0
- package/dist/widget/hooks/useIsSequenceWallet.d.ts +6 -0
- package/dist/widget/hooks/useIsSequenceWallet.d.ts.map +1 -0
- package/dist/widget/hooks/useQuote.d.ts +5 -8
- package/dist/widget/hooks/useQuote.d.ts.map +1 -1
- package/dist/widget/hooks/useRecentTokens.d.ts.map +1 -1
- package/dist/widget/hooks/useSelectedFeeOption.d.ts +30 -0
- package/dist/widget/hooks/useSelectedFeeOption.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +6 -15
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
- package/dist/widget/index.js +1 -1
- package/dist/widget/providers/TrailsProvider.d.ts +23 -12
- package/dist/widget/providers/TrailsProvider.d.ts.map +1 -1
- package/dist/widget/widget.d.ts +11 -0
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/analytics.ts +53 -21
- package/src/cctp.ts +0 -1016
- package/src/chains.ts +93 -39
- package/src/config.ts +24 -6
- package/src/constants.ts +1 -4
- package/src/contractUtils.ts +6 -6
- package/src/estimate.ts +3 -6
- package/src/fees.ts +5 -10
- package/src/gasless.ts +45 -0
- package/src/index.ts +7 -6
- package/src/indexerClient.ts +2 -2
- package/src/intents.ts +52 -206
- package/src/mutations.ts +3 -2
- package/src/paymasterSend.ts +2 -5
- package/src/prepareSend.ts +9 -12
- package/src/sendUserOp.ts +3 -64
- package/src/tokenBalances.ts +2 -1
- package/src/tokens.ts +62 -133
- package/src/trailsClient.ts +1 -1
- package/src/transactionIntent/deposits/depositOrchestrator.ts +14 -15
- package/src/transactionIntent/deposits/gaslessDeposit.ts +70 -100
- package/src/transactionIntent/deposits/standardDeposit.ts +22 -28
- package/src/transactionIntent/execution/transactionState.ts +2 -2
- package/src/transactionIntent/handlers/crossChain.ts +165 -385
- package/src/transactionIntent/handlers/index.ts +0 -1
- package/src/transactionIntent/handlers/sameChainSameToken.ts +228 -94
- package/src/transactionIntent/quote/normalizeQuote.ts +4 -6
- package/src/transactionIntent/quote/quoteHelpers.ts +35 -3
- package/src/transactionIntent/types.ts +6 -27
- package/src/transactionIntent/utils/index.ts +0 -1
- package/src/transactions.ts +6 -203
- package/src/umd.tsx +1 -3
- package/src/utils.ts +28 -8
- package/src/walletUtils.ts +42 -0
- package/src/wallets.ts +361 -203
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/AccountIntentTransactionHistory.tsx +73 -4
- package/src/widget/components/AccountSettings.tsx +17 -17
- package/src/widget/components/ChainList.tsx +3 -3
- package/src/widget/components/ClassicSwap.tsx +19 -10
- package/src/widget/components/ConfigDisplay.tsx +1 -1
- package/src/widget/components/FeeOption.tsx +63 -20
- package/src/widget/components/FeeOptions.tsx +54 -123
- package/src/widget/components/NativeGasOption.tsx +3 -1
- package/src/widget/components/Pay.tsx +18 -11
- package/src/widget/components/PoolDeposit.tsx +23 -10
- package/src/widget/components/QRCodeDeposit.tsx +50 -30
- package/src/widget/components/QRCodeWalletSelect.tsx +77 -0
- package/src/widget/components/QrCode.tsx +188 -233
- package/src/widget/components/QuoteDetails.tsx +48 -2
- package/src/widget/components/Receipt.tsx +5 -2
- package/src/widget/components/ScreenHeader.tsx +10 -8
- package/src/widget/components/Toast.tsx +10 -0
- package/src/widget/components/TokenImage.tsx +56 -13
- package/src/widget/hooks/useCheckout.ts +71 -0
- package/src/widget/hooks/useCurrentScreen.tsx +1 -0
- package/src/widget/hooks/useDebugScreens.ts +5 -0
- package/src/widget/hooks/useIntentTransactionHistory.ts +788 -418
- package/src/widget/hooks/useIsConnectedWalletSmartContract.ts +43 -0
- package/src/widget/hooks/useIsSequenceWallet.ts +17 -0
- package/src/widget/hooks/useQuote.ts +16 -17
- package/src/widget/hooks/useRecentTokens.ts +2 -1
- package/src/widget/hooks/useSelectedFeeOption.tsx +257 -0
- package/src/widget/hooks/useSendForm.ts +172 -47
- package/src/widget/hooks/useTokenList.ts +15 -2
- package/src/widget/providers/TrailsProvider.tsx +53 -25
- package/src/widget/widget.tsx +119 -48
- package/dist/cctpqueue.d.ts +0 -18
- package/dist/cctpqueue.d.ts.map +0 -1
- package/dist/preconditions.d.ts +0 -12
- package/dist/preconditions.d.ts.map +0 -1
- package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts +0 -62
- package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts.map +0 -1
- package/dist/transactionIntent/utils/lifiHelpers.d.ts +0 -10
- package/dist/transactionIntent/utils/lifiHelpers.d.ts.map +0 -1
- package/dist/widget/hooks/useSelectedFeeToken.d.ts +0 -33
- package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +0 -1
- package/src/cctpqueue.ts +0 -69
- package/src/preconditions.ts +0 -47
- package/src/transactionIntent/handlers/sameChainDifferentToken.ts +0 -323
- package/src/transactionIntent/utils/lifiHelpers.ts +0 -68
- package/src/widget/hooks/useSelectedFeeToken.tsx +0 -288
|
@@ -37,23 +37,23 @@ export const AccountSettings: React.FC<AccountSettingsProps> = ({ onBack }) => {
|
|
|
37
37
|
|
|
38
38
|
return (
|
|
39
39
|
<div className="flex flex-col h-full">
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
</
|
|
55
|
-
|
|
56
|
-
|
|
40
|
+
<ScreenHeader
|
|
41
|
+
headerContent="Settings"
|
|
42
|
+
headerContentAlign="left"
|
|
43
|
+
onBack={onBack}
|
|
44
|
+
rightSideContent={
|
|
45
|
+
<Tooltip message="Get help">
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
onClick={onHelp}
|
|
49
|
+
className="flex h-8 w-8 justify-center items-center rounded-full bg-gray-50 dark:bg-gray-700 cursor-pointer transition-colors text-gray-900 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600"
|
|
50
|
+
title="Get help"
|
|
51
|
+
>
|
|
52
|
+
<HelpCircle className="w-4 h-4" />
|
|
53
|
+
</button>
|
|
54
|
+
</Tooltip>
|
|
55
|
+
}
|
|
56
|
+
/>
|
|
57
57
|
|
|
58
58
|
<div className="flex-1">
|
|
59
59
|
<div className="space-y-4">
|
|
@@ -6,7 +6,7 @@ import { ChainImage } from "./ChainImage.js"
|
|
|
6
6
|
import { AllChainsIcon } from "./AllChainsIcon.js"
|
|
7
7
|
import { SearchInputField } from "./SearchInputField.js"
|
|
8
8
|
import { useChainFilter } from "../hooks/useChainFilter.js"
|
|
9
|
-
import { useSupportedChains } from "../../chains.js"
|
|
9
|
+
import { useSupportedChains, getNormalizedChainName } from "../../chains.js"
|
|
10
10
|
|
|
11
11
|
export interface ChainListItem {
|
|
12
12
|
chainId: number
|
|
@@ -105,7 +105,7 @@ export const ChainList: React.FC<ChainListProps> = ({ onBack }) => {
|
|
|
105
105
|
? "trails-list-item-selected"
|
|
106
106
|
: ""
|
|
107
107
|
}`}
|
|
108
|
-
title={`Select ${chain.name}`}
|
|
108
|
+
title={`Select ${chain.name} (${chain.chainId})`}
|
|
109
109
|
initial={{ opacity: 0, y: 20 }}
|
|
110
110
|
animate={{ opacity: 1, y: 0 }}
|
|
111
111
|
exit={{ opacity: 0, y: -20 }}
|
|
@@ -122,7 +122,7 @@ export const ChainList: React.FC<ChainListProps> = ({ onBack }) => {
|
|
|
122
122
|
|
|
123
123
|
<div className="flex-1 min-w-0 text-left">
|
|
124
124
|
<h3 className="text-sm font-medium truncate text-gray-900 dark:text-white">
|
|
125
|
-
{chain.name}
|
|
125
|
+
{getNormalizedChainName(chain.chainId, chain.name)}
|
|
126
126
|
</h3>
|
|
127
127
|
</div>
|
|
128
128
|
|
|
@@ -143,9 +143,9 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
|
|
|
143
143
|
quoteError,
|
|
144
144
|
quoteErrorPrettified,
|
|
145
145
|
isSameTokenWithoutCustomCalldata,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
processedFeeOptions,
|
|
147
|
+
selectedFeeOption,
|
|
148
|
+
setSelectedFeeOption,
|
|
149
149
|
} = useSendForm({
|
|
150
150
|
account,
|
|
151
151
|
toAmount: tradeType === TradeType.EXACT_OUTPUT ? buyAmount : toAmount,
|
|
@@ -771,13 +771,22 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
|
|
|
771
771
|
)}
|
|
772
772
|
|
|
773
773
|
{/* Fee Options */}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
774
|
+
{fundMethod !== "qr-code" && fundMethod !== "exchange" && (
|
|
775
|
+
<FeeOptions
|
|
776
|
+
processedFeeOptions={processedFeeOptions || []}
|
|
777
|
+
selectedFeeOption={selectedFeeOption}
|
|
778
|
+
setSelectedFeeOption={(feeOption) =>
|
|
779
|
+
setSelectedFeeOption(feeOption)
|
|
780
|
+
}
|
|
781
|
+
chainId={originToken?.chainId}
|
|
782
|
+
isRefetching={isLoadingQuote}
|
|
783
|
+
originTokenInfo={{
|
|
784
|
+
originToken: originToken!,
|
|
785
|
+
originTokenBalance: originToken?.balance ?? "0",
|
|
786
|
+
originTokenAmount: sellAmount,
|
|
787
|
+
}}
|
|
788
|
+
/>
|
|
789
|
+
)}
|
|
781
790
|
|
|
782
791
|
<button
|
|
783
792
|
type="submit"
|
|
@@ -61,7 +61,7 @@ export const ConfigDisplay: React.FC<ConfigDisplayProps> = ({
|
|
|
61
61
|
{
|
|
62
62
|
label: "Trails API Key",
|
|
63
63
|
value: config.trailsApiKey,
|
|
64
|
-
display: truncateString(config.trailsApiKey),
|
|
64
|
+
display: truncateString(config.trailsApiKey || "N/A"),
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
label: "WalletConnect Project ID",
|
|
@@ -1,24 +1,23 @@
|
|
|
1
|
+
import { useMemo } from "react"
|
|
1
2
|
import type React from "react"
|
|
2
3
|
import { TokenImage } from "./TokenImage.js"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
amountUSD: number
|
|
10
|
-
amountFormatted: string
|
|
11
|
-
amountUsdDisplay: string
|
|
12
|
-
tokenImageUrl?: string
|
|
13
|
-
notEnoughBalance?: boolean
|
|
14
|
-
chainId?: number
|
|
15
|
-
}
|
|
4
|
+
import type { ProcessedFeeOption } from "../hooks/useSelectedFeeOption.js"
|
|
5
|
+
import type { Token } from "../hooks/useOriginSelectedToken.js"
|
|
6
|
+
import { normalizeAddress } from "../../utils.js"
|
|
7
|
+
import { parseUnits } from "viem"
|
|
8
|
+
import { Tooltip } from "./Tooltip.js"
|
|
9
|
+
import { logger } from "../../logger.js"
|
|
16
10
|
|
|
17
11
|
interface FeeOptionProps {
|
|
18
|
-
option:
|
|
12
|
+
option: ProcessedFeeOption
|
|
19
13
|
isSelected: boolean
|
|
20
14
|
onClick: () => void
|
|
21
15
|
chainId?: number
|
|
16
|
+
originTokenInfo?: {
|
|
17
|
+
originToken: Token
|
|
18
|
+
originTokenBalance: string
|
|
19
|
+
originTokenAmount: string
|
|
20
|
+
}
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
export const FeeOption: React.FC<FeeOptionProps> = ({
|
|
@@ -26,19 +25,53 @@ export const FeeOption: React.FC<FeeOptionProps> = ({
|
|
|
26
25
|
isSelected,
|
|
27
26
|
onClick,
|
|
28
27
|
chainId,
|
|
28
|
+
originTokenInfo,
|
|
29
29
|
}) => {
|
|
30
|
-
|
|
30
|
+
const isDisabled = useMemo(() => {
|
|
31
|
+
try {
|
|
32
|
+
const originTokenAddress = originTokenInfo?.originToken?.contractAddress
|
|
33
|
+
const optionAddress = option.tokenAddress
|
|
34
|
+
const isOriginFeeOption =
|
|
35
|
+
originTokenAddress &&
|
|
36
|
+
optionAddress &&
|
|
37
|
+
normalizeAddress(originTokenAddress) === normalizeAddress(optionAddress)
|
|
38
|
+
|
|
39
|
+
let isInsufficientOriginBalance = false
|
|
40
|
+
|
|
41
|
+
if (isOriginFeeOption && originTokenInfo) {
|
|
42
|
+
const balanceInWei = BigInt(originTokenInfo.originTokenBalance ?? "0")
|
|
43
|
+
const originTokenAmountInWei = parseUnits(
|
|
44
|
+
originTokenInfo.originTokenAmount ?? "0",
|
|
45
|
+
originTokenInfo.originToken?.contractInfo?.decimals ?? 18,
|
|
46
|
+
)
|
|
47
|
+
const feeAmountInWei = BigInt(option.amount ?? "0")
|
|
48
|
+
|
|
49
|
+
isInsufficientOriginBalance =
|
|
50
|
+
originTokenAmountInWei + feeAmountInWei > balanceInWei
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return option.notEnoughBalance || isInsufficientOriginBalance
|
|
54
|
+
} catch (error) {
|
|
55
|
+
logger.console.error(
|
|
56
|
+
"[trails-sdk] [FEE-OPTION] error checking if fee option is disabled",
|
|
57
|
+
error,
|
|
58
|
+
)
|
|
59
|
+
return true
|
|
60
|
+
}
|
|
61
|
+
}, [originTokenInfo, option])
|
|
62
|
+
|
|
63
|
+
const button = (
|
|
31
64
|
<button
|
|
32
65
|
type="button"
|
|
33
66
|
className={`w-full flex items-center justify-between p-1.5 trails-border-radius-input border transition-colors ${
|
|
34
|
-
|
|
67
|
+
isDisabled
|
|
35
68
|
? "cursor-not-allowed opacity-50 bg-gray-100 dark:bg-gray-800 border-gray-300 dark:border-gray-600"
|
|
36
69
|
: isSelected
|
|
37
70
|
? "cursor-pointer trails-bg-primary/10 border-blue-500 bg-blue-50 dark:bg-blue-900/20"
|
|
38
71
|
: "cursor-pointer trails-bg-card trails-border-primary hover:trails-hover-bg"
|
|
39
72
|
}`}
|
|
40
|
-
onClick={
|
|
41
|
-
disabled={
|
|
73
|
+
onClick={isDisabled ? undefined : onClick}
|
|
74
|
+
disabled={isDisabled}
|
|
42
75
|
>
|
|
43
76
|
<div className="flex items-center space-x-2">
|
|
44
77
|
<TokenImage
|
|
@@ -50,7 +83,7 @@ export const FeeOption: React.FC<FeeOptionProps> = ({
|
|
|
50
83
|
<div className="ml-2 text-left">
|
|
51
84
|
<div
|
|
52
85
|
className={`text-xs font-medium ${
|
|
53
|
-
|
|
86
|
+
isDisabled
|
|
54
87
|
? "text-gray-400 dark:text-gray-500"
|
|
55
88
|
: "trails-text-primary"
|
|
56
89
|
}`}
|
|
@@ -63,7 +96,7 @@ export const FeeOption: React.FC<FeeOptionProps> = ({
|
|
|
63
96
|
<div className="text-right">
|
|
64
97
|
<div
|
|
65
98
|
className={`text-xs font-medium ${
|
|
66
|
-
|
|
99
|
+
isDisabled
|
|
67
100
|
? "text-gray-400 dark:text-gray-500"
|
|
68
101
|
: "trails-text-primary"
|
|
69
102
|
}`}
|
|
@@ -73,6 +106,16 @@ export const FeeOption: React.FC<FeeOptionProps> = ({
|
|
|
73
106
|
</div>
|
|
74
107
|
</button>
|
|
75
108
|
)
|
|
109
|
+
|
|
110
|
+
if (isDisabled) {
|
|
111
|
+
return (
|
|
112
|
+
<Tooltip message="Not enough balance to use this fee option">
|
|
113
|
+
{button}
|
|
114
|
+
</Tooltip>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return button
|
|
76
119
|
}
|
|
77
120
|
|
|
78
121
|
export default FeeOption
|
|
@@ -1,100 +1,60 @@
|
|
|
1
1
|
import { ChevronDown } from "lucide-react"
|
|
2
2
|
import type React from "react"
|
|
3
|
-
import { useRef, useState
|
|
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 { getTokenImageUrl } from "../../tokens.js"
|
|
3
|
+
import { useRef, useState } from "react"
|
|
11
4
|
import { TokenImage } from "./TokenImage.js"
|
|
12
|
-
import {
|
|
13
|
-
import type {
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const normalizeAddress = (address?: string | null): string =>
|
|
20
|
-
(address ?? "").toLowerCase()
|
|
21
|
-
|
|
22
|
-
const isNativeTokenAddress = (address?: string | null): boolean => {
|
|
23
|
-
const normalized = normalizeAddress(address)
|
|
24
|
-
return normalized === ZERO_ADDRESS || normalized === ETH_ADDRESS
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const safeFormatAmountWithSymbol = (option: APIFeeOption): string => {
|
|
28
|
-
const tokenDecimals =
|
|
29
|
-
typeof option.tokenDecimals === "number" &&
|
|
30
|
-
option.tokenDecimals > 0 &&
|
|
31
|
-
option.tokenDecimals <= 18
|
|
32
|
-
? option.tokenDecimals
|
|
33
|
-
: isNativeTokenAddress(option.tokenAddress)
|
|
34
|
-
? 18
|
|
35
|
-
: undefined
|
|
36
|
-
|
|
37
|
-
if (tokenDecimals === undefined) {
|
|
38
|
-
logger.console.warn("[trails-sdk] [FEE-OPTIONS] Missing token decimals", {
|
|
39
|
-
tokenAddress: option.tokenAddress,
|
|
40
|
-
tokenSymbol: option.tokenSymbol,
|
|
41
|
-
})
|
|
42
|
-
return option.tokenSymbol ? `-- ${option.tokenSymbol}` : "--"
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
const formattedAmount = formatRawAmount(option.amount, tokenDecimals)
|
|
47
|
-
return option.tokenSymbol
|
|
48
|
-
? `${formattedAmount} ${option.tokenSymbol}`
|
|
49
|
-
: formattedAmount
|
|
50
|
-
} catch (error) {
|
|
51
|
-
logger.console.warn(
|
|
52
|
-
"[trails-sdk] [FEE-OPTIONS] Failed to format fee option amount",
|
|
53
|
-
{
|
|
54
|
-
option,
|
|
55
|
-
tokenDecimals,
|
|
56
|
-
error,
|
|
57
|
-
},
|
|
58
|
-
)
|
|
59
|
-
return option.tokenSymbol ? `-- ${option.tokenSymbol}` : "--"
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const safeFormatUsdDisplay = (option: APIFeeOption): string => {
|
|
64
|
-
const usdValue =
|
|
65
|
-
(option as { amountUsd?: number }).amountUsd ?? option.amountUSD ?? 0
|
|
66
|
-
return formatUsdAmountDisplay(usdValue)
|
|
67
|
-
}
|
|
5
|
+
import { useSelectedFundMethod } from "../hooks/useSelectedFundMethod.js"
|
|
6
|
+
import type { ProcessedFeeOption } from "../hooks/useSelectedFeeOption.js"
|
|
7
|
+
import { logger } from "../../logger.js"
|
|
8
|
+
import type { FeeOption } from "@0xsequence/trails-api"
|
|
9
|
+
import { FeeOption as FeeOptionComponent } from "./FeeOption.js"
|
|
10
|
+
import { isNativeToken, normalizeAddress } from "../../utils.js"
|
|
11
|
+
import type { Token } from "../hooks/useOriginSelectedToken.js"
|
|
68
12
|
|
|
69
13
|
interface FeeOptionsProps {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
14
|
+
processedFeeOptions: ProcessedFeeOption[]
|
|
15
|
+
selectedFeeOption: FeeOption | null
|
|
16
|
+
setSelectedFeeOption: (token: FeeOption | null) => void
|
|
73
17
|
chainId?: number
|
|
74
18
|
isRefetching?: boolean // When true, the fee quote is stale and being refreshed, so hide the component
|
|
19
|
+
originTokenInfo?: {
|
|
20
|
+
originToken: Token
|
|
21
|
+
originTokenBalance: string
|
|
22
|
+
originTokenAmount: string
|
|
23
|
+
}
|
|
75
24
|
}
|
|
76
25
|
|
|
77
26
|
export const FeeOptions: React.FC<FeeOptionsProps> = ({
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
27
|
+
processedFeeOptions,
|
|
28
|
+
selectedFeeOption,
|
|
29
|
+
setSelectedFeeOption,
|
|
81
30
|
chainId,
|
|
82
31
|
isRefetching,
|
|
32
|
+
originTokenInfo,
|
|
83
33
|
}) => {
|
|
84
|
-
//
|
|
34
|
+
// All hooks must be called before any early returns
|
|
35
|
+
const { selectedFundMethod } = useSelectedFundMethod()
|
|
36
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
37
|
+
const accordionRef = useRef<HTMLDivElement>(null)
|
|
38
|
+
|
|
39
|
+
// Early returns AFTER all hooks - this is safe because hooks are always called first
|
|
40
|
+
// Hide component when fundMethod is qr-code or exchange
|
|
41
|
+
if (selectedFundMethod === "qr-code" || selectedFundMethod === "exchange") {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
85
45
|
// Hide component when fee quote is stale and being refreshed
|
|
86
46
|
if (isRefetching) {
|
|
87
47
|
return null
|
|
88
48
|
}
|
|
89
49
|
|
|
90
50
|
// Don't render if no fee options available
|
|
91
|
-
if (!
|
|
51
|
+
if (!processedFeeOptions || processedFeeOptions.length === 0) {
|
|
92
52
|
return null
|
|
93
53
|
}
|
|
94
54
|
|
|
95
55
|
// Check if there are non-native options before calling hooks
|
|
96
|
-
const hasNonNativeOptions =
|
|
97
|
-
(opt) => !
|
|
56
|
+
const hasNonNativeOptions = processedFeeOptions.some(
|
|
57
|
+
(opt) => !isNativeToken(opt.tokenAddress),
|
|
98
58
|
)
|
|
99
59
|
|
|
100
60
|
// Don't render if only native token is available
|
|
@@ -102,57 +62,27 @@ export const FeeOptions: React.FC<FeeOptionsProps> = ({
|
|
|
102
62
|
return null
|
|
103
63
|
}
|
|
104
64
|
|
|
105
|
-
// NOW we can safely call hooks after all early returns
|
|
106
|
-
const [isOpen, setIsOpen] = useState(false)
|
|
107
|
-
const accordionRef = useRef<HTMLDivElement>(null)
|
|
108
|
-
|
|
109
|
-
// Clear selected fee token when feeOptions change (stale quote)
|
|
110
|
-
// This resets the user's fee selection when they pick a different token
|
|
111
|
-
// Use comprehensive key that includes token addresses and amounts
|
|
112
|
-
const feeOptionsKey = feeOptions
|
|
113
|
-
.map((opt) => `${opt.tokenAddress}-${opt.tokenSymbol}-${opt.amount}`)
|
|
114
|
-
.join("|")
|
|
115
|
-
|
|
116
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: setSelectedFeeToken is stable
|
|
117
|
-
useEffect(() => {
|
|
118
|
-
setSelectedFeeToken(null)
|
|
119
|
-
}, [feeOptionsKey])
|
|
120
|
-
|
|
121
|
-
// Enhance ALL fee options with formatted values and image URLs (including native gas)
|
|
122
|
-
const enhancedFeeOptions: EnhancedFeeOptionType[] = feeOptions.map(
|
|
123
|
-
(option) => ({
|
|
124
|
-
...option,
|
|
125
|
-
amountFormatted: safeFormatAmountWithSymbol(option),
|
|
126
|
-
amountUsdDisplay: safeFormatUsdDisplay(option),
|
|
127
|
-
tokenImageUrl: getTokenImageUrl({
|
|
128
|
-
chainId: option.chainId || chainId,
|
|
129
|
-
contractAddress: option.tokenAddress,
|
|
130
|
-
symbol: option.tokenSymbol,
|
|
131
|
-
}),
|
|
132
|
-
}),
|
|
133
|
-
)
|
|
134
|
-
|
|
135
65
|
// Find native gas option (zero address) for header display
|
|
136
|
-
const nativeGasOption =
|
|
137
|
-
|
|
66
|
+
const nativeGasOption = processedFeeOptions.find((opt) =>
|
|
67
|
+
isNativeToken(opt.tokenAddress),
|
|
138
68
|
)
|
|
139
69
|
|
|
140
70
|
// Find the currently selected option from enhancedFeeOptions for header display
|
|
141
|
-
const getSelectedOption = ():
|
|
71
|
+
const getSelectedOption = (): ProcessedFeeOption | undefined => {
|
|
142
72
|
if (
|
|
143
|
-
|
|
144
|
-
|
|
73
|
+
selectedFeeOption === null ||
|
|
74
|
+
isNativeToken(selectedFeeOption?.tokenAddress)
|
|
145
75
|
) {
|
|
146
76
|
return nativeGasOption
|
|
147
77
|
}
|
|
148
78
|
|
|
149
|
-
if (!
|
|
79
|
+
if (!selectedFeeOption) {
|
|
150
80
|
return undefined
|
|
151
81
|
}
|
|
152
82
|
|
|
153
|
-
const selectedAddress = normalizeAddress(
|
|
83
|
+
const selectedAddress = normalizeAddress(selectedFeeOption.tokenAddress)
|
|
154
84
|
|
|
155
|
-
return
|
|
85
|
+
return processedFeeOptions.find(
|
|
156
86
|
(opt) => normalizeAddress(opt.tokenAddress) === selectedAddress,
|
|
157
87
|
)
|
|
158
88
|
}
|
|
@@ -162,16 +92,16 @@ export const FeeOptions: React.FC<FeeOptionsProps> = ({
|
|
|
162
92
|
// Use nativeGasOption as fallback when selectedOption is null/undefined
|
|
163
93
|
const displayOption = selectedOption || nativeGasOption
|
|
164
94
|
|
|
165
|
-
const
|
|
95
|
+
const handleFeeOptionSelect = (option: FeeOption) => {
|
|
166
96
|
// For native gas (zero/ETH address), set to null to trigger non-gasless flow
|
|
167
|
-
if (
|
|
168
|
-
|
|
97
|
+
if (isNativeToken(option.tokenAddress)) {
|
|
98
|
+
setSelectedFeeOption(null)
|
|
169
99
|
logger.console.log(
|
|
170
100
|
"[trails-sdk] [FEE-SELECT] Selected native gas fee option",
|
|
171
101
|
)
|
|
172
102
|
} else {
|
|
173
103
|
// Use the option directly - it already has all required fields
|
|
174
|
-
|
|
104
|
+
setSelectedFeeOption(option)
|
|
175
105
|
logger.console.log(
|
|
176
106
|
"[trails-sdk] [FEE-SELECT] Selected ERC20 fee option:",
|
|
177
107
|
option,
|
|
@@ -236,19 +166,20 @@ export const FeeOptions: React.FC<FeeOptionsProps> = ({
|
|
|
236
166
|
{isOpen && (
|
|
237
167
|
<div className="mt-2 space-y-1">
|
|
238
168
|
{/* All Fee Options (including native gas) */}
|
|
239
|
-
{
|
|
240
|
-
<
|
|
169
|
+
{processedFeeOptions.map((option, index) => (
|
|
170
|
+
<FeeOptionComponent
|
|
241
171
|
key={`${option.tokenAddress}-${index}`}
|
|
242
172
|
option={option}
|
|
243
173
|
isSelected={
|
|
244
|
-
|
|
245
|
-
?
|
|
246
|
-
|
|
247
|
-
: normalizeAddress(
|
|
174
|
+
isNativeToken(option.tokenAddress)
|
|
175
|
+
? selectedFeeOption === null ||
|
|
176
|
+
isNativeToken(selectedFeeOption?.tokenAddress)
|
|
177
|
+
: normalizeAddress(selectedFeeOption?.tokenAddress) ===
|
|
248
178
|
normalizeAddress(option.tokenAddress)
|
|
249
179
|
}
|
|
250
|
-
onClick={() =>
|
|
180
|
+
onClick={() => handleFeeOptionSelect(option)}
|
|
251
181
|
chainId={option.chainId || chainId}
|
|
182
|
+
originTokenInfo={originTokenInfo}
|
|
252
183
|
/>
|
|
253
184
|
))}
|
|
254
185
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type React from "react"
|
|
2
2
|
import { getChainInfo } from "../../chains.js"
|
|
3
3
|
import { TokenImage } from "./TokenImage.js"
|
|
4
|
-
import {
|
|
4
|
+
import { useGetTokenImageUrl } from "../../tokens.js"
|
|
5
5
|
import { zeroAddress } from "viem"
|
|
6
6
|
|
|
7
7
|
interface NativeGasOptionProps {
|
|
@@ -21,6 +21,8 @@ export const NativeGasOption: React.FC<NativeGasOptionProps> = ({
|
|
|
21
21
|
chainId,
|
|
22
22
|
notEnoughBalance = false,
|
|
23
23
|
}) => {
|
|
24
|
+
const { getTokenImageUrl } = useGetTokenImageUrl()
|
|
25
|
+
|
|
24
26
|
// Helper function to get native symbol for a chain
|
|
25
27
|
const getNativeSymbol = (chainId?: number) => {
|
|
26
28
|
if (!chainId) return "ETH"
|
|
@@ -194,9 +194,9 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
194
194
|
isValidCustomToken,
|
|
195
195
|
isSenderContractOnOrigin,
|
|
196
196
|
isSenderContractOnDestination,
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
197
|
+
processedFeeOptions,
|
|
198
|
+
selectedFeeOption,
|
|
199
|
+
setSelectedFeeOption,
|
|
200
200
|
} = useSendForm({
|
|
201
201
|
account,
|
|
202
202
|
toAmount: tokenAmountForBackend || toAmount, // Use the input amount as target amount for EXACT_OUTPUT
|
|
@@ -672,7 +672,7 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
672
672
|
{/* Payment Request Header */}
|
|
673
673
|
<div className="space-y-1 trails-bg-secondary trails-border-radius-container p-3">
|
|
674
674
|
<div className="flex justify-start">
|
|
675
|
-
<div className="flex items-center font-medium trails-text-primary text-sm whitespace-
|
|
675
|
+
<div className="flex items-center font-medium trails-text-primary text-sm flex-wrap whitespace-break-spaces break-all min-w-full">
|
|
676
676
|
{payMessage}
|
|
677
677
|
</div>
|
|
678
678
|
</div>
|
|
@@ -1019,13 +1019,20 @@ export const Pay: React.FC<PayProps> = ({
|
|
|
1019
1019
|
) : null}
|
|
1020
1020
|
|
|
1021
1021
|
{/* Fee Options */}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1022
|
+
{fundMethod !== "qr-code" && fundMethod !== "exchange" && (
|
|
1023
|
+
<FeeOptions
|
|
1024
|
+
processedFeeOptions={processedFeeOptions}
|
|
1025
|
+
selectedFeeOption={selectedFeeOption}
|
|
1026
|
+
setSelectedFeeOption={(feeOption) => setSelectedFeeOption(feeOption)}
|
|
1027
|
+
chainId={originToken?.chainId}
|
|
1028
|
+
isRefetching={isLoadingQuote}
|
|
1029
|
+
originTokenInfo={{
|
|
1030
|
+
originToken: originToken!,
|
|
1031
|
+
originTokenBalance: originToken?.balance ?? "0",
|
|
1032
|
+
originTokenAmount: tokenAmountForBackend ?? "0",
|
|
1033
|
+
}}
|
|
1034
|
+
/>
|
|
1035
|
+
)}
|
|
1029
1036
|
|
|
1030
1037
|
{/* Quote Details */}
|
|
1031
1038
|
{prepareSendQuote && (
|
|
@@ -39,6 +39,7 @@ import { FeeOptions } from "./FeeOptions.js"
|
|
|
39
39
|
import { generateAaveDepositCalldata } from "../../aave.js"
|
|
40
40
|
import { generateMorphoDepositCalldata } from "../../morpho.js"
|
|
41
41
|
import { TRAILS_ROUTER_PLACEHOLDER_AMOUNT } from "../../trailsRouter.js"
|
|
42
|
+
import { ScreenHeader } from "./ScreenHeader.js"
|
|
42
43
|
|
|
43
44
|
interface PoolDepositProps {
|
|
44
45
|
account?: Account
|
|
@@ -180,9 +181,9 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
|
|
|
180
181
|
isSubmitting,
|
|
181
182
|
buttonText,
|
|
182
183
|
isValidRecipient,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
processedFeeOptions,
|
|
185
|
+
selectedFeeOption,
|
|
186
|
+
setSelectedFeeOption,
|
|
186
187
|
} = useSendForm({
|
|
187
188
|
account,
|
|
188
189
|
toAmount: undefined,
|
|
@@ -320,6 +321,11 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
|
|
|
320
321
|
if (showOriginTokenSelector) {
|
|
321
322
|
return (
|
|
322
323
|
<div className="space-y-2">
|
|
324
|
+
<ScreenHeader
|
|
325
|
+
onBack={() => setShowOriginTokenSelector(false)}
|
|
326
|
+
headerContent="Select Token"
|
|
327
|
+
headerContentAlign="left"
|
|
328
|
+
/>
|
|
323
329
|
<TokenSelector
|
|
324
330
|
onTokenSelect={handleOriginTokenSelect}
|
|
325
331
|
onError={onError}
|
|
@@ -583,13 +589,20 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
|
|
|
583
589
|
) : null}
|
|
584
590
|
|
|
585
591
|
{/* Fee Options */}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
592
|
+
{fundMethod !== "qr-code" && fundMethod !== "exchange" && (
|
|
593
|
+
<FeeOptions
|
|
594
|
+
processedFeeOptions={processedFeeOptions}
|
|
595
|
+
selectedFeeOption={selectedFeeOption as any}
|
|
596
|
+
setSelectedFeeOption={(feeOption) => setSelectedFeeOption(feeOption)}
|
|
597
|
+
chainId={originToken?.chainId}
|
|
598
|
+
isRefetching={isLoadingQuote}
|
|
599
|
+
originTokenInfo={{
|
|
600
|
+
originToken: originToken!,
|
|
601
|
+
originTokenBalance: originToken?.balance ?? "0",
|
|
602
|
+
originTokenAmount: amount ?? "0",
|
|
603
|
+
}}
|
|
604
|
+
/>
|
|
605
|
+
)}
|
|
593
606
|
|
|
594
607
|
{/* Quote Details */}
|
|
595
608
|
{prepareSendQuote && (
|