Skip to content

feat: normalize contract errors into actionable messages - #930

Merged
greatest0fallt1me merged 1 commit into
Predictify-org:mainfrom
Bobai100:feat/normalize-contract-errors
Aug 29, 2026
Merged

feat: normalize contract errors into actionable messages#930
greatest0fallt1me merged 1 commit into
Predictify-org:mainfrom
Bobai100:feat/normalize-contract-errors

Conversation

@Bobai100

Copy link
Copy Markdown
Contributor

Summary

Replaces raw Soroban/Stellar/wallet error strings surfaced to users with structured, human-readable
messages that include a title, explanation, and concrete next step. Previously, errors like tx_bad_auth
or Error(Contract, #4) were shown directly to users or silently swallowed. Now every error path produces
consistent, actionable copy.

Changes

New: lib/stellar/contract-error-normalizer.ts

  • ContractErrorCode — 24 canonical codes covering Soroban contract indices (Landing Page Design #1Wallet Connection Page Design #5 mapped to
    TooManyMarkets, PlanTooLarge, Overflow, MarketNotFound, NotInitialized), Horizon tx_/op_ result codes,
    wallet states, and network errors
  • NormalizedError — structured type with code, title, description, actionHint, isRetryable, isSensitive,
    and raw fields
  • normalizeContractError(rawError?, resultCodes?) — pure, stateless, deterministic; 8-step classification
    precedence (Soroban index → WASM trap → Horizon op codes → Horizon tx codes → confirmation timeout →
    wallet errors → network errors → unknown)
  • normalizeFromFailureType(failureType, rawError?) — bridges TransactionFailureType vocabulary to
    NormalizedError
  • Invariant: title, description, and actionHint never contain addresses, transaction hashes, or raw
    internal error representations

Updated: lib/stellar/transaction.ts

  • normalizeErrorMessage() now routes Horizon error payloads through the normalizer so tx_bad_auth becomes
    "The transaction signature was rejected by the network."
  • New normalizeTransactionError() export for callers that need the full structured result

Updated: hooks/useTransaction.hook.ts

  • Toast title and description use normalizeFromFailureType() — users see "Invalid signature / Make sure
    your wallet is unlocked and try signing again." instead of a raw code
  • Raw error string is preserved in transactionError state for backward compatibility with existing
    callers

Updated: hooks/useWallet.hook.ts

  • The error state exposed to UI holds a normalized, user-safe description
  • Raw error is still passed to console.error for diagnostics

Security & observability

  • isSensitive: true is set on auth-related codes (BadAuth, OpBadAuth, Unknown) — callers must not echo
    the raw field in UI
  • raw is always available for server-side logging without leaking internal details to users
  • isRetryable lets callers decide whether to show a retry button without hardcoding failure type logic

Tests

  • 132 new tests in lib/stellar/tests/contract-error-normalizer.test.ts covering: all 24 error codes,
    Soroban contract indices, WASM trap/panic patterns, Horizon tx_/op_ codes via both resultCodes and raw
    strings, all wallet error patterns, 8-step priority ordering, boundary cases
    (undefined/empty/whitespace/null), sensitive data invariants, isRetryable semantics,
    normalizeFromFailureType mapping, and concurrency/statelessness
  • Updated lib/stellar/tests/transaction.test.ts to assert normalized descriptions instead of raw
    result codes (the old assertion was the bug being fixed)
  • All 139 tests across the 3 affected suites pass

Compatibility

Existing callers of useTransaction are unaffected — transactionError state still holds the raw error
string. Only the toast display layer uses normalized copy. The submitTransaction / pollForConfirmation
return shape is unchanged (error field now holds a user-safe string rather than a raw code, which is
strictly an improvement).

closes #900

Map raw Soroban/Stellar/wallet errors to structured NormalizedError
objects with user-safe title, description, actionHint, isRetryable,
and isSensitive fields.

- Add lib/stellar/contract-error-normalizer.ts
  - ContractErrorCode enum (24 codes: contract indices #1-Predictify-org#5,
    Horizon tx_*/op_* codes, wallet states, network errors)
  - normalizeContractError(rawError?, resultCodes?) — pure, stateless,
    8-step classification precedence
  - normalizeFromFailureType() — maps TransactionFailureType to
    NormalizedError for use in useTransaction hook
  - Invariant: title/description/actionHint never expose addresses,
    hashes, or raw Soroban error internals

- Update lib/stellar/transaction.ts
  - normalizeErrorMessage() routes Horizon payloads through normalizer
  - Add normalizeTransactionError() export for structured output
  - Catch blocks use normalizeContractError().description

- Update hooks/useTransaction.hook.ts
  - Toast title/description use normalizeFromFailureType()
  - Raw error preserved in transactionError state for compatibility
  - Add missing useRef import

- Update hooks/useWallet.hook.ts
  - error state holds normalized description; raw to console.error only

- Add lib/stellar/__tests__/contract-error-normalizer.test.ts (132 tests)
  - All 24 error codes, Soroban indices, WASM trap, Horizon codes,
    wallet patterns, priority ordering, boundary cases, sensitive data
    invariants, isRetryable semantics, concurrency/statelessness

- Update lib/stellar/__tests__/transaction.test.ts
  - Assert normalized description instead of raw tx_bad_auth code

Tests: 139 passed, 0 failed
@drips-wave

drips-wave Bot commented Aug 29, 2026

Copy link
Copy Markdown

@Bobai100 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@vercel

vercel Bot commented Aug 29, 2026

Copy link
Copy Markdown

@Bobai100 is attempting to deploy a commit to the Jagadeesh B's projects Team on Vercel.

A member of the Team first needs to authorize it.

@greatest0fallt1me
greatest0fallt1me merged commit fa6069a into Predictify-org:main Aug 29, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Quality-2][High] Normalize contract errors into actionable messages

2 participants