automatically adjust offer and trade amount for grpc fixed-price offers

This commit is contained in:
woodser 2023-09-09 09:31:51 -04:00
parent 4017fa108a
commit 849a1c9c55
3 changed files with 39 additions and 11 deletions

View file

@ -20,7 +20,10 @@ package haveno.core.api;
import haveno.common.handlers.ErrorMessageHandler; import haveno.common.handlers.ErrorMessageHandler;
import haveno.common.handlers.ResultHandler; import haveno.common.handlers.ResultHandler;
import haveno.core.offer.Offer; import haveno.core.offer.Offer;
import haveno.core.offer.OfferDirection;
import haveno.core.offer.OfferUtil;
import haveno.core.offer.takeoffer.TakeOfferModel; import haveno.core.offer.takeoffer.TakeOfferModel;
import haveno.core.payment.payload.PaymentMethod;
import haveno.core.support.messages.ChatMessage; import haveno.core.support.messages.ChatMessage;
import haveno.core.support.traderchat.TradeChatSession; import haveno.core.support.traderchat.TradeChatSession;
import haveno.core.support.traderchat.TraderChatManager; import haveno.core.support.traderchat.TraderChatManager;
@ -32,6 +35,7 @@ import haveno.core.trade.TradeUtil;
import haveno.core.trade.protocol.BuyerProtocol; import haveno.core.trade.protocol.BuyerProtocol;
import haveno.core.trade.protocol.SellerProtocol; import haveno.core.trade.protocol.SellerProtocol;
import haveno.core.user.User; import haveno.core.user.User;
import haveno.core.util.coin.CoinUtil;
import haveno.core.util.validation.BtcAddressValidator; import haveno.core.util.validation.BtcAddressValidator;
import haveno.core.xmr.model.AddressEntry; import haveno.core.xmr.model.AddressEntry;
import haveno.core.xmr.wallet.BtcWalletService; import haveno.core.xmr.wallet.BtcWalletService;
@ -64,6 +68,7 @@ class CoreTradesService {
private final TradeManager tradeManager; private final TradeManager tradeManager;
private final TraderChatManager traderChatManager; private final TraderChatManager traderChatManager;
private final TradeUtil tradeUtil; private final TradeUtil tradeUtil;
private final OfferUtil offerUtil;
private final User user; private final User user;
@Inject @Inject
@ -75,6 +80,7 @@ class CoreTradesService {
TradeManager tradeManager, TradeManager tradeManager,
TraderChatManager traderChatManager, TraderChatManager traderChatManager,
TradeUtil tradeUtil, TradeUtil tradeUtil,
OfferUtil offerUtil,
User user) { User user) {
this.coreContext = coreContext; this.coreContext = coreContext;
this.coreWalletsService = coreWalletsService; this.coreWalletsService = coreWalletsService;
@ -84,6 +90,7 @@ class CoreTradesService {
this.tradeManager = tradeManager; this.tradeManager = tradeManager;
this.traderChatManager = traderChatManager; this.traderChatManager = traderChatManager;
this.tradeUtil = tradeUtil; this.tradeUtil = tradeUtil;
this.offerUtil = offerUtil;
this.user = user; this.user = user;
} }
@ -105,6 +112,21 @@ class CoreTradesService {
// default to offer amount // default to offer amount
BigInteger amount = amountAsLong == 0 ? offer.getAmount() : BigInteger.valueOf(amountAsLong); BigInteger amount = amountAsLong == 0 ? offer.getAmount() : BigInteger.valueOf(amountAsLong);
// adjust amount for fixed-price offer (based on TakeOfferViewModel)
String currencyCode = offer.getCurrencyCode();
OfferDirection direction = offer.getOfferPayload().getDirection();
long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction);
if (offer.getPrice() != null) {
if (PaymentMethod.isRoundedForAtmCash(paymentAccount.getPaymentMethod().getId())) {
amount = CoinUtil.getRoundedAtmCashAmount(amount, offer.getPrice(), maxTradeLimit);
} else if (offer.isTraditionalOffer()
&& !amount.equals(offer.getMinAmount()) && !amount.equals(amount)) {
// We only apply the rounding if the amount is variable (minAmount is lower as amount).
// Otherwise we could get an amount lower then the minAmount set by rounding
amount = CoinUtil.getRoundedAmount(amount, offer.getPrice(), maxTradeLimit, offer.getCurrencyCode(), offer.getPaymentMethodId());
}
}
// synchronize access to take offer model // TODO (woodser): to avoid synchronizing, don't use stateful model // synchronize access to take offer model // TODO (woodser): to avoid synchronizing, don't use stateful model
BigInteger takerFee; BigInteger takerFee;
BigInteger fundsNeededForTrade; BigInteger fundsNeededForTrade;

View file

@ -100,7 +100,7 @@ public class CreateOfferService {
String currencyCode, String currencyCode,
BigInteger amount, BigInteger amount,
BigInteger minAmount, BigInteger minAmount,
Price price, Price fixedPrice,
boolean useMarketBasedPrice, boolean useMarketBasedPrice,
double marketPriceMargin, double marketPriceMargin,
double buyerSecurityDepositAsDouble, double buyerSecurityDepositAsDouble,
@ -109,7 +109,7 @@ public class CreateOfferService {
log.info("create and get offer with offerId={}, " + log.info("create and get offer with offerId={}, " +
"currencyCode={}, " + "currencyCode={}, " +
"direction={}, " + "direction={}, " +
"price={}, " + "fixedPrice={}, " +
"useMarketBasedPrice={}, " + "useMarketBasedPrice={}, " +
"marketPriceMargin={}, " + "marketPriceMargin={}, " +
"amount={}, " + "amount={}, " +
@ -118,7 +118,7 @@ public class CreateOfferService {
offerId, offerId,
currencyCode, currencyCode,
direction, direction,
price == null ? null : price.getValue(), fixedPrice == null ? null : fixedPrice.getValue(),
useMarketBasedPrice, useMarketBasedPrice,
marketPriceMargin, marketPriceMargin,
amount, amount,
@ -126,24 +126,31 @@ public class CreateOfferService {
buyerSecurityDepositAsDouble); buyerSecurityDepositAsDouble);
// verify fixed price xor market price with margin // verify fixed price xor market price with margin
if (price != null) { if (fixedPrice != null) {
if (useMarketBasedPrice) throw new IllegalArgumentException("Can create offer with fixed price or floating market price but not both"); if (useMarketBasedPrice) throw new IllegalArgumentException("Can create offer with fixed price or floating market price but not both");
if (marketPriceMargin != 0) throw new IllegalArgumentException("Cannot set market price margin with fixed price"); if (marketPriceMargin != 0) throw new IllegalArgumentException("Cannot set market price margin with fixed price");
} }
long creationTime = new Date().getTime(); long creationTime = new Date().getTime();
NodeAddress makerAddress = p2PService.getAddress(); NodeAddress makerAddress = p2PService.getAddress();
boolean useMarketBasedPriceValue = price == null && boolean useMarketBasedPriceValue = fixedPrice == null &&
useMarketBasedPrice && useMarketBasedPrice &&
isMarketPriceAvailable(currencyCode) && isMarketPriceAvailable(currencyCode) &&
!PaymentMethod.isFixedPriceOnly(paymentAccount.getPaymentMethod().getId()); !PaymentMethod.isFixedPriceOnly(paymentAccount.getPaymentMethod().getId());
// verify price // verify price
if (price == null && !useMarketBasedPriceValue) { if (fixedPrice == null && !useMarketBasedPriceValue) {
throw new IllegalArgumentException("Must provide fixed price because market price is unavailable"); throw new IllegalArgumentException("Must provide fixed price");
} }
long priceAsLong = price != null ? price.getValue() : 0L; // adjust amount and min amount for fixed-price offer
long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction);
if (fixedPrice != null) {
amount = CoinUtil.getRoundedAmount(amount, fixedPrice, maxTradeLimit, currencyCode, paymentAccount.getPaymentMethod().getId());
minAmount = CoinUtil.getRoundedAmount(minAmount, fixedPrice, maxTradeLimit, currencyCode, paymentAccount.getPaymentMethod().getId());
}
long priceAsLong = fixedPrice != null ? fixedPrice.getValue() : 0L;
double marketPriceMarginParam = useMarketBasedPriceValue ? marketPriceMargin : 0; double marketPriceMarginParam = useMarketBasedPriceValue ? marketPriceMargin : 0;
long amountAsLong = amount != null ? amount.longValueExact() : 0L; long amountAsLong = amount != null ? amount.longValueExact() : 0L;
long minAmountAsLong = minAmount != null ? minAmount.longValueExact() : 0L; long minAmountAsLong = minAmount != null ? minAmount.longValueExact() : 0L;
@ -158,7 +165,6 @@ public class CreateOfferService {
BigInteger makerFee = HavenoUtils.getMakerFee(amount); BigInteger makerFee = HavenoUtils.getMakerFee(amount);
BigInteger buyerSecurityDeposit = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble); BigInteger buyerSecurityDeposit = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble);
BigInteger sellerSecurityDeposit = getSellerSecurityDeposit(amount, sellerSecurityDepositAsDouble); BigInteger sellerSecurityDeposit = getSellerSecurityDeposit(amount, sellerSecurityDepositAsDouble);
long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction);
long maxTradePeriod = paymentAccount.getMaxTradePeriod(); long maxTradePeriod = paymentAccount.getMaxTradePeriod();
// reserved for future use cases // reserved for future use cases

View file

@ -302,10 +302,10 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
Price tradePrice = dataModel.tradePrice; Price tradePrice = dataModel.tradePrice;
long maxTradeLimit = dataModel.getMaxTradeLimit(); long maxTradeLimit = dataModel.getMaxTradeLimit();
if (PaymentMethod.isRoundedForAtmCash(dataModel.getPaymentMethod().getId())) { if (PaymentMethod.isRoundedForAtmCash(dataModel.getPaymentMethod().getId())) {
BigInteger adjustedAmountForHalCash = CoinUtil.getRoundedAtmCashAmount(dataModel.getAmount().get(), BigInteger adjustedAmountForAtm = CoinUtil.getRoundedAtmCashAmount(dataModel.getAmount().get(),
tradePrice, tradePrice,
maxTradeLimit); maxTradeLimit);
dataModel.applyAmount(adjustedAmountForHalCash); dataModel.applyAmount(adjustedAmountForAtm);
amount.set(HavenoUtils.formatXmr(dataModel.getAmount().get())); amount.set(HavenoUtils.formatXmr(dataModel.getAmount().get()));
} else if (dataModel.getOffer().isTraditionalOffer()) { } else if (dataModel.getOffer().isTraditionalOffer()) {
if (!isAmountEqualMinAmount(dataModel.getAmount().get()) && (!isAmountEqualMaxAmount(dataModel.getAmount().get()))) { if (!isAmountEqualMinAmount(dataModel.getAmount().get()) && (!isAmountEqualMaxAmount(dataModel.getAmount().get()))) {