support funding tabs: receive, send, transactions

This commit is contained in:
woodser 2022-07-14 09:05:48 -04:00
parent c71c61d1bb
commit fb3745c6df
18 changed files with 432 additions and 723 deletions

View File

@ -171,7 +171,7 @@ class CoreWalletsService {
String getXmrNewSubaddress() { String getXmrNewSubaddress() {
accountService.checkAccountOpen(); accountService.checkAccountOpen();
return xmrWalletService.getWallet().createSubaddress(0).getAddress(); return xmrWalletService.getNewAddressEntry().getAddressString();
} }
List<MoneroTxWallet> getXmrTxs() { List<MoneroTxWallet> getXmrTxs() {

View File

@ -25,8 +25,8 @@ public class XmrBalanceListener {
public XmrBalanceListener() { public XmrBalanceListener() {
} }
public XmrBalanceListener(Integer accountIndex) { public XmrBalanceListener(Integer subaddressIndex) {
this.subaddressIndex = accountIndex; this.subaddressIndex = subaddressIndex;
} }
public Integer getSubaddressIndex() { public Integer getSubaddressIndex() {

View File

@ -55,6 +55,7 @@ import monero.wallet.model.MoneroCheckTx;
import monero.wallet.model.MoneroDestination; import monero.wallet.model.MoneroDestination;
import monero.wallet.model.MoneroOutputWallet; import monero.wallet.model.MoneroOutputWallet;
import monero.wallet.model.MoneroSubaddress; import monero.wallet.model.MoneroSubaddress;
import monero.wallet.model.MoneroTransferQuery;
import monero.wallet.model.MoneroTxConfig; import monero.wallet.model.MoneroTxConfig;
import monero.wallet.model.MoneroTxQuery; import monero.wallet.model.MoneroTxQuery;
import monero.wallet.model.MoneroTxWallet; import monero.wallet.model.MoneroTxWallet;
@ -70,6 +71,7 @@ public class XmrWalletService {
// Monero configuration // Monero configuration
// TODO: don't hard code configuration, inject into classes? // TODO: don't hard code configuration, inject into classes?
public static final int NUM_BLOCKS_UNLOCK = 10;
private static final MoneroNetworkType MONERO_NETWORK_TYPE = getMoneroNetworkType(); private static final MoneroNetworkType MONERO_NETWORK_TYPE = getMoneroNetworkType();
private static final MoneroWalletRpcManager MONERO_WALLET_RPC_MANAGER = new MoneroWalletRpcManager(); private static final MoneroWalletRpcManager MONERO_WALLET_RPC_MANAGER = new MoneroWalletRpcManager();
private static final String MONERO_WALLET_RPC_DIR = System.getProperty("user.dir") + File.separator + ".localnet"; // .localnet contains monero-wallet-rpc and wallet files private static final String MONERO_WALLET_RPC_DIR = System.getProperty("user.dir") + File.separator + ".localnet"; // .localnet contains monero-wallet-rpc and wallet files
@ -634,6 +636,7 @@ public class XmrWalletService {
// clear wallets // clear wallets
wallet = null; wallet = null;
multisigWallets.clear(); multisigWallets.clear();
walletListeners.clear();
} }
private void backupWallet(String walletName) { private void backupWallet(String walletName) {
@ -650,6 +653,16 @@ public class XmrWalletService {
// ----------------------------- LEGACY APP ------------------------------- // ----------------------------- LEGACY APP -------------------------------
public XmrAddressEntry getNewAddressEntry() {
return getOrCreateAddressEntry(XmrAddressEntry.Context.AVAILABLE, Optional.empty());
}
public XmrAddressEntry getFreshAddressEntry() {
List<XmrAddressEntry> unusedAddressEntries = getUnusedAddressEntries();
if (unusedAddressEntries.isEmpty()) return getNewAddressEntry();
else return unusedAddressEntries.get(0);
}
public XmrAddressEntry recoverAddressEntry(String offerId, String address, XmrAddressEntry.Context context) { public XmrAddressEntry recoverAddressEntry(String offerId, String address, XmrAddressEntry.Context context) {
var available = findAddressEntry(address, XmrAddressEntry.Context.AVAILABLE); var available = findAddressEntry(address, XmrAddressEntry.Context.AVAILABLE);
if (!available.isPresent()) return null; if (!available.isPresent()) return null;
@ -761,12 +774,21 @@ public class XmrWalletService {
return xmrAddressEntryList.getAddressEntriesAsListImmutable(); return xmrAddressEntryList.getAddressEntriesAsListImmutable();
} }
public boolean isSubaddressUnused(int subaddressIndex) { public List<XmrAddressEntry> getUnusedAddressEntries() {
return subaddressIndex != 0 && getBalanceForSubaddress(subaddressIndex).value == 0; return getAvailableAddressEntries().stream()
// return !wallet.getSubaddress(accountIndex, 0).isUsed(); // TODO: isUsed() .filter(e -> isSubaddressUnused(e.getSubaddressIndex()))
// does not include unconfirmed funds .collect(Collectors.toList());
} }
public boolean isSubaddressUnused(int subaddressIndex) {
return getNumTxOutputsForSubaddress(subaddressIndex) == 0;
}
public Coin getBalanceForAddress(String address) {
return getBalanceForSubaddress(wallet.getAddressIndex(address).getIndex());
}
// TODO: Coin represents centineros everywhere, but here it's atomic units. reconcile
public Coin getBalanceForSubaddress(int subaddressIndex) { public Coin getBalanceForSubaddress(int subaddressIndex) {
// get subaddress balance // get subaddress balance
@ -786,6 +808,24 @@ public class XmrWalletService {
return Coin.valueOf(balance.longValueExact()); return Coin.valueOf(balance.longValueExact());
} }
public int getNumTxOutputsForSubaddress(int subaddressIndex) {
// get txs with transfers to the subaddress
List<MoneroTxWallet> txs = wallet.getTxs(new MoneroTxQuery()
.setTransferQuery((new MoneroTransferQuery()
.setAccountIndex(0)
.setSubaddressIndex(subaddressIndex)
.setIsIncoming(true)))
.setIncludeOutputs(true));
// count num outputs
int numUnspentOutputs = 0;
for (MoneroTxWallet tx : txs) {
numUnspentOutputs += tx.isConfirmed() ? tx.getOutputs().size() : 1; // TODO: monero-project does not provide outputs for unconfirmed txs
}
return numUnspentOutputs;
}
public Coin getAvailableConfirmedBalance() { public Coin getAvailableConfirmedBalance() {
return wallet != null ? Coin.valueOf(wallet.getUnlockedBalance(0).longValueExact()) : Coin.ZERO; return wallet != null ? Coin.valueOf(wallet.getUnlockedBalance(0).longValueExact()) : Coin.ZERO;
} }

View File

@ -26,7 +26,6 @@ import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection; import bisq.core.offer.OfferDirection;
import bisq.core.payment.payload.PaymentMethod; import bisq.core.payment.payload.PaymentMethod;
import bisq.core.proto.CoreProtoResolver; import bisq.core.proto.CoreProtoResolver;
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
import bisq.core.support.dispute.mediation.MediationResultState; import bisq.core.support.dispute.mediation.MediationResultState;
import bisq.core.support.dispute.refund.RefundResultState; import bisq.core.support.dispute.refund.RefundResultState;
import bisq.core.support.messages.ChatMessage; import bisq.core.support.messages.ChatMessage;
@ -886,7 +885,7 @@ public abstract class Trade implements Tradable, Model {
// check if deposit txs unlocked // check if deposit txs unlocked
if (txs.get(0).isConfirmed() && txs.get(1).isConfirmed()) { if (txs.get(0).isConfirmed() && txs.get(1).isConfirmed()) {
long unlockHeight = Math.max(txs.get(0).getHeight(), txs.get(0).getHeight()) + 9; long unlockHeight = Math.max(txs.get(0).getHeight(), txs.get(0).getHeight()) + XmrWalletService.NUM_BLOCKS_UNLOCK - 1;
if (havenoWallet.getHeight() >= unlockHeight) { if (havenoWallet.getHeight() >= unlockHeight) {
setConfirmedState(); setConfirmedState();
return; return;
@ -926,7 +925,7 @@ public abstract class Trade implements Tradable, Model {
// compute unlock height // compute unlock height
if (unlockHeight == null && txs.size() == 2 && txs.get(0).isConfirmed() && txs.get(1).isConfirmed()) { if (unlockHeight == null && txs.size() == 2 && txs.get(0).isConfirmed() && txs.get(1).isConfirmed()) {
unlockHeight = Math.max(txs.get(0).getHeight(), txs.get(0).getHeight()) + 9; unlockHeight = Math.max(txs.get(0).getHeight(), txs.get(0).getHeight()) + XmrWalletService.NUM_BLOCKS_UNLOCK - 1;
} }
// check if txs unlocked // check if txs unlocked

View File

@ -25,6 +25,10 @@ public class ParsingUtils {
return centinerosToAtomicUnits(coin.value); return centinerosToAtomicUnits(coin.value);
} }
public static double coinToXmr(Coin coin) {
return atomicUnitsToXmr(coinToAtomicUnits(coin));
}
public static BigInteger centinerosToAtomicUnits(long centineros) { public static BigInteger centinerosToAtomicUnits(long centineros) {
return BigInteger.valueOf(centineros).multiply(ParsingUtils.CENTINEROS_AU_MULTIPLIER); return BigInteger.valueOf(centineros).multiply(ParsingUtils.CENTINEROS_AU_MULTIPLIER);
} }
@ -41,6 +45,10 @@ public class ParsingUtils {
return atomicUnits.divide(CENTINEROS_AU_MULTIPLIER).longValueExact(); return atomicUnits.divide(CENTINEROS_AU_MULTIPLIER).longValueExact();
} }
public static Coin atomicUnitsToCoin(BigInteger atomicUnits) {
return Coin.valueOf(atomicUnitsToCentineros(atomicUnits));
}
public static double atomicUnitsToXmr(BigInteger atomicUnits) { public static double atomicUnitsToXmr(BigInteger atomicUnits) {
return new BigDecimal(atomicUnits).divide(new BigDecimal(XMR_AU_MULTIPLIER)).doubleValue(); return new BigDecimal(atomicUnits).divide(new BigDecimal(XMR_AU_MULTIPLIER)).doubleValue();
} }

View File

@ -127,7 +127,7 @@ shared.noDateAvailable=No date available
shared.noDetailsAvailable=No details available shared.noDetailsAvailable=No details available
shared.notUsedYet=Not used yet shared.notUsedYet=Not used yet
shared.date=Date shared.date=Date
shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount? shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3}\n\nThe recipient will receive: {4}\n\nAre you sure you want to withdraw this amount?
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
shared.sendFundsDetailsDust=Haveno detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Monero consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n shared.sendFundsDetailsDust=Haveno detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Monero consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n
shared.copyToClipboard=Copy to clipboard shared.copyToClipboard=Copy to clipboard

View File

@ -20,19 +20,17 @@ package bisq.desktop.components;
import bisq.desktop.components.indicator.TxConfidenceIndicator; import bisq.desktop.components.indicator.TxConfidenceIndicator;
import bisq.desktop.util.GUIUtil; import bisq.desktop.util.GUIUtil;
import bisq.core.btc.listeners.TxConfidenceListener; import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.user.BlockChainExplorer; import bisq.core.user.BlockChainExplorer;
import bisq.core.user.Preferences; import bisq.core.user.Preferences;
import bisq.common.util.Utilities; import bisq.common.util.Utilities;
import org.bitcoinj.core.TransactionConfidence;
import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.fontawesome.AwesomeIcon;
import java.math.BigInteger;
import com.jfoenix.controls.JFXTextField; import com.jfoenix.controls.JFXTextField;
import javafx.scene.control.Label; import javafx.scene.control.Label;
@ -42,21 +40,23 @@ import javafx.scene.layout.AnchorPane;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import monero.wallet.model.MoneroTxWallet;
import monero.wallet.model.MoneroWalletListener;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class TxIdTextField extends AnchorPane { public class TxIdTextField extends AnchorPane {
@Setter @Setter
private static Preferences preferences; private static Preferences preferences;
@Setter @Setter
private static BtcWalletService walletService; private static XmrWalletService xmrWalletService;
@Getter @Getter
private final TextField textField; private final TextField textField;
private final Tooltip progressIndicatorTooltip; private final Tooltip progressIndicatorTooltip;
private final TxConfidenceIndicator txConfidenceIndicator; private final TxConfidenceIndicator txConfidenceIndicator;
private final Label copyIcon, blockExplorerIcon, missingTxWarningIcon; private final Label copyIcon, blockExplorerIcon, missingTxWarningIcon;
private TxConfidenceListener txConfidenceListener;
private MoneroWalletListener txUpdater;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
@ -113,8 +113,10 @@ public class TxIdTextField extends AnchorPane {
} }
public void setup(@Nullable String txId) { public void setup(@Nullable String txId) {
if (txConfidenceListener != null) if (txUpdater != null) {
walletService.removeTxConfidenceListener(txConfidenceListener); xmrWalletService.removeWalletListener(txUpdater);
txUpdater = null;
}
if (txId == null) { if (txId == null) {
textField.setText(Res.get("shared.na")); textField.setText(Res.get("shared.na"));
@ -129,14 +131,21 @@ public class TxIdTextField extends AnchorPane {
return; return;
} }
txConfidenceListener = new TxConfidenceListener(txId) { // listen for tx updates
// TODO: this only listens for new blocks, listen for double spend
txUpdater = new MoneroWalletListener() {
@Override @Override
public void onTransactionConfidenceChanged(TransactionConfidence confidence) { public void onNewBlock(long height) {
updateConfidence(confidence); updateConfidence(txId);
}
@Override
public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) {
updateConfidence(txId);
} }
}; };
walletService.addTxConfidenceListener(txConfidenceListener); xmrWalletService.addWalletListener(txUpdater);
updateConfidence(walletService.getConfidenceForTxId(txId));
updateConfidence(txId);
textField.setText(txId); textField.setText(txId);
textField.setOnMouseClicked(mouseEvent -> openBlockExplorer(txId)); textField.setOnMouseClicked(mouseEvent -> openBlockExplorer(txId));
@ -145,9 +154,10 @@ public class TxIdTextField extends AnchorPane {
} }
public void cleanup() { public void cleanup() {
if (walletService != null && txConfidenceListener != null) if (xmrWalletService != null && txUpdater != null) {
walletService.removeTxConfidenceListener(txConfidenceListener); xmrWalletService.removeWalletListener(txUpdater);
txUpdater = null;
}
textField.setOnMouseClicked(null); textField.setOnMouseClicked(null);
blockExplorerIcon.setOnMouseClicked(null); blockExplorerIcon.setOnMouseClicked(null);
copyIcon.setOnMouseClicked(null); copyIcon.setOnMouseClicked(null);
@ -165,9 +175,15 @@ public class TxIdTextField extends AnchorPane {
} }
} }
private void updateConfidence(TransactionConfidence confidence) { private void updateConfidence(String txId) {
GUIUtil.updateConfidence(confidence, progressIndicatorTooltip, txConfidenceIndicator); MoneroTxWallet tx = null;
if (confidence != null) { try {
tx = xmrWalletService.getWallet().getTx(txId);
} catch (Exception e) {
// do nothing
}
GUIUtil.updateConfidence(tx, progressIndicatorTooltip, txConfidenceIndicator);
if (tx != null) {
if (txConfidenceIndicator.getProgress() != 0) { if (txConfidenceIndicator.getProgress() != 0) {
txConfidenceIndicator.setVisible(true); txConfidenceIndicator.setVisible(true);
AnchorPane.setRightAnchor(txConfidenceIndicator, 0.0); AnchorPane.setRightAnchor(txConfidenceIndicator, 0.0);

View File

@ -200,8 +200,8 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
sellButton.fire(); sellButton.fire();
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT4, keyEvent)) { } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT4, keyEvent)) {
portfolioButton.fire(); portfolioButton.fire();
// } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT5, keyEvent)) { } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT5, keyEvent)) {
// fundsButton.fire(); fundsButton.fire();
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT6, keyEvent)) { } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT6, keyEvent)) {
supportButton.fire(); supportButton.fire();
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT7, keyEvent)) { } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT7, keyEvent)) {
@ -304,7 +304,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
}); });
HBox primaryNav = new HBox(marketButton, getNavigationSeparator(), buyButton, getNavigationSeparator(), HBox primaryNav = new HBox(marketButton, getNavigationSeparator(), buyButton, getNavigationSeparator(),
sellButton, getNavigationSeparator(), portfolioButtonWithBadge, getNavigationSeparator()); sellButton, getNavigationSeparator(), portfolioButtonWithBadge, getNavigationSeparator(), fundsButton);
primaryNav.setAlignment(Pos.CENTER_LEFT); primaryNav.setAlignment(Pos.CENTER_LEFT);
primaryNav.getStyleClass().add("nav-primary"); primaryNav.getStyleClass().add("nav-primary");

View File

@ -43,7 +43,7 @@ import bisq.core.alert.PrivateNotificationManager;
import bisq.core.api.CoreMoneroConnectionsService; import bisq.core.api.CoreMoneroConnectionsService;
import bisq.core.app.HavenoSetup; import bisq.core.app.HavenoSetup;
import bisq.core.btc.nodes.LocalBitcoinNode; import bisq.core.btc.nodes.LocalBitcoinNode;
import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.CryptoCurrency; import bisq.core.locale.CryptoCurrency;
import bisq.core.locale.CurrencyUtil; import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res; import bisq.core.locale.Res;
@ -153,7 +153,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
@Inject @Inject
public MainViewModel(HavenoSetup bisqSetup, public MainViewModel(HavenoSetup bisqSetup,
CoreMoneroConnectionsService connectionService, CoreMoneroConnectionsService connectionService,
BtcWalletService btcWalletService, XmrWalletService xmrWalletService,
User user, User user,
BalancePresentation balancePresentation, BalancePresentation balancePresentation,
TradePresentation tradePresentation, TradePresentation tradePresentation,
@ -202,7 +202,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
TxIdTextField.setPreferences(preferences); TxIdTextField.setPreferences(preferences);
TxIdTextField.setWalletService(btcWalletService); TxIdTextField.setXmrWalletService(xmrWalletService);
GUIUtil.setFeeService(feeService); GUIUtil.setFeeService(feeService);
GUIUtil.setPreferences(preferences); GUIUtil.setPreferences(preferences);

View File

@ -17,117 +17,60 @@
package bisq.desktop.main.funds.deposit; package bisq.desktop.main.funds.deposit;
import bisq.desktop.components.indicator.TxConfidenceIndicator; import bisq.core.btc.listeners.XmrBalanceListener;
import bisq.desktop.util.GUIUtil; import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.btc.listeners.BalanceListener;
import bisq.core.btc.listeners.TxConfidenceListener;
import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.util.ParsingUtils;
import bisq.core.util.coin.CoinFormatter; import bisq.core.util.coin.CoinFormatter;
import java.math.BigInteger;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
import com.google.common.base.Suppliers;
import javafx.scene.control.Tooltip;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import java.util.function.Supplier;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Coin;
@Slf4j @Slf4j
class DepositListItem { class DepositListItem {
private final StringProperty balance = new SimpleStringProperty(); private final StringProperty balance = new SimpleStringProperty();
private final BtcWalletService walletService; private final XmrAddressEntry addressEntry;
private final XmrWalletService xmrWalletService;
private Coin balanceAsCoin; private Coin balanceAsCoin;
private final String addressString;
private String usage = "-"; private String usage = "-";
private TxConfidenceListener txConfidenceListener; private XmrBalanceListener balanceListener;
private BalanceListener balanceListener;
private int numTxOutputs = 0; private int numTxOutputs = 0;
private final Supplier<LazyFields> lazyFieldsSupplier;
private static class LazyFields { DepositListItem(XmrAddressEntry addressEntry, XmrWalletService xmrWalletService, CoinFormatter formatter) {
TxConfidenceIndicator txConfidenceIndicator; this.xmrWalletService = xmrWalletService;
Tooltip tooltip; this.addressEntry = addressEntry;
}
private LazyFields lazy() { balanceListener = new XmrBalanceListener(addressEntry.getSubaddressIndex()) {
return lazyFieldsSupplier.get();
}
DepositListItem(AddressEntry addressEntry, BtcWalletService walletService, CoinFormatter formatter) {
this.walletService = walletService;
addressString = addressEntry.getAddressString();
Address address = addressEntry.getAddress();
TransactionConfidence confidence = walletService.getConfidenceForAddress(address);
// confidence
lazyFieldsSupplier = Suppliers.memoize(() -> new LazyFields() {{
txConfidenceIndicator = new TxConfidenceIndicator();
txConfidenceIndicator.setId("funds-confidence");
tooltip = new Tooltip(Res.get("shared.notUsedYet"));
txConfidenceIndicator.setProgress(0);
txConfidenceIndicator.setTooltip(tooltip);
if (confidence != null) {
GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
}
}});
if (confidence != null) {
txConfidenceListener = new TxConfidenceListener(confidence.getTransactionHash().toString()) {
@Override @Override
public void onTransactionConfidenceChanged(TransactionConfidence confidence) { public void onBalanceChanged(BigInteger balance) {
GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator); DepositListItem.this.balanceAsCoin = ParsingUtils.atomicUnitsToCoin(balance);
}
};
walletService.addTxConfidenceListener(txConfidenceListener);
}
balanceListener = new BalanceListener(address) {
@Override
public void onBalanceChanged(Coin balanceAsCoin, Transaction tx) {
DepositListItem.this.balanceAsCoin = balanceAsCoin;
DepositListItem.this.balance.set(formatter.formatCoin(balanceAsCoin)); DepositListItem.this.balance.set(formatter.formatCoin(balanceAsCoin));
var confidence = walletService.getConfidenceForTxId(tx.getTxId().toString()); updateUsage(addressEntry.getSubaddressIndex());
GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator);
updateUsage(address);
} }
}; };
walletService.addBalanceListener(balanceListener); xmrWalletService.addBalanceListener(balanceListener);
balanceAsCoin = walletService.getBalanceForAddress(address); balanceAsCoin = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex()); // TODO: Coin represents centineros everywhere, but here it's atomic units. reconcile
balanceAsCoin = Coin.valueOf(ParsingUtils.atomicUnitsToCentineros(balanceAsCoin.longValue())); // in centineros
balance.set(formatter.formatCoin(balanceAsCoin)); balance.set(formatter.formatCoin(balanceAsCoin));
updateUsage(address); updateUsage(addressEntry.getSubaddressIndex());
} }
private void updateUsage(Address address) { private void updateUsage(int subaddressIndex) {
numTxOutputs = walletService.getNumTxOutputsForAddress(address); numTxOutputs = xmrWalletService.getNumTxOutputsForSubaddress(addressEntry.getSubaddressIndex());
usage = numTxOutputs == 0 ? Res.get("funds.deposit.unused") : Res.get("funds.deposit.usedInTx", numTxOutputs); usage = numTxOutputs == 0 ? Res.get("funds.deposit.unused") : Res.get("funds.deposit.usedInTx", numTxOutputs);
} }
public void cleanup() { public void cleanup() {
walletService.removeTxConfidenceListener(txConfidenceListener); xmrWalletService.removeBalanceListener(balanceListener);
walletService.removeBalanceListener(balanceListener);
}
public TxConfidenceIndicator getTxConfidenceIndicator() {
return lazy().txConfidenceIndicator;
} }
public String getAddressString() { public String getAddressString() {
return addressString; return addressEntry.getAddressString();
} }
public String getUsage() { public String getUsage() {
@ -149,4 +92,8 @@ class DepositListItem {
public int getNumTxOutputs() { public int getNumTxOutputs() {
return numTxOutputs; return numTxOutputs;
} }
public int getNumConfirmationsSinceFirstUsed() {
throw new RuntimeException("Not implemented");
}
} }

View File

@ -30,9 +30,9 @@ import bisq.desktop.main.overlays.windows.QRCodeWindow;
import bisq.desktop.util.GUIUtil; import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.Layout; import bisq.desktop.util.Layout;
import bisq.core.btc.listeners.BalanceListener; import bisq.core.btc.listeners.XmrBalanceListener;
import bisq.core.btc.model.AddressEntry; import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.user.Preferences; import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils; import bisq.core.util.FormattingUtils;
@ -41,21 +41,16 @@ import bisq.core.util.coin.CoinFormatter;
import bisq.common.UserThread; import bisq.common.UserThread;
import bisq.common.app.DevEnv; import bisq.common.app.DevEnv;
import bisq.common.config.Config;
import bisq.common.util.Tuple3; import bisq.common.util.Tuple3;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.SegwitAddress;
import org.bitcoinj.core.Transaction;
import net.glxn.qrgen.QRCode; import net.glxn.qrgen.QRCode;
import net.glxn.qrgen.image.ImageType; import net.glxn.qrgen.image.ImageType;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import monero.wallet.model.MoneroTxConfig;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Button; import javafx.scene.control.Button;
@ -85,7 +80,7 @@ import javafx.collections.transformation.SortedList;
import javafx.util.Callback; import javafx.util.Callback;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.util.Comparator; import java.util.Comparator;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -105,17 +100,16 @@ public class DepositView extends ActivatableView<VBox, Void> {
private ImageView qrCodeImageView; private ImageView qrCodeImageView;
private AddressTextField addressTextField; private AddressTextField addressTextField;
private Button generateNewAddressButton; private Button generateNewAddressButton;
private CheckBox generateNewAddressSegwitCheckbox;
private TitledGroupBg titledGroupBg; private TitledGroupBg titledGroupBg;
private InputTextField amountTextField; private InputTextField amountTextField;
private final BtcWalletService walletService; private final XmrWalletService xmrWalletService;
private final Preferences preferences; private final Preferences preferences;
private final CoinFormatter formatter; private final CoinFormatter formatter;
private String paymentLabelString; private String paymentLabelString;
private final ObservableList<DepositListItem> observableList = FXCollections.observableArrayList(); private final ObservableList<DepositListItem> observableList = FXCollections.observableArrayList();
private final SortedList<DepositListItem> sortedList = new SortedList<>(observableList); private final SortedList<DepositListItem> sortedList = new SortedList<>(observableList);
private BalanceListener balanceListener; private XmrBalanceListener balanceListener;
private Subscription amountTextFieldSubscription; private Subscription amountTextFieldSubscription;
private ChangeListener<DepositListItem> tableViewSelectionListener; private ChangeListener<DepositListItem> tableViewSelectionListener;
private int gridRow = 0; private int gridRow = 0;
@ -125,10 +119,10 @@ public class DepositView extends ActivatableView<VBox, Void> {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
private DepositView(BtcWalletService walletService, private DepositView(XmrWalletService xmrWalletService,
Preferences preferences, Preferences preferences,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter) { @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter) {
this.walletService = walletService; this.xmrWalletService = xmrWalletService;
this.preferences = preferences; this.preferences = preferences;
this.formatter = formatter; this.formatter = formatter;
} }
@ -143,7 +137,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
usageColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.usage"))); usageColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.usage")));
// trigger creation of at least 1 savings address // trigger creation of at least 1 savings address
walletService.getFreshAddressEntry(); xmrWalletService.getFreshAddressEntry();
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.deposit.noAddresses"))); tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.deposit.noAddresses")));
@ -161,7 +155,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
addressColumn.setComparator(Comparator.comparing(DepositListItem::getAddressString)); addressColumn.setComparator(Comparator.comparing(DepositListItem::getAddressString));
balanceColumn.setComparator(Comparator.comparing(DepositListItem::getBalanceAsCoin)); balanceColumn.setComparator(Comparator.comparing(DepositListItem::getBalanceAsCoin));
confirmationsColumn.setComparator(Comparator.comparingDouble(o -> o.getTxConfidenceIndicator().getProgress())); confirmationsColumn.setComparator(Comparator.comparingInt(o -> o.getNumConfirmationsSinceFirstUsed()));
usageColumn.setComparator(Comparator.comparingInt(DepositListItem::getNumTxOutputs)); usageColumn.setComparator(Comparator.comparingInt(DepositListItem::getNumTxOutputs));
tableView.getSortOrder().add(usageColumn); tableView.getSortOrder().add(usageColumn);
tableView.setItems(sortedList); tableView.setItems(sortedList);
@ -174,7 +168,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow"))); Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow")));
qrCodeImageView.setOnMouseClicked(e -> GUIUtil.showFeeInfoBeforeExecute( qrCodeImageView.setOnMouseClicked(e -> GUIUtil.showFeeInfoBeforeExecute(
() -> UserThread.runAfter( () -> UserThread.runAfter(
() -> new QRCodeWindow(getBitcoinURI()).show(), () -> new QRCodeWindow(getPaymentUri()).show(),
200, TimeUnit.MILLISECONDS))); 200, TimeUnit.MILLISECONDS)));
GridPane.setRowIndex(qrCodeImageView, gridRow); GridPane.setRowIndex(qrCodeImageView, gridRow);
GridPane.setRowSpan(qrCodeImageView, 4); GridPane.setRowSpan(qrCodeImageView, 4);
@ -201,23 +195,17 @@ public class DepositView extends ActivatableView<VBox, Void> {
Tuple3<Button, CheckBox, HBox> buttonCheckBoxHBox = addButtonCheckBoxWithBox(gridPane, ++gridRow, Tuple3<Button, CheckBox, HBox> buttonCheckBoxHBox = addButtonCheckBoxWithBox(gridPane, ++gridRow,
Res.get("funds.deposit.generateAddress"), Res.get("funds.deposit.generateAddress"),
Res.get("funds.deposit.generateAddressSegwit"), null,
15); 15);
buttonCheckBoxHBox.third.setSpacing(25); buttonCheckBoxHBox.third.setSpacing(25);
generateNewAddressButton = buttonCheckBoxHBox.first; generateNewAddressButton = buttonCheckBoxHBox.first;
generateNewAddressSegwitCheckbox = buttonCheckBoxHBox.second;
generateNewAddressSegwitCheckbox.setAllowIndeterminate(false);
generateNewAddressSegwitCheckbox.setSelected(true);
generateNewAddressButton.setOnAction(event -> { generateNewAddressButton.setOnAction(event -> {
boolean segwit = generateNewAddressSegwitCheckbox.isSelected(); boolean hasUnUsedAddress = observableList.stream().anyMatch(e -> e.getNumTxOutputs() == 0);
NetworkParameters params = Config.baseCurrencyNetworkParameters();
boolean hasUnUsedAddress = observableList.stream().anyMatch(e -> e.getNumTxOutputs() == 0
&& (Address.fromString(params, e.getAddressString()) instanceof SegwitAddress) == segwit);
if (hasUnUsedAddress) { if (hasUnUsedAddress) {
new Popup().warning(Res.get("funds.deposit.selectUnused")).show(); new Popup().warning(Res.get("funds.deposit.selectUnused")).show();
} else { } else {
AddressEntry newSavingsAddressEntry = walletService.getFreshAddressEntry(segwit); XmrAddressEntry newSavingsAddressEntry = xmrWalletService.getNewAddressEntry();
updateList(); updateList();
observableList.stream() observableList.stream()
.filter(depositListItem -> depositListItem.getAddressString().equals(newSavingsAddressEntry.getAddressString())) .filter(depositListItem -> depositListItem.getAddressString().equals(newSavingsAddressEntry.getAddressString()))
@ -226,9 +214,9 @@ public class DepositView extends ActivatableView<VBox, Void> {
} }
}); });
balanceListener = new BalanceListener() { balanceListener = new XmrBalanceListener() {
@Override @Override
public void onBalanceChanged(Coin balance, Transaction tx) { public void onBalanceChanged(BigInteger balance) {
updateList(); updateList();
} }
}; };
@ -243,7 +231,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
updateList(); updateList();
walletService.addBalanceListener(balanceListener); xmrWalletService.addBalanceListener(balanceListener);
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> { amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
addressTextField.setAmountAsCoin(ParsingUtils.parseToCoin(t, formatter)); addressTextField.setAmountAsCoin(ParsingUtils.parseToCoin(t, formatter));
updateQRCode(); updateQRCode();
@ -258,7 +246,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
tableView.getSelectionModel().selectedItemProperty().removeListener(tableViewSelectionListener); tableView.getSelectionModel().selectedItemProperty().removeListener(tableViewSelectionListener);
sortedList.comparatorProperty().unbind(); sortedList.comparatorProperty().unbind();
observableList.forEach(DepositListItem::cleanup); observableList.forEach(DepositListItem::cleanup);
walletService.removeBalanceListener(balanceListener); xmrWalletService.removeBalanceListener(balanceListener);
amountTextFieldSubscription.unsubscribe(); amountTextFieldSubscription.unsubscribe();
} }
@ -267,7 +255,6 @@ public class DepositView extends ActivatableView<VBox, Void> {
// UI handlers // UI handlers
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void fillForm(String address) { private void fillForm(String address) {
titledGroupBg.setVisible(true); titledGroupBg.setVisible(true);
titledGroupBg.setManaged(true); titledGroupBg.setManaged(true);
@ -287,7 +274,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
private void updateQRCode() { private void updateQRCode() {
if (addressTextField.getAddress() != null && !addressTextField.getAddress().isEmpty()) { if (addressTextField.getAddress() != null && !addressTextField.getAddress().isEmpty()) {
final byte[] imageBytes = QRCode final byte[] imageBytes = QRCode
.from(getBitcoinURI()) .from(getPaymentUri())
.withSize(150, 150) // code has 41 elements 8 px is border with 150 we get 3x scale and min. border .withSize(150, 150) // code has 41 elements 8 px is border with 150 we get 3x scale and min. border
.to(ImageType.PNG) .to(ImageType.PNG)
.stream() .stream()
@ -309,8 +296,8 @@ public class DepositView extends ActivatableView<VBox, Void> {
private void updateList() { private void updateList() {
observableList.forEach(DepositListItem::cleanup); observableList.forEach(DepositListItem::cleanup);
observableList.clear(); observableList.clear();
walletService.getAvailableAddressEntries() xmrWalletService.getAvailableAddressEntries()
.forEach(e -> observableList.add(new DepositListItem(e, walletService, formatter))); .forEach(e -> observableList.add(new DepositListItem(e, xmrWalletService, formatter)));
} }
private Coin getAmountAsCoin() { private Coin getAmountAsCoin() {
@ -318,10 +305,11 @@ public class DepositView extends ActivatableView<VBox, Void> {
} }
@NotNull @NotNull
private String getBitcoinURI() { private String getPaymentUri() {
return GUIUtil.getBitcoinURI(addressTextField.getAddress(), return xmrWalletService.getWallet().createPaymentUri(new MoneroTxConfig()
getAmountAsCoin(), .setAddress(addressTextField.getAddress())
paymentLabelString); .setAmount(ParsingUtils.coinToAtomicUnits(getAmountAsCoin()))
.setNote(paymentLabelString));
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -434,7 +422,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
super.updateItem(item, empty); super.updateItem(item, empty);
if (item != null && !empty) { if (item != null && !empty) {
setGraphic(item.getTxConfidenceIndicator()); //setGraphic(item.getTxConfidenceIndicator());
} else { } else {
setGraphic(null); setGraphic(null);
} }

View File

@ -17,34 +17,34 @@
package bisq.desktop.main.funds.transactions; package bisq.desktop.main.funds.transactions;
import bisq.desktop.components.indicator.TxConfidenceIndicator;
import bisq.core.btc.listeners.TxConfidenceListener;
import bisq.core.btc.wallet.XmrWalletService; import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.Res;
import bisq.core.offer.Offer;
import bisq.core.offer.OpenOffer;
import bisq.core.trade.Tradable; import bisq.core.trade.Tradable;
import bisq.core.trade.Trade;
import bisq.core.util.ParsingUtils;
import bisq.core.util.coin.CoinFormatter; import bisq.core.util.coin.CoinFormatter;
import bisq.desktop.components.indicator.TxConfidenceIndicator;
import org.bitcoinj.core.Coin; import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.GUIUtil;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import java.math.BigInteger;
import javafx.scene.control.Tooltip;
import java.util.Date; import java.util.Date;
import java.util.function.Supplier; import java.util.Optional;
import javafx.scene.control.Tooltip;
import javax.annotation.Nullable;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import monero.wallet.model.MoneroIncomingTransfer;
import javax.annotation.Nullable; import monero.wallet.model.MoneroOutgoingTransfer;
import monero.wallet.model.MoneroTxWallet; import monero.wallet.model.MoneroTxWallet;
import monero.wallet.model.MoneroWalletListener;
import org.bitcoinj.core.Coin;
@Slf4j @Slf4j
class TransactionsListItem { class TransactionsListItem {
private final XmrWalletService xmrWalletService;
private final CoinFormatter formatter; private final CoinFormatter formatter;
private String dateString; private String dateString;
private final Date date; private final Date date;
@ -54,12 +54,11 @@ class TransactionsListItem {
private String details = ""; private String details = "";
private String addressString = ""; private String addressString = "";
private String direction = ""; private String direction = "";
private TxConfidenceListener txConfidenceListener;
private boolean received; private boolean received;
private boolean detailsAvailable; private boolean detailsAvailable;
private Coin amountAsCoin = Coin.ZERO; private Coin amountAsCoin = Coin.ZERO;
private String memo = ""; private String memo = "";
private int confirmations = 0; private long confirmations = 0;
@Getter @Getter
private final boolean isDustAttackTx; private final boolean isDustAttackTx;
private boolean initialTxConfidenceVisibility = true; private boolean initialTxConfidenceVisibility = true;
@ -77,187 +76,140 @@ class TransactionsListItem {
// used at exportCSV // used at exportCSV
TransactionsListItem() { TransactionsListItem() {
date = null; date = null;
xmrWalletService = null;
txId = null; txId = null;
formatter = null; formatter = null;
isDustAttackTx = false; isDustAttackTx = false;
lazyFieldsSupplier = null; lazyFieldsSupplier = null;
} }
TransactionsListItem(MoneroTxWallet transaction, TransactionsListItem(MoneroTxWallet tx,
XmrWalletService xmrWalletService, XmrWalletService xmrWalletService,
TransactionAwareTradable transactionAwareTradable, TransactionAwareTradable transactionAwareTradable,
CoinFormatter formatter, CoinFormatter formatter,
long ignoreDustThreshold) { long ignoreDustThreshold) {
throw new RuntimeException("TransactionsListItem needs updated to use XMR wallet"); this.formatter = formatter;
// this.btcWalletService = btcWalletService; this.memo = tx.getNote();
// this.formatter = formatter; this.txId = tx.getHash();
// this.memo = transaction.getMemo();
// Optional<Tradable> optionalTradable = Optional.ofNullable(transactionAwareTradable)
// txId = transaction.getTxId().toString(); .map(TransactionAwareTradable::asTradable);
//
// Optional<Tradable> optionalTradable = Optional.ofNullable(transactionAwareTradable) Coin valueSentToMe = ParsingUtils.atomicUnitsToCoin(tx.getIncomingAmount() == null ? new BigInteger("0") : tx.getIncomingAmount());
// .map(TransactionAwareTradable::asTradable); Coin valueSentFromMe = ParsingUtils.atomicUnitsToCoin(tx.getOutgoingAmount() == null ? new BigInteger("0") : tx.getOutgoingAmount());
//
// Coin valueSentToMe = btcWalletService.getValueSentToMeForTransaction(transaction); if (tx.getTransfers().get(0).isIncoming()) {
// Coin valueSentFromMe = btcWalletService.getValueSentFromMeForTransaction(transaction); addressString = ((MoneroIncomingTransfer) tx.getTransfers().get(0)).getAddress();
// } else {
// // TODO check and refactor MoneroOutgoingTransfer transfer = (MoneroOutgoingTransfer) tx.getTransfers().get(0);
// if (valueSentToMe.isZero()) { if (transfer.getDestinations() != null) addressString = transfer.getDestinations().get(0).getAddress();
// amountAsCoin = valueSentFromMe.multiply(-1); else addressString = "unavailable";
// for (TransactionOutput output : transaction.getOutputs()) { }
// if (!btcWalletService.isTransactionOutputMine(output)) {
// received = false; if (valueSentFromMe.isZero()) {
// if (WalletService.isOutputScriptConvertibleToAddress(output)) { amountAsCoin = valueSentToMe;
// addressString = WalletService.getAddressStringFromOutput(output); direction = Res.get("funds.tx.direction.receivedWith");
// direction = Res.get("funds.tx.direction.sentTo"); received = true;
// break; } else {
// } amountAsCoin = valueSentFromMe.multiply(-1);
// } received = false;
// } direction = Res.get("funds.tx.direction.sentTo");
// } else if (valueSentFromMe.isZero()) { }
// amountAsCoin = valueSentToMe;
// direction = Res.get("funds.tx.direction.receivedWith"); if (optionalTradable.isPresent()) {
// received = true; tradable = optionalTradable.get();
// for (TransactionOutput output : transaction.getOutputs()) { detailsAvailable = true;
// if (btcWalletService.isTransactionOutputMine(output) && String tradeId = tradable.getShortId();
// WalletService.isOutputScriptConvertibleToAddress(output)) { if (tradable instanceof OpenOffer) {
// addressString = WalletService.getAddressStringFromOutput(output); details = Res.get("funds.tx.createOfferFee", tradeId);
// break; } else if (tradable instanceof Trade) {
// } Trade trade = (Trade) tradable;
// } if (trade.getTakerFeeTxId() != null && trade.getTakerFeeTxId().equals(txId)) {
// } else { details = Res.get("funds.tx.takeOfferFee", tradeId);
// amountAsCoin = valueSentToMe.subtract(valueSentFromMe); } else {
// boolean outgoing = false; Offer offer = trade.getOffer();
// for (TransactionOutput output : transaction.getOutputs()) { String offerFeePaymentTxID = offer.getOfferFeePaymentTxId();
// if (!btcWalletService.isTransactionOutputMine(output)) { if (offerFeePaymentTxID != null && offerFeePaymentTxID.equals(txId)) {
// if (WalletService.isOutputScriptConvertibleToAddress(output)) { details = Res.get("funds.tx.createOfferFee", tradeId);
// addressString = WalletService.getAddressStringFromOutput(output); } else if (trade.getSelf().getDepositTxHash() != null &&
// outgoing = true; trade.getSelf().getDepositTxHash().equals(txId)) {
// break; details = Res.get("funds.tx.multiSigDeposit", tradeId);
// } } else if (trade.getPayoutTxId() != null &&
// } else { trade.getPayoutTxId().equals(txId)) {
// addressString = WalletService.getAddressStringFromOutput(output); details = Res.get("funds.tx.multiSigPayout", tradeId);
// outgoing = (valueSentToMe.getValue() < valueSentFromMe.getValue()); if (amountAsCoin.isZero()) {
// if (!outgoing) { initialTxConfidenceVisibility = false;
// direction = Res.get("funds.tx.direction.receivedWith"); }
// received = true; } else {
// } Trade.DisputeState disputeState = trade.getDisputeState();
// } if (disputeState == Trade.DisputeState.DISPUTE_CLOSED) {
// } if (valueSentToMe.isPositive()) {
// details = Res.get("funds.tx.disputePayout", tradeId);
// if (outgoing) { } else {
// direction = Res.get("funds.tx.direction.sentTo"); details = Res.get("funds.tx.disputeLost", tradeId);
// received = false; }
// } } else if (disputeState == Trade.DisputeState.REFUND_REQUEST_CLOSED ||
// } disputeState == Trade.DisputeState.REFUND_REQUESTED ||
// disputeState == Trade.DisputeState.REFUND_REQUEST_STARTED_BY_PEER) {
// if (valueSentToMe.isPositive()) {
// if (optionalTradable.isPresent()) { details = Res.get("funds.tx.refund", tradeId);
// tradable = optionalTradable.get(); } else {
// detailsAvailable = true; // We have spent the deposit tx outputs to the Bisq donation address to enable
// String tradeId = tradable.getShortId(); // the refund process (refund agent -> reimbursement). As the funds have left our wallet
// if (tradable instanceof OpenOffer) { // already when funding the deposit tx we show 0 BTC as amount.
// details = Res.get("funds.tx.createOfferFee", tradeId); // Confirmation is not known from the BitcoinJ side (not 100% clear why) as no funds
// } else if (tradable instanceof Trade) { // left our wallet nor we received funds. So we set indicator invisible.
// Trade trade = (Trade) tradable; amountAsCoin = Coin.ZERO;
// TransactionAwareTrade transactionAwareTrade = (TransactionAwareTrade) transactionAwareTradable; details = Res.get("funds.tx.collateralForRefund", tradeId);
// if (trade.getTakerFeeTxId() != null && trade.getTakerFeeTxId().equals(txId)) { initialTxConfidenceVisibility = false;
// details = Res.get("funds.tx.takeOfferFee", tradeId); }
// } else { } else {
// Offer offer = trade.getOffer(); details = Res.get("funds.tx.unknown", tradeId);
// String offerFeePaymentTxID = offer.getOfferFeePaymentTxId(); }
// if (offerFeePaymentTxID != null && offerFeePaymentTxID.equals(txId)) { }
// details = Res.get("funds.tx.createOfferFee", tradeId); }
// } else if (trade.getDepositTx() != null && }
// trade.getDepositTx().getTxId().equals(Sha256Hash.wrap(txId))) { } else {
// details = Res.get("funds.tx.multiSigDeposit", tradeId); if (amountAsCoin.isZero()) {
// } else if (trade.getPayoutTx() != null && details = Res.get("funds.tx.noFundsFromDispute");
// trade.getPayoutTx().getTxId().equals(Sha256Hash.wrap(txId))) { }
// details = Res.get("funds.tx.multiSigPayout", tradeId); }
//
// if (amountAsCoin.isZero()) { this.date = new Date(0); // TODO: convert height to date
// initialTxConfidenceVisibility = false; dateString = DisplayUtils.formatDateTime(date);
// }
// } else { isDustAttackTx = received && valueSentToMe.value < ignoreDustThreshold;
// Trade.DisputeState disputeState = trade.getDisputeState(); if (isDustAttackTx) {
// if (disputeState == Trade.DisputeState.DISPUTE_CLOSED) { details = Res.get("funds.tx.dustAttackTx");
// if (valueSentToMe.isPositive()) { }
// details = Res.get("funds.tx.disputePayout", tradeId);
// } else { // confidence
// details = Res.get("funds.tx.disputeLost", tradeId); lazyFieldsSupplier = Suppliers.memoize(() -> new LazyFields() {{
// initialTxConfidenceVisibility = false; txConfidenceIndicator = new TxConfidenceIndicator();
// } txConfidenceIndicator.setId("funds-confidence");
// } else if (disputeState == Trade.DisputeState.REFUND_REQUEST_CLOSED || tooltip = new Tooltip(Res.get("shared.notUsedYet"));
// disputeState == Trade.DisputeState.REFUND_REQUESTED || txConfidenceIndicator.setProgress(0);
// disputeState == Trade.DisputeState.REFUND_REQUEST_STARTED_BY_PEER) { txConfidenceIndicator.setTooltip(tooltip);
// if (valueSentToMe.isPositive()) { txConfidenceIndicator.setVisible(initialTxConfidenceVisibility);
// details = Res.get("funds.tx.refund", tradeId);
// } else { GUIUtil.updateConfidence(tx, tooltip, txConfidenceIndicator);
// // We have spent the deposit tx outputs to the Bisq donation address to enable confirmations = tx.getNumConfirmations();
// // the refund process (refund agent -> reimbursement). As the funds have left our wallet }});
// // already when funding the deposit tx we show 0 BTC as amount.
// // Confirmation is not known from the BitcoinJ side (not 100% clear why) as no funds // listen for tx updates
// // left our wallet nor we received funds. So we set indicator invisible. // TODO: this only listens for new blocks, listen for double spend
// amountAsCoin = Coin.ZERO; xmrWalletService.addWalletListener(new MoneroWalletListener() {
// details = Res.get("funds.tx.collateralForRefund", tradeId); @Override
// initialTxConfidenceVisibility = false; public void onNewBlock(long height) {
// } MoneroTxWallet tx = xmrWalletService.getWallet().getTx(txId);
// } else { GUIUtil.updateConfidence(tx, lazy().tooltip, lazy().txConfidenceIndicator);
// if (transactionAwareTrade.isDelayedPayoutTx(txId)) { confirmations = tx.getNumConfirmations();
// details = Res.get("funds.tx.timeLockedPayoutTx", tradeId); }
// initialTxConfidenceVisibility = false; });
// } else {
// details = Res.get("funds.tx.unknown", tradeId);
// }
// }
// }
// }
// }
// } else {
// if (amountAsCoin.isZero()) {
// details = Res.get("funds.tx.noFundsFromDispute");
// initialTxConfidenceVisibility = false;
// }
// // Use tx.getIncludedInBestChainAt() when available, otherwise use tx.getUpdateTime()
// date = transaction.getIncludedInBestChainAt() != null ? transaction.getIncludedInBestChainAt() : transaction.getUpdateTime();
// dateString = DisplayUtils.formatDateTime(date);
//
// isDustAttackTx = received && valueSentToMe.value < ignoreDustThreshold;
// if (isDustAttackTx) {
// details = Res.get("funds.tx.dustAttackTx");
// }
//
// // confidence
// lazyFieldsSupplier = Suppliers.memoize(() -> new LazyFields() {{
// txConfidenceIndicator = new TxConfidenceIndicator();
// txConfidenceIndicator.setId("funds-confidence");
// tooltip = new Tooltip(Res.get("shared.notUsedYet"));
// txConfidenceIndicator.setProgress(0);
// txConfidenceIndicator.setTooltip(tooltip);
// txConfidenceIndicator.setVisible(initialTxConfidenceVisibility);
//
// TransactionConfidence confidence = transaction.getConfidence();
// GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
// confirmations = confidence.getDepthInBlocks();
// }});
//
// txConfidenceListener = new TxConfidenceListener(txId) {
// @Override
// public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
// GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator);
// confirmations = confidence.getDepthInBlocks();
// }
// };
// btcWalletService.addTxConfidenceListener(txConfidenceListener);
} }
public void cleanup() { public void cleanup() {
// TODO (woodser): remove wallet listener
//xmrWalletService.removeTxConfidenceListener(txConfidenceListener);
} }
public TxConfidenceIndicator getTxConfidenceIndicator() { public TxConfidenceIndicator getTxConfidenceIndicator() {
return lazy().txConfidenceIndicator; return lazy().txConfidenceIndicator;
} }
@ -309,8 +261,8 @@ class TransactionsListItem {
return tradable; return tradable;
} }
public String getNumConfirmations() { public long getNumConfirmations() {
return String.valueOf(confirmations); return confirmations;
} }
public String getMemo() { public String getMemo() {

View File

@ -29,10 +29,9 @@ import bisq.desktop.main.overlays.windows.OfferDetailsWindow;
import bisq.desktop.main.overlays.windows.TradeDetailsWindow; import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
import bisq.desktop.util.GUIUtil; import bisq.desktop.util.GUIUtil;
import bisq.core.api.CoreMoneroConnectionsService; import bisq.core.api.CoreMoneroConnectionsService;
import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.offer.OpenOffer; import bisq.core.offer.OpenOffer;
import bisq.core.trade.Tradable;
import bisq.core.trade.Trade; import bisq.core.trade.Trade;
import bisq.core.user.Preferences; import bisq.core.user.Preferences;
@ -40,13 +39,10 @@ import bisq.network.p2p.P2PService;
import bisq.common.util.Utilities; import bisq.common.util.Utilities;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.wallet.listeners.WalletChangeEventListener;
import com.googlecode.jcsv.writer.CSVEntryConverter; import com.googlecode.jcsv.writer.CSVEntryConverter;
import javax.inject.Inject; import javax.inject.Inject;
import monero.wallet.model.MoneroWalletListener;
import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.fontawesome.AwesomeIcon;
import javafx.fxml.FXML; import javafx.fxml.FXML;
@ -77,11 +73,9 @@ import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList; import javafx.collections.transformation.SortedList;
import javafx.util.Callback; import javafx.util.Callback;
import java.math.BigInteger;
import java.util.Comparator; import java.util.Comparator;
import javax.annotation.Nullable;
@FxmlView @FxmlView
public class TransactionsView extends ActivatableView<VBox, Void> { public class TransactionsView extends ActivatableView<VBox, Void> {
@ -100,33 +94,40 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
private final DisplayedTransactions displayedTransactions; private final DisplayedTransactions displayedTransactions;
private final SortedList<TransactionsListItem> sortedDisplayedTransactions; private final SortedList<TransactionsListItem> sortedDisplayedTransactions;
private final BtcWalletService btcWalletService; private final XmrWalletService xmrWalletService;
private final P2PService p2PService;
private final CoreMoneroConnectionsService connectionService;
private final Preferences preferences; private final Preferences preferences;
private final TradeDetailsWindow tradeDetailsWindow; private final TradeDetailsWindow tradeDetailsWindow;
private final OfferDetailsWindow offerDetailsWindow; private final OfferDetailsWindow offerDetailsWindow;
private WalletChangeEventListener walletChangeEventListener;
private EventHandler<KeyEvent> keyEventEventHandler; private EventHandler<KeyEvent> keyEventEventHandler;
private Scene scene; private Scene scene;
private TransactionsUpdater transactionsUpdater = new TransactionsUpdater();
private class TransactionsUpdater extends MoneroWalletListener {
@Override
public void onNewBlock(long height) {
displayedTransactions.update();
}
@Override
public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) {
displayedTransactions.update();
}
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle // Constructor, lifecycle
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
private TransactionsView(BtcWalletService btcWalletService, private TransactionsView(XmrWalletService xmrWalletService,
P2PService p2PService, P2PService p2PService,
CoreMoneroConnectionsService connectionService, CoreMoneroConnectionsService connectionService,
Preferences preferences, Preferences preferences,
TradeDetailsWindow tradeDetailsWindow, TradeDetailsWindow tradeDetailsWindow,
OfferDetailsWindow offerDetailsWindow, OfferDetailsWindow offerDetailsWindow,
DisplayedTransactionsFactory displayedTransactionsFactory) { DisplayedTransactionsFactory displayedTransactionsFactory) {
this.btcWalletService = btcWalletService; this.xmrWalletService = xmrWalletService;
this.p2PService = p2PService;
this.connectionService = connectionService;
this.preferences = preferences; this.preferences = preferences;
this.tradeDetailsWindow = tradeDetailsWindow; this.tradeDetailsWindow = tradeDetailsWindow;
this.offerDetailsWindow = offerDetailsWindow; this.offerDetailsWindow = offerDetailsWindow;
@ -168,16 +169,12 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
addressColumn.setComparator(Comparator.comparing(item -> item.getDirection() + item.getAddressString())); addressColumn.setComparator(Comparator.comparing(item -> item.getDirection() + item.getAddressString()));
transactionColumn.setComparator(Comparator.comparing(TransactionsListItem::getTxId)); transactionColumn.setComparator(Comparator.comparing(TransactionsListItem::getTxId));
amountColumn.setComparator(Comparator.comparing(TransactionsListItem::getAmountAsCoin)); amountColumn.setComparator(Comparator.comparing(TransactionsListItem::getAmountAsCoin));
confidenceColumn.setComparator(Comparator.comparingDouble(item -> item.getTxConfidenceIndicator().getProgress())); confidenceColumn.setComparator(Comparator.comparingLong(item -> item.getNumConfirmations()));
memoColumn.setComparator(Comparator.comparing(TransactionsListItem::getMemo)); memoColumn.setComparator(Comparator.comparing(TransactionsListItem::getMemo));
dateColumn.setSortType(TableColumn.SortType.DESCENDING); dateColumn.setSortType(TableColumn.SortType.DESCENDING);
tableView.getSortOrder().add(dateColumn); tableView.getSortOrder().add(dateColumn);
walletChangeEventListener = wallet -> {
displayedTransactions.update();
};
keyEventEventHandler = event -> { keyEventEventHandler = event -> {
// Not intended to be public to users as the feature is not well tested // Not intended to be public to users as the feature is not well tested
if (Utilities.isAltOrCtrlPressed(KeyCode.R, event)) { if (Utilities.isAltOrCtrlPressed(KeyCode.R, event)) {
@ -202,7 +199,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
tableView.setItems(sortedDisplayedTransactions); tableView.setItems(sortedDisplayedTransactions);
displayedTransactions.update(); displayedTransactions.update();
btcWalletService.addChangeEventListener(walletChangeEventListener); xmrWalletService.addWalletListener(transactionsUpdater);
scene = root.getScene(); scene = root.getScene();
if (scene != null) if (scene != null)
@ -226,7 +223,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
columns[3] = item.getTxId(); columns[3] = item.getTxId();
columns[4] = item.getAmount(); columns[4] = item.getAmount();
columns[5] = item.getMemo() == null ? "" : item.getMemo(); columns[5] = item.getMemo() == null ? "" : item.getMemo();
columns[6] = item.getNumConfirmations(); columns[6] = String.valueOf(item.getNumConfirmations());
return columns; return columns;
}; };
@ -239,7 +236,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
protected void deactivate() { protected void deactivate() {
sortedDisplayedTransactions.comparatorProperty().unbind(); sortedDisplayedTransactions.comparatorProperty().unbind();
displayedTransactions.forEach(TransactionsListItem::cleanup); displayedTransactions.forEach(TransactionsListItem::cleanup);
btcWalletService.removeChangeEventListener(walletChangeEventListener); xmrWalletService.removeWalletListener(transactionsUpdater);
if (scene != null) if (scene != null)
scene.removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler); scene.removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
@ -505,49 +502,15 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
@Override @Override
public void updateItem(final TransactionsListItem item, boolean empty) { public void updateItem(final TransactionsListItem item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
if (item != null && !empty) {
TransactionConfidence confidence = btcWalletService.getConfidenceForTxId(item.getTxId());
if (confidence != null) {
if (confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.PENDING) {
if (button == null) {
button = new AutoTooltipButton(Res.get("funds.tx.revert"));
setGraphic(button);
}
button.setOnAction(e -> revertTransaction(item.getTxId(), item.getTradable()));
} else {
setGraphic(null); setGraphic(null);
if (button != null) { if (button != null) {
button.setOnAction(null); button.setOnAction(null);
button = null; button = null;
} }
} }
}
} else {
setGraphic(null);
if (button != null) {
button.setOnAction(null);
button = null;
}
}
}
}; };
} }
}); });
} }
private void revertTransaction(String txId, @Nullable Tradable tradable) {
if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, connectionService)) {
try {
btcWalletService.doubleSpendTransaction(txId, () -> {
if (tradable != null)
btcWalletService.swapAnyTradeEntryContextToAvailableEntry(tradable.getId());
new Popup().information(Res.get("funds.tx.txSent")).show();
}, errorMessage -> new Popup().warning(errorMessage).show());
} catch (Throwable e) {
new Popup().warning(e.getMessage()).show();
}
}
}
} }

View File

@ -23,6 +23,7 @@ import bisq.core.btc.listeners.XmrBalanceListener;
import bisq.core.btc.model.XmrAddressEntry; import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.wallet.XmrWalletService; import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.util.ParsingUtils;
import bisq.core.util.coin.CoinFormatter; import bisq.core.util.coin.CoinFormatter;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
@ -71,7 +72,8 @@ class WithdrawalListItem {
} }
private void updateBalance() { private void updateBalance() {
balance = walletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex()); balance = walletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex()); // TODO: Coin represents centineros everywhere, but here it's atomic units. reconcile
balance = Coin.valueOf(ParsingUtils.atomicUnitsToCentineros(balance.longValue())); // in centineros
if (balance != null) if (balance != null)
balanceLabel.setText(formatter.formatCoin(this.balance)); balanceLabel.setText(formatter.formatCoin(this.balance));
} }

View File

@ -26,16 +26,20 @@ import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.InputTextField; import bisq.desktop.components.InputTextField;
import bisq.desktop.components.TitledGroupBg; import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.TxDetails;
import bisq.desktop.main.overlays.windows.WalletPasswordWindow; import bisq.desktop.main.overlays.windows.WalletPasswordWindow;
import bisq.desktop.util.GUIUtil; import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.Layout; import bisq.desktop.util.Layout;
import bisq.core.btc.listeners.XmrBalanceListener; import bisq.core.btc.listeners.XmrBalanceListener;
import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.XmrWalletService; import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.provider.fee.FeeService; import bisq.core.provider.fee.FeeService;
import bisq.core.trade.Trade;
import bisq.core.trade.TradeManager; import bisq.core.trade.TradeManager;
import bisq.core.user.DontShowAgainLookup;
import bisq.core.user.Preferences; import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils; import bisq.core.util.FormattingUtils;
import bisq.core.util.ParsingUtils; import bisq.core.util.ParsingUtils;
@ -43,17 +47,15 @@ import bisq.core.util.coin.CoinFormatter;
import bisq.core.util.validation.BtcAddressValidator; import bisq.core.util.validation.BtcAddressValidator;
import bisq.network.p2p.P2PService; import bisq.network.p2p.P2PService;
import bisq.common.util.Tuple2;
import bisq.common.util.Tuple3; import bisq.common.util.Tuple3;
import bisq.common.util.Tuple4;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import monero.wallet.model.MoneroTxConfig;
import com.google.common.util.concurrent.FutureCallback; import monero.wallet.model.MoneroTxWallet;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -69,7 +71,6 @@ import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView; import javafx.scene.control.TableView;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.control.Toggle; import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup; import javafx.scene.control.ToggleGroup;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
@ -88,12 +89,12 @@ import javafx.collections.transformation.SortedList;
import javafx.util.Callback; import javafx.util.Callback;
import org.bouncycastle.crypto.params.KeyParameter;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -109,36 +110,27 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
@FXML @FXML
TableColumn<WithdrawalListItem, WithdrawalListItem> addressColumn, balanceColumn, selectColumn; TableColumn<WithdrawalListItem, WithdrawalListItem> addressColumn, balanceColumn, selectColumn;
private RadioButton useAllInputsRadioButton, useCustomInputsRadioButton, feeExcludedRadioButton, feeIncludedRadioButton; private RadioButton useAllInputsRadioButton, useCustomInputsRadioButton;
private Label amountLabel; private Label amountLabel;
private TextField amountTextField, withdrawFromTextField, withdrawToTextField, withdrawMemoTextField, transactionFeeInputTextField; private TextField amountTextField, withdrawFromTextField, withdrawToTextField, withdrawMemoTextField;
private final XmrWalletService xmrWalletService; private final XmrWalletService xmrWalletService;
private final TradeManager tradeManager; private final TradeManager tradeManager;
private final P2PService p2PService; private final P2PService p2PService;
private final WalletsSetup walletsSetup;
private final CoinFormatter formatter; private final CoinFormatter formatter;
private final Preferences preferences; private final Preferences preferences;
private final BtcAddressValidator btcAddressValidator;
private final WalletPasswordWindow walletPasswordWindow;
private final ObservableList<WithdrawalListItem> observableList = FXCollections.observableArrayList(); private final ObservableList<WithdrawalListItem> observableList = FXCollections.observableArrayList();
private final SortedList<WithdrawalListItem> sortedList = new SortedList<>(observableList); private final SortedList<WithdrawalListItem> sortedList = new SortedList<>(observableList);
private final Set<WithdrawalListItem> selectedItems = new HashSet<>(); private final Set<WithdrawalListItem> selectedItems = new HashSet<>();
private XmrBalanceListener balanceListener; private XmrBalanceListener balanceListener;
private Set<String> fromAddresses = new HashSet<>();
private Coin totalAvailableAmountOfSelectedItems = Coin.ZERO; private Coin totalAvailableAmountOfSelectedItems = Coin.ZERO;
private Coin amountAsCoin = Coin.ZERO; private Coin amountAsCoin = Coin.ZERO;
private ChangeListener<String> amountListener; private ChangeListener<String> amountListener;
private ChangeListener<Boolean> amountFocusListener, useCustomFeeCheckboxListener, transactionFeeFocusedListener; private ChangeListener<Boolean> amountFocusListener;
private ChangeListener<Toggle> feeToggleGroupListener, inputsToggleGroupListener; private ChangeListener<Toggle> inputsToggleGroupListener;
private ChangeListener<Number> transactionFeeChangeListener; private ToggleGroup inputsToggleGroup;
private ToggleGroup feeToggleGroup, inputsToggleGroup;
private ToggleButton useCustomFee;
private final BooleanProperty useAllInputs = new SimpleBooleanProperty(true); private final BooleanProperty useAllInputs = new SimpleBooleanProperty(true);
private boolean feeExcluded;
private int rowIndex = 0; private int rowIndex = 0;
private final FeeService feeService;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle // Constructor, lifecycle
@ -154,16 +146,11 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
BtcAddressValidator btcAddressValidator, BtcAddressValidator btcAddressValidator,
WalletPasswordWindow walletPasswordWindow, WalletPasswordWindow walletPasswordWindow,
FeeService feeService) { FeeService feeService) {
// throw new RuntimeException("WithdrawalView needs updated to use XMR wallet");
this.xmrWalletService = xmrWalletService; this.xmrWalletService = xmrWalletService;
this.tradeManager = tradeManager; this.tradeManager = tradeManager;
this.p2PService = p2PService; this.p2PService = p2PService;
this.walletsSetup = walletsSetup;
this.formatter = formatter; this.formatter = formatter;
this.preferences = preferences; this.preferences = preferences;
this.btcAddressValidator = btcAddressValidator;
this.walletPasswordWindow = walletPasswordWindow;
this.feeService = feeService;
} }
@Override @Override
@ -189,20 +176,13 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
useAllInputsRadioButton = labelRadioButtonRadioButtonTuple3.second; useAllInputsRadioButton = labelRadioButtonRadioButtonTuple3.second;
useCustomInputsRadioButton = labelRadioButtonRadioButtonTuple3.third; useCustomInputsRadioButton = labelRadioButtonRadioButtonTuple3.third;
feeToggleGroup = new ToggleGroup(); final Tuple2<Label, InputTextField> feeTuple3 = addTopLabelInputTextField(gridPane, ++rowIndex,
final Tuple4<Label, TextField, RadioButton, RadioButton> feeTuple3 = addTopLabelTextFieldRadioButtonRadioButton(gridPane, ++rowIndex, feeToggleGroup,
Res.get("funds.withdrawal.receiverAmount", Res.getBaseCurrencyCode()), Res.get("funds.withdrawal.receiverAmount", Res.getBaseCurrencyCode()),
"",
Res.get("funds.withdrawal.feeExcluded"),
Res.get("funds.withdrawal.feeIncluded"),
0); 0);
amountLabel = feeTuple3.first; amountLabel = feeTuple3.first;
amountTextField = feeTuple3.second; amountTextField = feeTuple3.second;
amountTextField.setMinWidth(180); amountTextField.setMinWidth(180);
feeExcludedRadioButton = feeTuple3.third;
feeIncludedRadioButton = feeTuple3.fourth;
withdrawFromTextField = addTopLabelTextField(gridPane, ++rowIndex, withdrawFromTextField = addTopLabelTextField(gridPane, ++rowIndex,
Res.get("funds.withdrawal.fromLabel", Res.getBaseCurrencyCode())).second; Res.get("funds.withdrawal.fromLabel", Res.getBaseCurrencyCode())).second;
@ -213,52 +193,6 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
withdrawMemoTextField = addTopLabelInputTextField(gridPane, ++rowIndex, withdrawMemoTextField = addTopLabelInputTextField(gridPane, ++rowIndex,
Res.get("funds.withdrawal.memoLabel", Res.getBaseCurrencyCode())).second; Res.get("funds.withdrawal.memoLabel", Res.getBaseCurrencyCode())).second;
Tuple3<Label, InputTextField, ToggleButton> customFeeTuple = addTopLabelInputTextFieldSlideToggleButton(gridPane, ++rowIndex,
Res.get("funds.withdrawal.txFee"), Res.get("funds.withdrawal.useCustomFeeValue"));
transactionFeeInputTextField = customFeeTuple.second;
useCustomFee = customFeeTuple.third;
useCustomFeeCheckboxListener = (observable, oldValue, newValue) -> {
transactionFeeInputTextField.setEditable(newValue);
if (!newValue) {
try {
transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
} catch (Exception e) {
e.printStackTrace();
}
}
};
transactionFeeFocusedListener = (o, oldValue, newValue) -> {
if (oldValue && !newValue) {
String estimatedFee = String.valueOf(feeService.getTxFeePerVbyte().value);
try {
int withdrawalTxFeePerVbyte = Integer.parseInt(transactionFeeInputTextField.getText());
final long minFeePerVbyte = feeService.getMinFeePerVByte();
if (withdrawalTxFeePerVbyte < minFeePerVbyte) {
new Popup().warning(Res.get("funds.withdrawal.txFeeMin", minFeePerVbyte)).show();
transactionFeeInputTextField.setText(estimatedFee);
} else if (withdrawalTxFeePerVbyte > 5000) {
new Popup().warning(Res.get("funds.withdrawal.txFeeTooLarge")).show();
transactionFeeInputTextField.setText(estimatedFee);
} else {
preferences.setWithdrawalTxFeeInVbytes(withdrawalTxFeePerVbyte);
}
} catch (NumberFormatException t) {
log.error(t.toString());
t.printStackTrace();
new Popup().warning(Res.get("validation.integerOnly")).show();
transactionFeeInputTextField.setText(estimatedFee);
} catch (Throwable t) {
log.error(t.toString());
t.printStackTrace();
new Popup().warning(Res.get("validation.inputError", t.getMessage())).show();
transactionFeeInputTextField.setText(estimatedFee);
}
}
};
transactionFeeChangeListener = (observable, oldValue, newValue) -> transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
final Button withdrawButton = addButton(gridPane, ++rowIndex, Res.get("funds.withdrawal.withdrawButton"), 15); final Button withdrawButton = addButton(gridPane, ++rowIndex, Res.get("funds.withdrawal.withdrawButton"), 15);
withdrawButton.setOnAction(event -> onWithdraw()); withdrawButton.setOnAction(event -> onWithdraw());
@ -304,14 +238,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
amountTextField.setText(""); amountTextField.setText("");
} }
}; };
feeExcludedRadioButton.setToggleGroup(feeToggleGroup); amountLabel.setText(Res.get("funds.withdrawal.receiverAmount"));
feeIncludedRadioButton.setToggleGroup(feeToggleGroup);
feeToggleGroupListener = (observable, oldValue, newValue) -> {
feeExcluded = newValue == feeExcludedRadioButton;
amountLabel.setText(feeExcluded ?
Res.get("funds.withdrawal.receiverAmount") :
Res.get("funds.withdrawal.senderAmount"));
};
} }
private void updateInputSelection() { private void updateInputSelection() {
@ -333,22 +260,11 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
amountTextField.textProperty().addListener(amountListener); amountTextField.textProperty().addListener(amountListener);
amountTextField.focusedProperty().addListener(amountFocusListener); amountTextField.focusedProperty().addListener(amountFocusListener);
xmrWalletService.addBalanceListener(balanceListener); xmrWalletService.addBalanceListener(balanceListener);
feeToggleGroup.selectedToggleProperty().addListener(feeToggleGroupListener);
inputsToggleGroup.selectedToggleProperty().addListener(inputsToggleGroupListener); inputsToggleGroup.selectedToggleProperty().addListener(inputsToggleGroupListener);
if (feeToggleGroup.getSelectedToggle() == null)
feeToggleGroup.selectToggle(feeIncludedRadioButton);
if (inputsToggleGroup.getSelectedToggle() == null) if (inputsToggleGroup.getSelectedToggle() == null)
inputsToggleGroup.selectToggle(useAllInputsRadioButton); inputsToggleGroup.selectToggle(useAllInputsRadioButton);
useCustomFee.setSelected(false);
transactionFeeInputTextField.setEditable(false);
transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
feeService.feeUpdateCounterProperty().addListener(transactionFeeChangeListener);
useCustomFee.selectedProperty().addListener(useCustomFeeCheckboxListener);
transactionFeeInputTextField.focusedProperty().addListener(transactionFeeFocusedListener);
updateInputSelection(); updateInputSelection();
GUIUtil.requestFocus(withdrawToTextField); GUIUtil.requestFocus(withdrawToTextField);
} }
@ -360,12 +276,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
xmrWalletService.removeBalanceListener(balanceListener); xmrWalletService.removeBalanceListener(balanceListener);
amountTextField.textProperty().removeListener(amountListener); amountTextField.textProperty().removeListener(amountListener);
amountTextField.focusedProperty().removeListener(amountFocusListener); amountTextField.focusedProperty().removeListener(amountFocusListener);
feeToggleGroup.selectedToggleProperty().removeListener(feeToggleGroupListener);
inputsToggleGroup.selectedToggleProperty().removeListener(inputsToggleGroupListener); inputsToggleGroup.selectedToggleProperty().removeListener(inputsToggleGroupListener);
transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener);
if (transactionFeeChangeListener != null)
feeService.feeUpdateCounterProperty().removeListener(transactionFeeChangeListener);
useCustomFee.selectedProperty().removeListener(useCustomFeeCheckboxListener);
} }
@ -374,108 +285,72 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void onWithdraw() { private void onWithdraw() {
throw new RuntimeException("WithdrawalView.onWithdraw() not updated to XMR"); if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, xmrWalletService.getConnectionsService())) {
// if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, walletsSetup)) { try {
// try {
// final String withdrawToAddress = withdrawToTextField.getText(); // get withdraw address
// final Coin sendersAmount; final String withdrawToAddress = withdrawToTextField.getText();
//
// // We do not know sendersAmount if senderPaysFee is true. We repeat fee calculation after first attempt if senderPaysFee is true. // get receiver amount
// Transaction feeEstimationTransaction = btcWalletService.getFeeEstimationTransactionForMultipleAddresses(fromAddresses, amountAsCoin); Coin receiverAmount = amountAsCoin;
// if (feeExcluded && feeEstimationTransaction != null) { if (!receiverAmount.isPositive()) throw new RuntimeException(Res.get("portfolio.pending.step5_buyer.amountTooLow"));
// feeEstimationTransaction = btcWalletService.getFeeEstimationTransactionForMultipleAddresses(fromAddresses, amountAsCoin.add(feeEstimationTransaction.getFee()));
// } // create tx
// checkNotNull(feeEstimationTransaction, "feeEstimationTransaction must not be null"); MoneroTxWallet tx = xmrWalletService.getWallet().createTx(new MoneroTxConfig()
// .setAccountIndex(0)
// Coin dust = btcWalletService.getDust(feeEstimationTransaction); .setAmount(ParsingUtils.coinToAtomicUnits(receiverAmount)) // TODO: rename to centinerosToAtomicUnits()?
// Coin fee = feeEstimationTransaction.getFee().add(dust); .setAddress(withdrawToAddress));
// Coin receiverAmount;
// // amountAsCoin is what the user typed into the withdrawal field. // create confirmation message
// // this can be interpreted as either the senders amount or receivers amount depending Coin fee = ParsingUtils.atomicUnitsToCoin(tx.getFee());
// // on a radio button "fee excluded / fee included". Coin sendersAmount = receiverAmount.add(fee);
// // therefore we calculate the actual sendersAmount and receiverAmount as follows: String messageText = Res.get("shared.sendFundsDetailsWithFee",
// if (feeExcluded) { formatter.formatCoinWithCode(sendersAmount),
// receiverAmount = amountAsCoin; withdrawFromTextField.getText(),
// sendersAmount = receiverAmount.add(fee); withdrawToAddress,
// } else { formatter.formatCoinWithCode(fee),
// sendersAmount = amountAsCoin.add(dust); // sendersAmount bumped up to UTXO size when dust is in play formatter.formatCoinWithCode(receiverAmount));
// receiverAmount = sendersAmount.subtract(fee);
// } // popup confirmation message
// if (dust.isPositive()) { new Popup().headLine(Res.get("funds.withdrawal.confirmWithdrawalRequest"))
// log.info("Dust output ({} satoshi) was detected, the dust amount has been added to the fee (was {}, now {})", .confirmation(messageText)
// dust.value, .actionButtonText(Res.get("shared.yes"))
// feeEstimationTransaction.getFee(), .onAction(() -> {
// fee.value);
// } // relay tx
// try {
// if (areInputsValid(sendersAmount)) { xmrWalletService.getWallet().relayTx(tx);
// int txVsize = feeEstimationTransaction.getVsize(); String key = "showTransactionSent";
// log.info("Fee for tx with size {}: {} " + Res.getBaseCurrencyCode() + "", txVsize, fee.toPlainString()); if (DontShowAgainLookup.showAgain(key)) {
// new TxDetails(tx.getHash(), withdrawToAddress, formatter.formatCoinWithCode(sendersAmount))
// if (receiverAmount.isPositive()) { .dontShowAgainId(key)
// double vkb = txVsize / 1000d; .show();
// }
// String messageText = Res.get("shared.sendFundsDetailsWithFee", log.debug("onWithdraw onSuccess tx ID:{}", tx.getHash());
// formatter.formatCoinWithCode(sendersAmount),
// withdrawFromTextField.getText(), List<Trade> trades = new ArrayList<>(tradeManager.getObservableList());
// withdrawToAddress, trades.stream()
// formatter.formatCoinWithCode(fee), .filter(Trade::isPayoutPublished)
// Double.parseDouble(transactionFeeInputTextField.getText()), .forEach(trade -> xmrWalletService.getAddressEntry(trade.getId(), XmrAddressEntry.Context.TRADE_PAYOUT)
// vkb, .ifPresent(addressEntry -> {
// formatter.formatCoinWithCode(receiverAmount)); if (xmrWalletService.getBalanceForAddress(addressEntry.getAddressString()).isZero())
// if (dust.isPositive()) { tradeManager.onTradeCompleted(trade);
// messageText = Res.get("shared.sendFundsDetailsDust", }));
// dust.value, dust.value > 1 ? "s" : "") } catch (Exception e) {
// + messageText; e.printStackTrace();
// } }
// })
// new Popup().headLine(Res.get("funds.withdrawal.confirmWithdrawalRequest")) .closeButtonText(Res.get("shared.cancel"))
// .confirmation(messageText) .show();
// .actionButtonText(Res.get("shared.yes")) } catch (Throwable e) {
// .onAction(() -> doWithdraw(sendersAmount, fee, new FutureCallback<>() { if (e.getMessage().contains("enough")) new Popup().warning(Res.get("funds.withdrawal.warn.amountExceeds") + "\n\nError message:\n" + e.getMessage()).show();
// @Override else {
// public void onSuccess(@javax.annotation.Nullable Transaction transaction) { e.printStackTrace();
// if (transaction != null) { log.error(e.toString());
// String key = "showTransactionSent"; new Popup().warning(e.toString()).show();
// if (DontShowAgainLookup.showAgain(key)) { }
// new TxDetails(transaction.getTxId().toString(), withdrawToAddress, formatter.formatCoinWithCode(sendersAmount)) }
// .dontShowAgainId(key) }
// .show();
// }
// log.debug("onWithdraw onSuccess tx ID:{}", transaction.getTxId().toString());
// } else {
// log.error("onWithdraw transaction is null");
// }
//
// List<Trade> trades = new ArrayList<>(tradeManager.getObservableList());
// trades.stream()
// .filter(Trade::isPayoutPublished)
// .forEach(trade -> btcWalletService.getAddressEntry(trade.getId(), AddressEntry.Context.TRADE_PAYOUT)
// .ifPresent(addressEntry -> {
// if (btcWalletService.getBalanceForAddress(addressEntry.getAddress()).isZero())
// tradeManager.onTradeCompleted(trade);
// }));
// }
//
// @Override
// public void onFailure(@NotNull Throwable t) {
// log.error("onWithdraw onFailure");
// }
// }))
// .closeButtonText(Res.get("shared.cancel"))
// .show();
// } else {
// new Popup().warning(Res.get("portfolio.pending.step5_buyer.amountTooLow")).show();
// }
// }
// } catch (InsufficientFundsException e) {
// new Popup().warning(Res.get("funds.withdrawal.warn.amountExceeds") + "\n\nError message:\n" + e.getMessage()).show();
// } catch (Throwable e) {
// e.printStackTrace();
// log.error(e.toString());
// new Popup().warning(e.toString()).show();
// }
// }
} }
private void selectForWithdrawal(WithdrawalListItem item) { private void selectForWithdrawal(WithdrawalListItem item) {
@ -484,10 +359,6 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
else else
selectedItems.remove(item); selectedItems.remove(item);
fromAddresses = selectedItems.stream()
.map(WithdrawalListItem::getAddressString)
.collect(Collectors.toSet());
if (!selectedItems.isEmpty()) { if (!selectedItems.isEmpty()) {
totalAvailableAmountOfSelectedItems = Coin.valueOf(selectedItems.stream().mapToLong(e -> e.getBalance().getValue()).sum()); totalAvailableAmountOfSelectedItems = Coin.valueOf(selectedItems.stream().mapToLong(e -> e.getBalance().getValue()).sum());
if (totalAvailableAmountOfSelectedItems.isPositive()) { if (totalAvailableAmountOfSelectedItems.isPositive()) {
@ -533,7 +404,6 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void updateList() { private void updateList() {
//throw new RuntimeException("WithdrawalView.updateList() needs updated to use XMR");
observableList.forEach(WithdrawalListItem::cleanup); observableList.forEach(WithdrawalListItem::cleanup);
observableList.setAll(xmrWalletService.getAddressEntriesForAvailableBalanceStream() observableList.setAll(xmrWalletService.getAddressEntriesForAvailableBalanceStream()
.map(addressEntry -> new WithdrawalListItem(addressEntry, xmrWalletService, formatter)) .map(addressEntry -> new WithdrawalListItem(addressEntry, xmrWalletService, formatter))
@ -542,51 +412,6 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
updateInputSelection(); updateInputSelection();
} }
private void doWithdraw(Coin amount, Coin fee, FutureCallback<Transaction> callback) {
throw new RuntimeException("WithdrawalView.doWithdraw() not updated to XMR");
// if (xmrWalletService.isEncrypted()) {
// UserThread.runAfter(() -> walletPasswordWindow.onAesKey(aesKey ->
// sendFunds(amount, fee, aesKey, callback))
// .show(), 300, TimeUnit.MILLISECONDS);
// } else {
// sendFunds(amount, fee, null, callback);
// }
}
private void sendFunds(Coin amount, Coin fee, KeyParameter aesKey, FutureCallback<Transaction> callback) {
throw new RuntimeException("WithdrawalView.sendFunds() not updated to XMR");
// try {
// String memo = withdrawMemoTextField.getText();
// if (memo.isEmpty()) {
// memo = null;
// }
// Transaction transaction = btcWalletService.sendFundsForMultipleAddresses(fromAddresses,
// withdrawToTextField.getText(),
// amount,
// fee,
// null,
// aesKey,
// memo,
// callback);
//
// reset();
// updateList();
// } catch (AddressFormatException e) {
// new Popup().warning(Res.get("validation.btc.invalidAddress")).show();
// } catch (Wallet.DustySendRequested e) {
// new Popup().warning(Res.get("validation.amountBelowDust",
// formatter.formatCoinWithCode(Restrictions.getMinNonDustOutput()))).show();
// } catch (AddressEntryException e) {
// new Popup().error(e.getMessage()).show();
// } catch (InsufficientMoneyException e) {
// log.warn(e.getMessage());
// new Popup().warning(Res.get("funds.withdrawal.notEnoughFunds") + "\n\nError message:\n" + e.getMessage()).show();
// } catch (Throwable e) {
// log.warn(e.toString());
// new Popup().warning(e.toString()).show();
// }
}
private void reset() { private void reset() {
withdrawFromTextField.setText(""); withdrawFromTextField.setText("");
withdrawFromTextField.setPromptText(Res.get("funds.withdrawal.selectAddress")); withdrawFromTextField.setPromptText(Res.get("funds.withdrawal.selectAddress"));
@ -603,37 +428,10 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
withdrawMemoTextField.setText(""); withdrawMemoTextField.setText("");
withdrawMemoTextField.setPromptText(Res.get("funds.withdrawal.memo")); withdrawMemoTextField.setPromptText(Res.get("funds.withdrawal.memo"));
transactionFeeInputTextField.setText("");
transactionFeeInputTextField.setPromptText(Res.get("funds.withdrawal.useCustomFeeValueInfo"));
selectedItems.clear(); selectedItems.clear();
tableView.getSelectionModel().clearSelection(); tableView.getSelectionModel().clearSelection();
} }
private boolean areInputsValid(Coin sendersAmount) {
if (!sendersAmount.isPositive()) {
new Popup().warning(Res.get("validation.negative")).show();
return false;
}
if (!btcAddressValidator.validate(withdrawToTextField.getText()).isValid) {
new Popup().warning(Res.get("validation.btc.invalidAddress")).show();
return false;
}
if (!totalAvailableAmountOfSelectedItems.isPositive()) {
new Popup().warning(Res.get("funds.withdrawal.warn.noSourceAddressSelected")).show();
return false;
}
if (sendersAmount.compareTo(totalAvailableAmountOfSelectedItems) > 0) {
new Popup().warning(Res.get("funds.withdrawal.warn.amountExceeds")).show();
return false;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// ColumnCellFactories // ColumnCellFactories
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -21,7 +21,7 @@ import bisq.desktop.components.TxIdTextField;
import bisq.desktop.main.shared.PriceFeedComboBoxItem; import bisq.desktop.main.shared.PriceFeedComboBoxItem;
import bisq.desktop.util.GUIUtil; import bisq.desktop.util.GUIUtil;
import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.CurrencyUtil; import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.locale.TradeCurrency; import bisq.core.locale.TradeCurrency;
@ -86,7 +86,7 @@ public class MarketPricePresentation {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
public MarketPricePresentation(BtcWalletService btcWalletService, public MarketPricePresentation(XmrWalletService xmrWalletService,
PriceFeedService priceFeedService, PriceFeedService priceFeedService,
Preferences preferences, Preferences preferences,
FeeService feeService) { FeeService feeService) {
@ -96,7 +96,7 @@ public class MarketPricePresentation {
TxIdTextField.setPreferences(preferences); TxIdTextField.setPreferences(preferences);
// TODO // TODO
TxIdTextField.setWalletService(btcWalletService); TxIdTextField.setXmrWalletService(xmrWalletService);
GUIUtil.setFeeService(feeService); GUIUtil.setFeeService(feeService);
} }

View File

@ -1047,11 +1047,12 @@ public class FormBuilder {
String checkBoxTitle, String checkBoxTitle,
double top) { double top) {
Button button = new AutoTooltipButton(buttonTitle); Button button = new AutoTooltipButton(buttonTitle);
CheckBox checkBox = new AutoTooltipCheckBox(checkBoxTitle); CheckBox checkBox = checkBoxTitle == null ? null : new AutoTooltipCheckBox(checkBoxTitle);
HBox hBox = new HBox(20); HBox hBox = new HBox(20);
hBox.setAlignment(Pos.CENTER_LEFT); hBox.setAlignment(Pos.CENTER_LEFT);
hBox.getChildren().addAll(button, checkBox); hBox.getChildren().add(button);
if (checkBox != null) hBox.getChildren().add(button);
GridPane.setRowIndex(hBox, rowIndex); GridPane.setRowIndex(hBox, rowIndex);
hBox.setPadding(new Insets(top, 0, 0, 0)); hBox.setPadding(new Insets(top, 0, 0, 0));
gridPane.getChildren().add(hBox); gridPane.getChildren().add(hBox);

View File

@ -32,6 +32,7 @@ import bisq.core.account.witness.AccountAgeWitness;
import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.api.CoreMoneroConnectionsService; import bisq.core.api.CoreMoneroConnectionsService;
import bisq.core.app.HavenoSetup; import bisq.core.app.HavenoSetup;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.Country; import bisq.core.locale.Country;
import bisq.core.locale.CountryUtil; import bisq.core.locale.CountryUtil;
import bisq.core.locale.CurrencyUtil; import bisq.core.locale.CurrencyUtil;
@ -134,7 +135,7 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import monero.wallet.model.MoneroTxWallet;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -566,34 +567,28 @@ public class GUIUtil {
}; };
} }
public static void updateConfidence(TransactionConfidence confidence, public static void updateConfidence(MoneroTxWallet tx,
Tooltip tooltip, Tooltip tooltip,
TxConfidenceIndicator txConfidenceIndicator) { TxConfidenceIndicator txConfidenceIndicator) {
if (confidence != null) { if (tx != null) {
switch (confidence.getConfidenceType()) { if (!tx.isRelayed()) {
case UNKNOWN:
tooltip.setText(Res.get("confidence.unknown")); tooltip.setText(Res.get("confidence.unknown"));
txConfidenceIndicator.setProgress(0); txConfidenceIndicator.setProgress(0);
break; } else if (tx.isFailed()) {
case PENDING:
tooltip.setText(Res.get("confidence.seen", confidence.numBroadcastPeers()));
txConfidenceIndicator.setProgress(-1.0);
break;
case BUILDING:
tooltip.setText(Res.get("confidence.confirmed", confidence.getDepthInBlocks()));
txConfidenceIndicator.setProgress(Math.min(1, confidence.getDepthInBlocks() / 6.0));
break;
case DEAD:
tooltip.setText(Res.get("confidence.invalid")); tooltip.setText(Res.get("confidence.invalid"));
txConfidenceIndicator.setProgress(0); txConfidenceIndicator.setProgress(0);
break; } else if (tx.isConfirmed()) {
tooltip.setText(Res.get("confidence.confirmed", tx.getNumConfirmations()));
txConfidenceIndicator.setProgress(Math.min(1, tx.getNumConfirmations() / (double) XmrWalletService.NUM_BLOCKS_UNLOCK));
} else {
tooltip.setText(Res.get("confidence.seen", 0)); // TODO: replace with numBroadcastPeers
txConfidenceIndicator.setProgress(-1.0);
} }
txConfidenceIndicator.setPrefSize(24, 24); txConfidenceIndicator.setPrefSize(24, 24);
} }
} }
public static void openWebPage(String target) { public static void openWebPage(String target) {
openWebPage(target, true, null); openWebPage(target, true, null);
} }