@@ -4,6 +4,7 @@ import 'package:bb_mobile/core/wallet/domain/entities/wallet.dart';
44import 'package:bb_mobile/features/onboarding/complete_physical_backup_verification_usecase.dart' ;
55import 'package:bb_mobile/features/test_wallet_backup/domain/usecases/get_mnemonic_from_fingerprint_usecase.dart' ;
66import 'package:bb_mobile/features/test_wallet_backup/domain/usecases/load_wallets_for_network_usecase.dart' ;
7+ import 'package:bb_mobile/features/test_wallet_backup/domain/usecases/verify_physical_backup_usecase.dart' ;
78import 'package:flutter_bloc/flutter_bloc.dart' ;
89import 'package:freezed_annotation/freezed_annotation.dart' ;
910
@@ -13,146 +14,111 @@ part 'test_wallet_backup_state.dart';
1314
1415class TestWalletBackupBloc
1516 extends Bloc <TestWalletBackupEvent , TestWalletBackupState > {
17+ final CompletePhysicalBackupVerificationUsecase
18+ _completePhysicalBackupVerificationUsecase;
19+ final LoadWalletsForNetworkUsecase _loadWalletsForNetworkUsecase;
20+ final GetMnemonicFromFingerprintUsecase _getMnemonicFromFingerprintUsecase;
21+ final VerifyPhysicalBackupUsecase _verifyPhysicalBackupUsecase;
22+
1623 TestWalletBackupBloc ({
1724 required this ._completePhysicalBackupVerificationUsecase,
1825 required this ._loadWalletsForNetworkUsecase,
1926 required this ._getMnemonicFromFingerprintUsecase,
27+ required this ._verifyPhysicalBackupUsecase,
2028 }) : super (const TestWalletBackupState ()) {
21- on < OnWordsSelected > (_onWordsSelected);
22- on < VerifyPhysicalBackup > (_verifyPhysicalBackup);
23- on < StartPhysicalBackupVerification > ((event, emit) {});
2429 on < LoadWallets > (_onLoadWallets);
25- on < LoadMnemonicForWallet > (_onLoadMnemonicForWallet);
26- on < ClearError > ((event, emit) => emit (state.copyWith (statusError: '' )));
27- }
28-
29- final CompletePhysicalBackupVerificationUsecase
30- _completePhysicalBackupVerificationUsecase;
31- final LoadWalletsForNetworkUsecase _loadWalletsForNetworkUsecase;
32- final GetMnemonicFromFingerprintUsecase _getMnemonicFromFingerprintUsecase;
33-
34- /// Handles word selection during backup verification
35- /// Validates word order and updates test state
36- Future <void > _onWordsSelected (
37- OnWordsSelected event,
38- Emitter <TestWalletBackupState > emit,
39- ) async {
40- final mnemonic = state.mnemonic;
41- final reorderedMnemonic = List <String >.from (
42- state.reorderedMnemonic + [event.word],
43- );
44-
45- final isCorrect = mnemonic
46- .join (' ' )
47- .startsWith (reorderedMnemonic.join (' ' ));
48-
49- if (isCorrect) {
50- emit (
30+ on < WalletSelected > (_onWalletSelected);
31+ on < VerifyPhysicalBackup > (_verifyPhysicalBackup);
32+ on < ClearError > (
33+ (event, emit) => emit (
5134 state.copyWith (
52- reorderedMnemonic: [...state.reorderedMnemonic, event.word],
5335 statusError: '' ,
54- selectedMnemonicWords : [...state.selectedMnemonicWords, event.index] ,
36+ verificationStatus : BackupVerificationStatus .idle ,
5537 ),
56- );
57- } else {
58- final shuffled = List <String >.from (mnemonic)..shuffle ();
59- emit (
60- state.copyWith (
61- shuffledMnemonic: shuffled,
62- reorderedMnemonic: [],
63- selectedMnemonicWords: [],
64- statusError: 'Incorrect word order. Please try again.' ,
65- ),
66- );
67- }
38+ ),
39+ );
6840 }
6941
70- Future <void > _verifyPhysicalBackup (
71- VerifyPhysicalBackup event,
72- Emitter <TestWalletBackupState > emit,
73- ) async {
74- try {
75- if (state.mnemonic.isEmpty) {
76- emit (state.copyWith (statusError: 'No mnemonic loaded' ));
77- return ;
78- }
79-
80- if (state.reorderedMnemonic.length != state.mnemonic.length) {
81- emit (state.copyWith (statusError: 'Please select all words' ));
82- return ;
83- }
84-
85- // Compare with original mnemonic
86- final isCorrect =
87- state.mnemonic.join (' ' ) == state.reorderedMnemonic.join (' ' );
88-
89- if (isCorrect) {
90- await _completePhysicalBackupVerificationUsecase.execute ();
91- } else {
92- // Reset test state when wrong
93- final shuffled = state.mnemonic.toList ()..shuffle ();
94- emit (
95- state.copyWith (
96- statusError: 'Incorrect word order. Please try again.' ,
97- shuffledMnemonic: shuffled,
98- reorderedMnemonic: [],
99- selectedMnemonicWords: [],
100- ),
101- );
102- }
103- } catch (e) {
104- emit (state.copyWith (statusError: 'Verification failed: $e ' ));
42+ /// Reads the selected wallet's secret at the point of use.
43+ ///
44+ /// The mnemonic and passphrase are returned directly to the caller and are
45+ /// never held in bloc state: secrets must stay ephemeral and must never
46+ /// appear in the freezed `toString()` of the state.
47+ Future <(List <String >, String ?)> loadSelectedWalletMnemonic () {
48+ final wallet = state.selectedWallet;
49+ if (wallet == null ) {
50+ throw Exception ('No wallet selected' );
10551 }
52+ return _getMnemonicFromFingerprintUsecase.execute (wallet.masterFingerprint);
10653 }
10754
10855 Future <void > _onLoadWallets (
10956 LoadWallets event,
11057 Emitter <TestWalletBackupState > emit,
11158 ) async {
11259 try {
113- emit (state.copyWith (selectedWallet: null ));
114-
11560 final wallets = await _loadWalletsForNetworkUsecase.execute ();
11661 if (wallets.isEmpty) throw Exception ('No wallets found' );
11762 final Wallet selected = wallets.firstWhere (
11863 (w) => w.isDefault,
11964 orElse: () => wallets.first,
12065 );
121- emit (state.copyWith (wallets: wallets, selectedWallet: selected));
122-
123- add (LoadMnemonicForWallet (wallet: selected));
66+ emit (
67+ state.copyWith (
68+ wallets: wallets,
69+ selectedWallet: selected,
70+ verificationStatus: BackupVerificationStatus .idle,
71+ ),
72+ );
12473 } catch (e) {
12574 emit (state.copyWith (statusError: 'Failed to load wallets: $e ' ));
12675 }
12776 }
12877
129- Future <void > _onLoadMnemonicForWallet (
130- LoadMnemonicForWallet event,
78+ Future <void > _onWalletSelected (
79+ WalletSelected event,
80+ Emitter <TestWalletBackupState > emit,
81+ ) async {
82+ emit (
83+ state.copyWith (
84+ selectedWallet: event.wallet,
85+ statusError: '' ,
86+ verificationStatus: BackupVerificationStatus .idle,
87+ ),
88+ );
89+ }
90+
91+ Future <void > _verifyPhysicalBackup (
92+ VerifyPhysicalBackup event,
13193 Emitter <TestWalletBackupState > emit,
13294 ) async {
13395 try {
134- emit (state.copyWith (selectedWallet: null ));
96+ final wallet = state.selectedWallet;
97+ if (wallet == null ) {
98+ emit (state.copyWith (statusError: 'No wallet selected' ));
99+ return ;
100+ }
135101
136- final wallet = event.wallet;
137- final (
138- mnemonicWords,
139- passphrase,
140- ) = await _getMnemonicFromFingerprintUsecase.execute (
141- wallet.masterFingerprint,
102+ final isCorrect = await _verifyPhysicalBackupUsecase.execute (
103+ fingerprint: wallet.masterFingerprint,
104+ mnemonic: event.reorderedWords,
142105 );
143106
144- emit (
145- state.copyWith (
146- selectedWallet: wallet,
147- mnemonic: mnemonicWords,
148- passphrase: passphrase ?? '' ,
149- shuffledMnemonic: mnemonicWords.toList ()..shuffle (),
150- reorderedMnemonic: [],
151- selectedMnemonicWords: [],
152- ),
153- );
107+ if (isCorrect) {
108+ await _completePhysicalBackupVerificationUsecase.execute ();
109+ emit (
110+ state.copyWith (
111+ verificationStatus: BackupVerificationStatus .success,
112+ statusError: '' ,
113+ ),
114+ );
115+ } else {
116+ emit (
117+ state.copyWith (verificationStatus: BackupVerificationStatus .failure),
118+ );
119+ }
154120 } catch (e) {
155- emit (state.copyWith (statusError: 'Failed to load mnemonic : $e ' ));
121+ emit (state.copyWith (statusError: 'Verification failed : $e ' ));
156122 }
157123 }
158124}
0 commit comments