feat(web): map simulate errors to ErrorBanner via parseContractError (#561) - #630
Open
davismas23 wants to merge 2 commits into
Open
Conversation
…atix-Protocol#561) Simulation failures surfaced by queryContract (used for all read-only contract calls, including getPosition) were re-thrown as raw SDK error strings. UI components that called getPosition had to decide themselves how to handle them — PositionPanel was just forwarding the raw message and had no toast notification at all. Changes ------- ### apps/web/lib/contract-client.ts queryContract now parses every caught error through parseContractError before re-throwing: catch (error) { const { parseContractError } = await import('@/lib/errors'); throw new Error(parseContractError(error)); } This means: - Soroban host traps like Error(Contract, Vatix-Protocol#6) become 'Deposits are currently closed for this market.' - 'Simulation failed: <raw>...' becomes 'Transaction would fail: <raw>...' - Wallet rejections become 'Request was rejected in the wallet.' - Unrecognised errors pass through unchanged The import is dynamic (same pattern as the Freighter import in invokeContract) so this module stays safe on server-rendered paths. ### apps/web/components/PositionPanel.tsx - Added useToast import from @/context/ToastContext - Added parseContractError import from @/lib/errors (defensive layer in case any caller re-wraps before reaching here) - useToast() destructured; showToast added to the useCallback deps array - catch block now calls: const reason = parseContractError(err); setError(reason); // inline role='alert' block showToast(reason, 'error'); // global ErrorBanner toast - Updated JSDoc to document the simulation-error surfacing behaviour Error flow (after this PR) -------------------------- getPosition() └─ queryContract() └─ simulateTransaction() ← throws on simulate error └─ parseContractError() ← maps to user-facing copy └─ throw new Error(reason) PositionPanel.refresh() catches: const reason = parseContractError(err) ← already parsed, passthrough setError(reason) → inline <p role='alert'> showToast(reason) → global toast banner (auto-dismisses after 6 s) Acceptance criteria ------------------- - Failed simulate shows recoverable message (inline + toast) Closes Vatix-Protocol#561
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #561
Wires contract simulation failures through the existing
parseContractErrorhelper and surfaces them in both the inline error alert and the global toast (ErrorBanner) so users see a short, human-readable message rather than a raw Soroban SDK error string.Problem
queryContract(used by every read-only call includinggetPosition) was re-throwing raw errors.PositionPanel's catch block forwardederr.messagedirectly without mapping it throughparseContractError, and had no toast notification at all.A Soroban simulation error like:
was displayed verbatim. After this PR it becomes:
Changes
apps/web/lib/contract-client.tsqueryContractnow parses every thrown error throughparseContractErrorbefore re-throwing:MARKET_ERROR_MESSAGESSimulation failed: ...→Transaction would fail: ...Request was rejected in the wallet.The import is dynamic (same pattern as the Freighter import in
invokeContract) so this module stays safe on server-rendered paths.apps/web/components/PositionPanel.tsxuseToastfrom@/context/ToastContextparseContractErrorfrom@/lib/errors(defensive passthrough if already parsed)useToast()destructured in the component bodyshowToastadded to theuseCallbackdependency arraycatchblock now:Error flow after this PR
Acceptance criteria
Notes
useToast/parseContractError