support funding tabs: receive, send, transactions
This commit is contained in:
parent
c71c61d1bb
commit
fb3745c6df
@ -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() {
|
||||||
|
@ -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() {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
@ -39,7 +43,11 @@ public class ParsingUtils {
|
|||||||
|
|
||||||
public static long atomicUnitsToCentineros(BigInteger atomicUnits) {
|
public static long atomicUnitsToCentineros(BigInteger atomicUnits) {
|
||||||
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();
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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");
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
|
||||||
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
|
||||||
GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
walletService.addTxConfidenceListener(txConfidenceListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
balanceListener = new BalanceListener(address) {
|
|
||||||
@Override
|
@Override
|
||||||
public void onBalanceChanged(Coin balanceAsCoin, Transaction tx) {
|
public void onBalanceChanged(BigInteger balance) {
|
||||||
DepositListItem.this.balanceAsCoin = balanceAsCoin;
|
DepositListItem.this.balanceAsCoin = ParsingUtils.atomicUnitsToCoin(balance);
|
||||||
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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) {
|
setGraphic(null);
|
||||||
TransactionConfidence confidence = btcWalletService.getConfidenceForTxId(item.getTxId());
|
if (button != null) {
|
||||||
if (confidence != null) {
|
button.setOnAction(null);
|
||||||
if (confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.PENDING) {
|
button = null;
|
||||||
if (button == null) {
|
|
||||||
button = new AutoTooltipButton(Res.get("funds.tx.revert"));
|
|
||||||
setGraphic(button);
|
|
||||||
}
|
|
||||||
button.setOnAction(e -> revertTransaction(item.getTxId(), item.getTradable()));
|
|
||||||
} else {
|
|
||||||
setGraphic(null);
|
|
||||||
if (button != null) {
|
|
||||||
button.setOnAction(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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
} else if (tx.isFailed()) {
|
||||||
break;
|
tooltip.setText(Res.get("confidence.invalid"));
|
||||||
case PENDING:
|
txConfidenceIndicator.setProgress(0);
|
||||||
tooltip.setText(Res.get("confidence.seen", confidence.numBroadcastPeers()));
|
} else if (tx.isConfirmed()) {
|
||||||
txConfidenceIndicator.setProgress(-1.0);
|
tooltip.setText(Res.get("confidence.confirmed", tx.getNumConfirmations()));
|
||||||
break;
|
txConfidenceIndicator.setProgress(Math.min(1, tx.getNumConfirmations() / (double) XmrWalletService.NUM_BLOCKS_UNLOCK));
|
||||||
case BUILDING:
|
} else {
|
||||||
tooltip.setText(Res.get("confidence.confirmed", confidence.getDepthInBlocks()));
|
tooltip.setText(Res.get("confidence.seen", 0)); // TODO: replace with numBroadcastPeers
|
||||||
txConfidenceIndicator.setProgress(Math.min(1, confidence.getDepthInBlocks() / 6.0));
|
txConfidenceIndicator.setProgress(-1.0);
|
||||||
break;
|
|
||||||
case DEAD:
|
|
||||||
tooltip.setText(Res.get("confidence.invalid"));
|
|
||||||
txConfidenceIndicator.setProgress(0);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user