support funding tabs: receive, send, transactions
This commit is contained in:
parent
c71c61d1bb
commit
fb3745c6df
@ -171,7 +171,7 @@ class CoreWalletsService {
|
||||
|
||||
String getXmrNewSubaddress() {
|
||||
accountService.checkAccountOpen();
|
||||
return xmrWalletService.getWallet().createSubaddress(0).getAddress();
|
||||
return xmrWalletService.getNewAddressEntry().getAddressString();
|
||||
}
|
||||
|
||||
List<MoneroTxWallet> getXmrTxs() {
|
||||
|
@ -25,8 +25,8 @@ public class XmrBalanceListener {
|
||||
public XmrBalanceListener() {
|
||||
}
|
||||
|
||||
public XmrBalanceListener(Integer accountIndex) {
|
||||
this.subaddressIndex = accountIndex;
|
||||
public XmrBalanceListener(Integer subaddressIndex) {
|
||||
this.subaddressIndex = subaddressIndex;
|
||||
}
|
||||
|
||||
public Integer getSubaddressIndex() {
|
||||
|
@ -55,6 +55,7 @@ import monero.wallet.model.MoneroCheckTx;
|
||||
import monero.wallet.model.MoneroDestination;
|
||||
import monero.wallet.model.MoneroOutputWallet;
|
||||
import monero.wallet.model.MoneroSubaddress;
|
||||
import monero.wallet.model.MoneroTransferQuery;
|
||||
import monero.wallet.model.MoneroTxConfig;
|
||||
import monero.wallet.model.MoneroTxQuery;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
@ -70,6 +71,7 @@ public class XmrWalletService {
|
||||
|
||||
// Monero configuration
|
||||
// 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 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
|
||||
@ -634,6 +636,7 @@ public class XmrWalletService {
|
||||
// clear wallets
|
||||
wallet = null;
|
||||
multisigWallets.clear();
|
||||
walletListeners.clear();
|
||||
}
|
||||
|
||||
private void backupWallet(String walletName) {
|
||||
@ -649,7 +652,17 @@ public class XmrWalletService {
|
||||
}
|
||||
|
||||
// ----------------------------- 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) {
|
||||
var available = findAddressEntry(address, XmrAddressEntry.Context.AVAILABLE);
|
||||
if (!available.isPresent()) return null;
|
||||
@ -761,12 +774,21 @@ public class XmrWalletService {
|
||||
return xmrAddressEntryList.getAddressEntriesAsListImmutable();
|
||||
}
|
||||
|
||||
public boolean isSubaddressUnused(int subaddressIndex) {
|
||||
return subaddressIndex != 0 && getBalanceForSubaddress(subaddressIndex).value == 0;
|
||||
// return !wallet.getSubaddress(accountIndex, 0).isUsed(); // TODO: isUsed()
|
||||
// does not include unconfirmed funds
|
||||
public List<XmrAddressEntry> getUnusedAddressEntries() {
|
||||
return getAvailableAddressEntries().stream()
|
||||
.filter(e -> isSubaddressUnused(e.getSubaddressIndex()))
|
||||
.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) {
|
||||
|
||||
// get subaddress balance
|
||||
@ -786,6 +808,24 @@ public class XmrWalletService {
|
||||
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() {
|
||||
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.payment.payload.PaymentMethod;
|
||||
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.refund.RefundResultState;
|
||||
import bisq.core.support.messages.ChatMessage;
|
||||
@ -886,7 +885,7 @@ public abstract class Trade implements Tradable, Model {
|
||||
|
||||
// check if deposit txs unlocked
|
||||
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) {
|
||||
setConfirmedState();
|
||||
return;
|
||||
@ -926,7 +925,7 @@ public abstract class Trade implements Tradable, Model {
|
||||
|
||||
// compute unlock height
|
||||
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
|
||||
|
@ -25,6 +25,10 @@ public class ParsingUtils {
|
||||
return centinerosToAtomicUnits(coin.value);
|
||||
}
|
||||
|
||||
public static double coinToXmr(Coin coin) {
|
||||
return atomicUnitsToXmr(coinToAtomicUnits(coin));
|
||||
}
|
||||
|
||||
public static BigInteger centinerosToAtomicUnits(long centineros) {
|
||||
return BigInteger.valueOf(centineros).multiply(ParsingUtils.CENTINEROS_AU_MULTIPLIER);
|
||||
}
|
||||
@ -39,7 +43,11 @@ public class ParsingUtils {
|
||||
|
||||
public static long atomicUnitsToCentineros(BigInteger atomicUnits) {
|
||||
return atomicUnits.divide(CENTINEROS_AU_MULTIPLIER).longValueExact();
|
||||
}
|
||||
}
|
||||
|
||||
public static Coin atomicUnitsToCoin(BigInteger atomicUnits) {
|
||||
return Coin.valueOf(atomicUnitsToCentineros(atomicUnits));
|
||||
}
|
||||
|
||||
public static double atomicUnitsToXmr(BigInteger atomicUnits) {
|
||||
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.notUsedYet=Not used yet
|
||||
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"
|
||||
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
|
||||
|
@ -20,19 +20,17 @@ package bisq.desktop.components;
|
||||
import bisq.desktop.components.indicator.TxConfidenceIndicator;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
|
||||
import bisq.core.btc.listeners.TxConfidenceListener;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.user.BlockChainExplorer;
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
|
||||
import javafx.scene.control.Label;
|
||||
@ -42,21 +40,23 @@ import javafx.scene.layout.AnchorPane;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
import monero.wallet.model.MoneroWalletListener;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class TxIdTextField extends AnchorPane {
|
||||
@Setter
|
||||
private static Preferences preferences;
|
||||
@Setter
|
||||
private static BtcWalletService walletService;
|
||||
private static XmrWalletService xmrWalletService;
|
||||
|
||||
@Getter
|
||||
private final TextField textField;
|
||||
private final Tooltip progressIndicatorTooltip;
|
||||
private final TxConfidenceIndicator txConfidenceIndicator;
|
||||
private final Label copyIcon, blockExplorerIcon, missingTxWarningIcon;
|
||||
private TxConfidenceListener txConfidenceListener;
|
||||
|
||||
private MoneroWalletListener txUpdater;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
@ -113,8 +113,10 @@ public class TxIdTextField extends AnchorPane {
|
||||
}
|
||||
|
||||
public void setup(@Nullable String txId) {
|
||||
if (txConfidenceListener != null)
|
||||
walletService.removeTxConfidenceListener(txConfidenceListener);
|
||||
if (txUpdater != null) {
|
||||
xmrWalletService.removeWalletListener(txUpdater);
|
||||
txUpdater = null;
|
||||
}
|
||||
|
||||
if (txId == null) {
|
||||
textField.setText(Res.get("shared.na"));
|
||||
@ -128,15 +130,22 @@ public class TxIdTextField extends AnchorPane {
|
||||
missingTxWarningIcon.setManaged(true);
|
||||
return;
|
||||
}
|
||||
|
||||
txConfidenceListener = new TxConfidenceListener(txId) {
|
||||
|
||||
// listen for tx updates
|
||||
// TODO: this only listens for new blocks, listen for double spend
|
||||
txUpdater = new MoneroWalletListener() {
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
updateConfidence(confidence);
|
||||
public void onNewBlock(long height) {
|
||||
updateConfidence(txId);
|
||||
}
|
||||
@Override
|
||||
public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) {
|
||||
updateConfidence(txId);
|
||||
}
|
||||
};
|
||||
walletService.addTxConfidenceListener(txConfidenceListener);
|
||||
updateConfidence(walletService.getConfidenceForTxId(txId));
|
||||
xmrWalletService.addWalletListener(txUpdater);
|
||||
|
||||
updateConfidence(txId);
|
||||
|
||||
textField.setText(txId);
|
||||
textField.setOnMouseClicked(mouseEvent -> openBlockExplorer(txId));
|
||||
@ -145,9 +154,10 @@ public class TxIdTextField extends AnchorPane {
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
if (walletService != null && txConfidenceListener != null)
|
||||
walletService.removeTxConfidenceListener(txConfidenceListener);
|
||||
|
||||
if (xmrWalletService != null && txUpdater != null) {
|
||||
xmrWalletService.removeWalletListener(txUpdater);
|
||||
txUpdater = null;
|
||||
}
|
||||
textField.setOnMouseClicked(null);
|
||||
blockExplorerIcon.setOnMouseClicked(null);
|
||||
copyIcon.setOnMouseClicked(null);
|
||||
@ -165,9 +175,15 @@ public class TxIdTextField extends AnchorPane {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateConfidence(TransactionConfidence confidence) {
|
||||
GUIUtil.updateConfidence(confidence, progressIndicatorTooltip, txConfidenceIndicator);
|
||||
if (confidence != null) {
|
||||
private void updateConfidence(String txId) {
|
||||
MoneroTxWallet tx = null;
|
||||
try {
|
||||
tx = xmrWalletService.getWallet().getTx(txId);
|
||||
} catch (Exception e) {
|
||||
// do nothing
|
||||
}
|
||||
GUIUtil.updateConfidence(tx, progressIndicatorTooltip, txConfidenceIndicator);
|
||||
if (tx != null) {
|
||||
if (txConfidenceIndicator.getProgress() != 0) {
|
||||
txConfidenceIndicator.setVisible(true);
|
||||
AnchorPane.setRightAnchor(txConfidenceIndicator, 0.0);
|
||||
|
@ -200,8 +200,8 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
||||
sellButton.fire();
|
||||
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT4, keyEvent)) {
|
||||
portfolioButton.fire();
|
||||
// } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT5, keyEvent)) {
|
||||
// fundsButton.fire();
|
||||
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT5, keyEvent)) {
|
||||
fundsButton.fire();
|
||||
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT6, keyEvent)) {
|
||||
supportButton.fire();
|
||||
} 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(),
|
||||
sellButton, getNavigationSeparator(), portfolioButtonWithBadge, getNavigationSeparator());
|
||||
sellButton, getNavigationSeparator(), portfolioButtonWithBadge, getNavigationSeparator(), fundsButton);
|
||||
|
||||
primaryNav.setAlignment(Pos.CENTER_LEFT);
|
||||
primaryNav.getStyleClass().add("nav-primary");
|
||||
|
@ -43,7 +43,7 @@ import bisq.core.alert.PrivateNotificationManager;
|
||||
import bisq.core.api.CoreMoneroConnectionsService;
|
||||
import bisq.core.app.HavenoSetup;
|
||||
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.CurrencyUtil;
|
||||
import bisq.core.locale.Res;
|
||||
@ -153,7 +153,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
|
||||
@Inject
|
||||
public MainViewModel(HavenoSetup bisqSetup,
|
||||
CoreMoneroConnectionsService connectionService,
|
||||
BtcWalletService btcWalletService,
|
||||
XmrWalletService xmrWalletService,
|
||||
User user,
|
||||
BalancePresentation balancePresentation,
|
||||
TradePresentation tradePresentation,
|
||||
@ -202,7 +202,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
|
||||
|
||||
TxIdTextField.setPreferences(preferences);
|
||||
|
||||
TxIdTextField.setWalletService(btcWalletService);
|
||||
TxIdTextField.setXmrWalletService(xmrWalletService);
|
||||
|
||||
GUIUtil.setFeeService(feeService);
|
||||
GUIUtil.setPreferences(preferences);
|
||||
|
@ -17,117 +17,60 @@
|
||||
|
||||
package bisq.desktop.main.funds.deposit;
|
||||
|
||||
import bisq.desktop.components.indicator.TxConfidenceIndicator;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
|
||||
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.btc.listeners.XmrBalanceListener;
|
||||
import bisq.core.btc.model.XmrAddressEntry;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
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 java.math.BigInteger;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
@Slf4j
|
||||
class DepositListItem {
|
||||
private final StringProperty balance = new SimpleStringProperty();
|
||||
private final BtcWalletService walletService;
|
||||
private final XmrAddressEntry addressEntry;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private Coin balanceAsCoin;
|
||||
private final String addressString;
|
||||
private String usage = "-";
|
||||
private TxConfidenceListener txConfidenceListener;
|
||||
private BalanceListener balanceListener;
|
||||
private XmrBalanceListener balanceListener;
|
||||
private int numTxOutputs = 0;
|
||||
private final Supplier<LazyFields> lazyFieldsSupplier;
|
||||
|
||||
private static class LazyFields {
|
||||
TxConfidenceIndicator txConfidenceIndicator;
|
||||
Tooltip tooltip;
|
||||
}
|
||||
DepositListItem(XmrAddressEntry addressEntry, XmrWalletService xmrWalletService, CoinFormatter formatter) {
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.addressEntry = addressEntry;
|
||||
|
||||
private LazyFields lazy() {
|
||||
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) {
|
||||
balanceListener = new XmrBalanceListener(addressEntry.getSubaddressIndex()) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balanceAsCoin, Transaction tx) {
|
||||
DepositListItem.this.balanceAsCoin = balanceAsCoin;
|
||||
public void onBalanceChanged(BigInteger balance) {
|
||||
DepositListItem.this.balanceAsCoin = ParsingUtils.atomicUnitsToCoin(balance);
|
||||
DepositListItem.this.balance.set(formatter.formatCoin(balanceAsCoin));
|
||||
var confidence = walletService.getConfidenceForTxId(tx.getTxId().toString());
|
||||
GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator);
|
||||
updateUsage(address);
|
||||
updateUsage(addressEntry.getSubaddressIndex());
|
||||
}
|
||||
};
|
||||
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));
|
||||
|
||||
updateUsage(address);
|
||||
updateUsage(addressEntry.getSubaddressIndex());
|
||||
}
|
||||
|
||||
private void updateUsage(Address address) {
|
||||
numTxOutputs = walletService.getNumTxOutputsForAddress(address);
|
||||
private void updateUsage(int subaddressIndex) {
|
||||
numTxOutputs = xmrWalletService.getNumTxOutputsForSubaddress(addressEntry.getSubaddressIndex());
|
||||
usage = numTxOutputs == 0 ? Res.get("funds.deposit.unused") : Res.get("funds.deposit.usedInTx", numTxOutputs);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
walletService.removeTxConfidenceListener(txConfidenceListener);
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
public TxConfidenceIndicator getTxConfidenceIndicator() {
|
||||
return lazy().txConfidenceIndicator;
|
||||
xmrWalletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
public String getAddressString() {
|
||||
return addressString;
|
||||
return addressEntry.getAddressString();
|
||||
}
|
||||
|
||||
public String getUsage() {
|
||||
@ -149,4 +92,8 @@ class DepositListItem {
|
||||
public int getNumTxOutputs() {
|
||||
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.Layout;
|
||||
|
||||
import bisq.core.btc.listeners.BalanceListener;
|
||||
import bisq.core.btc.model.AddressEntry;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.listeners.XmrBalanceListener;
|
||||
import bisq.core.btc.model.XmrAddressEntry;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
@ -41,21 +41,16 @@ import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.DevEnv;
|
||||
import bisq.common.config.Config;
|
||||
import bisq.common.util.Tuple3;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
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.image.ImageType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import monero.wallet.model.MoneroTxConfig;
|
||||
import javafx.fxml.FXML;
|
||||
|
||||
import javafx.scene.control.Button;
|
||||
@ -85,7 +80,7 @@ import javafx.collections.transformation.SortedList;
|
||||
import javafx.util.Callback;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -105,17 +100,16 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
private ImageView qrCodeImageView;
|
||||
private AddressTextField addressTextField;
|
||||
private Button generateNewAddressButton;
|
||||
private CheckBox generateNewAddressSegwitCheckbox;
|
||||
private TitledGroupBg titledGroupBg;
|
||||
private InputTextField amountTextField;
|
||||
|
||||
private final BtcWalletService walletService;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final Preferences preferences;
|
||||
private final CoinFormatter formatter;
|
||||
private String paymentLabelString;
|
||||
private final ObservableList<DepositListItem> observableList = FXCollections.observableArrayList();
|
||||
private final SortedList<DepositListItem> sortedList = new SortedList<>(observableList);
|
||||
private BalanceListener balanceListener;
|
||||
private XmrBalanceListener balanceListener;
|
||||
private Subscription amountTextFieldSubscription;
|
||||
private ChangeListener<DepositListItem> tableViewSelectionListener;
|
||||
private int gridRow = 0;
|
||||
@ -125,10 +119,10 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
private DepositView(BtcWalletService walletService,
|
||||
private DepositView(XmrWalletService xmrWalletService,
|
||||
Preferences preferences,
|
||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter) {
|
||||
this.walletService = walletService;
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.preferences = preferences;
|
||||
this.formatter = formatter;
|
||||
}
|
||||
@ -143,7 +137,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
usageColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.usage")));
|
||||
|
||||
// trigger creation of at least 1 savings address
|
||||
walletService.getFreshAddressEntry();
|
||||
xmrWalletService.getFreshAddressEntry();
|
||||
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
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));
|
||||
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));
|
||||
tableView.getSortOrder().add(usageColumn);
|
||||
tableView.setItems(sortedList);
|
||||
@ -174,7 +168,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow")));
|
||||
qrCodeImageView.setOnMouseClicked(e -> GUIUtil.showFeeInfoBeforeExecute(
|
||||
() -> UserThread.runAfter(
|
||||
() -> new QRCodeWindow(getBitcoinURI()).show(),
|
||||
() -> new QRCodeWindow(getPaymentUri()).show(),
|
||||
200, TimeUnit.MILLISECONDS)));
|
||||
GridPane.setRowIndex(qrCodeImageView, gridRow);
|
||||
GridPane.setRowSpan(qrCodeImageView, 4);
|
||||
@ -201,23 +195,17 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
|
||||
Tuple3<Button, CheckBox, HBox> buttonCheckBoxHBox = addButtonCheckBoxWithBox(gridPane, ++gridRow,
|
||||
Res.get("funds.deposit.generateAddress"),
|
||||
Res.get("funds.deposit.generateAddressSegwit"),
|
||||
null,
|
||||
15);
|
||||
buttonCheckBoxHBox.third.setSpacing(25);
|
||||
generateNewAddressButton = buttonCheckBoxHBox.first;
|
||||
generateNewAddressSegwitCheckbox = buttonCheckBoxHBox.second;
|
||||
generateNewAddressSegwitCheckbox.setAllowIndeterminate(false);
|
||||
generateNewAddressSegwitCheckbox.setSelected(true);
|
||||
|
||||
generateNewAddressButton.setOnAction(event -> {
|
||||
boolean segwit = generateNewAddressSegwitCheckbox.isSelected();
|
||||
NetworkParameters params = Config.baseCurrencyNetworkParameters();
|
||||
boolean hasUnUsedAddress = observableList.stream().anyMatch(e -> e.getNumTxOutputs() == 0
|
||||
&& (Address.fromString(params, e.getAddressString()) instanceof SegwitAddress) == segwit);
|
||||
boolean hasUnUsedAddress = observableList.stream().anyMatch(e -> e.getNumTxOutputs() == 0);
|
||||
if (hasUnUsedAddress) {
|
||||
new Popup().warning(Res.get("funds.deposit.selectUnused")).show();
|
||||
} else {
|
||||
AddressEntry newSavingsAddressEntry = walletService.getFreshAddressEntry(segwit);
|
||||
XmrAddressEntry newSavingsAddressEntry = xmrWalletService.getNewAddressEntry();
|
||||
updateList();
|
||||
observableList.stream()
|
||||
.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
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
public void onBalanceChanged(BigInteger balance) {
|
||||
updateList();
|
||||
}
|
||||
};
|
||||
@ -243,7 +231,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
|
||||
updateList();
|
||||
|
||||
walletService.addBalanceListener(balanceListener);
|
||||
xmrWalletService.addBalanceListener(balanceListener);
|
||||
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
|
||||
addressTextField.setAmountAsCoin(ParsingUtils.parseToCoin(t, formatter));
|
||||
updateQRCode();
|
||||
@ -258,7 +246,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
tableView.getSelectionModel().selectedItemProperty().removeListener(tableViewSelectionListener);
|
||||
sortedList.comparatorProperty().unbind();
|
||||
observableList.forEach(DepositListItem::cleanup);
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
xmrWalletService.removeBalanceListener(balanceListener);
|
||||
amountTextFieldSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
@ -267,7 +255,6 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
// UI handlers
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
private void fillForm(String address) {
|
||||
titledGroupBg.setVisible(true);
|
||||
titledGroupBg.setManaged(true);
|
||||
@ -287,7 +274,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
private void updateQRCode() {
|
||||
if (addressTextField.getAddress() != null && !addressTextField.getAddress().isEmpty()) {
|
||||
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
|
||||
.to(ImageType.PNG)
|
||||
.stream()
|
||||
@ -309,8 +296,8 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
private void updateList() {
|
||||
observableList.forEach(DepositListItem::cleanup);
|
||||
observableList.clear();
|
||||
walletService.getAvailableAddressEntries()
|
||||
.forEach(e -> observableList.add(new DepositListItem(e, walletService, formatter)));
|
||||
xmrWalletService.getAvailableAddressEntries()
|
||||
.forEach(e -> observableList.add(new DepositListItem(e, xmrWalletService, formatter)));
|
||||
}
|
||||
|
||||
private Coin getAmountAsCoin() {
|
||||
@ -318,10 +305,11 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getBitcoinURI() {
|
||||
return GUIUtil.getBitcoinURI(addressTextField.getAddress(),
|
||||
getAmountAsCoin(),
|
||||
paymentLabelString);
|
||||
private String getPaymentUri() {
|
||||
return xmrWalletService.getWallet().createPaymentUri(new MoneroTxConfig()
|
||||
.setAddress(addressTextField.getAddress())
|
||||
.setAmount(ParsingUtils.coinToAtomicUnits(getAmountAsCoin()))
|
||||
.setNote(paymentLabelString));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -434,7 +422,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
setGraphic(item.getTxConfidenceIndicator());
|
||||
//setGraphic(item.getTxConfidenceIndicator());
|
||||
} else {
|
||||
setGraphic(null);
|
||||
}
|
||||
|
@ -17,34 +17,34 @@
|
||||
|
||||
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.locale.Res;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.OpenOffer;
|
||||
import bisq.core.trade.Tradable;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import bisq.desktop.components.indicator.TxConfidenceIndicator;
|
||||
import bisq.desktop.util.DisplayUtils;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
|
||||
import javafx.scene.control.Tooltip;
|
||||
|
||||
import java.math.BigInteger;
|
||||
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.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
|
||||
import monero.wallet.model.MoneroIncomingTransfer;
|
||||
import monero.wallet.model.MoneroOutgoingTransfer;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
import monero.wallet.model.MoneroWalletListener;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
@Slf4j
|
||||
class TransactionsListItem {
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final CoinFormatter formatter;
|
||||
private String dateString;
|
||||
private final Date date;
|
||||
@ -54,12 +54,11 @@ class TransactionsListItem {
|
||||
private String details = "";
|
||||
private String addressString = "";
|
||||
private String direction = "";
|
||||
private TxConfidenceListener txConfidenceListener;
|
||||
private boolean received;
|
||||
private boolean detailsAvailable;
|
||||
private Coin amountAsCoin = Coin.ZERO;
|
||||
private String memo = "";
|
||||
private int confirmations = 0;
|
||||
private long confirmations = 0;
|
||||
@Getter
|
||||
private final boolean isDustAttackTx;
|
||||
private boolean initialTxConfidenceVisibility = true;
|
||||
@ -77,187 +76,140 @@ class TransactionsListItem {
|
||||
// used at exportCSV
|
||||
TransactionsListItem() {
|
||||
date = null;
|
||||
xmrWalletService = null;
|
||||
txId = null;
|
||||
formatter = null;
|
||||
isDustAttackTx = false;
|
||||
lazyFieldsSupplier = null;
|
||||
}
|
||||
|
||||
TransactionsListItem(MoneroTxWallet transaction,
|
||||
TransactionsListItem(MoneroTxWallet tx,
|
||||
XmrWalletService xmrWalletService,
|
||||
TransactionAwareTradable transactionAwareTradable,
|
||||
CoinFormatter formatter,
|
||||
long ignoreDustThreshold) {
|
||||
throw new RuntimeException("TransactionsListItem needs updated to use XMR wallet");
|
||||
// this.btcWalletService = btcWalletService;
|
||||
// this.formatter = formatter;
|
||||
// this.memo = transaction.getMemo();
|
||||
//
|
||||
// txId = transaction.getTxId().toString();
|
||||
//
|
||||
// Optional<Tradable> optionalTradable = Optional.ofNullable(transactionAwareTradable)
|
||||
// .map(TransactionAwareTradable::asTradable);
|
||||
//
|
||||
// Coin valueSentToMe = btcWalletService.getValueSentToMeForTransaction(transaction);
|
||||
// Coin valueSentFromMe = btcWalletService.getValueSentFromMeForTransaction(transaction);
|
||||
//
|
||||
// // TODO check and refactor
|
||||
// if (valueSentToMe.isZero()) {
|
||||
// amountAsCoin = valueSentFromMe.multiply(-1);
|
||||
// for (TransactionOutput output : transaction.getOutputs()) {
|
||||
// if (!btcWalletService.isTransactionOutputMine(output)) {
|
||||
// received = false;
|
||||
// if (WalletService.isOutputScriptConvertibleToAddress(output)) {
|
||||
// addressString = WalletService.getAddressStringFromOutput(output);
|
||||
// direction = Res.get("funds.tx.direction.sentTo");
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else if (valueSentFromMe.isZero()) {
|
||||
// amountAsCoin = valueSentToMe;
|
||||
// direction = Res.get("funds.tx.direction.receivedWith");
|
||||
// received = true;
|
||||
// for (TransactionOutput output : transaction.getOutputs()) {
|
||||
// if (btcWalletService.isTransactionOutputMine(output) &&
|
||||
// WalletService.isOutputScriptConvertibleToAddress(output)) {
|
||||
// addressString = WalletService.getAddressStringFromOutput(output);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// amountAsCoin = valueSentToMe.subtract(valueSentFromMe);
|
||||
// boolean outgoing = false;
|
||||
// for (TransactionOutput output : transaction.getOutputs()) {
|
||||
// if (!btcWalletService.isTransactionOutputMine(output)) {
|
||||
// if (WalletService.isOutputScriptConvertibleToAddress(output)) {
|
||||
// addressString = WalletService.getAddressStringFromOutput(output);
|
||||
// outgoing = true;
|
||||
// break;
|
||||
// }
|
||||
// } else {
|
||||
// addressString = WalletService.getAddressStringFromOutput(output);
|
||||
// outgoing = (valueSentToMe.getValue() < valueSentFromMe.getValue());
|
||||
// if (!outgoing) {
|
||||
// direction = Res.get("funds.tx.direction.receivedWith");
|
||||
// received = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (outgoing) {
|
||||
// direction = Res.get("funds.tx.direction.sentTo");
|
||||
// received = false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (optionalTradable.isPresent()) {
|
||||
// tradable = optionalTradable.get();
|
||||
// detailsAvailable = true;
|
||||
// String tradeId = tradable.getShortId();
|
||||
// if (tradable instanceof OpenOffer) {
|
||||
// details = Res.get("funds.tx.createOfferFee", tradeId);
|
||||
// } else if (tradable instanceof Trade) {
|
||||
// Trade trade = (Trade) tradable;
|
||||
// TransactionAwareTrade transactionAwareTrade = (TransactionAwareTrade) transactionAwareTradable;
|
||||
// if (trade.getTakerFeeTxId() != null && trade.getTakerFeeTxId().equals(txId)) {
|
||||
// details = Res.get("funds.tx.takeOfferFee", tradeId);
|
||||
// } else {
|
||||
// Offer offer = trade.getOffer();
|
||||
// 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))) {
|
||||
// details = Res.get("funds.tx.multiSigDeposit", tradeId);
|
||||
// } else if (trade.getPayoutTx() != null &&
|
||||
// trade.getPayoutTx().getTxId().equals(Sha256Hash.wrap(txId))) {
|
||||
// details = Res.get("funds.tx.multiSigPayout", tradeId);
|
||||
//
|
||||
// if (amountAsCoin.isZero()) {
|
||||
// initialTxConfidenceVisibility = false;
|
||||
// }
|
||||
// } else {
|
||||
// Trade.DisputeState disputeState = trade.getDisputeState();
|
||||
// if (disputeState == Trade.DisputeState.DISPUTE_CLOSED) {
|
||||
// if (valueSentToMe.isPositive()) {
|
||||
// details = Res.get("funds.tx.disputePayout", tradeId);
|
||||
// } else {
|
||||
// details = Res.get("funds.tx.disputeLost", tradeId);
|
||||
// initialTxConfidenceVisibility = 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()) {
|
||||
// details = Res.get("funds.tx.refund", tradeId);
|
||||
// } else {
|
||||
// // We have spent the deposit tx outputs to the Bisq donation address to enable
|
||||
// // 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
|
||||
// // left our wallet nor we received funds. So we set indicator invisible.
|
||||
// amountAsCoin = Coin.ZERO;
|
||||
// details = Res.get("funds.tx.collateralForRefund", tradeId);
|
||||
// initialTxConfidenceVisibility = false;
|
||||
// }
|
||||
// } else {
|
||||
// if (transactionAwareTrade.isDelayedPayoutTx(txId)) {
|
||||
// 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);
|
||||
this.formatter = formatter;
|
||||
this.memo = tx.getNote();
|
||||
this.txId = tx.getHash();
|
||||
|
||||
Optional<Tradable> optionalTradable = Optional.ofNullable(transactionAwareTradable)
|
||||
.map(TransactionAwareTradable::asTradable);
|
||||
|
||||
Coin valueSentToMe = ParsingUtils.atomicUnitsToCoin(tx.getIncomingAmount() == null ? new BigInteger("0") : tx.getIncomingAmount());
|
||||
Coin valueSentFromMe = ParsingUtils.atomicUnitsToCoin(tx.getOutgoingAmount() == null ? new BigInteger("0") : tx.getOutgoingAmount());
|
||||
|
||||
if (tx.getTransfers().get(0).isIncoming()) {
|
||||
addressString = ((MoneroIncomingTransfer) tx.getTransfers().get(0)).getAddress();
|
||||
} else {
|
||||
MoneroOutgoingTransfer transfer = (MoneroOutgoingTransfer) tx.getTransfers().get(0);
|
||||
if (transfer.getDestinations() != null) addressString = transfer.getDestinations().get(0).getAddress();
|
||||
else addressString = "unavailable";
|
||||
}
|
||||
|
||||
if (valueSentFromMe.isZero()) {
|
||||
amountAsCoin = valueSentToMe;
|
||||
direction = Res.get("funds.tx.direction.receivedWith");
|
||||
received = true;
|
||||
} else {
|
||||
amountAsCoin = valueSentFromMe.multiply(-1);
|
||||
received = false;
|
||||
direction = Res.get("funds.tx.direction.sentTo");
|
||||
}
|
||||
|
||||
if (optionalTradable.isPresent()) {
|
||||
tradable = optionalTradable.get();
|
||||
detailsAvailable = true;
|
||||
String tradeId = tradable.getShortId();
|
||||
if (tradable instanceof OpenOffer) {
|
||||
details = Res.get("funds.tx.createOfferFee", tradeId);
|
||||
} else if (tradable instanceof Trade) {
|
||||
Trade trade = (Trade) tradable;
|
||||
if (trade.getTakerFeeTxId() != null && trade.getTakerFeeTxId().equals(txId)) {
|
||||
details = Res.get("funds.tx.takeOfferFee", tradeId);
|
||||
} else {
|
||||
Offer offer = trade.getOffer();
|
||||
String offerFeePaymentTxID = offer.getOfferFeePaymentTxId();
|
||||
if (offerFeePaymentTxID != null && offerFeePaymentTxID.equals(txId)) {
|
||||
details = Res.get("funds.tx.createOfferFee", tradeId);
|
||||
} else if (trade.getSelf().getDepositTxHash() != null &&
|
||||
trade.getSelf().getDepositTxHash().equals(txId)) {
|
||||
details = Res.get("funds.tx.multiSigDeposit", tradeId);
|
||||
} else if (trade.getPayoutTxId() != null &&
|
||||
trade.getPayoutTxId().equals(txId)) {
|
||||
details = Res.get("funds.tx.multiSigPayout", tradeId);
|
||||
if (amountAsCoin.isZero()) {
|
||||
initialTxConfidenceVisibility = false;
|
||||
}
|
||||
} else {
|
||||
Trade.DisputeState disputeState = trade.getDisputeState();
|
||||
if (disputeState == Trade.DisputeState.DISPUTE_CLOSED) {
|
||||
if (valueSentToMe.isPositive()) {
|
||||
details = Res.get("funds.tx.disputePayout", tradeId);
|
||||
} else {
|
||||
details = Res.get("funds.tx.disputeLost", tradeId);
|
||||
}
|
||||
} else if (disputeState == Trade.DisputeState.REFUND_REQUEST_CLOSED ||
|
||||
disputeState == Trade.DisputeState.REFUND_REQUESTED ||
|
||||
disputeState == Trade.DisputeState.REFUND_REQUEST_STARTED_BY_PEER) {
|
||||
if (valueSentToMe.isPositive()) {
|
||||
details = Res.get("funds.tx.refund", tradeId);
|
||||
} else {
|
||||
// We have spent the deposit tx outputs to the Bisq donation address to enable
|
||||
// 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
|
||||
// left our wallet nor we received funds. So we set indicator invisible.
|
||||
amountAsCoin = Coin.ZERO;
|
||||
details = Res.get("funds.tx.collateralForRefund", tradeId);
|
||||
initialTxConfidenceVisibility = false;
|
||||
}
|
||||
} else {
|
||||
details = Res.get("funds.tx.unknown", tradeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (amountAsCoin.isZero()) {
|
||||
details = Res.get("funds.tx.noFundsFromDispute");
|
||||
}
|
||||
}
|
||||
|
||||
this.date = new Date(0); // TODO: convert height to date
|
||||
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);
|
||||
|
||||
GUIUtil.updateConfidence(tx, tooltip, txConfidenceIndicator);
|
||||
confirmations = tx.getNumConfirmations();
|
||||
}});
|
||||
|
||||
// listen for tx updates
|
||||
// TODO: this only listens for new blocks, listen for double spend
|
||||
xmrWalletService.addWalletListener(new MoneroWalletListener() {
|
||||
@Override
|
||||
public void onNewBlock(long height) {
|
||||
MoneroTxWallet tx = xmrWalletService.getWallet().getTx(txId);
|
||||
GUIUtil.updateConfidence(tx, lazy().tooltip, lazy().txConfidenceIndicator);
|
||||
confirmations = tx.getNumConfirmations();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
// TODO (woodser): remove wallet listener
|
||||
//xmrWalletService.removeTxConfidenceListener(txConfidenceListener);
|
||||
}
|
||||
|
||||
|
||||
public TxConfidenceIndicator getTxConfidenceIndicator() {
|
||||
return lazy().txConfidenceIndicator;
|
||||
}
|
||||
@ -309,8 +261,8 @@ class TransactionsListItem {
|
||||
return tradable;
|
||||
}
|
||||
|
||||
public String getNumConfirmations() {
|
||||
return String.valueOf(confirmations);
|
||||
public long getNumConfirmations() {
|
||||
return confirmations;
|
||||
}
|
||||
|
||||
public String getMemo() {
|
||||
|
@ -29,10 +29,9 @@ import bisq.desktop.main.overlays.windows.OfferDetailsWindow;
|
||||
import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
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.offer.OpenOffer;
|
||||
import bisq.core.trade.Tradable;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
@ -40,13 +39,10 @@ import bisq.network.p2p.P2PService;
|
||||
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
import org.bitcoinj.wallet.listeners.WalletChangeEventListener;
|
||||
|
||||
import com.googlecode.jcsv.writer.CSVEntryConverter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import monero.wallet.model.MoneroWalletListener;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
@ -77,11 +73,9 @@ import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
|
||||
import javafx.util.Callback;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Comparator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@FxmlView
|
||||
public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@ -100,33 +94,40 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
private final DisplayedTransactions displayedTransactions;
|
||||
private final SortedList<TransactionsListItem> sortedDisplayedTransactions;
|
||||
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final P2PService p2PService;
|
||||
private final CoreMoneroConnectionsService connectionService;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final Preferences preferences;
|
||||
private final TradeDetailsWindow tradeDetailsWindow;
|
||||
private final OfferDetailsWindow offerDetailsWindow;
|
||||
|
||||
private WalletChangeEventListener walletChangeEventListener;
|
||||
|
||||
private EventHandler<KeyEvent> keyEventEventHandler;
|
||||
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
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
private TransactionsView(BtcWalletService btcWalletService,
|
||||
private TransactionsView(XmrWalletService xmrWalletService,
|
||||
P2PService p2PService,
|
||||
CoreMoneroConnectionsService connectionService,
|
||||
Preferences preferences,
|
||||
TradeDetailsWindow tradeDetailsWindow,
|
||||
OfferDetailsWindow offerDetailsWindow,
|
||||
DisplayedTransactionsFactory displayedTransactionsFactory) {
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.p2PService = p2PService;
|
||||
this.connectionService = connectionService;
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.preferences = preferences;
|
||||
this.tradeDetailsWindow = tradeDetailsWindow;
|
||||
this.offerDetailsWindow = offerDetailsWindow;
|
||||
@ -168,16 +169,12 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
addressColumn.setComparator(Comparator.comparing(item -> item.getDirection() + item.getAddressString()));
|
||||
transactionColumn.setComparator(Comparator.comparing(TransactionsListItem::getTxId));
|
||||
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));
|
||||
|
||||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
tableView.getSortOrder().add(dateColumn);
|
||||
|
||||
walletChangeEventListener = wallet -> {
|
||||
displayedTransactions.update();
|
||||
};
|
||||
|
||||
keyEventEventHandler = event -> {
|
||||
// Not intended to be public to users as the feature is not well tested
|
||||
if (Utilities.isAltOrCtrlPressed(KeyCode.R, event)) {
|
||||
@ -202,7 +199,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
tableView.setItems(sortedDisplayedTransactions);
|
||||
displayedTransactions.update();
|
||||
|
||||
btcWalletService.addChangeEventListener(walletChangeEventListener);
|
||||
xmrWalletService.addWalletListener(transactionsUpdater);
|
||||
|
||||
scene = root.getScene();
|
||||
if (scene != null)
|
||||
@ -226,7 +223,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
columns[3] = item.getTxId();
|
||||
columns[4] = item.getAmount();
|
||||
columns[5] = item.getMemo() == null ? "" : item.getMemo();
|
||||
columns[6] = item.getNumConfirmations();
|
||||
columns[6] = String.valueOf(item.getNumConfirmations());
|
||||
return columns;
|
||||
};
|
||||
|
||||
@ -239,7 +236,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
protected void deactivate() {
|
||||
sortedDisplayedTransactions.comparatorProperty().unbind();
|
||||
displayedTransactions.forEach(TransactionsListItem::cleanup);
|
||||
btcWalletService.removeChangeEventListener(walletChangeEventListener);
|
||||
xmrWalletService.removeWalletListener(transactionsUpdater);
|
||||
|
||||
if (scene != null)
|
||||
scene.removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
|
||||
@ -505,49 +502,15 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
@Override
|
||||
public void updateItem(final TransactionsListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty) {
|
||||
TransactionConfidence confidence = btcWalletService.getConfidenceForTxId(item.getTxId());
|
||||
if (confidence != null) {
|
||||
if (confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.PENDING) {
|
||||
if (button == null) {
|
||||
button = new AutoTooltipButton(Res.get("funds.tx.revert"));
|
||||
setGraphic(button);
|
||||
}
|
||||
button.setOnAction(e -> revertTransaction(item.getTxId(), item.getTradable()));
|
||||
} else {
|
||||
setGraphic(null);
|
||||
if (button != null) {
|
||||
button.setOnAction(null);
|
||||
button = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setGraphic(null);
|
||||
if (button != null) {
|
||||
button.setOnAction(null);
|
||||
button = null;
|
||||
}
|
||||
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.wallet.XmrWalletService;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
@ -71,7 +72,8 @@ class WithdrawalListItem {
|
||||
}
|
||||
|
||||
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)
|
||||
balanceLabel.setText(formatter.formatCoin(this.balance));
|
||||
}
|
||||
|
@ -26,16 +26,20 @@ import bisq.desktop.components.HyperlinkWithIcon;
|
||||
import bisq.desktop.components.InputTextField;
|
||||
import bisq.desktop.components.TitledGroupBg;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.overlays.windows.TxDetails;
|
||||
import bisq.desktop.main.overlays.windows.WalletPasswordWindow;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
import bisq.desktop.util.Layout;
|
||||
|
||||
import bisq.core.btc.listeners.XmrBalanceListener;
|
||||
import bisq.core.btc.model.XmrAddressEntry;
|
||||
import bisq.core.btc.setup.WalletsSetup;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.TradeManager;
|
||||
import bisq.core.user.DontShowAgainLookup;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
@ -43,17 +47,15 @@ import bisq.core.util.coin.CoinFormatter;
|
||||
import bisq.core.util.validation.BtcAddressValidator;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
import bisq.common.util.Tuple3;
|
||||
import bisq.common.util.Tuple4;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import monero.wallet.model.MoneroTxConfig;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@ -69,7 +71,6 @@ import javafx.scene.control.TableColumn;
|
||||
import javafx.scene.control.TableView;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.Toggle;
|
||||
import javafx.scene.control.ToggleButton;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.GridPane;
|
||||
@ -88,12 +89,12 @@ import javafx.collections.transformation.SortedList;
|
||||
|
||||
import javafx.util.Callback;
|
||||
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -109,36 +110,27 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
@FXML
|
||||
TableColumn<WithdrawalListItem, WithdrawalListItem> addressColumn, balanceColumn, selectColumn;
|
||||
|
||||
private RadioButton useAllInputsRadioButton, useCustomInputsRadioButton, feeExcludedRadioButton, feeIncludedRadioButton;
|
||||
private RadioButton useAllInputsRadioButton, useCustomInputsRadioButton;
|
||||
private Label amountLabel;
|
||||
private TextField amountTextField, withdrawFromTextField, withdrawToTextField, withdrawMemoTextField, transactionFeeInputTextField;
|
||||
private TextField amountTextField, withdrawFromTextField, withdrawToTextField, withdrawMemoTextField;
|
||||
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final TradeManager tradeManager;
|
||||
private final P2PService p2PService;
|
||||
private final WalletsSetup walletsSetup;
|
||||
private final CoinFormatter formatter;
|
||||
private final Preferences preferences;
|
||||
private final BtcAddressValidator btcAddressValidator;
|
||||
private final WalletPasswordWindow walletPasswordWindow;
|
||||
private final ObservableList<WithdrawalListItem> observableList = FXCollections.observableArrayList();
|
||||
private final SortedList<WithdrawalListItem> sortedList = new SortedList<>(observableList);
|
||||
private final Set<WithdrawalListItem> selectedItems = new HashSet<>();
|
||||
private XmrBalanceListener balanceListener;
|
||||
private Set<String> fromAddresses = new HashSet<>();
|
||||
private Coin totalAvailableAmountOfSelectedItems = Coin.ZERO;
|
||||
private Coin amountAsCoin = Coin.ZERO;
|
||||
private ChangeListener<String> amountListener;
|
||||
private ChangeListener<Boolean> amountFocusListener, useCustomFeeCheckboxListener, transactionFeeFocusedListener;
|
||||
private ChangeListener<Toggle> feeToggleGroupListener, inputsToggleGroupListener;
|
||||
private ChangeListener<Number> transactionFeeChangeListener;
|
||||
private ToggleGroup feeToggleGroup, inputsToggleGroup;
|
||||
private ToggleButton useCustomFee;
|
||||
private ChangeListener<Boolean> amountFocusListener;
|
||||
private ChangeListener<Toggle> inputsToggleGroupListener;
|
||||
private ToggleGroup inputsToggleGroup;
|
||||
private final BooleanProperty useAllInputs = new SimpleBooleanProperty(true);
|
||||
private boolean feeExcluded;
|
||||
private int rowIndex = 0;
|
||||
private final FeeService feeService;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor, lifecycle
|
||||
@ -154,16 +146,11 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
BtcAddressValidator btcAddressValidator,
|
||||
WalletPasswordWindow walletPasswordWindow,
|
||||
FeeService feeService) {
|
||||
// throw new RuntimeException("WithdrawalView needs updated to use XMR wallet");
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.tradeManager = tradeManager;
|
||||
this.p2PService = p2PService;
|
||||
this.walletsSetup = walletsSetup;
|
||||
this.formatter = formatter;
|
||||
this.preferences = preferences;
|
||||
this.btcAddressValidator = btcAddressValidator;
|
||||
this.walletPasswordWindow = walletPasswordWindow;
|
||||
this.feeService = feeService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -189,20 +176,13 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
useAllInputsRadioButton = labelRadioButtonRadioButtonTuple3.second;
|
||||
useCustomInputsRadioButton = labelRadioButtonRadioButtonTuple3.third;
|
||||
|
||||
feeToggleGroup = new ToggleGroup();
|
||||
|
||||
final Tuple4<Label, TextField, RadioButton, RadioButton> feeTuple3 = addTopLabelTextFieldRadioButtonRadioButton(gridPane, ++rowIndex, feeToggleGroup,
|
||||
final Tuple2<Label, InputTextField> feeTuple3 = addTopLabelInputTextField(gridPane, ++rowIndex,
|
||||
Res.get("funds.withdrawal.receiverAmount", Res.getBaseCurrencyCode()),
|
||||
"",
|
||||
Res.get("funds.withdrawal.feeExcluded"),
|
||||
Res.get("funds.withdrawal.feeIncluded"),
|
||||
0);
|
||||
|
||||
amountLabel = feeTuple3.first;
|
||||
amountTextField = feeTuple3.second;
|
||||
amountTextField.setMinWidth(180);
|
||||
feeExcludedRadioButton = feeTuple3.third;
|
||||
feeIncludedRadioButton = feeTuple3.fourth;
|
||||
|
||||
withdrawFromTextField = addTopLabelTextField(gridPane, ++rowIndex,
|
||||
Res.get("funds.withdrawal.fromLabel", Res.getBaseCurrencyCode())).second;
|
||||
@ -213,52 +193,6 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
withdrawMemoTextField = addTopLabelInputTextField(gridPane, ++rowIndex,
|
||||
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);
|
||||
|
||||
withdrawButton.setOnAction(event -> onWithdraw());
|
||||
@ -304,14 +238,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
amountTextField.setText("");
|
||||
}
|
||||
};
|
||||
feeExcludedRadioButton.setToggleGroup(feeToggleGroup);
|
||||
feeIncludedRadioButton.setToggleGroup(feeToggleGroup);
|
||||
feeToggleGroupListener = (observable, oldValue, newValue) -> {
|
||||
feeExcluded = newValue == feeExcludedRadioButton;
|
||||
amountLabel.setText(feeExcluded ?
|
||||
Res.get("funds.withdrawal.receiverAmount") :
|
||||
Res.get("funds.withdrawal.senderAmount"));
|
||||
};
|
||||
amountLabel.setText(Res.get("funds.withdrawal.receiverAmount"));
|
||||
}
|
||||
|
||||
private void updateInputSelection() {
|
||||
@ -333,22 +260,11 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
amountTextField.textProperty().addListener(amountListener);
|
||||
amountTextField.focusedProperty().addListener(amountFocusListener);
|
||||
xmrWalletService.addBalanceListener(balanceListener);
|
||||
feeToggleGroup.selectedToggleProperty().addListener(feeToggleGroupListener);
|
||||
inputsToggleGroup.selectedToggleProperty().addListener(inputsToggleGroupListener);
|
||||
|
||||
if (feeToggleGroup.getSelectedToggle() == null)
|
||||
feeToggleGroup.selectToggle(feeIncludedRadioButton);
|
||||
|
||||
if (inputsToggleGroup.getSelectedToggle() == null)
|
||||
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();
|
||||
GUIUtil.requestFocus(withdrawToTextField);
|
||||
}
|
||||
@ -360,12 +276,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
xmrWalletService.removeBalanceListener(balanceListener);
|
||||
amountTextField.textProperty().removeListener(amountListener);
|
||||
amountTextField.focusedProperty().removeListener(amountFocusListener);
|
||||
feeToggleGroup.selectedToggleProperty().removeListener(feeToggleGroupListener);
|
||||
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() {
|
||||
throw new RuntimeException("WithdrawalView.onWithdraw() not updated to XMR");
|
||||
// if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, walletsSetup)) {
|
||||
// try {
|
||||
// final String withdrawToAddress = withdrawToTextField.getText();
|
||||
// final Coin sendersAmount;
|
||||
//
|
||||
// // We do not know sendersAmount if senderPaysFee is true. We repeat fee calculation after first attempt if senderPaysFee is true.
|
||||
// Transaction feeEstimationTransaction = btcWalletService.getFeeEstimationTransactionForMultipleAddresses(fromAddresses, amountAsCoin);
|
||||
// if (feeExcluded && feeEstimationTransaction != null) {
|
||||
// feeEstimationTransaction = btcWalletService.getFeeEstimationTransactionForMultipleAddresses(fromAddresses, amountAsCoin.add(feeEstimationTransaction.getFee()));
|
||||
// }
|
||||
// checkNotNull(feeEstimationTransaction, "feeEstimationTransaction must not be null");
|
||||
//
|
||||
// Coin dust = btcWalletService.getDust(feeEstimationTransaction);
|
||||
// Coin fee = feeEstimationTransaction.getFee().add(dust);
|
||||
// Coin receiverAmount;
|
||||
// // amountAsCoin is what the user typed into the withdrawal field.
|
||||
// // this can be interpreted as either the senders amount or receivers amount depending
|
||||
// // on a radio button "fee excluded / fee included".
|
||||
// // therefore we calculate the actual sendersAmount and receiverAmount as follows:
|
||||
// if (feeExcluded) {
|
||||
// receiverAmount = amountAsCoin;
|
||||
// sendersAmount = receiverAmount.add(fee);
|
||||
// } else {
|
||||
// sendersAmount = amountAsCoin.add(dust); // sendersAmount bumped up to UTXO size when dust is in play
|
||||
// receiverAmount = sendersAmount.subtract(fee);
|
||||
// }
|
||||
// if (dust.isPositive()) {
|
||||
// log.info("Dust output ({} satoshi) was detected, the dust amount has been added to the fee (was {}, now {})",
|
||||
// dust.value,
|
||||
// feeEstimationTransaction.getFee(),
|
||||
// fee.value);
|
||||
// }
|
||||
//
|
||||
// if (areInputsValid(sendersAmount)) {
|
||||
// int txVsize = feeEstimationTransaction.getVsize();
|
||||
// log.info("Fee for tx with size {}: {} " + Res.getBaseCurrencyCode() + "", txVsize, fee.toPlainString());
|
||||
//
|
||||
// if (receiverAmount.isPositive()) {
|
||||
// double vkb = txVsize / 1000d;
|
||||
//
|
||||
// String messageText = Res.get("shared.sendFundsDetailsWithFee",
|
||||
// formatter.formatCoinWithCode(sendersAmount),
|
||||
// withdrawFromTextField.getText(),
|
||||
// withdrawToAddress,
|
||||
// formatter.formatCoinWithCode(fee),
|
||||
// Double.parseDouble(transactionFeeInputTextField.getText()),
|
||||
// vkb,
|
||||
// formatter.formatCoinWithCode(receiverAmount));
|
||||
// if (dust.isPositive()) {
|
||||
// messageText = Res.get("shared.sendFundsDetailsDust",
|
||||
// dust.value, dust.value > 1 ? "s" : "")
|
||||
// + messageText;
|
||||
// }
|
||||
//
|
||||
// new Popup().headLine(Res.get("funds.withdrawal.confirmWithdrawalRequest"))
|
||||
// .confirmation(messageText)
|
||||
// .actionButtonText(Res.get("shared.yes"))
|
||||
// .onAction(() -> doWithdraw(sendersAmount, fee, new FutureCallback<>() {
|
||||
// @Override
|
||||
// public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
|
||||
// if (transaction != null) {
|
||||
// String key = "showTransactionSent";
|
||||
// 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();
|
||||
// }
|
||||
// }
|
||||
if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, xmrWalletService.getConnectionsService())) {
|
||||
try {
|
||||
|
||||
// get withdraw address
|
||||
final String withdrawToAddress = withdrawToTextField.getText();
|
||||
|
||||
// get receiver amount
|
||||
Coin receiverAmount = amountAsCoin;
|
||||
if (!receiverAmount.isPositive()) throw new RuntimeException(Res.get("portfolio.pending.step5_buyer.amountTooLow"));
|
||||
|
||||
// create tx
|
||||
MoneroTxWallet tx = xmrWalletService.getWallet().createTx(new MoneroTxConfig()
|
||||
.setAccountIndex(0)
|
||||
.setAmount(ParsingUtils.coinToAtomicUnits(receiverAmount)) // TODO: rename to centinerosToAtomicUnits()?
|
||||
.setAddress(withdrawToAddress));
|
||||
|
||||
// create confirmation message
|
||||
Coin fee = ParsingUtils.atomicUnitsToCoin(tx.getFee());
|
||||
Coin sendersAmount = receiverAmount.add(fee);
|
||||
String messageText = Res.get("shared.sendFundsDetailsWithFee",
|
||||
formatter.formatCoinWithCode(sendersAmount),
|
||||
withdrawFromTextField.getText(),
|
||||
withdrawToAddress,
|
||||
formatter.formatCoinWithCode(fee),
|
||||
formatter.formatCoinWithCode(receiverAmount));
|
||||
|
||||
// popup confirmation message
|
||||
new Popup().headLine(Res.get("funds.withdrawal.confirmWithdrawalRequest"))
|
||||
.confirmation(messageText)
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.onAction(() -> {
|
||||
|
||||
// relay tx
|
||||
try {
|
||||
xmrWalletService.getWallet().relayTx(tx);
|
||||
String key = "showTransactionSent";
|
||||
if (DontShowAgainLookup.showAgain(key)) {
|
||||
new TxDetails(tx.getHash(), withdrawToAddress, formatter.formatCoinWithCode(sendersAmount))
|
||||
.dontShowAgainId(key)
|
||||
.show();
|
||||
}
|
||||
log.debug("onWithdraw onSuccess tx ID:{}", tx.getHash());
|
||||
|
||||
List<Trade> trades = new ArrayList<>(tradeManager.getObservableList());
|
||||
trades.stream()
|
||||
.filter(Trade::isPayoutPublished)
|
||||
.forEach(trade -> xmrWalletService.getAddressEntry(trade.getId(), XmrAddressEntry.Context.TRADE_PAYOUT)
|
||||
.ifPresent(addressEntry -> {
|
||||
if (xmrWalletService.getBalanceForAddress(addressEntry.getAddressString()).isZero())
|
||||
tradeManager.onTradeCompleted(trade);
|
||||
}));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
})
|
||||
.closeButtonText(Res.get("shared.cancel"))
|
||||
.show();
|
||||
} catch (Throwable e) {
|
||||
if (e.getMessage().contains("enough")) new Popup().warning(Res.get("funds.withdrawal.warn.amountExceeds") + "\n\nError message:\n" + e.getMessage()).show();
|
||||
else {
|
||||
e.printStackTrace();
|
||||
log.error(e.toString());
|
||||
new Popup().warning(e.toString()).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void selectForWithdrawal(WithdrawalListItem item) {
|
||||
@ -484,10 +359,6 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
else
|
||||
selectedItems.remove(item);
|
||||
|
||||
fromAddresses = selectedItems.stream()
|
||||
.map(WithdrawalListItem::getAddressString)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (!selectedItems.isEmpty()) {
|
||||
totalAvailableAmountOfSelectedItems = Coin.valueOf(selectedItems.stream().mapToLong(e -> e.getBalance().getValue()).sum());
|
||||
if (totalAvailableAmountOfSelectedItems.isPositive()) {
|
||||
@ -533,7 +404,6 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void updateList() {
|
||||
//throw new RuntimeException("WithdrawalView.updateList() needs updated to use XMR");
|
||||
observableList.forEach(WithdrawalListItem::cleanup);
|
||||
observableList.setAll(xmrWalletService.getAddressEntriesForAvailableBalanceStream()
|
||||
.map(addressEntry -> new WithdrawalListItem(addressEntry, xmrWalletService, formatter))
|
||||
@ -542,51 +412,6 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
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() {
|
||||
withdrawFromTextField.setText("");
|
||||
withdrawFromTextField.setPromptText(Res.get("funds.withdrawal.selectAddress"));
|
||||
@ -603,37 +428,10 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
withdrawMemoTextField.setText("");
|
||||
withdrawMemoTextField.setPromptText(Res.get("funds.withdrawal.memo"));
|
||||
|
||||
transactionFeeInputTextField.setText("");
|
||||
transactionFeeInputTextField.setPromptText(Res.get("funds.withdrawal.useCustomFeeValueInfo"));
|
||||
|
||||
selectedItems.clear();
|
||||
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
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -21,7 +21,7 @@ import bisq.desktop.components.TxIdTextField;
|
||||
import bisq.desktop.main.shared.PriceFeedComboBoxItem;
|
||||
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.Res;
|
||||
import bisq.core.locale.TradeCurrency;
|
||||
@ -86,7 +86,7 @@ public class MarketPricePresentation {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public MarketPricePresentation(BtcWalletService btcWalletService,
|
||||
public MarketPricePresentation(XmrWalletService xmrWalletService,
|
||||
PriceFeedService priceFeedService,
|
||||
Preferences preferences,
|
||||
FeeService feeService) {
|
||||
@ -96,7 +96,7 @@ public class MarketPricePresentation {
|
||||
TxIdTextField.setPreferences(preferences);
|
||||
|
||||
// TODO
|
||||
TxIdTextField.setWalletService(btcWalletService);
|
||||
TxIdTextField.setXmrWalletService(xmrWalletService);
|
||||
|
||||
GUIUtil.setFeeService(feeService);
|
||||
}
|
||||
|
@ -1047,11 +1047,12 @@ public class FormBuilder {
|
||||
String checkBoxTitle,
|
||||
double top) {
|
||||
Button button = new AutoTooltipButton(buttonTitle);
|
||||
CheckBox checkBox = new AutoTooltipCheckBox(checkBoxTitle);
|
||||
CheckBox checkBox = checkBoxTitle == null ? null : new AutoTooltipCheckBox(checkBoxTitle);
|
||||
|
||||
HBox hBox = new HBox(20);
|
||||
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);
|
||||
hBox.setPadding(new Insets(top, 0, 0, 0));
|
||||
gridPane.getChildren().add(hBox);
|
||||
|
@ -32,6 +32,7 @@ import bisq.core.account.witness.AccountAgeWitness;
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.api.CoreMoneroConnectionsService;
|
||||
import bisq.core.app.HavenoSetup;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.Country;
|
||||
import bisq.core.locale.CountryUtil;
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
@ -134,7 +135,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
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,
|
||||
TxConfidenceIndicator txConfidenceIndicator) {
|
||||
if (confidence != null) {
|
||||
switch (confidence.getConfidenceType()) {
|
||||
case UNKNOWN:
|
||||
tooltip.setText(Res.get("confidence.unknown"));
|
||||
txConfidenceIndicator.setProgress(0);
|
||||
break;
|
||||
case PENDING:
|
||||
tooltip.setText(Res.get("confidence.seen", confidence.numBroadcastPeers()));
|
||||
txConfidenceIndicator.setProgress(-1.0);
|
||||
break;
|
||||
case BUILDING:
|
||||
tooltip.setText(Res.get("confidence.confirmed", confidence.getDepthInBlocks()));
|
||||
txConfidenceIndicator.setProgress(Math.min(1, confidence.getDepthInBlocks() / 6.0));
|
||||
break;
|
||||
case DEAD:
|
||||
tooltip.setText(Res.get("confidence.invalid"));
|
||||
txConfidenceIndicator.setProgress(0);
|
||||
break;
|
||||
if (tx != null) {
|
||||
if (!tx.isRelayed()) {
|
||||
tooltip.setText(Res.get("confidence.unknown"));
|
||||
txConfidenceIndicator.setProgress(0);
|
||||
} else if (tx.isFailed()) {
|
||||
tooltip.setText(Res.get("confidence.invalid"));
|
||||
txConfidenceIndicator.setProgress(0);
|
||||
} else if (tx.isConfirmed()) {
|
||||
tooltip.setText(Res.get("confidence.confirmed", tx.getNumConfirmations()));
|
||||
txConfidenceIndicator.setProgress(Math.min(1, tx.getNumConfirmations() / (double) XmrWalletService.NUM_BLOCKS_UNLOCK));
|
||||
} else {
|
||||
tooltip.setText(Res.get("confidence.seen", 0)); // TODO: replace with numBroadcastPeers
|
||||
txConfidenceIndicator.setProgress(-1.0);
|
||||
}
|
||||
|
||||
txConfidenceIndicator.setPrefSize(24, 24);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void openWebPage(String target) {
|
||||
openWebPage(target, true, null);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user