Change the wallet creation ux to set the password later
MODIFY
WalletWasabi.Fluent/ViewModels/AddWallet/Create/ConfirmRecoveryWordsViewModel.cs
WalletWasabi.Fluent/ViewModels/AddWallet/Create/ConfirmRecoveryWordsViewModel.cs
@@ -10,6 +10,7 @@ using NBitcoin;
using ReactiveUI;
using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Fluent.ViewModels.CoinJoinProfiles;
+using WalletWasabi.Fluent.ViewModels.Dialogs;
using WalletWasabi.Fluent.ViewModels.Navigation;
namespace WalletWasabi.Fluent.ViewModels.AddWallet.Create;
@@ -21,7 +22,8 @@ public partial class ConfirmRecoveryWordsViewModel : RoutableViewModel
private SourceList<RecoveryWordViewModel> _confirmationWordsSourceList;
[AutoNotify] private bool _isSkipEnable;
- public ConfirmRecoveryWordsViewModel(List<RecoveryWordViewModel> mnemonicWords, KeyManager keyManager)
+ public ConfirmRecoveryWordsViewModel(List<RecoveryWordViewModel> mnemonicWords, Mnemonic mnemonic,
+ KeyManager? keyManager, string? walletName)
{
_confirmationWordsSourceList = new SourceList<RecoveryWordViewModel>();
_isSkipEnable = Services.WalletManager.Network != Network.Main || System.Diagnostics.Debugger.IsAttached;
@@ -35,7 +37,7 @@ public partial class ConfirmRecoveryWordsViewModel : RoutableViewModel
EnableBack = true;
- NextCommand = ReactiveCommand.CreateFromTask(() => OnNextAsync(keyManager), nextCommandCanExecute);
+ NextCommand = ReactiveCommand.CreateFromTask(() => OnNextAsync(mnemonic, keyManager, walletName), nextCommandCanExecute);
if (_isSkipEnable)
{
@@ -58,9 +60,37 @@ public partial class ConfirmRecoveryWordsViewModel : RoutableViewModel
public ReadOnlyObservableCollection<RecoveryWordViewModel> ConfirmationWords => _confirmationWords;
- private async Task OnNextAsync(KeyManager keyManager)
+ private async Task OnNextAsync(Mnemonic mnemonics, KeyManager? keyManager = null, string? walletName = "")
+ {
+ if (keyManager is { })
{
await NavigateDialogAsync(new CoinJoinProfilesViewModel(keyManager, true), NavigationTarget.DialogScreen);
+ return;
+ }
+
+ var dialogResult = await NavigateDialogAsync(
+ new CreatePasswordDialogViewModel("Add Password", enableEmpty: true),
+ NavigationTarget.CompactDialogScreen);
+
+ if (dialogResult.Result is { } password)
+ {
+ IsBusy = true;
+
+ var (km, mnemonic) = await Task.Run(
+ () =>
+ {
+ var walletGenerator = new WalletGenerator(
+ Services.WalletManager.WalletDirectories.WalletsDir,
+ Services.WalletManager.Network)
+ {
+ TipHeight = Services.BitcoinStore.SmartHeaderChain.TipHeight
+ };
+ return walletGenerator.CreateWalletWithMnemonic(mnemonics, walletName, password);
+ });
+ IsBusy = false;
+ await NavigateDialogAsync(new CoinJoinProfilesViewModel(km, true), NavigationTarget.DialogScreen);
+ }
+
}
private void OnCancel()

MODIFY
WalletWasabi.Fluent/ViewModels/AddWallet/Create/RecoveryWordsViewModel.cs
WalletWasabi.Fluent/ViewModels/AddWallet/Create/RecoveryWordsViewModel.cs
@@ -10,7 +10,7 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet.Create;
[NavigationMetaData(Title = "Recovery Words")]
public partial class RecoveryWordsViewModel : RoutableViewModel
{
- public RecoveryWordsViewModel(KeyManager keyManager, Mnemonic mnemonic)
+ public RecoveryWordsViewModel(Mnemonic mnemonic, KeyManager keyManager = null, string? waletName = "")
{
MnemonicWords = new List<RecoveryWordViewModel>();
@@ -21,15 +21,15 @@ public partial class RecoveryWordsViewModel : RoutableViewModel
EnableBack = true;
- NextCommand = ReactiveCommand.Create(() => OnNext(keyManager));
+ NextCommand = ReactiveCommand.Create(() => OnNext(mnemonic, keyManager, waletName));
CancelCommand = ReactiveCommand.Create(OnCancel);
}
public List<RecoveryWordViewModel> MnemonicWords { get; set; }
- private void OnNext(KeyManager keyManager)
+ private void OnNext(Mnemonic mnemonic, KeyManager? keyManager, string? walletName)
{
- Navigate().To(new ConfirmRecoveryWordsViewModel(MnemonicWords, keyManager));
+ Navigate().To(new ConfirmRecoveryWordsViewModel(MnemonicWords, mnemonic, keyManager, walletName));
}
private void OnCancel()

MODIFY
WalletWasabi.Fluent/ViewModels/AddWallet/WalletNamePageViewModel.cs
WalletWasabi.Fluent/ViewModels/AddWallet/WalletNamePageViewModel.cs
@@ -44,7 +44,7 @@ public partial class WalletNamePageViewModel : RoutableViewModel
switch (creationOption)
{
case WalletCreationOption.AddNewWallet:
- await CreatePasswordAsync(walletName);
+ await CreateMnemonics(walletName);
break;
case WalletCreationOption.ConnectToHardwareWallet:
@@ -78,17 +78,11 @@ public partial class WalletNamePageViewModel : RoutableViewModel
}
}
- private async Task CreatePasswordAsync(string walletName)
- {
- var dialogResult = await NavigateDialogAsync(
- new CreatePasswordDialogViewModel("Add Password", enableEmpty: true),
- NavigationTarget.CompactDialogScreen);
-
- if (dialogResult.Result is { } password)
+ private async Task CreateMnemonics(string walletName)
{
IsBusy = true;
- var (km, mnemonic) = await Task.Run(
+ var mnemonic = await Task.Run(
() =>
{
var walletGenerator = new WalletGenerator(
@@ -97,13 +91,12 @@ public partial class WalletNamePageViewModel : RoutableViewModel
{
TipHeight = Services.BitcoinStore.SmartHeaderChain.TipHeight
};
- return walletGenerator.GenerateWallet(walletName, password);
+ return walletGenerator.GenerateDummyWalletMnemonic(walletName);
});
- Navigate().To(new RecoveryWordsViewModel(km, mnemonic));
-
IsBusy = false;
- }
+
+ Navigate().To(new RecoveryWordsViewModel(mnemonic, null, walletName));
}
private void ValidateWalletName(IValidationErrors errors)

MODIFY
WalletWasabi/Blockchain/Keys/KeyManager.cs
WalletWasabi/Blockchain/Keys/KeyManager.cs
@@ -212,6 +212,20 @@ public class KeyManager
return new KeyManager(encryptedSecret, extKey.ChainCode, masterFingerprint, extPubKey, skipSynchronization: true, AbsoluteMinGapLimit, blockchainState, filePath, keyPath);
}
+ public static KeyManager CreateNewWithMnemonic(Mnemonic mnemonic, string password, Network network, string? filePath = null)
+ {
+ password ??= "";
+
+ ExtKey extKey = mnemonic.DeriveExtKey(password);
+ var encryptedSecret = extKey.PrivateKey.GetEncryptedBitcoinSecret(password, Network.Main);
+
+ HDFingerprint masterFingerprint = extKey.Neuter().PubKey.GetHDFingerPrint();
+ BlockchainState blockchainState = new(network);
+ KeyPath keyPath = GetAccountKeyPath(network);
+ ExtPubKey extPubKey = extKey.Derive(keyPath).Neuter();
+ return new KeyManager(encryptedSecret, extKey.ChainCode, masterFingerprint, extPubKey, skipSynchronization: true, AbsoluteMinGapLimit, blockchainState, filePath, keyPath);
+ }
+
public static KeyManager CreateNewWatchOnly(ExtPubKey extPubKey, string? filePath = null)
{
return new KeyManager(null, null, null, extPubKey, skipSynchronization: false, AbsoluteMinGapLimit, new BlockchainState(), filePath);

MODIFY
WalletWasabi/Blockchain/Keys/WalletGenerator.cs
WalletWasabi/Blockchain/Keys/WalletGenerator.cs
@@ -49,6 +49,48 @@ public class WalletGenerator
return (km, mnemonic);
}
+ public (KeyManager, Mnemonic) CreateWalletWithMnemonic(Mnemonic mnemonic,string walletName, string password)
+ {
+ if (!ValidateWalletName(walletName))
+ {
+ throw new ArgumentException("Invalid wallet name.");
+ }
+
+ string walletFilePath = Path.Combine(WalletsDir, $"{walletName}.json");
+ if (File.Exists(walletFilePath))
+ {
+ throw new ArgumentException("Wallet name is already taken.");
+ }
+
+ // Here we are not letting anything that will be autocorrected later. We need to generate the wallet exactly with the entered password because of compatibility.
+ PasswordHelper.Guard(password);
+
+ var km = KeyManager.CreateNewWithMnemonic(mnemonic, password, Network);
+ km.AutoCoinJoin = true;
+ km.SetNetwork(Network);
+ km.SetBestHeight(new Height(TipHeight));
+ km.SetFilePath(walletFilePath);
+ return (km, mnemonic);
+ }
+
+ public Mnemonic GenerateDummyWalletMnemonic(string walletName)
+ {
+ if (!ValidateWalletName(walletName))
+ {
+ throw new ArgumentException("Invalid wallet name.");
+ }
+
+ var walletFilePath = Path.Combine(WalletsDir, $"{walletName}.json");
+ if (File.Exists(walletFilePath))
+ {
+ throw new ArgumentException("Wallet name is already taken.");
+ }
+
+ var km = KeyManager.CreateNew(out Mnemonic mnemonic, string.Empty, Network);
+
+ return mnemonic;
+ }
+
public static bool ValidateWalletName(string walletName)
{
if (string.IsNullOrWhiteSpace(walletName))

Change the wallet creation ux to set the password later