Remove mining fee check

This commit is contained in:
Manfred Karrer 2016-03-31 22:21:38 +02:00
parent 9f42c5ac61
commit f9a33a00ff
6 changed files with 142 additions and 125 deletions

View file

@ -17,21 +17,19 @@
package io.bitsquare.gui.main.offer.createoffer; package io.bitsquare.gui.main.offer.createoffer;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture;
import com.google.inject.Inject; import com.google.inject.Inject;
import io.bitsquare.arbitration.Arbitrator; import io.bitsquare.arbitration.Arbitrator;
import io.bitsquare.btc.*; import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.btc.blockchain.BlockchainService; import io.bitsquare.btc.blockchain.BlockchainService;
import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.pricefeed.PriceFeed; import io.bitsquare.btc.pricefeed.PriceFeed;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.gui.Navigation; import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.model.ActivatableDataModel; import io.bitsquare.gui.common.model.ActivatableDataModel;
import io.bitsquare.gui.main.overlays.notifications.Notification; import io.bitsquare.gui.main.overlays.notifications.Notification;
import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow; import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.locale.TradeCurrency; import io.bitsquare.locale.TradeCurrency;
@ -50,7 +48,6 @@ import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
import org.bitcoinj.utils.ExchangeRate; import org.bitcoinj.utils.ExchangeRate;
import org.bitcoinj.utils.Fiat; import org.bitcoinj.utils.Fiat;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -92,10 +89,10 @@ class CreateOfferDataModel extends ActivatableDataModel {
final StringProperty btcCode = new SimpleStringProperty(); final StringProperty btcCode = new SimpleStringProperty();
final BooleanProperty isWalletFunded = new SimpleBooleanProperty(); final BooleanProperty isWalletFunded = new SimpleBooleanProperty();
final BooleanProperty isMainNet = new SimpleBooleanProperty(); //final BooleanProperty isMainNet = new SimpleBooleanProperty();
final BooleanProperty isFeeFromFundingTxSufficient = new SimpleBooleanProperty(); final BooleanProperty isFeeFromFundingTxSufficient = new SimpleBooleanProperty();
final ObjectProperty<Coin> feeFromFundingTxProperty = new SimpleObjectProperty(Coin.NEGATIVE_SATOSHI); // final ObjectProperty<Coin> feeFromFundingTxProperty = new SimpleObjectProperty(Coin.NEGATIVE_SATOSHI);
final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>(); final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>();
final ObjectProperty<Coin> minAmountAsCoin = new SimpleObjectProperty<>(); final ObjectProperty<Coin> minAmountAsCoin = new SimpleObjectProperty<>();
final ObjectProperty<Fiat> priceAsFiat = new SimpleObjectProperty<>(); final ObjectProperty<Fiat> priceAsFiat = new SimpleObjectProperty<>();
@ -135,7 +132,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
this.blockchainService = blockchainService; this.blockchainService = blockchainService;
this.formatter = formatter; this.formatter = formatter;
isMainNet.set(preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET); // isMainNet.set(preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET);
offerId = UUID.randomUUID().toString(); offerId = UUID.randomUUID().toString();
addressEntry = walletService.getTradeAddressEntry(offerId); addressEntry = walletService.getTradeAddressEntry(offerId);
@ -148,7 +145,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
public void onBalanceChanged(Coin balance, Transaction tx) { public void onBalanceChanged(Coin balance, Transaction tx) {
updateBalance(); updateBalance();
if (preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET) { /* if (isMainNet.get()) {
SettableFuture<Coin> future = blockchainService.requestFee(tx.getHashAsString()); SettableFuture<Coin> future = blockchainService.requestFee(tx.getHashAsString());
Futures.addCallback(future, new FutureCallback<Coin>() { Futures.addCallback(future, new FutureCallback<Coin>() {
public void onSuccess(Coin fee) { public void onSuccess(Coin fee) {
@ -168,9 +165,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
.show()); .show());
} }
}); });
} else { }*/
feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx());
}
} }
}; };
@ -362,9 +357,9 @@ class CreateOfferDataModel extends ActivatableDataModel {
return user.getAcceptedArbitrators().size() > 0; return user.getAcceptedArbitrators().size() > 0;
} }
boolean isFeeFromFundingTxSufficient() { /*boolean isFeeFromFundingTxSufficient() {
return feeFromFundingTxProperty.get().compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0; return !isMainNet.get() || feeFromFundingTxProperty.get().compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0;
} }*/
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -416,6 +411,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
} }
missingCoin.set(totalToPayAsCoin.get().subtract(balance.get())); missingCoin.set(totalToPayAsCoin.get().subtract(balance.get()));
if (missingCoin.get().isNegative())
missingCoin.set(Coin.ZERO);
isWalletFunded.set(isBalanceSufficient(balance.get())); isWalletFunded.set(isBalanceSufficient(balance.get()));
if (isWalletFunded.get()) { if (isWalletFunded.get()) {

View file

@ -112,7 +112,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private ChangeListener<Boolean> showWarningAdjustedVolumeListener; private ChangeListener<Boolean> showWarningAdjustedVolumeListener;
private ChangeListener<String> errorMessageListener; private ChangeListener<String> errorMessageListener;
private ChangeListener<Boolean> placeOfferCompletedListener; private ChangeListener<Boolean> placeOfferCompletedListener;
private ChangeListener<Coin> feeFromFundingTxListener; // private ChangeListener<Coin> feeFromFundingTxListener;
private EventHandler<ActionEvent> paymentAccountsComboBoxSelectionHandler; private EventHandler<ActionEvent> paymentAccountsComboBoxSelectionHandler;
private EventHandler<ActionEvent> currencyComboBoxSelectionHandler; private EventHandler<ActionEvent> currencyComboBoxSelectionHandler;
@ -123,6 +123,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private ChangeListener<Coin> balanceListener; private ChangeListener<Coin> balanceListener;
private HBox fundingHBox; private HBox fundingHBox;
private Subscription isSpinnerVisibleSubscription; private Subscription isSpinnerVisibleSubscription;
private Subscription cancelButton2StyleSubscription;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -267,7 +268,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
.show(); .show();
} }
} else { } else {
new Popup().information("You need to wait until your are bootstrapped to the network.\n" + new Popup().information("You need to wait until bootstrapping to the network is completed.\n" +
"That might take up to about 2 minutes at startup.").show(); "That might take up to about 2 minutes at startup.").show();
} }
} }
@ -484,10 +485,13 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
isSpinnerVisibleSubscription = EasyBind.subscribe(model.isSpinnerVisible, isSpinnerVisibleSubscription = EasyBind.subscribe(model.isSpinnerVisible,
isSpinnerVisible -> spinner.setProgress(isSpinnerVisible ? -1 : 0)); isSpinnerVisible -> spinner.setProgress(isSpinnerVisible ? -1 : 0));
cancelButton2StyleSubscription = EasyBind.subscribe(placeOfferButton.visibleProperty(),
isVisible -> cancelButton2.setId(isVisible ? "cancel-button" : null));
} }
private void removeSubscriptions() { private void removeSubscriptions() {
isSpinnerVisibleSubscription.unsubscribe(); isSpinnerVisibleSubscription.unsubscribe();
cancelButton2StyleSubscription.unsubscribe();
} }
private void createListeners() { private void createListeners() {
@ -535,7 +539,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
.show(), 100, TimeUnit.MILLISECONDS); .show(), 100, TimeUnit.MILLISECONDS);
}; };
feeFromFundingTxListener = (observable, oldValue, newValue) -> { /* feeFromFundingTxListener = (observable, oldValue, newValue) -> {
log.debug("feeFromFundingTxListener " + newValue); log.debug("feeFromFundingTxListener " + newValue);
if (!model.dataModel.isFeeFromFundingTxSufficient()) { if (!model.dataModel.isFeeFromFundingTxSufficient()) {
new Popup().warning("The mining fee from your funding transaction is not sufficiently high.\n\n" + new Popup().warning("The mining fee from your funding transaction is not sufficiently high.\n\n" +
@ -556,7 +560,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
}) })
.show(); .show();
} }
}; };*/
paymentAccountsComboBoxSelectionHandler = e -> onPaymentAccountsComboBoxSelected(); paymentAccountsComboBoxSelectionHandler = e -> onPaymentAccountsComboBoxSelected();
currencyComboBoxSelectionHandler = e -> onCurrencyComboBoxSelected(); currencyComboBoxSelectionHandler = e -> onCurrencyComboBoxSelected();
@ -609,7 +613,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
model.showWarningInvalidFiatDecimalPlaces.addListener(showWarningInvalidFiatDecimalPlacesPlacesListener); model.showWarningInvalidFiatDecimalPlaces.addListener(showWarningInvalidFiatDecimalPlacesPlacesListener);
model.showWarningAdjustedVolume.addListener(showWarningAdjustedVolumeListener); model.showWarningAdjustedVolume.addListener(showWarningAdjustedVolumeListener);
model.errorMessage.addListener(errorMessageListener); model.errorMessage.addListener(errorMessageListener);
model.dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener); // model.dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
model.placeOfferCompleted.addListener(placeOfferCompletedListener); model.placeOfferCompleted.addListener(placeOfferCompletedListener);
@ -633,7 +637,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
model.showWarningInvalidFiatDecimalPlaces.removeListener(showWarningInvalidFiatDecimalPlacesPlacesListener); model.showWarningInvalidFiatDecimalPlaces.removeListener(showWarningInvalidFiatDecimalPlacesPlacesListener);
model.showWarningAdjustedVolume.removeListener(showWarningAdjustedVolumeListener); model.showWarningAdjustedVolume.removeListener(showWarningAdjustedVolumeListener);
model.errorMessage.removeListener(errorMessageListener); model.errorMessage.removeListener(errorMessageListener);
model.dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener); // model.dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
model.placeOfferCompleted.removeListener(placeOfferCompletedListener); model.placeOfferCompleted.removeListener(placeOfferCompletedListener);

View file

@ -66,6 +66,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
private String directionLabel; private String directionLabel;
private String addressAsString; private String addressAsString;
private final String paymentLabel; private final String paymentLabel;
private boolean createOfferRequested;
final StringProperty amount = new SimpleStringProperty(); final StringProperty amount = new SimpleStringProperty();
final StringProperty minAmount = new SimpleStringProperty(); final StringProperty minAmount = new SimpleStringProperty();
@ -109,7 +110,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
private ChangeListener<Fiat> priceAsFiatListener; private ChangeListener<Fiat> priceAsFiatListener;
private ChangeListener<Fiat> volumeAsFiatListener; private ChangeListener<Fiat> volumeAsFiatListener;
private ChangeListener<Boolean> isWalletFundedListener; private ChangeListener<Boolean> isWalletFundedListener;
private ChangeListener<Coin> feeFromFundingTxListener; //private ChangeListener<Coin> feeFromFundingTxListener;
private ChangeListener<String> errorMessageListener; private ChangeListener<String> errorMessageListener;
private Offer offer; private Offer offer;
private Timer timeoutTimer; private Timer timeoutTimer;
@ -254,9 +255,9 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
isWalletFundedListener = (ov, oldValue, newValue) -> { isWalletFundedListener = (ov, oldValue, newValue) -> {
updateButtonDisableState(); updateButtonDisableState();
}; };
feeFromFundingTxListener = (ov, oldValue, newValue) -> { /* feeFromFundingTxListener = (ov, oldValue, newValue) -> {
updateButtonDisableState(); updateButtonDisableState();
}; };*/
} }
private void addListeners() { private void addListeners() {
@ -273,7 +274,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
dataModel.priceAsFiat.addListener(priceAsFiatListener); dataModel.priceAsFiat.addListener(priceAsFiatListener);
dataModel.volumeAsFiat.addListener(volumeAsFiatListener); dataModel.volumeAsFiat.addListener(volumeAsFiatListener);
dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener); // dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
dataModel.isWalletFunded.addListener(isWalletFundedListener); dataModel.isWalletFunded.addListener(isWalletFundedListener);
} }
@ -289,7 +290,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
dataModel.priceAsFiat.removeListener(priceAsFiatListener); dataModel.priceAsFiat.removeListener(priceAsFiatListener);
dataModel.volumeAsFiat.removeListener(volumeAsFiatListener); dataModel.volumeAsFiat.removeListener(volumeAsFiatListener);
dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener); //dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
dataModel.isWalletFunded.removeListener(isWalletFundedListener); dataModel.isWalletFunded.removeListener(isWalletFundedListener);
if (offer != null && errorMessageListener != null) if (offer != null && errorMessageListener != null)
@ -314,24 +315,24 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
void onPlaceOffer(Offer offer, Runnable resultHandler) { void onPlaceOffer(Offer offer, Runnable resultHandler) {
errorMessage.set(null); errorMessage.set(null);
createOfferRequested = true;
isPlaceOfferButtonDisabled.set(true);
cancelButtonDisabled.set(true);
if (timeoutTimer == null) { if (timeoutTimer == null) {
timeoutTimer = UserThread.runAfter(() -> { timeoutTimer = UserThread.runAfter(() -> {
stopTimeoutTimer(); stopTimeoutTimer();
isPlaceOfferButtonDisabled.set(false); createOfferRequested = false;
cancelButtonDisabled.set(false);
errorMessage.set("A timeout occurred at publishing the offer."); errorMessage.set("A timeout occurred at publishing the offer.");
updateButtonDisableState();
updateSpinnerInfo();
resultHandler.run(); resultHandler.run();
}, 30); }, 30);
} }
errorMessageListener = (observable, oldValue, newValue) -> { errorMessageListener = (observable, oldValue, newValue) -> {
if (newValue != null) { if (newValue != null) {
stopTimeoutTimer(); stopTimeoutTimer();
isPlaceOfferButtonDisabled.set(false); createOfferRequested = false;
cancelButtonDisabled.set(false);
if (offer.getState() == Offer.State.OFFER_FEE_PAID) if (offer.getState() == Offer.State.OFFER_FEE_PAID)
errorMessage.set(newValue + errorMessage.set(newValue +
"\n\nThe offer fee is already paid. In the worst case you have lost that fee. " + "\n\nThe offer fee is already paid. In the worst case you have lost that fee. " +
@ -340,10 +341,15 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
else else
errorMessage.set(newValue); errorMessage.set(newValue);
updateButtonDisableState();
updateSpinnerInfo();
resultHandler.run(); resultHandler.run();
} }
}; };
offer.errorMessageProperty().addListener(errorMessageListener); offer.errorMessageProperty().addListener(errorMessageListener);
dataModel.onPlaceOffer(offer, transaction -> { dataModel.onPlaceOffer(offer, transaction -> {
stopTimeoutTimer(); stopTimeoutTimer();
resultHandler.run(); resultHandler.run();
@ -351,16 +357,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
errorMessage.set(null); errorMessage.set(null);
}); });
updateButtonDisableState();
updateSpinnerInfo(); updateSpinnerInfo();
} }
private void stopTimeoutTimer() {
if (timeoutTimer != null) {
timeoutTimer.stop();
timeoutTimer = null;
}
}
public void onPaymentAccountSelected(PaymentAccount paymentAccount) { public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
btcValidator.setMaxTradeLimitInBitcoin(paymentAccount.getPaymentMethod().getMaxTradeLimitInBitcoin()); btcValidator.setMaxTradeLimitInBitcoin(paymentAccount.getPaymentMethod().getMaxTradeLimitInBitcoin());
dataModel.onPaymentAccountSelected(paymentAccount); dataModel.onPaymentAccountSelected(paymentAccount);
@ -370,33 +370,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
dataModel.onCurrencySelected(tradeCurrency); dataModel.onCurrencySelected(tradeCurrency);
} }
public boolean isPriceInRange() {
MarketPrice marketPrice = priceFeed.getMarketPrice(getTradeCurrency().getCode());
if (marketPrice != null) {
double marketPriceAsDouble = marketPrice.getPrice(PriceFeed.Type.LAST);
Fiat priceAsFiat = dataModel.priceAsFiat.get();
long shiftDivisor = checkedPow(10, priceAsFiat.smallestUnitExponent());
double offerPrice = ((double) priceAsFiat.longValue()) / ((double) shiftDivisor);
if (marketPriceAsDouble != 0 && Math.abs(1 - (offerPrice / marketPriceAsDouble)) > preferences.getMaxPriceDistanceInPercent()) {
Popup popup = new Popup();
popup.warning("The price you have entered is outside the max. allowed deviation from the market price.\n" +
"The max. allowed deviation is " +
formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent()) +
" and can be adjusted in the preferences.")
.actionButtonText("Change price")
.onAction(() -> popup.hide())
.closeButtonText("Go to \"Preferences\"")
.onClose(() -> navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class))
.show();
return false;
} else {
return true;
}
} else {
return true;
}
}
void onShowPayFundsScreen() { void onShowPayFundsScreen() {
showPayFundsScreenDisplayed.set(true); showPayFundsScreenDisplayed.set(true);
updateSpinnerInfo(); updateSpinnerInfo();
@ -514,6 +487,33 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public boolean isPriceInRange() {
MarketPrice marketPrice = priceFeed.getMarketPrice(getTradeCurrency().getCode());
if (marketPrice != null) {
double marketPriceAsDouble = marketPrice.getPrice(PriceFeed.Type.LAST);
Fiat priceAsFiat = dataModel.priceAsFiat.get();
long shiftDivisor = checkedPow(10, priceAsFiat.smallestUnitExponent());
double offerPrice = ((double) priceAsFiat.longValue()) / ((double) shiftDivisor);
if (marketPriceAsDouble != 0 && Math.abs(1 - (offerPrice / marketPriceAsDouble)) > preferences.getMaxPriceDistanceInPercent()) {
Popup popup = new Popup();
popup.warning("The price you have entered is outside the max. allowed deviation from the market price.\n" +
"The max. allowed deviation is " +
formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent()) +
" and can be adjusted in the preferences.")
.actionButtonText("Change price")
.onAction(() -> popup.hide())
.closeButtonText("Go to \"Preferences\"")
.onClose(() -> navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class))
.show();
return false;
} else {
return true;
}
} else {
return true;
}
}
BSFormatter getFormatter() { BSFormatter getFormatter() {
return formatter; return formatter;
} }
@ -579,6 +579,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
return p2PService.isBootstrapped(); return p2PService.isBootstrapped();
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Utils // Utils
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -660,9 +661,16 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
isFiatInputValid(volume.get()).isValid && isFiatInputValid(volume.get()).isValid &&
dataModel.isMinAmountLessOrEqualAmount(); dataModel.isMinAmountLessOrEqualAmount();
isNextButtonDisabled.set(!inputDataValid); isNextButtonDisabled.set(!inputDataValid);
isPlaceOfferButtonDisabled.set(!(inputDataValid && // boolean notSufficientFees = dataModel.isWalletFunded.get() && dataModel.isMainNet.get() && !dataModel.isFeeFromFundingTxSufficient.get();
dataModel.isWalletFunded.get() && //isPlaceOfferButtonDisabled.set(createOfferRequested || !inputDataValid || notSufficientFees);
(dataModel.useSavingsWallet || dataModel.isFeeFromFundingTxSufficient())) isPlaceOfferButtonDisabled.set(createOfferRequested || !inputDataValid || !dataModel.isWalletFunded.get());
); }
private void stopTimeoutTimer() {
if (timeoutTimer != null) {
timeoutTimer.stop();
timeoutTimer = null;
}
} }
} }

View file

@ -17,20 +17,18 @@
package io.bitsquare.gui.main.offer.takeoffer; package io.bitsquare.gui.main.offer.takeoffer;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture;
import com.google.inject.Inject; import com.google.inject.Inject;
import io.bitsquare.app.BitsquareApp; import io.bitsquare.app.BitsquareApp;
import io.bitsquare.arbitration.Arbitrator; import io.bitsquare.arbitration.Arbitrator;
import io.bitsquare.btc.*; import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.btc.blockchain.BlockchainService; import io.bitsquare.btc.blockchain.BlockchainService;
import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.pricefeed.PriceFeed; import io.bitsquare.btc.pricefeed.PriceFeed;
import io.bitsquare.common.UserThread;
import io.bitsquare.gui.common.model.ActivatableDataModel; import io.bitsquare.gui.common.model.ActivatableDataModel;
import io.bitsquare.gui.main.overlays.notifications.Notification; import io.bitsquare.gui.main.overlays.notifications.Notification;
import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow; import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.locale.CurrencyUtil; import io.bitsquare.locale.CurrencyUtil;
@ -47,10 +45,8 @@ import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.utils.ExchangeRate; import org.bitcoinj.utils.ExchangeRate;
import org.bitcoinj.utils.Fiat; import org.bitcoinj.utils.Fiat;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -77,15 +73,15 @@ class TakeOfferDataModel extends ActivatableDataModel {
private final Coin takerFeeAsCoin; private final Coin takerFeeAsCoin;
private final Coin networkFeeAsCoin; private final Coin networkFeeAsCoin;
private final Coin securityDepositAsCoin; private final Coin securityDepositAsCoin;
Coin feeFromFundingTx = Coin.NEGATIVE_SATOSHI; // Coin feeFromFundingTx = Coin.NEGATIVE_SATOSHI;
private Offer offer; private Offer offer;
private AddressEntry addressEntry; private AddressEntry addressEntry;
final StringProperty btcCode = new SimpleStringProperty(); final StringProperty btcCode = new SimpleStringProperty();
final BooleanProperty isWalletFunded = new SimpleBooleanProperty(); final BooleanProperty isWalletFunded = new SimpleBooleanProperty();
final BooleanProperty isFeeFromFundingTxSufficient = new SimpleBooleanProperty(); // final BooleanProperty isFeeFromFundingTxSufficient = new SimpleBooleanProperty();
final BooleanProperty isMainNet = new SimpleBooleanProperty(); // final BooleanProperty isMainNet = new SimpleBooleanProperty();
final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>(); final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>();
final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>(); final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>();
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>(); final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
@ -124,7 +120,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
networkFeeAsCoin = FeePolicy.getFixedTxFeeForTrades(); networkFeeAsCoin = FeePolicy.getFixedTxFeeForTrades();
securityDepositAsCoin = FeePolicy.getSecurityDeposit(); securityDepositAsCoin = FeePolicy.getSecurityDeposit();
isMainNet.set(preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET); // isMainNet.set(preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET);
} }
@Override @Override
@ -179,20 +175,16 @@ class TakeOfferDataModel extends ActivatableDataModel {
if (BitsquareApp.DEV_MODE) if (BitsquareApp.DEV_MODE)
amountAsCoin.set(offer.getAmount()); amountAsCoin.set(offer.getAmount());
calculateTotalToPay();
calculateVolume(); calculateVolume();
calculateTotalToPay(); calculateTotalToPay();
updateBalance();
balanceListener = new BalanceListener(addressEntry.getAddress()) { balanceListener = new BalanceListener(addressEntry.getAddress()) {
@Override @Override
public void onBalanceChanged(Coin balance, Transaction tx) { public void onBalanceChanged(Coin balance, Transaction tx) {
updateBalance(); updateBalance();
if (tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) { /*if (isMainNet.get()) {
}
if (isMainNet.get()) {
SettableFuture<Coin> future = blockchainService.requestFee(tx.getHashAsString()); SettableFuture<Coin> future = blockchainService.requestFee(tx.getHashAsString());
Futures.addCallback(future, new FutureCallback<Coin>() { Futures.addCallback(future, new FutureCallback<Coin>() {
public void onSuccess(Coin fee) { public void onSuccess(Coin fee) {
@ -215,7 +207,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
} else { } else {
setFeeFromFundingTx(FeePolicy.getMinRequiredFeeForFundingTx()); setFeeFromFundingTx(FeePolicy.getMinRequiredFeeForFundingTx());
isFeeFromFundingTxSufficient.set(feeFromFundingTx.compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0); isFeeFromFundingTxSufficient.set(feeFromFundingTx.compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0);
} }*/
} }
}; };
@ -347,20 +339,23 @@ class TakeOfferDataModel 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);
} else { } else {
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())
missingCoin.set(Coin.ZERO);
}
isWalletFunded.set(isBalanceSufficient(balance.get())); isWalletFunded.set(isBalanceSufficient(balance.get()));
if (isWalletFunded.get()) { if (isWalletFunded.get()) {
// walletService.removeBalanceListener(balanceListener); // walletService.removeBalanceListener(balanceListener);
if (walletFundedNotification == null) { 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" +
@ -378,13 +373,13 @@ class TakeOfferDataModel extends ActivatableDataModel {
public void swapTradeToSavings() { public void swapTradeToSavings() {
walletService.swapTradeToSavings(offer.getId()); walletService.swapTradeToSavings(offer.getId());
setFeeFromFundingTx(Coin.NEGATIVE_SATOSHI); //setFeeFromFundingTx(Coin.NEGATIVE_SATOSHI);
} }
private void setFeeFromFundingTx(Coin fee) { /* private void setFeeFromFundingTx(Coin fee) {
feeFromFundingTx = fee; feeFromFundingTx = fee;
isFeeFromFundingTxSufficient.set(feeFromFundingTx.compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0); isFeeFromFundingTxSufficient.set(feeFromFundingTx.compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0);
} }*/
boolean isMinAmountLessOrEqualAmount() { boolean isMinAmountLessOrEqualAmount() {
//noinspection SimplifiableIfStatement //noinspection SimplifiableIfStatement

View file

@ -64,7 +64,6 @@ 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;
import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.Subscription;
import org.fxmisc.easybind.monadic.MonadicBinding;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.inject.Inject; import javax.inject.Inject;
@ -115,9 +114,10 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
private ImageView qrCodeImageView; private ImageView qrCodeImageView;
private HBox fundingHBox; private HBox fundingHBox;
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 totalToPaySubscription;
private Subscription cancelButton2StyleSubscription;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -507,7 +507,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
} }
}); });
noSufficientFeeBinding = EasyBind.combine(model.dataModel.isWalletFunded, model.dataModel.isMainNet, model.dataModel.isFeeFromFundingTxSufficient, /* noSufficientFeeBinding = EasyBind.combine(model.dataModel.isWalletFunded, model.dataModel.isMainNet, model.dataModel.isFeeFromFundingTxSufficient,
(isWalletFunded, isMainNet, isFeeSufficient) -> isWalletFunded && isMainNet && !isFeeSufficient); (isWalletFunded, isMainNet, isFeeSufficient) -> isWalletFunded && isMainNet && !isFeeSufficient);
noSufficientFeeSubscription = noSufficientFeeBinding.subscribe((observable, oldValue, newValue) -> { noSufficientFeeSubscription = noSufficientFeeBinding.subscribe((observable, oldValue, newValue) -> {
if (newValue) if (newValue)
@ -527,10 +527,12 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class); navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class);
}) })
.show(); .show();
}); });*/
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)); totalToPaySubscription = EasyBind.subscribe(model.dataModel.totalToPayAsCoin, newValue -> balanceTextField.setTargetAmount(newValue));
cancelButton2StyleSubscription = EasyBind.subscribe(takeOfferButton.visibleProperty(),
isVisible -> cancelButton2.setId(isVisible ? "cancel-button" : null));
} }
private void removeSubscriptions() { private void removeSubscriptions() {
@ -540,9 +542,10 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
isSpinnerVisibleSubscription.unsubscribe(); isSpinnerVisibleSubscription.unsubscribe();
showWarningInvalidBtcDecimalPlacesSubscription.unsubscribe(); showWarningInvalidBtcDecimalPlacesSubscription.unsubscribe();
showTransactionPublishedScreenSubscription.unsubscribe(); showTransactionPublishedScreenSubscription.unsubscribe();
noSufficientFeeSubscription.unsubscribe(); // noSufficientFeeSubscription.unsubscribe();
balanceSubscription.unsubscribe(); balanceSubscription.unsubscribe();
totalToPaySubscription.unsubscribe(); totalToPaySubscription.unsubscribe();
cancelButton2StyleSubscription.unsubscribe();
} }
@ -647,7 +650,10 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
cancelButton1 = new Button(BSResources.get("shared.cancel")); cancelButton1 = new Button(BSResources.get("shared.cancel"));
cancelButton1.setDefaultButton(false); cancelButton1.setDefaultButton(false);
cancelButton1.setId("cancel-button"); cancelButton1.setId("cancel-button");
cancelButton1.setOnAction(e -> close()); cancelButton1.setOnAction(e -> {
model.dataModel.swapTradeToSavings();
close();
});
offerAvailabilitySpinner = new ProgressIndicator(0); offerAvailabilitySpinner = new ProgressIndicator(0);
offerAvailabilitySpinner.setPrefSize(18, 18); offerAvailabilitySpinner.setPrefSize(18, 18);
@ -758,15 +764,20 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
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("Yes, cancel") .actionButtonText("No")
.onAction(() -> close()) .onClose(() -> {
.closeButtonText("No") model.dataModel.swapTradeToSavings();
.show();
else
close(); close();
})
.closeButtonText("Yes, cancel")
.show();
} else {
close();
model.dataModel.swapTradeToSavings();
}
}); });
cancelButton2.setDefaultButton(false); cancelButton2.setDefaultButton(false);
cancelButton2.setVisible(false); cancelButton2.setVisible(false);

View file

@ -42,8 +42,6 @@ import javafx.beans.value.ChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import org.bitcoinj.core.Address; import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.List; import java.util.List;
@ -99,7 +97,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
private ChangeListener<Offer.State> offerStateListener; private ChangeListener<Offer.State> offerStateListener;
private ChangeListener<String> offerErrorListener; private ChangeListener<String> offerErrorListener;
private ConnectionListener connectionListener; private ConnectionListener connectionListener;
private Subscription isFeeSufficientSubscription; // private Subscription isFeeSufficientSubscription;
private Runnable takeOfferSucceededHandler; private Runnable takeOfferSucceededHandler;
@ -199,9 +197,11 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
applyTradeState(trade.getState()); applyTradeState(trade.getState());
trade.errorMessageProperty().addListener(tradeErrorListener); trade.errorMessageProperty().addListener(tradeErrorListener);
applyTradeErrorMessage(trade.errorMessageProperty().get()); applyTradeErrorMessage(trade.errorMessageProperty().get());
updateButtonDisableState();
takeOfferCompleted.set(true); takeOfferCompleted.set(true);
}); });
updateButtonDisableState();
updateSpinnerInfo();
} }
public void onPaymentAccountSelected(PaymentAccount paymentAccount) { public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
@ -390,8 +390,9 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
&& !dataModel.isAmountLargerThanOfferAmount() && !dataModel.isAmountLargerThanOfferAmount()
&& isOfferAvailable.get(); && isOfferAvailable.get();
isNextButtonDisabled.set(!inputDataValid); isNextButtonDisabled.set(!inputDataValid);
boolean notSufficientFees = dataModel.isWalletFunded.get() && dataModel.isMainNet.get() && !dataModel.isFeeFromFundingTxSufficient.get(); // boolean notSufficientFees = dataModel.isWalletFunded.get() && dataModel.isMainNet.get() && !dataModel.isFeeFromFundingTxSufficient.get();
isTakeOfferButtonDisabled.set(takeOfferRequested || !inputDataValid || notSufficientFees); // isTakeOfferButtonDisabled.set(takeOfferRequested || !inputDataValid || notSufficientFees);
isTakeOfferButtonDisabled.set(takeOfferRequested || !inputDataValid || !dataModel.isWalletFunded.get());
} }
@ -467,11 +468,12 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
showTransactionPublishedScreen.get()) { showTransactionPublishedScreen.get()) {
spinnerInfoText.set(""); spinnerInfoText.set("");
} else if (dataModel.isWalletFunded.get()) { } else if (dataModel.isWalletFunded.get()) {
if (dataModel.isFeeFromFundingTxSufficient.get()) { spinnerInfoText.set("");
/* if (dataModel.isFeeFromFundingTxSufficient.get()) {
spinnerInfoText.set(""); spinnerInfoText.set("");
} else { } else {
spinnerInfoText.set("Check if funding tx miner fee is sufficient..."); spinnerInfoText.set("Check if funding tx miner fee is sufficient...");
} }*/
} else { } else {
spinnerInfoText.set("Waiting for funds..."); spinnerInfoText.set("Waiting for funds...");
} }
@ -489,10 +491,10 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
dataModel.isWalletFunded.addListener(isWalletFundedListener); dataModel.isWalletFunded.addListener(isWalletFundedListener);
p2PService.getNetworkNode().addConnectionListener(connectionListener); p2PService.getNetworkNode().addConnectionListener(connectionListener);
isFeeSufficientSubscription = EasyBind.subscribe(dataModel.isFeeFromFundingTxSufficient, newValue -> { /* isFeeSufficientSubscription = EasyBind.subscribe(dataModel.isFeeFromFundingTxSufficient, newValue -> {
updateButtonDisableState(); updateButtonDisableState();
updateSpinnerInfo(); updateSpinnerInfo();
}); });*/
} }
private void removeListeners() { private void removeListeners() {
@ -512,7 +514,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
trade.errorMessageProperty().removeListener(tradeErrorListener); trade.errorMessageProperty().removeListener(tradeErrorListener);
} }
p2PService.getNetworkNode().removeConnectionListener(connectionListener); p2PService.getNetworkNode().removeConnectionListener(connectionListener);
isFeeSufficientSubscription.unsubscribe(); //isFeeSufficientSubscription.unsubscribe();
} }