enable floating price offers for cardless cash

This commit is contained in:
woodser 2025-01-29 09:24:20 -05:00
parent 97569bad37
commit 88c3f04be0
4 changed files with 24 additions and 36 deletions

View file

@ -597,7 +597,6 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
} }
public static boolean isFixedPriceOnly(String id) { public static boolean isFixedPriceOnly(String id) {
return id.equals(PaymentMethod.CASH_AT_ATM_ID) || return id.equals(PaymentMethod.HAL_CASH_ID);
id.equals(PaymentMethod.HAL_CASH_ID);
} }
} }

View file

@ -3014,9 +3014,11 @@ payment.tradingRestrictions=Please review the maker's terms and conditions.\n\
If you do not meet the requirements do not take this trade. If you do not meet the requirements do not take this trade.
payment.cashAtAtm.info=Cardless Cash: Cardless withdraw at ATM using code\n\n\ payment.cashAtAtm.info=Cardless Cash: Cardless withdraw at ATM using code\n\n\
To use this payment method:\n\n\ To use this payment method:\n\n\
1. Create a Cardless Cash payment account, lising your accepted banks, regions, or other terms to be shown with the offer.\n\n\ 1. Create a Cardless Cash payment account, listing your accepted banks, regions, or other terms to be shown with the offer.\n\n\
2. Create or take an offer with the payment account.\n\n\ 2. Create or take an offer with the payment account.\n\n\
3. When the offer is taken, chat with your peer to coordinate a time to complete the payment and share the payment details.\n\n\ 3. When the offer is taken, chat with your peer to coordinate a time to complete the payment and share the payment details.\n\n\
If the trade amount is above the cash withdrawal limit, traders should split it into multiple transactions.\n\n\
ATM cash trades must be in multiples of 10. Using range offers is recommended so the XMR amount can adjust to match the exact price.\n\n\
If you cannot complete the transaction as specified in your trade contract, you may lose some (or all) of your security deposit. If you cannot complete the transaction as specified in your trade contract, you may lose some (or all) of your security deposit.
payment.cashAtAtm.extraInfo.prompt=Please state on your offers: \n\n\ payment.cashAtAtm.extraInfo.prompt=Please state on your offers: \n\n\
Your accepted banks / locations; \n\ Your accepted banks / locations; \n\

View file

@ -183,7 +183,7 @@ class TakeOfferDataModel extends OfferDataModel {
checkArgument(!possiblePaymentAccounts.isEmpty(), "possiblePaymentAccounts.isEmpty()"); checkArgument(!possiblePaymentAccounts.isEmpty(), "possiblePaymentAccounts.isEmpty()");
paymentAccount = getLastSelectedPaymentAccount(); paymentAccount = getLastSelectedPaymentAccount();
this.amount.set(offer.getAmount().min(BigInteger.valueOf(getMaxTradeLimit()))); this.amount.set(BigInteger.valueOf(getMaxTradeLimit()));
updateSecurityDeposit(); updateSecurityDeposit();
@ -292,8 +292,7 @@ class TakeOfferDataModel extends OfferDataModel {
if (paymentAccount != null) { if (paymentAccount != null) {
this.paymentAccount = paymentAccount; this.paymentAccount = paymentAccount;
long myLimit = getMaxTradeLimit(); this.amount.set(BigInteger.valueOf(getMaxTradeLimit()));
this.amount.set(offer.getMinAmount().max(amount.get().min(BigInteger.valueOf(myLimit))));
preferences.setTakeOfferSelectedPaymentAccountId(paymentAccount.getId()); preferences.setTakeOfferSelectedPaymentAccountId(paymentAccount.getId());
} }
@ -338,7 +337,7 @@ class TakeOfferDataModel extends OfferDataModel {
.orElse(firstItem); .orElse(firstItem);
} }
long getMaxTradeLimit() { long getMyMaxTradeLimit() {
if (paymentAccount != null) { if (paymentAccount != null) {
return accountAgeWitnessService.getMyTradeLimit(paymentAccount, getCurrencyCode(), return accountAgeWitnessService.getMyTradeLimit(paymentAccount, getCurrencyCode(),
offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit()); offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit());
@ -347,6 +346,10 @@ class TakeOfferDataModel extends OfferDataModel {
} }
} }
long getMaxTradeLimit() {
return Math.min(offer.getAmount().longValueExact(), getMyMaxTradeLimit());
}
boolean canTakeOffer() { boolean canTakeOffer() {
return GUIUtil.canCreateOrTakeOfferOrShowPopup(user, navigation) && return GUIUtil.canCreateOrTakeOfferOrShowPopup(user, navigation) &&
GUIUtil.isBootstrappedOrShowPopup(p2PService); GUIUtil.isBootstrappedOrShowPopup(p2PService);
@ -383,8 +386,10 @@ class TakeOfferDataModel extends OfferDataModel {
} }
} }
void applyAmount(BigInteger amount) { void maybeApplyAmount(BigInteger amount) {
this.amount.set(amount.min(BigInteger.valueOf(getMaxTradeLimit()))); if (amount.compareTo(offer.getMinAmount()) >= 0 && amount.compareTo(BigInteger.valueOf(getMaxTradeLimit())) <= 0) {
this.amount.set(amount);
}
calculateTotalToPay(); calculateTotalToPay();
} }

View file

@ -208,7 +208,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
errorMessage.set(offer.getErrorMessage()); errorMessage.set(offer.getErrorMessage());
xmrValidator.setMaxValue(offer.getAmount()); xmrValidator.setMaxValue(offer.getAmount());
xmrValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit()).min(offer.getAmount())); xmrValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit()));
xmrValidator.setMinValue(offer.getMinAmount()); xmrValidator.setMinValue(offer.getMinAmount());
} }
@ -237,7 +237,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
public void onPaymentAccountSelected(PaymentAccount paymentAccount) { public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
dataModel.onPaymentAccountSelected(paymentAccount); dataModel.onPaymentAccountSelected(paymentAccount);
xmrValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit()).min(offer.getAmount())); xmrValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit()));
updateButtonDisableState(); updateButtonDisableState();
} }
@ -299,20 +299,13 @@ 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 adjustedAmountForAtm = CoinUtil.getRoundedAtmCashAmount(dataModel.getAmount().get(), BigInteger adjustedAmountForAtm = CoinUtil.getRoundedAtmCashAmount(dataModel.getAmount().get(), tradePrice, maxTradeLimit);
tradePrice, dataModel.maybeApplyAmount(adjustedAmountForAtm);
maxTradeLimit);
dataModel.applyAmount(adjustedAmountForAtm);
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()))) { BigInteger roundedAmount = CoinUtil.getRoundedAmount(dataModel.getAmount().get(), tradePrice, maxTradeLimit, dataModel.getOffer().getCurrencyCode(), dataModel.getOffer().getPaymentMethodId());
// We only apply the rounding if the amount is variable (minAmount is lower as amount). dataModel.maybeApplyAmount(roundedAmount);
// Otherwise we could get an amount lower then the minAmount set by rounding
BigInteger roundedAmount = CoinUtil.getRoundedAmount(dataModel.getAmount().get(), tradePrice, maxTradeLimit, dataModel.getOffer().getCurrencyCode(), dataModel.getOffer().getPaymentMethodId());
dataModel.applyAmount(roundedAmount);
}
amount.set(HavenoUtils.formatXmr(dataModel.getAmount().get()));
} }
amount.set(HavenoUtils.formatXmr(dataModel.getAmount().get()));
if (!dataModel.isMinAmountLessOrEqualAmount()) if (!dataModel.isMinAmountLessOrEqualAmount())
amountValidationResult.set(new InputValidator.ValidationResult(false, amountValidationResult.set(new InputValidator.ValidationResult(false,
@ -580,25 +573,14 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
if (price != null) { if (price != null) {
if (dataModel.isRoundedForAtmCash()) { if (dataModel.isRoundedForAtmCash()) {
amount = CoinUtil.getRoundedAtmCashAmount(amount, price, maxTradeLimit); amount = CoinUtil.getRoundedAtmCashAmount(amount, price, maxTradeLimit);
} else if (dataModel.getOffer().isTraditionalOffer() } else if (dataModel.getOffer().isTraditionalOffer()) {
&& !isAmountEqualMinAmount(amount) && !isAmountEqualMaxAmount(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, price, maxTradeLimit, dataModel.getOffer().getCurrencyCode(), dataModel.getOffer().getPaymentMethodId()); amount = CoinUtil.getRoundedAmount(amount, price, maxTradeLimit, dataModel.getOffer().getCurrencyCode(), dataModel.getOffer().getPaymentMethodId());
} }
} }
dataModel.applyAmount(amount); dataModel.maybeApplyAmount(amount);
} }
} }
private boolean isAmountEqualMinAmount(BigInteger amount) {
return offer.getMinAmount().equals(amount);
}
private boolean isAmountEqualMaxAmount(BigInteger amount) {
return offer.getAmount().equals(amount);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////