diff --git a/app/vibenet/demos/account/components/TransactionModal.tsx b/app/vibenet/demos/account/components/TransactionModal.tsx index 74f5d8a..af45c64 100644 --- a/app/vibenet/demos/account/components/TransactionModal.tsx +++ b/app/vibenet/demos/account/components/TransactionModal.tsx @@ -64,6 +64,7 @@ import { conciseError, EstimateRevertedError, TxPendingError, + TxRevertedError, useAccountEngine, } from '../useAccountEngine'; @@ -155,7 +156,7 @@ export function TransactionModal({ onClose, preset }: TransactionModalProps) { const [estimateBlocked, setEstimateBlocked] = useState(null); const [payerRetry, setPayerRetry] = useState(null); // Error UI is local to this dialog — nothing leaks onto the page behind it. - const [error, setError] = useState(''); + const [error, setError] = useState(null); const [txStep, setTxStep] = useState<'build' | 'review' | 'submitted'>( preset?.calls ? 'review' : 'build', ); @@ -218,13 +219,21 @@ export function TransactionModal({ onClose, preset }: TransactionModalProps) { }); }; - const surfaceSendError = (message: string) => { - setError(message); - toast.error(message); + const surfaceSendError = (message: string, txHash?: Hex) => { + setError({ message, txHash }); + toast.error(txHash ? `${message} (${txHash}).` : message); }; const sendError = (err: unknown) => { - if (err instanceof EstimateRevertedError) setEstimateBlocked(err.reason); + if (err instanceof TxRevertedError) { + surfaceSendError('Transaction reverted onchain', err.txHash); + return; + } + if (err instanceof EstimateRevertedError) { + setEstimateBlocked(err.reason); + surfaceSendError(err.reason); + return; + } const e = err as { message?: string; name?: string }; surfaceSendError(conciseError(e.message ?? String(err))); }; @@ -364,7 +373,7 @@ export function TransactionModal({ onClose, preset }: TransactionModalProps) { const confirmSend = async (forceEstimate = false, retry: PayerRetry | null = null) => { const mode = retry?.kind === 'usdv' ? 'usdv' : gasMode; if (retry?.kind === 'usdv') setGasMode('usdv'); - setError(''); + setError(null); setEstimateBlocked(null); setPayerRetry(null); setTxStep('submitted'); @@ -404,7 +413,7 @@ export function TransactionModal({ onClose, preset }: TransactionModalProps) { const startSend = () => { if (!callsValid || !activeSigner) return; - setError(''); + setError(null); setResult(null); setTxStep('review'); }; @@ -436,12 +445,15 @@ export function TransactionModal({ onClose, preset }: TransactionModalProps) { size="sm" onClick={() => { setTxStep('review'); - setError(''); + setError(null); setPayerRetry(null); }} > Back + {error.txHash ? ( + + ) : null} @@ -461,7 +473,7 @@ export function TransactionModal({ onClose, preset }: TransactionModalProps) { size="sm" onClick={() => { setTxStep('build'); - setError(''); + setError(null); }} disabled={signing} > @@ -867,7 +879,7 @@ type ReviewBodyProps = { gasEstimate: number; nonceless: boolean; txSigner: WalletSigner | null; - error: string; + error: DialogError | null; }; function ReviewBody({ @@ -943,7 +955,7 @@ function ReviewBody({ {error ? (
- {error} +
) : (
@@ -991,7 +1003,7 @@ function SubmittedBody({ }: { signing: boolean; submitStatus: '' | 'submitting' | 'confirming'; - error: string; + error: DialogError | null; result: SubmittedResult; }) { if (signing) { @@ -1013,8 +1025,11 @@ function SubmittedBody({ return (
- - {error} + +
); @@ -1057,6 +1072,25 @@ function SubmittedBody({ ); } +type DialogError = { message: string; txHash?: Hex }; + +function ErrorCopy({ error }: { error: DialogError }) { + if (!error.txHash) return error.message; + return ( + + {error.message} + + {error.txHash} + + + ); +} + function ErrorGlyph({ size = 16 }: { size?: number }) { return (