mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-20 02:12:00 +01:00
Merge pull request #2498 from ManfredKarrer/make-buyer-security-deposit-percentage-based
Use percentage based value for security deposits
This commit is contained in:
commit
ac3cae101e
@ -1278,7 +1278,7 @@ message PreferencesPayload {
|
||||
string bitcoin_nodes = 27;
|
||||
repeated string ignore_traders_list = 28;
|
||||
string directory_chooser_path = 29;
|
||||
int64 buyer_security_deposit_as_long = 30;
|
||||
int64 buyer_security_deposit_as_long = 30; // Deprectated: Superseded by buyerSecurityDepositAsPercent
|
||||
bool use_animations = 31;
|
||||
PaymentAccount selectedPayment_account_for_createOffer = 32;
|
||||
bool pay_fee_in_Btc = 33;
|
||||
@ -1298,6 +1298,7 @@ message PreferencesPayload {
|
||||
string rpc_user = 47;
|
||||
string rpc_pw = 48;
|
||||
string take_offer_selected_payment_account_id = 49;
|
||||
double buyer_security_deposit_as_percent = 50;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -23,9 +23,7 @@ import org.bitcoinj.core.Coin;
|
||||
|
||||
public class Restrictions {
|
||||
private static Coin MIN_TRADE_AMOUNT;
|
||||
private static Coin MAX_BUYER_SECURITY_DEPOSIT;
|
||||
private static Coin MIN_BUYER_SECURITY_DEPOSIT;
|
||||
private static Coin DEFAULT_BUYER_SECURITY_DEPOSIT;
|
||||
// For the seller we use a fixed one as there is no way the seller can cancel the trade
|
||||
// To make it editable would just increase complexity.
|
||||
private static Coin SELLER_SECURITY_DEPOSIT;
|
||||
@ -48,32 +46,38 @@ public class Restrictions {
|
||||
|
||||
public static Coin getMinTradeAmount() {
|
||||
if (MIN_TRADE_AMOUNT == null)
|
||||
MIN_TRADE_AMOUNT = Coin.valueOf(10_000); // 2 USD @ 20000 USD/BTC
|
||||
MIN_TRADE_AMOUNT = Coin.valueOf(10_000); // 0,4 USD @ 4000 USD/BTC
|
||||
return MIN_TRADE_AMOUNT;
|
||||
}
|
||||
|
||||
// Can be reduced but not increased. Otherwise would break existing offers!
|
||||
public static Coin getMaxBuyerSecurityDeposit() {
|
||||
if (MAX_BUYER_SECURITY_DEPOSIT == null)
|
||||
MAX_BUYER_SECURITY_DEPOSIT = Coin.valueOf(5_000_000); // 1000 USD @ 20000 USD/BTC
|
||||
return MAX_BUYER_SECURITY_DEPOSIT;
|
||||
public static double getDefaultBuyerSecurityDepositAsPercent() {
|
||||
return 0.02; // 2% of trade amount. For a 1 BTC trade it is about 80 USD @ 4000 USD/BTC.
|
||||
}
|
||||
|
||||
public static Coin getMinBuyerSecurityDeposit() {
|
||||
public static double getMinBuyerSecurityDepositAsPercent() {
|
||||
return 0.0005; // 0.05% of trade amount. For a 1 BTC trade it is about 2 USD @ 4000 USD/BTC but MIN_BUYER_SECURITY_DEPOSIT would require 0.001 BTC anyway (4 USD)
|
||||
}
|
||||
|
||||
public static double getMaxBuyerSecurityDepositAsPercent() {
|
||||
return 0.1; // 10% of trade amount. For a 1 BTC trade it is about 400 USD @ 4000 USD/BTC
|
||||
}
|
||||
|
||||
// We use MIN_BUYER_SECURITY_DEPOSIT as well as lower bound in case of small trade amounts.
|
||||
// So 0.0005 BTC is the min. buyer security deposit even with amount of 0.0001 BTC and 0.05% percentage value.
|
||||
public static Coin getMinBuyerSecurityDepositAsCoin() {
|
||||
if (MIN_BUYER_SECURITY_DEPOSIT == null)
|
||||
MIN_BUYER_SECURITY_DEPOSIT = Coin.valueOf(50_000); // 10 USD @ 20000 USD/BTC
|
||||
MIN_BUYER_SECURITY_DEPOSIT = Coin.parseCoin("0.001"); // 0.001 BTC about 4 USD @ 4000 USD/BTC
|
||||
return MIN_BUYER_SECURITY_DEPOSIT;
|
||||
}
|
||||
|
||||
public static Coin getDefaultBuyerSecurityDeposit() {
|
||||
if (DEFAULT_BUYER_SECURITY_DEPOSIT == null)
|
||||
DEFAULT_BUYER_SECURITY_DEPOSIT = Coin.valueOf(1_000_000); // 200 EUR @ 20000 USD/BTC
|
||||
return DEFAULT_BUYER_SECURITY_DEPOSIT;
|
||||
|
||||
public static double getSellerSecurityDepositAsPercent() {
|
||||
return 0.005; // 0.5% of trade amount.
|
||||
}
|
||||
|
||||
public static Coin getSellerSecurityDeposit() {
|
||||
public static Coin getMinSellerSecurityDepositAsCoin() {
|
||||
if (SELLER_SECURITY_DEPOSIT == null)
|
||||
SELLER_SECURITY_DEPOSIT = Coin.valueOf(300_000); // 60 USD @ 20000 USD/BTC
|
||||
SELLER_SECURITY_DEPOSIT = Coin.parseCoin("0.005"); // 0.005 BTC about 20 USD @ 4000 USD/BTC
|
||||
return SELLER_SECURITY_DEPOSIT;
|
||||
}
|
||||
}
|
||||
|
@ -347,18 +347,18 @@ public class OfferUtil {
|
||||
|
||||
public static void validateOfferData(FilterManager filterManager,
|
||||
P2PService p2PService,
|
||||
Coin buyerSecurityDepositAsCoin,
|
||||
double buyerSecurityDeposit,
|
||||
PaymentAccount paymentAccount,
|
||||
String currencyCode,
|
||||
Coin makerFeeAsCoin) {
|
||||
checkNotNull(makerFeeAsCoin, "makerFee must not be null");
|
||||
checkNotNull(p2PService.getAddress(), "Address must not be null");
|
||||
checkArgument(buyerSecurityDepositAsCoin.compareTo(Restrictions.getMaxBuyerSecurityDeposit()) <= 0,
|
||||
"securityDeposit must be not exceed " +
|
||||
Restrictions.getMaxBuyerSecurityDeposit().toFriendlyString());
|
||||
checkArgument(buyerSecurityDepositAsCoin.compareTo(Restrictions.getMinBuyerSecurityDeposit()) >= 0,
|
||||
"securityDeposit must be not be less than " +
|
||||
Restrictions.getMinBuyerSecurityDeposit().toFriendlyString());
|
||||
checkArgument(buyerSecurityDeposit <= Restrictions.getMaxBuyerSecurityDepositAsPercent(),
|
||||
"securityDeposit must not exceed " +
|
||||
Restrictions.getMaxBuyerSecurityDepositAsPercent());
|
||||
checkArgument(buyerSecurityDeposit >= Restrictions.getMinBuyerSecurityDepositAsPercent(),
|
||||
"securityDeposit must not be less than " +
|
||||
Restrictions.getMinBuyerSecurityDepositAsPercent());
|
||||
checkArgument(!filterManager.isCurrencyBanned(currencyCode),
|
||||
Res.get("offerbook.warning.currencyBanned"));
|
||||
checkArgument(!filterManager.isPaymentMethodBanned(paymentAccount.getPaymentMethod()),
|
||||
|
@ -619,9 +619,9 @@ public abstract class Trade implements Tradable, Model {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void setState(State state) {
|
||||
log.info("Set new state at {} (id={}): {}", this.getClass().getSimpleName(), getShortId(), state);
|
||||
log.debug("Set new state at {} (id={}): {}", this.getClass().getSimpleName(), getShortId(), state);
|
||||
if (state.getPhase().ordinal() < this.state.getPhase().ordinal()) {
|
||||
final String message = "We got a state change to a previous phase.\n" +
|
||||
String message = "We got a state change to a previous phase.\n" +
|
||||
"Old state is: " + this.state + ". New state is: " + state;
|
||||
log.warn(message);
|
||||
}
|
||||
|
@ -39,8 +39,6 @@ import bisq.common.proto.persistable.PersistedDataHost;
|
||||
import bisq.common.storage.Storage;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
@ -487,10 +485,10 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
withdrawalTxFeeInBytesProperty.set(withdrawalTxFeeInBytes);
|
||||
}
|
||||
|
||||
public void setBuyerSecurityDepositAsLong(long buyerSecurityDepositAsLong) {
|
||||
prefPayload.setBuyerSecurityDepositAsLong(Math.min(Restrictions.getMaxBuyerSecurityDeposit().value,
|
||||
Math.max(Restrictions.getMinBuyerSecurityDeposit().value,
|
||||
buyerSecurityDepositAsLong)));
|
||||
public void setBuyerSecurityDepositAsPercent(double buyerSecurityDepositAsPercent) {
|
||||
double max = Restrictions.getMaxBuyerSecurityDepositAsPercent();
|
||||
double min = Restrictions.getMinBuyerSecurityDepositAsPercent();
|
||||
prefPayload.setBuyerSecurityDepositAsPercent(Math.min(max, Math.max(min, buyerSecurityDepositAsPercent)));
|
||||
persist();
|
||||
}
|
||||
|
||||
@ -696,8 +694,9 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
return withdrawalTxFeeInBytesProperty;
|
||||
}
|
||||
|
||||
public Coin getBuyerSecurityDepositAsCoin() {
|
||||
return Coin.valueOf(prefPayload.getBuyerSecurityDepositAsLong());
|
||||
public double getBuyerSecurityDepositAsPercent() {
|
||||
double value = prefPayload.getBuyerSecurityDepositAsPercent();
|
||||
return value == 0 ? Restrictions.getDefaultBuyerSecurityDepositAsPercent() : value;
|
||||
}
|
||||
|
||||
//TODO remove and use isPayFeeInBtc instead
|
||||
@ -775,8 +774,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
|
||||
void setWithdrawalTxFeeInBytes(long withdrawalTxFeeInBytes);
|
||||
|
||||
void setBuyerSecurityDepositAsLong(long buyerSecurityDepositAsLong);
|
||||
|
||||
void setSelectedPaymentAccountForCreateOffer(@Nullable PaymentAccount paymentAccount);
|
||||
|
||||
void setBsqBlockChainExplorer(BlockChainExplorer bsqBlockChainExplorer);
|
||||
@ -832,5 +829,9 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
void setRpcPw(String value);
|
||||
|
||||
void setTakeOfferSelectedPaymentAccountId(String value);
|
||||
|
||||
void setBuyerSecurityDepositAsPercent(double buyerSecurityDepositAsPercent);
|
||||
|
||||
double getBuyerSecurityDepositAsPercent();
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,10 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
||||
private String bitcoinNodes = "";
|
||||
private List<String> ignoreTradersList = new ArrayList<>();
|
||||
private String directoryChooserPath;
|
||||
private long buyerSecurityDepositAsLong = Restrictions.getDefaultBuyerSecurityDeposit().value;
|
||||
|
||||
@Deprecated // Superseded by buyerSecurityDepositAsPercent
|
||||
private long buyerSecurityDepositAsLong;
|
||||
|
||||
private boolean useAnimations;
|
||||
@Nullable
|
||||
private PaymentAccount selectedPaymentAccountForCreateOffer;
|
||||
@ -117,6 +120,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
||||
String rpcPw;
|
||||
@Nullable
|
||||
String takeOfferSelectedPaymentAccountId;
|
||||
private double buyerSecurityDepositAsPercent = Restrictions.getDefaultBuyerSecurityDepositAsPercent();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -172,7 +176,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
||||
.setUseMarketNotifications(useMarketNotifications)
|
||||
.setUsePriceNotifications(usePriceNotifications)
|
||||
.setUseStandbyMode(useStandbyMode)
|
||||
.setIsDaoFullNode(isDaoFullNode);
|
||||
.setIsDaoFullNode(isDaoFullNode)
|
||||
.setBuyerSecurityDepositAsPercent(buyerSecurityDepositAsPercent);
|
||||
Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory);
|
||||
Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((PB.TradeCurrency) e.toProtoMessage()));
|
||||
Optional.ofNullable(offerBookChartScreenCurrencyCode).ifPresent(builder::setOfferBookChartScreenCurrencyCode);
|
||||
@ -253,6 +258,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
||||
proto.getIsDaoFullNode(),
|
||||
proto.getRpcUser().isEmpty() ? null : proto.getRpcUser(),
|
||||
proto.getRpcPw().isEmpty() ? null : proto.getRpcPw(),
|
||||
proto.getTakeOfferSelectedPaymentAccountId().isEmpty() ? null : proto.getTakeOfferSelectedPaymentAccountId());
|
||||
proto.getTakeOfferSelectedPaymentAccountId().isEmpty() ? null : proto.getTakeOfferSelectedPaymentAccountId(),
|
||||
proto.getBuyerSecurityDepositAsPercent());
|
||||
}
|
||||
}
|
||||
|
@ -41,4 +41,24 @@ public class CoinUtil {
|
||||
public static double getFeePerByte(Coin miningFee, int txSize) {
|
||||
return MathUtils.roundDouble(((double) miningFee.value / (double) txSize), 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value Btc amount to be converted to percent value. E.g. 0.01 BTC is 1% (of 1 BTC)
|
||||
* @return The percentage value as double (e.g. 1% is 0.01)
|
||||
*/
|
||||
public static double getAsPercentPerBtc(Coin value) {
|
||||
double asDouble = (double) value.value;
|
||||
double btcAsDouble = (double) Coin.COIN.value;
|
||||
return MathUtils.roundDouble(asDouble / btcAsDouble, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param percent The percentage value as double (e.g. 1% is 0.01)
|
||||
* @param amount The amount as Coin for the percentage calculation
|
||||
* @return The percentage as Coin (e.g. 1% of 1 BTC is 0.01 BTC)
|
||||
*/
|
||||
public static Coin getPercentOfAmountAsCoin(double percent, Coin amount) {
|
||||
double amountAsDouble = (double) amount.value;
|
||||
return Coin.valueOf(Math.round(percent * amountAsDouble));
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +368,7 @@ createOffer.amountPriceBox.amountDescription=Amount of BTC to {0}
|
||||
createOffer.amountPriceBox.buy.volumeDescription=Amount in {0} to spend
|
||||
createOffer.amountPriceBox.sell.volumeDescription=Amount in {0} to receive
|
||||
createOffer.amountPriceBox.minAmountDescription=Minimum amount of BTC
|
||||
createOffer.securityDeposit.prompt=Security deposit in BTC
|
||||
createOffer.securityDeposit.prompt=Security deposit
|
||||
createOffer.fundsBox.title=Fund your offer
|
||||
createOffer.fundsBox.offerFee=Trade fee
|
||||
createOffer.fundsBox.networkFee=Mining fee
|
||||
@ -416,7 +416,8 @@ createOffer.priceOutSideOfDeviation=The price you have entered is outside the ma
|
||||
createOffer.changePrice=Change price
|
||||
createOffer.tac=With publishing this offer I agree to trade with any trader who fulfills the conditions as defined in this screen.
|
||||
createOffer.currencyForFee=Trade fee
|
||||
createOffer.setDeposit=Set buyer's security deposit
|
||||
createOffer.setDeposit=Set buyer's security deposit (%)
|
||||
createOffer.securityDepositInfo=Your buyer''s security deposit will be {0}
|
||||
|
||||
|
||||
####################################################################
|
||||
|
@ -530,7 +530,7 @@ bg color of non edit textFields: fafafa
|
||||
-fx-pref-width: 30;
|
||||
}
|
||||
|
||||
.jfx-badge .label {
|
||||
.jfx-badge .badge-pane .label {
|
||||
-fx-font-weight: bold;
|
||||
-fx-font-size: 0.692em;
|
||||
-fx-text-fill: -bs-rd-white;
|
||||
|
@ -157,25 +157,27 @@ public class InfoInputTextField extends AnchorPane {
|
||||
|
||||
private void setActionHandlers(Node node) {
|
||||
|
||||
currentIcon.setManaged(true);
|
||||
currentIcon.setVisible(true);
|
||||
if (node != null) {
|
||||
currentIcon.setManaged(true);
|
||||
currentIcon.setVisible(true);
|
||||
|
||||
// As we don't use binding here we need to recreate it on mouse over to reflect the current state
|
||||
currentIcon.setOnMouseEntered(e -> {
|
||||
hidePopover = false;
|
||||
showPopOver(node);
|
||||
});
|
||||
currentIcon.setOnMouseExited(e -> {
|
||||
if (popover != null)
|
||||
popover.hide();
|
||||
hidePopover = true;
|
||||
UserThread.runAfter(() -> {
|
||||
if (hidePopover) {
|
||||
// As we don't use binding here we need to recreate it on mouse over to reflect the current state
|
||||
currentIcon.setOnMouseEntered(e -> {
|
||||
hidePopover = false;
|
||||
showPopOver(node);
|
||||
});
|
||||
currentIcon.setOnMouseExited(e -> {
|
||||
if (popover != null)
|
||||
popover.hide();
|
||||
hidePopover = false;
|
||||
}
|
||||
}, 250, TimeUnit.MILLISECONDS);
|
||||
});
|
||||
hidePopover = true;
|
||||
UserThread.runAfter(() -> {
|
||||
if (hidePopover) {
|
||||
popover.hide();
|
||||
hidePopover = false;
|
||||
}
|
||||
}, 250, TimeUnit.MILLISECONDS);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void showPopOver(Node node) {
|
||||
|
34
desktop/src/main/java/bisq/desktop/components/NewBadge.java
Normal file
34
desktop/src/main/java/bisq/desktop/components/NewBadge.java
Normal file
@ -0,0 +1,34 @@
|
||||
package bisq.desktop.components;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
import com.jfoenix.controls.JFXBadge;
|
||||
|
||||
import javafx.scene.Node;
|
||||
|
||||
import javafx.collections.MapChangeListener;
|
||||
|
||||
public class NewBadge extends JFXBadge {
|
||||
|
||||
private final String key;
|
||||
|
||||
public NewBadge(Node control, String key, Preferences preferences) {
|
||||
super(control);
|
||||
|
||||
this.key = key;
|
||||
|
||||
setText(Res.get("shared.new"));
|
||||
getStyleClass().add("new");
|
||||
|
||||
setEnabled(!preferences.getDontShowAgainMap().containsKey(key));
|
||||
refreshBadge();
|
||||
|
||||
preferences.getDontShowAgainMapAsObservable().addListener((MapChangeListener<? super String, ? super Boolean>) change -> {
|
||||
if (change.getKey().equals(key)) {
|
||||
setEnabled(!change.wasAdded());
|
||||
refreshBadge();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
package bisq.desktop.components.paymentmethods;
|
||||
|
||||
import bisq.desktop.components.InputTextField;
|
||||
import bisq.desktop.components.NewBadge;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.util.FormBuilder;
|
||||
import bisq.desktop.util.Layout;
|
||||
@ -34,6 +35,7 @@ import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.payload.AssetsAccountPayload;
|
||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
||||
import bisq.core.payment.validation.AltCoinAddressValidator;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.BSFormatter;
|
||||
import bisq.core.util.validation.InputValidator;
|
||||
|
||||
@ -45,8 +47,13 @@ import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
|
||||
import javafx.util.StringConverter;
|
||||
@ -55,14 +62,17 @@ import java.util.Optional;
|
||||
|
||||
import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextField;
|
||||
import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon;
|
||||
import static bisq.desktop.util.FormBuilder.addLabelCheckBox;
|
||||
import static bisq.desktop.util.FormBuilder.addTopLabelTextField;
|
||||
import static bisq.desktop.util.GUIUtil.getComboBoxButtonCell;
|
||||
|
||||
public class AssetsForm extends PaymentMethodForm {
|
||||
public static final String INSTANT_TRADE_NEWS = "instantTradeNews0.9.5";
|
||||
private final AssetAccount assetAccount;
|
||||
private final AltCoinAddressValidator altCoinAddressValidator;
|
||||
private final AssetService assetService;
|
||||
private final FilterManager filterManager;
|
||||
private final Preferences preferences;
|
||||
|
||||
private InputTextField addressInputTextField;
|
||||
private CheckBox tradeInstantCheckBox;
|
||||
@ -85,12 +95,14 @@ public class AssetsForm extends PaymentMethodForm {
|
||||
int gridRow,
|
||||
BSFormatter formatter,
|
||||
AssetService assetService,
|
||||
FilterManager filterManager) {
|
||||
FilterManager filterManager,
|
||||
Preferences preferences) {
|
||||
super(paymentAccount, accountAgeWitnessService, inputValidator, gridPane, gridRow, formatter);
|
||||
this.assetAccount = (AssetAccount) paymentAccount;
|
||||
this.altCoinAddressValidator = altCoinAddressValidator;
|
||||
this.assetService = assetService;
|
||||
this.filterManager = filterManager;
|
||||
this.preferences = preferences;
|
||||
|
||||
tradeInstant = paymentAccount instanceof InstantCryptoCurrencyAccount;
|
||||
}
|
||||
@ -102,7 +114,7 @@ public class AssetsForm extends PaymentMethodForm {
|
||||
addTradeCurrencyComboBox();
|
||||
currencyComboBox.setPrefWidth(250);
|
||||
|
||||
tradeInstantCheckBox = FormBuilder.addLabelCheckBox(gridPane, ++gridRow,
|
||||
tradeInstantCheckBox = addLabelCheckBox(gridPane, ++gridRow,
|
||||
Res.get("payment.altcoin.tradeInstantCheckbox"), 10);
|
||||
tradeInstantCheckBox.setSelected(tradeInstant);
|
||||
tradeInstantCheckBox.setOnAction(e -> {
|
||||
@ -111,6 +123,20 @@ public class AssetsForm extends PaymentMethodForm {
|
||||
new Popup<>().information(Res.get("payment.altcoin.tradeInstant.popup")).show();
|
||||
});
|
||||
|
||||
// add new badge for this new feature for this release
|
||||
// TODO: remove it with 0.9.6+
|
||||
gridPane.getChildren().remove(tradeInstantCheckBox);
|
||||
tradeInstantCheckBox.setPadding(new Insets(0, 40, 0, 0));
|
||||
|
||||
NewBadge instantTradeNewsBadge = new NewBadge(tradeInstantCheckBox, INSTANT_TRADE_NEWS, preferences);
|
||||
instantTradeNewsBadge.setAlignment(Pos.CENTER_LEFT);
|
||||
instantTradeNewsBadge.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
|
||||
|
||||
GridPane.setRowIndex(instantTradeNewsBadge, gridRow);
|
||||
GridPane.setHgrow(instantTradeNewsBadge, Priority.NEVER);
|
||||
GridPane.setMargin(instantTradeNewsBadge, new Insets(10, 0, 0, 0));
|
||||
gridPane.getChildren().add(instantTradeNewsBadge);
|
||||
|
||||
addressInputTextField = FormBuilder.addInputTextField(gridPane, ++gridRow,
|
||||
Res.get("payment.altcoin.address"));
|
||||
addressInputTextField.setValidator(altCoinAddressValidator);
|
||||
|
@ -37,6 +37,7 @@ import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.PaymentAccountFactory;
|
||||
import bisq.core.payment.payload.PaymentMethod;
|
||||
import bisq.core.payment.validation.AltCoinAddressValidator;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.BSFormatter;
|
||||
import bisq.core.util.validation.InputValidator;
|
||||
|
||||
@ -60,6 +61,7 @@ import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static bisq.desktop.components.paymentmethods.AssetsForm.INSTANT_TRADE_NEWS;
|
||||
import static bisq.desktop.util.FormBuilder.add2ButtonsAfterGroup;
|
||||
import static bisq.desktop.util.FormBuilder.add3ButtonsAfterGroup;
|
||||
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
|
||||
@ -74,6 +76,7 @@ public class AltCoinAccountsView extends PaymentAccountsView<GridPane, AltCoinAc
|
||||
private final AssetService assetService;
|
||||
private final FilterManager filterManager;
|
||||
private final BSFormatter formatter;
|
||||
private final Preferences preferences;
|
||||
|
||||
private PaymentMethodForm paymentMethodForm;
|
||||
private TitledGroupBg accountTitledGroupBg;
|
||||
@ -87,7 +90,8 @@ public class AltCoinAccountsView extends PaymentAccountsView<GridPane, AltCoinAc
|
||||
AccountAgeWitnessService accountAgeWitnessService,
|
||||
AssetService assetService,
|
||||
FilterManager filterManager,
|
||||
BSFormatter formatter) {
|
||||
BSFormatter formatter,
|
||||
Preferences preferences) {
|
||||
super(model);
|
||||
|
||||
this.inputValidator = inputValidator;
|
||||
@ -96,6 +100,7 @@ public class AltCoinAccountsView extends PaymentAccountsView<GridPane, AltCoinAc
|
||||
this.assetService = assetService;
|
||||
this.filterManager = filterManager;
|
||||
this.formatter = formatter;
|
||||
this.preferences = preferences;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -148,11 +153,15 @@ public class AltCoinAccountsView extends PaymentAccountsView<GridPane, AltCoinAc
|
||||
} else {
|
||||
new Popup<>().warning(Res.get("shared.accountNameAlreadyUsed")).show();
|
||||
}
|
||||
|
||||
preferences.dontShowAgain(INSTANT_TRADE_NEWS, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void onCancelNewAccount() {
|
||||
removeNewAccountForm();
|
||||
|
||||
preferences.dontShowAgain(INSTANT_TRADE_NEWS, true);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -228,7 +237,7 @@ public class AltCoinAccountsView extends PaymentAccountsView<GridPane, AltCoinAc
|
||||
|
||||
private PaymentMethodForm getPaymentMethodForm(PaymentAccount paymentAccount) {
|
||||
return new AssetsForm(paymentAccount, accountAgeWitnessService, altCoinAddressValidator,
|
||||
inputValidator, root, gridRow, formatter, assetService, filterManager);
|
||||
inputValidator, root, gridRow, formatter, assetService, filterManager, preferences);
|
||||
}
|
||||
|
||||
private void removeNewAccountForm() {
|
||||
|
@ -46,6 +46,7 @@ import bisq.core.trade.statistics.ReferralIdService;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.user.User;
|
||||
import bisq.core.util.BSFormatter;
|
||||
import bisq.core.util.CoinUtil;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
@ -62,11 +63,14 @@ import com.google.inject.Inject;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyStringProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleDoubleProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
@ -105,8 +109,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
private final BalanceListener btcBalanceListener;
|
||||
private final SetChangeListener<PaymentAccount> paymentAccountsChangeListener;
|
||||
|
||||
private final Coin sellerSecurityDeposit;
|
||||
|
||||
protected OfferPayload.Direction direction;
|
||||
protected TradeCurrency tradeCurrency;
|
||||
protected final StringProperty tradeCurrencyCode = new SimpleStringProperty();
|
||||
@ -119,7 +121,10 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
protected final ObjectProperty<Coin> minAmount = new SimpleObjectProperty<>();
|
||||
protected final ObjectProperty<Price> price = new SimpleObjectProperty<>();
|
||||
protected final ObjectProperty<Volume> volume = new SimpleObjectProperty<>();
|
||||
protected final ObjectProperty<Coin> buyerSecurityDeposit = new SimpleObjectProperty<>();
|
||||
|
||||
// Percentage value of buyer security deposit. E.g. 0.01 means 1% of trade amount
|
||||
protected final DoubleProperty buyerSecurityDeposit = new SimpleDoubleProperty();
|
||||
protected final DoubleProperty sellerSecurityDeposit = new SimpleDoubleProperty();
|
||||
|
||||
protected final ObservableList<PaymentAccount> paymentAccounts = FXCollections.observableArrayList();
|
||||
|
||||
@ -174,8 +179,8 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
addressEntry = btcWalletService.getOrCreateAddressEntry(offerId, AddressEntry.Context.OFFER_FUNDING);
|
||||
|
||||
useMarketBasedPrice.set(preferences.isUsePercentageBasedPrice());
|
||||
buyerSecurityDeposit.set(preferences.getBuyerSecurityDepositAsCoin());
|
||||
sellerSecurityDeposit = Restrictions.getSellerSecurityDeposit();
|
||||
buyerSecurityDeposit.set(preferences.getBuyerSecurityDepositAsPercent());
|
||||
sellerSecurityDeposit.set(Restrictions.getSellerSecurityDepositAsPercent());
|
||||
|
||||
btcBalanceListener = new BalanceListener(getAddressEntry().getAddress()) {
|
||||
@Override
|
||||
@ -230,13 +235,13 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
user.getPaymentAccountsAsObservable().addListener(paymentAccountsChangeListener);
|
||||
}
|
||||
|
||||
|
||||
private void removeListeners() {
|
||||
btcWalletService.removeBalanceListener(btcBalanceListener);
|
||||
bsqWalletService.removeBsqBalanceListener(this);
|
||||
user.getPaymentAccountsAsObservable().removeListener(paymentAccountsChangeListener);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -316,8 +321,8 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
String counterCurrencyCode = isCryptoCurrency ? Res.getBaseCurrencyCode() : currencyCode;
|
||||
|
||||
double marketPriceMarginParam = useMarketBasedPriceValue ? marketPriceMargin : 0;
|
||||
long amount = this.amount.get() != null ? this.amount.get().getValue() : 0L;
|
||||
long minAmount = this.minAmount.get() != null ? this.minAmount.get().getValue() : 0L;
|
||||
long amountAsLong = this.amount.get() != null ? this.amount.get().getValue() : 0L;
|
||||
long minAmountAsLong = this.minAmount.get() != null ? this.minAmount.get().getValue() : 0L;
|
||||
|
||||
List<String> acceptedCountryCodes = PaymentAccountUtil.getAcceptedCountryCodes(paymentAccount);
|
||||
List<String> acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount);
|
||||
@ -335,7 +340,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
long lowerClosePrice = 0;
|
||||
long upperClosePrice = 0;
|
||||
String hashOfChallenge = null;
|
||||
Coin buyerSecurityDepositAsCoin = buyerSecurityDeposit.get();
|
||||
|
||||
Coin makerFeeAsCoin = getMakerFee();
|
||||
|
||||
Map<String, String> extraDataMap = OfferUtil.getExtraDataMap(accountAgeWitnessService,
|
||||
@ -345,7 +350,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
|
||||
OfferUtil.validateOfferData(filterManager,
|
||||
p2PService,
|
||||
buyerSecurityDepositAsCoin,
|
||||
buyerSecurityDeposit.get(),
|
||||
paymentAccount,
|
||||
currencyCode,
|
||||
makerFeeAsCoin);
|
||||
@ -358,8 +363,8 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
priceAsLong,
|
||||
marketPriceMarginParam,
|
||||
useMarketBasedPriceValue,
|
||||
amount,
|
||||
minAmount,
|
||||
amountAsLong,
|
||||
minAmountAsLong,
|
||||
baseCurrencyCode,
|
||||
counterCurrencyCode,
|
||||
Lists.newArrayList(user.getAcceptedArbitratorAddresses()),
|
||||
@ -376,8 +381,8 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
txFeeFromFeeService.value,
|
||||
makerFeeAsCoin.value,
|
||||
isCurrencyForMakerFeeBtc(),
|
||||
buyerSecurityDepositAsCoin.value,
|
||||
sellerSecurityDeposit.value,
|
||||
getBuyerSecurityDepositAsCoin().value,
|
||||
getSellerSecurityDepositAsCoin().value,
|
||||
maxTradeLimit,
|
||||
maxTradePeriod,
|
||||
useAutoClose,
|
||||
@ -642,7 +647,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
}
|
||||
|
||||
Coin getSecurityDeposit() {
|
||||
return isBuyOffer() ? buyerSecurityDeposit.get() : sellerSecurityDeposit;
|
||||
return isBuyOffer() ? getBuyerSecurityDepositAsCoin() : getSellerSecurityDepositAsCoin();
|
||||
}
|
||||
|
||||
public boolean isBuyOffer() {
|
||||
@ -677,9 +682,9 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
this.volume.set(volume);
|
||||
}
|
||||
|
||||
void setBuyerSecurityDeposit(Coin buyerSecurityDeposit) {
|
||||
this.buyerSecurityDeposit.set(buyerSecurityDeposit);
|
||||
preferences.setBuyerSecurityDepositAsLong(buyerSecurityDeposit.value);
|
||||
void setBuyerSecurityDeposit(double value) {
|
||||
this.buyerSecurityDeposit.set(value);
|
||||
preferences.setBuyerSecurityDepositAsPercent(value);
|
||||
}
|
||||
|
||||
protected boolean isUseMarketBasedPriceValue() {
|
||||
@ -718,12 +723,34 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||
return useMarketBasedPrice;
|
||||
}
|
||||
|
||||
ReadOnlyObjectProperty<Coin> getBuyerSecurityDeposit() {
|
||||
ReadOnlyDoubleProperty getBuyerSecurityDeposit() {
|
||||
return buyerSecurityDeposit;
|
||||
}
|
||||
|
||||
Coin getSellerSecurityDeposit() {
|
||||
return sellerSecurityDeposit;
|
||||
protected Coin getBuyerSecurityDepositAsCoin() {
|
||||
Coin percentOfAmountAsCoin = CoinUtil.getPercentOfAmountAsCoin(buyerSecurityDeposit.get(), amount.get());
|
||||
return getBoundedBuyerSecurityDepositAsCoin(percentOfAmountAsCoin);
|
||||
}
|
||||
|
||||
Coin getSellerSecurityDepositAsCoin() {
|
||||
Coin amountAsCoin = this.amount.get();
|
||||
if (amountAsCoin == null)
|
||||
amountAsCoin = Coin.ZERO;
|
||||
|
||||
Coin percentOfAmountAsCoin = CoinUtil.getPercentOfAmountAsCoin(sellerSecurityDeposit.get(), amountAsCoin);
|
||||
return getBoundedSellerSecurityDepositAsCoin(percentOfAmountAsCoin);
|
||||
}
|
||||
|
||||
private Coin getBoundedBuyerSecurityDepositAsCoin(Coin value) {
|
||||
// We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the
|
||||
// MinBuyerSecurityDepositAsCoin from Restrictions.
|
||||
return Coin.valueOf(Math.max(Restrictions.getMinBuyerSecurityDepositAsCoin().value, value.value));
|
||||
}
|
||||
|
||||
private Coin getBoundedSellerSecurityDepositAsCoin(Coin value) {
|
||||
// We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the
|
||||
// MinSellerSecurityDepositAsCoin from Restrictions.
|
||||
return Coin.valueOf(Math.max(Restrictions.getMinSellerSecurityDepositAsCoin().value, value.value));
|
||||
}
|
||||
|
||||
ReadOnlyObjectProperty<Coin> totalToPayAsCoinProperty() {
|
||||
|
@ -28,6 +28,7 @@ import bisq.desktop.components.BusyAnimation;
|
||||
import bisq.desktop.components.FundsTextField;
|
||||
import bisq.desktop.components.InfoInputTextField;
|
||||
import bisq.desktop.components.InputTextField;
|
||||
import bisq.desktop.components.NewBadge;
|
||||
import bisq.desktop.components.TitledGroupBg;
|
||||
import bisq.desktop.main.MainView;
|
||||
import bisq.desktop.main.account.AccountView;
|
||||
@ -122,6 +123,7 @@ import static bisq.desktop.util.FormBuilder.*;
|
||||
import static javafx.beans.binding.Bindings.createStringBinding;
|
||||
|
||||
public abstract class MutableOfferView<M extends MutableOfferViewModel> extends ActivatableViewAndModel<AnchorPane, M> {
|
||||
public static final String BUYER_SECURITY_DEPOSIT_NEWS = "buyerSecurityDepositNews0.9.5";
|
||||
protected final Navigation navigation;
|
||||
private final Preferences preferences;
|
||||
private final Transitions transitions;
|
||||
@ -136,8 +138,9 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel> extends
|
||||
private BusyAnimation waitingForFundsSpinner;
|
||||
private AutoTooltipButton nextButton, cancelButton1, cancelButton2, placeOfferButton;
|
||||
private Button priceTypeToggleButton;
|
||||
private InputTextField buyerSecurityDepositInputTextField, fixedPriceTextField, marketBasedPriceTextField;
|
||||
protected InputTextField amountTextField, minAmountTextField, volumeTextField;
|
||||
private InputTextField fixedPriceTextField;
|
||||
private InputTextField marketBasedPriceTextField;
|
||||
protected InputTextField amountTextField, minAmountTextField, volumeTextField, buyerSecurityDepositInputTextField;
|
||||
private TextField currencyTextField;
|
||||
private AddressTextField addressTextField;
|
||||
private BalanceTextField balanceTextField;
|
||||
@ -161,7 +164,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel> extends
|
||||
priceAsPercentageFocusedListener, getShowWalletFundedNotificationListener,
|
||||
tradeFeeInBtcToggleListener, tradeFeeInBsqToggleListener, tradeFeeVisibleListener;
|
||||
private ChangeListener<String> tradeCurrencyCodeListener, errorMessageListener,
|
||||
marketPriceMarginListener, volumeListener;
|
||||
marketPriceMarginListener, volumeListener, buyerSecurityDepositInBTCListener;
|
||||
private ChangeListener<Number> marketPriceAvailableListener;
|
||||
private EventHandler<ActionEvent> currencyComboBoxSelectionHandler, paymentAccountsComboBoxSelectionHandler;
|
||||
private OfferView.CloseHandler closeHandler;
|
||||
@ -169,7 +172,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel> extends
|
||||
protected int gridRow = 0;
|
||||
private final List<Node> editOfferElements = new ArrayList<>();
|
||||
private boolean clearXchangeWarningDisplayed, isActivated;
|
||||
private InfoInputTextField marketBasedPriceInfoInputTextField, volumeInfoInputTextField;
|
||||
private InfoInputTextField marketBasedPriceInfoInputTextField, volumeInfoInputTextField,
|
||||
buyerSecurityDepositInfoInputTextField;
|
||||
private AutoTooltipSlideToggleButton tradeFeeInBtcToggle, tradeFeeInBsqToggle;
|
||||
private Text xIcon, fakeXIcon;
|
||||
|
||||
@ -761,6 +765,15 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel> extends
|
||||
}
|
||||
};
|
||||
|
||||
buyerSecurityDepositInBTCListener = (observable, oldValue, newValue) -> {
|
||||
if (!newValue.equals("")) {
|
||||
Label depositInBTCInfo = createPopoverLabel(Res.get("createOffer.securityDepositInfo", newValue));
|
||||
buyerSecurityDepositInfoInputTextField.setContentForInfoPopOver(depositInBTCInfo);
|
||||
} else {
|
||||
buyerSecurityDepositInfoInputTextField.setContentForInfoPopOver(null);
|
||||
}
|
||||
};
|
||||
|
||||
volumeListener = (observable, oldValue, newValue) -> {
|
||||
if (!newValue.equals("") && CurrencyUtil.isFiatCurrency(model.tradeCurrencyCode.get())) {
|
||||
volumeInfoInputTextField.setContentForPrivacyPopOver(createPopoverLabel(Res.get("offerbook.info.roundedFiatVolume")));
|
||||
@ -860,6 +873,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel> extends
|
||||
model.marketPriceMargin.addListener(marketPriceMarginListener);
|
||||
model.volume.addListener(volumeListener);
|
||||
model.isTradeFeeVisible.addListener(tradeFeeVisibleListener);
|
||||
model.buyerSecurityDepositInBTC.addListener(buyerSecurityDepositInBTCListener);
|
||||
|
||||
tradeFeeInBtcToggle.selectedProperty().addListener(tradeFeeInBtcToggleListener);
|
||||
tradeFeeInBsqToggle.selectedProperty().addListener(tradeFeeInBsqToggleListener);
|
||||
@ -892,6 +906,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel> extends
|
||||
model.marketPriceMargin.removeListener(marketPriceMarginListener);
|
||||
model.volume.removeListener(volumeListener);
|
||||
model.isTradeFeeVisible.removeListener(tradeFeeVisibleListener);
|
||||
model.buyerSecurityDepositInBTC.removeListener(buyerSecurityDepositInBTCListener);
|
||||
tradeFeeInBtcToggle.selectedProperty().removeListener(tradeFeeInBtcToggleListener);
|
||||
tradeFeeInBsqToggle.selectedProperty().removeListener(tradeFeeInBsqToggleListener);
|
||||
|
||||
@ -1029,7 +1044,12 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel> extends
|
||||
GridPane.setMargin(advancedOptionsBox, new Insets(Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0));
|
||||
gridPane.getChildren().add(advancedOptionsBox);
|
||||
|
||||
advancedOptionsBox.getChildren().addAll(getBuyerSecurityDepositBox(), getTradeFeeFieldsBox());
|
||||
// add new badge for this new feature for this release
|
||||
// TODO: remove it with 0.9.6+
|
||||
NewBadge securityDepositBoxWithNewBadge = new NewBadge(getBuyerSecurityDepositBox(),
|
||||
BUYER_SECURITY_DEPOSIT_NEWS, preferences);
|
||||
|
||||
advancedOptionsBox.getChildren().addAll(securityDepositBoxWithNewBadge, getTradeFeeFieldsBox());
|
||||
|
||||
|
||||
Tuple2<Button, Button> tuple = add2ButtonsAfterGroup(gridPane, ++gridRow,
|
||||
@ -1099,16 +1119,19 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel> extends
|
||||
}
|
||||
|
||||
private VBox getBuyerSecurityDepositBox() {
|
||||
Tuple3<HBox, InputTextField, Label> tuple = getEditableValueBox(
|
||||
Tuple3<HBox, InfoInputTextField, Label> tuple = getEditableValueBoxWithInfo(
|
||||
Res.get("createOffer.securityDeposit.prompt"));
|
||||
buyerSecurityDepositInputTextField = tuple.second;
|
||||
Label buyerSecurityDepositBtcLabel = tuple.third;
|
||||
buyerSecurityDepositInfoInputTextField = tuple.second;
|
||||
buyerSecurityDepositInputTextField = buyerSecurityDepositInfoInputTextField.getInputTextField();
|
||||
Label buyerSecurityDepositPercentageLabel = tuple.third;
|
||||
// getEditableValueBox delivers BTC, so we overwrite it with %
|
||||
buyerSecurityDepositPercentageLabel.setText("%");
|
||||
|
||||
VBox depositBox = getTradeInputBox(tuple.first, Res.get("createOffer.setDeposit")).second;
|
||||
depositBox.setMaxWidth(310);
|
||||
|
||||
editOfferElements.add(buyerSecurityDepositInputTextField);
|
||||
editOfferElements.add(buyerSecurityDepositBtcLabel);
|
||||
editOfferElements.add(buyerSecurityDepositPercentageLabel);
|
||||
|
||||
return depositBox;
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
public final StringProperty amount = new SimpleStringProperty();
|
||||
public final StringProperty minAmount = new SimpleStringProperty();
|
||||
final StringProperty buyerSecurityDeposit = new SimpleStringProperty();
|
||||
final String sellerSecurityDeposit;
|
||||
final StringProperty buyerSecurityDepositInBTC = new SimpleStringProperty();
|
||||
|
||||
// Price in the viewModel is always dependent on fiat/altcoin: Fiat Fiat/BTC, for altcoins we use inverted price.
|
||||
// The domain (dataModel) uses always the same price model (otherCurrencyBTC)
|
||||
@ -158,7 +158,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
private ChangeListener<Coin> minAmountAsCoinListener;
|
||||
private ChangeListener<Price> priceListener;
|
||||
private ChangeListener<Volume> volumeListener;
|
||||
private ChangeListener<Coin> securityDepositAsCoinListener;
|
||||
private ChangeListener<Number> securityDepositAsDoubleListener;
|
||||
|
||||
private ChangeListener<Boolean> isWalletFundedListener;
|
||||
//private ChangeListener<Coin> feeFromFundingTxListener;
|
||||
@ -209,7 +209,6 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
this.bsqFormatter = bsqFormatter;
|
||||
|
||||
paymentLabel = Res.get("createOffer.fundsBox.paymentLabel", dataModel.shortOfferId);
|
||||
sellerSecurityDeposit = btcFormatter.formatCoin(dataModel.getSellerSecurityDeposit());
|
||||
|
||||
if (dataModel.getAddressEntry() != null) {
|
||||
addressAsString = dataModel.getAddressEntry().getAddressString();
|
||||
@ -223,7 +222,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
if (DevEnv.isDevMode()) {
|
||||
UserThread.runAfter(() -> {
|
||||
amount.set("1");
|
||||
price.set("0.0002");
|
||||
price.set("0.03");
|
||||
minAmount.set(amount.get());
|
||||
onFocusOutPriceAsPercentageTextField(true, false);
|
||||
applyMakerFee();
|
||||
@ -422,10 +421,13 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
|
||||
|
||||
amountAsCoinListener = (ov, oldValue, newValue) -> {
|
||||
if (newValue != null)
|
||||
if (newValue != null) {
|
||||
amount.set(btcFormatter.formatCoin(newValue));
|
||||
else
|
||||
buyerSecurityDepositInBTC.set(btcFormatter.formatCoinWithCode(dataModel.getBuyerSecurityDepositAsCoin()));
|
||||
} else {
|
||||
amount.set("");
|
||||
buyerSecurityDepositInBTC.set("");
|
||||
}
|
||||
|
||||
applyMakerFee();
|
||||
};
|
||||
@ -456,11 +458,14 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
applyMakerFee();
|
||||
};
|
||||
|
||||
securityDepositAsCoinListener = (ov, oldValue, newValue) -> {
|
||||
if (newValue != null)
|
||||
buyerSecurityDeposit.set(btcFormatter.formatCoin(newValue));
|
||||
else
|
||||
securityDepositAsDoubleListener = (ov, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
buyerSecurityDeposit.set(btcFormatter.formatToPercent((double) newValue));
|
||||
buyerSecurityDepositInBTC.set(btcFormatter.formatCoinWithCode(dataModel.getBuyerSecurityDepositAsCoin()));
|
||||
} else {
|
||||
buyerSecurityDeposit.set("");
|
||||
buyerSecurityDepositInBTC.set("");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -543,7 +548,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
dataModel.getMinAmount().addListener(minAmountAsCoinListener);
|
||||
dataModel.getPrice().addListener(priceListener);
|
||||
dataModel.getVolume().addListener(volumeListener);
|
||||
dataModel.getBuyerSecurityDeposit().addListener(securityDepositAsCoinListener);
|
||||
dataModel.getBuyerSecurityDeposit().addListener(securityDepositAsDoubleListener);
|
||||
|
||||
// dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
|
||||
dataModel.getIsBtcWalletFunded().addListener(isWalletFundedListener);
|
||||
@ -565,7 +570,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
dataModel.getMinAmount().removeListener(minAmountAsCoinListener);
|
||||
dataModel.getPrice().removeListener(priceListener);
|
||||
dataModel.getVolume().removeListener(volumeListener);
|
||||
dataModel.getBuyerSecurityDeposit().removeListener(securityDepositAsCoinListener);
|
||||
dataModel.getBuyerSecurityDeposit().removeListener(securityDepositAsDoubleListener);
|
||||
|
||||
//dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
|
||||
dataModel.getIsBtcWalletFunded().removeListener(isWalletFundedListener);
|
||||
@ -593,7 +598,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
amountDescription = Res.get("createOffer.amountPriceBox.amountDescription",
|
||||
isBuy ? Res.get("shared.buy") : Res.get("shared.sell"));
|
||||
|
||||
buyerSecurityDeposit.set(btcFormatter.formatCoin(dataModel.getBuyerSecurityDeposit().get()));
|
||||
buyerSecurityDeposit.set(btcFormatter.formatToPercent(dataModel.getBuyerSecurityDeposit().get()));
|
||||
|
||||
applyMakerFee();
|
||||
return result;
|
||||
@ -834,22 +839,22 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
InputValidator.ValidationResult result = securityDepositValidator.validate(buyerSecurityDeposit.get());
|
||||
buyerSecurityDepositValidationResult.set(result);
|
||||
if (result.isValid) {
|
||||
Coin defaultSecurityDeposit = Restrictions.getDefaultBuyerSecurityDeposit();
|
||||
String key = "buyerSecurityDepositLowerAsDefault";
|
||||
if (preferences.showAgain(key) &&
|
||||
btcFormatter.parseToCoin(buyerSecurityDeposit.get()).compareTo(defaultSecurityDeposit) < 0) {
|
||||
final String postfix = dataModel.isBuyOffer() ?
|
||||
double defaultSecurityDeposit = Restrictions.getDefaultBuyerSecurityDepositAsPercent();
|
||||
String key = "buyerSecurityDepositIsLowerAsDefault";
|
||||
double depositAsDouble = btcFormatter.parsePercentStringToDouble(buyerSecurityDeposit.get());
|
||||
if (preferences.showAgain(key) && depositAsDouble < defaultSecurityDeposit) {
|
||||
String postfix = dataModel.isBuyOffer() ?
|
||||
Res.get("createOffer.tooLowSecDeposit.makerIsBuyer") :
|
||||
Res.get("createOffer.tooLowSecDeposit.makerIsSeller");
|
||||
new Popup<>()
|
||||
.warning(Res.get("createOffer.tooLowSecDeposit.warning",
|
||||
btcFormatter.formatCoinWithCode(defaultSecurityDeposit)) + "\n\n" + postfix)
|
||||
btcFormatter.formatToPercentWithSymbol(defaultSecurityDeposit)) + "\n\n" + postfix)
|
||||
.width(800)
|
||||
.actionButtonText(Res.get("createOffer.resetToDefault"))
|
||||
.onAction(() -> {
|
||||
dataModel.setBuyerSecurityDeposit(defaultSecurityDeposit);
|
||||
ignoreSecurityDepositStringListener = true;
|
||||
buyerSecurityDeposit.set(btcFormatter.formatCoin(dataModel.getBuyerSecurityDeposit().get()));
|
||||
buyerSecurityDeposit.set(btcFormatter.formatToPercent(dataModel.getBuyerSecurityDeposit().get()));
|
||||
ignoreSecurityDepositStringListener = false;
|
||||
})
|
||||
.closeButtonText(Res.get("createOffer.useLowerValue"))
|
||||
@ -866,7 +871,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
private void applyBuyerSecurityDepositOnFocusOut() {
|
||||
setBuyerSecurityDepositToModel();
|
||||
ignoreSecurityDepositStringListener = true;
|
||||
buyerSecurityDeposit.set(btcFormatter.formatCoin(dataModel.getBuyerSecurityDeposit().get()));
|
||||
buyerSecurityDeposit.set(btcFormatter.formatToPercent(dataModel.getBuyerSecurityDeposit().get()));
|
||||
ignoreSecurityDepositStringListener = false;
|
||||
}
|
||||
|
||||
@ -1091,13 +1096,12 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||
|
||||
private void setBuyerSecurityDepositToModel() {
|
||||
if (buyerSecurityDeposit.get() != null && !buyerSecurityDeposit.get().isEmpty()) {
|
||||
dataModel.setBuyerSecurityDeposit(btcFormatter.parseToCoinWith4Decimals(buyerSecurityDeposit.get()));
|
||||
dataModel.setBuyerSecurityDeposit(btcFormatter.parsePercentStringToDouble(buyerSecurityDeposit.get()));
|
||||
} else {
|
||||
dataModel.setBuyerSecurityDeposit(null);
|
||||
dataModel.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private InputValidator.ValidationResult isBtcInputValid(String input) {
|
||||
return btcValidator.validate(input);
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.desktop.main.offer.MutableOfferView.BUYER_SECURITY_DEPOSIT_NEWS;
|
||||
|
||||
public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
||||
|
||||
private OfferBookView offerBookView;
|
||||
@ -271,6 +273,8 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
||||
offerBookView.enableCreateOfferButton();
|
||||
|
||||
navigation.navigateTo(MainView.class, this.getClass(), OfferBookView.class);
|
||||
|
||||
preferences.dontShowAgain(BUYER_SECURITY_DEPOSIT_NEWS, true);
|
||||
}
|
||||
|
||||
private void onTakeOfferViewRemoved() {
|
||||
|
@ -39,6 +39,7 @@ import bisq.core.trade.statistics.ReferralIdService;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.user.User;
|
||||
import bisq.core.util.BSFormatter;
|
||||
import bisq.core.util.CoinUtil;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
@ -98,7 +99,7 @@ class EditOfferDataModel extends MutableOfferDataModel {
|
||||
minAmount.set(null);
|
||||
price.set(null);
|
||||
volume.set(null);
|
||||
buyerSecurityDeposit.set(null);
|
||||
buyerSecurityDeposit.set(0);
|
||||
paymentAccounts.clear();
|
||||
paymentAccount = null;
|
||||
marketPriceMargin = 0;
|
||||
@ -112,7 +113,7 @@ class EditOfferDataModel extends MutableOfferDataModel {
|
||||
CurrencyUtil.getTradeCurrency(offer.getCurrencyCode())
|
||||
.ifPresent(c -> this.tradeCurrency = c);
|
||||
tradeCurrencyCode.set(offer.getCurrencyCode());
|
||||
buyerSecurityDeposit.set(offer.getBuyerSecurityDeposit());
|
||||
buyerSecurityDeposit.set(CoinUtil.getAsPercentPerBtc(offer.getBuyerSecurityDeposit()));
|
||||
|
||||
this.initialState = openOffer.getState();
|
||||
PaymentAccount tmpPaymentAccount = user.getPaymentAccount(openOffer.getOffer().getMakerPaymentAccountId());
|
||||
|
@ -21,17 +21,15 @@ import bisq.core.btc.wallet.Restrictions;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.util.BSFormatter;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class SecurityDepositValidator extends BtcValidator {
|
||||
public class SecurityDepositValidator extends NumberValidator {
|
||||
|
||||
private final BSFormatter formatter;
|
||||
|
||||
@Inject
|
||||
public SecurityDepositValidator(BSFormatter formatter) {
|
||||
super(formatter);
|
||||
setMaxValue(Restrictions.getMaxBuyerSecurityDeposit());
|
||||
setMinValue(Restrictions.getMinBuyerSecurityDeposit());
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
|
||||
@ -46,22 +44,34 @@ public class SecurityDepositValidator extends BtcValidator {
|
||||
if (result.isValid) {
|
||||
result = validateIfNotZero(input)
|
||||
.and(validateIfNotNegative(input))
|
||||
.and(validateIfNotTooLowBtcValue(input))
|
||||
.and(validateIfNotFractionalBtcValue(input))
|
||||
.and(validateIfNotExceedsMaxBtcValue(input));
|
||||
.and(validateIfNotTooLowPercentageValue(input))
|
||||
.and(validateIfNotTooHighPercentageValue(input));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
protected ValidationResult validateIfNotTooLowBtcValue(String input) {
|
||||
private ValidationResult validateIfNotTooLowPercentageValue(String input) {
|
||||
try {
|
||||
final Coin coin = Coin.parseCoin(input);
|
||||
Coin minSecurityDeposit = Restrictions.getMinBuyerSecurityDeposit();
|
||||
if (coin.compareTo(minSecurityDeposit) < 0)
|
||||
double percentage = formatter.parsePercentStringToDouble(input);
|
||||
double minPercentage = Restrictions.getMinBuyerSecurityDepositAsPercent();
|
||||
if (percentage < minPercentage)
|
||||
return new ValidationResult(false,
|
||||
Res.get("validation.securityDeposit.toSmall", formatter.formatCoinWithCode(minSecurityDeposit)));
|
||||
Res.get("validation.inputTooSmall", formatter.formatToPercentWithSymbol(minPercentage)));
|
||||
else
|
||||
return new ValidationResult(true);
|
||||
} catch (Throwable t) {
|
||||
return new ValidationResult(false, Res.get("validation.invalidInput", t.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private ValidationResult validateIfNotTooHighPercentageValue(String input) {
|
||||
try {
|
||||
double percentage = formatter.parsePercentStringToDouble(input);
|
||||
double maxPercentage = Restrictions.getMaxBuyerSecurityDepositAsPercent();
|
||||
if (percentage > maxPercentage)
|
||||
return new ValidationResult(false,
|
||||
Res.get("validation.inputTooLarge", formatter.formatToPercentWithSymbol(maxPercentage)));
|
||||
else
|
||||
return new ValidationResult(true);
|
||||
} catch (Throwable t) {
|
||||
|
@ -66,7 +66,7 @@ public class CreateOfferDataModelTest {
|
||||
|
||||
when(btcWalletService.getOrCreateAddressEntry(anyString(), any())).thenReturn(addressEntry);
|
||||
when(preferences.isUsePercentageBasedPrice()).thenReturn(true);
|
||||
when(preferences.getBuyerSecurityDepositAsCoin()).thenReturn(Coin.FIFTY_COINS);
|
||||
when(preferences.getBuyerSecurityDepositAsPercent()).thenReturn(0.01);
|
||||
|
||||
model = new CreateOfferDataModel(null, btcWalletService,
|
||||
null, preferences, user, null,
|
||||
|
Loading…
Reference in New Issue
Block a user