From c7048fd152ad79cba4a08cacc29ada30a13f5230 Mon Sep 17 00:00:00 2001
From: XMRZombie <monerozombie@proton.me>
Date: Thu, 12 Dec 2024 18:52:32 +0000
Subject: [PATCH 1/4] Update SeedWordsView.java

Uses net.glxn.qrgen.QRCode to show seedwords as a QRCode for external wallet backup
---
 .../content/seedwords/SeedWordsView.java      | 257 ++++++------------
 1 file changed, 79 insertions(+), 178 deletions(-)

diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
index 9d90994029..0ada28e55d 100644
--- a/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
+++ b/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
@@ -1,36 +1,36 @@
 /*
- * This file is part of Bisq.
- *
- * Bisq is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or (at
- * your option) any later version.
- *
- * Bisq is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
- * License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with Bisq. If not, see <http://www.gnu.org/licenses/>.
- */
+* This file is part of Bisq.
+*
+* Bisq is free software: you can redistribute it and/or modify it
+* under the terms of the GNU Affero General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or (at
+* your option) any later version.
+*
+* Bisq is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+* License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
+*/
 
 /*
- * This file is part of Haveno.
- *
- * Haveno is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or (at
- * your option) any later version.
- *
- * Haveno is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
- * License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with Haveno. If not, see <http://www.gnu.org/licenses/>.
- */
+* This file is part of Haveno.
+*
+* Haveno is free software: you can redistribute it and/or modify it
+* under the terms of the GNU Affero General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or (at
+* your option) any later version.
+*
+* Haveno is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+* License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
+*/
 
 package haveno.desktop.main.account.content.seedwords;
 
@@ -53,25 +53,29 @@ import static haveno.desktop.util.FormBuilder.addTitledGroupBg;
 import static haveno.desktop.util.FormBuilder.addTopLabelDatePicker;
 import static haveno.desktop.util.FormBuilder.addTopLabelTextArea;
 import haveno.desktop.util.Layout;
-import java.io.File;
-import java.io.IOException;
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.util.TimeZone;
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.SimpleBooleanProperty;
 import javafx.beans.value.ChangeListener;
 import javafx.scene.control.Button;
 import javafx.scene.control.DatePicker;
 import javafx.scene.control.TextArea;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
 import javafx.scene.layout.GridPane;
+import javafx.scene.layout.HBox;
+import net.glxn.qrgen.QRCode;
+import net.glxn.qrgen.image.ImageType;
 import org.bitcoinj.crypto.MnemonicCode;
 import org.bitcoinj.crypto.MnemonicException;
 import org.bitcoinj.wallet.DeterministicSeed;
-//import static javafx.beans.binding.Bindings.createBooleanBinding;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.List;
 
 @FxmlView
 public class SeedWordsView extends ActivatableView<GridPane, Void> {
@@ -93,6 +97,7 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
     private String seedWordText;
     private LocalDate walletCreationDate;
 
+    private ImageView qrCodeImageView;
 
     ///////////////////////////////////////////////////////////////////////////////////////////
     // Constructor, lifecycle
@@ -100,10 +105,10 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
 
     @Inject
     private SeedWordsView(WalletsManager walletsManager,
-                          OpenOfferManager openOfferManager,
-                          XmrWalletService xmrWalletService,
-                          WalletPasswordWindow walletPasswordWindow,
-                          @Named(Config.STORAGE_DIR) File storageDir) {
+                        OpenOfferManager openOfferManager,
+                        XmrWalletService xmrWalletService,
+                        WalletPasswordWindow walletPasswordWindow,
+                        @Named(Config.STORAGE_DIR) File storageDir) {
         this.walletsManager = walletsManager;
         this.openOfferManager = openOfferManager;
         this.xmrWalletService = xmrWalletService;
@@ -120,28 +125,21 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
         displaySeedWordsTextArea.setMaxHeight(70);
         displaySeedWordsTextArea.setEditable(false);
 
+        // Create a container for seed words and QR code
+        HBox hBox = new HBox();
+        qrCodeImageView = new ImageView();
+        qrCodeImageView.setFitWidth(150);
+        qrCodeImageView.setFitHeight(150);
+        hBox.getChildren().add(displaySeedWordsTextArea);  // Seed words text
+        hBox.getChildren().add(qrCodeImageView);           // QR code image view
+
+        root.add(hBox, 0, ++gridRow, 2, 1);  // Add the HBox to the grid
+
         datePicker = addTopLabelDatePicker(root, ++gridRow, Res.get("seed.date"), 10).second;
         datePicker.setMouseTransparent(true);
 
-        // TODO: to re-enable restore functionality:
-        // - uncomment code throughout this file
-        // - support getting wallet's restore height
-        // - support translating between date and restore height
-        // - clear XmrAddressEntries which are incompatible with new wallet and other tests
-        // - update mnemonic validation and restore calls
-
-        // addTitledGroupBg(root, ++gridRow, 3, Res.get("seed.restore.title"), Layout.GROUP_DISTANCE);
-        // seedWordsTextArea = addTopLabelTextArea(root, gridRow, Res.get("seed.seedWords"), "", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
-        // seedWordsTextArea.getStyleClass().add("wallet-seed-words");
-        // seedWordsTextArea.setPrefHeight(40);
-        // seedWordsTextArea.setMaxHeight(40);
-
-        // restoreDatePicker = addTopLabelDatePicker(root, ++gridRow, Res.get("seed.date"), 10).second;
-        // restoreButton = addPrimaryActionButtonAFterGroup(root, ++gridRow, Res.get("seed.restore"));
-
         addTitledGroupBg(root, ++gridRow, 1, Res.get("shared.information"), Layout.GROUP_DISTANCE);
-        addMultilineLabel(root, gridRow, Res.get("account.seed.info"),
-                Layout.FIRST_ROW_AND_GROUP_DISTANCE);
+        addMultilineLabel(root, gridRow, Res.get("account.seed.info"), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
 
         seedWordsValidChangeListener = (observable, oldValue, newValue) -> {
             if (newValue) {
@@ -165,22 +163,6 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
 
     @Override
     public void activate() {
-        // seedWordsValid.addListener(seedWordsValidChangeListener);
-        // seedWordsTextArea.textProperty().addListener(seedWordsTextAreaChangeListener);
-        // restoreButton.disableProperty().bind(createBooleanBinding(() -> !seedWordsValid.get() || !seedWordsEdited.get(),
-        //         seedWordsValid, seedWordsEdited));
-
-        // restoreButton.setOnAction(e -> {
-        //     new Popup().information(Res.get("account.seed.restore.info"))
-        //             .closeButtonText(Res.get("shared.cancel"))
-        //             .actionButtonText(Res.get("account.seed.restore.ok"))
-        //             .onAction(this::onRestore)
-        //             .show();
-        // });
-
-        // seedWordsTextArea.getStyleClass().remove("validation-error");
-        // restoreDatePicker.getStyleClass().remove("validation-error");
-
         String key = "showBackupWarningAtSeedPhrase";
         if (DontShowAgainLookup.showAgain(key)) {
             new Popup().warning(Res.get("account.seed.backup.warning"))
@@ -199,40 +181,27 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
         if (xmrWalletService.isWalletEncrypted()) {
             askForPassword();
         } else {
-            String key = "showSeedWordsWarning";
-            if (DontShowAgainLookup.showAgain(key)) {
-                new Popup().warning(Res.get("account.seed.warn.noPw.msg"))
-                        .actionButtonText(Res.get("account.seed.warn.noPw.yes"))
-                        .onAction(() -> {
-                            DontShowAgainLookup.dontShowAgain(key, true);
-                            initSeedWords(xmrWalletService.getWallet().getSeed());
-                            showSeedScreen();
-                        })
-                        .closeButtonText(Res.get("shared.no"))
-                        .show();
-            } else {
-                initSeedWords(xmrWalletService.getWallet().getSeed());
-                showSeedScreen();
-            }
+            initSeedWords(xmrWalletService.getWallet().getSeed());
+            showSeedScreen();
         }
     }
 
-    @Override
-    protected void deactivate() {
-        displaySeedWordsTextArea.setText("");
-        datePicker.setValue(null);
+    private void showSeedScreen() {
+        displaySeedWordsTextArea.setText(seedWordText);
+        walletCreationDate = Instant.ofEpochSecond(xmrWalletService.getWalletCreationDate()).atZone(ZoneId.systemDefault()).toLocalDate();
+        datePicker.setValue(walletCreationDate);
 
-        // seedWordsValid.removeListener(seedWordsValidChangeListener);
-        // seedWordsTextArea.textProperty().removeListener(seedWordsTextAreaChangeListener);
-        // restoreButton.disableProperty().unbind();
-        // restoreButton.setOnAction(null);
+        // Generate QR code from the seed words and display it
+        generateAndDisplayQRCode(seedWordText);
+    }
 
-        // seedWordsTextArea.setText("");
-
-        // restoreDatePicker.setValue(null);
-
-        // seedWordsTextArea.getStyleClass().remove("validation-error");
-        // restoreDatePicker.getStyleClass().remove("validation-error");
+    private void generateAndDisplayQRCode(String seedWords) {
+        // Generate QR Code using the net.glxn.qrgen library
+        ByteArrayInputStream qrCodeStream = new ByteArrayInputStream(
+            QRCode.from(seedWords).to(ImageType.PNG).stream().toByteArray()
+        );
+        Image qrCodeImage = new Image(qrCodeStream);
+        qrCodeImageView.setImage(qrCodeImage);
     }
 
     private void askForPassword() {
@@ -246,77 +215,9 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
         seedWordText = seed;
     }
 
-    private void showSeedScreen() {
-        displaySeedWordsTextArea.setText(seedWordText);
-        walletCreationDate = Instant.ofEpochSecond(xmrWalletService.getWalletCreationDate()).atZone(ZoneId.systemDefault()).toLocalDate();
-        datePicker.setValue(walletCreationDate);
-    }
-
-    private void onRestore() {
-        if (walletsManager.hasPositiveBalance()) {
-            new Popup().warning(Res.get("seed.warn.walletNotEmpty.msg"))
-                    .actionButtonText(Res.get("seed.warn.walletNotEmpty.restore"))
-                    .onAction(this::checkIfEncrypted)
-                    .closeButtonText(Res.get("seed.warn.walletNotEmpty.emptyWallet"))
-                    .show();
-        } else {
-            checkIfEncrypted();
-        }
-    }
-
-    private void checkIfEncrypted() {
-        if (walletsManager.areWalletsEncrypted()) {
-            new Popup().information(Res.get("seed.warn.notEncryptedAnymore"))
-                    .closeButtonText(Res.get("shared.no"))
-                    .actionButtonText(Res.get("shared.yes"))
-                    .onAction(this::doRestoreDateCheck)
-                    .show();
-        } else {
-            doRestoreDateCheck();
-        }
-    }
-
-    private void doRestoreDateCheck() {
-        if (restoreDatePicker.getValue() == null) {
-            // Provide feedback when attempting to restore a wallet from seed words without specifying a date
-            new Popup().information(Res.get("seed.warn.walletDateEmpty"))
-                    .closeButtonText(Res.get("shared.no"))
-                    .actionButtonText(Res.get("shared.yes"))
-                    .onAction(this::doRestore)
-                    .show();
-        } else {
-            doRestore();
-        }
-    }
-
-    private LocalDate getWalletDate() {
-        LocalDate walletDate = restoreDatePicker.getValue();
-        // Even though no current Haveno wallet could have been created before the v0.5 release date (2017.06.28),
-        // the user may want to import from a seed generated by another wallet.
-        // So use when the BIP39 standard was finalised (2013.10.09) as the oldest possible wallet date.
-        LocalDate oldestWalletDate = LocalDate.ofInstant(
-                Instant.ofEpochMilli(MnemonicCode.BIP39_STANDARDISATION_TIME_SECS * 1000),
-                TimeZone.getDefault().toZoneId());
-        if (walletDate == null) {
-            // No date was specified, perhaps the user doesn't know the wallet date
-            walletDate = oldestWalletDate;
-        } else if (walletDate.isBefore(oldestWalletDate)) {
-            walletDate = oldestWalletDate;
-        } else if (walletDate.isAfter(LocalDate.now())) {
-            walletDate = LocalDate.now();
-        }
-        return walletDate;
-    }
-
-    private void doRestore() {
-        LocalDate walletDate = getWalletDate();
-        // We subtract 1 day to be sure to not have any issues with timezones. Even if we can be sure that the timezone
-        // is handled correctly it could be that the user created the wallet in one timezone and make a restore at
-        // a different timezone which could lead in the worst case that he miss the first day of the wallet transactions.
-        LocalDateTime localDateTime = walletDate.atStartOfDay().minusDays(1);
-        long date = localDateTime.toEpochSecond(ZoneOffset.UTC);
-
-        DeterministicSeed seed = new DeterministicSeed(Splitter.on(" ").splitToList(seedWordsTextArea.getText()), null, "", date);
-        SharedPresentation.restoreSeedWords(walletsManager, openOfferManager, seed, storageDir);
+    @Override
+    protected void deactivate() {
+        displaySeedWordsTextArea.setText("");
+        datePicker.setValue(null);
     }
 }

From 1933cc8b28d5a9304340aa20ca27a7a58b32937d Mon Sep 17 00:00:00 2001
From: XMRZombie <monerozombie@proton.me>
Date: Sun, 15 Dec 2024 12:09:49 +0000
Subject: [PATCH 2/4] Update SeedWordsView.java

Summaries:
- Restored lost code parts.
- Added UI updates
- Using char for memory safe seed usage
---
 .../content/seedwords/SeedWordsView.java      | 198 +++++++++++++++---
 1 file changed, 168 insertions(+), 30 deletions(-)

diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
index 0ada28e55d..c6303b00fd 100644
--- a/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
+++ b/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
@@ -53,6 +53,18 @@ import static haveno.desktop.util.FormBuilder.addTitledGroupBg;
 import static haveno.desktop.util.FormBuilder.addTopLabelDatePicker;
 import static haveno.desktop.util.FormBuilder.addTopLabelTextArea;
 import haveno.desktop.util.Layout;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.util.TimeZone;
+import java.util.List;
+import javafx.application.Platform;
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.SimpleBooleanProperty;
 import javafx.beans.value.ChangeListener;
@@ -68,14 +80,7 @@ import net.glxn.qrgen.image.ImageType;
 import org.bitcoinj.crypto.MnemonicCode;
 import org.bitcoinj.crypto.MnemonicException;
 import org.bitcoinj.wallet.DeterministicSeed;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.ZoneId;
-import java.util.List;
+//import static javafx.beans.binding.Bindings.createBooleanBinding;
 
 @FxmlView
 public class SeedWordsView extends ActivatableView<GridPane, Void> {
@@ -94,7 +99,7 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
     private final SimpleBooleanProperty seedWordsValid = new SimpleBooleanProperty(false);
     private ChangeListener<String> seedWordsTextAreaChangeListener;
     private final BooleanProperty seedWordsEdited = new SimpleBooleanProperty();
-    private String seedWordText;
+    private char[] seedWordText;
     private LocalDate walletCreationDate;
 
     private ImageView qrCodeImageView;
@@ -138,8 +143,25 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
         datePicker = addTopLabelDatePicker(root, ++gridRow, Res.get("seed.date"), 10).second;
         datePicker.setMouseTransparent(true);
 
+        // TODO: to re-enable restore functionality:
+        // - uncomment code throughout this file
+        // - support getting wallet's restore height
+        // - support translating between date and restore height
+        // - clear XmrAddressEntries which are incompatible with new wallet and other tests
+        // - update mnemonic validation and restore calls
+
+        // addTitledGroupBg(root, ++gridRow, 3, Res.get("seed.restore.title"), Layout.GROUP_DISTANCE);
+        // seedWordsTextArea = addTopLabelTextArea(root, gridRow, Res.get("seed.seedWords"), "", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
+        // seedWordsTextArea.getStyleClass().add("wallet-seed-words");
+        // seedWordsTextArea.setPrefHeight(40);
+        // seedWordsTextArea.setMaxHeight(40);
+
+        // restoreDatePicker = addTopLabelDatePicker(root, ++gridRow, Res.get("seed.date"), 10).second;
+        // restoreButton = addPrimaryActionButtonAFterGroup(root, ++gridRow, Res.get("seed.restore"));
+
         addTitledGroupBg(root, ++gridRow, 1, Res.get("shared.information"), Layout.GROUP_DISTANCE);
-        addMultilineLabel(root, gridRow, Res.get("account.seed.info"), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
+        addMultilineLabel(root, gridRow, Res.get("account.seed.info"),
+        Layout.FIRST_ROW_AND_GROUP_DISTANCE);
 
         seedWordsValidChangeListener = (observable, oldValue, newValue) -> {
             if (newValue) {
@@ -163,6 +185,22 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
 
     @Override
     public void activate() {
+        // seedWordsValid.addListener(seedWordsValidChangeListener);
+        // seedWordsTextArea.textProperty().addListener(seedWordsTextAreaChangeListener);
+        // restoreButton.disableProperty().bind(createBooleanBinding(() -> !seedWordsValid.get() || !seedWordsEdited.get(),
+        //         seedWordsValid, seedWordsEdited));
+
+        // restoreButton.setOnAction(e -> {
+        //     new Popup().information(Res.get("account.seed.restore.info"))
+        //             .closeButtonText(Res.get("shared.cancel"))
+        //             .actionButtonText(Res.get("account.seed.restore.ok"))
+        //             .onAction(this::onRestore)
+        //             .show();
+        // });
+
+        // seedWordsTextArea.getStyleClass().remove("validation-error");
+        // restoreDatePicker.getStyleClass().remove("validation-error");
+
         String key = "showBackupWarningAtSeedPhrase";
         if (DontShowAgainLookup.showAgain(key)) {
             new Popup().warning(Res.get("account.seed.backup.warning"))
@@ -181,43 +219,143 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
         if (xmrWalletService.isWalletEncrypted()) {
             askForPassword();
         } else {
-            initSeedWords(xmrWalletService.getWallet().getSeed());
-            showSeedScreen();
+            String key = "showSeedWordsWarning";
+            if (DontShowAgainLookup.showAgain(key)) {
+                new Popup().warning(Res.get("account.seed.warn.noPw.msg"))
+                        .actionButtonText(Res.get("account.seed.warn.noPw.yes"))
+                        .onAction(() -> {
+                            DontShowAgainLookup.dontShowAgain(key, true);
+                            initSeedWords(xmrWalletService.getWallet().getSeed());
+                            Platform.runLater(this::showSeedScreen);
+                        })
+                        .closeButtonText(Res.get("shared.no"))
+                        .show();
+            } else {
+                initSeedWords(xmrWalletService.getWallet().getSeed());
+                Platform.runLater(this::showSeedScreen);
+            }
         }
     }
 
-    private void showSeedScreen() {
-        displaySeedWordsTextArea.setText(seedWordText);
-        walletCreationDate = Instant.ofEpochSecond(xmrWalletService.getWalletCreationDate()).atZone(ZoneId.systemDefault()).toLocalDate();
-        datePicker.setValue(walletCreationDate);
+    @Override
+    protected void deactivate() {
+        clearSensitiveData();
+        datePicker.setValue(null);
 
-        // Generate QR code from the seed words and display it
-        generateAndDisplayQRCode(seedWordText);
+        // seedWordsValid.removeListener(seedWordsValidChangeListener);
+        // seedWordsTextArea.textProperty().removeListener(seedWordsTextAreaChangeListener);
+        // restoreButton.disableProperty().unbind();
+        // restoreButton.setOnAction(null);
+
+        // seedWordsTextArea.setText("");
+
+        // restoreDatePicker.setValue(null);
+
+        // seedWordsTextArea.getStyleClass().remove("validation-error");
+        // restoreDatePicker.getStyleClass().remove("validation-error");
+    }
+
+    private void clearSensitiveData() {
+        if (seedWordText != null) {
+            // Overwrite seed words in memory before clearing
+            java.util.Arrays.fill(seedWordText, ' ');
+        }
     }
 
     private void generateAndDisplayQRCode(String seedWords) {
-        // Generate QR Code using the net.glxn.qrgen library
-        ByteArrayInputStream qrCodeStream = new ByteArrayInputStream(
-            QRCode.from(seedWords).to(ImageType.PNG).stream().toByteArray()
-        );
-        Image qrCodeImage = new Image(qrCodeStream);
-        qrCodeImageView.setImage(qrCodeImage);
+        Platform.runLater(() -> {
+            // Generate QR Code using the net.glxn.qrgen library
+            ByteArrayInputStream qrCodeStream = new ByteArrayInputStream(
+                QRCode.from(seedWords).to(ImageType.PNG).stream().toByteArray()
+            );
+            Image qrCodeImage = new Image(qrCodeStream);
+            qrCodeImageView.setImage(qrCodeImage);
+        });
     }
 
     private void askForPassword() {
         walletPasswordWindow.headLine(Res.get("account.seed.enterPw")).onSuccess(() -> {
             initSeedWords(xmrWalletService.getWallet().getSeed());
-            showSeedScreen();
+            Platform.runLater(this::showSeedScreen);
         }).hideForgotPasswordButton().show();
     }
 
     private void initSeedWords(String seed) {
-        seedWordText = seed;
+        seedWordText = seed.toCharArray();
     }
 
-    @Override
-    protected void deactivate() {
-        displaySeedWordsTextArea.setText("");
-        datePicker.setValue(null);
+    private void showSeedScreen() {
+        displaySeedWordsTextArea.setText(new String(seedWordText));
+        walletCreationDate = Instant.ofEpochSecond(xmrWalletService.getWalletCreationDate()).atZone(ZoneId.systemDefault()).toLocalDate();
+        datePicker.setValue(walletCreationDate);
+        generateAndDisplayQRCode(new String(seedWordText));
+    }
+
+    private void onRestore() {
+        if (walletsManager.hasPositiveBalance()) {
+            new Popup().warning(Res.get("seed.warn.walletNotEmpty.msg"))
+                    .actionButtonText(Res.get("seed.warn.walletNotEmpty.restore"))
+                    .onAction(this::checkIfEncrypted)
+                    .closeButtonText(Res.get("seed.warn.walletNotEmpty.emptyWallet"))
+                    .show();
+        } else {
+            checkIfEncrypted();
+        }
+    }
+
+    private void checkIfEncrypted() {
+        if (walletsManager.areWalletsEncrypted()) {
+            new Popup().information(Res.get("seed.warn.notEncryptedAnymore"))
+                    .closeButtonText(Res.get("shared.no"))
+                    .actionButtonText(Res.get("shared.yes"))
+                    .onAction(this::doRestoreDateCheck)
+                    .show();
+        } else {
+            doRestoreDateCheck();
+        }
+    }
+
+    private void doRestoreDateCheck() {
+        if (restoreDatePicker.getValue() == null) {
+            // Provide feedback when attempting to restore a wallet from seed words without specifying a date
+            new Popup().information(Res.get("seed.warn.walletDateEmpty"))
+                    .closeButtonText(Res.get("shared.no"))
+                    .actionButtonText(Res.get("shared.yes"))
+                    .onAction(this::doRestore)
+                    .show();
+        } else {
+            doRestore();
+        }
+    }
+
+    private LocalDate getWalletDate() {
+        LocalDate walletDate = restoreDatePicker.getValue();
+        // Even though no current Haveno wallet could have been created before the v0.5 release date (2017.06.28),
+        // the user may want to import from a seed generated by another wallet.
+        // So use when the BIP39 standard was finalised (2013.10.09) as the oldest possible wallet date.
+        LocalDate oldestWalletDate = LocalDate.ofInstant(
+                Instant.ofEpochMilli(MnemonicCode.BIP39_STANDARDISATION_TIME_SECS * 1000),
+                TimeZone.getDefault().toZoneId());
+        if (walletDate == null) {
+            // No date was specified, perhaps the user doesn't know the wallet date
+            walletDate = oldestWalletDate;
+        } else if (walletDate.isBefore(oldestWalletDate)) {
+            walletDate = oldestWalletDate;
+        } else if (walletDate.isAfter(LocalDate.now())) {
+            walletDate = LocalDate.now();
+        }
+        return walletDate;
+    }
+
+    private void doRestore() {
+        LocalDate walletDate = getWalletDate();
+        // We subtract 1 day to be sure to not have any issues with timezones. Even if we can be sure that the timezone
+        // is handled correctly it could be that the user created the wallet in one timezone and make a restore at
+        // a different timezone which could lead in the worst case that he miss the first day of the wallet transactions.
+        LocalDateTime localDateTime = walletDate.atStartOfDay().minusDays(1);
+        long date = localDateTime.toEpochSecond(ZoneOffset.UTC);
+
+        DeterministicSeed seed = new DeterministicSeed(List.of(new String(seedWordText).split(" ")), null, "", date);
+        SharedPresentation.restoreSeedWords(walletsManager, openOfferManager, seed, storageDir);
     }
 }

From 5b754bff9d24ea0090899cd5dfd9921ad471c833 Mon Sep 17 00:00:00 2001
From: XMRZombie <monerozombie@proton.me>
Date: Sun, 15 Dec 2024 12:17:47 +0000
Subject: [PATCH 3/4] Update SeedWordsView.java

Removing unused import
---
 .../desktop/main/account/content/seedwords/SeedWordsView.java    | 1 -
 1 file changed, 1 deletion(-)

diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
index c6303b00fd..b7b9299d6a 100644
--- a/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
+++ b/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
@@ -56,7 +56,6 @@ import haveno.desktop.util.Layout;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
-import java.nio.CharBuffer;
 import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;

From 197997ba7f2957b134d9d8da680d887fb46942e0 Mon Sep 17 00:00:00 2001
From: XMRZombie <monerozombie@proton.me>
Date: Sun, 15 Dec 2024 12:44:43 +0000
Subject: [PATCH 4/4] Update SeedWordsView.java

Using cakewallet's QR Code format
```String formattedSeed = "monero-wallet:?seed=" + seedWords.replace(" ", "+");```
---
 .../main/account/content/seedwords/SeedWordsView.java        | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
index b7b9299d6a..1568c5ddde 100644
--- a/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
+++ b/desktop/src/main/java/haveno/desktop/main/account/content/seedwords/SeedWordsView.java
@@ -263,9 +263,12 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
 
     private void generateAndDisplayQRCode(String seedWords) {
         Platform.runLater(() -> {
+            // Using cakewallet's QR Code format 
+            String formattedSeed = "monero-wallet:?seed=" + seedWords.replace(" ", "+");
+    
             // Generate QR Code using the net.glxn.qrgen library
             ByteArrayInputStream qrCodeStream = new ByteArrayInputStream(
-                QRCode.from(seedWords).to(ImageType.PNG).stream().toByteArray()
+                QRCode.from(formattedSeed).to(ImageType.PNG).stream().toByteArray()
             );
             Image qrCodeImage = new Image(qrCodeStream);
             qrCodeImageView.setImage(qrCodeImage);