Skip to content

Commit

Permalink
Merge pull request #3870 from ripcurlx/show-if-minimum-values-are-used
Browse files Browse the repository at this point in the history
Display minimum limit if used during trade
  • Loading branch information
sqrrm authored Jan 7, 2020
2 parents 6198d2c + e7c16a6 commit 81a14d3
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 18 deletions.
5 changes: 3 additions & 2 deletions core/src/main/resources/i18n/displayStrings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ shared.makerTxFee=Maker: {0}
shared.takerTxFee=Taker: {0}
shared.securityDepositBox.description=Security deposit for BTC {0}
shared.iConfirm=I confirm
shared.tradingFeeInBsqInfo=equivalent to {0} used as mining fee
shared.tradingFeeInBsqInfo=equivalent to {0} used as trading fee
shared.openURL=Open {0}
shared.fiat=Fiat
shared.crypto=Crypto
Expand Down Expand Up @@ -479,6 +479,7 @@ createOffer.setDeposit=Set buyer's security deposit (%)
createOffer.setDepositAsBuyer=Set my security deposit as buyer (%)
createOffer.securityDepositInfo=Your buyer''s security deposit will be {0}
createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0}
createOffer.minSecurityDepositUsed=Min. buyer security deposit is used


####################################################################
Expand Down Expand Up @@ -2738,7 +2739,7 @@ URL: \"{0}\"
guiUtil.openWebBrowser.doOpen=Open the web page and don't ask again
guiUtil.openWebBrowser.copyUrl=Copy URL and cancel
guiUtil.ofTradeAmount=of trade amount

guiUtil.requiredMinimum=(required minimum)

####################################################################
# Component specific
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -713,4 +713,8 @@ boolean canPlaceOffer() {
return GUIUtil.isBootstrappedOrShowPopup(p2PService) &&
GUIUtil.canCreateOrTakeOfferOrShowPopup(user, navigation);
}

public boolean isMinBuyerSecurityDeposit() {
return !getBuyerSecurityDepositAsCoin().isGreaterThan(Restrictions.getMinBuyerSecurityDepositAsCoin());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
private FundsTextField totalToPayTextField;
private Label amountDescriptionLabel, priceCurrencyLabel, priceDescriptionLabel, volumeDescriptionLabel,
waitingForFundsLabel, marketBasedPriceLabel, percentagePriceDescription, tradeFeeDescriptionLabel,
resultLabel, tradeFeeInBtcLabel, tradeFeeInBsqLabel, xLabel, fakeXLabel, buyerSecurityDepositLabel;
resultLabel, tradeFeeInBtcLabel, tradeFeeInBsqLabel, xLabel, fakeXLabel, buyerSecurityDepositLabel,
buyerSecurityDepositPercentageLabel;
protected Label amountBtcLabel, volumeCurrencyLabel, minAmountBtcLabel;
private ComboBox<PaymentAccount> paymentAccountsComboBox;
private ComboBox<TradeCurrency> currencyComboBox;
Expand All @@ -159,7 +160,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
private ChangeListener<Boolean> amountFocusedListener, minAmountFocusedListener, volumeFocusedListener,
buyerSecurityDepositFocusedListener, priceFocusedListener, placeOfferCompletedListener,
priceAsPercentageFocusedListener, getShowWalletFundedNotificationListener,
tradeFeeInBtcToggleListener, tradeFeeInBsqToggleListener, tradeFeeVisibleListener;
tradeFeeInBtcToggleListener, tradeFeeInBsqToggleListener, tradeFeeVisibleListener,
isMinBuyerSecurityDepositListener;
private ChangeListener<String> tradeCurrencyCodeListener, errorMessageListener,
marketPriceMarginListener, volumeListener, buyerSecurityDepositInBTCListener;
private ChangeListener<Number> marketPriceAvailableListener;
Expand Down Expand Up @@ -825,6 +827,18 @@ private void createListeners() {
tradeFeeInBsqToggle.setVisible(newValue);
}
};

isMinBuyerSecurityDepositListener = ((observable, oldValue, newValue) -> {
if (newValue) {
// show BTC
buyerSecurityDepositPercentageLabel.setText(Res.getBaseCurrencyCode());
buyerSecurityDepositInputTextField.setDisable(true);
} else {
// show %
buyerSecurityDepositPercentageLabel.setText("%");
buyerSecurityDepositInputTextField.setDisable(false);
}
});
}

private void setIsCurrencyForMakerFeeBtc(boolean isCurrencyForMakerFeeBtc) {
Expand Down Expand Up @@ -862,6 +876,7 @@ private void addListeners() {
model.volume.addListener(volumeListener);
model.isTradeFeeVisible.addListener(tradeFeeVisibleListener);
model.buyerSecurityDepositInBTC.addListener(buyerSecurityDepositInBTCListener);
model.isMinBuyerSecurityDeposit.addListener(isMinBuyerSecurityDepositListener);

tradeFeeInBtcToggle.selectedProperty().addListener(tradeFeeInBtcToggleListener);
tradeFeeInBsqToggle.selectedProperty().addListener(tradeFeeInBsqToggleListener);
Expand Down Expand Up @@ -897,6 +912,7 @@ private void removeListeners() {
model.buyerSecurityDepositInBTC.removeListener(buyerSecurityDepositInBTCListener);
tradeFeeInBtcToggle.selectedProperty().removeListener(tradeFeeInBtcToggleListener);
tradeFeeInBsqToggle.selectedProperty().removeListener(tradeFeeInBsqToggleListener);
model.isMinBuyerSecurityDeposit.removeListener(isMinBuyerSecurityDepositListener);

// focus out
amountTextField.focusedProperty().removeListener(amountFocusedListener);
Expand Down Expand Up @@ -1105,7 +1121,7 @@ private VBox getBuyerSecurityDepositBox() {
Res.get("createOffer.securityDeposit.prompt"));
buyerSecurityDepositInfoInputTextField = tuple.second;
buyerSecurityDepositInputTextField = buyerSecurityDepositInfoInputTextField.getInputTextField();
Label buyerSecurityDepositPercentageLabel = tuple.third;
buyerSecurityDepositPercentageLabel = tuple.third;
// getEditableValueBox delivers BTC, so we overwrite it with %
buyerSecurityDepositPercentageLabel.setText("%");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import bisq.core.offer.OfferRestrictions;
import bisq.core.offer.OfferUtil;
import bisq.core.payment.PaymentAccount;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.MarketPrice;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.user.Preferences;
Expand Down Expand Up @@ -147,6 +148,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
final BooleanProperty showPayFundsScreenDisplayed = new SimpleBooleanProperty();
private final BooleanProperty showTransactionPublishedScreen = new SimpleBooleanProperty();
final BooleanProperty isWaitingForFunds = new SimpleBooleanProperty();
final BooleanProperty isMinBuyerSecurityDeposit = new SimpleBooleanProperty();

final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>();
final ObjectProperty<InputValidator.ValidationResult> minAmountValidationResult = new SimpleObjectProperty<>();
Expand Down Expand Up @@ -299,6 +301,7 @@ private void createListeners() {
dataModel.calculateVolume();
dataModel.calculateTotalToPay();
}
updateBuyerSecurityDeposit();
updateButtonDisableState();
}
};
Expand Down Expand Up @@ -970,7 +973,9 @@ public String getSecurityDepositPopOverLabel(String depositInBTC) {

public String getSecurityDepositInfo() {
return btcFormatter.formatCoinWithCode(dataModel.getSecurityDeposit()) +
GUIUtil.getPercentageOfTradeAmount(dataModel.getSecurityDeposit(), dataModel.getAmount().get());
GUIUtil.getPercentageOfTradeAmount(dataModel.getSecurityDeposit(),
dataModel.getAmount().get(),
Restrictions.getMinBuyerSecurityDepositAsCoin());
}

public String getSecurityDepositWithCode() {
Expand All @@ -982,7 +987,8 @@ public String getTradeFee() {
final Coin makerFeeAsCoin = dataModel.getMakerFee();
final String makerFee = getFormatterForMakerFee().formatCoinWithCode(makerFeeAsCoin);
if (dataModel.isCurrencyForMakerFeeBtc())
return makerFee + GUIUtil.getPercentageOfTradeAmount(makerFeeAsCoin, dataModel.getAmount().get());
return makerFee + GUIUtil.getPercentageOfTradeAmount(makerFeeAsCoin, dataModel.getAmount().get(),
FeeService.getMinMakerFee(dataModel.isCurrencyForMakerFeeBtc()));
else
return makerFee + " (" + Res.get("shared.tradingFeeInBsqInfo", btcFormatter.formatCoinWithCode(makerFeeAsCoin)) + ")";
}
Expand Down Expand Up @@ -1018,7 +1024,7 @@ public String getFundsStructure() {
public String getTxFee() {
Coin txFeeAsCoin = dataModel.getTxFee();
return btcFormatter.formatCoinWithCode(txFeeAsCoin) +
GUIUtil.getPercentageOfTradeAmount(txFeeAsCoin, dataModel.getAmount().get());
GUIUtil.getPercentageOfTradeAmount(txFeeAsCoin, dataModel.getAmount().get(), Coin.ZERO);

}

Expand Down Expand Up @@ -1196,18 +1202,34 @@ private void updateSpinnerInfo() {
isWaitingForFunds.set(!waitingForFundsText.get().isEmpty());
}

private void updateBuyerSecurityDeposit() {
isMinBuyerSecurityDeposit.set(dataModel.isMinBuyerSecurityDeposit());

if (dataModel.isMinBuyerSecurityDeposit()) {
buyerSecurityDepositLabel.set(Res.get("createOffer.minSecurityDepositUsed"));
buyerSecurityDeposit.set(btcFormatter.formatCoin(Restrictions.getMinBuyerSecurityDepositAsCoin()));
} else {
buyerSecurityDepositLabel.set(getSecurityDepositLabel());
buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDeposit().get()));
}
}

private void updateButtonDisableState() {
log.debug("updateButtonDisableState");
boolean inputDataValid = isBtcInputValid(amount.get()).isValid &&
isBtcInputValid(minAmount.get()).isValid &&
isPriceInputValid(price.get()).isValid &&
securityDepositValidator.validate(buyerSecurityDeposit.get()).isValid &&
dataModel.getPrice().get() != null &&
dataModel.getPrice().get().getValue() != 0 &&
isVolumeInputValid(volume.get()).isValid &&
isVolumeInputValid(DisplayUtils.formatVolume(dataModel.getMinVolume().get())).isValid &&
dataModel.isMinAmountLessOrEqualAmount();

// validating the percentage deposit value only makes sense if it is actually used
if (!dataModel.isMinBuyerSecurityDeposit()) {
inputDataValid = inputDataValid && securityDepositValidator.validate(buyerSecurityDeposit.get()).isValid;
}

isNextButtonDisabled.set(!inputDataValid);
// boolean notSufficientFees = dataModel.isWalletFunded.get() && dataModel.isMainNet.get() && !dataModel.isFeeFromFundingTxSufficient.get();
//isPlaceOfferButtonDisabled.set(createOfferRequested || !inputDataValid || notSufficientFees);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import bisq.desktop.util.validation.BtcValidator;

import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.monetary.Price;
Expand All @@ -39,6 +40,7 @@
import bisq.core.offer.OfferUtil;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.Trade;
import bisq.core.user.Preferences;
Expand Down Expand Up @@ -707,7 +709,9 @@ String getTradeAmount() {

public String getSecurityDepositInfo() {
return btcFormatter.formatCoinWithCode(dataModel.getSecurityDeposit()) +
GUIUtil.getPercentageOfTradeAmount(dataModel.getSecurityDeposit(), dataModel.getAmount().get());
GUIUtil.getPercentageOfTradeAmount(dataModel.getSecurityDeposit(),
dataModel.getAmount().get(),
Restrictions.getMinBuyerSecurityDepositAsCoin());
}

public String getSecurityDepositWithCode() {
Expand All @@ -719,7 +723,8 @@ public String getTradeFee() {
final Coin takerFeeAsCoin = dataModel.getTakerFee();
final String takerFee = getFormatterForTakerFee().formatCoinWithCode(takerFeeAsCoin);
if (dataModel.isCurrencyForTakerFeeBtc())
return takerFee + GUIUtil.getPercentageOfTradeAmount(takerFeeAsCoin, dataModel.getAmount().get());
return takerFee + GUIUtil.getPercentageOfTradeAmount(takerFeeAsCoin, dataModel.getAmount().get(),
FeeService.getMinTakerFee(dataModel.isCurrencyForTakerFeeBtc()));
else
return takerFee + " (" + Res.get("shared.tradingFeeInBsqInfo", btcFormatter.formatCoinWithCode(takerFeeAsCoin)) + ")";
}
Expand All @@ -743,7 +748,8 @@ public String getTotalToPayInfo() {
public String getTxFee() {
Coin txFeeAsCoin = dataModel.getTotalTxFee();
return btcFormatter.formatCoinWithCode(txFeeAsCoin) +
GUIUtil.getPercentageOfTradeAmount(txFeeAsCoin, dataModel.getAmount().get());
GUIUtil.getPercentageOfTradeAmount(txFeeAsCoin, dataModel.getAmount().get(),
Coin.ZERO);

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@

import bisq.core.account.witness.AccountAgeWitness;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.network.MessageState;
import bisq.core.offer.Offer;
import bisq.core.provider.fee.FeeService;
import bisq.core.trade.Contract;
import bisq.core.trade.Trade;
import bisq.core.trade.closed.ClosedTradableManager;
Expand Down Expand Up @@ -291,7 +293,8 @@ public String getFiatVolume() {
public String getTxFee() {
if (trade != null && trade.getTradeAmount() != null) {
Coin txFee = dataModel.getTxFee();
String percentage = GUIUtil.getPercentageOfTradeAmount(txFee, trade.getTradeAmount());
String percentage = GUIUtil.getPercentageOfTradeAmount(txFee, trade.getTradeAmount(),
Coin.ZERO);
return btcFormatter.formatCoinWithCode(txFee) + percentage;
} else {
return "";
Expand All @@ -303,7 +306,13 @@ public String getTradeFee() {
if (dataModel.isMaker() && dataModel.getOffer().isCurrencyForMakerFeeBtc() ||
!dataModel.isMaker() && dataModel.getTrade().isCurrencyForTakerFeeBtc()) {
Coin tradeFeeInBTC = dataModel.getTradeFeeInBTC();
String percentage = GUIUtil.getPercentageOfTradeAmount(tradeFeeInBTC, trade.getTradeAmount());

Coin minTradeFee = dataModel.isMaker() ?
FeeService.getMinMakerFee(true) :
FeeService.getMinTakerFee(true);

String percentage = GUIUtil.getPercentageOfTradeAmount(tradeFeeInBTC, trade.getTradeAmount(),
minTradeFee);
return btcFormatter.formatCoinWithCode(tradeFeeInBTC) + percentage;
} else {
return bsqFormatter.formatCoinWithCode(dataModel.getTradeFeeAsBsq());
Expand All @@ -320,7 +329,14 @@ public String getSecurityDeposit() {
Coin securityDeposit = dataModel.isBuyer() ?
offer.getBuyerSecurityDeposit()
: offer.getSellerSecurityDeposit();
String percentage = GUIUtil.getPercentageOfTradeAmount(securityDeposit, trade.getTradeAmount());

Coin minSecurityDeposit = dataModel.isBuyer() ?
Restrictions.getMinBuyerSecurityDepositAsCoin() :
Restrictions.getMinSellerSecurityDepositAsCoin();

String percentage = GUIUtil.getPercentageOfTradeAmount(securityDeposit,
trade.getTradeAmount(),
minSecurityDeposit);
return btcFormatter.formatCoinWithCode(securityDeposit) + percentage;
} else {
return "";
Expand Down
12 changes: 9 additions & 3 deletions desktop/src/main/java/bisq/desktop/util/GUIUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@
import bisq.core.user.DontShowAgainLookup;
import bisq.core.user.Preferences;
import bisq.core.user.User;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.BsqFormatter;
import bisq.core.util.coin.CoinFormatter;
import bisq.core.util.coin.CoinUtil;
import bisq.core.util.FormattingUtils;

import bisq.network.p2p.P2PService;

Expand Down Expand Up @@ -662,9 +662,15 @@ private static void doOpenWebPage(String target) {
}
}

public static String getPercentageOfTradeAmount(Coin fee, Coin tradeAmount) {
return " (" + getPercentage(fee, tradeAmount) +
public static String getPercentageOfTradeAmount(Coin fee, Coin tradeAmount, Coin minFee) {
String result = " (" + getPercentage(fee, tradeAmount) +
" " + Res.get("guiUtil.ofTradeAmount") + ")";

if (!fee.isGreaterThan(minFee)) {
result = " " + Res.get("guiUtil.requiredMinimum");
}

return result;
}

public static String getPercentage(Coin part, Coin total) {
Expand Down
36 changes: 36 additions & 0 deletions desktop/src/test/java/bisq/desktop/util/GUIUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
import bisq.core.user.DontShowAgainLookup;
import bisq.core.user.Preferences;

import org.bitcoinj.core.Coin;
import org.bitcoinj.core.CoinMaker;

import javafx.util.StringConverter;

import java.util.HashMap;
Expand All @@ -35,6 +38,11 @@

import static bisq.desktop.maker.TradeCurrencyMakers.bitcoin;
import static bisq.desktop.maker.TradeCurrencyMakers.euro;
import static com.natpryce.makeiteasy.MakeItEasy.a;
import static com.natpryce.makeiteasy.MakeItEasy.make;
import static com.natpryce.makeiteasy.MakeItEasy.with;
import static org.bitcoinj.core.CoinMaker.oneBitcoin;
import static org.bitcoinj.core.CoinMaker.satoshis;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -103,4 +111,32 @@ public void testOpenURLWithoutCampaignParameters() throws Exception {
assertEquals("https://www.github.com", captor.getValue().toString());
*/
}

@Test
public void percentageOfTradeAmount_higherFeeAsMin() {

Coin fee = make(a(CoinMaker.Coin).but(with(satoshis, 20000L)));
Coin min = make(a(CoinMaker.Coin).but(with(satoshis, 10000L)));

assertEquals(" (0.02% of trade amount)", GUIUtil.getPercentageOfTradeAmount(fee, oneBitcoin, min));
}

@Test
public void percentageOfTradeAmount_minFee() {

Coin fee = make(a(CoinMaker.Coin).but(with(satoshis, 10000L)));
Coin min = make(a(CoinMaker.Coin).but(with(satoshis, 10000L)));

assertEquals(" (required minimum)",
GUIUtil.getPercentageOfTradeAmount(fee, oneBitcoin, min));
}

@Test
public void percentageOfTradeAmount_minFeeZERO() {

Coin fee = make(a(CoinMaker.Coin).but(with(satoshis, 10000L)));

assertEquals(" (0.01% of trade amount)",
GUIUtil.getPercentageOfTradeAmount(fee, oneBitcoin, Coin.ZERO));
}
}

0 comments on commit 81a14d3

Please sign in to comment.