isWatchOnlyProvider top-level property

Provider<bool> isWatchOnlyProvider
final

Is the active wallet WATCH-ONLY (#397 §3.7 D4/D5)? — the UI chrome key: a "Watch-only" badge, hidden Send/Shield/Swap affordances, and the Security screen's export-instead-of-backup arm read this. CHROME-ONLY by design: the SDK's typed WalletErrorKind.watchOnly refusals stand regardless, so this provider is allowed to fail SAFE — false (the full-spend chrome). A spurious false only shows an affordance the SDK then refuses honestly (never a wrong spend); a spurious true only hides an affordance.

SYNCHRONOUS on the package-gate path (the converged HIGH fix): the wallet KIND is IMMUTABLE and is captured into OnboardingActive.isWatchOnly at the moment the session becomes active (a watch-only import ⇒ known true; a create/restore ⇒ known false; a boot open ⇒ the controller's bounded once-read). Reading it from there resolves the chrome on the FIRST frame — no flash of Send/Swap/Shield while an async FFI read settles (the old .value ?? false painted the full-spend chrome on every watch-only mount until the read landed). A SESSION-ONLY host (which overrides walletSessionProvider and never reaches OnboardingActive) has no captured kind to read, so it falls back to the bounded async once-read below — keyed to the wallet identity so a watch-only true never bleeds onto the next wallet.

Implementation

final isWatchOnlyProvider = Provider<bool>((ref) {
  final session = ref.watch(walletSessionProvider);
  if (session == null) return false;
  final onboarding = ref.watch(onboardingControllerProvider);
  if (onboarding is OnboardingActive &&
      identical(onboarding.session, session)) {
    return onboarding.isWatchOnly;
  }
  // Session-only host: no captured kind — the bounded async fallback (fail-safe
  // false until it resolves; the SDK's typed refusals stand meanwhile).
  final identity = ref.watch(walletIdentityProvider);
  return ref.watch(_isWatchOnlyReadProvider(identity)).value ?? false;
});