Skip to content

Add wallet recovery word status tracking and error handling#665

Closed
kartikangiras wants to merge 2 commits into
bitcoinppl:masterfrom
kartikangiras:seedwords
Closed

Add wallet recovery word status tracking and error handling#665
kartikangiras wants to merge 2 commits into
bitcoinppl:masterfrom
kartikangiras:seedwords

Conversation

@kartikangiras

@kartikangiras kartikangiras commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Display a clear warning in My Wallets when a hot wallet's recovery words are unavailable on device. This addresses the issue where users restore to a new iPhone and don't realize their seed words are missing from the Secure Enclave which doesn't transfer between devices.

Fixes #316

Testing

  • just build-ios
  • just ci
  • just fmt

Platform Coverage

  • Tested on iOS device
  • Tested on Android device
  • Tested on iOS simulator
  • Tested on Android simulator
  • Not tested
    Recovery words warning only appears when keychain entry is missing which happens in device restore. Could not simulate this in simulator without manual keychain deletion.

Checklist

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced error messaging for recovery words with improved visual styling, including color-coded alerts for better clarity.
    • Fixed wallet item layout in sidebar to prevent text overflow and added border styling.
    • QR code button now displays only when recovery words are available.

@coderabbitai

coderabbitai Bot commented Apr 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@praveenperera has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 34 minutes and 14 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6216477d-e8fb-4be8-a96c-348d47f3aeca

📥 Commits

Reviewing files that changed from the base of the PR and between 8f54680 and 2d4658e.

📒 Files selected for processing (2)
  • android/app/src/main/java/org/bitcoinppl/cove/secret_words/SecretWordsScreen.kt
  • ios/Cove/Flows/SelectedWalletFlow/SecretWordsScreen.swift
📝 Walkthrough

Walkthrough

Enhanced error handling in SecretWordsScreen by replacing generic errorMessage state with typed MnemonicError state and derived user-facing messaging. Added explicit error styling with red-tinted background, border, and callout font for improved visibility. Updated toolbar QR button visibility and refined SidebarView wallet item layout with spacing adjustments.

Changes

Cohort / File(s) Summary
Error Handling & Styling
ios/Cove/Flows/SelectedWalletFlow/SecretWordsScreen.swift
Replaced single errorMessage state with loadError: MnemonicError? (private) and fallbackErrorMessage: String?. Added recoveryWordsErrorMessage computed property that maps specific error cases (.NotAvailable, .GetWalletKeychain, .UnknownWord) to user-facing messages. Enhanced UI rendering with red-tinted background, red border, callout font, and padding. Updated onAppear to catch MnemonicError and separate mnemonic-specific errors from fallback errors. Changed QR toolbar button visibility to only display when words are loaded.
Layout Refinements
ios/Cove/Views/SidebarView.swift
Added Spacer(minLength: 0) after wallet names to prevent text from consuming remaining horizontal space. Applied .overlay with RoundedRectangle stroke styling to wallet items for consistent border appearance.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Poem

🐰 With whiskers twitched and errors caught in sight,
Missing words now glow in warning's light!
Red borders frame the messages clear,
No more confusion, just help drawing near! 🔴✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The SidebarView.swift changes (Spacer insertion and overlay styling) appear tangential to the recovery words error handling objective from issue #316; they seem unrelated to the main scope of tracking missing seed words. Clarify or remove the SidebarView styling changes, or document their connection to the missing seed words warning feature in the PR description.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding wallet recovery word status tracking and error handling, which aligns with the PR's objective of detecting missing seed words.
Description check ✅ Passed The description includes a clear summary of the change, testing details with platform coverage, and completed checklist items, meeting the repository's template requirements.
Linked Issues check ✅ Passed The code changes implement the requirements from issue #316: detecting missing mnemonics via MnemonicError, displaying error messages in the recovery words screen, and surfacing warnings when keychain entries are unavailable.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Apr 16, 2026

Copy link
Copy Markdown

Greptile Summary

This PR tracks whether hot wallet recovery words are accessible on the device and surfaces a warning in the sidebar and on the SecretWordsScreen when they are not — a clear UX improvement for users who restore to a new iPhone and lose their keychain seed data. The implementation cleanly hooks into the existing event-reconciliation pattern in AppManager.

  • "Loading…" rendered inside the red error box: Before onAppear runs and populates words or loadError, the else branch in SecretWordsScreen renders the error-styled red box with the text "Loading…", creating a brief visual artifact that looks like an error state.
  • Synchronous keychain I/O on the main thread: refreshWalletRecoveryWordStatus() calls Mnemonic(id:) — a blocking Rust FFI / keychain read — for every hot wallet synchronously inside DispatchQueue.main.async handlers and during init(). With a large number of wallets or slow keychain access (common post-device-restore), this can cause main-thread jank.

Confidence Score: 5/5

Safe to merge; all findings are P2 style/polish suggestions with no blocking correctness or security issues.

All three comments are P2 (style, UX polish, and a minor visibility concern). No data loss, broken logic, or security issues were found. The core feature — detecting missing recovery words via MnemonicError.NotAvailable and surfacing the warning in the sidebar — is implemented correctly and follows the existing AppManager reconcile pattern.

ios/Cove/AppManager.swift (synchronous keychain reads on main thread) and ios/Cove/Flows/SelectedWalletFlow/SecretWordsScreen.swift (loading state rendered with error styling).

Important Files Changed

Filename Overview
ios/Cove/AppManager.swift Adds walletsMissingRecoveryWords set and refreshWalletRecoveryWordStatus() helper; called from init and all reconcile events that touch wallet data — synchronous keychain reads on the main thread are the main concern.
ios/Cove/Flows/SelectedWalletFlow/SecretWordsScreen.swift Replaces generic errorMessage with typed MnemonicError state and improves error messaging; "Loading…" initial state is incorrectly styled with the red error box before onAppear populates the actual state.
ios/Cove/Views/SidebarView.swift Adds per-wallet missing-recovery-words indicator (red background, orange triangle label) cleanly using the new walletMissingRecoveryWords helper.

Sequence Diagram

sequenceDiagram
    participant App as App Startup / Event
    participant AM as AppManager
    participant KC as Keychain (Mnemonic FFI)
    participant SB as SidebarView
    participant SW as SecretWordsScreen

    App->>AM: init() / reconcile(.walletsChanged)
    AM->>AM: refreshWalletRecoveryWordStatus()
    loop each hot wallet
        AM->>KC: Mnemonic(id:) [blocking, main thread]
        alt NotAvailable
            KC-->>AM: throws MnemonicError.NotAvailable
            AM->>AM: missingWalletIds.insert(wallet.id)
        else success
            KC-->>AM: Mnemonic object (discarded)
        end
    end
    AM->>AM: walletsMissingRecoveryWords = missingWalletIds

    SB->>AM: walletMissingRecoveryWords(wallet)
    AM-->>SB: Bool
    alt missing
        SB->>SB: show red bg + orange warning label
    end

    SW->>SW: onAppear → auth.lock()
    SW->>KC: Mnemonic(id:) [blocking, main thread]
    alt success
        KC-->>SW: words set → show word grid + QR button
    else MnemonicError.NotAvailable
        KC-->>SW: loadError = .NotAvailable → show red error box
    else MnemonicError.GetWalletKeychain
        KC-->>SW: loadError = .GetWalletKeychain → show red error box
    else other
        KC-->>SW: fallbackErrorMessage → show red error box
    end
Loading

Reviews (1): Last reviewed commit: "Add wallet recovery word status tracking..." | Re-trigger Greptile

Comment thread ios/Cove/Flows/SelectedWalletFlow/SecretWordsScreen.swift Outdated
Comment thread ios/Cove/AppManager.swift Outdated
Comment thread ios/Cove/AppManager.swift Outdated
Comment thread ios/Cove/Flows/SelectedWalletFlow/SecretWordsScreen.swift Outdated
Comment thread ios/Cove/AppManager.swift Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
ios/Cove/Views/SidebarView.swift (1)

79-87: Avoid competing flexible width in the wallet row.

Text(wallet.name) already expands with .frame(maxWidth: .infinity, alignment: .leading), so the added Spacer(minLength: 0) is redundant and can make long wallet names truncate/scale earlier. Keep one flexible layout mechanism.

♻️ Proposed cleanup
 Text(wallet.name)
     .font(.footnote)
     .fontWeight(.medium)
     .foregroundStyle(.white)
     .lineLimit(1)
     .minimumScaleFactor(0.80)
     .frame(maxWidth: .infinity, alignment: .leading)
-
- Spacer(minLength: 0)

As per coding guidelines, ios/Cove/**/*.swift: “Review SwiftUI view code for proper layout, best practices”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/Cove/Views/SidebarView.swift` around lines 79 - 87, The wallet row uses
two competing flexible width mechanisms—Text(wallet.name).frame(maxWidth:
.infinity, alignment: .leading) and Spacer(minLength: 0)—which causes premature
truncation/scaling; remove the redundant Spacer(minLength: 0) (or alternatively
remove the .frame(maxWidth: .infinity) and keep the Spacer) so only one flexible
layout mechanism remains, updating the wallet row around Text(wallet.name) to
rely on a single approach.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ios/Cove/Flows/SelectedWalletFlow/SecretWordsScreen.swift`:
- Around line 30-40: The computed property recoveryWordsErrorMessage treats the
loading state as an error ("Loading...") so the UI callout always shows; change
it to return an optional String (or return nil) for the loading case and only
provide actual error strings for real loadError cases (.NotAvailable,
.GetWalletKeychain, .UnknownWord) or fallbackErrorMessage when present; then
update the view that renders the red error callout (where
recoveryWordsErrorMessage is consumed) to only show the callout when
recoveryWordsErrorMessage is non-nil/non-empty (i.e., when a real error exists)
so normal loading remains neutral.

In `@ios/Cove/Views/SidebarView.swift`:
- Around line 93-96: The overlay using RoundedRectangle(cornerRadius:
10).stroke(Color.clear, lineWidth: 1) inside SidebarView is a no-op and should
either be removed or replaced with a visible warning that is conditionally shown
based on the wallet’s recovery-word availability; update the SidebarView (or the
specific view that contains the .overlay call) to check the wallet/recoveryWords
state and, when recovery words are missing, replace the clear stroke with a
visible badge/overlay (e.g., .stroke(Color.red, lineWidth: 2) or a small
Text/Image warning inside a ZStack) and ensure the UI binds to the wallet model
so the warning appears and disappears correctly.

---

Nitpick comments:
In `@ios/Cove/Views/SidebarView.swift`:
- Around line 79-87: The wallet row uses two competing flexible width
mechanisms—Text(wallet.name).frame(maxWidth: .infinity, alignment: .leading) and
Spacer(minLength: 0)—which causes premature truncation/scaling; remove the
redundant Spacer(minLength: 0) (or alternatively remove the .frame(maxWidth:
.infinity) and keep the Spacer) so only one flexible layout mechanism remains,
updating the wallet row around Text(wallet.name) to rely on a single approach.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5cc76d53-8aa9-4f92-9160-5ecf8e35b7a2

📥 Commits

Reviewing files that changed from the base of the PR and between ba61cd2 and 8f54680.

📒 Files selected for processing (2)
  • ios/Cove/Flows/SelectedWalletFlow/SecretWordsScreen.swift
  • ios/Cove/Views/SidebarView.swift

Comment thread ios/Cove/Flows/SelectedWalletFlow/SecretWordsScreen.swift Outdated
Comment thread ios/Cove/Views/SidebarView.swift Outdated
Comment thread ios/Cove/Flows/SelectedWalletFlow/SecretWordsScreen.swift Outdated
@praveenperera

Copy link
Copy Markdown
Contributor

@kartikangiras can you respond to my comments inline, and resolve or respond to the bots too thanks

@kartikangiras

Copy link
Copy Markdown
Contributor Author

@praveenperera I have address all the inline review comments and from bots as well.

Text(
text = errorMessage ?: stringResource(R.string.label_loading),
color = Color.White.copy(alpha = 0.7f),
text = loadError?.message ?: stringResource(R.string.label_loading),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loadError?.message ?: stringResource(R.string.label_loading) is rendered inside the same red error-styled Text, so the normal initial loading state still appears as an error on Android while Mnemonic(id = walletId) is running. Split this branch like the iOS version: show a neutral loading view when loadError == null, and only apply the red background/padding to actual RecoveryWordsLoadError messages.

@praveenperera

Copy link
Copy Markdown
Contributor

Closing this for now after maintainer triage. The branch is conflicting, CI is not green, and the PR scope/title no longer matches the current diff. Please reopen or send a fresh PR after rebasing, fixing the Android loading/error state, and aligning the scope with the implementation.

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.

Need better "missing seed words" warning

2 participants