mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-07-27 17:05:29 -04:00
Savings wallet (WIP)
This commit is contained in:
parent
1d57bd194e
commit
f36d8241f3
10 changed files with 212 additions and 186 deletions
|
@ -17,26 +17,21 @@
|
||||||
|
|
||||||
package io.bitsquare.btc;
|
package io.bitsquare.btc;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import org.bitcoinj.core.Address;
|
||||||
import org.bitcoinj.core.*;
|
import org.bitcoinj.core.NetworkParameters;
|
||||||
import org.bitcoinj.wallet.CoinSelection;
|
import org.bitcoinj.core.TransactionOutput;
|
||||||
import org.bitcoinj.wallet.CoinSelector;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.math.BigInteger;
|
import java.util.Set;
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We use a specialized version of the CoinSelector based on the DefaultCoinSelector implementation.
|
* We use a specialized version of the CoinSelector based on the DefaultCoinSelector implementation.
|
||||||
* We lookup for spendable outputs which matches our address of our addressEntry.
|
* We lookup for spendable outputs which matches our address of our addressEntry.
|
||||||
*/
|
*/
|
||||||
class AddressBasedCoinSelector implements CoinSelector {
|
class AddressBasedCoinSelector extends SavingsWalletCoinSelector {
|
||||||
private static final Logger log = LoggerFactory.getLogger(AddressBasedCoinSelector.class);
|
private static final Logger log = LoggerFactory.getLogger(AddressBasedCoinSelector.class);
|
||||||
private final NetworkParameters params;
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Set<AddressEntry> addressEntries;
|
private Set<AddressEntry> addressEntries;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -47,62 +42,22 @@ class AddressBasedCoinSelector implements CoinSelector {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public AddressBasedCoinSelector(NetworkParameters params) {
|
public AddressBasedCoinSelector(NetworkParameters params) {
|
||||||
this.params = params;
|
super(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddressBasedCoinSelector(NetworkParameters params, @Nullable AddressEntry addressEntry) {
|
public AddressBasedCoinSelector(NetworkParameters params, @Nullable AddressEntry addressEntry) {
|
||||||
this.params = params;
|
super(params);
|
||||||
this.addressEntry = addressEntry;
|
this.addressEntry = addressEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddressBasedCoinSelector(NetworkParameters params, Set<AddressEntry> addressEntries) {
|
public AddressBasedCoinSelector(NetworkParameters params, @Nullable Set<AddressEntry> addressEntries) {
|
||||||
this.params = params;
|
super(params);
|
||||||
this.addressEntries = addressEntries;
|
this.addressEntries = addressEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@Override
|
||||||
private static void sortOutputs(ArrayList<TransactionOutput> outputs) {
|
protected boolean matchesRequirement(TransactionOutput transactionOutput) {
|
||||||
Collections.sort(outputs, (a, b) -> {
|
if (transactionOutput.getScriptPubKey().isSentToAddress() || transactionOutput.getScriptPubKey().isPayToScriptHash()) {
|
||||||
int depth1 = a.getParentTransactionDepthInBlocks();
|
|
||||||
int depth2 = b.getParentTransactionDepthInBlocks();
|
|
||||||
Coin aValue = a.getValue();
|
|
||||||
Coin bValue = b.getValue();
|
|
||||||
BigInteger aCoinDepth = BigInteger.valueOf(aValue.value).multiply(BigInteger.valueOf(depth1));
|
|
||||||
BigInteger bCoinDepth = BigInteger.valueOf(bValue.value).multiply(BigInteger.valueOf(depth2));
|
|
||||||
int c1 = bCoinDepth.compareTo(aCoinDepth);
|
|
||||||
if (c1 != 0) return c1;
|
|
||||||
// The "coin*days" destroyed are equal, sort by value alone to get the lowest transaction size.
|
|
||||||
int c2 = bValue.compareTo(aValue);
|
|
||||||
if (c2 != 0) return c2;
|
|
||||||
// They are entirely equivalent (possibly pending) so sort by hash to ensure a total ordering.
|
|
||||||
checkNotNull(a.getParentTransactionHash(), "a.getParentTransactionHash() must not be null");
|
|
||||||
checkNotNull(b.getParentTransactionHash(), "b.getParentTransactionHash() must not be null");
|
|
||||||
BigInteger aHash = a.getParentTransactionHash().toBigInteger();
|
|
||||||
BigInteger bHash = b.getParentTransactionHash().toBigInteger();
|
|
||||||
return aHash.compareTo(bHash);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isInBlockChainOrPending(Transaction tx) {
|
|
||||||
// Pick chain-included transactions and transactions that are pending.
|
|
||||||
TransactionConfidence confidence = tx.getConfidence();
|
|
||||||
TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
|
|
||||||
|
|
||||||
log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers());
|
|
||||||
return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
|
|
||||||
type.equals(TransactionConfidence.ConfidenceType.PENDING);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sub-classes can override this to just customize whether transactions are usable, but keep age sorting.
|
|
||||||
*/
|
|
||||||
private boolean shouldSelect(Transaction tx) {
|
|
||||||
return isInBlockChainOrPending(tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchesRequiredAddress(TransactionOutput transactionOutput) {
|
|
||||||
if (transactionOutput.getScriptPubKey().isSentToAddress() || transactionOutput.getScriptPubKey().isPayToScriptHash
|
|
||||||
()) {
|
|
||||||
Address addressOutput = transactionOutput.getScriptPubKey().getToAddress(params);
|
Address addressOutput = transactionOutput.getScriptPubKey().getToAddress(params);
|
||||||
log.trace("matchesRequiredAddress(es)?");
|
log.trace("matchesRequiredAddress(es)?");
|
||||||
log.trace(addressOutput.toString());
|
log.trace(addressOutput.toString());
|
||||||
|
@ -123,48 +78,9 @@ class AddressBasedCoinSelector implements CoinSelector {
|
||||||
|
|
||||||
log.trace("No match found at matchesRequiredAddress addressOutput / addressEntries " + addressOutput.toString
|
log.trace("No match found at matchesRequiredAddress addressOutput / addressEntries " + addressOutput.toString
|
||||||
() + " / " + addressEntries.toString());
|
() + " / " + addressEntries.toString());
|
||||||
} else {
|
|
||||||
// use savings wallet
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public CoinSelection select(Coin target, List<TransactionOutput> candidates) {
|
|
||||||
log.trace("candidates.size: " + candidates.size());
|
|
||||||
long targetAsLong = target.longValue();
|
|
||||||
log.trace("value needed: " + targetAsLong);
|
|
||||||
HashSet<TransactionOutput> selected = new HashSet<>();
|
|
||||||
// Sort the inputs by age*value so we get the highest "coindays" spent.
|
|
||||||
ArrayList<TransactionOutput> sortedOutputs = new ArrayList<>(candidates);
|
|
||||||
// When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting
|
|
||||||
// them in order to improve performance.
|
|
||||||
if (!target.equals(NetworkParameters.MAX_MONEY)) {
|
|
||||||
sortOutputs(sortedOutputs);
|
|
||||||
}
|
|
||||||
// Now iterate over the sorted outputs until we have got as close to the target as possible or a little
|
|
||||||
// bit over (excessive value will be change).
|
|
||||||
long total = 0;
|
|
||||||
for (TransactionOutput output : sortedOutputs) {
|
|
||||||
if (total >= targetAsLong) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Only pick chain-included transactions, or transactions that are ours and pending.
|
|
||||||
// Only select outputs from our defined address(es)
|
|
||||||
if (!shouldSelect(output.getParentTransaction()) || !matchesRequiredAddress(output)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
selected.add(output);
|
|
||||||
total += output.getValue().longValue();
|
|
||||||
|
|
||||||
log.debug("adding up outputs: output/total: " + output.getValue().longValue() + "/" + total);
|
|
||||||
}
|
|
||||||
// Total may be lower than target here, if the given candidates were insufficient to create to requested
|
|
||||||
// transaction.
|
|
||||||
return new CoinSelection(Coin.valueOf(total), selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bitsquare.
|
||||||
|
*
|
||||||
|
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.bitsquare.btc;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.bitcoinj.core.*;
|
||||||
|
import org.bitcoinj.wallet.CoinSelection;
|
||||||
|
import org.bitcoinj.wallet.CoinSelector;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We use a specialized version of the CoinSelector based on the DefaultCoinSelector implementation.
|
||||||
|
* We lookup for spendable outputs which matches our address of our addressEntry.
|
||||||
|
*/
|
||||||
|
class SavingsWalletCoinSelector implements CoinSelector {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(SavingsWalletCoinSelector.class);
|
||||||
|
protected final NetworkParameters params;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public SavingsWalletCoinSelector(NetworkParameters params) {
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
private static void sortOutputs(ArrayList<TransactionOutput> outputs) {
|
||||||
|
Collections.sort(outputs, (a, b) -> {
|
||||||
|
int depth1 = a.getParentTransactionDepthInBlocks();
|
||||||
|
int depth2 = b.getParentTransactionDepthInBlocks();
|
||||||
|
Coin aValue = a.getValue();
|
||||||
|
Coin bValue = b.getValue();
|
||||||
|
BigInteger aCoinDepth = BigInteger.valueOf(aValue.value).multiply(BigInteger.valueOf(depth1));
|
||||||
|
BigInteger bCoinDepth = BigInteger.valueOf(bValue.value).multiply(BigInteger.valueOf(depth2));
|
||||||
|
int c1 = bCoinDepth.compareTo(aCoinDepth);
|
||||||
|
if (c1 != 0) return c1;
|
||||||
|
// The "coin*days" destroyed are equal, sort by value alone to get the lowest transaction size.
|
||||||
|
int c2 = bValue.compareTo(aValue);
|
||||||
|
if (c2 != 0) return c2;
|
||||||
|
// They are entirely equivalent (possibly pending) so sort by hash to ensure a total ordering.
|
||||||
|
checkNotNull(a.getParentTransactionHash(), "a.getParentTransactionHash() must not be null");
|
||||||
|
checkNotNull(b.getParentTransactionHash(), "b.getParentTransactionHash() must not be null");
|
||||||
|
BigInteger aHash = a.getParentTransactionHash().toBigInteger();
|
||||||
|
BigInteger bHash = b.getParentTransactionHash().toBigInteger();
|
||||||
|
return aHash.compareTo(bHash);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isInBlockChainOrPending(Transaction tx) {
|
||||||
|
// Pick chain-included transactions and transactions that are pending.
|
||||||
|
TransactionConfidence confidence = tx.getConfidence();
|
||||||
|
TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
|
||||||
|
|
||||||
|
log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers());
|
||||||
|
return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
|
||||||
|
type.equals(TransactionConfidence.ConfidenceType.PENDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sub-classes can override this to just customize whether transactions are usable, but keep age sorting.
|
||||||
|
*/
|
||||||
|
protected boolean shouldSelect(Transaction tx) {
|
||||||
|
return isInBlockChainOrPending(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean matchesRequirement(TransactionOutput transactionOutput) {
|
||||||
|
return (transactionOutput.getScriptPubKey().isSentToAddress() || transactionOutput.getScriptPubKey().isPayToScriptHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CoinSelection select(Coin target, List<TransactionOutput> candidates) {
|
||||||
|
log.trace("candidates.size: " + candidates.size());
|
||||||
|
long targetAsLong = target.longValue();
|
||||||
|
log.trace("value needed: " + targetAsLong);
|
||||||
|
HashSet<TransactionOutput> selected = new HashSet<>();
|
||||||
|
// Sort the inputs by age*value so we get the highest "coindays" spent.
|
||||||
|
ArrayList<TransactionOutput> sortedOutputs = new ArrayList<>(candidates);
|
||||||
|
// When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting
|
||||||
|
// them in order to improve performance.
|
||||||
|
if (!target.equals(NetworkParameters.MAX_MONEY)) {
|
||||||
|
sortOutputs(sortedOutputs);
|
||||||
|
}
|
||||||
|
// Now iterate over the sorted outputs until we have got as close to the target as possible or a little
|
||||||
|
// bit over (excessive value will be change).
|
||||||
|
long total = 0;
|
||||||
|
for (TransactionOutput output : sortedOutputs) {
|
||||||
|
if (total >= targetAsLong) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Only pick chain-included transactions, or transactions that are ours and pending.
|
||||||
|
// Only select outputs from our defined address(es)
|
||||||
|
if (!shouldSelect(output.getParentTransaction()) || !matchesRequirement(output)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
selected.add(output);
|
||||||
|
total += output.getValue().longValue();
|
||||||
|
|
||||||
|
log.debug("adding up outputs: output/total: " + output.getValue().longValue() + "/" + total);
|
||||||
|
}
|
||||||
|
// Total may be lower than target here, if the given candidates were insufficient to create to requested
|
||||||
|
// transaction.
|
||||||
|
return new CoinSelection(Coin.valueOf(total), selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -158,7 +158,7 @@ public class TradeWalletService {
|
||||||
sendRequest.shuffleOutputs = false;
|
sendRequest.shuffleOutputs = false;
|
||||||
sendRequest.aesKey = aesKey;
|
sendRequest.aesKey = aesKey;
|
||||||
if (useSavingsWallet)
|
if (useSavingsWallet)
|
||||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params);
|
sendRequest.coinSelector = new SavingsWalletCoinSelector(params);
|
||||||
else
|
else
|
||||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry);
|
sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry);
|
||||||
// We use a fixed fee
|
// We use a fixed fee
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
package io.bitsquare.gui.components;
|
package io.bitsquare.gui.components;
|
||||||
|
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.btc.listeners.BalanceListener;
|
|
||||||
import io.bitsquare.gui.util.BSFormatter;
|
import io.bitsquare.gui.util.BSFormatter;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.effect.BlurType;
|
import javafx.scene.effect.BlurType;
|
||||||
|
@ -26,14 +25,11 @@ import javafx.scene.effect.DropShadow;
|
||||||
import javafx.scene.effect.Effect;
|
import javafx.scene.effect.Effect;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import org.bitcoinj.core.Address;
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.core.Transaction;
|
|
||||||
|
|
||||||
public class BalanceTextField extends AnchorPane {
|
public class BalanceTextField extends AnchorPane {
|
||||||
|
|
||||||
private static WalletService walletService;
|
private static WalletService walletService;
|
||||||
private BalanceListener balanceListener;
|
|
||||||
private Coin targetAmount;
|
private Coin targetAmount;
|
||||||
|
|
||||||
public static void setWalletService(WalletService walletService) {
|
public static void setWalletService(WalletService walletService) {
|
||||||
|
@ -65,22 +61,6 @@ public class BalanceTextField extends AnchorPane {
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupBalanceListener(Address address) {
|
|
||||||
balanceListener = new BalanceListener(address) {
|
|
||||||
@Override
|
|
||||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
|
||||||
updateBalance(balance);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
walletService.addBalanceListener(balanceListener);
|
|
||||||
updateBalance(walletService.getBalanceForAddress(address));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cleanup() {
|
|
||||||
if (balanceListener != null)
|
|
||||||
walletService.removeBalanceListener(balanceListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBalance(Coin balance) {
|
public void setBalance(Coin balance) {
|
||||||
updateBalance(balance);
|
updateBalance(balance);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import io.bitsquare.gui.util.BSFormatter;
|
||||||
import io.bitsquare.gui.util.Layout;
|
import io.bitsquare.gui.util.Layout;
|
||||||
import io.bitsquare.user.Preferences;
|
import io.bitsquare.user.Preferences;
|
||||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.collections.transformation.SortedList;
|
import javafx.collections.transformation.SortedList;
|
||||||
|
@ -91,6 +92,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
private InputTextField amountTextField;
|
private InputTextField amountTextField;
|
||||||
private Subscription amountTextFieldSubscription;
|
private Subscription amountTextFieldSubscription;
|
||||||
private String paymentLabel;
|
private String paymentLabel;
|
||||||
|
private ChangeListener<DepositListItem> tableViewSelectionListener;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -108,8 +110,15 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
// trigger creation of at least 1 savings address
|
||||||
|
walletService.getUnusedSavingsAddressEntry();
|
||||||
|
|
||||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
tableView.setPlaceholder(new Label("No deposit addresses are generated yet"));
|
tableView.setPlaceholder(new Label("No deposit addresses are generated yet"));
|
||||||
|
tableViewSelectionListener = (observableValue, oldValue, newValue) -> {
|
||||||
|
if (newValue != null)
|
||||||
|
fillForm(newValue.getAddressString());
|
||||||
|
};
|
||||||
|
|
||||||
setSelectColumnCellFactory();
|
setSelectColumnCellFactory();
|
||||||
setAddressColumnCellFactory();
|
setAddressColumnCellFactory();
|
||||||
|
@ -174,12 +183,17 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
generateNewAddressButton.setOnAction(event -> {
|
generateNewAddressButton.setOnAction(event -> {
|
||||||
boolean hasUnUsedAddress = observableList.stream().filter(e -> e.getNumTxOutputs() == 0).findAny().isPresent();
|
boolean hasUnUsedAddress = observableList.stream().filter(e -> e.getNumTxOutputs() == 0).findAny().isPresent();
|
||||||
if (hasUnUsedAddress) {
|
if (hasUnUsedAddress) {
|
||||||
new Popup().warning("You have addresses which are not used in any transaction.\n" +
|
new Popup().warning("You have already at least one address which is not used yet in any transaction.\n" +
|
||||||
"Please select in the address table any unused address.").show();
|
"Please select in the address table an unused address.").show();
|
||||||
} else {
|
} else {
|
||||||
AddressEntry newSavingsAddressEntry = walletService.getNewSavingsAddressEntry();
|
AddressEntry newSavingsAddressEntry = walletService.getNewSavingsAddressEntry();
|
||||||
fillForm(newSavingsAddressEntry.getAddressString());
|
//fillForm(newSavingsAddressEntry.getAddressString());
|
||||||
updateList();
|
updateList();
|
||||||
|
observableList.stream()
|
||||||
|
.filter(depositListItem -> depositListItem.getAddressString().equals(newSavingsAddressEntry.getAddressString()))
|
||||||
|
.findAny()
|
||||||
|
.ifPresent(depositListItem -> tableView.getSelectionModel().select(depositListItem));
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -212,6 +226,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void activate() {
|
protected void activate() {
|
||||||
|
tableView.getSelectionModel().selectedItemProperty().addListener(tableViewSelectionListener);
|
||||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||||
tableView.setItems(sortedList);
|
tableView.setItems(sortedList);
|
||||||
updateList();
|
updateList();
|
||||||
|
@ -225,6 +240,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void deactivate() {
|
protected void deactivate() {
|
||||||
|
tableView.getSelectionModel().selectedItemProperty().removeListener(tableViewSelectionListener);
|
||||||
sortedList.comparatorProperty().unbind();
|
sortedList.comparatorProperty().unbind();
|
||||||
observableList.forEach(DepositListItem::cleanup);
|
observableList.forEach(DepositListItem::cleanup);
|
||||||
walletService.removeBalanceListener(balanceListener);
|
walletService.removeBalanceListener(balanceListener);
|
||||||
|
@ -256,7 +272,6 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
addressTextField.setAddress(address);
|
addressTextField.setAddress(address);
|
||||||
|
|
||||||
updateQRCode();
|
updateQRCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateQRCode() {
|
private void updateQRCode() {
|
||||||
|
@ -343,7 +358,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
if (item != null && !empty) {
|
if (item != null && !empty) {
|
||||||
if (button == null) {
|
if (button == null) {
|
||||||
button = new Button("Select");
|
button = new Button("Select");
|
||||||
button.setOnAction(e -> fillForm(item.getAddressString()));
|
button.setOnAction(e -> tableView.getSelectionModel().select(item));
|
||||||
setGraphic(button);
|
setGraphic(button);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -379,7 +394,10 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
if (item != null && !empty) {
|
if (item != null && !empty) {
|
||||||
String addressString = item.getAddressString();
|
String addressString = item.getAddressString();
|
||||||
field = new HyperlinkWithIcon(addressString, AwesomeIcon.EXTERNAL_LINK);
|
field = new HyperlinkWithIcon(addressString, AwesomeIcon.EXTERNAL_LINK);
|
||||||
field.setOnAction(event -> openBlockExplorer(item));
|
field.setOnAction(event -> {
|
||||||
|
openBlockExplorer(item);
|
||||||
|
tableView.getSelectionModel().select(item);
|
||||||
|
});
|
||||||
field.setTooltip(new Tooltip("Open external blockchain explorer for " +
|
field.setTooltip(new Tooltip("Open external blockchain explorer for " +
|
||||||
"address: " + addressString));
|
"address: " + addressString));
|
||||||
setGraphic(field);
|
setGraphic(field);
|
||||||
|
|
|
@ -178,11 +178,11 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||||
addListeners();
|
addListeners();
|
||||||
|
|
||||||
paymentAccounts.setAll(user.getPaymentAccounts());
|
paymentAccounts.setAll(user.getPaymentAccounts());
|
||||||
calculateTotalToPay();
|
|
||||||
updateBalance();
|
|
||||||
|
|
||||||
if (isTabSelected)
|
if (isTabSelected)
|
||||||
priceFeed.setCurrencyCode(tradeCurrencyCode.get());
|
priceFeed.setCurrencyCode(tradeCurrencyCode.get());
|
||||||
|
|
||||||
|
updateBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -225,6 +225,10 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||||
paymentAccount = account;
|
paymentAccount = account;
|
||||||
|
|
||||||
priceFeed.setCurrencyCode(tradeCurrencyCode.get());
|
priceFeed.setCurrencyCode(tradeCurrencyCode.get());
|
||||||
|
|
||||||
|
calculateVolume();
|
||||||
|
calculateTotalToPay();
|
||||||
|
updateBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTabSelected(boolean isSelected) {
|
void onTabSelected(boolean isSelected) {
|
||||||
|
@ -373,6 +377,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||||
!priceAsFiat.get().isZero()) {
|
!priceAsFiat.get().isZero()) {
|
||||||
volumeAsFiat.set(new ExchangeRate(priceAsFiat.get()).coinToFiat(amountAsCoin.get()));
|
volumeAsFiat.set(new ExchangeRate(priceAsFiat.get()).coinToFiat(amountAsCoin.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateAmount() {
|
void calculateAmount() {
|
||||||
|
@ -402,7 +408,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||||
Coin savingWalletBalance = walletService.getSavingWalletBalance();
|
Coin savingWalletBalance = walletService.getSavingWalletBalance();
|
||||||
totalAvailableBalance = savingWalletBalance.add(tradeWalletBalance);
|
totalAvailableBalance = savingWalletBalance.add(tradeWalletBalance);
|
||||||
|
|
||||||
if (totalAvailableBalance.compareTo(totalToPayAsCoin.get()) > 0)
|
if (totalToPayAsCoin.get() != null && totalAvailableBalance.compareTo(totalToPayAsCoin.get()) > 0)
|
||||||
balance.set(totalToPayAsCoin.get());
|
balance.set(totalToPayAsCoin.get());
|
||||||
else
|
else
|
||||||
balance.set(totalAvailableBalance);
|
balance.set(totalAvailableBalance);
|
||||||
|
@ -410,14 +416,14 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||||
balance.set(tradeWalletBalance);
|
balance.set(tradeWalletBalance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (totalToPayAsCoin.get() != null) {
|
||||||
missingCoin.set(totalToPayAsCoin.get().subtract(balance.get()));
|
missingCoin.set(totalToPayAsCoin.get().subtract(balance.get()));
|
||||||
if (missingCoin.get().isNegative())
|
if (missingCoin.get().isNegative())
|
||||||
missingCoin.set(Coin.ZERO);
|
missingCoin.set(Coin.ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
isWalletFunded.set(isBalanceSufficient(balance.get()));
|
isWalletFunded.set(isBalanceSufficient(balance.get()));
|
||||||
if (isWalletFunded.get()) {
|
if (totalToPayAsCoin.get() != null && isWalletFunded.get() && walletFundedNotification == null) {
|
||||||
//walletService.removeBalanceListener(balanceListener);
|
|
||||||
if (walletFundedNotification == null) {
|
|
||||||
walletFundedNotification = new Notification()
|
walletFundedNotification = new Notification()
|
||||||
.headLine("Trading wallet update")
|
.headLine("Trading wallet update")
|
||||||
.notification("Your trading wallet is sufficiently funded.\n" +
|
.notification("Your trading wallet is sufficiently funded.\n" +
|
||||||
|
@ -427,7 +433,6 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||||
walletFundedNotification.show();
|
walletFundedNotification.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBalanceSufficient(Coin balance) {
|
private boolean isBalanceSufficient(Coin balance) {
|
||||||
return totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0;
|
return totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0;
|
||||||
|
|
|
@ -62,7 +62,6 @@ import javafx.stage.Window;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
import net.glxn.qrgen.QRCode;
|
import net.glxn.qrgen.QRCode;
|
||||||
import net.glxn.qrgen.image.ImageType;
|
import net.glxn.qrgen.image.ImageType;
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
import org.bitcoinj.uri.BitcoinURI;
|
import org.bitcoinj.uri.BitcoinURI;
|
||||||
import org.controlsfx.control.PopOver;
|
import org.controlsfx.control.PopOver;
|
||||||
import org.fxmisc.easybind.EasyBind;
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
@ -120,10 +119,10 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
private ChangeListener<String> tradeCurrencyCodeListener;
|
private ChangeListener<String> tradeCurrencyCodeListener;
|
||||||
private ImageView qrCodeImageView;
|
private ImageView qrCodeImageView;
|
||||||
private ChangeListener<Coin> balanceListener;
|
|
||||||
private HBox fundingHBox;
|
private HBox fundingHBox;
|
||||||
private Subscription isSpinnerVisibleSubscription;
|
private Subscription isSpinnerVisibleSubscription;
|
||||||
private Subscription cancelButton2StyleSubscription;
|
private Subscription cancelButton2StyleSubscription;
|
||||||
|
private Subscription balanceSubscription;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -150,7 +149,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
createListeners();
|
createListeners();
|
||||||
|
|
||||||
balanceTextField.setFormatter(model.getFormatter());
|
balanceTextField.setFormatter(model.getFormatter());
|
||||||
balanceListener = (observable, oldValue, newValue) -> balanceTextField.setBalance(newValue);
|
|
||||||
|
|
||||||
paymentAccountsComboBox.setConverter(new StringConverter<PaymentAccount>() {
|
paymentAccountsComboBox.setConverter(new StringConverter<PaymentAccount>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -185,7 +183,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
|
|
||||||
onPaymentAccountsComboBoxSelected();
|
onPaymentAccountsComboBoxSelected();
|
||||||
|
|
||||||
balanceTextField.setBalance(model.dataModel.balance.get());
|
|
||||||
balanceTextField.setTargetAmount(model.dataModel.totalToPayAsCoin.get());
|
balanceTextField.setTargetAmount(model.dataModel.totalToPayAsCoin.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,9 +194,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
|
|
||||||
if (spinner != null)
|
if (spinner != null)
|
||||||
spinner.setProgress(0);
|
spinner.setProgress(0);
|
||||||
|
|
||||||
if (balanceTextField != null)
|
|
||||||
balanceTextField.cleanup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -487,11 +481,14 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
|
|
||||||
cancelButton2StyleSubscription = EasyBind.subscribe(placeOfferButton.visibleProperty(),
|
cancelButton2StyleSubscription = EasyBind.subscribe(placeOfferButton.visibleProperty(),
|
||||||
isVisible -> cancelButton2.setId(isVisible ? "cancel-button" : null));
|
isVisible -> cancelButton2.setId(isVisible ? "cancel-button" : null));
|
||||||
|
|
||||||
|
balanceSubscription = EasyBind.subscribe(model.dataModel.balance, newValue -> balanceTextField.setBalance(newValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeSubscriptions() {
|
private void removeSubscriptions() {
|
||||||
isSpinnerVisibleSubscription.unsubscribe();
|
isSpinnerVisibleSubscription.unsubscribe();
|
||||||
cancelButton2StyleSubscription.unsubscribe();
|
cancelButton2StyleSubscription.unsubscribe();
|
||||||
|
balanceSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createListeners() {
|
private void createListeners() {
|
||||||
|
@ -600,7 +597,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
|
|
||||||
private void addListeners() {
|
private void addListeners() {
|
||||||
model.tradeCurrencyCode.addListener(tradeCurrencyCodeListener);
|
model.tradeCurrencyCode.addListener(tradeCurrencyCodeListener);
|
||||||
model.dataModel.balance.addListener(balanceListener);
|
|
||||||
|
|
||||||
// focus out
|
// focus out
|
||||||
amountTextField.focusedProperty().addListener(amountFocusedListener);
|
amountTextField.focusedProperty().addListener(amountFocusedListener);
|
||||||
|
@ -624,7 +620,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
|
|
||||||
private void removeListeners() {
|
private void removeListeners() {
|
||||||
model.tradeCurrencyCode.removeListener(tradeCurrencyCodeListener);
|
model.tradeCurrencyCode.removeListener(tradeCurrencyCodeListener);
|
||||||
model.dataModel.balance.removeListener(balanceListener);
|
|
||||||
|
|
||||||
// focus out
|
// focus out
|
||||||
amountTextField.focusedProperty().removeListener(amountFocusedListener);
|
amountTextField.focusedProperty().removeListener(amountFocusedListener);
|
||||||
|
@ -846,9 +841,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
if (model.dataModel.isWalletFunded.get()) {
|
if (model.dataModel.isWalletFunded.get()) {
|
||||||
new Popup().warning("You have already paid in the funds.\n" +
|
new Popup().warning("You have already paid in the funds.\n" +
|
||||||
"Are you sure you want to cancel.")
|
"Are you sure you want to cancel.")
|
||||||
.actionButtonText("No")
|
.closeButtonText("No")
|
||||||
.closeButtonText("Yes, close")
|
.actionButtonText("Yes, cancel")
|
||||||
.onClose(() -> {
|
.onAction(() -> {
|
||||||
close();
|
close();
|
||||||
model.dataModel.swapTradeToSavings();
|
model.dataModel.swapTradeToSavings();
|
||||||
})
|
})
|
||||||
|
|
|
@ -131,7 +131,6 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
||||||
addBindings();
|
addBindings();
|
||||||
addListeners();
|
addListeners();
|
||||||
|
|
||||||
calculateTotalToPay();
|
|
||||||
updateBalance();
|
updateBalance();
|
||||||
|
|
||||||
// TODO In case that we have funded but restarted, or canceled but took again the offer we would need to
|
// TODO In case that we have funded but restarted, or canceled but took again the offer we would need to
|
||||||
|
@ -353,9 +352,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
isWalletFunded.set(isBalanceSufficient(balance.get()));
|
isWalletFunded.set(isBalanceSufficient(balance.get()));
|
||||||
if (isWalletFunded.get()) {
|
if (totalToPayAsCoin.get() != null && isWalletFunded.get() && walletFundedNotification == null) {
|
||||||
// walletService.removeBalanceListener(balanceListener);
|
|
||||||
if (totalToPayAsCoin.get() != null && walletFundedNotification == null) {
|
|
||||||
walletFundedNotification = new Notification()
|
walletFundedNotification = new Notification()
|
||||||
.headLine("Trading wallet update")
|
.headLine("Trading wallet update")
|
||||||
.notification("Your trading wallet is sufficiently funded.\n" +
|
.notification("Your trading wallet is sufficiently funded.\n" +
|
||||||
|
@ -365,7 +362,6 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
||||||
walletFundedNotification.show();
|
walletFundedNotification.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBalanceSufficient(Coin balance) {
|
private boolean isBalanceSufficient(Coin balance) {
|
||||||
return totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0;
|
return totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0;
|
||||||
|
|
|
@ -116,7 +116,6 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
private Subscription balanceSubscription;
|
private Subscription balanceSubscription;
|
||||||
// private Subscription noSufficientFeeSubscription;
|
// private Subscription noSufficientFeeSubscription;
|
||||||
// private MonadicBinding<Boolean> noSufficientFeeBinding;
|
// private MonadicBinding<Boolean> noSufficientFeeBinding;
|
||||||
private Subscription totalToPaySubscription;
|
|
||||||
private Subscription cancelButton2StyleSubscription;
|
private Subscription cancelButton2StyleSubscription;
|
||||||
|
|
||||||
|
|
||||||
|
@ -143,6 +142,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
addAmountPriceGroup();
|
addAmountPriceGroup();
|
||||||
addFundingGroup();
|
addFundingGroup();
|
||||||
|
|
||||||
|
balanceTextField.setFormatter(model.getFormatter());
|
||||||
|
|
||||||
amountFocusedListener = (o, oldValue, newValue) -> {
|
amountFocusedListener = (o, oldValue, newValue) -> {
|
||||||
model.onFocusOutAmountTextField(oldValue, newValue, amountTextField.getText());
|
model.onFocusOutAmountTextField(oldValue, newValue, amountTextField.getText());
|
||||||
amountTextField.setText(model.amount.get());
|
amountTextField.setText(model.amount.get());
|
||||||
|
@ -170,6 +171,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
paymentAccountsComboBox.setItems(model.getPossiblePaymentAccounts());
|
paymentAccountsComboBox.setItems(model.getPossiblePaymentAccounts());
|
||||||
paymentAccountsComboBox.getSelectionModel().select(0);
|
paymentAccountsComboBox.getSelectionModel().select(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
balanceTextField.setTargetAmount(model.dataModel.totalToPayAsCoin.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -183,9 +186,6 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
|
|
||||||
if (spinner != null)
|
if (spinner != null)
|
||||||
spinner.setProgress(0);
|
spinner.setProgress(0);
|
||||||
|
|
||||||
if (balanceTextField != null)
|
|
||||||
balanceTextField.cleanup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -212,8 +212,6 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
takeOfferButton.setText("Review take offer for selling bitcoin");
|
takeOfferButton.setText("Review take offer for selling bitcoin");
|
||||||
}
|
}
|
||||||
|
|
||||||
balanceTextField.setFormatter(model.getFormatter());
|
|
||||||
balanceTextField.setupBalanceListener(model.address.get());
|
|
||||||
|
|
||||||
boolean showComboBox = model.getPossiblePaymentAccounts().size() > 1;
|
boolean showComboBox = model.getPossiblePaymentAccounts().size() > 1;
|
||||||
paymentAccountsLabel.setVisible(showComboBox);
|
paymentAccountsLabel.setVisible(showComboBox);
|
||||||
|
@ -232,7 +230,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
amountRangeTextField.setText(model.getAmountRange());
|
amountRangeTextField.setText(model.getAmountRange());
|
||||||
priceTextField.setText(model.getPrice());
|
priceTextField.setText(model.getPrice());
|
||||||
addressTextField.setPaymentLabel(model.getPaymentLabel());
|
addressTextField.setPaymentLabel(model.getPaymentLabel());
|
||||||
addressTextField.setAddress(model.getAddressAsString());
|
addressTextField.setAddress(model.dataModel.getAddressEntry().getAddressString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCloseHandler(OfferView.CloseHandler closeHandler) {
|
public void setCloseHandler(OfferView.CloseHandler closeHandler) {
|
||||||
|
@ -290,6 +288,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
priceTextField.setMouseTransparent(true);
|
priceTextField.setMouseTransparent(true);
|
||||||
volumeTextField.setMouseTransparent(true);
|
volumeTextField.setMouseTransparent(true);
|
||||||
|
|
||||||
|
balanceTextField.setTargetAmount(model.dataModel.totalToPayAsCoin.get());
|
||||||
|
|
||||||
|
|
||||||
if (!BitsquareApp.DEV_MODE) {
|
if (!BitsquareApp.DEV_MODE) {
|
||||||
String key = "securityDepositInfo";
|
String key = "securityDepositInfo";
|
||||||
new Popup().backgroundInfo("To ensure that both traders follow the trade protocol they need to pay a security deposit.\n\n" +
|
new Popup().backgroundInfo("To ensure that both traders follow the trade protocol they need to pay a security deposit.\n\n" +
|
||||||
|
@ -309,7 +310,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
"The amount is the sum of " + tradeAmountText + "the security deposit, the trading fee and " +
|
"The amount is the sum of " + tradeAmountText + "the security deposit, the trading fee and " +
|
||||||
"the bitcoin mining fee.\n\n" +
|
"the bitcoin mining fee.\n\n" +
|
||||||
"Please send from your external Bitcoin wallet the exact amount to the address: " +
|
"Please send from your external Bitcoin wallet the exact amount to the address: " +
|
||||||
model.getAddressAsString() + "\n(you can copy the address in the screen below after closing that popup)\n\n" +
|
model.dataModel.getAddressEntry().getAddressString() + "\n(you can copy the address in the screen below after closing that popup)\n\n" +
|
||||||
"Make sure you use a sufficiently high mining fee of at least " +
|
"Make sure you use a sufficiently high mining fee of at least " +
|
||||||
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) +
|
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) +
|
||||||
" to avoid problems that your transaction does not get confirmed in the blockchain.\n" +
|
" to avoid problems that your transaction does not get confirmed in the blockchain.\n" +
|
||||||
|
@ -530,7 +531,6 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
balanceSubscription = EasyBind.subscribe(model.dataModel.balance, newValue -> balanceTextField.setBalance(newValue));
|
balanceSubscription = EasyBind.subscribe(model.dataModel.balance, newValue -> balanceTextField.setBalance(newValue));
|
||||||
totalToPaySubscription = EasyBind.subscribe(model.dataModel.totalToPayAsCoin, newValue -> balanceTextField.setTargetAmount(newValue));
|
|
||||||
cancelButton2StyleSubscription = EasyBind.subscribe(takeOfferButton.visibleProperty(),
|
cancelButton2StyleSubscription = EasyBind.subscribe(takeOfferButton.visibleProperty(),
|
||||||
isVisible -> cancelButton2.setId(isVisible ? "cancel-button" : null));
|
isVisible -> cancelButton2.setId(isVisible ? "cancel-button" : null));
|
||||||
}
|
}
|
||||||
|
@ -544,7 +544,6 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
showTransactionPublishedScreenSubscription.unsubscribe();
|
showTransactionPublishedScreenSubscription.unsubscribe();
|
||||||
// noSufficientFeeSubscription.unsubscribe();
|
// noSufficientFeeSubscription.unsubscribe();
|
||||||
balanceSubscription.unsubscribe();
|
balanceSubscription.unsubscribe();
|
||||||
totalToPaySubscription.unsubscribe();
|
|
||||||
cancelButton2StyleSubscription.unsubscribe();
|
cancelButton2StyleSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -757,22 +756,19 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
takeOfferButton.setManaged(false);
|
takeOfferButton.setManaged(false);
|
||||||
takeOfferButton.setMinHeight(40);
|
takeOfferButton.setMinHeight(40);
|
||||||
takeOfferButton.setPadding(new Insets(0, 20, 0, 20));
|
takeOfferButton.setPadding(new Insets(0, 20, 0, 20));
|
||||||
takeOfferButton.setOnAction(e -> {
|
takeOfferButton.setOnAction(e -> onTakeOffer());
|
||||||
onTakeOffer();
|
|
||||||
balanceTextField.cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel"));
|
cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel"));
|
||||||
cancelButton2.setOnAction(e -> {
|
cancelButton2.setOnAction(e -> {
|
||||||
if (model.dataModel.isWalletFunded.get()) {
|
if (model.dataModel.isWalletFunded.get()) {
|
||||||
new Popup().warning("You have already paid in the funds.\n" +
|
new Popup().warning("You have already paid in the funds.\n" +
|
||||||
"Are you sure you want to cancel.")
|
"Are you sure you want to cancel.")
|
||||||
.actionButtonText("No")
|
.closeButtonText("No")
|
||||||
.onClose(() -> {
|
.actionButtonText("Yes, cancel")
|
||||||
|
.onAction(() -> {
|
||||||
model.dataModel.swapTradeToSavings();
|
model.dataModel.swapTradeToSavings();
|
||||||
close();
|
close();
|
||||||
})
|
})
|
||||||
.closeButtonText("Yes, cancel")
|
|
||||||
.show();
|
.show();
|
||||||
} else {
|
} else {
|
||||||
close();
|
close();
|
||||||
|
@ -785,7 +781,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String getBitcoinURI() {
|
private String getBitcoinURI() {
|
||||||
return model.getAddressAsString() != null ? BitcoinURI.convertToBitcoinURI(model.getAddressAsString(), model.dataModel.missingCoin.get(),
|
String addressString = model.dataModel.getAddressEntry().getAddressString();
|
||||||
|
return addressString != null ? BitcoinURI.convertToBitcoinURI(addressString, model.dataModel.missingCoin.get(),
|
||||||
model.getPaymentLabel(), null) : "";
|
model.getPaymentLabel(), null) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,6 @@ import io.bitsquare.trade.offer.Offer;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.*;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import org.bitcoinj.core.Address;
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
@ -57,7 +56,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||||
final BSFormatter formatter;
|
final BSFormatter formatter;
|
||||||
|
|
||||||
private String amountRange;
|
private String amountRange;
|
||||||
private String addressAsString;
|
|
||||||
private String paymentLabel;
|
private String paymentLabel;
|
||||||
private boolean takeOfferRequested;
|
private boolean takeOfferRequested;
|
||||||
private Trade trade;
|
private Trade trade;
|
||||||
|
@ -86,9 +84,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||||
|
|
||||||
final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>();
|
final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
// Those are needed for the addressTextField
|
|
||||||
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
|
||||||
|
|
||||||
private ChangeListener<String> amountListener;
|
private ChangeListener<String> amountListener;
|
||||||
private ChangeListener<Coin> amountAsCoinListener;
|
private ChangeListener<Coin> amountAsCoinListener;
|
||||||
private ChangeListener<Boolean> isWalletFundedListener;
|
private ChangeListener<Boolean> isWalletFundedListener;
|
||||||
|
@ -169,9 +164,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||||
|
|
||||||
checkNotNull(dataModel.getAddressEntry(), "dataModel.getAddressEntry() must not be null");
|
checkNotNull(dataModel.getAddressEntry(), "dataModel.getAddressEntry() must not be null");
|
||||||
|
|
||||||
addressAsString = dataModel.getAddressEntry().getAddressString();
|
|
||||||
address.set(dataModel.getAddressEntry().getAddress());
|
|
||||||
|
|
||||||
offerErrorListener = (observable, oldValue, newValue) -> {
|
offerErrorListener = (observable, oldValue, newValue) -> {
|
||||||
if (newValue != null)
|
if (newValue != null)
|
||||||
errorMessage.set(newValue);
|
errorMessage.set(newValue);
|
||||||
|
@ -560,10 +552,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||||
return paymentLabel;
|
return paymentLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAddressAsString() {
|
|
||||||
return addressAsString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPrice() {
|
public String getPrice() {
|
||||||
return price;
|
return price;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue