From 655583477a975bbe3e7a297e220458e75b7205bc Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 31 May 2023 08:21:14 -0400 Subject: [PATCH] support cash by atm payment method #626 --- .../java/haveno/core/locale/CurrencyUtil.java | 6 + .../haveno/core/offer/CreateOfferService.java | 5 +- .../main/java/haveno/core/offer/Offer.java | 5 +- .../core/offer/takeoffer/TakeOfferModel.java | 11 +- .../haveno/core/payment/CashByAtmAccount.java | 64 ++++++++++ .../core/payment/PaymentAccountFactory.java | 2 + .../payload/CashByAtmAccountPayload.java | 96 +++++++++++++++ .../core/payment/payload/PaymentMethod.java | 14 +++ .../haveno/core/proto/CoreProtoResolver.java | 3 + .../main/java/haveno/core/trade/Contract.java | 8 +- .../main/java/haveno/core/trade/Trade.java | 11 +- .../java/haveno/core/util/VolumeUtil.java | 23 +++- .../java/haveno/core/util/coin/CoinUtil.java | 26 +++- .../resources/i18n/displayStrings.properties | 29 +++-- .../paymentmethods/CashByAtmForm.java | 116 ++++++++++++++++++ .../TraditionalAccountsView.java | 12 ++ .../main/offer/MutableOfferDataModel.java | 18 +-- .../desktop/main/offer/MutableOfferView.java | 3 +- .../main/offer/MutableOfferViewModel.java | 17 +-- .../offer/takeoffer/TakeOfferDataModel.java | 10 +- .../offer/takeoffer/TakeOfferViewModel.java | 21 ++-- proto/src/main/proto/pb.proto | 5 + 22 files changed, 410 insertions(+), 95 deletions(-) create mode 100644 core/src/main/java/haveno/core/payment/CashByAtmAccount.java create mode 100644 core/src/main/java/haveno/core/payment/payload/CashByAtmAccountPayload.java create mode 100644 desktop/src/main/java/haveno/desktop/components/paymentmethods/CashByAtmForm.java diff --git a/core/src/main/java/haveno/core/locale/CurrencyUtil.java b/core/src/main/java/haveno/core/locale/CurrencyUtil.java index d94fb3a70c..99c65812c2 100644 --- a/core/src/main/java/haveno/core/locale/CurrencyUtil.java +++ b/core/src/main/java/haveno/core/locale/CurrencyUtil.java @@ -81,6 +81,12 @@ public class CurrencyUtil { public static List getAllFiatCurrencies() { return getAllTraditionalCurrencies().stream() + .filter(currency -> CurrencyUtil.isFiatCurrency(currency.getCode())) + .collect(Collectors.toList()); + } + + public static List getAllSortedFiatCurrencies() { + return getAllSortedTraditionalCurrencies().stream() .filter(currency -> CurrencyUtil.isFiatCurrency(currency.getCode())) .collect(Collectors.toList()); // sorted by currency name } diff --git a/core/src/main/java/haveno/core/offer/CreateOfferService.java b/core/src/main/java/haveno/core/offer/CreateOfferService.java index ac8abb0900..2b60736099 100644 --- a/core/src/main/java/haveno/core/offer/CreateOfferService.java +++ b/core/src/main/java/haveno/core/offer/CreateOfferService.java @@ -25,6 +25,7 @@ import haveno.core.locale.Res; import haveno.core.monetary.Price; import haveno.core.payment.PaymentAccount; import haveno.core.payment.PaymentAccountUtil; +import haveno.core.payment.payload.PaymentMethod; import haveno.core.provider.price.MarketPrice; import haveno.core.provider.price.PriceFeedService; import haveno.core.support.dispute.arbitration.arbitrator.ArbitratorManager; @@ -47,8 +48,6 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import static haveno.core.payment.payload.PaymentMethod.HAL_CASH_ID; - @Slf4j @Singleton public class CreateOfferService { @@ -137,7 +136,7 @@ public class CreateOfferService { boolean useMarketBasedPriceValue = price == null && useMarketBasedPrice && isMarketPriceAvailable(currencyCode) && - !paymentAccount.hasPaymentMethodWithId(HAL_CASH_ID); + !PaymentMethod.isFixedPriceOnly(paymentAccount.getPaymentMethod().getId()); // verify price if (price == null && !useMarketBasedPriceValue) { diff --git a/core/src/main/java/haveno/core/offer/Offer.java b/core/src/main/java/haveno/core/offer/Offer.java index e62fa51aed..bfcc06a61b 100644 --- a/core/src/main/java/haveno/core/offer/Offer.java +++ b/core/src/main/java/haveno/core/offer/Offer.java @@ -246,10 +246,7 @@ public class Offer implements NetworkPayload, PersistablePayload { return null; } Volume volumeByAmount = price.getVolumeByAmount(amount); - if (offerPayload.getPaymentMethodId().equals(PaymentMethod.HAL_CASH_ID)) - volumeByAmount = VolumeUtil.getAdjustedVolumeForHalCash(volumeByAmount); - else if (isFiatOffer()) - volumeByAmount = VolumeUtil.getRoundedFiatVolume(volumeByAmount); + volumeByAmount = VolumeUtil.getAdjustedVolume(volumeByAmount, getPaymentMethod().getId()); return volumeByAmount; } diff --git a/core/src/main/java/haveno/core/offer/takeoffer/TakeOfferModel.java b/core/src/main/java/haveno/core/offer/takeoffer/TakeOfferModel.java index 15f8b11e21..f97839c944 100644 --- a/core/src/main/java/haveno/core/offer/takeoffer/TakeOfferModel.java +++ b/core/src/main/java/haveno/core/offer/takeoffer/TakeOfferModel.java @@ -19,15 +19,14 @@ package haveno.core.offer.takeoffer; import haveno.common.taskrunner.Model; import haveno.core.account.witness.AccountAgeWitnessService; -import haveno.core.locale.CurrencyUtil; import haveno.core.monetary.Price; import haveno.core.monetary.Volume; import haveno.core.offer.Offer; import haveno.core.offer.OfferUtil; import haveno.core.payment.PaymentAccount; -import haveno.core.payment.payload.PaymentMethod; import haveno.core.provider.price.PriceFeedService; import haveno.core.trade.HavenoUtils; +import haveno.core.util.VolumeUtil; import haveno.core.xmr.model.XmrAddressEntry; import haveno.core.xmr.wallet.XmrWalletService; import lombok.Getter; @@ -41,8 +40,6 @@ import java.util.Objects; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static haveno.core.offer.OfferDirection.SELL; -import static haveno.core.util.VolumeUtil.getAdjustedVolumeForHalCash; -import static haveno.core.util.VolumeUtil.getRoundedFiatVolume; import static haveno.core.xmr.model.XmrAddressEntry.Context.OFFER_FUNDING; @Slf4j @@ -136,11 +133,7 @@ public class TakeOfferModel implements Model { private void calculateVolume() { Price tradePrice = offer.getPrice(); Volume volumeByAmount = Objects.requireNonNull(tradePrice).getVolumeByAmount(amount); - - if (offer.getPaymentMethod().getId().equals(PaymentMethod.HAL_CASH_ID)) - volumeByAmount = getAdjustedVolumeForHalCash(volumeByAmount); - else if (CurrencyUtil.isFiatCurrency(offer.getCurrencyCode())) - volumeByAmount = getRoundedFiatVolume(volumeByAmount); + volumeByAmount = VolumeUtil.getAdjustedVolume(volumeByAmount, offer.getPaymentMethod().getId()); volume = volumeByAmount; diff --git a/core/src/main/java/haveno/core/payment/CashByAtmAccount.java b/core/src/main/java/haveno/core/payment/CashByAtmAccount.java new file mode 100644 index 0000000000..b91bc78df5 --- /dev/null +++ b/core/src/main/java/haveno/core/payment/CashByAtmAccount.java @@ -0,0 +1,64 @@ +/* + * This file is part of Haveno. + * + * Haveno is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Haveno is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Haveno. If not, see . + */ + +package haveno.core.payment; + +import haveno.core.api.model.PaymentAccountFormField; +import haveno.core.locale.CurrencyUtil; +import haveno.core.locale.TradeCurrency; +import haveno.core.payment.payload.CashByAtmAccountPayload; +import haveno.core.payment.payload.PaymentAccountPayload; +import haveno.core.payment.payload.PaymentMethod; +import lombok.NonNull; + +import java.util.List; + +public final class CashByAtmAccount extends PaymentAccount { + + public static final List SUPPORTED_CURRENCIES = CurrencyUtil.getAllFiatCurrencies(); + + private static final List INPUT_FIELD_IDS = List.of( + PaymentAccountFormField.FieldId.EXTRA_INFO + ); + + public CashByAtmAccount() { + super(PaymentMethod.CASH_BY_ATM); + } + + @Override + protected PaymentAccountPayload createPayload() { + return new CashByAtmAccountPayload(paymentMethod.getId(), id); + } + + @Override + public @NonNull List getSupportedCurrencies() { + return SUPPORTED_CURRENCIES; + } + + @Override + public @NonNull List getInputFieldIds() { + return INPUT_FIELD_IDS; + } + + public void setExtraInfo(String extraInfo) { + ((CashByAtmAccountPayload) paymentAccountPayload).setExtraInfo(extraInfo); + } + + public String getExtraInfo() { + return ((CashByAtmAccountPayload) paymentAccountPayload).getExtraInfo(); + } +} diff --git a/core/src/main/java/haveno/core/payment/PaymentAccountFactory.java b/core/src/main/java/haveno/core/payment/PaymentAccountFactory.java index be4a74d772..3be7449d2b 100644 --- a/core/src/main/java/haveno/core/payment/PaymentAccountFactory.java +++ b/core/src/main/java/haveno/core/payment/PaymentAccountFactory.java @@ -76,6 +76,8 @@ public class PaymentAccountFactory { return new F2FAccount(); case PaymentMethod.PAY_BY_MAIL_ID: return new PayByMailAccount(); + case PaymentMethod.CASH_BY_ATM_ID: + return new CashByAtmAccount(); case PaymentMethod.PROMPT_PAY_ID: return new PromptPayAccount(); case PaymentMethod.ADVANCED_CASH_ID: diff --git a/core/src/main/java/haveno/core/payment/payload/CashByAtmAccountPayload.java b/core/src/main/java/haveno/core/payment/payload/CashByAtmAccountPayload.java new file mode 100644 index 0000000000..00ebf24942 --- /dev/null +++ b/core/src/main/java/haveno/core/payment/payload/CashByAtmAccountPayload.java @@ -0,0 +1,96 @@ +/* + * This file is part of Haveno. + * + * Haveno is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Haveno is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Haveno. If not, see . + */ + +package haveno.core.payment.payload; + +import com.google.protobuf.Message; +import haveno.core.locale.Res; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +@EqualsAndHashCode(callSuper = true) +@ToString +@Setter +@Getter +@Slf4j +public final class CashByAtmAccountPayload extends PaymentAccountPayload { + private String extraInfo = ""; + + public CashByAtmAccountPayload(String paymentMethod, String id) { + super(paymentMethod, id); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private CashByAtmAccountPayload(String paymentMethod, String id, + String extraInfo, + long maxTradePeriod, + Map excludeFromJsonDataMap) { + super(paymentMethod, + id, + maxTradePeriod, + excludeFromJsonDataMap); + this.extraInfo = extraInfo; + } + + @Override + public Message toProtoMessage() { + return getPaymentAccountPayloadBuilder() + .setCashByAtmAccountPayload(protobuf.CashByAtmAccountPayload.newBuilder() + .setExtraInfo(extraInfo)) + .build(); + } + + public static CashByAtmAccountPayload fromProto(protobuf.PaymentAccountPayload proto) { + return new CashByAtmAccountPayload(proto.getPaymentMethodId(), + proto.getId(), + proto.getCashByAtmAccountPayload().getExtraInfo(), + proto.getMaxTradePeriod(), + new HashMap<>(proto.getExcludeFromJsonDataMap())); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public String getPaymentDetails() { + return Res.getWithCol("payment.shared.extraInfo") + " " + extraInfo; + } + + @Override + public String getPaymentDetailsForTradePopup() { + return Res.getWithCol("payment.shared.extraInfo") + " " + extraInfo; + } + + @Override + public byte[] getAgeWitnessInputData() { + return super.getAgeWitnessInputData(ArrayUtils.addAll(id.getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/core/src/main/java/haveno/core/payment/payload/PaymentMethod.java b/core/src/main/java/haveno/core/payment/payload/PaymentMethod.java index e9fcc5d293..151c7c7e66 100644 --- a/core/src/main/java/haveno/core/payment/payload/PaymentMethod.java +++ b/core/src/main/java/haveno/core/payment/payload/PaymentMethod.java @@ -31,6 +31,7 @@ import haveno.core.payment.AmazonGiftCardAccount; import haveno.core.payment.AustraliaPayidAccount; import haveno.core.payment.BizumAccount; import haveno.core.payment.CapitualAccount; +import haveno.core.payment.CashByAtmAccount; import haveno.core.payment.PayByMailAccount; import haveno.core.payment.CashDepositAccount; import haveno.core.payment.CelPayAccount; @@ -162,6 +163,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable. + */ + +package haveno.desktop.components.paymentmethods; + +import com.jfoenix.controls.JFXTextArea; +import haveno.core.account.witness.AccountAgeWitnessService; +import haveno.core.locale.CurrencyUtil; +import haveno.core.locale.Res; +import haveno.core.locale.TradeCurrency; +import haveno.core.payment.CashByAtmAccount; +import haveno.core.payment.PaymentAccount; +import haveno.core.payment.payload.CashByAtmAccountPayload; +import haveno.core.payment.payload.PaymentAccountPayload; +import haveno.core.util.coin.CoinFormatter; +import haveno.core.util.validation.InputValidator; +import haveno.desktop.util.Layout; +import javafx.collections.FXCollections; +import javafx.scene.control.TextArea; +import javafx.scene.layout.GridPane; + +import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextArea; +import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextField; +import static haveno.desktop.util.FormBuilder.addTopLabelTextArea; +import static haveno.desktop.util.FormBuilder.addTopLabelTextFieldWithCopyIcon; + +public class CashByAtmForm extends PaymentMethodForm { + private final CashByAtmAccount cashByAtmAccount; + + public static int addFormForBuyer(GridPane gridPane, int gridRow, + PaymentAccountPayload paymentAccountPayload) { + CashByAtmAccountPayload cbm = (CashByAtmAccountPayload) paymentAccountPayload; + addTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1, + Res.get("payment.shared.extraInfo"), + cbm.getExtraInfo(), + Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE); + + TextArea textExtraInfo = addCompactTopLabelTextArea(gridPane, gridRow, 1, Res.get("payment.shared.extraInfo"), "").second; + textExtraInfo.setMinHeight(70); + textExtraInfo.setEditable(false); + textExtraInfo.setText(cbm.getExtraInfo()); + return gridRow; + } + + public CashByAtmForm(PaymentAccount paymentAccount, + AccountAgeWitnessService accountAgeWitnessService, + InputValidator inputValidator, GridPane gridPane, int gridRow, CoinFormatter formatter) { + super(paymentAccount, accountAgeWitnessService, inputValidator, gridPane, gridRow, formatter); + this.cashByAtmAccount = (CashByAtmAccount) paymentAccount; + } + + @Override + public void addFormForAddAccount() { + gridRowFrom = gridRow + 1; + + addTradeCurrencyComboBox(); + currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getAllSortedFiatCurrencies())); + + TextArea extraTextArea = addTopLabelTextArea(gridPane, ++gridRow, + Res.get("payment.shared.optionalExtra"), Res.get("payment.cashByAtm.extraInfo.prompt")).second; + extraTextArea.setMinHeight(70); + ((JFXTextArea) extraTextArea).setLabelFloat(false); + extraTextArea.textProperty().addListener((ov, oldValue, newValue) -> { + cashByAtmAccount.setExtraInfo(newValue); + updateFromInputs(); + }); + + addLimitations(false); + addAccountNameTextFieldWithAutoFillToggleButton(); + } + + @Override + protected void autoFillNameTextField() { + setAccountNameWithString(cashByAtmAccount.getExtraInfo().substring(0, Math.min(50, cashByAtmAccount.getExtraInfo().length()))); + } + + @Override + public void addFormForEditAccount() { + gridRowFrom = gridRow; + addAccountNameTextFieldWithAutoFillToggleButton(); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("shared.paymentMethod"), + Res.get(cashByAtmAccount.getPaymentMethod().getId())); + + TradeCurrency tradeCurrency = paymentAccount.getSingleTradeCurrency(); + String nameAndCode = tradeCurrency != null ? tradeCurrency.getNameAndCode() : ""; + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("shared.currency"), nameAndCode); + + TextArea textAreaExtra = addCompactTopLabelTextArea(gridPane, ++gridRow, Res.get("payment.shared.extraInfo"), "").second; + textAreaExtra.setText(cashByAtmAccount.getExtraInfo()); + textAreaExtra.setMinHeight(70); + textAreaExtra.setEditable(false); + + addLimitations(true); + } + + @Override + public void updateAllInputsValid() { + allInputsValid.set(isAccountNameValid() + && !cashByAtmAccount.getExtraInfo().isEmpty() + && paymentAccount.getSingleTradeCurrency() != null); + } +} diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java index aef3d182fe..e654bfef97 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java @@ -26,6 +26,7 @@ import haveno.core.locale.Res; import haveno.core.offer.OfferRestrictions; import haveno.core.payment.AmazonGiftCardAccount; import haveno.core.payment.AustraliaPayidAccount; +import haveno.core.payment.CashByAtmAccount; import haveno.core.payment.PayByMailAccount; import haveno.core.payment.CashDepositAccount; import haveno.core.payment.ZelleAccount; @@ -72,6 +73,7 @@ import haveno.desktop.components.paymentmethods.AmazonGiftCardForm; import haveno.desktop.components.paymentmethods.AustraliaPayidForm; import haveno.desktop.components.paymentmethods.BizumForm; import haveno.desktop.components.paymentmethods.CapitualForm; +import haveno.desktop.components.paymentmethods.CashByAtmForm; import haveno.desktop.components.paymentmethods.PayByMailForm; import haveno.desktop.components.paymentmethods.CashDepositForm; import haveno.desktop.components.paymentmethods.CelPayForm; @@ -270,6 +272,14 @@ public class TraditionalAccountsView extends PaymentAccountsView doSaveNewAccount(paymentAccount)) .show(); + } else if (paymentAccount instanceof CashByAtmAccount) { + // CashByAtm has no chargeback risk so we don't show the text from payment.limits.info. + new Popup().information(Res.get("payment.cashByAtm.info")) + .width(850) + .closeButtonText(Res.get("shared.cancel")) + .actionButtonText(Res.get("shared.iUnderstand")) + .onAction(() -> doSaveNewAccount(paymentAccount)) + .show(); } else if (paymentAccount instanceof HalCashAccount) { // HalCash has no chargeback risk so we don't show the text from payment.limits.info. new Popup().information(Res.get("payment.halCash.info")) @@ -559,6 +569,8 @@ public class TraditionalAccountsView extends PaymentAccountsView minAmount) { Volume volumeByAmount = price.get().getVolumeByAmount(minAmount.get()); - - // For HalCash we want multiple of 10 EUR - if (isUsingHalCashAccount()) - volumeByAmount = VolumeUtil.getAdjustedVolumeForHalCash(volumeByAmount); - else if (CurrencyUtil.isFiatCurrency(tradeCurrencyCode.get())) - volumeByAmount = VolumeUtil.getRoundedFiatVolume(volumeByAmount); + volumeByAmount = VolumeUtil.getAdjustedVolume(volumeByAmount, paymentAccount.getPaymentMethod().getId()); return volumeByAmount; } @@ -516,10 +511,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { if (isNonZeroPrice.test(price) && isNonZeroVolume.test(volume) && allowAmountUpdate) { try { BigInteger value = HavenoUtils.coinToAtomicUnits(DisplayUtils.reduceTo4Decimals(HavenoUtils.atomicUnitsToCoin(price.get().getAmountByVolume(volume.get())), btcFormatter)); - if (isUsingHalCashAccount()) - value = CoinUtil.getAdjustedAmountForHalCash(value, price.get(), getMaxTradeLimit()); - else if (CurrencyUtil.isFiatCurrency(tradeCurrencyCode.get())) - value = CoinUtil.getRoundedFiatAmount(value, price.get(), getMaxTradeLimit()); + value = CoinUtil.getRoundedAmount(value, price.get(), getMaxTradeLimit(), tradeCurrencyCode.get(), paymentAccount.getPaymentMethod().getId()); calculateVolume(); @@ -680,7 +672,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { this.triggerPrice = triggerPrice; } - public boolean isUsingHalCashAccount() { - return paymentAccount.hasPaymentMethodWithId(HAL_CASH_ID); + public boolean isUsingRoundedAtmCashAccount() { + return PaymentMethod.isRoundedForAtmCash(paymentAccount.getPaymentMethod().getId()); } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java index 4316cc92fd..55741c10a1 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java @@ -100,7 +100,6 @@ import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; -import static haveno.core.payment.payload.PaymentMethod.HAL_CASH_ID; import static haveno.desktop.main.offer.OfferViewUtil.addPayInfoEntry; import static haveno.desktop.util.FormBuilder.add2ButtonsAfterGroup; import static haveno.desktop.util.FormBuilder.addAddressTextField; @@ -828,7 +827,7 @@ public abstract class MutableOfferView> exten int marketPriceAvailableValue = model.marketPriceAvailableProperty.get(); if (marketPriceAvailableValue > -1) { boolean showPriceToggle = marketPriceAvailableValue == 1 && - !model.getDataModel().paymentAccount.hasPaymentMethodWithId(HAL_CASH_ID); + !PaymentMethod.isFixedPriceOnly(model.getDataModel().paymentAccount.getPaymentMethod().getId()); percentagePriceBox.setVisible(showPriceToggle); priceTypeToggleButton.setVisible(showPriceToggle); boolean fixedPriceSelected = !model.getDataModel().getUseMarketBasedPrice().get() || !showPriceToggle; diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java index 5970818444..3b62b3c0ea 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java @@ -841,12 +841,7 @@ public abstract class MutableOfferViewModel ext Volume volume = dataModel.getVolume().get(); if (volume != null) { - // For HalCash we want multiple of 10 EUR - if (dataModel.isUsingHalCashAccount()) - volume = VolumeUtil.getAdjustedVolumeForHalCash(volume); - else if (CurrencyUtil.isFiatCurrency(tradeCurrencyCode.get())) - volume = VolumeUtil.getRoundedFiatVolume(volume); - + volume = VolumeUtil.getAdjustedVolume(volume, dataModel.getPaymentAccount().getPaymentMethod().getId()); this.volume.set(VolumeUtil.formatVolume(volume)); } @@ -1082,10 +1077,7 @@ public abstract class MutableOfferViewModel ext long maxTradeLimit = dataModel.getMaxTradeLimit(); Price price = dataModel.getPrice().get(); if (price != null && price.isPositive()) { - if (dataModel.isUsingHalCashAccount()) - amount = CoinUtil.getAdjustedAmountForHalCash(amount, price, maxTradeLimit); - else if (CurrencyUtil.isFiatCurrency(tradeCurrencyCode.get())) - amount = CoinUtil.getRoundedFiatAmount(amount, price, maxTradeLimit); + amount = CoinUtil.getRoundedAmount(amount, price, maxTradeLimit, tradeCurrencyCode.get(), dataModel.getPaymentAccount().getPaymentMethod().getId()); } dataModel.setAmount(amount); if (syncMinAmountWithAmount || @@ -1106,10 +1098,7 @@ public abstract class MutableOfferViewModel ext Price price = dataModel.getPrice().get(); long maxTradeLimit = dataModel.getMaxTradeLimit(); if (price != null && price.isPositive()) { - if (dataModel.isUsingHalCashAccount()) - minAmount = CoinUtil.getAdjustedAmountForHalCash(minAmount, price, maxTradeLimit); - else if (CurrencyUtil.isFiatCurrency(tradeCurrencyCode.get())) - minAmount = CoinUtil.getRoundedFiatAmount(minAmount, price, maxTradeLimit); + minAmount = CoinUtil.getRoundedAmount(minAmount, price, maxTradeLimit, tradeCurrencyCode.get(), dataModel.getPaymentAccount().getPaymentMethod().getId()); } dataModel.setMinAmount(minAmount); diff --git a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java index 30fa04b6cc..61745fb8cb 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java @@ -63,7 +63,6 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static haveno.core.payment.payload.PaymentMethod.HAL_CASH_ID; /** * Domain for that UI element. @@ -376,10 +375,7 @@ class TakeOfferDataModel extends OfferDataModel { amount.get() != null && amount.get().compareTo(BigInteger.valueOf(0)) != 0) { Volume volumeByAmount = tradePrice.getVolumeByAmount(amount.get()); - if (offer.getPaymentMethod().getId().equals(PaymentMethod.HAL_CASH_ID)) - volumeByAmount = VolumeUtil.getAdjustedVolumeForHalCash(volumeByAmount); - else if (offer.isFiatOffer()) - volumeByAmount = VolumeUtil.getRoundedFiatVolume(volumeByAmount); + volumeByAmount = VolumeUtil.getAdjustedVolume(volumeByAmount, offer.getPaymentMethod().getId()); volume.set(volumeByAmount); @@ -491,7 +487,7 @@ class TakeOfferDataModel extends OfferDataModel { return offer.getSellerSecurityDeposit(); } - public boolean isUsingHalCashAccount() { - return paymentAccount.hasPaymentMethodWithId(HAL_CASH_ID); + public boolean isRoundedForAtmCash() { + return PaymentMethod.isRoundedForAtmCash(paymentAccount.getPaymentMethod().getId()); } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java index 674c92cc90..6063d4599c 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java @@ -302,18 +302,19 @@ class TakeOfferViewModel extends ActivatableWithDataModel im Price tradePrice = dataModel.tradePrice; long maxTradeLimit = dataModel.getMaxTradeLimit(); - if (dataModel.getPaymentMethod().getId().equals(PaymentMethod.HAL_CASH_ID)) { - BigInteger adjustedAmountForHalCash = CoinUtil.getAdjustedAmountForHalCash(dataModel.getAmount().get(), + if (PaymentMethod.isRoundedForAtmCash(dataModel.getPaymentMethod().getId())) { + BigInteger adjustedAmountForHalCash = CoinUtil.getRoundedAtmCashAmount(dataModel.getAmount().get(), tradePrice, maxTradeLimit); dataModel.applyAmount(adjustedAmountForHalCash); amount.set(HavenoUtils.formatXmr(dataModel.getAmount().get())); - } else if (dataModel.getOffer().isFiatOffer()) { + } else if (dataModel.getOffer().isTraditionalOffer()) { if (!isAmountEqualMinAmount(dataModel.getAmount().get()) && (!isAmountEqualMaxAmount(dataModel.getAmount().get()))) { // 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 - BigInteger roundedAmount = CoinUtil.getRoundedFiatAmount(dataModel.getAmount().get(), tradePrice, - maxTradeLimit); + BigInteger roundedAmount = dataModel.getOffer().isFiatOffer() ? + CoinUtil.getRoundedFiatAmount(dataModel.getAmount().get(), tradePrice, maxTradeLimit) : + CoinUtil.getRoundedTraditionalAmount(dataModel.getAmount().get(), tradePrice, maxTradeLimit); dataModel.applyAmount(roundedAmount); } amount.set(HavenoUtils.formatXmr(dataModel.getAmount().get())); @@ -585,13 +586,15 @@ class TakeOfferViewModel extends ActivatableWithDataModel im long maxTradeLimit = dataModel.getMaxTradeLimit(); Price price = dataModel.tradePrice; if (price != null) { - if (dataModel.isUsingHalCashAccount()) { - amount = CoinUtil.getAdjustedAmountForHalCash(amount, price, maxTradeLimit); - } else if (dataModel.getOffer().isFiatOffer() + if (dataModel.isRoundedForAtmCash()) { + amount = CoinUtil.getRoundedAtmCashAmount(amount, price, maxTradeLimit); + } 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.getRoundedFiatAmount(amount, price, maxTradeLimit); + amount = dataModel.getOffer().isFiatOffer() ? + CoinUtil.getRoundedFiatAmount(amount, price, maxTradeLimit) : + CoinUtil.getRoundedTraditionalAmount(amount, price, maxTradeLimit); } } dataModel.applyAmount(amount); diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index cddf8be050..b5bbf530bf 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -856,6 +856,7 @@ message PaymentAccountPayload { CelPayAccountPayload cel_pay_account_payload = 37; MoneseAccountPayload monese_account_payload = 38; VerseAccountPayload verse_account_payload = 39; + CashByAtmAccountPayload cash_by_atm_account_payload = 40; } } @@ -1112,6 +1113,10 @@ message PayByMailAccountPayload { string extra_info = 3; } +message CashByAtmAccountPayload { + string extra_info = 1; +} + message PromptPayAccountPayload { string prompt_pay_id = 1; }