diff --git a/mobile-app/lib/features/main/screens/create_account_screen.dart b/mobile-app/lib/features/main/screens/create_account_screen.dart index 6453247e..4179af72 100644 --- a/mobile-app/lib/features/main/screens/create_account_screen.dart +++ b/mobile-app/lib/features/main/screens/create_account_screen.dart @@ -76,7 +76,7 @@ class _CreateAccountScreenState extends ConsumerState { _isLoading = true; }); try { - final account = await _accountsService.createNewAccount(); + final account = await _accountsService.createNewAccount(walletIndex: 0); final checkphrase = await _checksumService.getHumanReadableName(account.accountId); if (mounted) { diff --git a/mobile-app/lib/features/main/screens/create_wallet_and_backup_screen.dart b/mobile-app/lib/features/main/screens/create_wallet_and_backup_screen.dart index 92d590ad..74e986e4 100644 --- a/mobile-app/lib/features/main/screens/create_wallet_and_backup_screen.dart +++ b/mobile-app/lib/features/main/screens/create_wallet_and_backup_screen.dart @@ -100,14 +100,16 @@ class CreateWalletAndBackupScreenState extends ConsumerState[]; // Extract data or empty list if (accounts.isEmpty) { - await _accountsService.addAccount(Account(index: 0, name: _accountName.value.text, accountId: _address)); + await _accountsService.addAccount( + Account(walletIndex: walletIndex, index: 0, name: _accountName.value.text, accountId: _address), + ); await _referralService.submitAddressToBackend(); } ref.invalidate(accountsProvider); diff --git a/mobile-app/lib/features/main/screens/import_wallet_screen.dart b/mobile-app/lib/features/main/screens/import_wallet_screen.dart index 67145d3c..de4b73c3 100644 --- a/mobile-app/lib/features/main/screens/import_wallet_screen.dart +++ b/mobile-app/lib/features/main/screens/import_wallet_screen.dart @@ -59,7 +59,7 @@ class ImportWalletScreenState extends ConsumerState { } } - Future _importWallet() async { + Future _importWallet({required int walletIndex}) async { setState(() { _isLoading = true; _errorMessage = ''; @@ -81,8 +81,10 @@ class ImportWalletScreenState extends ConsumerState { } final key = HdWalletService().keyPairAtIndex(mnemonic, 0); - await _settingsService.setMnemonic(mnemonic); - await _accountsService.addAccount(Account(index: 0, name: 'Account 1', accountId: key.ss58Address)); + await _settingsService.setMnemonic(mnemonic, walletIndex); + await _accountsService.addAccount( + Account(walletIndex: walletIndex, index: 0, name: 'Account 1', accountId: key.ss58Address), + ); await _discoverAccounts(mnemonic); // We set check status to true so we will not prompt user to input refferal code. @@ -190,7 +192,7 @@ class ImportWalletScreenState extends ConsumerState { Button( variant: ButtonVariant.primary, label: 'Import Wallet', - onPressed: _importWallet, + onPressed: () => _importWallet(walletIndex: 0), isLoading: _isLoading, ), SizedBox(height: context.themeSize.bottomButtonSpacing), diff --git a/mobile-app/lib/features/main/screens/show_recovery_phrase_screen.dart b/mobile-app/lib/features/main/screens/show_recovery_phrase_screen.dart index ef3c6c63..04e41b7e 100644 --- a/mobile-app/lib/features/main/screens/show_recovery_phrase_screen.dart +++ b/mobile-app/lib/features/main/screens/show_recovery_phrase_screen.dart @@ -12,7 +12,9 @@ import 'package:resonance_network_wallet/shared/extensions/clipboard_extensions. import 'package:resonance_network_wallet/shared/extensions/media_query_data_extension.dart'; class ShowRecoveryPhraseScreen extends StatefulWidget { - const ShowRecoveryPhraseScreen({super.key}); + const ShowRecoveryPhraseScreen({super.key, this.walletIndex = 0}); + + final int walletIndex; @override State createState() => _ShowRecoveryPhraseScreenState(); @@ -30,7 +32,7 @@ class _ShowRecoveryPhraseScreenState extends State { } Future _loadMnemonic() async { - final mnemonic = await _settingsService.getMnemonic(); + final mnemonic = await _settingsService.getMnemonic(widget.walletIndex); if (mnemonic != null) { setState(() { _recoveryPhrase = mnemonic.split(' '); diff --git a/mobile-app/test/widget/send_screen_widget_test.dart b/mobile-app/test/widget/send_screen_widget_test.dart index 2e08ba42..4ee6a14d 100644 --- a/mobile-app/test/widget/send_screen_widget_test.dart +++ b/mobile-app/test/widget/send_screen_widget_test.dart @@ -45,7 +45,7 @@ void main() { // --- 1. Settings Service Stubs --- when(mockSettingsService.getActiveAccount()).thenAnswer((_) async { - return const Account(index: 0, name: 'Test User', accountId: 'test_account_id'); + return const Account(walletIndex: 0, index: 0, name: 'Test User', accountId: 'test_account_id'); }); when(mockSettingsService.getReversibleTimeSeconds()).thenAnswer((_) async => 600); diff --git a/mobile-app/test/widget/send_screen_widget_test.mocks.dart b/mobile-app/test/widget/send_screen_widget_test.mocks.dart index 48f41cc2..c534fa15 100644 --- a/mobile-app/test/widget/send_screen_widget_test.mocks.dart +++ b/mobile-app/test/widget/send_screen_widget_test.mocks.dart @@ -169,7 +169,7 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { as _i3.Future); @override - _i3.Future setMnemonic(String? mnemonic) => + _i3.Future setMnemonic(String? mnemonic, int? mnemonicIndex) => (super.noSuchMethod( Invocation.method(#setMnemonic, [mnemonic]), returnValue: _i3.Future.value(), @@ -178,7 +178,7 @@ class MockSettingsService extends _i1.Mock implements _i2.SettingsService { as _i3.Future); @override - _i3.Future getMnemonic() => + _i3.Future getMnemonic(int? mnemonicIndex) => (super.noSuchMethod(Invocation.method(#getMnemonic, []), returnValue: _i3.Future.value()) as _i3.Future); diff --git a/quantus_sdk/lib/src/constants/app_constants.dart b/quantus_sdk/lib/src/constants/app_constants.dart index f938ae93..fedb6db2 100644 --- a/quantus_sdk/lib/src/constants/app_constants.dart +++ b/quantus_sdk/lib/src/constants/app_constants.dart @@ -40,7 +40,6 @@ class AppConstants { // Shared Preferences keys static const String hasWalletKey = 'has_wallet'; - static const String mnemonicKey = 'mnemonic'; static const String accountIdKey = 'account_id'; // Reversible time settings diff --git a/quantus_sdk/lib/src/extensions/account_extension.dart b/quantus_sdk/lib/src/extensions/account_extension.dart index 4a4fc633..b743ab4a 100644 --- a/quantus_sdk/lib/src/extensions/account_extension.dart +++ b/quantus_sdk/lib/src/extensions/account_extension.dart @@ -7,6 +7,6 @@ extension HDWalletAccount on Account { } Future getMnemonic() async { - return SettingsService().getMnemonic(); + return SettingsService().getMnemonic(walletIndex); } } diff --git a/quantus_sdk/lib/src/models/account.dart b/quantus_sdk/lib/src/models/account.dart index 6a894d23..da2baac5 100644 --- a/quantus_sdk/lib/src/models/account.dart +++ b/quantus_sdk/lib/src/models/account.dart @@ -1,21 +1,48 @@ import 'package:flutter/foundation.dart'; +enum AccountType { local, keystone } + @immutable class Account { + final int walletIndex; final int index; // derivation index final String name; final String accountId; // address - const Account({required this.index, required this.name, required this.accountId}); + final AccountType accountType; + const Account({ + required this.walletIndex, + required this.index, + required this.name, + required this.accountId, + this.accountType = AccountType.local, + }); factory Account.fromJson(Map json) { - return Account(index: json['index'] as int, name: json['name'] as String, accountId: json['accountId'] as String); + return Account( + walletIndex: (json['walletIndex'] ?? 0) as int, + index: json['index'] as int, + name: json['name'] as String, + accountId: json['accountId'] as String, + accountType: AccountType.values.byName(json['accountType'] as String? ?? AccountType.local.name), + ); } Map toJson() { - return {'index': index, 'name': name, 'accountId': accountId}; + return { + 'walletIndex': walletIndex, + 'index': index, + 'name': name, + 'accountId': accountId, + 'accountType': accountType.name, + }; } - Account copyWith({int? index, String? name, String? accountId, int? uiPosition}) { - return Account(index: index ?? this.index, name: name ?? this.name, accountId: accountId ?? this.accountId); + Account copyWith({int? walletIndex, int? index, String? name, String? accountId, int? uiPosition}) { + return Account( + walletIndex: walletIndex ?? this.walletIndex, + index: index ?? this.index, + name: name ?? this.name, + accountId: accountId ?? this.accountId, + ); } } diff --git a/quantus_sdk/lib/src/services/account_discovery_service.dart b/quantus_sdk/lib/src/services/account_discovery_service.dart index 7ffc43db..cb2b0779 100644 --- a/quantus_sdk/lib/src/services/account_discovery_service.dart +++ b/quantus_sdk/lib/src/services/account_discovery_service.dart @@ -21,7 +21,9 @@ class AccountDiscoveryService { // Add raw account final rawKeyPair = _substrateService.nonHDdilithiumKeypairFromMnemonic(mnemonic); + final baseWalletIndex = 0; final rawAccount = Account( + walletIndex: baseWalletIndex, index: -1, // indicator for a raw account name: 'Primary Account', accountId: rawKeyPair.ss58Address, @@ -31,7 +33,12 @@ class AccountDiscoveryService { // Add HD accounts for (var i = 0; i < count; i++) { final keyPair = _hdWalletService.keyPairAtIndex(mnemonic, i); - final account = Account(index: i, name: 'Account ${i + 1}', accountId: keyPair.ss58Address); + final account = Account( + walletIndex: baseWalletIndex, + index: i, + name: 'Account ${i + 1}', + accountId: keyPair.ss58Address, + ); allPossibleAccounts.add(account); } diff --git a/quantus_sdk/lib/src/services/accounts_service.dart b/quantus_sdk/lib/src/services/accounts_service.dart index ef0d05df..0d285a1f 100644 --- a/quantus_sdk/lib/src/services/accounts_service.dart +++ b/quantus_sdk/lib/src/services/accounts_service.dart @@ -18,14 +18,15 @@ class AccountsService { final SettingsService _settingsService = SettingsService(); void Function()? onAccountsChanged; - Future createNewAccount() async { - final mnemonic = await _settingsService.getMnemonic(); + Future createNewAccount({required int walletIndex}) async { + final mnemonic = await _settingsService.getMnemonic(walletIndex); if (mnemonic == null) { throw Exception('Mnemonic not found. Cannot create new account.'); } final nextIndex = await _settingsService.getNextFreeAccountIndex(); final keypair = HdWalletService().keyPairAtIndex(mnemonic, nextIndex); final newAccount = Account( + walletIndex: walletIndex, index: nextIndex, name: 'Account ${nextIndex + 1}', // Default name accountId: keypair.ss58Address, diff --git a/quantus_sdk/lib/src/services/migration_service.dart b/quantus_sdk/lib/src/services/migration_service.dart index 4ec5217b..7e812004 100644 --- a/quantus_sdk/lib/src/services/migration_service.dart +++ b/quantus_sdk/lib/src/services/migration_service.dart @@ -9,6 +9,7 @@ import 'package:quantus_sdk/src/services/settings_service.dart'; class MigrationService { final SettingsService _settingsService; final HdWalletService _hdWalletService; + final int baseWalletIndex = 0; MigrationService(this._settingsService, this._hdWalletService); @@ -20,7 +21,7 @@ class MigrationService { /// Get migration data including old accounts with their public keys Future> getMigrationData() async { final oldAccounts = _settingsService.getOldAccounts(); - final mnemonic = await _settingsService.getMnemonic(); + final mnemonic = await _settingsService.getMnemonic(baseWalletIndex); if (mnemonic == null) { throw Exception('No mnemonic found for migration'); @@ -54,6 +55,7 @@ class MigrationService { ); final newAccount = Account( + walletIndex: baseWalletIndex, index: data.oldAccount.index, name: data.oldAccount.name, accountId: data.newAccountId, @@ -76,9 +78,14 @@ class MigrationService { /// Debug method to create test old accounts Future createDebugOldAccounts() async { final debugAccounts = [ - const Account(index: -1, name: 'Primary Account', accountId: 'qznd1YWbgQrviV76psu5n8d24mHSuHtAc9JmJLB42gTELksvQ'), - const Account(index: 0, name: 'Account 0', accountId: 'debug_id_0'), - const Account(index: 1, name: 'Account 1', accountId: 'debug_id_1'), + const Account( + walletIndex: 0, + index: -1, + name: 'Primary Account', + accountId: 'qznd1YWbgQrviV76psu5n8d24mHSuHtAc9JmJLB42gTELksvQ', + ), + const Account(walletIndex: 0, index: 0, name: 'Account 0', accountId: 'debug_id_0'), + const Account(walletIndex: 0, index: 1, name: 'Account 1', accountId: 'debug_id_1'), ]; final jsonData = jsonEncode(debugAccounts.map((a) => a.toJson()).toList()); diff --git a/quantus_sdk/lib/src/services/settings_service.dart b/quantus_sdk/lib/src/services/settings_service.dart index c35685a4..e633896b 100644 --- a/quantus_sdk/lib/src/services/settings_service.dart +++ b/quantus_sdk/lib/src/services/settings_service.dart @@ -52,7 +52,7 @@ class SettingsService { final oldAccountId = _prefs.getString('account_id'); if (oldAccountId != null) { final oldWalletName = _prefs.getString('wallet_name') ?? 'Account 1'; - final account = Account(index: 0, name: oldWalletName, accountId: oldAccountId); + final account = Account(walletIndex: 0, index: 0, name: oldWalletName, accountId: oldAccountId); await saveAccounts([account]); await setActiveAccount(account); // Clean up old keys after migration @@ -178,13 +178,15 @@ class SettingsService { return accounts.isEmpty; } + String getMnemonicKey(int mnemonicIndex) => mnemonicIndex == 0 ? 'mnemonic' : 'mnemonic_$mnemonicIndex'; + // Mnemonic Settings - Using secure storage - Future setMnemonic(String mnemonic) async { - await _secureStorage.write(key: 'mnemonic', value: mnemonic); + Future setMnemonic(String mnemonic, int mnemonicIndex) async { + await _secureStorage.write(key: getMnemonicKey(mnemonicIndex), value: mnemonic); } - Future getMnemonic() async { - return await _secureStorage.read(key: 'mnemonic'); + Future getMnemonic(int mnemonicIndex) async { + return await _secureStorage.read(key: getMnemonicKey(mnemonicIndex)); } // Reversible Time Settings diff --git a/quantus_sdk/lib/src/services/taskmaster_service.dart b/quantus_sdk/lib/src/services/taskmaster_service.dart index 3656b271..9b0f1922 100644 --- a/quantus_sdk/lib/src/services/taskmaster_service.dart +++ b/quantus_sdk/lib/src/services/taskmaster_service.dart @@ -165,7 +165,7 @@ class TaskmasterService { } Future getOldMiningAccountId() async { - final mnemonic = await _settingsService.getMnemonic(); + final mnemonic = await _settingsService.getMnemonic(0); if (mnemonic == null) { throw Exception('Mnemonic not found.'); } @@ -174,7 +174,7 @@ class TaskmasterService { } Future loginWithAccount1() async { - final mnemonic = await _settingsService.getMnemonic(); + final mnemonic = await _settingsService.getMnemonic(0); if (mnemonic == null) { throw Exception('Mnemonic not found.'); } diff --git a/quantus_sdk/test/services/settings_service_test.dart b/quantus_sdk/test/services/settings_service_test.dart index 77a039bd..258904c2 100644 --- a/quantus_sdk/test/services/settings_service_test.dart +++ b/quantus_sdk/test/services/settings_service_test.dart @@ -9,9 +9,9 @@ void main() { late SettingsService settingsService; // Accounts for testing - const account1 = Account(index: 0, name: 'Account 1', accountId: 'id_1'); - const account2 = Account(index: 1, name: 'Account 2', accountId: 'id_2'); - const account3 = Account(index: 2, name: 'Account 3', accountId: 'id_3'); + const account1 = Account(walletIndex: 0, index: 0, name: 'Account 1', accountId: 'id_1'); + const account2 = Account(walletIndex: 0, index: 1, name: 'Account 2', accountId: 'id_2'); + const account3 = Account(walletIndex: 0, index: 2, name: 'Account 3', accountId: 'id_3'); setUp(() async { // Reset mock storage BEFORE any SharedPreferences.getInstance() calls