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.ResultHandler;
import haveno.core.offer.Offer;
import haveno.core.offer.OfferDirection;
import haveno.core.offer.OfferUtil;
import haveno.core.offer.takeoffer.TakeOfferModel;
import haveno.core.payment.payload.PaymentMethod;
import haveno.core.support.messages.ChatMessage;
import haveno.core.support.traderchat.TradeChatSession;
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.SellerProtocol;
import haveno.core.user.User;
import haveno.core.util.coin.CoinUtil;
import haveno.core.util.validation.BtcAddressValidator;
import haveno.core.xmr.model.AddressEntry;
import haveno.core.xmr.wallet.BtcWalletService;
@ -64,6 +68,7 @@ class CoreTradesService {
private final TradeManager tradeManager;
private final TraderChatManager traderChatManager;
private final TradeUtil tradeUtil;
private final OfferUtil offerUtil;
private final User user;
@Inject
@ -75,6 +80,7 @@ class CoreTradesService {
TradeManager tradeManager,
TraderChatManager traderChatManager,
TradeUtil tradeUtil,
OfferUtil offerUtil,
User user) {
this.coreContext = coreContext;
this.coreWalletsService = coreWalletsService;
@ -84,6 +90,7 @@ class CoreTradesService {
this.tradeManager = tradeManager;
this.traderChatManager = traderChatManager;
this.tradeUtil = tradeUtil;
this.offerUtil = offerUtil;
this.user = user;
}
@ -105,6 +112,21 @@ class CoreTradesService {
// default to offer amount
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
BigInteger takerFee;
BigInteger fundsNeededForTrade;

View file

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

View file

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