Skip to content

Commit 062a0d9

Browse files
authored
[1/2] Multiple wallet support (#337)
* add wallet index * add account type * formatting * fix widget test * added feature flag * fix copyWith * fix wallet index * renaming mnemonicIndex to walletIndex for consistency couldn't really decide
1 parent ff5e1fd commit 062a0d9

16 files changed

Lines changed: 96 additions & 41 deletions

mobile-app/lib/features/main/screens/create_account_screen.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class _CreateAccountScreenState extends ConsumerState<CreateAccountScreen> {
7676
_isLoading = true;
7777
});
7878
try {
79-
final account = await _accountsService.createNewAccount();
79+
final account = await _accountsService.createNewAccount(walletIndex: 0);
8080
final checkphrase = await _checksumService.getHumanReadableName(account.accountId);
8181

8282
if (mounted) {

mobile-app/lib/features/main/screens/create_wallet_and_backup_screen.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,16 @@ class CreateWalletAndBackupScreenState extends ConsumerState<CreateWalletAndBack
100100
setState(() {
101101
_isSubmitting = true;
102102
});
103-
103+
final walletIndex = 0;
104104
try {
105-
await _settingsService.setMnemonic(_mnemonic);
105+
await _settingsService.setMnemonic(_mnemonic, walletIndex);
106106

107107
final asyncAccounts = ref.read(accountsProvider); // Gets notifier state
108108
final accounts = asyncAccounts.value ?? <Account>[]; // Extract data or empty list
109109
if (accounts.isEmpty) {
110-
await _accountsService.addAccount(Account(index: 0, name: _accountName.value.text, accountId: _address));
110+
await _accountsService.addAccount(
111+
Account(walletIndex: walletIndex, index: 0, name: _accountName.value.text, accountId: _address),
112+
);
111113
try {
112114
// this is more like a shortcut - it will happen anyway any time we try to log in.
113115
_referralService.submitAddressToBackend();

mobile-app/lib/features/main/screens/import_wallet_screen.dart

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,17 @@ class ImportWalletScreenState extends ConsumerState<ImportWalletScreen> {
3030
SubstrateService(),
3131
);
3232

33-
Future<void> _discoverAccounts(String mnemonic) async {
33+
Future<void> _discoverAccounts(String mnemonic, int walletIndex) async {
3434
if (!mounted) return;
3535
setState(() {
3636
_isDiscovering = true;
3737
});
3838

3939
try {
40-
final discoveredAccounts = await _accountDiscoveryService.discoverAccounts(mnemonic: mnemonic);
40+
final discoveredAccounts = await _accountDiscoveryService.discoverAccounts(
41+
mnemonic: mnemonic,
42+
walletIndex: walletIndex,
43+
);
4144

4245
final existingAccountsSet = (await _accountsService.getAccounts()).map((e) => e.accountId).toSet();
4346

@@ -59,7 +62,7 @@ class ImportWalletScreenState extends ConsumerState<ImportWalletScreen> {
5962
}
6063
}
6164

62-
Future<void> _importWallet() async {
65+
Future<void> _importWallet({required int walletIndex}) async {
6366
setState(() {
6467
_isLoading = true;
6568
_errorMessage = '';
@@ -81,10 +84,12 @@ class ImportWalletScreenState extends ConsumerState<ImportWalletScreen> {
8184
}
8285

8386
final key = HdWalletService().keyPairAtIndex(mnemonic, 0);
84-
await _settingsService.setMnemonic(mnemonic);
85-
await _accountsService.addAccount(Account(index: 0, name: 'Account 1', accountId: key.ss58Address));
87+
await _settingsService.setMnemonic(mnemonic, walletIndex);
88+
await _accountsService.addAccount(
89+
Account(walletIndex: walletIndex, index: 0, name: 'Account 1', accountId: key.ss58Address),
90+
);
8691

87-
await _discoverAccounts(mnemonic);
92+
await _discoverAccounts(mnemonic, walletIndex);
8893
// We set check status to true so we will not prompt user to input refferal code.
8994
// This is because we know they can only import if they have gone through create process.
9095
_settingsService.setReferralCheckCompleted();
@@ -190,7 +195,7 @@ class ImportWalletScreenState extends ConsumerState<ImportWalletScreen> {
190195
Button(
191196
variant: ButtonVariant.primary,
192197
label: 'Import Wallet',
193-
onPressed: _importWallet,
198+
onPressed: () => _importWallet(walletIndex: 0),
194199
isLoading: _isLoading,
195200
),
196201
SizedBox(height: context.themeSize.bottomButtonSpacing),

mobile-app/lib/features/main/screens/show_recovery_phrase_screen.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import 'package:resonance_network_wallet/shared/extensions/clipboard_extensions.
1212
import 'package:resonance_network_wallet/shared/extensions/media_query_data_extension.dart';
1313

1414
class ShowRecoveryPhraseScreen extends StatefulWidget {
15-
const ShowRecoveryPhraseScreen({super.key});
15+
const ShowRecoveryPhraseScreen({super.key, this.walletIndex = 0});
16+
17+
final int walletIndex;
1618

1719
@override
1820
State<ShowRecoveryPhraseScreen> createState() => _ShowRecoveryPhraseScreenState();
@@ -30,7 +32,7 @@ class _ShowRecoveryPhraseScreenState extends State<ShowRecoveryPhraseScreen> {
3032
}
3133

3234
Future<void> _loadMnemonic() async {
33-
final mnemonic = await _settingsService.getMnemonic();
35+
final mnemonic = await _settingsService.getMnemonic(widget.walletIndex);
3436
if (mnemonic != null) {
3537
setState(() {
3638
_recoveryPhrase = mnemonic.split(' ');

mobile-app/test/widget/send_screen_widget_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ void main() {
4545

4646
// --- 1. Settings Service Stubs ---
4747
when(mockSettingsService.getActiveAccount()).thenAnswer((_) async {
48-
return const Account(index: 0, name: 'Test User', accountId: 'test_account_id');
48+
return const Account(walletIndex: 0, index: 0, name: 'Test User', accountId: 'test_account_id');
4949
});
5050
when(mockSettingsService.getReversibleTimeSeconds()).thenAnswer((_) async => 600);
5151

mobile-app/test/widget/send_screen_widget_test.mocks.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService {
169169
as _i3.Future<bool>);
170170

171171
@override
172-
_i3.Future<void> setMnemonic(String? mnemonic) =>
172+
_i3.Future<void> setMnemonic(String? mnemonic, int? walletIndex) =>
173173
(super.noSuchMethod(
174174
Invocation.method(#setMnemonic, [mnemonic]),
175175
returnValue: _i3.Future<void>.value(),
@@ -178,7 +178,7 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService {
178178
as _i3.Future<void>);
179179

180180
@override
181-
_i3.Future<String?> getMnemonic() =>
181+
_i3.Future<String?> getMnemonic(int? walletIndex) =>
182182
(super.noSuchMethod(Invocation.method(#getMnemonic, []), returnValue: _i3.Future<String?>.value())
183183
as _i3.Future<String?>);
184184

quantus_sdk/lib/src/constants/app_constants.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ class AppConstants {
4141

4242
// Shared Preferences keys
4343
static const String hasWalletKey = 'has_wallet';
44-
static const String mnemonicKey = 'mnemonic';
4544
static const String accountIdKey = 'account_id';
4645

4746
// Reversible time settings
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class FeatureFlags {
2+
static const bool showKeystoneHardwareWallet = true;
3+
}

quantus_sdk/lib/src/extensions/account_extension.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ extension HDWalletAccount on Account {
77
}
88

99
Future<String?> getMnemonic() async {
10-
return SettingsService().getMnemonic();
10+
return SettingsService().getMnemonic(walletIndex);
1111
}
1212
}
Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,49 @@
11
import 'package:flutter/foundation.dart';
22

3+
enum AccountType { local, keystone }
4+
35
@immutable
46
class Account {
7+
final int walletIndex;
58
final int index; // derivation index
69
final String name;
710
final String accountId; // address
8-
const Account({required this.index, required this.name, required this.accountId});
11+
final AccountType accountType;
12+
const Account({
13+
required this.walletIndex,
14+
required this.index,
15+
required this.name,
16+
required this.accountId,
17+
this.accountType = AccountType.local,
18+
});
919

1020
factory Account.fromJson(Map<String, dynamic> json) {
11-
return Account(index: json['index'] as int, name: json['name'] as String, accountId: json['accountId'] as String);
21+
return Account(
22+
walletIndex: (json['walletIndex'] ?? 0) as int,
23+
index: json['index'] as int,
24+
name: json['name'] as String,
25+
accountId: json['accountId'] as String,
26+
accountType: AccountType.values.byName(json['accountType'] as String? ?? AccountType.local.name),
27+
);
1228
}
1329

1430
Map<String, dynamic> toJson() {
15-
return {'index': index, 'name': name, 'accountId': accountId};
31+
return {
32+
'walletIndex': walletIndex,
33+
'index': index,
34+
'name': name,
35+
'accountId': accountId,
36+
'accountType': accountType.name,
37+
};
1638
}
1739

18-
Account copyWith({int? index, String? name, String? accountId, int? uiPosition}) {
19-
return Account(index: index ?? this.index, name: name ?? this.name, accountId: accountId ?? this.accountId);
40+
Account copyWith({int? walletIndex, int? index, String? name, String? accountId, AccountType? accountType}) {
41+
return Account(
42+
walletIndex: walletIndex ?? this.walletIndex,
43+
index: index ?? this.index,
44+
name: name ?? this.name,
45+
accountId: accountId ?? this.accountId,
46+
accountType: accountType ?? this.accountType,
47+
);
2048
}
2149
}

0 commit comments

Comments
 (0)