diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties
index 9eb3417d60d..c9661460714 100644
--- a/core/src/main/resources/i18n/displayStrings.properties
+++ b/core/src/main/resources/i18n/displayStrings.properties
@@ -1042,7 +1042,7 @@ funds.tx.noTxAvailable=No transactions available
funds.tx.revert=Revert
funds.tx.txSent=Transaction successfully sent to a new address in the local Bisq wallet.
funds.tx.direction.self=Sent to yourself
-funds.tx.daoTxFee=Miner fee for DAO tx
+funds.tx.daoTxFee=Miner fee for BSQ tx
funds.tx.reimbursementRequestTxFee=Reimbursement request
funds.tx.compensationRequestTxFee=Compensation request
funds.tx.dustAttackTx=Received dust
@@ -1227,7 +1227,7 @@ settings.preferences.editCustomExplorer.headline=Explorer Settings
settings.preferences.editCustomExplorer.description=Choose a system defined explorer from the list on the left, and/or \
customize to suit your own preferences.
settings.preferences.editCustomExplorer.available=Available explorers
-settings.preferences.editCustomExplorer.chosen = Chosen explorer settings
+settings.preferences.editCustomExplorer.chosen=Chosen explorer settings
settings.preferences.editCustomExplorer.name=Name
settings.preferences.editCustomExplorer.txUrl=Transaction URL
settings.preferences.editCustomExplorer.addressUrl=Address URL
@@ -1390,9 +1390,26 @@ account.menu.paymentAccount=National currency accounts
account.menu.altCoinsAccountView=Altcoin accounts
account.menu.password=Wallet password
account.menu.seedWords=Wallet seed
+account.menu.walletInfo=Wallet info
account.menu.backup=Backup
account.menu.notifications=Notifications
+account.menu.walletInfo.balance.headLine=Wallet balances
+account.menu.walletInfo.balance.info=This shows the internal wallet balance including unconfirmed transactions.\n\
+ For Bitcoin the sum of the 'available balance' and the 'reserved for offers balance' must match the internal wallet balance \
+ displayed here.
+account.menu.walletInfo.xpub.headLine=Watch keys (xpub keys)
+account.menu.walletInfo.walletSelector={0} {1} wallet
+account.menu.walletInfo.path.headLine=HD keychain paths
+account.menu.walletInfo.path.info=If you import the seed words in another wallet (like Electrum) you need to define the \
+ path. Use that only in emergency cases when you lost access to the Bisq wallet and the data directory.\n\
+ Spending funds from another wallet can easily screw up the Bisq internal data structures associated with the wallet \
+ data and can lead to failed trades.\n\
+ Do NEVER send BSQ from another wallet as that lead very likely to an invalid BSQ transaction and your \
+ BSQ get burned.
+
+account.menu.walletInfo.openDetails=Show raw wallet details and private keys
+
## TODO should we rename the following to a gereric name?
account.arbitratorRegistration.pubKey=Public key
diff --git a/desktop/src/main/java/bisq/desktop/main/account/AccountView.fxml b/desktop/src/main/java/bisq/desktop/main/account/AccountView.fxml
index e7b9c5b0fed..cf17546702d 100644
--- a/desktop/src/main/java/bisq/desktop/main/account/AccountView.fxml
+++ b/desktop/src/main/java/bisq/desktop/main/account/AccountView.fxml
@@ -44,7 +44,12 @@
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"/>
-
+
+
+
+
diff --git a/desktop/src/main/java/bisq/desktop/main/account/AccountView.java b/desktop/src/main/java/bisq/desktop/main/account/AccountView.java
index 71cc5822578..531f3ac3c09 100644
--- a/desktop/src/main/java/bisq/desktop/main/account/AccountView.java
+++ b/desktop/src/main/java/bisq/desktop/main/account/AccountView.java
@@ -30,6 +30,7 @@
import bisq.desktop.main.account.content.notifications.MobileNotificationsView;
import bisq.desktop.main.account.content.password.PasswordView;
import bisq.desktop.main.account.content.seedwords.SeedWordsView;
+import bisq.desktop.main.account.content.walletinfo.WalletInfoView;
import bisq.desktop.main.account.register.arbitrator.ArbitratorRegistrationView;
import bisq.desktop.main.account.register.mediator.MediatorRegistrationView;
import bisq.desktop.main.account.register.refundagent.RefundAgentRegistrationView;
@@ -67,7 +68,7 @@ public class AccountView extends ActivatableView {
@FXML
Tab fiatAccountsTab, altcoinAccountsTab, notificationTab,
- passwordTab, seedwordsTab, backupTab;
+ passwordTab, seedWordsTab, walletInfoTab, backupTab;
private Navigation.Listener navigationListener;
private ChangeListener tabChangeListener;
@@ -101,7 +102,8 @@ public void initialize() {
altcoinAccountsTab.setText(Res.get("account.menu.altCoinsAccountView").toUpperCase());
notificationTab.setText(Res.get("account.menu.notifications").toUpperCase());
passwordTab.setText(Res.get("account.menu.password").toUpperCase());
- seedwordsTab.setText(Res.get("account.menu.seedWords").toUpperCase());
+ seedWordsTab.setText(Res.get("account.menu.seedWords").toUpperCase());
+ walletInfoTab.setText(Res.get("account.menu.walletInfo").toUpperCase());
backupTab.setText(Res.get("account.menu.backup").toUpperCase());
navigationListener = viewPath -> {
@@ -161,8 +163,10 @@ public void initialize() {
navigation.navigateTo(MainView.class, AccountView.class, MobileNotificationsView.class);
} else if (newValue == passwordTab && selectedTab != passwordTab) {
navigation.navigateTo(MainView.class, AccountView.class, PasswordView.class);
- } else if (newValue == seedwordsTab && selectedTab != seedwordsTab) {
+ } else if (newValue == seedWordsTab && selectedTab != seedWordsTab) {
navigation.navigateTo(MainView.class, AccountView.class, SeedWordsView.class);
+ } else if (newValue == walletInfoTab && selectedTab != walletInfoTab) {
+ navigation.navigateTo(MainView.class, AccountView.class, WalletInfoView.class);
} else if (newValue == backupTab && selectedTab != backupTab) {
navigation.navigateTo(MainView.class, AccountView.class, BackupView.class);
}
@@ -251,8 +255,10 @@ else if (root.getSelectionModel().getSelectedItem() == notificationTab)
navigation.navigateTo(MainView.class, AccountView.class, MobileNotificationsView.class);
else if (root.getSelectionModel().getSelectedItem() == passwordTab)
navigation.navigateTo(MainView.class, AccountView.class, PasswordView.class);
- else if (root.getSelectionModel().getSelectedItem() == seedwordsTab)
+ else if (root.getSelectionModel().getSelectedItem() == seedWordsTab)
navigation.navigateTo(MainView.class, AccountView.class, SeedWordsView.class);
+ else if (root.getSelectionModel().getSelectedItem() == walletInfoTab)
+ navigation.navigateTo(MainView.class, AccountView.class, WalletInfoView.class);
else if (root.getSelectionModel().getSelectedItem() == backupTab)
navigation.navigateTo(MainView.class, AccountView.class, BackupView.class);
else
@@ -314,7 +320,9 @@ private void loadView(Class extends View> viewClass) {
} else if (view instanceof PasswordView) {
selectedTab = passwordTab;
} else if (view instanceof SeedWordsView) {
- selectedTab = seedwordsTab;
+ selectedTab = seedWordsTab;
+ } else if (view instanceof WalletInfoView) {
+ selectedTab = walletInfoTab;
} else if (view instanceof BackupView) {
selectedTab = backupTab;
} else {
diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/walletinfo/WalletInfoView.fxml b/desktop/src/main/java/bisq/desktop/main/account/content/walletinfo/WalletInfoView.fxml
new file mode 100644
index 00000000000..2f00ffb2163
--- /dev/null
+++ b/desktop/src/main/java/bisq/desktop/main/account/content/walletinfo/WalletInfoView.fxml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/walletinfo/WalletInfoView.java b/desktop/src/main/java/bisq/desktop/main/account/content/walletinfo/WalletInfoView.java
new file mode 100644
index 00000000000..265992eb1a9
--- /dev/null
+++ b/desktop/src/main/java/bisq/desktop/main/account/content/walletinfo/WalletInfoView.java
@@ -0,0 +1,170 @@
+/*
+ * 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 .
+ */
+
+package bisq.desktop.main.account.content.walletinfo;
+
+import bisq.desktop.common.view.ActivatableView;
+import bisq.desktop.common.view.FxmlView;
+import bisq.desktop.main.overlays.popups.Popup;
+import bisq.desktop.main.overlays.windows.ShowWalletDataWindow;
+import bisq.desktop.util.Layout;
+
+import bisq.core.btc.listeners.BalanceListener;
+import bisq.core.btc.wallet.BsqWalletService;
+import bisq.core.btc.wallet.BtcWalletService;
+import bisq.core.btc.wallet.WalletService;
+import bisq.core.btc.wallet.WalletsManager;
+import bisq.core.locale.Res;
+import bisq.core.util.FormattingUtils;
+import bisq.core.util.coin.BsqFormatter;
+import bisq.core.util.coin.CoinFormatter;
+
+import bisq.common.config.Config;
+
+import org.bitcoinj.core.Coin;
+import org.bitcoinj.core.Transaction;
+import org.bitcoinj.script.Script;
+import org.bitcoinj.wallet.DeterministicKeyChain;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import javafx.scene.control.Button;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.GridPane;
+
+import static bisq.desktop.util.FormBuilder.addButtonAfterGroup;
+import static bisq.desktop.util.FormBuilder.addMultilineLabel;
+import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
+import static bisq.desktop.util.FormBuilder.addTopLabelTextField;
+import static org.bitcoinj.wallet.Wallet.BalanceType.ESTIMATED_SPENDABLE;
+
+@FxmlView
+public class WalletInfoView extends ActivatableView {
+
+ private final WalletsManager walletsManager;
+ private final BtcWalletService btcWalletService;
+ private final BsqWalletService bsqWalletService;
+ private final CoinFormatter btcFormatter;
+ private final BsqFormatter bsqFormatter;
+ private int gridRow = 0;
+ private Button openDetailsButton;
+ private TextField btcTextField, bsqTextField;
+ private BalanceListener btcWalletBalanceListener;
+ private BalanceListener bsqWalletBalanceListener;
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor, lifecycle
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Inject
+ private WalletInfoView(WalletsManager walletsManager,
+ BtcWalletService btcWalletService,
+ BsqWalletService bsqWalletService,
+ @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
+ BsqFormatter bsqFormatter) {
+ this.walletsManager = walletsManager;
+ this.btcWalletService = btcWalletService;
+ this.bsqWalletService = bsqWalletService;
+ this.btcFormatter = btcFormatter;
+ this.bsqFormatter = bsqFormatter;
+ }
+
+ @Override
+ public void initialize() {
+ addTitledGroupBg(root, gridRow, 3, Res.get("account.menu.walletInfo.balance.headLine"));
+ addMultilineLabel(root, gridRow, Res.get("account.menu.walletInfo.balance.info"), Layout.FIRST_ROW_DISTANCE, Double.MAX_VALUE);
+ btcTextField = addTopLabelTextField(root, ++gridRow, "BTC", -Layout.FLOATING_LABEL_DISTANCE).second;
+ bsqTextField = addTopLabelTextField(root, ++gridRow, "BSQ", -Layout.FLOATING_LABEL_DISTANCE).second;
+
+ addTitledGroupBg(root, ++gridRow, 3, Res.get("account.menu.walletInfo.xpub.headLine"), Layout.GROUP_DISTANCE);
+ addXpubKeys(btcWalletService, "BTC", gridRow, Layout.FIRST_ROW_AND_GROUP_DISTANCE);
+ ++gridRow; // update gridRow
+ addXpubKeys(bsqWalletService, "BSQ", ++gridRow, -Layout.FLOATING_LABEL_DISTANCE);
+
+ addTitledGroupBg(root, ++gridRow, 4, Res.get("account.menu.walletInfo.path.headLine"), Layout.GROUP_DISTANCE);
+ addMultilineLabel(root, gridRow, Res.get("account.menu.walletInfo.path.info"), Layout.FIRST_ROW_AND_GROUP_DISTANCE, Double.MAX_VALUE);
+ addTopLabelTextField(root, ++gridRow, Res.get("account.menu.walletInfo.walletSelector", "BTC", "legacy"), "44'/0'/0'", -Layout.FLOATING_LABEL_DISTANCE);
+ addTopLabelTextField(root, ++gridRow, Res.get("account.menu.walletInfo.walletSelector", "BTC", "segwit"), "44'/0'/1'", -Layout.FLOATING_LABEL_DISTANCE);
+ addTopLabelTextField(root, ++gridRow, Res.get("account.menu.walletInfo.walletSelector", "BSQ", ""), "44'/142'/0'", -Layout.FLOATING_LABEL_DISTANCE);
+
+ openDetailsButton = addButtonAfterGroup(root, ++gridRow, Res.get("account.menu.walletInfo.openDetails"));
+
+ btcWalletBalanceListener = new BalanceListener() {
+ @Override
+ public void onBalanceChanged(Coin balanceAsCoin, Transaction tx) {
+ updateBalances(btcWalletService);
+ }
+ };
+ bsqWalletBalanceListener = new BalanceListener() {
+ @Override
+ public void onBalanceChanged(Coin balanceAsCoin, Transaction tx) {
+ updateBalances(bsqWalletService);
+ }
+ };
+ }
+
+
+ @Override
+ protected void activate() {
+ btcWalletService.addBalanceListener(btcWalletBalanceListener);
+ bsqWalletService.addBalanceListener(bsqWalletBalanceListener);
+ updateBalances(btcWalletService);
+ updateBalances(bsqWalletService);
+
+ openDetailsButton.setOnAction(e -> {
+ if (walletsManager.areWalletsAvailable()) {
+ new ShowWalletDataWindow(walletsManager).width(root.getWidth()).show();
+ } else {
+ new Popup().warning(Res.get("popup.warning.walletNotInitialized")).show();
+ }
+ });
+ }
+
+ @Override
+ protected void deactivate() {
+ btcWalletService.removeBalanceListener(btcWalletBalanceListener);
+ bsqWalletService.removeBalanceListener(bsqWalletBalanceListener);
+ openDetailsButton.setOnAction(null);
+ }
+
+ private void addXpubKeys(WalletService walletService, String currency, int gridRow, double top) {
+ int row = gridRow;
+ double topDist = top;
+ for (DeterministicKeyChain chain : walletService.getWallet().getActiveKeyChains()) {
+ Script.ScriptType outputScriptType = chain.getOutputScriptType();
+ String type = outputScriptType == Script.ScriptType.P2WPKH ? "segwit" : "legacy";
+ String key = chain.getWatchingKey().serializePubB58(Config.baseCurrencyNetworkParameters(), outputScriptType);
+ addTopLabelTextField(root, row,
+ Res.get("account.menu.walletInfo.walletSelector", currency, type),
+ key, topDist);
+ row++;
+ topDist = -Layout.FLOATING_LABEL_DISTANCE;
+ }
+ }
+
+ private void updateBalances(WalletService walletService) {
+ if (walletService instanceof BtcWalletService) {
+ btcTextField.setText(btcFormatter.formatCoinWithCode(walletService.getBalance(ESTIMATED_SPENDABLE)));
+ } else {
+ bsqTextField.setText(bsqFormatter.formatCoinWithCode(walletService.getBalance(ESTIMATED_SPENDABLE)));
+ }
+ }
+
+}
+
diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/ShowWalletDataWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/ShowWalletDataWindow.java
index 92039095069..f6f25715602 100644
--- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/ShowWalletDataWindow.java
+++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/ShowWalletDataWindow.java
@@ -54,7 +54,7 @@ public void show() {
if (headLine == null)
headLine = Res.get("showWalletDataWindow.walletData");
- width = 660;
+ width = 1000;
createGridPane();
addHeadLine();
addContent();