mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-08-07 06:02:33 -04:00
set minimum trade amount to 0.05 XMR (#1857)
This commit is contained in:
parent
3680e1d4ee
commit
fd2c0f335f
12 changed files with 75 additions and 67 deletions
|
@ -43,7 +43,7 @@ import java.util.stream.Collectors;
|
||||||
import static haveno.apitest.config.ApiTestConfig.BTC;
|
import static haveno.apitest.config.ApiTestConfig.BTC;
|
||||||
import static haveno.apitest.config.ApiTestRateMeterInterceptorConfig.getTestRateMeterInterceptorConfig;
|
import static haveno.apitest.config.ApiTestRateMeterInterceptorConfig.getTestRateMeterInterceptorConfig;
|
||||||
import static haveno.cli.table.builder.TableType.BTC_BALANCE_TBL;
|
import static haveno.cli.table.builder.TableType.BTC_BALANCE_TBL;
|
||||||
import static haveno.core.xmr.wallet.Restrictions.getDefaultSecurityDepositAsPercent;
|
import static haveno.core.xmr.wallet.Restrictions.getDefaultSecurityDepositPct;
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static java.util.Arrays.stream;
|
import static java.util.Arrays.stream;
|
||||||
|
@ -158,7 +158,7 @@ public class MethodTest extends ApiTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Supplier<Double> defaultSecurityDepositPct = () -> {
|
public static final Supplier<Double> defaultSecurityDepositPct = () -> {
|
||||||
var defaultPct = BigDecimal.valueOf(getDefaultSecurityDepositAsPercent());
|
var defaultPct = BigDecimal.valueOf(getDefaultSecurityDepositPct());
|
||||||
if (defaultPct.precision() != 2)
|
if (defaultPct.precision() != 2)
|
||||||
throw new IllegalStateException(format(
|
throw new IllegalStateException(format(
|
||||||
"Unexpected decimal precision, expected 2 but actual is %d%n."
|
"Unexpected decimal precision, expected 2 but actual is %d%n."
|
||||||
|
|
|
@ -404,7 +404,7 @@ public class OfferBookService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate max offers with same key images
|
// validate max offers with same key images
|
||||||
if (numOffersWithSharedKeyImages > Restrictions.MAX_OFFERS_WITH_SHARED_FUNDS) throw new RuntimeException("More than " + Restrictions.MAX_OFFERS_WITH_SHARED_FUNDS + " offers exist with same same key images as new offerId=" + offerPayload.getId());
|
if (numOffersWithSharedKeyImages > Restrictions.getMaxOffersWithSharedFunds()) throw new RuntimeException("More than " + Restrictions.getMaxOffersWithSharedFunds() + " offers exist with same same key images as new offerId=" + offerPayload.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,8 +61,8 @@ import haveno.core.trade.statistics.ReferralIdService;
|
||||||
import haveno.core.user.AutoConfirmSettings;
|
import haveno.core.user.AutoConfirmSettings;
|
||||||
import haveno.core.user.Preferences;
|
import haveno.core.user.Preferences;
|
||||||
import haveno.core.util.coin.CoinFormatter;
|
import haveno.core.util.coin.CoinFormatter;
|
||||||
import static haveno.core.xmr.wallet.Restrictions.getMaxSecurityDepositAsPercent;
|
import static haveno.core.xmr.wallet.Restrictions.getMaxSecurityDepositPct;
|
||||||
import static haveno.core.xmr.wallet.Restrictions.getMinSecurityDepositAsPercent;
|
import static haveno.core.xmr.wallet.Restrictions.getMinSecurityDepositPct;
|
||||||
import haveno.network.p2p.P2PService;
|
import haveno.network.p2p.P2PService;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -240,12 +240,12 @@ public class OfferUtil {
|
||||||
PaymentAccount paymentAccount,
|
PaymentAccount paymentAccount,
|
||||||
String currencyCode) {
|
String currencyCode) {
|
||||||
checkNotNull(p2PService.getAddress(), "Address must not be null");
|
checkNotNull(p2PService.getAddress(), "Address must not be null");
|
||||||
checkArgument(securityDeposit <= getMaxSecurityDepositAsPercent(),
|
checkArgument(securityDeposit <= getMaxSecurityDepositPct(),
|
||||||
"securityDeposit must not exceed " +
|
"securityDeposit must not exceed " +
|
||||||
getMaxSecurityDepositAsPercent());
|
getMaxSecurityDepositPct());
|
||||||
checkArgument(securityDeposit >= getMinSecurityDepositAsPercent(),
|
checkArgument(securityDeposit >= getMinSecurityDepositPct(),
|
||||||
"securityDeposit must not be less than " +
|
"securityDeposit must not be less than " +
|
||||||
getMinSecurityDepositAsPercent() + " but was " + securityDeposit);
|
getMinSecurityDepositPct() + " but was " + securityDeposit);
|
||||||
checkArgument(!filterManager.isCurrencyBanned(currencyCode),
|
checkArgument(!filterManager.isCurrencyBanned(currencyCode),
|
||||||
Res.get("offerbook.warning.currencyBanned"));
|
Res.get("offerbook.warning.currencyBanned"));
|
||||||
checkArgument(!filterManager.isPaymentMethodBanned(paymentAccount.getPaymentMethod()),
|
checkArgument(!filterManager.isPaymentMethodBanned(paymentAccount.getPaymentMethod()),
|
||||||
|
|
|
@ -540,8 +540,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
|
|
||||||
// check clone limit
|
// check clone limit
|
||||||
int numClones = getOpenOfferGroup(sourceOffer.getGroupId()).size();
|
int numClones = getOpenOfferGroup(sourceOffer.getGroupId()).size();
|
||||||
if (numClones >= Restrictions.MAX_OFFERS_WITH_SHARED_FUNDS) {
|
if (numClones >= Restrictions.getMaxOffersWithSharedFunds()) {
|
||||||
errorMessageHandler.handleErrorMessage("Cannot create offer because maximum number of " + Restrictions.MAX_OFFERS_WITH_SHARED_FUNDS + " cloned offers with shared funds reached.");
|
errorMessageHandler.handleErrorMessage("Cannot create offer because maximum number of " + Restrictions.getMaxOffersWithSharedFunds() + " cloned offers with shared funds reached.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1565,8 +1565,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify max length of extra info
|
// verify max length of extra info
|
||||||
if (offer.getOfferPayload().getExtraInfo() != null && offer.getOfferPayload().getExtraInfo().length() > Restrictions.MAX_EXTRA_INFO_LENGTH) {
|
if (offer.getOfferPayload().getExtraInfo() != null && offer.getOfferPayload().getExtraInfo().length() > Restrictions.getMaxExtraInfoLength()) {
|
||||||
errorMessage = "Extra info is too long for offer " + request.offerId + ". Max length is " + Restrictions.MAX_EXTRA_INFO_LENGTH + " but got " + offer.getOfferPayload().getExtraInfo().length();
|
errorMessage = "Extra info is too long for offer " + request.offerId + ". Max length is " + Restrictions.getMaxExtraInfoLength() + " but got " + offer.getOfferPayload().getExtraInfo().length();
|
||||||
log.warn(errorMessage);
|
log.warn(errorMessage);
|
||||||
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
||||||
return;
|
return;
|
||||||
|
@ -1611,8 +1611,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify maker security deposit
|
// verify maker security deposit
|
||||||
if (offer.getSellerSecurityDepositPct() != Restrictions.MIN_SECURITY_DEPOSIT_PCT) {
|
if (offer.getSellerSecurityDepositPct() != Restrictions.getMinSecurityDepositPct()) {
|
||||||
errorMessage = "Wrong seller security deposit for offer " + request.offerId + ". Expected " + Restrictions.MIN_SECURITY_DEPOSIT_PCT + " but got " + offer.getSellerSecurityDepositPct();
|
errorMessage = "Wrong seller security deposit for offer " + request.offerId + ". Expected " + Restrictions.getMinSecurityDepositPct() + " but got " + offer.getSellerSecurityDepositPct();
|
||||||
log.warn(errorMessage);
|
log.warn(errorMessage);
|
||||||
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
||||||
return;
|
return;
|
||||||
|
@ -1652,16 +1652,16 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify seller's security deposit
|
// verify seller's security deposit
|
||||||
if (offer.getSellerSecurityDepositPct() < Restrictions.MIN_SECURITY_DEPOSIT_PCT) {
|
if (offer.getSellerSecurityDepositPct() < Restrictions.getMinSecurityDepositPct()) {
|
||||||
errorMessage = "Insufficient seller security deposit for offer " + request.offerId + ". Expected at least " + Restrictions.MIN_SECURITY_DEPOSIT_PCT + " but got " + offer.getSellerSecurityDepositPct();
|
errorMessage = "Insufficient seller security deposit for offer " + request.offerId + ". Expected at least " + Restrictions.getMinSecurityDepositPct() + " but got " + offer.getSellerSecurityDepositPct();
|
||||||
log.warn(errorMessage);
|
log.warn(errorMessage);
|
||||||
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify buyer's security deposit
|
// verify buyer's security deposit
|
||||||
if (offer.getBuyerSecurityDepositPct() < Restrictions.MIN_SECURITY_DEPOSIT_PCT) {
|
if (offer.getBuyerSecurityDepositPct() < Restrictions.getMinSecurityDepositPct()) {
|
||||||
errorMessage = "Insufficient buyer security deposit for offer " + request.offerId + ". Expected at least " + Restrictions.MIN_SECURITY_DEPOSIT_PCT + " but got " + offer.getBuyerSecurityDepositPct();
|
errorMessage = "Insufficient buyer security deposit for offer " + request.offerId + ". Expected at least " + Restrictions.getMinSecurityDepositPct() + " but got " + offer.getBuyerSecurityDepositPct();
|
||||||
log.warn(errorMessage);
|
log.warn(errorMessage);
|
||||||
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class SecurityDepositValidator extends NumberValidator {
|
||||||
private ValidationResult validateIfNotTooLowPercentageValue(String input) {
|
private ValidationResult validateIfNotTooLowPercentageValue(String input) {
|
||||||
try {
|
try {
|
||||||
double percentage = ParsingUtils.parsePercentStringToDouble(input);
|
double percentage = ParsingUtils.parsePercentStringToDouble(input);
|
||||||
double minPercentage = Restrictions.getMinSecurityDepositAsPercent();
|
double minPercentage = Restrictions.getMinSecurityDepositPct();
|
||||||
if (percentage < minPercentage)
|
if (percentage < minPercentage)
|
||||||
return new ValidationResult(false,
|
return new ValidationResult(false,
|
||||||
Res.get("validation.inputTooSmall", FormattingUtils.formatToPercentWithSymbol(minPercentage)));
|
Res.get("validation.inputTooSmall", FormattingUtils.formatToPercentWithSymbol(minPercentage)));
|
||||||
|
@ -73,7 +73,7 @@ public class SecurityDepositValidator extends NumberValidator {
|
||||||
private ValidationResult validateIfNotTooHighPercentageValue(String input) {
|
private ValidationResult validateIfNotTooHighPercentageValue(String input) {
|
||||||
try {
|
try {
|
||||||
double percentage = ParsingUtils.parsePercentStringToDouble(input);
|
double percentage = ParsingUtils.parsePercentStringToDouble(input);
|
||||||
double maxPercentage = Restrictions.getMaxSecurityDepositAsPercent();
|
double maxPercentage = Restrictions.getMaxSecurityDepositPct();
|
||||||
if (percentage > maxPercentage)
|
if (percentage > maxPercentage)
|
||||||
return new ValidationResult(false,
|
return new ValidationResult(false,
|
||||||
Res.get("validation.inputTooLarge", FormattingUtils.formatToPercentWithSymbol(maxPercentage)));
|
Res.get("validation.inputTooLarge", FormattingUtils.formatToPercentWithSymbol(maxPercentage)));
|
||||||
|
|
|
@ -623,8 +623,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSecurityDepositAsPercent(double securityDepositAsPercent, PaymentAccount paymentAccount) {
|
public void setSecurityDepositAsPercent(double securityDepositAsPercent, PaymentAccount paymentAccount) {
|
||||||
double max = Restrictions.getMaxSecurityDepositAsPercent();
|
double max = Restrictions.getMaxSecurityDepositPct();
|
||||||
double min = Restrictions.getMinSecurityDepositAsPercent();
|
double min = Restrictions.getMinSecurityDepositPct();
|
||||||
|
|
||||||
if (PaymentAccountUtil.isCryptoCurrencyAccount(paymentAccount))
|
if (PaymentAccountUtil.isCryptoCurrencyAccount(paymentAccount))
|
||||||
prefPayload.setSecurityDepositAsPercentForCrypto(Math.min(max, Math.max(min, securityDepositAsPercent)));
|
prefPayload.setSecurityDepositAsPercentForCrypto(Math.min(max, Math.max(min, securityDepositAsPercent)));
|
||||||
|
@ -853,12 +853,12 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||||
double value = PaymentAccountUtil.isCryptoCurrencyAccount(paymentAccount) ?
|
double value = PaymentAccountUtil.isCryptoCurrencyAccount(paymentAccount) ?
|
||||||
prefPayload.getSecurityDepositAsPercentForCrypto() : prefPayload.getSecurityDepositAsPercent();
|
prefPayload.getSecurityDepositAsPercentForCrypto() : prefPayload.getSecurityDepositAsPercent();
|
||||||
|
|
||||||
if (value < Restrictions.getMinSecurityDepositAsPercent()) {
|
if (value < Restrictions.getMinSecurityDepositPct()) {
|
||||||
value = Restrictions.getMinSecurityDepositAsPercent();
|
value = Restrictions.getMinSecurityDepositPct();
|
||||||
setSecurityDepositAsPercent(value, paymentAccount);
|
setSecurityDepositAsPercent(value, paymentAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value == 0 ? Restrictions.getDefaultSecurityDepositAsPercent() : value;
|
return value == 0 ? Restrictions.getDefaultSecurityDepositPct() : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -41,7 +41,7 @@ import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static haveno.core.xmr.wallet.Restrictions.getDefaultSecurityDepositAsPercent;
|
import static haveno.core.xmr.wallet.Restrictions.getDefaultSecurityDepositPct;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Data
|
@Data
|
||||||
|
@ -120,10 +120,10 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
||||||
private String rpcPw;
|
private String rpcPw;
|
||||||
@Nullable
|
@Nullable
|
||||||
private String takeOfferSelectedPaymentAccountId;
|
private String takeOfferSelectedPaymentAccountId;
|
||||||
private double securityDepositAsPercent = getDefaultSecurityDepositAsPercent();
|
private double securityDepositAsPercent = getDefaultSecurityDepositPct();
|
||||||
private int ignoreDustThreshold = 600;
|
private int ignoreDustThreshold = 600;
|
||||||
private int clearDataAfterDays = Preferences.CLEAR_DATA_AFTER_DAYS_DEFAULT;
|
private int clearDataAfterDays = Preferences.CLEAR_DATA_AFTER_DAYS_DEFAULT;
|
||||||
private double securityDepositAsPercentForCrypto = getDefaultSecurityDepositAsPercent();
|
private double securityDepositAsPercentForCrypto = getDefaultSecurityDepositPct();
|
||||||
private int blockNotifyPort;
|
private int blockNotifyPort;
|
||||||
private boolean tacAcceptedV120;
|
private boolean tacAcceptedV120;
|
||||||
private double bsqAverageTrimThreshold = 0.05;
|
private double bsqAverageTrimThreshold = 0.05;
|
||||||
|
|
|
@ -26,12 +26,12 @@ import java.math.BigInteger;
|
||||||
public class Restrictions {
|
public class Restrictions {
|
||||||
|
|
||||||
// configure restrictions
|
// configure restrictions
|
||||||
public static final double MIN_SECURITY_DEPOSIT_PCT = 0.15;
|
private static final double MIN_SECURITY_DEPOSIT_PCT = 0.15;
|
||||||
public static final double MAX_SECURITY_DEPOSIT_PCT = 0.5;
|
private static final double MAX_SECURITY_DEPOSIT_PCT = 0.5;
|
||||||
public static BigInteger MIN_TRADE_AMOUNT = HavenoUtils.xmrToAtomicUnits(0.1);
|
private static final BigInteger MIN_TRADE_AMOUNT = HavenoUtils.xmrToAtomicUnits(0.05);
|
||||||
public static BigInteger MIN_SECURITY_DEPOSIT = HavenoUtils.xmrToAtomicUnits(0.1);
|
private static final BigInteger MIN_SECURITY_DEPOSIT = HavenoUtils.xmrToAtomicUnits(0.1);
|
||||||
public static int MAX_EXTRA_INFO_LENGTH = 1500;
|
private static final int MAX_EXTRA_INFO_LENGTH = 1500;
|
||||||
public static int MAX_OFFERS_WITH_SHARED_FUNDS = 10;
|
private static final int MAX_OFFERS_WITH_SHARED_FUNDS = 10;
|
||||||
|
|
||||||
// At mediation we require a min. payout to the losing party to keep incentive for the trader to accept the
|
// At mediation we require a min. payout to the losing party to keep incentive for the trader to accept the
|
||||||
// mediated payout. For Refund agent cases we do not have that restriction.
|
// mediated payout. For Refund agent cases we do not have that restriction.
|
||||||
|
@ -57,15 +57,15 @@ public class Restrictions {
|
||||||
return MIN_TRADE_AMOUNT;
|
return MIN_TRADE_AMOUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double getDefaultSecurityDepositAsPercent() {
|
public static double getDefaultSecurityDepositPct() {
|
||||||
return MIN_SECURITY_DEPOSIT_PCT;
|
return MIN_SECURITY_DEPOSIT_PCT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double getMinSecurityDepositAsPercent() {
|
public static double getMinSecurityDepositPct() {
|
||||||
return MIN_SECURITY_DEPOSIT_PCT;
|
return MIN_SECURITY_DEPOSIT_PCT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double getMaxSecurityDepositAsPercent() {
|
public static double getMaxSecurityDepositPct() {
|
||||||
return MAX_SECURITY_DEPOSIT_PCT;
|
return MAX_SECURITY_DEPOSIT_PCT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +73,14 @@ public class Restrictions {
|
||||||
return MIN_SECURITY_DEPOSIT;
|
return MIN_SECURITY_DEPOSIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getMaxExtraInfoLength() {
|
||||||
|
return MAX_EXTRA_INFO_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getMaxOffersWithSharedFunds() {
|
||||||
|
return MAX_OFFERS_WITH_SHARED_FUNDS;
|
||||||
|
}
|
||||||
|
|
||||||
// This value must be lower than MIN_BUYER_SECURITY_DEPOSIT and SELLER_SECURITY_DEPOSIT
|
// This value must be lower than MIN_BUYER_SECURITY_DEPOSIT and SELLER_SECURITY_DEPOSIT
|
||||||
public static BigInteger getMinRefundAtMediatedDispute() {
|
public static BigInteger getMinRefundAtMediatedDispute() {
|
||||||
if (MIN_REFUND_AT_MEDIATED_DISPUTE == null)
|
if (MIN_REFUND_AT_MEDIATED_DISPUTE == null)
|
||||||
|
|
|
@ -76,11 +76,11 @@ public class CoinUtilTest {
|
||||||
BigInteger result = CoinUtil.getAdjustedAmount(
|
BigInteger result = CoinUtil.getAdjustedAmount(
|
||||||
HavenoUtils.xmrToAtomicUnits(0.1),
|
HavenoUtils.xmrToAtomicUnits(0.1),
|
||||||
Price.valueOf("USD", 1000_0000),
|
Price.valueOf("USD", 1000_0000),
|
||||||
HavenoUtils.xmrToAtomicUnits(0.1),
|
Restrictions.getMinTradeAmount(),
|
||||||
HavenoUtils.xmrToAtomicUnits(0.2),
|
HavenoUtils.xmrToAtomicUnits(0.2),
|
||||||
1);
|
1);
|
||||||
assertEquals(
|
assertEquals(
|
||||||
HavenoUtils.formatXmr(Restrictions.MIN_TRADE_AMOUNT, true),
|
HavenoUtils.formatXmr(Restrictions.getMinTradeAmount(), true),
|
||||||
HavenoUtils.formatXmr(result, true),
|
HavenoUtils.formatXmr(result, true),
|
||||||
"Minimum trade amount allowed should be adjusted to the smallest trade allowed."
|
"Minimum trade amount allowed should be adjusted to the smallest trade allowed."
|
||||||
);
|
);
|
||||||
|
@ -95,7 +95,7 @@ public class CoinUtilTest {
|
||||||
fail("Expected IllegalArgumentException to be thrown when amount is too low.");
|
fail("Expected IllegalArgumentException to be thrown when amount is too low.");
|
||||||
} catch (IllegalArgumentException iae) {
|
} catch (IllegalArgumentException iae) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"amount must be above minimum of 0.1 xmr but was 0.0 xmr",
|
"amount must be above minimum of 0.05 xmr but was 0.0 xmr",
|
||||||
iae.getMessage(),
|
iae.getMessage(),
|
||||||
"Unexpected exception message."
|
"Unexpected exception message."
|
||||||
);
|
);
|
||||||
|
@ -104,11 +104,11 @@ public class CoinUtilTest {
|
||||||
result = CoinUtil.getAdjustedAmount(
|
result = CoinUtil.getAdjustedAmount(
|
||||||
HavenoUtils.xmrToAtomicUnits(0.1),
|
HavenoUtils.xmrToAtomicUnits(0.1),
|
||||||
Price.valueOf("USD", 1000_0000),
|
Price.valueOf("USD", 1000_0000),
|
||||||
HavenoUtils.xmrToAtomicUnits(0.1),
|
Restrictions.getMinTradeAmount(),
|
||||||
HavenoUtils.xmrToAtomicUnits(0.2),
|
HavenoUtils.xmrToAtomicUnits(0.2),
|
||||||
1);
|
1);
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"0.10 XMR",
|
"0.05 XMR",
|
||||||
HavenoUtils.formatXmr(result, true),
|
HavenoUtils.formatXmr(result, true),
|
||||||
"Minimum allowed trade amount should not be adjusted."
|
"Minimum allowed trade amount should not be adjusted."
|
||||||
);
|
);
|
||||||
|
@ -116,11 +116,11 @@ public class CoinUtilTest {
|
||||||
result = CoinUtil.getAdjustedAmount(
|
result = CoinUtil.getAdjustedAmount(
|
||||||
HavenoUtils.xmrToAtomicUnits(0.1),
|
HavenoUtils.xmrToAtomicUnits(0.1),
|
||||||
Price.valueOf("USD", 1000_0000),
|
Price.valueOf("USD", 1000_0000),
|
||||||
HavenoUtils.xmrToAtomicUnits(0.1),
|
Restrictions.getMinTradeAmount(),
|
||||||
HavenoUtils.xmrToAtomicUnits(0.25),
|
HavenoUtils.xmrToAtomicUnits(0.25),
|
||||||
1);
|
1);
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"0.10 XMR",
|
"0.05 XMR",
|
||||||
HavenoUtils.formatXmr(result, true),
|
HavenoUtils.formatXmr(result, true),
|
||||||
"Minimum trade amount allowed should respect maxTradeLimit and factor, if possible."
|
"Minimum trade amount allowed should respect maxTradeLimit and factor, if possible."
|
||||||
);
|
);
|
||||||
|
|
|
@ -168,7 +168,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
||||||
reserveExactAmount = preferences.getSplitOfferOutput();
|
reserveExactAmount = preferences.getSplitOfferOutput();
|
||||||
|
|
||||||
useMarketBasedPrice.set(preferences.isUsePercentageBasedPrice());
|
useMarketBasedPrice.set(preferences.isUsePercentageBasedPrice());
|
||||||
securityDepositPct.set(Restrictions.getMinSecurityDepositAsPercent());
|
securityDepositPct.set(Restrictions.getMinSecurityDepositPct());
|
||||||
|
|
||||||
paymentAccountsChangeListener = change -> fillPaymentAccounts();
|
paymentAccountsChangeListener = change -> fillPaymentAccounts();
|
||||||
}
|
}
|
||||||
|
@ -338,7 +338,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSuggestedSecurityDeposit(PaymentAccount paymentAccount) {
|
private void setSuggestedSecurityDeposit(PaymentAccount paymentAccount) {
|
||||||
var minSecurityDeposit = Restrictions.getMinSecurityDepositAsPercent();
|
var minSecurityDeposit = Restrictions.getMinSecurityDepositPct();
|
||||||
try {
|
try {
|
||||||
if (getTradeCurrency() == null) {
|
if (getTradeCurrency() == null) {
|
||||||
setSecurityDepositPct(minSecurityDeposit);
|
setSecurityDepositPct(minSecurityDeposit);
|
||||||
|
@ -369,7 +369,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
||||||
}
|
}
|
||||||
// Suggested deposit is double the trade range over the previous lock time period, bounded by min/max deposit
|
// Suggested deposit is double the trade range over the previous lock time period, bounded by min/max deposit
|
||||||
var suggestedSecurityDeposit =
|
var suggestedSecurityDeposit =
|
||||||
Math.min(2 * (max - min) / max, Restrictions.getMaxSecurityDepositAsPercent());
|
Math.min(2 * (max - min) / max, Restrictions.getMaxSecurityDepositPct());
|
||||||
securityDepositPct.set(Math.max(suggestedSecurityDeposit, minSecurityDeposit));
|
securityDepositPct.set(Math.max(suggestedSecurityDeposit, minSecurityDeposit));
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
log.error(t.toString());
|
log.error(t.toString());
|
||||||
|
@ -472,14 +472,14 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
||||||
return marketPriceMarginPct;
|
return marketPriceMarginPct;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInteger getMaxTradeLimit() {
|
|
||||||
return offerUtil.getMaxTradeLimitForRelease(paymentAccount, tradeCurrencyCode.get(), direction, buyerAsTakerWithoutDeposit.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
BigInteger getMinTradeLimit() {
|
BigInteger getMinTradeLimit() {
|
||||||
return Restrictions.getMinTradeAmount();
|
return Restrictions.getMinTradeAmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BigInteger getMaxTradeLimit() {
|
||||||
|
return offerUtil.getMaxTradeLimitForRelease(paymentAccount, tradeCurrencyCode.get(), direction, buyerAsTakerWithoutDeposit.get());
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Utils
|
// Utils
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -537,10 +537,10 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
||||||
// if the volume != amount * price, we need to adjust the amount
|
// if the volume != amount * price, we need to adjust the amount
|
||||||
if (amount.get() == null || !volumeBefore.equals(price.get().getVolumeByAmount(amount.get()))) {
|
if (amount.get() == null || !volumeBefore.equals(price.get().getVolumeByAmount(amount.get()))) {
|
||||||
BigInteger value = price.get().getAmountByVolume(volumeBefore);
|
BigInteger value = price.get().getAmountByVolume(volumeBefore);
|
||||||
|
BigInteger minAmount = getMinTradeLimit();
|
||||||
BigInteger maxAmount = getMaxTradeLimit();
|
BigInteger maxAmount = getMaxTradeLimit();
|
||||||
BigInteger minAmount = Restrictions.getMinTradeAmount();
|
|
||||||
value = value.min(maxAmount); // adjust if above maximum
|
|
||||||
value = value.max(minAmount); // adjust if below minimum
|
value = value.max(minAmount); // adjust if below minimum
|
||||||
|
value = value.min(maxAmount); // adjust if above maximum
|
||||||
value = CoinUtil.getRoundedAmount(value, price.get(), minAmount, maxAmount, tradeCurrencyCode.get(), paymentAccount.getPaymentMethod().getId());
|
value = CoinUtil.getRoundedAmount(value, price.get(), minAmount, maxAmount, tradeCurrencyCode.get(), paymentAccount.getPaymentMethod().getId());
|
||||||
amount.set(value);
|
amount.set(value);
|
||||||
}
|
}
|
||||||
|
@ -689,7 +689,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
||||||
double offerSellerSecurityDepositAsPercent = CoinUtil.getAsPercentPerXmr(offerSellerSecurityDeposit,
|
double offerSellerSecurityDepositAsPercent = CoinUtil.getAsPercentPerXmr(offerSellerSecurityDeposit,
|
||||||
offer.getAmount());
|
offer.getAmount());
|
||||||
return Math.min(offerSellerSecurityDepositAsPercent,
|
return Math.min(offerSellerSecurityDepositAsPercent,
|
||||||
Restrictions.getMaxSecurityDepositAsPercent());
|
Restrictions.getMaxSecurityDepositPct());
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlyObjectProperty<BigInteger> totalToPayAsProperty() {
|
ReadOnlyObjectProperty<BigInteger> totalToPayAsProperty() {
|
||||||
|
|
|
@ -483,6 +483,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
buyerAsTakerWithoutDepositListener = (ov, oldValue, newValue) -> {
|
buyerAsTakerWithoutDepositListener = (ov, oldValue, newValue) -> {
|
||||||
if (dataModel.paymentAccount != null) xmrValidator.setMaxValue(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimit(dataModel.getTradeCurrencyCode().get()));
|
if (dataModel.paymentAccount != null) xmrValidator.setMaxValue(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimit(dataModel.getTradeCurrencyCode().get()));
|
||||||
xmrValidator.setMaxTradeLimit(dataModel.getMaxTradeLimit());
|
xmrValidator.setMaxTradeLimit(dataModel.getMaxTradeLimit());
|
||||||
|
xmrValidator.setMinValue(dataModel.getMinTradeLimit());
|
||||||
if (amount.get() != null) amountValidationResult.set(isXmrInputValid(amount.get()));
|
if (amount.get() != null) amountValidationResult.set(isXmrInputValid(amount.get()));
|
||||||
updateSecurityDeposit();
|
updateSecurityDeposit();
|
||||||
setSecurityDepositToModel();
|
setSecurityDepositToModel();
|
||||||
|
@ -601,7 +602,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
if (dataModel.paymentAccount != null)
|
if (dataModel.paymentAccount != null)
|
||||||
xmrValidator.setMaxValue(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimit(dataModel.getTradeCurrencyCode().get()));
|
xmrValidator.setMaxValue(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimit(dataModel.getTradeCurrencyCode().get()));
|
||||||
xmrValidator.setMaxTradeLimit(dataModel.getMaxTradeLimit());
|
xmrValidator.setMaxTradeLimit(dataModel.getMaxTradeLimit());
|
||||||
xmrValidator.setMinValue(Restrictions.getMinTradeAmount());
|
xmrValidator.setMinValue(dataModel.getMinTradeLimit());
|
||||||
|
|
||||||
final boolean isBuy = dataModel.getDirection() == OfferDirection.BUY;
|
final boolean isBuy = dataModel.getDirection() == OfferDirection.BUY;
|
||||||
|
|
||||||
|
@ -741,7 +742,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
if (minAmount.get() != null)
|
if (minAmount.get() != null)
|
||||||
minAmountValidationResult.set(isXmrInputValid(minAmount.get()));
|
minAmountValidationResult.set(isXmrInputValid(minAmount.get()));
|
||||||
} else if (amount.get() != null && xmrValidator.getMaxTradeLimit() != null && xmrValidator.getMaxTradeLimit().longValueExact() == OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.longValueExact()) {
|
} else if (amount.get() != null && xmrValidator.getMaxTradeLimit() != null && xmrValidator.getMaxTradeLimit().longValueExact() == OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.longValueExact()) {
|
||||||
if (ParsingUtils.parseNumberStringToDouble(amount.get()) < HavenoUtils.atomicUnitsToXmr(Restrictions.getMinTradeAmount())) {
|
if (ParsingUtils.parseNumberStringToDouble(amount.get()) < HavenoUtils.atomicUnitsToXmr(dataModel.getMinTradeLimit())) {
|
||||||
amountValidationResult.set(result);
|
amountValidationResult.set(result);
|
||||||
} else {
|
} else {
|
||||||
amount.set(HavenoUtils.formatXmr(xmrValidator.getMaxTradeLimit()));
|
amount.set(HavenoUtils.formatXmr(xmrValidator.getMaxTradeLimit()));
|
||||||
|
@ -980,7 +981,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
InputValidator.ValidationResult result = securityDepositValidator.validate(securityDeposit.get());
|
InputValidator.ValidationResult result = securityDepositValidator.validate(securityDeposit.get());
|
||||||
securityDepositValidationResult.set(result);
|
securityDepositValidationResult.set(result);
|
||||||
if (result.isValid) {
|
if (result.isValid) {
|
||||||
double defaultSecurityDeposit = Restrictions.getDefaultSecurityDepositAsPercent();
|
double defaultSecurityDeposit = Restrictions.getDefaultSecurityDepositPct();
|
||||||
String key = "buyerSecurityDepositIsLowerAsDefault";
|
String key = "buyerSecurityDepositIsLowerAsDefault";
|
||||||
double depositAsDouble = ParsingUtils.parsePercentStringToDouble(securityDeposit.get());
|
double depositAsDouble = ParsingUtils.parsePercentStringToDouble(securityDeposit.get());
|
||||||
if (preferences.showAgain(key) && depositAsDouble < defaultSecurityDeposit) {
|
if (preferences.showAgain(key) && depositAsDouble < defaultSecurityDeposit) {
|
||||||
|
@ -1167,10 +1168,9 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
if (amount.get() != null && !amount.get().isEmpty()) {
|
if (amount.get() != null && !amount.get().isEmpty()) {
|
||||||
BigInteger amount = HavenoUtils.coinToAtomicUnits(DisplayUtils.parseToCoinWith4Decimals(this.amount.get(), xmrFormatter));
|
BigInteger amount = HavenoUtils.coinToAtomicUnits(DisplayUtils.parseToCoinWith4Decimals(this.amount.get(), xmrFormatter));
|
||||||
|
|
||||||
BigInteger maxTradeLimit = dataModel.getMaxTradeLimit();
|
|
||||||
Price price = dataModel.getPrice().get();
|
Price price = dataModel.getPrice().get();
|
||||||
if (price != null && price.isPositive()) {
|
if (price != null && price.isPositive()) {
|
||||||
amount = CoinUtil.getRoundedAmount(amount, price, dataModel.getMinAmount().get(), maxTradeLimit, tradeCurrencyCode.get(), dataModel.getPaymentAccount().getPaymentMethod().getId());
|
amount = CoinUtil.getRoundedAmount(amount, price, dataModel.getMinTradeLimit(), dataModel.getMaxTradeLimit(), tradeCurrencyCode.get(), dataModel.getPaymentAccount().getPaymentMethod().getId());
|
||||||
}
|
}
|
||||||
dataModel.setAmount(amount);
|
dataModel.setAmount(amount);
|
||||||
if (syncMinAmountWithAmount ||
|
if (syncMinAmountWithAmount ||
|
||||||
|
@ -1227,7 +1227,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
if (securityDeposit.get() != null && !securityDeposit.get().isEmpty() && !isMinSecurityDeposit.get()) {
|
if (securityDeposit.get() != null && !securityDeposit.get().isEmpty() && !isMinSecurityDeposit.get()) {
|
||||||
dataModel.setSecurityDepositPct(ParsingUtils.parsePercentStringToDouble(securityDeposit.get()));
|
dataModel.setSecurityDepositPct(ParsingUtils.parsePercentStringToDouble(securityDeposit.get()));
|
||||||
} else {
|
} else {
|
||||||
dataModel.setSecurityDepositPct(Restrictions.getDefaultSecurityDepositAsPercent());
|
dataModel.setSecurityDepositPct(Restrictions.getDefaultSecurityDepositPct());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1243,7 +1243,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
// If the security deposit in the model is not valid percent
|
// If the security deposit in the model is not valid percent
|
||||||
String value = FormattingUtils.formatToPercent(dataModel.getSecurityDepositPct().get());
|
String value = FormattingUtils.formatToPercent(dataModel.getSecurityDepositPct().get());
|
||||||
if (!securityDepositValidator.validate(value).isValid) {
|
if (!securityDepositValidator.validate(value).isValid) {
|
||||||
dataModel.setSecurityDepositPct(Restrictions.getDefaultSecurityDepositAsPercent());
|
dataModel.setSecurityDepositPct(Restrictions.getDefaultSecurityDepositPct());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1299,7 +1299,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
} else {
|
} else {
|
||||||
boolean hasBuyerAsTakerWithoutDeposit = dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer();
|
boolean hasBuyerAsTakerWithoutDeposit = dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer();
|
||||||
securityDeposit.set(FormattingUtils.formatToPercent(hasBuyerAsTakerWithoutDeposit ?
|
securityDeposit.set(FormattingUtils.formatToPercent(hasBuyerAsTakerWithoutDeposit ?
|
||||||
Restrictions.getDefaultSecurityDepositAsPercent() : // use default percent if no deposit from buyer
|
Restrictions.getDefaultSecurityDepositPct() : // use default percent if no deposit from buyer
|
||||||
dataModel.getSecurityDepositPct().get()));
|
dataModel.getSecurityDepositPct().get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1333,8 +1333,8 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
}
|
}
|
||||||
|
|
||||||
private ValidationResult getExtraInfoValidationResult() {
|
private ValidationResult getExtraInfoValidationResult() {
|
||||||
if (extraInfo.get() != null && !extraInfo.get().isEmpty() && extraInfo.get().length() > Restrictions.MAX_EXTRA_INFO_LENGTH) {
|
if (extraInfo.get() != null && !extraInfo.get().isEmpty() && extraInfo.get().length() > Restrictions.getMaxExtraInfoLength()) {
|
||||||
return new InputValidator.ValidationResult(false, Res.get("createOffer.extraInfo.invalid.tooLong", Restrictions.MAX_EXTRA_INFO_LENGTH));
|
return new InputValidator.ValidationResult(false, Res.get("createOffer.extraInfo.invalid.tooLong", Restrictions.getMaxExtraInfoLength()));
|
||||||
} else {
|
} else {
|
||||||
return new InputValidator.ValidationResult(true);
|
return new InputValidator.ValidationResult(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,9 +130,9 @@ class EditOfferDataModel extends MutableOfferDataModel {
|
||||||
// by percentage than the restriction. We can't determine the percentage originally entered at offer
|
// by percentage than the restriction. We can't determine the percentage originally entered at offer
|
||||||
// creation, so just use the default value as it doesn't matter anyway.
|
// creation, so just use the default value as it doesn't matter anyway.
|
||||||
double securityDepositPercent = CoinUtil.getAsPercentPerXmr(offer.getMaxSellerSecurityDeposit(), offer.getAmount());
|
double securityDepositPercent = CoinUtil.getAsPercentPerXmr(offer.getMaxSellerSecurityDeposit(), offer.getAmount());
|
||||||
if (securityDepositPercent > Restrictions.getMaxSecurityDepositAsPercent()
|
if (securityDepositPercent > Restrictions.getMaxSecurityDepositPct()
|
||||||
&& offer.getMaxSellerSecurityDeposit().equals(Restrictions.getMinSecurityDeposit()))
|
&& offer.getMaxSellerSecurityDeposit().equals(Restrictions.getMinSecurityDeposit()))
|
||||||
securityDepositPct.set(Restrictions.getDefaultSecurityDepositAsPercent());
|
securityDepositPct.set(Restrictions.getDefaultSecurityDepositPct());
|
||||||
else
|
else
|
||||||
securityDepositPct.set(securityDepositPercent);
|
securityDepositPct.set(securityDepositPercent);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue