walletOnboardingOverrides function

List<Override> walletOnboardingOverrides({
  1. required WalletConfig config,
  2. bool? supportsScreenshotBlock,
  3. WalletProvisioner decorateProvisioner(
    1. WalletProvisioner provisioner
    )?,
})

The production onboarding seam overrides, co-wired.

config is the host's OWN WalletConfig — endpoint, network, Tor policy, seed persistence, broadcast jitter, and the resolved dbDir all come from the host (the SDK validates the config; it does not choose it). A host with its own lightwalletd/Tor infrastructure constructs its WalletConfig directly; a host that wants the reference defaults (zec.rocks, Tor off) passes buildWalletConfig verbatim — both reach this seam the same way, so the ONE ergonomic wiring call is no longer coupled to the reference policy.

supportsScreenshotBlock defaults to the real platform capability and is injectable so a test can drive the Android branch on the host VM.

decorateProvisioner lets a host wrap the real provisioner — to run its own steps around a wallet lifecycle call — without re-wiring the seam: the decorator receives the real provisioner and returns the one the wallet UI uses. It must DELEGATE to what it was given (the co-wiring rule above holds because the real provisioner is still the one underneath). The reference use is the example's Tor plugin: deleteWallet stops the plugin before the wipe and offers the Tor-identity reset after it.

Implementation

List<Override> walletOnboardingOverrides({
  required WalletConfig config,
  bool? supportsScreenshotBlock,
  WalletProvisioner Function(WalletProvisioner provisioner)?
  decorateProvisioner,
}) {
  // Programming-error belt: an EMPTY or RELATIVE dbDir is
  // never right — on desktop a relative path would silently create the wallet
  // under the launch cwd, so a funded wallet "disappears" when the app is
  // launched from elsewhere and onboarding offers a fresh create over it.
  // Loud at wiring time; the Rust config door rejects the same shapes typed
  // (InvalidDbDir, RW-CFG-003 — landed with #324, covering direct-SDK
  // consumers too). This belt stays: an ArgumentError at wiring names the
  // bug instantly, where the typed door surfaces later, at create/open.
  if (config.dbDir.isEmpty || !Directory(config.dbDir).isAbsolute) {
    throw ArgumentError.value(
      config.dbDir,
      'config.dbDir',
      'must be an absolute path (see resolveWalletDbDir for the reference '
          'resolution)',
    );
  }
  final supportsBlock =
      supportsScreenshotBlock ?? defaultSupportsScreenshotBlock();
  final real = FrbWalletProvisioner(config: config);
  final provisioner = decorateProvisioner?.call(real) ?? real;
  final store = SharedPrefsOnboardingStore();
  // Refcount-wrap the selected adapter (#344) so stacked secure screens share
  // one flag — via the single wrap chokepoint. The co-wiring assert below
  // unwraps to check the effective adapter.
  final security = refCountedScreenSecurityFor(
    supportsNativeBlock: supportsBlock,
  );

  // Co-wired by construction; the assert is a future-edit tripwire, HONESTLY
  // scoped: it can only ever bite in a host-VM test that injects
  // supportsScreenshotBlock: true (on a real device build the debug deferral
  // makes supportsBlock false in debug, and asserts strip in release). The
  // guard that actually covers a release device is screenSecurityProvider's
  // platform-correct DEFAULT (onboarding_providers.dart), not this assert.
  assert(
    screenSecurityCoWiringHolds(
      provisionerIsReal: true,
      security: security,
      supportsBlock: supportsBlock,
    ),
    'a real WalletProvisioner must co-wire a non-no-op ScreenSecurity where the '
    'platform can block the screen (crypto audit H2)',
  );

  // The sync sheet's "Server" row: the endpoint HOST, derived
  // from the SAME config the provisioner opens with, so the display and the
  // actual connection can never drift. Host only — never the full URL.
  final endpointHost = Uri.tryParse(config.endpointUrl)?.host;

  return [
    walletProvisionerProvider.overrideWithValue(provisioner),
    onboardingStoreProvider.overrideWithValue(store),
    screenSecurityProvider.overrideWithValue(security),
    if (endpointHost != null && endpointHost.isNotEmpty)
      walletEndpointHostProvider.overrideWithValue(endpointHost),
  ];
}