@jaw.id/ui 0.0.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/.babelrc +12 -0
- package/README.md +55 -0
- package/components.json +22 -0
- package/dist/ccip-Cdkx99iZ.js +147 -0
- package/dist/components/ConnectDialog/index.d.ts +4 -0
- package/dist/components/ConnectDialog/index.d.ts.map +1 -0
- package/dist/components/ConnectDialog/types.d.ts +20 -0
- package/dist/components/ConnectDialog/types.d.ts.map +1 -0
- package/dist/components/DefaultDialog/index.d.ts +14 -0
- package/dist/components/DefaultDialog/index.d.ts.map +1 -0
- package/dist/components/Eip712Dialog/index.d.ts +4 -0
- package/dist/components/Eip712Dialog/index.d.ts.map +1 -0
- package/dist/components/Eip712Dialog/types.d.ts +19 -0
- package/dist/components/Eip712Dialog/types.d.ts.map +1 -0
- package/dist/components/FeeTokenSelector/index.d.ts +26 -0
- package/dist/components/FeeTokenSelector/index.d.ts.map +1 -0
- package/dist/components/OnboardingDialog/index.d.ts +4 -0
- package/dist/components/OnboardingDialog/index.d.ts.map +1 -0
- package/dist/components/OnboardingDialog/types.d.ts +36 -0
- package/dist/components/OnboardingDialog/types.d.ts.map +1 -0
- package/dist/components/OrSeparator/index.d.ts +2 -0
- package/dist/components/OrSeparator/index.d.ts.map +1 -0
- package/dist/components/PermissionDialog/index.d.ts +4 -0
- package/dist/components/PermissionDialog/index.d.ts.map +1 -0
- package/dist/components/PermissionDialog/types.d.ts +49 -0
- package/dist/components/PermissionDialog/types.d.ts.map +1 -0
- package/dist/components/SignatureDialog/index.d.ts +4 -0
- package/dist/components/SignatureDialog/index.d.ts.map +1 -0
- package/dist/components/SignatureDialog/types.d.ts +19 -0
- package/dist/components/SignatureDialog/types.d.ts.map +1 -0
- package/dist/components/SiweDialog/index.d.ts +4 -0
- package/dist/components/SiweDialog/index.d.ts.map +1 -0
- package/dist/components/SiweDialog/types.d.ts +22 -0
- package/dist/components/SiweDialog/types.d.ts.map +1 -0
- package/dist/components/TransactionDialog/DecodedCalldata.d.ts +9 -0
- package/dist/components/TransactionDialog/DecodedCalldata.d.ts.map +1 -0
- package/dist/components/TransactionDialog/index.d.ts +4 -0
- package/dist/components/TransactionDialog/index.d.ts.map +1 -0
- package/dist/components/TransactionDialog/types.d.ts +34 -0
- package/dist/components/TransactionDialog/types.d.ts.map +1 -0
- package/dist/components/ui/accordion.d.ts +8 -0
- package/dist/components/ui/accordion.d.ts.map +1 -0
- package/dist/components/ui/avatar.d.ts +7 -0
- package/dist/components/ui/avatar.d.ts.map +1 -0
- package/dist/components/ui/button.d.ts +11 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/card.d.ts +10 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/checkbox.d.ts +5 -0
- package/dist/components/ui/checkbox.d.ts.map +1 -0
- package/dist/components/ui/dialog.d.ts +18 -0
- package/dist/components/ui/dialog.d.ts.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +26 -0
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/components/ui/form.d.ts +25 -0
- package/dist/components/ui/form.d.ts.map +1 -0
- package/dist/components/ui/input.d.ts +8 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/label.d.ts +5 -0
- package/dist/components/ui/label.d.ts.map +1 -0
- package/dist/components/ui/popover.d.ts +8 -0
- package/dist/components/ui/popover.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.d.ts +6 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/select.d.ts +14 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/separator.d.ts +5 -0
- package/dist/components/ui/separator.d.ts.map +1 -0
- package/dist/components/ui/sheet.d.ts +14 -0
- package/dist/components/ui/sheet.d.ts.map +1 -0
- package/dist/components/ui/spinner.d.ts +3 -0
- package/dist/components/ui/spinner.d.ts.map +1 -0
- package/dist/components/ui/tabs.d.ts +8 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/ui/tooltip.d.ts +8 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/useChainIconURI.d.ts +12 -0
- package/dist/hooks/useChainIconURI.d.ts.map +1 -0
- package/dist/hooks/useDecodedCalldata.d.ts +15 -0
- package/dist/hooks/useDecodedCalldata.d.ts.map +1 -0
- package/dist/hooks/useFeeTokenPrice.d.ts +8 -0
- package/dist/hooks/useFeeTokenPrice.d.ts.map +1 -0
- package/dist/hooks/useGasEstimation.d.ts +77 -0
- package/dist/hooks/useGasEstimation.d.ts.map +1 -0
- package/dist/hooks/useIsMobile.d.ts +2 -0
- package/dist/hooks/useIsMobile.d.ts.map +1 -0
- package/dist/icons/index.d.ts +12 -0
- package/dist/icons/index.d.ts.map +1 -0
- package/dist/index-DlfjdYW4.js +83119 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/react/ReactUIHandler.d.ts +35 -0
- package/dist/react/ReactUIHandler.d.ts.map +1 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/utils/formatAddress.d.ts +18 -0
- package/dist/utils/formatAddress.d.ts.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/justaNameInstance.d.ts +7 -0
- package/dist/utils/justaNameInstance.d.ts.map +1 -0
- package/dist/utils/tokenBalance.d.ts +10 -0
- package/dist/utils/tokenBalance.d.ts.map +1 -0
- package/dist/utils/tokenPrice.d.ts +12 -0
- package/dist/utils/tokenPrice.d.ts.map +1 -0
- package/package.json +67 -0
- package/postcss.config.cjs +6 -0
- package/src/components/ConnectDialog/index.tsx +270 -0
- package/src/components/ConnectDialog/types.ts +34 -0
- package/src/components/DefaultDialog/index.tsx +99 -0
- package/src/components/Eip712Dialog/index.tsx +525 -0
- package/src/components/Eip712Dialog/types.ts +27 -0
- package/src/components/FeeTokenSelector/index.tsx +308 -0
- package/src/components/OnboardingDialog/index.tsx +317 -0
- package/src/components/OnboardingDialog/types.ts +43 -0
- package/src/components/OrSeparator/index.tsx +13 -0
- package/src/components/PermissionDialog/index.tsx +598 -0
- package/src/components/PermissionDialog/types.ts +77 -0
- package/src/components/SignatureDialog/index.tsx +180 -0
- package/src/components/SignatureDialog/types.ts +27 -0
- package/src/components/SiweDialog/index.tsx +231 -0
- package/src/components/SiweDialog/types.ts +34 -0
- package/src/components/TransactionDialog/DecodedCalldata.tsx +79 -0
- package/src/components/TransactionDialog/index.tsx +663 -0
- package/src/components/TransactionDialog/types.ts +54 -0
- package/src/components/ui/accordion.tsx +66 -0
- package/src/components/ui/avatar.tsx +53 -0
- package/src/components/ui/button.tsx +59 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.tsx +32 -0
- package/src/components/ui/dialog.tsx +183 -0
- package/src/components/ui/dropdown-menu.tsx +258 -0
- package/src/components/ui/form.tsx +167 -0
- package/src/components/ui/input.tsx +60 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/popover.tsx +48 -0
- package/src/components/ui/scroll-area.tsx +58 -0
- package/src/components/ui/select.tsx +160 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +169 -0
- package/src/components/ui/spinner.tsx +18 -0
- package/src/components/ui/tabs.tsx +69 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/useChainIconURI.tsx +117 -0
- package/src/hooks/useDecodedCalldata.ts +128 -0
- package/src/hooks/useFeeTokenPrice.tsx +36 -0
- package/src/hooks/useGasEstimation.ts +474 -0
- package/src/hooks/useIsMobile.ts +36 -0
- package/src/icons/index.tsx +114 -0
- package/src/index.ts +19 -0
- package/src/lib/utils.ts +6 -0
- package/src/react/ReactUIHandler.tsx +3004 -0
- package/src/react/index.ts +2 -0
- package/src/styles.css +210 -0
- package/src/utils/formatAddress.ts +24 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/justaNameInstance.ts +25 -0
- package/src/utils/tokenBalance.ts +41 -0
- package/src/utils/tokenPrice.ts +58 -0
- package/tailwind.config.js +130 -0
- package/tsconfig.json +19 -0
- package/tsconfig.lib.json +43 -0
- package/vite.config.ts +45 -0
|
@@ -0,0 +1,663 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Button } from "../ui/button";
|
|
4
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "../ui/accordion";
|
|
5
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip";
|
|
6
|
+
import { DefaultDialog } from "../DefaultDialog";
|
|
7
|
+
import { FeeTokenSelector } from "../FeeTokenSelector";
|
|
8
|
+
import { CopiedIcon, CopyIcon, WalletIcon } from "../../icons";
|
|
9
|
+
import { useState, useEffect } from "react";
|
|
10
|
+
import { formatEther } from "viem";
|
|
11
|
+
import { Info } from "lucide-react";
|
|
12
|
+
import { TransactionDialogProps } from "./types";
|
|
13
|
+
import { useIsMobile, useChainIconURI, useFeeTokenPrice } from "../../hooks";
|
|
14
|
+
import { getJustaNameInstance, getDisplayAddress } from "../../utils";
|
|
15
|
+
import { DecodedCalldata } from "./DecodedCalldata";
|
|
16
|
+
|
|
17
|
+
export const TransactionDialog = ({
|
|
18
|
+
// open,
|
|
19
|
+
// onOpenChange,
|
|
20
|
+
transactions,
|
|
21
|
+
walletAddress,
|
|
22
|
+
gasFee,
|
|
23
|
+
gasFeeLoading,
|
|
24
|
+
gasEstimationError,
|
|
25
|
+
sponsored,
|
|
26
|
+
onConfirm,
|
|
27
|
+
onCancel,
|
|
28
|
+
isProcessing,
|
|
29
|
+
transactionStatus,
|
|
30
|
+
networkName,
|
|
31
|
+
apiKey,
|
|
32
|
+
// Fee token props
|
|
33
|
+
feeTokens,
|
|
34
|
+
feeTokensLoading,
|
|
35
|
+
selectedFeeToken,
|
|
36
|
+
onFeeTokenSelect,
|
|
37
|
+
showFeeTokenSelector,
|
|
38
|
+
isPayingWithErc20,
|
|
39
|
+
// RPC configuration
|
|
40
|
+
mainnetRpcUrl,
|
|
41
|
+
}: TransactionDialogProps) => {
|
|
42
|
+
const isMobile = useIsMobile();
|
|
43
|
+
const [isDataCopied, setIsDataCopied] = useState<{ [key: number]: boolean }>({});
|
|
44
|
+
const [resolvedAddresses, setResolvedAddresses] = useState<Record<string, string>>({});
|
|
45
|
+
|
|
46
|
+
const totalTransactions = transactions.length;
|
|
47
|
+
const isSingleTransaction = totalTransactions === 1;
|
|
48
|
+
const currentTransaction = transactions[0];
|
|
49
|
+
|
|
50
|
+
// Get chain icon using the hook - fetch from capabilities chainMetadata
|
|
51
|
+
const chainIcon = useChainIconURI(currentTransaction?.chainId || 1, apiKey, 24);
|
|
52
|
+
|
|
53
|
+
// Get native token symbol from feeTokens (defaults to ETH if not found)
|
|
54
|
+
const nativeToken = feeTokens?.find(t => t.isNative);
|
|
55
|
+
const nativeSymbol = nativeToken?.symbol || 'ETH';
|
|
56
|
+
|
|
57
|
+
// Fetch native token price dynamically based on the chain's native token symbol
|
|
58
|
+
const nativeTokenPrice = useFeeTokenPrice(nativeSymbol);
|
|
59
|
+
|
|
60
|
+
// Check if there are any selectable payment options
|
|
61
|
+
// If feeTokens is not loaded yet (null/undefined/empty), assume there are selectable options
|
|
62
|
+
const hasSelectablePaymentOption = !feeTokens || feeTokens.length === 0
|
|
63
|
+
? true
|
|
64
|
+
: feeTokens.some(t => t.isSelectable);
|
|
65
|
+
|
|
66
|
+
// Initialize JustaName and resolve addresses
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
const justaName = getJustaNameInstance(mainnetRpcUrl);
|
|
69
|
+
|
|
70
|
+
// Resolve wallet address
|
|
71
|
+
if (walletAddress && currentTransaction?.chainId) {
|
|
72
|
+
justaName.subnames.reverseResolve({
|
|
73
|
+
address: walletAddress as `0x${string}`,
|
|
74
|
+
chainId: currentTransaction.chainId,
|
|
75
|
+
}).then((result) => {
|
|
76
|
+
console.log('result', result);
|
|
77
|
+
if (result) {
|
|
78
|
+
setResolvedAddresses(prev => ({ ...prev, [walletAddress]: result }));
|
|
79
|
+
}
|
|
80
|
+
}).catch(() => {
|
|
81
|
+
// Silently fail if resolution fails
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Resolve transaction 'to' addresses
|
|
86
|
+
transactions.forEach((transaction) => {
|
|
87
|
+
if (transaction.to && transaction.chainId) {
|
|
88
|
+
justaName.subnames.reverseResolve({
|
|
89
|
+
address: transaction.to as `0x${string}`,
|
|
90
|
+
chainId: transaction.chainId,
|
|
91
|
+
}).then((result) => {
|
|
92
|
+
if (result) {
|
|
93
|
+
setResolvedAddresses(prev => ({ ...prev, [transaction.to]: result }));
|
|
94
|
+
}
|
|
95
|
+
}).catch(() => {
|
|
96
|
+
// Silently fail if resolution fails
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}, [walletAddress, transactions, currentTransaction?.chainId]);
|
|
101
|
+
|
|
102
|
+
// Get display addresses - use resolved name or formatted address
|
|
103
|
+
const displayWalletAddress = getDisplayAddress(resolvedAddresses[walletAddress], walletAddress);
|
|
104
|
+
const displayToAddress = currentTransaction?.to
|
|
105
|
+
? getDisplayAddress(resolvedAddresses[currentTransaction.to], currentTransaction.to)
|
|
106
|
+
: '';
|
|
107
|
+
|
|
108
|
+
// Helper function to format value for display
|
|
109
|
+
const formatTransactionValue = (value?: string) => {
|
|
110
|
+
if (!value || value === '0' || value === '0x0') return null;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
// Handle hex strings (0x...)
|
|
114
|
+
if (value.startsWith('0x')) {
|
|
115
|
+
const bigIntValue = BigInt(value);
|
|
116
|
+
return formatEther(bigIntValue);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// If value looks like wei (long number, no decimals), format it from wei to ETH
|
|
120
|
+
if (/^\d+$/.test(value) && value.length > 10) {
|
|
121
|
+
return formatEther(BigInt(value));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// If it's already a decimal string (like "0.001"), return as is
|
|
125
|
+
if (/^\d+\.?\d*$/.test(value) && value.length <= 20) {
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Try to parse as BigInt and format
|
|
130
|
+
const bigIntValue = BigInt(value);
|
|
131
|
+
return formatEther(bigIntValue);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
// If parsing fails, return null to hide the value
|
|
134
|
+
console.warn('Failed to format transaction value:', value, error);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Determine if confirmation is allowed:
|
|
140
|
+
// - Not processing
|
|
141
|
+
// - Gas estimation not loading
|
|
142
|
+
// - No gas estimation error (unless sponsored)
|
|
143
|
+
// - Must have at least one selectable payment option
|
|
144
|
+
const hasInsufficientFunds = !hasSelectablePaymentOption || (gasEstimationError && !sponsored && !isPayingWithErc20);
|
|
145
|
+
const canConfirm = !isProcessing && !gasFeeLoading && !hasInsufficientFunds;
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<DefaultDialog
|
|
149
|
+
// open={open}
|
|
150
|
+
// onOpenChange={!isProcessing ? onOpenChange : undefined}
|
|
151
|
+
open={true}
|
|
152
|
+
onOpenChange={isProcessing ? undefined : () => {
|
|
153
|
+
// Empty handler to prevent dialog close
|
|
154
|
+
}}
|
|
155
|
+
handleClose={isProcessing ? undefined : onCancel}
|
|
156
|
+
header={
|
|
157
|
+
<div className="flex flex-col gap-2.5 p-3.5">
|
|
158
|
+
<p className="text-xs font-bold text-muted-foreground leading-[100%]">
|
|
159
|
+
{new Date().toLocaleDateString('en-US', {
|
|
160
|
+
weekday: 'long',
|
|
161
|
+
day: 'numeric',
|
|
162
|
+
month: 'long'
|
|
163
|
+
})} at {new Date().toLocaleTimeString('en-US', {
|
|
164
|
+
hour: '2-digit',
|
|
165
|
+
minute: '2-digit',
|
|
166
|
+
second: '2-digit',
|
|
167
|
+
timeZoneName: 'short'
|
|
168
|
+
})}
|
|
169
|
+
</p>
|
|
170
|
+
<p className="text-[30px] font-normal leading-[100%] text-foreground">
|
|
171
|
+
{'Review Transaction'}
|
|
172
|
+
</p>
|
|
173
|
+
{totalTransactions > 1 && currentTransaction?.description && (
|
|
174
|
+
<p className="text-sm text-muted-foreground">
|
|
175
|
+
{currentTransaction.action}: {currentTransaction.description}
|
|
176
|
+
</p>
|
|
177
|
+
)}
|
|
178
|
+
</div>
|
|
179
|
+
}
|
|
180
|
+
contentStyle={isMobile ? {
|
|
181
|
+
width: '100%',
|
|
182
|
+
height: '100%',
|
|
183
|
+
maxWidth: 'none',
|
|
184
|
+
maxHeight: 'none',
|
|
185
|
+
overflowY: 'auto',
|
|
186
|
+
} : {
|
|
187
|
+
width: '500px',
|
|
188
|
+
minWidth: '500px',
|
|
189
|
+
maxHeight: !isSingleTransaction ? '85vh' : undefined,
|
|
190
|
+
}}
|
|
191
|
+
>
|
|
192
|
+
<div className={`flex flex-col gap-6 justify-between max-md:h-full ${!isSingleTransaction ? 'overflow-hidden h-full' : ''}`}>
|
|
193
|
+
{isSingleTransaction ? (
|
|
194
|
+
// Single Transaction Layout
|
|
195
|
+
<>
|
|
196
|
+
<div className="flex flex-col gap-3 flex-1 overflow-y-auto min-h-0 max-h-[60vh]">
|
|
197
|
+
{/* From - To */}
|
|
198
|
+
<div className="flex flex-row justify-between items-center gap-2.5 p-3.5 border border-border rounded-[6px]">
|
|
199
|
+
<div className="flex flex-col text-foreground gap-0.5 min-w-0 flex-1">
|
|
200
|
+
<p className="text-xs font-bold leading-[133%]">From</p>
|
|
201
|
+
<div className="flex flex-row items-center gap-1 min-w-0">
|
|
202
|
+
<WalletIcon className="w-3 h-3 flex-shrink-0" stroke="black" />
|
|
203
|
+
<p className="text-base font-normal leading-[150%]">{displayWalletAddress}</p>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
<div className="w-[1px] rounded-full bg-border h-full flex-shrink-0 min-h-[70px]" />
|
|
207
|
+
<div className="flex flex-col text-foreground gap-0.5 min-w-0 flex-1">
|
|
208
|
+
<p className="text-xs font-bold leading-[133%]">To</p>
|
|
209
|
+
<div className="flex flex-row items-center gap-1 min-w-0">
|
|
210
|
+
<WalletIcon className="w-3 h-3 flex-shrink-0" stroke="black" />
|
|
211
|
+
<p className="text-base font-normal leading-[150%]">
|
|
212
|
+
{displayToAddress}
|
|
213
|
+
</p>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
{/* Value */}
|
|
219
|
+
{formatTransactionValue(currentTransaction?.value) && (
|
|
220
|
+
<div className="flex flex-row justify-between items-center gap-2.5 p-3.5 border border-border rounded-[6px]">
|
|
221
|
+
<div className="flex flex-col text-foreground gap-0.5">
|
|
222
|
+
<p className="text-xs font-bold leading-[133%]">Value</p>
|
|
223
|
+
<p className="text-base font-normal leading-[150%]">{formatTransactionValue(currentTransaction?.value)} {nativeSymbol}</p>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
)}
|
|
227
|
+
|
|
228
|
+
{/* Network - Fees */}
|
|
229
|
+
<div className="flex flex-row justify-between items-center gap-2.5 p-3.5 border border-border rounded-[6px]">
|
|
230
|
+
<div className="flex flex-col text-foreground flex-1 gap-0.5">
|
|
231
|
+
<p className="text-xs font-bold leading-[133%]">Network</p>
|
|
232
|
+
<div className="flex flex-row items-center gap-1">
|
|
233
|
+
{chainIcon}
|
|
234
|
+
<p className="text-base font-normal text-ellipsis leading-[150%] truncate">{networkName || 'Ethereum'}</p>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
<div className="w-[1px] rounded-full bg-border h-full min-h-[50px]" />
|
|
238
|
+
<div className="flex flex-col text-foreground flex-1 gap-0.5">
|
|
239
|
+
<div className="flex items-center gap-1">
|
|
240
|
+
<p className="text-xs font-bold leading-[133%]">Network Fees</p>
|
|
241
|
+
<TooltipProvider delayDuration={0}>
|
|
242
|
+
<Tooltip>
|
|
243
|
+
<TooltipTrigger asChild>
|
|
244
|
+
<Info className="size-3 text-muted-foreground cursor-help" />
|
|
245
|
+
</TooltipTrigger>
|
|
246
|
+
<TooltipContent side="top" className="max-w-[200px] text-xs">
|
|
247
|
+
<p>Gas fees paid to network validators to process your transaction. You can pay with {nativeSymbol} or supported tokens.</p>
|
|
248
|
+
</TooltipContent>
|
|
249
|
+
</Tooltip>
|
|
250
|
+
</TooltipProvider>
|
|
251
|
+
</div>
|
|
252
|
+
<div className="flex flex-row items-center w-full justify-between gap-1">
|
|
253
|
+
{gasFeeLoading && !isPayingWithErc20 ? (
|
|
254
|
+
<p className="text-base font-normal text-muted-foreground">Estimating...</p>
|
|
255
|
+
) : gasEstimationError && !sponsored ? (
|
|
256
|
+
<div className="flex flex-col">
|
|
257
|
+
<p className="text-sm text-red-600 font-medium">{gasEstimationError}</p>
|
|
258
|
+
</div>
|
|
259
|
+
) : sponsored ? (
|
|
260
|
+
<div className="flex flex-col">
|
|
261
|
+
<div className="flex items-center gap-2">
|
|
262
|
+
{gasFee && gasFee !== 'sponsored' && nativeTokenPrice > 0 && (
|
|
263
|
+
<div className="flex flex-col line-through text-muted-foreground">
|
|
264
|
+
<p className="text-base font-normal">
|
|
265
|
+
${(nativeTokenPrice * Number(gasFee)).toFixed(4)}
|
|
266
|
+
</p>
|
|
267
|
+
</div>
|
|
268
|
+
)}
|
|
269
|
+
<span className="text-xs font-semibold text-green-600 bg-green-100 px-2 py-0.5 rounded">
|
|
270
|
+
Sponsored
|
|
271
|
+
</span>
|
|
272
|
+
</div>
|
|
273
|
+
<p className="text-xs font-normal text-muted-foreground">
|
|
274
|
+
{gasFee && gasFee !== 'sponsored' ? (() => {
|
|
275
|
+
const gasValue = Number(gasFee);
|
|
276
|
+
if (gasValue > 0 && gasValue < 0.0001) {
|
|
277
|
+
return `< 0.0001 ${nativeSymbol}`;
|
|
278
|
+
}
|
|
279
|
+
return `${gasValue.toFixed(4)} ${nativeSymbol}`;
|
|
280
|
+
})() : 'Gas fees covered'}
|
|
281
|
+
</p>
|
|
282
|
+
</div>
|
|
283
|
+
) : isPayingWithErc20 && selectedFeeToken ? (
|
|
284
|
+
<div className="flex flex-col gap-0.5 w-full">
|
|
285
|
+
<div className="flex items-center justify-between w-full">
|
|
286
|
+
<p className="text-base font-normal text-foreground">
|
|
287
|
+
{/* Show estimated cost from paymaster quote - don't fallback to ETH calculation */}
|
|
288
|
+
{selectedFeeToken.gasCostFormatted ? (
|
|
289
|
+
// For stablecoins like USDC/USDT, the value is approximately USD
|
|
290
|
+
`$${selectedFeeToken.gasCostFormatted}`
|
|
291
|
+
) : (
|
|
292
|
+
<span className="text-muted-foreground">Estimating...</span>
|
|
293
|
+
)}
|
|
294
|
+
</p>
|
|
295
|
+
{/* Inline Fee Token Selector */}
|
|
296
|
+
{showFeeTokenSelector && feeTokens && onFeeTokenSelect && (
|
|
297
|
+
<FeeTokenSelector
|
|
298
|
+
tokens={feeTokens}
|
|
299
|
+
selectedToken={selectedFeeToken}
|
|
300
|
+
onSelect={onFeeTokenSelect}
|
|
301
|
+
isLoading={feeTokensLoading ?? false}
|
|
302
|
+
disabled={isProcessing}
|
|
303
|
+
nativeTokenPrice={nativeTokenPrice}
|
|
304
|
+
estimatedGasEth={gasFee || '0'}
|
|
305
|
+
/>
|
|
306
|
+
)}
|
|
307
|
+
</div>
|
|
308
|
+
{selectedFeeToken.gasCostFormatted && (
|
|
309
|
+
<p className="text-xs font-normal text-muted-foreground">
|
|
310
|
+
Up to {selectedFeeToken.gasCostFormatted} {selectedFeeToken.symbol}
|
|
311
|
+
</p>
|
|
312
|
+
)}
|
|
313
|
+
</div>
|
|
314
|
+
) : gasFee && gasFee !== 'sponsored' ? (
|
|
315
|
+
<div className="flex flex-col gap-0.5 w-full">
|
|
316
|
+
<div className="flex items-center justify-between w-full">
|
|
317
|
+
<p className="text-base font-normal text-foreground">
|
|
318
|
+
{nativeTokenPrice > 0 ? `$${(nativeTokenPrice * Number(gasFee)).toFixed(4)}` : ''}
|
|
319
|
+
</p>
|
|
320
|
+
{/* Inline Fee Token Selector */}
|
|
321
|
+
{showFeeTokenSelector && !sponsored && feeTokens && onFeeTokenSelect && (
|
|
322
|
+
<FeeTokenSelector
|
|
323
|
+
tokens={feeTokens}
|
|
324
|
+
selectedToken={selectedFeeToken ?? null}
|
|
325
|
+
onSelect={onFeeTokenSelect}
|
|
326
|
+
isLoading={feeTokensLoading ?? false}
|
|
327
|
+
disabled={isProcessing}
|
|
328
|
+
nativeTokenPrice={nativeTokenPrice}
|
|
329
|
+
estimatedGasEth={gasFee}
|
|
330
|
+
/>
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
<p className="text-xs font-normal text-muted-foreground">
|
|
334
|
+
{(() => {
|
|
335
|
+
const gasValue = Number(gasFee);
|
|
336
|
+
if (gasValue > 0 && gasValue < 0.0001) {
|
|
337
|
+
return `< 0.0001 ${nativeSymbol}`;
|
|
338
|
+
}
|
|
339
|
+
return `${gasValue.toFixed(4)} ${nativeSymbol}`;
|
|
340
|
+
})()}
|
|
341
|
+
</p>
|
|
342
|
+
</div>
|
|
343
|
+
) : (
|
|
344
|
+
<p className="text-base font-normal text-muted-foreground">Unable to estimate</p>
|
|
345
|
+
)}
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
|
|
350
|
+
{/* Show Data section if data is provided */}
|
|
351
|
+
{currentTransaction?.data && (
|
|
352
|
+
<div className="flex flex-col p-3.5 gap-2.5 border border-border rounded-[6px]">
|
|
353
|
+
<div className="flex flex-row items-center justify-between w-full">
|
|
354
|
+
<p className="text-xs font-bold leading-[133%] text-foreground">Data</p>
|
|
355
|
+
{isDataCopied[0] ? (
|
|
356
|
+
<CopiedIcon width={16} height={16} />
|
|
357
|
+
) : (
|
|
358
|
+
<CopyIcon width={16} height={16} onClick={() => {
|
|
359
|
+
if (typeof window !== 'undefined' && navigator?.clipboard) {
|
|
360
|
+
navigator.clipboard.writeText(currentTransaction?.data ?? '');
|
|
361
|
+
setIsDataCopied({ ...isDataCopied, 0: true });
|
|
362
|
+
setTimeout(() => setIsDataCopied(prev => ({ ...prev, 0: false })), 3000);
|
|
363
|
+
}
|
|
364
|
+
}} className="cursor-pointer" />
|
|
365
|
+
)}
|
|
366
|
+
</div>
|
|
367
|
+
<DecodedCalldata
|
|
368
|
+
to={currentTransaction.to}
|
|
369
|
+
data={currentTransaction.data!}
|
|
370
|
+
chainId={currentTransaction.chainId}
|
|
371
|
+
apiKey={apiKey}
|
|
372
|
+
/>
|
|
373
|
+
</div>
|
|
374
|
+
)}
|
|
375
|
+
|
|
376
|
+
{/* Transaction Status */}
|
|
377
|
+
{transactionStatus && (
|
|
378
|
+
<div className={`text-sm p-3 rounded-lg ${transactionStatus.includes('Error') ? 'bg-red-50 text-red-600' :
|
|
379
|
+
transactionStatus.includes('successfully') ? 'bg-green-50 text-green-600' :
|
|
380
|
+
'bg-blue-50 text-blue-600'
|
|
381
|
+
}`}>
|
|
382
|
+
{transactionStatus}
|
|
383
|
+
</div>
|
|
384
|
+
)}
|
|
385
|
+
</div>
|
|
386
|
+
|
|
387
|
+
{/* Action Buttons */}
|
|
388
|
+
<div className="flex gap-3 p-3.5 max-md:mt-auto flex-shrink-0">
|
|
389
|
+
<Button
|
|
390
|
+
variant="outline"
|
|
391
|
+
onClick={onCancel}
|
|
392
|
+
disabled={isProcessing}
|
|
393
|
+
className="flex-1"
|
|
394
|
+
>
|
|
395
|
+
Cancel
|
|
396
|
+
</Button>
|
|
397
|
+
<Button
|
|
398
|
+
onClick={onConfirm}
|
|
399
|
+
disabled={!canConfirm}
|
|
400
|
+
className="flex-1"
|
|
401
|
+
>
|
|
402
|
+
{hasInsufficientFunds ? 'Insufficient Funds' :
|
|
403
|
+
isProcessing ? 'Processing...' : 'Transact'}
|
|
404
|
+
</Button>
|
|
405
|
+
</div>
|
|
406
|
+
</>
|
|
407
|
+
) : (
|
|
408
|
+
// Multiple Transactions Layout with Accordion
|
|
409
|
+
<>
|
|
410
|
+
<div className="flex flex-col gap-3 flex-1 min-h-0 overflow-y-auto max-h-[60vh]">
|
|
411
|
+
{/* From Address */}
|
|
412
|
+
<div className="p-3.5 border border-border rounded-[6px] flex-shrink-0">
|
|
413
|
+
<p className="text-xs font-bold leading-[133%] text-foreground mb-1">From</p>
|
|
414
|
+
<div className="flex flex-row items-center gap-1">
|
|
415
|
+
<WalletIcon className="w-3 h-3 flex-shrink-0" stroke="black" />
|
|
416
|
+
<p className="text-base font-normal leading-[150%]">{displayWalletAddress}</p>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
|
|
420
|
+
{/* Accordion for Transactions */}
|
|
421
|
+
<div className="flex-1 overflow-y-auto min-h-0">
|
|
422
|
+
<Accordion type="multiple" className="w-full space-y-3" defaultValue={transactions.map((_, index) => `transaction-${index}`)}>
|
|
423
|
+
{transactions.map((transaction, index) => (
|
|
424
|
+
<AccordionItem key={index} value={`transaction-${index}`} className="border border-border rounded-[6px] overflow-hidden">
|
|
425
|
+
<AccordionTrigger className="px-3.5 py-2.5 hover:no-underline">
|
|
426
|
+
<span className="text-base font-medium">Call {index + 1}</span>
|
|
427
|
+
</AccordionTrigger>
|
|
428
|
+
<AccordionContent className="px-3.5 pb-3.5">
|
|
429
|
+
<div className="flex flex-col gap-3">
|
|
430
|
+
{/* Interacting with (To) */}
|
|
431
|
+
<div className="flex flex-col gap-1 border border-border rounded-[6px] p-2">
|
|
432
|
+
<p className="text-xs font-bold leading-[133%] text-black">Interacting with (to)</p>
|
|
433
|
+
<div className="flex flex-row items-center gap-1">
|
|
434
|
+
<WalletIcon className="w-3 h-3 flex-shrink-0" stroke="black" />
|
|
435
|
+
<p className="text-sm font-normal leading-[150%]">
|
|
436
|
+
{getDisplayAddress(resolvedAddresses[transaction.to], transaction.to)}
|
|
437
|
+
</p>
|
|
438
|
+
</div>
|
|
439
|
+
</div>
|
|
440
|
+
|
|
441
|
+
{/* Value */}
|
|
442
|
+
{formatTransactionValue(transaction.value) && (
|
|
443
|
+
<div className="flex items-center gap-2 border border-border rounded-[6px] p-2">
|
|
444
|
+
<div className="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0">
|
|
445
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
|
446
|
+
<path d="M10 2L3 10L10 14L17 10L10 2Z" fill="currentColor" className="text-primary" />
|
|
447
|
+
</svg>
|
|
448
|
+
</div>
|
|
449
|
+
<div className="flex-1">
|
|
450
|
+
<p className="text-xs font-bold leading-[133%] text-muted-foreground">Value</p>
|
|
451
|
+
<div className="flex items-baseline gap-2">
|
|
452
|
+
<p className="text-base font-normal">{formatTransactionValue(transaction.value)} {nativeSymbol}</p>
|
|
453
|
+
{nativeTokenPrice > 0 && (
|
|
454
|
+
<p className="text-sm text-muted-foreground">
|
|
455
|
+
${(Number(formatTransactionValue(transaction.value)) * nativeTokenPrice).toFixed(2)}
|
|
456
|
+
</p>
|
|
457
|
+
)}
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
</div>
|
|
461
|
+
)}
|
|
462
|
+
|
|
463
|
+
{/* Data */}
|
|
464
|
+
{transaction.data && (
|
|
465
|
+
<div className="flex flex-col gap-1 border border-border rounded-[6px] p-2">
|
|
466
|
+
<div className="flex items-center justify-between mb-2">
|
|
467
|
+
<p className="text-xs font-bold leading-[133%] text-muted-foreground">Data</p>
|
|
468
|
+
{isDataCopied[index] ? (
|
|
469
|
+
<CopiedIcon width={16} height={16} />
|
|
470
|
+
) : (
|
|
471
|
+
<CopyIcon
|
|
472
|
+
width={16}
|
|
473
|
+
height={16}
|
|
474
|
+
onClick={(e) => {
|
|
475
|
+
e.stopPropagation();
|
|
476
|
+
if (typeof window !== 'undefined' && navigator?.clipboard) {
|
|
477
|
+
navigator.clipboard.writeText(transaction.data ?? '');
|
|
478
|
+
setIsDataCopied({ ...isDataCopied, [index]: true });
|
|
479
|
+
setTimeout(() => setIsDataCopied(prev => ({ ...prev, [index]: false })), 3000);
|
|
480
|
+
}
|
|
481
|
+
}}
|
|
482
|
+
className="cursor-pointer"
|
|
483
|
+
/>
|
|
484
|
+
)}
|
|
485
|
+
</div>
|
|
486
|
+
<DecodedCalldata
|
|
487
|
+
to={transaction.to}
|
|
488
|
+
data={transaction.data!}
|
|
489
|
+
chainId={transaction.chainId}
|
|
490
|
+
apiKey={apiKey}
|
|
491
|
+
/>
|
|
492
|
+
</div>
|
|
493
|
+
)}
|
|
494
|
+
</div>
|
|
495
|
+
</AccordionContent>
|
|
496
|
+
</AccordionItem>
|
|
497
|
+
))}
|
|
498
|
+
</Accordion>
|
|
499
|
+
</div>
|
|
500
|
+
|
|
501
|
+
{/* Transaction Status */}
|
|
502
|
+
{transactionStatus && (
|
|
503
|
+
<div className={`text-sm p-3 rounded-lg ${transactionStatus.includes('Error') ? 'bg-red-50 text-red-600' :
|
|
504
|
+
transactionStatus.includes('successfully') ? 'bg-green-50 text-green-600' :
|
|
505
|
+
'bg-blue-50 text-blue-600'
|
|
506
|
+
}`}>
|
|
507
|
+
{transactionStatus}
|
|
508
|
+
</div>
|
|
509
|
+
)}
|
|
510
|
+
</div>
|
|
511
|
+
|
|
512
|
+
{/* Fixed Bottom Section */}
|
|
513
|
+
<div className="border-t pt-3 space-y-3 flex-shrink-0">
|
|
514
|
+
{/* Network and Fees */}
|
|
515
|
+
<div className="flex flex-row justify-between items-center gap-2.5 p-3.5 border border-border rounded-[6px]">
|
|
516
|
+
<div className="flex flex-col text-foreground flex-1 gap-0.5">
|
|
517
|
+
<p className="text-xs font-bold leading-[133%]">Network</p>
|
|
518
|
+
<div className="flex flex-row items-center gap-1">
|
|
519
|
+
{chainIcon}
|
|
520
|
+
<p className="text-base font-normal text-ellipsis leading-[150%] truncate">{networkName || 'Ethereum'}</p>
|
|
521
|
+
</div>
|
|
522
|
+
</div>
|
|
523
|
+
<div className="w-[1px] rounded-full bg-border h-full min-h-[50px]" />
|
|
524
|
+
<div className="flex flex-col text-foreground flex-1 gap-0.5">
|
|
525
|
+
<div className="flex items-center gap-1">
|
|
526
|
+
<p className="text-xs font-bold leading-[133%]">Network Fees</p>
|
|
527
|
+
<TooltipProvider delayDuration={0}>
|
|
528
|
+
<Tooltip>
|
|
529
|
+
<TooltipTrigger asChild>
|
|
530
|
+
<Info className="size-3 text-muted-foreground cursor-help" />
|
|
531
|
+
</TooltipTrigger>
|
|
532
|
+
<TooltipContent side="top" className="max-w-[200px] text-xs">
|
|
533
|
+
<p>Gas fees paid to network validators to process your transaction. You can pay with {nativeSymbol} or supported tokens.</p>
|
|
534
|
+
</TooltipContent>
|
|
535
|
+
</Tooltip>
|
|
536
|
+
</TooltipProvider>
|
|
537
|
+
</div>
|
|
538
|
+
<div className="flex flex-row items-center w-full justify-between gap-1">
|
|
539
|
+
{gasFeeLoading && !isPayingWithErc20 ? (
|
|
540
|
+
<p className="text-base font-normal text-muted-foreground">Estimating...</p>
|
|
541
|
+
) : gasEstimationError && !sponsored ? (
|
|
542
|
+
<div className="flex flex-col">
|
|
543
|
+
<p className="text-sm text-red-600 font-medium">{gasEstimationError}</p>
|
|
544
|
+
</div>
|
|
545
|
+
) : sponsored ? (
|
|
546
|
+
<div className="flex flex-col">
|
|
547
|
+
<div className="flex items-center gap-2">
|
|
548
|
+
{gasFee && gasFee !== 'sponsored' && nativeTokenPrice > 0 && (
|
|
549
|
+
<div className="flex flex-col line-through text-muted-foreground">
|
|
550
|
+
<p className="text-base font-normal">
|
|
551
|
+
${(nativeTokenPrice * Number(gasFee)).toFixed(4)}
|
|
552
|
+
</p>
|
|
553
|
+
</div>
|
|
554
|
+
)}
|
|
555
|
+
<span className="text-xs font-semibold text-green-600 bg-green-100 px-2 py-0.5 rounded">
|
|
556
|
+
Sponsored
|
|
557
|
+
</span>
|
|
558
|
+
</div>
|
|
559
|
+
<p className="text-xs font-normal text-muted-foreground">
|
|
560
|
+
{gasFee && gasFee !== 'sponsored' ? (() => {
|
|
561
|
+
const gasValue = Number(gasFee);
|
|
562
|
+
if (gasValue > 0 && gasValue < 0.0001) {
|
|
563
|
+
return `< 0.0001 ${nativeSymbol}`;
|
|
564
|
+
}
|
|
565
|
+
return `${gasValue.toFixed(4)} ${nativeSymbol}`;
|
|
566
|
+
})() : 'Gas fees covered'}
|
|
567
|
+
</p>
|
|
568
|
+
</div>
|
|
569
|
+
) : isPayingWithErc20 && selectedFeeToken ? (
|
|
570
|
+
<div className="flex flex-col gap-0.5 w-full">
|
|
571
|
+
<div className="flex items-center justify-between w-full">
|
|
572
|
+
<p className="text-base font-normal text-foreground">
|
|
573
|
+
{/* Show estimated cost from paymaster quote - don't fallback to ETH calculation */}
|
|
574
|
+
{selectedFeeToken.gasCostFormatted ? (
|
|
575
|
+
// For stablecoins like USDC/USDT, the value is approximately USD
|
|
576
|
+
`$${selectedFeeToken.gasCostFormatted}`
|
|
577
|
+
) : (
|
|
578
|
+
<span className="text-muted-foreground">Estimating...</span>
|
|
579
|
+
)}
|
|
580
|
+
</p>
|
|
581
|
+
{/* Inline Fee Token Selector */}
|
|
582
|
+
{showFeeTokenSelector && feeTokens && onFeeTokenSelect && (
|
|
583
|
+
<FeeTokenSelector
|
|
584
|
+
tokens={feeTokens}
|
|
585
|
+
selectedToken={selectedFeeToken}
|
|
586
|
+
onSelect={onFeeTokenSelect}
|
|
587
|
+
isLoading={feeTokensLoading ?? false}
|
|
588
|
+
disabled={isProcessing}
|
|
589
|
+
nativeTokenPrice={nativeTokenPrice}
|
|
590
|
+
estimatedGasEth={gasFee || '0'}
|
|
591
|
+
/>
|
|
592
|
+
)}
|
|
593
|
+
</div>
|
|
594
|
+
{selectedFeeToken.gasCostFormatted && (
|
|
595
|
+
<p className="text-xs font-normal text-muted-foreground">
|
|
596
|
+
Up to {selectedFeeToken.gasCostFormatted} {selectedFeeToken.symbol}
|
|
597
|
+
</p>
|
|
598
|
+
)}
|
|
599
|
+
</div>
|
|
600
|
+
) : gasFee && gasFee !== 'sponsored' ? (
|
|
601
|
+
<div className="flex flex-col gap-0.5 w-full">
|
|
602
|
+
<div className="flex items-center justify-between w-full">
|
|
603
|
+
<p className="text-base font-normal text-foreground">
|
|
604
|
+
{nativeTokenPrice > 0 ? `$${(nativeTokenPrice * Number(gasFee)).toFixed(4)}` : ''}
|
|
605
|
+
</p>
|
|
606
|
+
{/* Inline Fee Token Selector */}
|
|
607
|
+
{showFeeTokenSelector && !sponsored && feeTokens && onFeeTokenSelect && (
|
|
608
|
+
<FeeTokenSelector
|
|
609
|
+
tokens={feeTokens}
|
|
610
|
+
selectedToken={selectedFeeToken ?? null}
|
|
611
|
+
onSelect={onFeeTokenSelect}
|
|
612
|
+
isLoading={feeTokensLoading ?? false}
|
|
613
|
+
disabled={isProcessing}
|
|
614
|
+
nativeTokenPrice={nativeTokenPrice}
|
|
615
|
+
estimatedGasEth={gasFee}
|
|
616
|
+
/>
|
|
617
|
+
)}
|
|
618
|
+
</div>
|
|
619
|
+
<p className="text-xs font-normal text-muted-foreground">
|
|
620
|
+
{(() => {
|
|
621
|
+
const gasValue = Number(gasFee);
|
|
622
|
+
if (gasValue > 0 && gasValue < 0.0001) {
|
|
623
|
+
return `< 0.0001 ${nativeSymbol}`;
|
|
624
|
+
}
|
|
625
|
+
return `${gasValue.toFixed(4)} ${nativeSymbol}`;
|
|
626
|
+
})()}
|
|
627
|
+
</p>
|
|
628
|
+
</div>
|
|
629
|
+
) : (
|
|
630
|
+
<p className="text-base font-normal text-muted-foreground">Unable to estimate</p>
|
|
631
|
+
)}
|
|
632
|
+
</div>
|
|
633
|
+
</div>
|
|
634
|
+
</div>
|
|
635
|
+
|
|
636
|
+
{/* Action Buttons */}
|
|
637
|
+
<div className="flex gap-3 px-3.5 flex-shrink-0">
|
|
638
|
+
<Button
|
|
639
|
+
variant="outline"
|
|
640
|
+
onClick={onCancel}
|
|
641
|
+
disabled={isProcessing}
|
|
642
|
+
className="flex-1"
|
|
643
|
+
>
|
|
644
|
+
Cancel
|
|
645
|
+
</Button>
|
|
646
|
+
<Button
|
|
647
|
+
onClick={onConfirm}
|
|
648
|
+
disabled={!canConfirm}
|
|
649
|
+
className="flex-1"
|
|
650
|
+
>
|
|
651
|
+
{gasEstimationError && !sponsored ? 'Insufficient Funds' :
|
|
652
|
+
isProcessing ? 'Processing...' : 'Transact'}
|
|
653
|
+
</Button>
|
|
654
|
+
</div>
|
|
655
|
+
</div>
|
|
656
|
+
</>
|
|
657
|
+
)}
|
|
658
|
+
</div>
|
|
659
|
+
</DefaultDialog>
|
|
660
|
+
)
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
export * from './types';
|