Implement Import Hardware Wallet
MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -683,11 +683,15 @@ namespace WalletWasabi.Gui
throw new NotSupportedException("This is impossible.");
}
- public string GetNextHardwareWalletName(Hwi.Models.HardwareWalletInfo hwi)
+ public string GetNextHardwareWalletName(Hwi.Models.HardwareWalletInfo hwi = null, string customPrefix = null)
{
+ var prefix = customPrefix is null ?
+ (hwi is null ? "HardwareWallet" : hwi.Type.ToString())
+ : customPrefix;
+
for (int i = 0; i < int.MaxValue; i++)
{
- var name = $"{hwi.Type}{i}";
+ var name = $"{prefix}{i}";
if (!File.Exists(Path.Combine(WalletsDir, $"{name}.json")))
{
return name;

MODIFY
WalletWasabi.Gui/Tabs/WalletManager/LoadWalletView.xaml
WalletWasabi.Gui/Tabs/WalletManager/LoadWalletView.xaml
<TextBlock Text="{Binding LoadButtonText}" />
</StackPanel>
</Button>
+ <Button IsVisible="{Binding IsHardwareWallet}" Content="Import Wallet File" Command="{Binding ImportCommand}" DockPanel.Dock="Right" Margin="0 0 10 0" />
<Button IsVisible="{Binding IsDesktopWallet}" Command="{Binding OpenFolderCommand}" DockPanel.Dock="Right" Margin="0 0 10 0">
<StackPanel Orientation="Horizontal" Spacing="8">
<DrawingPresenter Width="28">

MODIFY
WalletWasabi.Gui/Tabs/WalletManager/LoadWalletViewModel.cs
WalletWasabi.Gui/Tabs/WalletManager/LoadWalletViewModel.cs
+using Avalonia;
+using Avalonia.Controls;
using Avalonia.Threading;
using AvalonStudio.Extensibility;
using AvalonStudio.Shell;
@@ -96,10 +98,39 @@ namespace WalletWasabi.Gui.Tabs.WalletManager
LoadCommand = ReactiveCommand.CreateFromTask(async () => await LoadWalletAsync(), this.WhenAnyValue(x => x.CanLoadWallet));
TestPasswordCommand = ReactiveCommand.CreateFromTask(async () => await LoadKeyManagerAsync(requirePassword: true, isHardwareWallet: false), this.WhenAnyValue(x => x.CanTestPassword));
OpenFolderCommand = ReactiveCommand.Create(OpenWalletsFolder);
+ ImportCommand = ReactiveCommand.CreateFromTask(async () =>
+ {
+ try
+ {
+ var ofd = new OpenFileDialog();
+ var selected = await ofd.ShowAsync(Application.Current.MainWindow);
+ if (selected != null && selected.Any())
+ {
+ var path = selected.First();
+ var jsonString = await File.ReadAllTextAsync(path);
+ var json = JObject.Parse(jsonString);
+ var xpubString = json["ExtPubKey"].ToString();
+ var mfpString = json["MasterFingerprint"].ToString();
+ HDFingerprint mfp = NBitcoinHelpers.BetterParseHDFingerprint(mfpString);
+ ExtPubKey extPubKey = NBitcoinHelpers.BetterParseExtPubKey(xpubString);
+ Logger.LogInfo<LoadWalletViewModel>("Creating new wallet file.");
+ var walletName = Global.GetNextHardwareWalletName();
+ var walletFullPath = Global.GetWalletFullPath(walletName);
+ KeyManager.CreateNewHardwareWalletWatchOnly(mfp, extPubKey, walletFullPath);
+ owner.SelectLoadWallet();
+ }
+ }
+ catch (Exception ex)
+ {
+ SetWarningMessage(ex.ToTypeMessageString());
+ Logger.LogError<TransactionBroadcasterViewModel>(ex);
+ }
+ }, outputScheduler: RxApp.MainThreadScheduler);
LoadCommand.ThrownExceptions.Subscribe(ex => Logger.LogWarning<LoadWalletViewModel>(ex));
TestPasswordCommand.ThrownExceptions.Subscribe(ex => Logger.LogWarning<LoadWalletViewModel>(ex));
OpenFolderCommand.ThrownExceptions.Subscribe(ex => Logger.LogWarning<LoadWalletViewModel>(ex));
+ ImportCommand.ThrownExceptions.Subscribe(ex => Logger.LogWarning<LoadWalletViewModel>(ex));
SetLoadButtonText();
@@ -313,6 +344,7 @@ namespace WalletWasabi.Gui.Tabs.WalletManager
public ReactiveCommand<Unit, Unit> LoadCommand { get; }
public ReactiveCommand<Unit, KeyManager> TestPasswordCommand { get; }
+ public ReactiveCommand<Unit, Unit> ImportCommand { get; set; }
public void TryRefreshHardwareWallets(IEnumerable<HardwareWalletInfo> hwis)
{

MODIFY
WalletWasabi.Gui/Tabs/WalletManager/RecoverWalletViewModel.cs
WalletWasabi.Gui/Tabs/WalletManager/RecoverWalletViewModel.cs
@@ -81,7 +81,7 @@ namespace WalletWasabi.Gui.Tabs.WalletManager
catch (Exception ex)
{
ValidationMessage = ex.ToTypeMessageString();
- Logger.LogError<LoadWalletViewModel>(ex);
+ Logger.LogError<RecoverWalletViewModel>(ex);
}
}
});

MODIFY
WalletWasabi/Helpers/NBitcoinHelpers.cs
WalletWasabi/Helpers/NBitcoinHelpers.cs
@@ -75,6 +75,24 @@ namespace WalletWasabi.Helpers
return epk;
}
+ public static HDFingerprint BetterParseHDFingerprint(string hdFingerPrintString)
+ {
+ hdFingerPrintString = Guard.NotNullOrEmptyOrWhitespace(nameof(hdFingerPrintString), hdFingerPrintString, trim: true);
+
+ HDFingerprint hdfp;
+ try
+ {
+ var hdfpu = uint.Parse(hdFingerPrintString);
+ hdfp = new HDFingerprint(hdfpu);
+ }
+ catch
+ {
+ // Try hex, Old wallet format was like this.
+ hdfp = new HDFingerprint(ByteHelpers.FromHex(hdFingerPrintString));
+ }
+ return hdfp;
+ }
+
public static async Task<AddressManager> LoadAddressManagerFromPeerFileAsync(string filePath, Network expectedNetwork = null)
{
byte[] data, hash;

Implement Import Hardware Wallet