Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use different security deposit for fiat-crypto and crypto-crypto trades #2742

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions common/src/main/proto/pb.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,7 @@ message PreferencesPayload {
string take_offer_selected_payment_account_id = 49;
double buyer_security_deposit_as_percent = 50;
int32 ignore_dust_threshold = 51;
double buyer_security_deposit_as_percent_for_crypto = 52;
}

///////////////////////////////////////////////////////////////////////////////////////////
Expand Down
25 changes: 19 additions & 6 deletions core/src/main/java/bisq/core/btc/wallet/Restrictions.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@
package bisq.core.btc.wallet;

import bisq.core.app.BisqEnvironment;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.PaymentAccountUtil;

import org.bitcoinj.core.Coin;

import javax.annotation.Nullable;

public class Restrictions {
private static Coin MIN_TRADE_AMOUNT;
private static Coin MIN_BUYER_SECURITY_DEPOSIT;
Expand Down Expand Up @@ -50,16 +54,25 @@ public static Coin getMinTradeAmount() {
return MIN_TRADE_AMOUNT;
}

public static double getDefaultBuyerSecurityDepositAsPercent() {
return 0.1; // 10% of trade amount.
public static double getDefaultBuyerSecurityDepositAsPercent(@Nullable PaymentAccount paymentAccount) {
if (PaymentAccountUtil.isCryptoCurrencyAccount(paymentAccount))
return 0.02; // 2% of trade amount.
else
return 0.1; // 10% of trade amount.
}

public static double getMinBuyerSecurityDepositAsPercent() {
return 0.05; // 5% of trade amount.
public static double getMinBuyerSecurityDepositAsPercent(@Nullable PaymentAccount paymentAccount) {
if (PaymentAccountUtil.isCryptoCurrencyAccount(paymentAccount))
return 0.005; // 0.5% of trade amount.
else
return 0.05; // 5% of trade amount.
}

public static double getMaxBuyerSecurityDepositAsPercent() {
return 0.5; // 50% of trade amount. For a 1 BTC trade it is about 800 USD @ 4000 USD/BTC
public static double getMaxBuyerSecurityDepositAsPercent(@Nullable PaymentAccount paymentAccount) {
if (PaymentAccountUtil.isCryptoCurrencyAccount(paymentAccount))
return 0.2; // 20% of trade amount. For a 1 BTC trade it is about 800 USD @ 4000 USD/BTC
else
return 0.5; // 50% of trade amount. For a 1 BTC trade it is about 2000 USD @ 4000 USD/BTC
}

// We use MIN_BUYER_SECURITY_DEPOSIT as well as lower bound in case of small trade amounts.
Expand Down
8 changes: 4 additions & 4 deletions core/src/main/java/bisq/core/offer/OfferUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -353,12 +353,12 @@ public static void validateOfferData(FilterManager filterManager,
Coin makerFeeAsCoin) {
checkNotNull(makerFeeAsCoin, "makerFee must not be null");
checkNotNull(p2PService.getAddress(), "Address must not be null");
checkArgument(buyerSecurityDeposit <= Restrictions.getMaxBuyerSecurityDepositAsPercent(),
checkArgument(buyerSecurityDeposit <= Restrictions.getMaxBuyerSecurityDepositAsPercent(paymentAccount),
"securityDeposit must not exceed " +
Restrictions.getMaxBuyerSecurityDepositAsPercent());
checkArgument(buyerSecurityDeposit >= Restrictions.getMinBuyerSecurityDepositAsPercent(),
Restrictions.getMaxBuyerSecurityDepositAsPercent(paymentAccount));
checkArgument(buyerSecurityDeposit >= Restrictions.getMinBuyerSecurityDepositAsPercent(paymentAccount),
"securityDeposit must not be less than " +
Restrictions.getMinBuyerSecurityDepositAsPercent());
Restrictions.getMinBuyerSecurityDepositAsPercent(paymentAccount));
checkArgument(!filterManager.isCurrencyBanned(currencyCode),
Res.get("offerbook.warning.currencyBanned"));
checkArgument(!filterManager.isPaymentMethodBanned(paymentAccount.getPaymentMethod()),
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/java/bisq/core/payment/PaymentAccountUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import bisq.core.locale.Country;
import bisq.core.offer.Offer;
import bisq.core.payment.payload.PaymentMethod;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
Expand Down Expand Up @@ -114,6 +115,11 @@ public static String getCountryCode(PaymentAccount paymentAccount) {
return null;
}

public static boolean isCryptoCurrencyAccount(PaymentAccount paymentAccount) {
return (paymentAccount != null && paymentAccount.getPaymentMethod().equals(PaymentMethod.BLOCK_CHAINS) ||
paymentAccount != null && paymentAccount.getPaymentMethod().equals(PaymentMethod.BLOCK_CHAINS_INSTANT));
}

// TODO no code duplication found in UI code (added for API)
// That is optional and set to null if not supported (AltCoins,...)
/* public static String getCountryCode(PaymentAccount paymentAccount) {
Expand Down
26 changes: 16 additions & 10 deletions core/src/main/java/bisq/core/user/Preferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import bisq.core.locale.GlobalSettings;
import bisq.core.locale.TradeCurrency;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.PaymentAccountUtil;

import bisq.network.p2p.network.BridgeAddressProvider;

Expand Down Expand Up @@ -493,10 +494,14 @@ public void setWithdrawalTxFeeInBytes(long withdrawalTxFeeInBytes) {
withdrawalTxFeeInBytesProperty.set(withdrawalTxFeeInBytes);
}

public void setBuyerSecurityDepositAsPercent(double buyerSecurityDepositAsPercent) {
double max = Restrictions.getMaxBuyerSecurityDepositAsPercent();
double min = Restrictions.getMinBuyerSecurityDepositAsPercent();
prefPayload.setBuyerSecurityDepositAsPercent(Math.min(max, Math.max(min, buyerSecurityDepositAsPercent)));
public void setBuyerSecurityDepositAsPercent(double buyerSecurityDepositAsPercent, PaymentAccount paymentAccount) {
double max = Restrictions.getMaxBuyerSecurityDepositAsPercent(paymentAccount);
double min = Restrictions.getMinBuyerSecurityDepositAsPercent(paymentAccount);

if (PaymentAccountUtil.isCryptoCurrencyAccount(paymentAccount))
prefPayload.setBuyerSecurityDepositAsPercentForCrypto(Math.min(max, Math.max(min, buyerSecurityDepositAsPercent)));
else
prefPayload.setBuyerSecurityDepositAsPercent(Math.min(max, Math.max(min, buyerSecurityDepositAsPercent)));
persist();
}

Expand Down Expand Up @@ -715,15 +720,16 @@ public LongProperty withdrawalTxFeeInBytesProperty() {
return withdrawalTxFeeInBytesProperty;
}

public double getBuyerSecurityDepositAsPercent() {
double value = prefPayload.getBuyerSecurityDepositAsPercent();
public double getBuyerSecurityDepositAsPercent(PaymentAccount paymentAccount) {
double value = PaymentAccountUtil.isCryptoCurrencyAccount(paymentAccount) ?
prefPayload.getBuyerSecurityDepositAsPercentForCrypto() : prefPayload.getBuyerSecurityDepositAsPercent();

if (value < Restrictions.getMinBuyerSecurityDepositAsPercent()) {
value = Restrictions.getMinBuyerSecurityDepositAsPercent();
setBuyerSecurityDepositAsPercent(value);
if (value < Restrictions.getMinBuyerSecurityDepositAsPercent(paymentAccount)) {
value = Restrictions.getMinBuyerSecurityDepositAsPercent(paymentAccount);
setBuyerSecurityDepositAsPercent(value, paymentAccount);
}

return value == 0 ? Restrictions.getDefaultBuyerSecurityDepositAsPercent() : value;
return value == 0 ? Restrictions.getDefaultBuyerSecurityDepositAsPercent(paymentAccount) : value;
}

//TODO remove and use isPayFeeInBtc instead
Expand Down
16 changes: 11 additions & 5 deletions core/src/main/java/bisq/core/user/PreferencesPayload.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@

package bisq.core.user;

import bisq.core.btc.wallet.Restrictions;
import bisq.core.locale.Country;
import bisq.core.locale.CryptoCurrency;
import bisq.core.locale.FiatCurrency;
import bisq.core.locale.TradeCurrency;
import bisq.core.payment.CryptoCurrencyAccount;
import bisq.core.payment.PaymentAccount;
import bisq.core.proto.CoreProtoResolver;

Expand All @@ -47,6 +47,8 @@

import javax.annotation.Nullable;

import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More static imports, in case you meant to revert them all, otherwise it's ok, just feels a bit excessive for these kinds of use cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I left it by intent to reduce the line length for L125 & L127


@Slf4j
@Data
@AllArgsConstructor
Expand Down Expand Up @@ -120,8 +122,9 @@ public final class PreferencesPayload implements PersistableEnvelope {
String rpcPw;
@Nullable
String takeOfferSelectedPaymentAccountId;
private double buyerSecurityDepositAsPercent = Restrictions.getDefaultBuyerSecurityDepositAsPercent();
private double buyerSecurityDepositAsPercent = getDefaultBuyerSecurityDepositAsPercent(null);
private int ignoreDustThreshold = 600;
private double buyerSecurityDepositAsPercentForCrypto = getDefaultBuyerSecurityDepositAsPercent(new CryptoCurrencyAccount());


///////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -178,8 +181,9 @@ public Message toProtoMessage() {
.setUsePriceNotifications(usePriceNotifications)
.setUseStandbyMode(useStandbyMode)
.setIsDaoFullNode(isDaoFullNode)
.setBuyerSecurityDepositAsPercent(buyerSecurityDepositAsPercent).
setIgnoreDustThreshold(ignoreDustThreshold);
.setBuyerSecurityDepositAsPercent(buyerSecurityDepositAsPercent)
.setIgnoreDustThreshold(ignoreDustThreshold)
.setBuyerSecurityDepositAsPercentForCrypto(buyerSecurityDepositAsPercentForCrypto);
Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory);
Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((PB.TradeCurrency) e.toProtoMessage()));
Optional.ofNullable(offerBookChartScreenCurrencyCode).ifPresent(builder::setOfferBookChartScreenCurrencyCode);
Expand Down Expand Up @@ -262,6 +266,8 @@ public static PersistableEnvelope fromProto(PB.PreferencesPayload proto, CorePro
proto.getRpcPw().isEmpty() ? null : proto.getRpcPw(),
proto.getTakeOfferSelectedPaymentAccountId().isEmpty() ? null : proto.getTakeOfferSelectedPaymentAccountId(),
proto.getBuyerSecurityDepositAsPercent(),
proto.getIgnoreDustThreshold());
proto.getIgnoreDustThreshold(),
proto.getBuyerSecurityDepositAsPercentForCrypto());

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ public MutableOfferDataModel(OpenOfferManager openOfferManager,
addressEntry = btcWalletService.getOrCreateAddressEntry(offerId, AddressEntry.Context.OFFER_FUNDING);

useMarketBasedPrice.set(preferences.isUsePercentageBasedPrice());
buyerSecurityDeposit.set(preferences.getBuyerSecurityDepositAsPercent());
buyerSecurityDeposit.set(preferences.getBuyerSecurityDepositAsPercent(null));
sellerSecurityDeposit.set(Restrictions.getSellerSecurityDepositAsPercent());

btcBalanceListener = new BalanceListener(getAddressEntry().getAddress()) {
Expand Down Expand Up @@ -436,6 +436,8 @@ void onPaymentAccountSelected(PaymentAccount paymentAccount) {

setTradeCurrencyFromPaymentAccount(paymentAccount);

buyerSecurityDeposit.set(preferences.getBuyerSecurityDepositAsPercent(getPaymentAccount()));

long myLimit = accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrencyCode.get());
if (amount.get() != null)
this.amount.set(Coin.valueOf(Math.min(amount.get().value, myLimit)));
Expand Down Expand Up @@ -710,7 +712,7 @@ protected void setVolume(Volume volume) {

void setBuyerSecurityDeposit(double value) {
this.buyerSecurityDeposit.set(value);
preferences.setBuyerSecurityDepositAsPercent(value);
preferences.setBuyerSecurityDepositAsPercent(value, getPaymentAccount());
}

protected boolean isUseMarketBasedPriceValue() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ boolean initWithData(OfferPayload.Direction direction, TradeCurrency tradeCurren
amountDescription = Res.get("createOffer.amountPriceBox.amountDescription",
isBuy ? Res.get("shared.buy") : Res.get("shared.sell"));

securityDepositValidator.setPaymentAccount(dataModel.paymentAccount);
buyerSecurityDeposit.set(btcFormatter.formatToPercent(dataModel.getBuyerSecurityDeposit().get()));
buyerSecurityDepositLabel.set(getSecurityDepositLabel());

Expand Down Expand Up @@ -663,6 +664,8 @@ public void onPaymentAccountSelected(PaymentAccount paymentAccount) {

btcValidator.setMaxValue(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimitAsCoin(dataModel.getTradeCurrencyCode().get()));
btcValidator.setMaxTradeLimit(Coin.valueOf(dataModel.getMaxTradeLimit()));

securityDepositValidator.setPaymentAccount(paymentAccount);
}

public void onCurrencySelected(TradeCurrency tradeCurrency) {
Expand Down Expand Up @@ -853,7 +856,7 @@ void onFocusOutBuyerSecurityDepositTextField(boolean oldValue, boolean newValue)
InputValidator.ValidationResult result = securityDepositValidator.validate(buyerSecurityDeposit.get());
buyerSecurityDepositValidationResult.set(result);
if (result.isValid) {
double defaultSecurityDeposit = Restrictions.getDefaultBuyerSecurityDepositAsPercent();
double defaultSecurityDeposit = Restrictions.getDefaultBuyerSecurityDepositAsPercent(getPaymentAccount());
String key = "buyerSecurityDepositIsLowerAsDefault";
double depositAsDouble = btcFormatter.parsePercentStringToDouble(buyerSecurityDeposit.get());
if (preferences.showAgain(key) && depositAsDouble < defaultSecurityDeposit) {
Expand Down Expand Up @@ -1121,7 +1124,7 @@ private void setBuyerSecurityDepositToModel() {
if (buyerSecurityDeposit.get() != null && !buyerSecurityDeposit.get().isEmpty()) {
dataModel.setBuyerSecurityDeposit(btcFormatter.parsePercentStringToDouble(buyerSecurityDeposit.get()));
} else {
dataModel.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent());
dataModel.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent(getPaymentAccount()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,28 @@

package bisq.desktop.util.validation;

import bisq.core.btc.wallet.Restrictions;
import bisq.core.locale.Res;
import bisq.core.payment.PaymentAccount;
import bisq.core.util.BSFormatter;

import javax.inject.Inject;

import static bisq.core.btc.wallet.Restrictions.getMaxBuyerSecurityDepositAsPercent;
import static bisq.core.btc.wallet.Restrictions.getMinBuyerSecurityDepositAsPercent;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More static imports

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it doesn't help to make the code more readable. Commit below.


public class SecurityDepositValidator extends NumberValidator {

private final BSFormatter formatter;
private PaymentAccount paymentAccount;

@Inject
public SecurityDepositValidator(BSFormatter formatter) {
this.formatter = formatter;
}

public void setPaymentAccount(PaymentAccount paymentAccount) {
this.paymentAccount = paymentAccount;
}

@Override
public ValidationResult validate(String input) {
Expand All @@ -54,7 +61,7 @@ public ValidationResult validate(String input) {
private ValidationResult validateIfNotTooLowPercentageValue(String input) {
try {
double percentage = formatter.parsePercentStringToDouble(input);
double minPercentage = Restrictions.getMinBuyerSecurityDepositAsPercent();
double minPercentage = getMinBuyerSecurityDepositAsPercent(paymentAccount);
if (percentage < minPercentage)
return new ValidationResult(false,
Res.get("validation.inputTooSmall", formatter.formatToPercentWithSymbol(minPercentage)));
Expand All @@ -68,7 +75,7 @@ private ValidationResult validateIfNotTooLowPercentageValue(String input) {
private ValidationResult validateIfNotTooHighPercentageValue(String input) {
try {
double percentage = formatter.parsePercentStringToDouble(input);
double maxPercentage = Restrictions.getMaxBuyerSecurityDepositAsPercent();
double maxPercentage = getMaxBuyerSecurityDepositAsPercent(paymentAccount);
if (percentage > maxPercentage)
return new ValidationResult(false,
Res.get("validation.inputTooLarge", formatter.formatToPercentWithSymbol(maxPercentage)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public void setUp() {

when(btcWalletService.getOrCreateAddressEntry(anyString(), any())).thenReturn(addressEntry);
when(preferences.isUsePercentageBasedPrice()).thenReturn(true);
when(preferences.getBuyerSecurityDepositAsPercent()).thenReturn(0.01);
when(preferences.getBuyerSecurityDepositAsPercent(null)).thenReturn(0.01);

model = new CreateOfferDataModel(null, btcWalletService,
null, preferences, user, null,
Expand Down