Skip to content

chore(frontend): DO NOT MERGE — simulate chain-fusion-signer failures#13146

Draft
sbpublic wants to merge 9 commits into
fix/frontend/signer-unavailable-toastfrom
demo/frontend/simulate-cfs-sign-failures
Draft

chore(frontend): DO NOT MERGE — simulate chain-fusion-signer failures#13146
sbpublic wants to merge 9 commits into
fix/frontend/signer-unavailable-toastfrom
demo/frontend/simulate-cfs-sign-failures

Conversation

@sbpublic

@sbpublic sbpublic commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Motivation

⚠️ DO NOT MERGE. This branch is a demo/QA harness, stacked on #13145 (which is itself stacked on #13144). It exists only to reproduce the signer error paths without a real backend cycles outage.

The signer-unavailable / per-user-limit toasts (#13145) and the cfs_sign event (#13144) are normally only triggered by an actual chain-fusion-signer failure, which is hard to produce on demand. This adds a tiny, opt-in simulator so a reviewer can trigger each error path from the running app and confirm the UX + analytics behave as intended.

Changes

  • src/frontend/src/lib/utils/signer-failure-simulator.utils.tssimulateSignerFailureIfEnabled(), which throws a chosen error when opted in.
  • src/frontend/src/lib/api/signer.api.ts — calls it at the start of each signing call (signTransaction, signBtc, sendBtc, signMessage, signPrehash, signWithSchnorr, genericSignWithEcdsa). Address/balance reads are intentionally left untouched so the wallet still loads.

Because the error is thrown from inside the tracked signer call, it flows through the exact real path: cfs_sign is emitted (PR #13144) → the error propagates to the wizard/service catch → the toast logic (PR #13145) runs.

Safety: only active when LOCAL || STAGING (never on the ic production build) and only when explicitly opted in. No production code path can reach it.

How to reproduce

1. Enable a simulated signer failure

Pick a mode and set it one of two ways (the query param wins):

  • URL query param (shareable): append ?simulate_signer_failure=<mode> to the app URL.
  • Console / localStorage (persists across navigations):
    localStorage.setItem('OISY_SIMULATE_SIGNER_FAILURE', '<mode>'); // then reload
    // disable again:
    localStorage.removeItem('OISY_SIMULATE_SIGNER_FAILURE');
<mode> Simulated signer error Expected result
payment SignerCanisterPaymentError (InsufficientFunds, low cycles) Friendly sign.error.unavailable toast — no raw Ledger error: … text
allowance SignerCanisterPaymentError (LedgerWithdrawFromError → InsufficientAllowance) Per-user limit sign.error.limit_reached toast
internal generic InternalError Existing fallback toast (send.error.unexpected, with the raw error appended)
signing generic SigningError Existing fallback toast

2. Trigger a signing operation

With a mode enabled, do any of these — each routes through a wrapped signer.api.ts call:

  • ETH / BTC / SOL send — open Send, fill a valid address + amount, confirm. (SOL keeps its mapSolanaErrorMsg fallback.)
  • ETH NFT send — send any NFT.
  • WalletConnect signingpersonal_sign / eth_sign / a WC send (covers all chains via the central execute() wrapper).
  • OpenCryptoPay — pay an invoice; the inline failure screen shows the friendly / limit message for the payment / allowance modes.

payment: the neutral "Signing is temporarily unavailable. Please try again shortly." message.
allowance: the "You've reached your signing limit. Please try again shortly." message.
internal / signing: the normal per-flow error toast.

3. Confirm the genuine user errors are unchanged (no simulation needed)

These are caught before signing, so they don't need the simulator — just use the UI normally:

  • Bad address — type an invalid destination → immediate destination_address_invalid toast.
  • Insufficient token balance / gas — enter an amount above balance → the existing specific assertion toast.

This demonstrates the PR #13145 guarantee: only the signer payment failures are rewritten; real user errors keep their specific messages.

4. (Optional) Observe the cfs_sign event (PR #13144)

With a mode enabled and a signing attempt made, the cfs_sign event fires with event_context=cfs, result_status=error, and a matching event_code + result_error_severity per mode: cfs_payment_failed_backend_out_of_cycles/blocker (payment), cfs_payment_failed_user_allowance_exhausted/major (allowance), cfs_generic_error/critical (internal / signing). Success emits event_code=success. Observe it via the Plausible dashboard on a build where analytics is enabled, or the outgoing network request.

Tests

None — demo branch, not for merge. prettier, eslint --max-warnings 0, and svelte-check (no problems in the changed files) pass.


🤖 Generated with Claude Code — model: Claude Opus 4.8 (claude-opus-4-8)

sbpublic and others added 9 commits June 18, 2026 17:38
Demo-only harness to reproduce the signer-unavailable toast (#13145) and the
fallback error paths end-to-end — including the cfs_sign Plausible event
(#13144), since the error is thrown from inside the tracked signer call —
without a real backend cycles outage.

A dev/test-only guard (LOCAL || STAGING, opt-in via query param or localStorage)
makes signer.api.ts signing calls throw a chosen error: a payment error (low
cycles), a generic InternalError, or a SigningError. Reads are left untouched so
the wallet still loads. Not for merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…toast' into demo/frontend/simulate-cfs-sign-failures
…simulator

Adds the 'allowance' mode (exhausted per-user ICRC-2 allowance) so the new
sign.error.limit_reached message can be reproduced alongside payment/internal/
signing. Merged latest PR #13145 so the limit_reached path is present.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…toast' into demo/frontend/simulate-cfs-sign-failures
…toast' into demo/frontend/simulate-cfs-sign-failures
…toast' into demo/frontend/simulate-cfs-sign-failures
…toast' into demo/frontend/simulate-cfs-sign-failures
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.

1 participant