Skip to content

feat: map contract error codes 1-5 to human-readable messages in the SDK - #346

Open
theo372001 wants to merge 1 commit into
crackedstudio:mainfrom
theo372001:feat/issue-53-contract-error-messages
Open

feat: map contract error codes 1-5 to human-readable messages in the SDK#346
theo372001 wants to merge 1 commit into
crackedstudio:mainfrom
theo372001:feat/issue-53-contract-error-messages

Conversation

@theo372001

Copy link
Copy Markdown

What changed

Adds describeContractError(code) to packages/client/src/errors.ts, covering the 5 stable error codes documented in contracts/sharibo/src/lib.rs (CircleNotFound, RoundNotFunded, WrongRoundTag, AlreadyClaimed, InvalidProof) — each returning a name, a user-facing sentence, and a hint. Adds parseContractErrorCode() to extract the numeric code out of a signAndSend() failure, and describeError() to tie both together with a raw-string fallback for anything unrecognized. Wires this into the app's claimAgain deliberate-replay demo so it now shows "AlreadyClaimed: ..." prose instead of the raw Error(Contract, #4).

Why

Closes #53

When the contract rejects a claim, users currently see Soroban's raw Error(Contract, #4) instead of something readable.

Implementation notes

  • Failure shape: signAndSend()'s underlying @stellar/stellar-sdk (AssembledTransaction) throws a plain Error whose .message embeds Error(Contract, #<code>) — I traced this through the installed SDK's contract/assembled_transaction.js (simulationData getter → SimulationFailed with the RPC's raw diagnostic string) and contract/utils.js, which defines its own contractErrorPattern = /Error\(Contract, #(\d+)\)/ for exactly this purpose. This matches the existing raw-string checks already in this repo at scripts/e2e.ts's replay path (message.includes("Error(Contract, #4)")) and scripts/smoke.ts (msg.includes("Error(Contract, #1)")). I was not able to run scripts/e2e.ts live — it requires a deployed testnet contract/admin secret I don't have, and the script currently has unrelated pre-existing syntax errors around its funding/proof-generation steps (duplicated blocks, mismatched braces) that predate this change and are out of scope here. parseContractErrorCode()'s regex is a superset of the SDK's own pattern and is covered by unit tests using both a realistic multi-line simulation-failure message and the exact minimal strings already used in e2e.ts/smoke.ts.
  • getErrorMessage in app/src/App.tsx: this function was already called in claimAgain's catch block and in the Freighter fundMember path, but was never defined anywhere in the file (a pre-existing bug — the app didn't compile). I added it as a small wrapper around the new describeError(), mirroring the existing toUiError helper's Friendbot special-case.
  • index.ts export: packages/client/src/index.ts didn't re-export errors.ts even though App.tsx and scripts/e2e.ts already import ContractError/RpcError from @sharibo/client — also pre-existing and fixed here since it's required for this feature to be importable at all.
  • This package (packages/client) has substantial pre-existing, unrelated breakage on main (missing exports in tree.ts/prove.ts/config.ts, an undefined withRetry in contract.ts, a duplicate MerkleTree identifier in tree.test.ts, etc. — confirmed via tsc --noEmit before and after this change, identical error list both times). I did not touch any of that; it's out of scope for Map on-chain error codes #1–#5 to human-readable messages in the SDK #53.

Testing

  • packages/client/src/errors.test.ts (new, 12 cases covering all 5 known codes, 4 unknown/out-of-scope codes 0/6/7/8/999, the exact signAndSend-shaped failure string, the minimal e2e.ts/smoke.ts strings, a ContractError instance, non-contract errors, and non-Error throws) — run via npx tsx --test src/errors.test.ts (the package's own vitest run doesn't pick up any of this package's node:test-style test files, including the pre-existing ones, due to a pre-existing test-runner mismatch unrelated to this change) — 12/12 passing.
  • npx tsc --noEmit in packages/client and app — my changes introduce zero new type errors versus the pre-existing baseline (verified by diffing output before/after via git stash).
  • Circuit tests (not touched)
  • Contract tests (not touched)
  • E2E against testnet — not run; no deployed contract/testnet credentials available in this environment, and the script has pre-existing unrelated syntax errors (see above)
  • App tested manually (cd app && npm run dev) — not run end-to-end; no live testnet contract available to exercise the actual replay demo in the browser. Unit-verified the exact string transformation instead (Error(Contract, #4)"AlreadyClaimed: this proof's nullifier was already used; ...") via the describeError test cases above.
  • Docs-only: N/A (code changed)

Screenshots

Not applicable — no visual/layout change, only the text shown in the existing rejection banner.

Adds describeContractError(code) to packages/client/src/errors.ts, covering
the 5 stable error codes documented in contracts/sharibo/src/lib.rs
(CircleNotFound, RoundNotFunded, WrongRoundTag, AlreadyClaimed,
InvalidProof), each with a name, a user-facing sentence, and a hint.

parseContractErrorCode() extracts the numeric code from a signAndSend()
failure by matching the `Error(Contract, #N)` string @stellar/stellar-sdk's
own AssembledTransaction embeds in a simulation-failure Error's message
(traced through node_modules/@stellar/stellar-sdk's assembled_transaction.js
and utils.js's `contractErrorPattern`, and cross-checked against the
existing raw-string checks in scripts/e2e.ts's replay path and
scripts/smoke.ts). describeError() ties the two together and falls back to
the raw message for unrecognized codes or non-contract errors.

Wires this into the app's claimAgain deliberate-replay demo (previously
displaying the raw Error(Contract, crackedstudio#4)) via a new getErrorMessage() helper
in App.tsx — filling in a function that was already referenced there and in
fundMember's Freighter path but never defined.

Also re-exports errors.js from packages/client/src/index.ts, which App.tsx
and scripts/e2e.ts already import ContractError/RpcError from but which
wasn't actually re-exported.

Closes crackedstudio#53
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.

Map on-chain error codes #1–#5 to human-readable messages in the SDK

1 participant