From dece11191a8b77dd09d8d040ba526e04f020d0b8 Mon Sep 17 00:00:00 2001 From: Chris Hunter Date: Tue, 20 Oct 2026 12:05:01 -0400 Subject: [PATCH 1/2] fix(vibenet): explain reverting account transactions Gas estimates were showing viem's "RPC Request failed" instead of the node's revert reason, and an included revert printed the hash as plain text. --- .../account/components/TransactionModal.tsx | 56 +++++-- .../account/library/revertReason.test.ts | 100 +++++++++++++ .../demos/account/library/revertReason.ts | 141 ++++++++++++++++++ .../demos/account/useAccountEngine.tsx | 44 ++---- 4 files changed, 300 insertions(+), 41 deletions(-) create mode 100644 app/vibenet/demos/account/library/revertReason.test.ts create mode 100644 app/vibenet/demos/account/library/revertReason.ts diff --git a/app/vibenet/demos/account/components/TransactionModal.tsx b/app/vibenet/demos/account/components/TransactionModal.tsx index 74f5d8a..d44b911 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) { @@ -1014,7 +1026,7 @@ function SubmittedBody({
- {error} +
); @@ -1057,6 +1069,24 @@ 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 (
- +
@@ -1074,16 +1077,17 @@ type DialogError = { message: string; txHash?: Hex }; function ErrorCopy({ error }: { error: DialogError }) { if (!error.txHash) return error.message; return ( - <> - {error.message} ( + {error.message} + {error.txHash} - ). - + + ); }