From 071659aec12a90cb03d4d0b1fe8f70f0125bcb1d Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 19 Jul 2025 08:00:46 -0400 Subject: [PATCH] fix amount adjustment when creating new offer (#1868) --- .../haveno/core/offer/CreateOfferService.java | 8 ++++---- .../main/java/haveno/core/offer/OfferUtil.java | 18 ++++++++++++++++++ .../java/haveno/core/trade/HavenoUtils.java | 16 +++++++++++----- .../java/haveno/core/util/coin/CoinUtil.java | 17 ++++++++++++++--- .../haveno/core/util/coin/CoinUtilTest.java | 2 +- .../main/offer/MutableOfferDataModel.java | 12 +----------- 6 files changed, 49 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/CreateOfferService.java b/core/src/main/java/haveno/core/offer/CreateOfferService.java index 89f1149447..4a35f3c43c 100644 --- a/core/src/main/java/haveno/core/offer/CreateOfferService.java +++ b/core/src/main/java/haveno/core/offer/CreateOfferService.java @@ -35,6 +35,7 @@ import haveno.core.trade.HavenoUtils; import haveno.core.trade.statistics.TradeStatisticsManager; import haveno.core.user.User; import haveno.core.util.coin.CoinUtil; +import haveno.core.xmr.wallet.Restrictions; import haveno.core.xmr.wallet.XmrWalletService; import haveno.network.p2p.NodeAddress; import haveno.network.p2p.P2PService; @@ -92,7 +93,6 @@ public class CreateOfferService { Version.VERSION.replace(".", ""); } - // TODO: add trigger price? public Offer createAndGetOffer(String offerId, OfferDirection direction, String currencyCode, @@ -158,8 +158,9 @@ public class CreateOfferService { } // adjust amount and min amount - amount = CoinUtil.getRoundedAmount(amount, fixedPrice, minAmount, amount, currencyCode, paymentAccount.getPaymentMethod().getId()); - minAmount = CoinUtil.getRoundedAmount(minAmount, fixedPrice, minAmount, amount, currencyCode, paymentAccount.getPaymentMethod().getId()); + BigInteger maxTradeLimit = offerUtil.getMaxTradeLimitForRelease(paymentAccount, currencyCode, direction, buyerAsTakerWithoutDeposit); + amount = CoinUtil.getRoundedAmount(amount, fixedPrice, Restrictions.getMinTradeAmount(), maxTradeLimit, currencyCode, paymentAccount.getPaymentMethod().getId()); + minAmount = CoinUtil.getRoundedAmount(minAmount, fixedPrice, Restrictions.getMinTradeAmount(), maxTradeLimit, currencyCode, paymentAccount.getPaymentMethod().getId()); // generate one-time challenge for private offer String challenge = null; @@ -241,7 +242,6 @@ public class CreateOfferService { return offer; } - // TODO: add trigger price? public Offer createClonedOffer(Offer sourceOffer, String currencyCode, Price fixedPrice, diff --git a/core/src/main/java/haveno/core/offer/OfferUtil.java b/core/src/main/java/haveno/core/offer/OfferUtil.java index 8f7c1957aa..64de6fec9e 100644 --- a/core/src/main/java/haveno/core/offer/OfferUtil.java +++ b/core/src/main/java/haveno/core/offer/OfferUtil.java @@ -56,6 +56,7 @@ import haveno.core.payment.PayPalAccount; import haveno.core.payment.PaymentAccount; import haveno.core.provider.price.MarketPrice; import haveno.core.provider.price.PriceFeedService; +import haveno.core.trade.HavenoUtils; import haveno.core.trade.statistics.ReferralIdService; import haveno.core.user.AutoConfirmSettings; import haveno.core.user.Preferences; @@ -269,4 +270,21 @@ public class OfferUtil { public static boolean isCryptoOffer(Offer offer) { return offer.getCounterCurrencyCode().equals("XMR"); } + + public BigInteger getMaxTradeLimitForRelease(PaymentAccount paymentAccount, + String currencyCode, + OfferDirection direction, + boolean buyerAsTakerWithoutDeposit) { + + // disallow offers which no buyer can take due to trade limits on release + if (HavenoUtils.isReleasedWithinDays(HavenoUtils.RELEASE_LIMIT_DAYS)) { + return BigInteger.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, currencyCode, OfferDirection.BUY, buyerAsTakerWithoutDeposit)); + } + + if (paymentAccount != null) { + return BigInteger.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, currencyCode, direction, buyerAsTakerWithoutDeposit)); + } else { + return BigInteger.ZERO; + } + } } diff --git a/core/src/main/java/haveno/core/trade/HavenoUtils.java b/core/src/main/java/haveno/core/trade/HavenoUtils.java index b022cf3cfc..24ee1c1dda 100644 --- a/core/src/main/java/haveno/core/trade/HavenoUtils.java +++ b/core/src/main/java/haveno/core/trade/HavenoUtils.java @@ -148,11 +148,17 @@ public class HavenoUtils { @SuppressWarnings("unused") public static Date getReleaseDate() { if (RELEASE_DATE == null) return null; - try { - return DATE_FORMAT.parse(RELEASE_DATE); - } catch (Exception e) { - log.error("Failed to parse release date: " + RELEASE_DATE, e); - throw new IllegalArgumentException(e); + return parseDate(RELEASE_DATE); + } + + private static Date parseDate(String date) { + synchronized (DATE_FORMAT) { + try { + return DATE_FORMAT.parse(date); + } catch (Exception e) { + log.error("Failed to parse date: " + date, e); + throw new IllegalArgumentException(e); + } } } diff --git a/core/src/main/java/haveno/core/util/coin/CoinUtil.java b/core/src/main/java/haveno/core/util/coin/CoinUtil.java index eef242da7c..bc079c9b5e 100644 --- a/core/src/main/java/haveno/core/util/coin/CoinUtil.java +++ b/core/src/main/java/haveno/core/util/coin/CoinUtil.java @@ -126,16 +126,27 @@ public class CoinUtil { static BigInteger getAdjustedAmount(BigInteger amount, Price price, BigInteger minAmount, BigInteger maxAmount, int factor) { checkArgument( amount.longValueExact() >= Restrictions.getMinTradeAmount().longValueExact(), - "amount needs to be above minimum of " + HavenoUtils.atomicUnitsToXmr(Restrictions.getMinTradeAmount()) + " xmr but was " + HavenoUtils.atomicUnitsToXmr(amount) + " xmr" + "amount must be above minimum of " + HavenoUtils.atomicUnitsToXmr(Restrictions.getMinTradeAmount()) + " xmr but was " + HavenoUtils.atomicUnitsToXmr(amount) + " xmr" ); if (minAmount == null) minAmount = Restrictions.getMinTradeAmount(); checkArgument( minAmount.longValueExact() >= Restrictions.getMinTradeAmount().longValueExact(), - "minAmount needs to be above minimum of " + HavenoUtils.atomicUnitsToXmr(Restrictions.getMinTradeAmount()) + " xmr but was " + HavenoUtils.atomicUnitsToXmr(minAmount) + " xmr" + "minAmount must be above minimum of " + HavenoUtils.atomicUnitsToXmr(Restrictions.getMinTradeAmount()) + " xmr but was " + HavenoUtils.atomicUnitsToXmr(minAmount) + " xmr" ); + if (maxAmount != null) { + checkArgument( + amount.longValueExact() <= maxAmount.longValueExact(), + "amount must be below maximum of " + HavenoUtils.atomicUnitsToXmr(maxAmount) + " xmr but was " + HavenoUtils.atomicUnitsToXmr(amount) + " xmr" + ); + checkArgument( + maxAmount.longValueExact() >= minAmount.longValueExact(), + "maxAmount must be above minimum of " + HavenoUtils.atomicUnitsToXmr(Restrictions.getMinTradeAmount()) + " xmr but was " + HavenoUtils.atomicUnitsToXmr(maxAmount) + " xmr" + ); + } + checkArgument( factor > 0, - "factor needs to be positive" + "factor must be positive" ); // Amount must result in a volume of min factor units of the fiat currency, e.g. 1 EUR or 10 EUR in case of HalCash. diff --git a/core/src/test/java/haveno/core/util/coin/CoinUtilTest.java b/core/src/test/java/haveno/core/util/coin/CoinUtilTest.java index 1a1a33435c..f14f41afdc 100644 --- a/core/src/test/java/haveno/core/util/coin/CoinUtilTest.java +++ b/core/src/test/java/haveno/core/util/coin/CoinUtilTest.java @@ -95,7 +95,7 @@ public class CoinUtilTest { fail("Expected IllegalArgumentException to be thrown when amount is too low."); } catch (IllegalArgumentException iae) { assertEquals( - "amount needs to be above minimum of 0.1 xmr but was 0.0 xmr", + "amount must be above minimum of 0.1 xmr but was 0.0 xmr", iae.getMessage(), "Unexpected exception message." ); diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java index 327640968c..b8bbdcbd41 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java @@ -473,17 +473,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { } BigInteger getMaxTradeLimit() { - - // disallow offers which no buyer can take due to trade limits on release - if (HavenoUtils.isReleasedWithinDays(HavenoUtils.RELEASE_LIMIT_DAYS)) { - return BigInteger.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrencyCode.get(), OfferDirection.BUY, buyerAsTakerWithoutDeposit.get())); - } - - if (paymentAccount != null) { - return BigInteger.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrencyCode.get(), direction, buyerAsTakerWithoutDeposit.get())); - } else { - return BigInteger.ZERO; - } + return offerUtil.getMaxTradeLimitForRelease(paymentAccount, tradeCurrencyCode.get(), direction, buyerAsTakerWithoutDeposit.get()); } BigInteger getMinTradeLimit() {