diff --git a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java index 6e77988b..edf5d902 100644 --- a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java @@ -305,13 +305,13 @@ public class XmrWalletService { * * @param returnAddress return address for reserved funds * @param tradeFee trade fee - * @param peerAmount amount to give peer + * @param sendAmount amount to give peer * @param securityDeposit security deposit amount * @return a transaction to reserve a trade */ - public MoneroTxWallet createReserveTx(BigInteger tradeFee, BigInteger peerAmount, BigInteger securityDeposit, String returnAddress) { - log.info("Creating reserve tx with fee={}, peerAmount={}, securityDeposit={}", tradeFee, peerAmount, securityDeposit); - return createTradeTx(tradeFee, peerAmount, securityDeposit, returnAddress); + public MoneroTxWallet createReserveTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String returnAddress) { + log.info("Creating reserve tx with fee={}, sendAmount={}, securityDeposit={}", tradeFee, sendAmount, securityDeposit); + return createTradeTx(tradeFee, sendAmount, securityDeposit, returnAddress); } /** @@ -324,7 +324,7 @@ public class XmrWalletService { Offer offer = trade.getProcessModel().getOffer(); String multisigAddress = trade.getProcessModel().getMultisigAddress(); BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(trade instanceof MakerTrade ? trade.getOffer().getMakerFee() : trade.getTakerFee()); - BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(trade instanceof BuyerTrade ? Coin.ZERO : offer.getAmount()); + BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(trade instanceof BuyerTrade ? Coin.ZERO : offer.getAmount()); BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(trade instanceof BuyerTrade ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit()); // thaw reserved outputs then create deposit tx @@ -336,12 +336,12 @@ public class XmrWalletService { thawOutputs(trade.getSelf().getReserveTxKeyImages()); } - log.info("Creating deposit tx with fee={}, peerAmount={}, securityDeposit={}", tradeFee, peerAmount, securityDeposit); - return createTradeTx(tradeFee, peerAmount, securityDeposit, multisigAddress); + log.info("Creating deposit tx with fee={}, sendAmount={}, securityDeposit={}", tradeFee, sendAmount, securityDeposit); + return createTradeTx(tradeFee, sendAmount, securityDeposit, multisigAddress); } } - private MoneroTxWallet createTradeTx(BigInteger tradeFee, BigInteger peerAmount, BigInteger securityDeposit, String address) { + private MoneroTxWallet createTradeTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address) { MoneroWallet wallet = getWallet(); synchronized (wallet) { @@ -349,7 +349,7 @@ public class XmrWalletService { MoneroTxWallet tradeTx = null; double appliedTolerance = 0.0; // percent of tolerance to apply, thereby decreasing security deposit double searchDiff = 1.0; // difference for next binary search - BigInteger maxAmount = peerAmount.add(securityDeposit); + BigInteger maxAmount = sendAmount.add(securityDeposit); for (int i = 0; i < 10; i++) { try { BigInteger amount = new BigDecimal(maxAmount).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE * appliedTolerance)).toBigInteger(); @@ -379,7 +379,7 @@ public class XmrWalletService { * The transaction is submitted to the pool then flushed without relaying. * * @param tradeFee trade fee - * @param peerAmount amount to give peer + * @param sendAmount amount to give peer * @param securityDeposit security deposit amount * @param address expected destination address for the deposit amount * @param txHash transaction hash @@ -387,7 +387,7 @@ public class XmrWalletService { * @param txKey transaction key * @param keyImages expected key images of inputs, ignored if null */ - public void verifyTradeTx(BigInteger tradeFee, BigInteger peerAmount, BigInteger securityDeposit, String address, String txHash, String txHex, String txKey, List keyImages) { + public void verifyTradeTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, String txHash, String txHex, String txKey, List keyImages) { MoneroDaemonRpc daemon = getDaemon(); MoneroWallet wallet = getWallet(); try { @@ -426,7 +426,7 @@ public class XmrWalletService { // verify deposit amount check = wallet.checkTxKey(txHash, txKey, address); if (!check.isGood()) throw new RuntimeException("Invalid proof of deposit amount"); - BigInteger minAmount = new BigDecimal(peerAmount.add(securityDeposit)).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE)).toBigInteger(); + BigInteger minAmount = new BigDecimal(sendAmount.add(securityDeposit)).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE)).toBigInteger(); if (check.getReceivedAmount().compareTo(minAmount) < 0) throw new RuntimeException("Deposit amount is not enough, needed " + minAmount + " but was " + check.getReceivedAmount()); } finally { try { diff --git a/core/src/main/java/bisq/core/offer/CreateOfferService.java b/core/src/main/java/bisq/core/offer/CreateOfferService.java index 3888c738..aabf9105 100644 --- a/core/src/main/java/bisq/core/offer/CreateOfferService.java +++ b/core/src/main/java/bisq/core/offer/CreateOfferService.java @@ -27,6 +27,7 @@ import bisq.core.payment.PaymentAccountUtil; import bisq.core.provider.price.MarketPrice; import bisq.core.provider.price.PriceFeedService; import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; +import bisq.core.trade.HavenoUtils; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.Preferences; import bisq.core.user.User; @@ -155,7 +156,7 @@ public class CreateOfferService { String bankId = PaymentAccountUtil.getBankId(paymentAccount); List acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount); double sellerSecurityDeposit = getSellerSecurityDepositAsDouble(buyerSecurityDepositAsDouble); - Coin makerFeeAsCoin = offerUtil.getMakerFee(amount); + Coin makerFeeAsCoin = HavenoUtils.getMakerFee(amount); Coin buyerSecurityDepositAsCoin = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble); Coin sellerSecurityDepositAsCoin = getSellerSecurityDeposit(amount, sellerSecurityDeposit); long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction); diff --git a/core/src/main/java/bisq/core/offer/OfferUtil.java b/core/src/main/java/bisq/core/offer/OfferUtil.java index 63ec73da..ca1dd9c8 100644 --- a/core/src/main/java/bisq/core/offer/OfferUtil.java +++ b/core/src/main/java/bisq/core/offer/OfferUtil.java @@ -29,12 +29,10 @@ import bisq.core.payment.F2FAccount; import bisq.core.payment.PaymentAccount; import bisq.core.provider.price.MarketPrice; import bisq.core.provider.price.PriceFeedService; -import bisq.core.trade.HavenoUtils; import bisq.core.trade.statistics.ReferralIdService; import bisq.core.user.AutoConfirmSettings; import bisq.core.user.Preferences; import bisq.core.util.coin.CoinFormatter; -import bisq.core.util.coin.CoinUtil; import bisq.network.p2p.P2PService; @@ -58,8 +56,6 @@ import java.util.UUID; import lombok.extern.slf4j.Slf4j; -import javax.annotation.Nullable; - import static bisq.common.util.MathUtils.roundDoubleToLong; import static bisq.common.util.MathUtils.scaleUpByPowerOf10; import static bisq.core.btc.wallet.Restrictions.getMaxBuyerSecurityDepositAsPercent; @@ -163,41 +159,6 @@ public class OfferUtil { return MathUtils.roundDouble(manualPrice / marketPrice, 4); } - /** - * Returns the makerFee as Coin, this can be priced in BTC. - * - * @param amount the amount of BTC to trade - * @return the maker fee for the given trade amount, or {@code null} if the amount - * is {@code null} - */ - @Nullable - public Coin getMakerFee(@Nullable Coin amount) { - return CoinUtil.getMakerFee(amount); - } - - public Coin getTxFeeByVsize(Coin txFeePerVbyteFromFeeService, int vsizeInVbytes) { - return txFeePerVbyteFromFeeService.multiply(getAverageTakerFeeTxVsize(vsizeInVbytes)); - } - - // We use the sum of the size of the trade fee and the deposit tx to get an average. - // Miners will take the trade fee tx if the total fee of both dependent txs are good - // enough. With that we avoid that we overpay in case that the trade fee has many - // inputs and we would apply that fee for the other 2 txs as well. We still might - // overpay a bit for the payout tx. - public int getAverageTakerFeeTxVsize(int txVsize) { - return (txVsize + 233) / 2; - } - - @Nullable - public Coin getTakerFee(@Nullable Coin amount) { - if (amount != null) { - Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), amount); - return CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee()); - } else { - return null; - } - } - public boolean isBlockChainPaymentMethod(Offer offer) { return offer != null && offer.getPaymentMethod().isBlockchain(); } diff --git a/core/src/main/java/bisq/core/offer/OpenOfferManager.java b/core/src/main/java/bisq/core/offer/OpenOfferManager.java index 6e6fb3bb..7e45ec19 100644 --- a/core/src/main/java/bisq/core/offer/OpenOfferManager.java +++ b/core/src/main/java/bisq/core/offer/OpenOfferManager.java @@ -856,15 +856,23 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); return; } + + // verify maker's trade fee + Offer offer = new Offer(request.getOfferPayload()); + BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(HavenoUtils.getMakerFee(offer.getAmount())); + if (!tradeFee.equals(HavenoUtils.coinToAtomicUnits(offer.getMakerFee()))) { + errorMessage = "Wrong trade fee for offer " + request.offerId; + log.info(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } // verify maker's reserve tx (double spend, trade fee, trade amount, mining fee) - Offer offer = new Offer(request.getOfferPayload()); - BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(offer.getMakerFee()); - BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount()); + BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount()); BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit()); xmrWalletService.verifyTradeTx( tradeFee, - peerAmount, + sendAmount, securityDeposit, request.getPayoutAddress(), request.getReserveTxHash(), diff --git a/core/src/main/java/bisq/core/offer/availability/tasks/SendOfferAvailabilityRequest.java b/core/src/main/java/bisq/core/offer/availability/tasks/SendOfferAvailabilityRequest.java index 517e661f..cf5238aa 100644 --- a/core/src/main/java/bisq/core/offer/availability/tasks/SendOfferAvailabilityRequest.java +++ b/core/src/main/java/bisq/core/offer/availability/tasks/SendOfferAvailabilityRequest.java @@ -24,6 +24,7 @@ import bisq.core.offer.Offer; import bisq.core.offer.OfferUtil; import bisq.core.offer.availability.OfferAvailabilityModel; import bisq.core.offer.messages.OfferAvailabilityRequest; +import bisq.core.trade.HavenoUtils; import bisq.core.trade.messages.InitTradeRequest; import bisq.core.user.User; import bisq.network.p2p.P2PService; @@ -55,7 +56,6 @@ public class SendOfferAvailabilityRequest extends Task { User user = model.getUser(); P2PService p2PService = model.getP2PService(); XmrWalletService walletService = model.getXmrWalletService(); - OfferUtil offerUtil = model.getOfferUtil(); String paymentAccountId = model.getPaymentAccountId(); String paymentMethodId = user.getPaymentAccount(paymentAccountId).getPaymentAccountPayload().getPaymentMethodId(); String payoutAddress = walletService.getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(); // reserve new payout address @@ -74,7 +74,7 @@ public class SendOfferAvailabilityRequest extends Task { p2PService.getKeyRing().getPubKeyRing(), offer.getAmount().value, price.getValue(), - offerUtil.getTakerFee(offer.getAmount()).value, + HavenoUtils.getTakerFee(offer.getAmount()).value, user.getAccountId(), paymentAccountId, paymentMethodId, diff --git a/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java b/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java index 74285b5b..625ada36 100644 --- a/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java +++ b/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java @@ -52,10 +52,10 @@ public class MakerReserveOfferFunds extends Task { // create reserve tx BigInteger makerFee = HavenoUtils.coinToAtomicUnits(offer.getMakerFee()); - BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount()); + BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount()); BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit()); String returnAddress = model.getXmrWalletService().getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(); - MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(makerFee, peerAmount, securityDeposit, returnAddress); + MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(makerFee, sendAmount, securityDeposit, returnAddress); // collect reserved key images List reservedKeyImages = new ArrayList(); diff --git a/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java b/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java index a5578457..a0a5d6c5 100644 --- a/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java +++ b/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java @@ -28,7 +28,7 @@ import bisq.core.offer.OfferUtil; import bisq.core.payment.PaymentAccount; import bisq.core.payment.payload.PaymentMethod; import bisq.core.provider.price.PriceFeedService; - +import bisq.core.trade.HavenoUtils; import bisq.common.taskrunner.Model; import org.bitcoinj.core.Coin; @@ -111,7 +111,7 @@ public class TakeOfferModel implements Model { this.securityDeposit = offer.getDirection() == SELL ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit(); - this.takerFee = offerUtil.getTakerFee(amount); + this.takerFee = HavenoUtils.getTakerFee(amount); calculateVolume(); calculateTotalToPay(); diff --git a/core/src/main/java/bisq/core/trade/BuyerAsMakerTrade.java b/core/src/main/java/bisq/core/trade/BuyerAsMakerTrade.java index ce86301b..cb3d049f 100644 --- a/core/src/main/java/bisq/core/trade/BuyerAsMakerTrade.java +++ b/core/src/main/java/bisq/core/trade/BuyerAsMakerTrade.java @@ -32,8 +32,6 @@ import java.util.UUID; import lombok.extern.slf4j.Slf4j; -import javax.annotation.Nullable; - @Slf4j public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade { diff --git a/core/src/main/java/bisq/core/trade/HavenoUtils.java b/core/src/main/java/bisq/core/trade/HavenoUtils.java index 9155e441..40881417 100644 --- a/core/src/main/java/bisq/core/trade/HavenoUtils.java +++ b/core/src/main/java/bisq/core/trade/HavenoUtils.java @@ -28,6 +28,7 @@ import bisq.core.trade.messages.PaymentReceivedMessage; import bisq.core.trade.messages.PaymentSentMessage; import bisq.core.util.JsonUtil; import bisq.core.util.ParsingUtils; +import bisq.core.util.coin.CoinUtil; import lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; @@ -39,6 +40,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; + import org.bitcoinj.core.Coin; import org.bitcoinj.utils.MonetaryFormat; import com.google.common.base.CaseFormat; @@ -99,8 +102,27 @@ public class HavenoUtils { private static final MonetaryFormat xmrCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat(); + @Nullable + public static Coin getMakerFee(@Nullable Coin amount) { + if (amount != null) { + Coin feePerXmr = getFeePerXmr(HavenoUtils.getMakerFeePerXmr(), amount); + return CoinUtil.maxCoin(feePerXmr, HavenoUtils.getMinMakerFee()); + } else { + return null; + } + } - public static Coin getMakerFeePerBtc() { + @Nullable + public static Coin getTakerFee(@Nullable Coin amount) { + if (amount != null) { + Coin feePerXmr = HavenoUtils.getFeePerXmr(HavenoUtils.getTakerFeePerXmr(), amount); + return CoinUtil.maxCoin(feePerXmr, HavenoUtils.getMinTakerFee()); + } else { + return null; + } + } + + private static Coin getMakerFeePerXmr() { return ParsingUtils.parseToCoin("0.001", xmrCoinFormat); } @@ -108,7 +130,7 @@ public class HavenoUtils { return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat); } - public static Coin getTakerFeePerBtc() { + private static Coin getTakerFeePerXmr() { return ParsingUtils.parseToCoin("0.003", xmrCoinFormat); } @@ -116,6 +138,14 @@ public class HavenoUtils { return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat); } + public static Coin getFeePerXmr(Coin feePerXmr, Coin amount) { + double feePerBtcAsDouble = feePerXmr != null ? (double) feePerXmr.value : 0; + double amountAsDouble = amount != null ? (double) amount.value : 0; + double btcAsDouble = (double) Coin.COIN.value; + double fact = amountAsDouble / btcAsDouble; + return Coin.valueOf(Math.round(feePerBtcAsDouble * fact)); + } + /** * Get address to collect trade fees. * diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java index 5d4e078a..20ac1a9a 100644 --- a/core/src/main/java/bisq/core/trade/TradeManager.java +++ b/core/src/main/java/bisq/core/trade/TradeManager.java @@ -55,7 +55,6 @@ import bisq.core.trade.statistics.ReferralIdService; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.User; import bisq.core.util.Validator; -import bisq.core.util.coin.CoinUtil; import bisq.network.p2p.BootstrapListener; import bisq.network.p2p.DecryptedDirectMessageListener; import bisq.network.p2p.DecryptedMessageWithPubKey; @@ -456,9 +455,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi return; } - // compute expected taker fee - Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), Coin.valueOf(offer.getOfferPayload().getAmount())); - Coin takerFee = CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee()); + // get expected taker fee + Coin takerFee = HavenoUtils.getTakerFee(Coin.valueOf(offer.getOfferPayload().getAmount())); // create arbitrator trade trade = new ArbitratorTrade(offer, @@ -522,13 +520,17 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi return; } + // reserve open offer openOfferManager.reserveOpenOffer(openOffer); // TODO (woodser): reserve offer if arbitrator? probably. or, arbitrator does not have open offer? + // get expected taker fee + Coin takerFee = HavenoUtils.getTakerFee(Coin.valueOf(offer.getOfferPayload().getAmount())); + Trade trade; if (offer.isBuyOffer()) trade = new BuyerAsMakerTrade(offer, Coin.valueOf(offer.getOfferPayload().getAmount()), - Coin.valueOf(offer.getOfferPayload().getMakerFee()), // TODO (woodser): this is maker fee, but Trade calls it taker fee, why not have both? + takerFee, offer.getOfferPayload().getPrice(), xmrWalletService, getNewProcessModel(offer), @@ -539,7 +541,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi else trade = new SellerAsMakerTrade(offer, Coin.valueOf(offer.getOfferPayload().getAmount()), - Coin.valueOf(offer.getOfferPayload().getMakerFee()), + takerFee, offer.getOfferPayload().getPrice(), xmrWalletService, getNewProcessModel(offer), diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java index 4fb2fd82..2e2eb073 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java @@ -75,22 +75,19 @@ public class ArbitratorProcessDepositRequest extends TradeTask { // collect expected values Offer offer = trade.getOffer(); - boolean isFromTaker = request.getSenderNodeAddress().equals(trade.getTaker().getNodeAddress()); - boolean isFromBuyer = isFromTaker ? offer.getDirection() == OfferDirection.SELL : offer.getDirection() == OfferDirection.BUY; - BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(isFromBuyer ? Coin.ZERO : offer.getAmount()); + TradingPeer trader = trade.getTradingPeer(request.getSenderNodeAddress()); + boolean isFromTaker = trader == trade.getTaker(); + boolean isFromBuyer = trader == trade.getBuyer(); + BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(isFromTaker ? trade.getTakerFee() : trade.getMakerFee()); + BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(isFromBuyer ? Coin.ZERO : offer.getAmount()); BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(isFromBuyer ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit()); String depositAddress = processModel.getMultisigAddress(); - BigInteger tradeFee; - TradingPeer trader = trade.getTradingPeer(request.getSenderNodeAddress()); - if (trader == processModel.getMaker()) tradeFee = HavenoUtils.coinToAtomicUnits(trade.getOffer().getMakerFee()); - else if (trader == processModel.getTaker()) tradeFee = HavenoUtils.coinToAtomicUnits(trade.getTakerFee()); - else throw new RuntimeException("DepositRequest is not from maker or taker"); // verify deposit tx try { trade.getXmrWalletService().verifyTradeTx( tradeFee, - peerAmount, + sendAmount, securityDeposit, depositAddress, trader.getDepositTxHash(), diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java index 14be923e..97c26b1b 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java @@ -54,14 +54,14 @@ public class ArbitratorProcessReserveTx extends TradeTask { // TODO (woodser): if signer online, should never be called by maker - // process reserve tx with expected terms - BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(isFromTaker ? trade.getTakerFee() : offer.getMakerFee()); - BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(isFromBuyer ? Coin.ZERO : offer.getAmount()); + // process reserve tx with expected values + BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(isFromTaker ? trade.getTakerFee() : trade.getMakerFee()); + BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(isFromBuyer ? Coin.ZERO : offer.getAmount()); BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(isFromBuyer ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit()); try { trade.getXmrWalletService().verifyTradeTx( tradeFee, - peerAmount, + sendAmount, securityDeposit, request.getPayoutAddress(), request.getReserveTxHash(), diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/TakerReserveTradeFunds.java b/core/src/main/java/bisq/core/trade/protocol/tasks/TakerReserveTradeFunds.java index 184e3462..1122632b 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/TakerReserveTradeFunds.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/TakerReserveTradeFunds.java @@ -44,10 +44,10 @@ public class TakerReserveTradeFunds extends TradeTask { // create reserve tx BigInteger takerFee = HavenoUtils.coinToAtomicUnits(trade.getTakerFee()); - BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getOffer().getAmount() : Coin.ZERO); + BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getOffer().getAmount() : Coin.ZERO); BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getOffer().getSellerSecurityDeposit() : trade.getOffer().getBuyerSecurityDeposit()); String returnAddress = model.getXmrWalletService().getOrCreateAddressEntry(trade.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(); - MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(takerFee, peerAmount, securityDeposit, returnAddress); + MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(takerFee, sendAmount, securityDeposit, returnAddress); // collect reserved key images List reservedKeyImages = new ArrayList(); diff --git a/core/src/main/java/bisq/core/util/coin/CoinUtil.java b/core/src/main/java/bisq/core/util/coin/CoinUtil.java index 0cb319f1..f92210e3 100644 --- a/core/src/main/java/bisq/core/util/coin/CoinUtil.java +++ b/core/src/main/java/bisq/core/util/coin/CoinUtil.java @@ -20,28 +20,17 @@ package bisq.core.util.coin; import bisq.core.btc.wallet.Restrictions; import bisq.core.monetary.Price; import bisq.core.monetary.Volume; -import bisq.core.trade.HavenoUtils; import bisq.common.util.MathUtils; import org.bitcoinj.core.Coin; import com.google.common.annotations.VisibleForTesting; -import javax.annotation.Nullable; - import static bisq.core.util.VolumeUtil.getAdjustedFiatVolume; import static com.google.common.base.Preconditions.checkArgument; public class CoinUtil { - // Get the fee per amount - public static Coin getFeePerBtc(Coin feePerBtc, Coin amount) { - double feePerBtcAsDouble = feePerBtc != null ? (double) feePerBtc.value : 0; - double amountAsDouble = amount != null ? (double) amount.value : 0; - double btcAsDouble = (double) Coin.COIN.value; - double fact = amountAsDouble / btcAsDouble; - return Coin.valueOf(Math.round(feePerBtcAsDouble * fact)); - } public static Coin minCoin(Coin a, Coin b) { return a.compareTo(b) <= 0 ? a : b; @@ -87,23 +76,6 @@ public class CoinUtil { return Coin.valueOf(Math.round(percent * amountAsDouble)); } - - /** - * Calculates the maker fee for the given amount, marketPrice and marketPriceMargin. - * - * @param amount the amount of BTC to trade - * @return the maker fee for the given trade amount, or {@code null} if the amount is {@code null} - */ - @Nullable - public static Coin getMakerFee(@Nullable Coin amount) { - if (amount != null) { - Coin feePerBtc = getFeePerBtc(HavenoUtils.getMakerFeePerBtc(), amount); - return maxCoin(feePerBtc, HavenoUtils.getMinMakerFee()); - } else { - return null; - } - } - /** * Calculate the possibly adjusted amount for {@code amount}, taking into account the * {@code price} and {@code maxTradeLimit} and {@code factor}. diff --git a/core/src/test/java/bisq/core/util/coin/CoinUtilTest.java b/core/src/test/java/bisq/core/util/coin/CoinUtilTest.java index 1cfefb92..5bb1c8b0 100644 --- a/core/src/test/java/bisq/core/util/coin/CoinUtilTest.java +++ b/core/src/test/java/bisq/core/util/coin/CoinUtilTest.java @@ -18,6 +18,7 @@ package bisq.core.util.coin; import bisq.core.monetary.Price; +import bisq.core.trade.HavenoUtils; import org.bitcoinj.core.Coin; @@ -30,10 +31,10 @@ public class CoinUtilTest { @Test public void testGetFeePerBtc() { - assertEquals(Coin.parseCoin("1"), CoinUtil.getFeePerBtc(Coin.parseCoin("1"), Coin.parseCoin("1"))); - assertEquals(Coin.parseCoin("0.1"), CoinUtil.getFeePerBtc(Coin.parseCoin("0.1"), Coin.parseCoin("1"))); - assertEquals(Coin.parseCoin("0.01"), CoinUtil.getFeePerBtc(Coin.parseCoin("0.1"), Coin.parseCoin("0.1"))); - assertEquals(Coin.parseCoin("0.015"), CoinUtil.getFeePerBtc(Coin.parseCoin("0.3"), Coin.parseCoin("0.05"))); + assertEquals(Coin.parseCoin("1"), HavenoUtils.getFeePerXmr(Coin.parseCoin("1"), Coin.parseCoin("1"))); + assertEquals(Coin.parseCoin("0.1"), HavenoUtils.getFeePerXmr(Coin.parseCoin("0.1"), Coin.parseCoin("1"))); + assertEquals(Coin.parseCoin("0.01"), HavenoUtils.getFeePerXmr(Coin.parseCoin("0.1"), Coin.parseCoin("0.1"))); + assertEquals(Coin.parseCoin("0.015"), HavenoUtils.getFeePerXmr(Coin.parseCoin("0.3"), Coin.parseCoin("0.05"))); } @Test diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java index 80ec061e..9e3c5d31 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java @@ -37,6 +37,7 @@ import bisq.core.offer.OfferUtil; import bisq.core.offer.OpenOfferManager; import bisq.core.payment.PaymentAccount; import bisq.core.provider.price.PriceFeedService; +import bisq.core.trade.HavenoUtils; import bisq.core.trade.handlers.TransactionResultHandler; import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.trade.statistics.TradeStatisticsManager; @@ -674,11 +675,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { } public Coin getMakerFee() { - return offerUtil.getMakerFee(amount.get()); - } - - public Coin getMakerFeeInBtc() { - return CoinUtil.getMakerFee(amount.get()); + return HavenoUtils.getMakerFee(amount.get()); } boolean canPlaceOffer() { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java index 8925b1d5..a454ff70 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java @@ -506,7 +506,7 @@ public abstract class MutableOfferViewModel ext isTradeFeeVisible.setValue(true); tradeFee.set(getFormatterForMakerFee().formatCoin(makerFeeAsCoin)); tradeFeeInBtcWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil, - dataModel.getMakerFeeInBtc(), + dataModel.getMakerFee(), btcFormatter)); } @@ -996,7 +996,7 @@ public abstract class MutableOfferViewModel ext public String getTradeFee() { return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil, - dataModel.getMakerFeeInBtc(), + dataModel.getMakerFee(), dataModel.getAmount().get(), btcFormatter, HavenoUtils.getMinMakerFee()); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java index 32832d0b..42f9b7e2 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java @@ -434,14 +434,7 @@ class TakeOfferDataModel extends OfferDataModel { @Nullable Coin getTakerFee() { - Coin amount = this.amount.get(); - if (amount != null) { - // TODO write unit test for that - Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), amount); - return CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee()); - } else { - return null; - } + return HavenoUtils.getTakerFee(this.amount.get()); } public void swapTradeToSavings() { @@ -523,8 +516,4 @@ class TakeOfferDataModel extends OfferDataModel { public boolean isUsingHalCashAccount() { return paymentAccount.hasPaymentMethodWithId(HAL_CASH_ID); } - - public Coin getTakerFeeInBtc() { - return offerUtil.getTakerFee(amount.get()); - } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java index 87c156fa..de9ac430 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java @@ -547,7 +547,7 @@ public class TakeOfferView extends ActivatableViewAndModel CurrencyUtil.getCounterCurrency(model.dataModel.getCurrencyCode()))); priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty()); nextButton.disableProperty().bind(model.isNextButtonDisabled); - tradeFeeInBtcLabel.textProperty().bind(model.tradeFeeInBtcWithFiat); + tradeFeeInBtcLabel.textProperty().bind(model.tradeFeeInXmrWithFiat); tradeFeeDescriptionLabel.textProperty().bind(model.tradeFeeDescription); tradeFeeInBtcLabel.visibleProperty().bind(model.isTradeFeeVisible); tradeFeeDescriptionLabel.visibleProperty().bind(model.isTradeFeeVisible); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java index e280a0e6..ef4e3ddd 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java @@ -103,7 +103,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel im final StringProperty offerWarning = new SimpleStringProperty(); final StringProperty spinnerInfoText = new SimpleStringProperty(""); final StringProperty tradeFee = new SimpleStringProperty(); - final StringProperty tradeFeeInBtcWithFiat = new SimpleStringProperty(); + final StringProperty tradeFeeInXmrWithFiat = new SimpleStringProperty(); final StringProperty tradeFeeDescription = new SimpleStringProperty(); final BooleanProperty isTradeFeeVisible = new SimpleBooleanProperty(false); @@ -283,8 +283,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel im isTradeFeeVisible.setValue(true); tradeFee.set(getFormatterForTakerFee().formatCoin(takerFeeAsCoin)); - tradeFeeInBtcWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil, - dataModel.getTakerFeeInBtc(), + tradeFeeInXmrWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil, + dataModel.getTakerFee(), xmrFormatter)); } @@ -689,7 +689,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel im public String getTradeFee() { return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil, - dataModel.getTakerFeeInBtc(), + dataModel.getTakerFee(), dataModel.getAmount().get(), xmrFormatter, HavenoUtils.getMinMakerFee()); diff --git a/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferDataModelTest.java b/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferDataModelTest.java index 7072cfdd..e9eb69bb 100644 --- a/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferDataModelTest.java +++ b/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferDataModelTest.java @@ -17,8 +17,6 @@ import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.Preferences; import bisq.core.user.User; -import org.bitcoinj.core.Coin; - import javafx.collections.FXCollections; import java.util.HashSet; @@ -47,7 +45,7 @@ public class CreateOfferDataModelTest { Res.setup(); XmrAddressEntry addressEntry = mock(XmrAddressEntry.class); - XmrWalletService btcWalletService = mock(XmrWalletService.class); + XmrWalletService xmrWalletService = mock(XmrWalletService.class); PriceFeedService priceFeedService = mock(PriceFeedService.class); CreateOfferService createOfferService = mock(CreateOfferService.class); preferences = mock(Preferences.class); @@ -55,7 +53,7 @@ public class CreateOfferDataModelTest { user = mock(User.class); var tradeStats = mock(TradeStatisticsManager.class); - when(btcWalletService.getOrCreateAddressEntry(anyString(), any())).thenReturn(addressEntry); + when(xmrWalletService.getOrCreateAddressEntry(anyString(), any())).thenReturn(addressEntry); when(preferences.isUsePercentageBasedPrice()).thenReturn(true); when(preferences.getBuyerSecurityDepositAsPercent(null)).thenReturn(0.01); when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString()); @@ -64,7 +62,7 @@ public class CreateOfferDataModelTest { model = new CreateOfferDataModel(createOfferService, null, offerUtil, - btcWalletService, + xmrWalletService, preferences, user, null, @@ -91,7 +89,6 @@ public class CreateOfferDataModelTest { when(user.getPaymentAccounts()).thenReturn(paymentAccounts); when(preferences.getSelectedPaymentAccountForCreateOffer()).thenReturn(revolutAccount); - when(offerUtil.getMakerFee(any())).thenReturn(Coin.ZERO); model.initWithData(OfferDirection.BUY, new FiatCurrency("USD")); assertEquals("USD", model.getTradeCurrencyCode().get()); @@ -113,7 +110,6 @@ public class CreateOfferDataModelTest { when(user.getPaymentAccounts()).thenReturn(paymentAccounts); when(user.findFirstPaymentAccountWithCurrency(new FiatCurrency("USD"))).thenReturn(zelleAccount); when(preferences.getSelectedPaymentAccountForCreateOffer()).thenReturn(revolutAccount); - when(offerUtil.getMakerFee(any())).thenReturn(Coin.ZERO); model.initWithData(OfferDirection.BUY, new FiatCurrency("USD")); assertEquals("USD", model.getTradeCurrencyCode().get());