From dd0a307a844bae9c9fd5014884d150d5ace14772 Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 3 Dec 2022 14:33:55 +0000 Subject: [PATCH] support funding make or take offer directly QR code encodes payment URI security deposit absorbs miner fee up to 5% use binary search to maximize security deposit and minimize dust show itemized funding popup on create offer --- .../src/main/java/bisq/core/btc/Balances.java | 4 +- .../core/btc/wallet/XmrWalletService.java | 163 ++++++++---------- .../bisq/core/offer/OpenOfferManager.java | 22 +-- .../tasks/MakerReserveOfferFunds.java | 19 +- .../core/payment/payload/PaymentMethod.java | 6 +- .../core/support/dispute/DisputeManager.java | 4 +- .../arbitration/ArbitrationManager.java | 8 +- .../java/bisq/core/trade/HavenoUtils.java | 48 ++++++ core/src/main/java/bisq/core/trade/Trade.java | 4 +- .../ArbitratorProcessDepositRequest.java | 23 ++- .../tasks/ArbitratorProcessReserveTx.java | 18 +- .../tasks/TakerReserveTradeFunds.java | 17 +- .../java/bisq/core/util/ParsingUtils.java | 46 ----- .../resources/i18n/displayStrings.properties | 15 +- .../i18n/displayStrings_cs.properties | 2 - .../i18n/displayStrings_de.properties | 2 - .../i18n/displayStrings_es.properties | 2 - .../i18n/displayStrings_fa.properties | 2 - .../i18n/displayStrings_fr.properties | 2 - .../i18n/displayStrings_it.properties | 2 - .../i18n/displayStrings_ja.properties | 2 - .../i18n/displayStrings_pt-br.properties | 2 - .../i18n/displayStrings_pt.properties | 2 - .../i18n/displayStrings_ru.properties | 2 - .../i18n/displayStrings_th.properties | 2 - .../i18n/displayStrings_vi.properties | 2 - .../i18n/displayStrings_zh-hans.properties | 2 - .../i18n/displayStrings_zh-hant.properties | 2 - .../bisq/daemon/grpc/GrpcDisputesService.java | 4 +- .../bisq/daemon/grpc/GrpcOffersService.java | 6 +- .../desktop/components/AddressTextField.java | 9 +- .../main/funds/deposit/DepositListItem.java | 7 +- .../main/funds/deposit/DepositView.java | 8 +- .../transactions/TransactionsListItem.java | 6 +- .../funds/withdrawal/WithdrawalListItem.java | 3 +- .../main/funds/withdrawal/WithdrawalView.java | 5 +- .../main/offer/MutableOfferDataModel.java | 11 +- .../desktop/main/offer/MutableOfferView.java | 35 ++-- .../main/offer/MutableOfferViewModel.java | 17 +- .../desktop/main/offer/OfferDataModel.java | 1 + .../offer/takeoffer/TakeOfferDataModel.java | 7 +- .../main/offer/takeoffer/TakeOfferView.java | 32 ++-- .../offer/takeoffer/TakeOfferViewModel.java | 15 -- .../main/java/bisq/desktop/util/GUIUtil.java | 25 ++- 44 files changed, 263 insertions(+), 353 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/Balances.java b/core/src/main/java/bisq/core/btc/Balances.java index 6c773ffe..5e7f7531 100644 --- a/core/src/main/java/bisq/core/btc/Balances.java +++ b/core/src/main/java/bisq/core/btc/Balances.java @@ -25,10 +25,10 @@ import bisq.core.offer.OpenOfferManager; import bisq.core.support.dispute.Dispute; import bisq.core.support.dispute.refund.RefundManager; import bisq.core.trade.ClosedTradableManager; +import bisq.core.trade.HavenoUtils; import bisq.core.trade.Trade; import bisq.core.trade.TradeManager; import bisq.core.trade.failed.FailedTradesManager; -import bisq.core.util.ParsingUtils; import bisq.network.p2p.P2PService; import java.math.BigInteger; import java.util.List; @@ -137,7 +137,7 @@ public class Balances { } else { reservedAmt = trade.getContract().isMyRoleBuyer(tradeManager.getKeyRing().getPubKeyRing()) ? offerPayload.getBuyerSecurityDeposit() : offerPayload.getAmount() + offerPayload.getSellerSecurityDeposit(); } - sum = sum.add(Coin.valueOf(ParsingUtils.centinerosToAtomicUnits(reservedAmt).longValueExact())); + sum = sum.add(Coin.valueOf(HavenoUtils.centinerosToAtomicUnits(reservedAmt).longValueExact())); } reservedTradeBalance.set(sum); } 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 974aea6f..8db7b016 100644 --- a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java @@ -17,16 +17,16 @@ import bisq.core.btc.setup.MoneroWalletRpcManager; import bisq.core.btc.setup.WalletsSetup; import bisq.core.offer.Offer; import bisq.core.trade.MakerTrade; -import bisq.core.trade.SellerTrade; import bisq.core.trade.Trade; import bisq.core.trade.TradeManager; +import bisq.core.trade.BuyerTrade; import bisq.core.trade.HavenoUtils; -import bisq.core.util.ParsingUtils; import com.google.common.util.concurrent.Service.State; import com.google.inject.name.Named; import common.utils.JsonUtils; import java.io.File; +import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; @@ -83,8 +83,8 @@ public class XmrWalletService { private static final String MONERO_WALLET_RPC_DEFAULT_PASSWORD = "password"; // only used if account password is null private static final String MONERO_WALLET_NAME = "haveno_XMR"; private static final String MONERO_MULTISIG_WALLET_PREFIX = "xmr_multisig_trade_"; - private static final int MINER_FEE_PADDING_MULTIPLIER = 2; // extra padding for miner fees = estimated fee * multiplier - private static final double MINER_FEE_TOLERANCE = 0.25; // miner fee must be within percent of estimated fee + public static final double MINER_FEE_TOLERANCE = 0.25; // miner fee must be within percent of estimated fee + private static final double SECURITY_DEPOSIT_TOLERANCE = Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL ? 0.25 : 0.05; // security deposit absorbs miner fee up to percent private final CoreAccountService accountService; private final CoreMoneroConnectionsService connectionsService; @@ -265,106 +265,86 @@ public class XmrWalletService { } /** - * Create the reserve tx and freeze its inputs. The deposit amount is returned - * to the sender's payout address. Additional funds are reserved to allow - * fluctuations in the mining fee. + * Create the reserve tx and freeze its inputs. The full amount is returned + * to the sender's payout address less the trade fee. * - * @param tradeFee - trade fee - * @param depositAmount - amount needed for the trade minus the trade fee - * @param returnAddress - return address for deposit amount - * @param addPadding - reserve additional padding to cover future mining fee + * @param returnAddress return address for reserved funds + * @param tradeFee trade fee + * @param peerAmount amount to give peer + * @param securityDeposit security deposit amount * @return a transaction to reserve a trade */ - public MoneroTxWallet createReserveTx(BigInteger tradeFee, String returnAddress, BigInteger depositAmount, boolean addPadding) { - MoneroWallet wallet = getWallet(); - synchronized (wallet) { - - // add miner fee padding to deposit amount - if (addPadding) { - - // get estimated mining fee with deposit amount - MoneroTxWallet feeEstimateTx = wallet.createTx(new MoneroTxConfig() - .setAccountIndex(0) - .addDestination(HavenoUtils.getTradeFeeAddress(), tradeFee) - .addDestination(returnAddress, depositAmount)); - BigInteger feeEstimate = feeEstimateTx.getFee(); - - BigInteger daemonFeeEstimate = getFeeEstimate(feeEstimateTx.getWeight()); - log.info("createReserveTx() 1st feeEstimateTx with weight {} has fee {} versus daemon fee estimate of {} (diff={})", feeEstimateTx.getWeight(), feeEstimateTx.getFee(), daemonFeeEstimate, (feeEstimateTx.getFee().subtract(daemonFeeEstimate))); - - // get estimated mining fee with deposit amount + previous estimated mining fee for better accuracy - feeEstimateTx = wallet.createTx(new MoneroTxConfig() - .setAccountIndex(0) - .addDestination(HavenoUtils.getTradeFeeAddress(), tradeFee) - .addDestination(returnAddress, depositAmount.add(feeEstimate.multiply(BigInteger.valueOf(MINER_FEE_PADDING_MULTIPLIER))))); - feeEstimate = feeEstimateTx.getFee(); - - log.info("createReserveTx() 2nd feeEstimateTx with weight {} has fee {} versus daemon fee estimate of {} (diff={})", feeEstimateTx.getWeight(), feeEstimateTx.getFee(), daemonFeeEstimate, (feeEstimateTx.getFee().subtract(daemonFeeEstimate))); - - // add padding to deposit amount - BigInteger minerFeePadding = feeEstimate.multiply(BigInteger.valueOf(MINER_FEE_PADDING_MULTIPLIER)); - depositAmount = depositAmount.add(minerFeePadding); - } - - // create reserve tx - MoneroTxWallet reserveTx = wallet.createTx(new MoneroTxConfig() - .setAccountIndex(0) - .addDestination(HavenoUtils.getTradeFeeAddress(), tradeFee) - .addDestination(returnAddress, depositAmount)); - log.info("Reserve tx weight={}, fee={}, depositAmount={}", reserveTx.getWeight(), reserveTx.getFee(), depositAmount); - - // freeze inputs - for (MoneroOutput input : reserveTx.getInputs()) wallet.freezeOutput(input.getKeyImage().getHex()); - wallet.save(); - return reserveTx; - } + 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); } /** * Create the multisig deposit tx and freeze its inputs. * + * @param trade the trade to create a deposit tx from * @return MoneroTxWallet the multisig deposit tx */ public MoneroTxWallet createDepositTx(Trade trade) { - BigInteger tradeFee = ParsingUtils.coinToAtomicUnits(trade instanceof MakerTrade ? trade.getOffer().getMakerFee() : trade.getTakerFee()); Offer offer = trade.getProcessModel().getOffer(); - BigInteger depositAmount = ParsingUtils.coinToAtomicUnits(trade instanceof SellerTrade ? offer.getAmount().add(offer.getSellerSecurityDeposit()) : offer.getBuyerSecurityDeposit()); 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 securityDeposit = HavenoUtils.coinToAtomicUnits(trade instanceof BuyerTrade ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit()); + log.info("Creating deposit tx with fee={}, peerAmount={}, securityDeposit={}", tradeFee, peerAmount, securityDeposit); + return createTradeTx(tradeFee, peerAmount, securityDeposit, multisigAddress); + } + + private MoneroTxWallet createTradeTx(BigInteger tradeFee, BigInteger peerAmount, BigInteger securityDeposit, String address) { MoneroWallet wallet = getWallet(); synchronized (wallet) { - // create deposit tx - MoneroTxWallet depositTx = wallet.createTx(new MoneroTxConfig() - .setAccountIndex(0) - .addDestination(HavenoUtils.getTradeFeeAddress(), tradeFee) - .addDestination(multisigAddress, depositAmount)); + // binary search to maximize security deposit, thereby minimizing potential dust + 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); + for (int i = 0; i < 10; i++) { + try { + BigInteger amount = new BigDecimal(maxAmount).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE * appliedTolerance)).toBigInteger(); + tradeTx = wallet.createTx(new MoneroTxConfig() + .setAccountIndex(0) + .addDestination(HavenoUtils.getTradeFeeAddress(), tradeFee) + .addDestination(address, amount)); + appliedTolerance -= searchDiff; // apply less tolerance to increase security deposit + if (appliedTolerance < 0.0) break; // can send full security deposit + } catch (MoneroError e) { + appliedTolerance += searchDiff; // apply more tolerance to decrease security deposit + if (appliedTolerance > 1.0) throw e; // not enough money + } + searchDiff /= 2; + } - // freeze deposit inputs - for (MoneroOutput input : depositTx.getInputs()) wallet.freezeOutput(input.getKeyImage().getHex()); + // freeze inputs + for (MoneroOutput input : tradeTx.getInputs()) wallet.freezeOutput(input.getKeyImage().getHex()); wallet.save(); - return depositTx; + return tradeTx; } } /** - * Verify a reserve or deposit transaction used during trading. - * Checks double spends, deposit amount and destination, trade fee, and mining fee. - * The transaction is submitted but not relayed to the pool then flushed. + * Verify a reserve or deposit transaction. + * Checks double spends, trade fee, deposit amount and destination, and miner fee. + * The transaction is submitted to the pool then flushed without relaying. * - * @param depositAddress is the expected destination address for the deposit amount - * @param depositAmount is the expected amount deposited to multisig - * @param tradeFee is the expected fee for trading - * @param txHash is the transaction hash - * @param txHex is the transaction hex - * @param txKey is the transaction key - * @param keyImages are expected key images of inputs, ignored if null - * @param addPadding verifies depositAmount has additional padding to cover future mining fee + * @param tradeFee trade fee + * @param peerAmount amount to give peer + * @param securityDeposit security deposit amount + * @param address expected destination address for the deposit amount + * @param txHash transaction hash + * @param txHex transaction hex + * @param txKey transaction key + * @param keyImages expected key images of inputs, ignored if null */ - public void verifyTradeTx(String depositAddress, BigInteger depositAmount, BigInteger tradeFee, String txHash, String txHex, String txKey, List keyImages, boolean addPadding) { + public void verifyTradeTx(BigInteger tradeFee, BigInteger peerAmount, BigInteger securityDeposit, String address, String txHash, String txHex, String txKey, List keyImages) { MoneroDaemonRpc daemon = getDaemon(); MoneroWallet wallet = getWallet(); try { - log.info("Verifying trade tx with deposit amount={}", depositAmount); // verify tx not submitted to pool MoneroTx tx = daemon.getTx(txHash); @@ -375,14 +355,14 @@ public class XmrWalletService { if (!result.isGood()) throw new RuntimeException("Failed to submit tx to daemon: " + JsonUtils.serialize(result)); tx = getTx(txHash); - // verify reserved key images + // verify key images if (keyImages != null) { Set txKeyImages = new HashSet(); for (MoneroOutput input : tx.getInputs()) txKeyImages.add(input.getKeyImage().getHex()); - if (!txKeyImages.equals(new HashSet(keyImages))) throw new Error("Reserve tx's inputs do not match claimed key images"); + if (!txKeyImages.equals(new HashSet(keyImages))) throw new Error("Tx inputs do not match claimed key images"); } - // verify the unlock height + // verify unlock height if (tx.getUnlockHeight() != 0) throw new RuntimeException("Unlock height must be 0"); // verify trade fee @@ -391,22 +371,17 @@ public class XmrWalletService { if (!check.isGood()) throw new RuntimeException("Invalid proof of trade fee"); if (!check.getReceivedAmount().equals(tradeFee)) throw new RuntimeException("Trade fee is incorrect amount, expected " + tradeFee + " but was " + check.getReceivedAmount()); - // verify mining fee + // verify miner fee BigInteger feeEstimate = getFeeEstimate(tx.getWeight()); - double feeDiff = tx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); - if (feeDiff > MINER_FEE_TOLERANCE) throw new Error("Mining fee is not within " + (MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + tx.getFee()); + double feeDiff = tx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); // TODO: use BigDecimal? + if (feeDiff > MINER_FEE_TOLERANCE) throw new Error("Miner fee is not within " + (MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + tx.getFee()); log.info("Trade tx fee {} is within tolerance, diff%={}", tx.getFee(), feeDiff); // verify deposit amount - check = wallet.checkTxKey(txHash, txKey, depositAddress); + check = wallet.checkTxKey(txHash, txKey, address); if (!check.isGood()) throw new RuntimeException("Invalid proof of deposit amount"); - if (addPadding) { - BigInteger minPadding = BigInteger.valueOf((long) (tx.getFee().multiply(BigInteger.valueOf(MINER_FEE_PADDING_MULTIPLIER)).doubleValue() * (1.0 - MINER_FEE_TOLERANCE))); - BigInteger actualPadding = check.getReceivedAmount().subtract(depositAmount); - if (actualPadding.compareTo(minPadding) < 0) throw new RuntimeException("Deposit amount is not enough, needed " + depositAmount.add(minPadding) + " (with padding) but was " + check.getReceivedAmount()); - } else if (check.getReceivedAmount().compareTo(depositAmount) < 0) { - throw new RuntimeException("Deposit amount is not enough, needed " + depositAmount + " but was " + check.getReceivedAmount()); - } + BigInteger minAmount = new BigDecimal(peerAmount.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 { daemon.flushTxPool(txHash); // flush tx from pool @@ -915,7 +890,6 @@ public class XmrWalletService { return getBalanceForSubaddress(wallet.getAddressIndex(address).getIndex()); } - // TODO: Coin represents centineros everywhere, but here it's atomic units. reconcile public Coin getBalanceForSubaddress(int subaddressIndex) { // get subaddress balance @@ -931,12 +905,11 @@ public class XmrWalletService { // } System.out.println("Returning balance for subaddress " + subaddressIndex + ": " + balance.longValueExact()); - - return Coin.valueOf(balance.longValueExact()); + return HavenoUtils.atomicUnitsToCoin(balance); } public Coin getAvailableConfirmedBalance() { - return wallet != null ? Coin.valueOf(wallet.getUnlockedBalance(0).longValueExact()) : Coin.ZERO; + return wallet != null ? HavenoUtils.atomicUnitsToCoin(wallet.getUnlockedBalance(0)) : Coin.ZERO; } public Coin getSavingWalletBalance() { diff --git a/core/src/main/java/bisq/core/offer/OpenOfferManager.java b/core/src/main/java/bisq/core/offer/OpenOfferManager.java index 8a2dbbfa..1ed30796 100644 --- a/core/src/main/java/bisq/core/offer/OpenOfferManager.java +++ b/core/src/main/java/bisq/core/offer/OpenOfferManager.java @@ -42,7 +42,6 @@ import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.Preferences; import bisq.core.user.User; import bisq.core.util.JsonUtil; -import bisq.core.util.ParsingUtils; import bisq.core.util.Validator; import bisq.network.p2p.AckMessage; @@ -626,14 +625,15 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe ErrorMessageHandler errorMessageHandler) { new Thread(() -> { List errorMessages = new ArrayList(); - for (OpenOffer scheduledOffer : openOffers.getObservableList()) { + for (OpenOffer scheduledOffer : new ArrayList(openOffers.getObservableList())) { if (scheduledOffer.getState() != OpenOffer.State.SCHEDULED) continue; CountDownLatch latch = new CountDownLatch(1); processUnpostedOffer(scheduledOffer, (transaction) -> { latch.countDown(); }, errorMessage -> { - latch.countDown(); + onRemoved(scheduledOffer); errorMessages.add(errorMessage); + latch.countDown(); }); HavenoUtils.awaitLatch(latch); } @@ -655,7 +655,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe // get offer reserve amount Coin offerReserveAmountCoin = openOffer.getOffer().getReserveAmount(); - BigInteger offerReserveAmount = ParsingUtils.centinerosToAtomicUnits(offerReserveAmountCoin.value); + BigInteger offerReserveAmount = HavenoUtils.centinerosToAtomicUnits(offerReserveAmountCoin.value); // handle sufficient available balance if (xmrWalletService.getWallet().getUnlockedBalance(0).compareTo(offerReserveAmount) >= 0) { @@ -773,6 +773,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe // set offer state openOffer.setState(OpenOffer.State.AVAILABLE); + requestPersistence(); resultHandler.handleResult(transaction); if (!stopped) { @@ -832,17 +833,18 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe // verify maker's reserve tx (double spend, trade fee, trade amount, mining fee) Offer offer = new Offer(request.getOfferPayload()); - BigInteger tradeFee = ParsingUtils.coinToAtomicUnits(offer.getMakerFee()); - BigInteger depositAmount = ParsingUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getAmount().add(offer.getSellerSecurityDeposit())); + BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(offer.getMakerFee()); + BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount()); + BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit()); xmrWalletService.verifyTradeTx( - request.getPayoutAddress(), - depositAmount, tradeFee, + peerAmount, + securityDeposit, + request.getPayoutAddress(), request.getReserveTxHash(), request.getReserveTxHex(), request.getReserveTxKey(), - request.getReserveTxKeyImages(), - true); + request.getReserveTxKeyImages()); // arbitrator signs offer to certify they have valid reserve tx String offerPayloadAsJson = JsonUtil.objectToJson(request.getOfferPayload()); 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 a6ab4b4c..74285b5b 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 @@ -21,13 +21,17 @@ import bisq.common.taskrunner.Task; import bisq.common.taskrunner.TaskRunner; import bisq.core.btc.model.XmrAddressEntry; import bisq.core.offer.Offer; +import bisq.core.offer.OfferDirection; import bisq.core.offer.placeoffer.PlaceOfferModel; -import bisq.core.util.ParsingUtils; +import bisq.core.trade.HavenoUtils; import lombok.extern.slf4j.Slf4j; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; + +import org.bitcoinj.core.Coin; + import monero.daemon.model.MoneroOutput; import monero.wallet.model.MoneroTxWallet; @@ -46,19 +50,18 @@ public class MakerReserveOfferFunds extends Task { try { runInterceptHook(); - // create reserve tx with padding + // create reserve tx + BigInteger makerFee = HavenoUtils.coinToAtomicUnits(offer.getMakerFee()); + BigInteger peerAmount = 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(); - BigInteger makerFee = ParsingUtils.coinToAtomicUnits(offer.getMakerFee()); - BigInteger depositAmount = ParsingUtils.coinToAtomicUnits(model.getReservedFundsForOffer()); - log.info("Maker creating reserve tx with maker fee={} and depositAmount={}", makerFee, depositAmount); - MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(makerFee, returnAddress, depositAmount, true); + MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(makerFee, peerAmount, securityDeposit, returnAddress); - // collect reserved key images // TODO (woodser): switch to proof of reserve? + // collect reserved key images List reservedKeyImages = new ArrayList(); for (MoneroOutput input : reserveTx.getInputs()) reservedKeyImages.add(input.getKeyImage().getHex()); // save offer state - // TODO (woodser): persist model.setReserveTx(reserveTx); offer.getOfferPayload().setReserveTxKeyImages(reservedKeyImages); offer.setOfferFeePaymentTxId(reserveTx.getHash()); // TODO (woodser): don't use this field diff --git a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java index ac2880c5..b46239fc 100644 --- a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java +++ b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java @@ -68,7 +68,7 @@ import bisq.core.payment.UpiAccount; import bisq.core.payment.VerseAccount; import bisq.core.payment.WeChatPayAccount; import bisq.core.payment.WesternUnionAccount; -import bisq.core.util.ParsingUtils; +import bisq.core.trade.HavenoUtils; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.locale.TradeCurrency; @@ -490,8 +490,8 @@ public final class PaymentMethod implements PersistablePayload, Comparable MAX_FIAT_STAGENET_XMR) { - riskBasedTradeLimit = ParsingUtils.xmrToCentineros(MAX_FIAT_STAGENET_XMR); + if (isFiat && isStagenet && HavenoUtils.centinerosToXmr(riskBasedTradeLimit) > MAX_FIAT_STAGENET_XMR) { + riskBasedTradeLimit = HavenoUtils.xmrToCentineros(MAX_FIAT_STAGENET_XMR); } return Coin.valueOf(riskBasedTradeLimit); } diff --git a/core/src/main/java/bisq/core/support/dispute/DisputeManager.java b/core/src/main/java/bisq/core/support/dispute/DisputeManager.java index d9feae5f..5c689997 100644 --- a/core/src/main/java/bisq/core/support/dispute/DisputeManager.java +++ b/core/src/main/java/bisq/core/support/dispute/DisputeManager.java @@ -834,8 +834,8 @@ public abstract class DisputeManager> extends Sup (contract.isBuyerMakerAndSellerTaker() ? contract.getMakerPayoutAddressString() : contract.getTakerPayoutAddressString()) : (contract.isBuyerMakerAndSellerTaker() ? contract.getTakerPayoutAddressString() : contract.getMakerPayoutAddressString()); String loserPayoutAddress = winnerPayoutAddress.equals(contract.getMakerPayoutAddressString()) ? contract.getTakerPayoutAddressString() : contract.getMakerPayoutAddressString(); - BigInteger winnerPayoutAmount = ParsingUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getBuyerPayoutAmount() : disputeResult.getSellerPayoutAmount()); - BigInteger loserPayoutAmount = ParsingUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getSellerPayoutAmount() : disputeResult.getBuyerPayoutAmount()); + BigInteger winnerPayoutAmount = HavenoUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getBuyerPayoutAmount() : disputeResult.getSellerPayoutAmount()); + BigInteger loserPayoutAmount = HavenoUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getSellerPayoutAmount() : disputeResult.getBuyerPayoutAmount()); // create transaction to get fee estimate MoneroTxConfig txConfig = new MoneroTxConfig().setAccountIndex(0).setRelay(false); diff --git a/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java b/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java index c70bebe6..148f6e91 100644 --- a/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java +++ b/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java @@ -35,9 +35,9 @@ import bisq.core.support.messages.ChatMessage; import bisq.core.support.messages.SupportMessage; import bisq.core.trade.ClosedTradableManager; import bisq.core.trade.Contract; +import bisq.core.trade.HavenoUtils; import bisq.core.trade.Trade; import bisq.core.trade.TradeManager; -import bisq.core.util.ParsingUtils; import bisq.network.p2p.AckMessageSourceType; import bisq.network.p2p.NodeAddress; @@ -318,12 +318,12 @@ public final class ArbitrationManager extends DisputeManager reservedKeyImages = new ArrayList(); @@ -51,7 +56,7 @@ public class TakerReserveTradeFunds extends TradeTask { // save process state processModel.setReserveTx(reserveTx); processModel.getTaker().setReserveTxKeyImages(reservedKeyImages); - + processModel.getTradeManager().requestPersistence(); complete(); } catch (Throwable t) { trade.setErrorMessage("An error occurred.\n" + diff --git a/core/src/main/java/bisq/core/util/ParsingUtils.java b/core/src/main/java/bisq/core/util/ParsingUtils.java index 304f959e..6e30572c 100644 --- a/core/src/main/java/bisq/core/util/ParsingUtils.java +++ b/core/src/main/java/bisq/core/util/ParsingUtils.java @@ -9,58 +9,12 @@ import org.bitcoinj.core.Coin; import org.bitcoinj.utils.MonetaryFormat; import org.apache.commons.lang3.StringUtils; -import java.math.BigDecimal; -import java.math.BigInteger; import lombok.extern.slf4j.Slf4j; @Slf4j public class ParsingUtils { - // multipliers to convert units - private static BigInteger CENTINEROS_AU_MULTIPLIER = new BigInteger("10000"); - private static BigInteger XMR_AU_MULTIPLIER = new BigInteger("1000000000000"); - - public static BigInteger coinToAtomicUnits(Coin coin) { - return centinerosToAtomicUnits(coin.value); - } - - public static double coinToXmr(Coin coin) { - return atomicUnitsToXmr(coinToAtomicUnits(coin)); - } - - public static BigInteger centinerosToAtomicUnits(long centineros) { - return BigInteger.valueOf(centineros).multiply(ParsingUtils.CENTINEROS_AU_MULTIPLIER); - } - - public static double centinerosToXmr(long centineros) { - return atomicUnitsToXmr(centinerosToAtomicUnits(centineros)); - } - - public static long atomicUnitsToCentineros(long atomicUnits) { // TODO: atomic units should be BigInteger; remove this? - return atomicUnits / CENTINEROS_AU_MULTIPLIER.longValue(); - } - - public static long atomicUnitsToCentineros(BigInteger atomicUnits) { - return atomicUnits.divide(CENTINEROS_AU_MULTIPLIER).longValueExact(); - } - - public static Coin atomicUnitsToCoin(BigInteger atomicUnits) { - return Coin.valueOf(atomicUnitsToCentineros(atomicUnits)); - } - - public static double atomicUnitsToXmr(BigInteger atomicUnits) { - return new BigDecimal(atomicUnits).divide(new BigDecimal(XMR_AU_MULTIPLIER)).doubleValue(); - } - - public static BigInteger xmrToAtomicUnits(double xmr) { - return BigDecimal.valueOf(xmr).multiply(new BigDecimal(XMR_AU_MULTIPLIER)).toBigInteger(); - } - - public static long xmrToCentineros(double xmr) { - return atomicUnitsToCentineros(xmrToAtomicUnits(xmr)); - } - public static Coin parseToCoin(String input, CoinFormatter coinFormatter) { return parseToCoin(input, coinFormatter.getMonetaryFormat()); } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index d3f4bc13..9d6d1358 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -451,7 +451,7 @@ createOffer.fundsBox.offerFee=Trade fee createOffer.fundsBox.networkFee=Mining fee createOffer.fundsBox.placeOfferSpinnerInfo=Offer publishing is in progress ... createOffer.fundsBox.paymentLabel=Haveno trade with ID {0} -createOffer.fundsBox.fundsStructure=({0} security deposit, {1} trade fee, {2} mining fee) +createOffer.fundsBox.fundsStructure=({0} security deposit, {1} trade fee) createOffer.success.headline=Your offer has been published createOffer.success.info=You can manage your open offers at \"Portfolio/My open offers\". createOffer.info.sellAtMarketPrice=You will always sell at market price as the price of your offer will be continuously updated. @@ -477,13 +477,11 @@ createOffer.createOfferFundWalletInfo.headline=Fund your offer # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Trade amount: {0} \n createOffer.createOfferFundWalletInfo.msg=You need to deposit {0} to this offer.\n\n\ - Those funds are reserved in your local wallet and will get locked into the multisig deposit address once someone takes your offer.\n\n\ + Those funds are reserved in your local wallet and will get locked into the multisig wallet once someone takes your offer.\n\n\ The amount is the sum of:\n\ {1}\ - Your security deposit: {2}\n\ - - Trading fee: {3}\n\ - - Mining fee: {4}\n\n\ - You can choose between two options when funding your trade:\n- Use your Haveno wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. + - Trading fee: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=An error occurred when placing the offer:\n\n{0}\n\n\ @@ -533,7 +531,7 @@ takeOffer.fundsBox.offerFee=Trade fee takeOffer.fundsBox.networkFee=Total mining fees takeOffer.fundsBox.takeOfferSpinnerInfo=Take offer in progress ... takeOffer.fundsBox.paymentLabel=Haveno trade with ID {0} -takeOffer.fundsBox.fundsStructure=({0} security deposit, {1} trade fee, {2} mining fee) +takeOffer.fundsBox.fundsStructure=({0} security deposit, {1} trade fee) takeOffer.success.headline=You have successfully taken an offer. takeOffer.success.info=You can see the status of your trade at \"Portfolio/Open trades\". takeOffer.error.message=An error occurred when taking the offer.\n\n{0} @@ -545,7 +543,7 @@ takeOffer.noPriceFeedAvailable=You cannot take that offer as it uses a percentag takeOffer.takeOfferFundWalletInfo.headline=Fund your trade # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Trade amount: {0} \n -takeOffer.takeOfferFundWalletInfo.msg=You need to deposit {0} for taking this offer.\n\nThe amount is the sum of:\n{1}- Your security deposit: {2}\n- Trading fee: {3}\n- Total mining fees: {4}\n\nYou can choose between two options when funding your trade:\n- Use your Haveno wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. +takeOffer.takeOfferFundWalletInfo.msg=You need to deposit {0} for taking this offer.\n\nThe amount is the sum of:\n{1}- Your security deposit: {2}\n- Trading fee: {3} takeOffer.alreadyPaidInFunds=If you have already paid in funds you can withdraw it in the \"Funds/Send funds\" screen. takeOffer.setAmountPrice=Set amount takeOffer.alreadyFunded.askCancel=You have already funded that offer.\nIf you cancel now, your funds will be moved to your local Haveno wallet and are available for withdrawal in the \"Funds/Send funds\" screen.\nAre you sure you want to cancel? @@ -2242,9 +2240,6 @@ systemTray.tooltip=Haveno: A decentralized bitcoin exchange network # GUI Util #################################################################### -guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is \ -at least {0} satoshis/vbyte. Otherwise, the trade transactions may not be confirmed in time and the trade will end up in a dispute. - guiUtil.accountExport.savedToPath=Trading accounts saved to path:\n{0} guiUtil.accountExport.noAccountSetup=You don't have trading accounts set up for exporting. guiUtil.accountExport.selectPath=Select path to {0} diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 9dc05992..dcd0dcd3 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -1710,8 +1710,6 @@ systemTray.tooltip=Bisq: Decentralizovaná směnárna bitcoinů # GUI Util #################################################################### -guiUtil.miningFeeInfo=Ujistěte se, že poplatek za těžbu používaný vaší externí peněženkou je alespoň {0} satoshi/vbyte. Jinak nemusí být obchodní transakce potvrzeny včas a obchod skončí sporem. - guiUtil.accountExport.savedToPath=Obchodní účty uložené na:\n{0} guiUtil.accountExport.noAccountSetup=Nemáte nastaveny obchodní účty pro export. guiUtil.accountExport.selectPath=Vyberte cestu k {0} diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index b6a45435..dc7b9a52 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -1710,8 +1710,6 @@ systemTray.tooltip=Bisq: Ein dezentrales Bitcoin-Tauschbörsen-Netzwerk # GUI Util #################################################################### -guiUtil.miningFeeInfo=Bitte stellen Sie sicher, dass die Mining-Gebühr für Ihre externe Wallet mindestens {0} satoshis/byte ist. Ansonsten wird die Transaktion des Trades nicht rechtzeitig bestätigt und der Trade wird in einem Konflikt enden. - guiUtil.accountExport.savedToPath=Handelskonten in Verzeichnis gespeichert:\n{0} guiUtil.accountExport.noAccountSetup=Sie haben kein Handelskonto zum Exportieren eingerichtet. guiUtil.accountExport.selectPath=Verzeichnis auswählen zum {0} diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index 3108a1ee..a328c465 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -1710,8 +1710,6 @@ systemTray.tooltip=Bisq: Una red de intercambio de bitcoin descentralizada # GUI Util #################################################################### -guiUtil.miningFeeInfo=Por favor asegúrese de que la comisión de minado usada en su monedero externo es de al menos {0} sat/vbyte. De lo contrario, las transacciones de intercambio podrían no confirmarse y el intercambio acabaría en disputa. - guiUtil.accountExport.savedToPath=Las cuentas de intercambio se han guardado en el directorio:\n{0} guiUtil.accountExport.noAccountSetup=No tiene cuentas de intercambio configuradas para exportar. guiUtil.accountExport.selectPath=Seleccionar directorio a {0} diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 7cf2ee73..35faec87 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -1710,8 +1710,6 @@ systemTray.tooltip=Bisq: A decentralized bitcoin exchange network # GUI Util #################################################################### -guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/vbyte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. - guiUtil.accountExport.savedToPath=حساب های معاملاتی در مسیر ذیل ذخیره شد:\n{0} guiUtil.accountExport.noAccountSetup=شما حساب های معاملاتی برای صادرات ندارید. guiUtil.accountExport.selectPath=انتخاب مسیر به {0} diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index c6b1d1b7..b4d69e05 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -1711,8 +1711,6 @@ systemTray.tooltip=Bisq: Une plateforme d''échange décentralisée sur le rése # GUI Util #################################################################### -guiUtil.miningFeeInfo=Veuillez vous assurer que les frais de minage utilisés par votre portefeuille externe sont d'au moins {0} satoshis/vbyte. Le cas échéant les transactions de trade pourraient ne peut être confirmée à temps et le trade aboutirait à une dispute. - guiUtil.accountExport.savedToPath=Les comptes de trading sont sauvegardés vers l''arborescence:\n{0} guiUtil.accountExport.noAccountSetup=Vous n'avez pas de comptes de trading configurés pour exportation. guiUtil.accountExport.selectPath=Sélectionner l''arborescence vers {0} diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 55a62063..a3a2402c 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -1710,8 +1710,6 @@ systemTray.tooltip=Bisq: una rete di scambio decentralizzata di bitcoin # GUI Util #################################################################### -guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/vbyte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. - guiUtil.accountExport.savedToPath=Account di trading salvati nel percorso:\n{0} guiUtil.accountExport.noAccountSetup=Non hai account di trading impostati per l'esportazione. guiUtil.accountExport.selectPath=Seleziona il percorso per {0} diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 98539d42..db714966 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -1710,8 +1710,6 @@ systemTray.tooltip=Bisq: 分散的ビットコイン取引ネットワーク # GUI Util #################################################################### -guiUtil.miningFeeInfo=外部ウォレットで使用されるマイニング手数料が少なくとも{0} satoshis/vbyte あることを確認してください。 そうでなければ、このトレードトランザクションは承認されない可能性、トレードは係争に終わる可能性があります。 - guiUtil.accountExport.savedToPath=取引アカウントを下記パスに保存しました:\n{0} guiUtil.accountExport.noAccountSetup=取引アカウントのエクスポート設定がされていません guiUtil.accountExport.selectPath={0}のパスを選択 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 168af59d..eb8be63a 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -1718,8 +1718,6 @@ systemTray.tooltip=Bisq: a rede de exchange decentralizada de bitcoin # GUI Util #################################################################### -guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/vbyte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. - guiUtil.accountExport.savedToPath=Contas de negociação salvas na pasta:\n{0} guiUtil.accountExport.noAccountSetup=Você não tem contas de negociação para exportar. guiUtil.accountExport.selectPath=Selecione pasta de {0} diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 0b519015..eb746b1f 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -1708,8 +1708,6 @@ systemTray.tooltip=Bisq: Uma rede de echange de bitcoin descentralizada # GUI Util #################################################################### -guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/vbyte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. - guiUtil.accountExport.savedToPath=Contas de negociação guardadas em:\n{0} guiUtil.accountExport.noAccountSetup=Você não tem contas de negociação prontas para exportar. guiUtil.accountExport.selectPath=Selecione diretório de {0} diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 29df20d1..adc63c03 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -1710,8 +1710,6 @@ systemTray.tooltip=Bisq: A decentralized bitcoin exchange network # GUI Util #################################################################### -guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/vbyte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. - guiUtil.accountExport.savedToPath=Торговые счета в:\n{0} guiUtil.accountExport.noAccountSetup=У вас нет торговых счетов для экспортирования. guiUtil.accountExport.selectPath=Выбрать путь к {0} diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 133d68bb..2ff544a5 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -1710,8 +1710,6 @@ systemTray.tooltip=Bisq: A decentralized bitcoin exchange network # GUI Util #################################################################### -guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/vbyte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. - guiUtil.accountExport.savedToPath=บัญชีการค้าที่บันทึกไว้ในเส้นทาง: \n{0} guiUtil.accountExport.noAccountSetup=คุณไม่มีบัญชีการซื้อขายที่ตั้งค่าไว้สำหรับการส่งออก guiUtil.accountExport.selectPath=เลือกเส้นทางไปที่ {0} diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 4fc7da57..b7ed1219 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -1712,8 +1712,6 @@ systemTray.tooltip=Bisq: A decentralized bitcoin exchange network # GUI Util #################################################################### -guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/vbyte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute. - guiUtil.accountExport.savedToPath=tài khoản giao dịch được lưu vào đường dẫn:\n{0} guiUtil.accountExport.noAccountSetup=Bạn không có tài khoản giao dịch được thiết lập để truy xuất. guiUtil.accountExport.selectPath=Chọn đường dẫn đến {0} diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 5d32e1b6..a3651b6b 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -1714,8 +1714,6 @@ systemTray.tooltip=Bisq:去中心化比特币交易网络 # GUI Util #################################################################### -guiUtil.miningFeeInfo=请确保您的外部钱包使用的矿工手续费费用足够高至少为 {0} 聪/字节,以便矿工接受资金交易。\n否则交易所交易无法确认,交易最终将会出现纠纷。 - guiUtil.accountExport.savedToPath=交易账户保存在路径:\n{0} guiUtil.accountExport.noAccountSetup=您没有交易账户设置导出。 guiUtil.accountExport.selectPath=选择路径 {0} diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 84194542..93e79155 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -1710,8 +1710,6 @@ systemTray.tooltip=Bisq:去中心化比特幣交易網絡 # GUI Util #################################################################### -guiUtil.miningFeeInfo=請確保您的外部錢包使用的礦工手續費費用足夠高至少為 {0} 聰/字節,以便礦工接受資金交易。\n否則交易所交易無法確認,交易最終將會出現糾紛。 - guiUtil.accountExport.savedToPath=交易賬户保存在路徑:\n{0} guiUtil.accountExport.noAccountSetup=您沒有交易賬户設置導出。 guiUtil.accountExport.selectPath=選擇路徑 {0} diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcDisputesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcDisputesService.java index deaca505..bca73e03 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcDisputesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcDisputesService.java @@ -3,7 +3,7 @@ package bisq.daemon.grpc; import bisq.core.api.CoreApi; import bisq.core.support.dispute.Attachment; import bisq.core.support.dispute.DisputeResult; -import bisq.core.util.ParsingUtils; +import bisq.core.trade.HavenoUtils; import bisq.common.proto.ProtoUtil; @@ -109,7 +109,7 @@ public class GrpcDisputesService extends DisputesImplBase { var winner = ProtoUtil.enumFromProto(DisputeResult.Winner.class, req.getWinner().name()); var reason = ProtoUtil.enumFromProto(DisputeResult.Reason.class, req.getReason().name()); // scale atomic unit to centineros for consistency TODO switch base to atomic units? - var customPayoutAmount = ParsingUtils.atomicUnitsToCentineros(req.getCustomPayoutAmount()); + var customPayoutAmount = HavenoUtils.atomicUnitsToCentineros(req.getCustomPayoutAmount()); coreApi.resolveDispute(req.getTradeId(), winner, reason, req.getSummaryNotes(), customPayoutAmount); var reply = ResolveDisputeReply.newBuilder().build(); responseObserver.onNext(reply); diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java index 7f759980..7773c1b2 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java @@ -21,7 +21,7 @@ import bisq.core.api.CoreApi; import bisq.core.api.model.OfferInfo; import bisq.core.offer.Offer; import bisq.core.offer.OpenOffer; -import bisq.core.util.ParsingUtils; +import bisq.core.trade.HavenoUtils; import bisq.proto.grpc.CancelOfferReply; import bisq.proto.grpc.CancelOfferRequest; import bisq.proto.grpc.PostOfferReply; @@ -154,8 +154,8 @@ class GrpcOffersService extends OffersImplBase { req.getPrice(), req.getUseMarketBasedPrice(), req.getMarketPriceMarginPct(), - ParsingUtils.atomicUnitsToCentineros(req.getAmount()), // scale atomic unit to centineros for consistency TODO switch base to atomic units? - ParsingUtils.atomicUnitsToCentineros(req.getMinAmount()), + HavenoUtils.atomicUnitsToCentineros(req.getAmount()), // scale atomic unit to centineros for consistency TODO switch base to atomic units? + HavenoUtils.atomicUnitsToCentineros(req.getMinAmount()), req.getBuyerSecurityDepositPct(), req.getTriggerPrice(), req.getPaymentAccountId(), diff --git a/desktop/src/main/java/bisq/desktop/components/AddressTextField.java b/desktop/src/main/java/bisq/desktop/components/AddressTextField.java index 5ee5d952..4ccf984f 100644 --- a/desktop/src/main/java/bisq/desktop/components/AddressTextField.java +++ b/desktop/src/main/java/bisq/desktop/components/AddressTextField.java @@ -71,8 +71,7 @@ public class AddressTextField extends AnchorPane { textField.setOnMousePressed(event -> wasPrimaryButtonDown = event.isPrimaryButtonDown()); textField.setOnMouseReleased(event -> { - if (wasPrimaryButtonDown) - GUIUtil.showFeeInfoBeforeExecute(AddressTextField.this::openWallet); + if (wasPrimaryButtonDown) openWallet(); wasPrimaryButtonDown = false; }); @@ -83,17 +82,17 @@ public class AddressTextField extends AnchorPane { extWalletIcon.getStyleClass().addAll("icon", "highlight"); extWalletIcon.setTooltip(new Tooltip(tooltipText)); AwesomeDude.setIcon(extWalletIcon, AwesomeIcon.SIGNIN); - extWalletIcon.setOnMouseClicked(e -> GUIUtil.showFeeInfoBeforeExecute(this::openWallet)); + extWalletIcon.setOnMouseClicked(e -> openWallet()); Label copyIcon = new Label(); copyIcon.setLayoutY(3); copyIcon.getStyleClass().addAll("icon", "highlight"); Tooltip.install(copyIcon, new Tooltip(Res.get("addressTextField.copyToClipboard"))); AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); - copyIcon.setOnMouseClicked(e -> GUIUtil.showFeeInfoBeforeExecute(() -> { + copyIcon.setOnMouseClicked(e -> { if (address.get() != null && address.get().length() > 0) Utilities.copyToClipboard(address.get()); - })); + }); AnchorPane.setRightAnchor(copyIcon, 30.0); AnchorPane.setRightAnchor(extWalletIcon, 5.0); diff --git a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java index 96757b0a..b25bb23a 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java @@ -21,7 +21,7 @@ import bisq.core.btc.listeners.XmrBalanceListener; import bisq.core.btc.model.XmrAddressEntry; import bisq.core.btc.wallet.XmrWalletService; import bisq.core.locale.Res; -import bisq.core.util.ParsingUtils; +import bisq.core.trade.HavenoUtils; import bisq.core.util.coin.CoinFormatter; import bisq.desktop.components.indicator.TxConfidenceIndicator; import bisq.desktop.util.GUIUtil; @@ -66,15 +66,14 @@ class DepositListItem { balanceListener = new XmrBalanceListener(addressEntry.getSubaddressIndex()) { @Override public void onBalanceChanged(BigInteger balance) { - DepositListItem.this.balanceAsCoin = ParsingUtils.atomicUnitsToCoin(balance); + DepositListItem.this.balanceAsCoin = HavenoUtils.atomicUnitsToCoin(balance); DepositListItem.this.balance.set(formatter.formatCoin(balanceAsCoin)); updateUsage(addressEntry.getSubaddressIndex()); } }; xmrWalletService.addBalanceListener(balanceListener); - balanceAsCoin = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex()); // TODO: Coin represents centineros everywhere, but here it's atomic units. reconcile - balanceAsCoin = Coin.valueOf(ParsingUtils.atomicUnitsToCentineros(balanceAsCoin.longValue())); // in centineros + balanceAsCoin = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex()); balance.set(formatter.formatCoin(balanceAsCoin)); updateUsage(addressEntry.getSubaddressIndex()); diff --git a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java index 9024bf6c..c5cf3fa8 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java @@ -34,6 +34,7 @@ import bisq.core.btc.listeners.XmrBalanceListener; import bisq.core.btc.model.XmrAddressEntry; import bisq.core.btc.wallet.XmrWalletService; import bisq.core.locale.Res; +import bisq.core.trade.HavenoUtils; import bisq.core.user.Preferences; import bisq.core.util.FormattingUtils; import bisq.core.util.ParsingUtils; @@ -166,10 +167,9 @@ public class DepositView extends ActivatableView { qrCodeImageView = new ImageView(); qrCodeImageView.getStyleClass().add("qr-code"); Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow"))); - qrCodeImageView.setOnMouseClicked(e -> GUIUtil.showFeeInfoBeforeExecute( - () -> UserThread.runAfter( + qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter( () -> new QRCodeWindow(getPaymentUri()).show(), - 200, TimeUnit.MILLISECONDS))); + 200, TimeUnit.MILLISECONDS)); GridPane.setRowIndex(qrCodeImageView, gridRow); GridPane.setRowSpan(qrCodeImageView, 4); GridPane.setColumnIndex(qrCodeImageView, 1); @@ -308,7 +308,7 @@ public class DepositView extends ActivatableView { private String getPaymentUri() { return xmrWalletService.getWallet().getPaymentUri(new MoneroTxConfig() .setAddress(addressTextField.getAddress()) - .setAmount(ParsingUtils.coinToAtomicUnits(getAmountAsCoin())) + .setAmount(HavenoUtils.coinToAtomicUnits(getAmountAsCoin())) .setNote(paymentLabelString)); } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java index 3d1f53f3..683d5a0a 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java @@ -21,9 +21,9 @@ import bisq.core.btc.wallet.XmrWalletService; import bisq.core.locale.Res; import bisq.core.offer.Offer; import bisq.core.offer.OpenOffer; +import bisq.core.trade.HavenoUtils; import bisq.core.trade.Tradable; import bisq.core.trade.Trade; -import bisq.core.util.ParsingUtils; import bisq.core.util.coin.CoinFormatter; import bisq.desktop.components.indicator.TxConfidenceIndicator; import bisq.desktop.util.DisplayUtils; @@ -94,8 +94,8 @@ class TransactionsListItem { Optional optionalTradable = Optional.ofNullable(transactionAwareTradable) .map(TransactionAwareTradable::asTradable); - Coin valueSentToMe = ParsingUtils.atomicUnitsToCoin(tx.getIncomingAmount() == null ? new BigInteger("0") : tx.getIncomingAmount()); - Coin valueSentFromMe = ParsingUtils.atomicUnitsToCoin(tx.getOutgoingAmount() == null ? new BigInteger("0") : tx.getOutgoingAmount()); + Coin valueSentToMe = HavenoUtils.atomicUnitsToCoin(tx.getIncomingAmount() == null ? new BigInteger("0") : tx.getIncomingAmount()); + Coin valueSentFromMe = HavenoUtils.atomicUnitsToCoin(tx.getOutgoingAmount() == null ? new BigInteger("0") : tx.getOutgoingAmount()); if (tx.getTransfers().get(0).isIncoming()) { addressString = ((MoneroIncomingTransfer) tx.getTransfers().get(0)).getAddress(); diff --git a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java index b37d3958..a945b833 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java @@ -72,8 +72,7 @@ class WithdrawalListItem { } private void updateBalance() { - balance = walletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex()); // TODO: Coin represents centineros everywhere, but here it's atomic units. reconcile - balance = Coin.valueOf(ParsingUtils.atomicUnitsToCentineros(balance.longValue())); // in centineros + balance = walletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex()); if (balance != null) balanceLabel.setText(formatter.formatCoin(this.balance)); } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java index 29fe323f..5cd693ba 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java @@ -32,6 +32,7 @@ import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.XmrWalletService; import bisq.core.locale.Res; import bisq.core.provider.fee.FeeService; +import bisq.core.trade.HavenoUtils; import bisq.core.trade.Trade; import bisq.core.trade.TradeManager; import bisq.core.user.DontShowAgainLookup; @@ -191,12 +192,12 @@ public class WithdrawalView extends ActivatableView { // create tx MoneroTxWallet tx = xmrWalletService.getWallet().createTx(new MoneroTxConfig() .setAccountIndex(0) - .setAmount(ParsingUtils.coinToAtomicUnits(receiverAmount)) // TODO: rename to centinerosToAtomicUnits()? + .setAmount(HavenoUtils.coinToAtomicUnits(receiverAmount)) // TODO: rename to centinerosToAtomicUnits()? .setAddress(withdrawToAddress) .setNote(withdrawMemoTextField.getText())); // create confirmation message - Coin fee = ParsingUtils.atomicUnitsToCoin(tx.getFee()); + Coin fee = HavenoUtils.atomicUnitsToCoin(tx.getFee()); Coin sendersAmount = receiverAmount.add(fee); String messageText = Res.get("shared.sendFundsDetailsWithFee", formatter.formatCoinWithCode(sendersAmount), 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 8c3750c6..2d1fa405 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java @@ -260,10 +260,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel { priceFeedService.setCurrencyCode(tradeCurrencyCode.get()); - // Set the default values (in rare cases if the fee request was not done yet we get the hard coded default values) - // But offer creation happens usually after that so we should have already the value from the estimation service. - txFeeFromFeeService = feeService.getTxFee(feeTxVsize); - calculateVolume(); calculateTotalToPay(); updateBalance(); @@ -557,8 +553,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { // The mining fee for the createOfferFee tx is deducted from the createOfferFee and not visible to the trader final Coin makerFee = getMakerFee(); if (direction != null && amount.get() != null && makerFee != null) { - Coin feeAndSecDeposit = getTxFee().add(getSecurityDeposit()); - feeAndSecDeposit = feeAndSecDeposit.add(makerFee); + Coin feeAndSecDeposit = getSecurityDeposit().add(makerFee); Coin total = isBuyOffer() ? feeAndSecDeposit : feeAndSecDeposit.add(amount.get()); totalToPayAsCoin.set(total); updateBalance(); @@ -569,10 +564,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel { return isBuyOffer() ? getBuyerSecurityDepositAsCoin() : getSellerSecurityDepositAsCoin(); } - public Coin getTxFee() { - return txFeeFromFeeService; - } - void swapTradeToSavings() { xmrWalletService.resetAddressEntriesForOpenOffer(offerId); } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java index 8c0e7ab6..0599c10e 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java @@ -22,7 +22,6 @@ import bisq.desktop.common.view.ActivatableViewAndModel; import bisq.desktop.components.AddressTextField; import bisq.desktop.components.AutoTooltipButton; import bisq.desktop.components.AutoTooltipLabel; -import bisq.desktop.components.AutoTooltipSlideToggleButton; import bisq.desktop.components.BalanceTextField; import bisq.desktop.components.BusyAnimation; import bisq.desktop.components.FundsTextField; @@ -32,10 +31,6 @@ import bisq.desktop.components.TitledGroupBg; import bisq.desktop.main.MainView; import bisq.desktop.main.account.AccountView; import bisq.desktop.main.account.content.fiataccounts.FiatAccountsView; -import bisq.desktop.main.offer.ClosableView; -import bisq.desktop.main.offer.OfferView; -import bisq.desktop.main.offer.OfferViewUtil; -import bisq.desktop.main.offer.SelectableView; import bisq.desktop.main.overlays.notifications.Notification; import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.overlays.windows.OfferDetailsWindow; @@ -391,8 +386,7 @@ public abstract class MutableOfferView> exten model.getTotalToPayInfo(), tradeAmountText, model.getSecurityDepositInfo(), - model.getTradeFee(), - model.getTxFee() + model.getTradeFee() ); new Popup().headLine(Res.get("createOffer.createOfferFundWalletInfo.headline")) .instruction(message) @@ -770,7 +764,7 @@ public abstract class MutableOfferView> exten missingCoinListener = (observable, oldValue, newValue) -> { if (!newValue.toString().equals("")) { final byte[] imageBytes = QRCode - .from(getBitcoinURI()) + .from(getMoneroURI()) .withSize(98, 98) // code has 41 elements 8 px is border with 98 we get double scale and min. border .to(ImageType.PNG) .stream() @@ -1080,10 +1074,9 @@ public abstract class MutableOfferView> exten qrCodeImageView.setFitWidth(150); qrCodeImageView.getStyleClass().add("qr-code"); Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow"))); - qrCodeImageView.setOnMouseClicked(e -> GUIUtil.showFeeInfoBeforeExecute( - () -> UserThread.runAfter( - () -> new QRCodeWindow(getBitcoinURI()).show(), - 200, TimeUnit.MILLISECONDS))); + qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter( + () -> new QRCodeWindow(getMoneroURI()).show(), + 200, TimeUnit.MILLISECONDS)); GridPane.setRowIndex(qrCodeImageView, gridRow); GridPane.setColumnIndex(qrCodeImageView, 1); GridPane.setRowSpan(qrCodeImageView, 3); @@ -1111,7 +1104,7 @@ public abstract class MutableOfferView> exten label.setPadding(new Insets(5, 0, 0, 0)); Button fundFromExternalWalletButton = new AutoTooltipButton(Res.get("shared.fundFromExternalWalletButton")); fundFromExternalWalletButton.setDefaultButton(false); - fundFromExternalWalletButton.setOnAction(e -> GUIUtil.showFeeInfoBeforeExecute(this::openWallet)); + fundFromExternalWalletButton.setOnAction(e -> openWallet()); waitingForFundsSpinner = new BusyAnimation(false); waitingForFundsLabel = new AutoTooltipLabel(); waitingForFundsLabel.setPadding(new Insets(5, 0, 0, 0)); @@ -1178,7 +1171,7 @@ public abstract class MutableOfferView> exten private void openWallet() { try { - Utilities.openURI(URI.create(getBitcoinURI())); + Utilities.openURI(URI.create(getMoneroURI())); } catch (Exception ex) { log.warn(ex.getMessage()); new Popup().warning(Res.get("shared.openDefaultWalletFailed")).show(); @@ -1186,10 +1179,12 @@ public abstract class MutableOfferView> exten } @NotNull - private String getBitcoinURI() { - return "TODO"; // TODO (woodser): wallet.createPaymentUri(); -// return GUIUtil.getBitcoinURI(addressTextField.getAddress(), model.getDataModel().getMissingCoin().get(), -// model.getPaymentLabel()); + private String getMoneroURI() { + return GUIUtil.getMoneroURI( + addressTextField.getAddress(), + model.getDataModel().getMissingCoin().get(), + model.getPaymentLabel(), + model.dataModel.getXmrWalletService().getWallet()); } private void addAmountPriceFields() { @@ -1274,6 +1269,9 @@ public abstract class MutableOfferView> exten firstRowHBox.getChildren().add(2, fixedPriceBox); if (!secondRowHBox.getChildren().contains(percentagePriceBox)) secondRowHBox.getChildren().add(2, percentagePriceBox); + + model.triggerPrice.set(""); + model.onTriggerPriceTextFieldChanged(); } else { firstRowHBox.getChildren().remove(fixedPriceBox); secondRowHBox.getChildren().remove(percentagePriceBox); @@ -1394,7 +1392,6 @@ public abstract class MutableOfferView> exten addPayInfoEntry(infoGridPane, i++, Res.getWithCol("shared.yourSecurityDeposit"), model.getSecurityDepositInfo()); addPayInfoEntry(infoGridPane, i++, Res.getWithCol("createOffer.fundsBox.offerFee"), model.getTradeFee()); - addPayInfoEntry(infoGridPane, i++, Res.getWithCol("createOffer.fundsBox.networkFee"), model.getTxFee()); Separator separator = new Separator(); separator.setOrientation(Orientation.HORIZONTAL); separator.getStyleClass().add("offer-separator"); 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 ce48e9f3..5ee08b1a 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java @@ -648,6 +648,7 @@ public abstract class MutableOfferViewModel ext } void onShowPayFundsScreen(Runnable actionHandler) { + actionHandler.run(); showPayFundsScreenDisplayed.set(true); updateSpinnerInfo(); } @@ -1015,24 +1016,10 @@ public abstract class MutableOfferViewModel ext public String getFundsStructure() { String fundsStructure; fundsStructure = Res.get("createOffer.fundsBox.fundsStructure", - getSecurityDepositWithCode(), getMakerFeePercentage(), getTxFeePercentage()); + getSecurityDepositWithCode(), getMakerFeePercentage()); return fundsStructure; } - public String getTxFee() { - return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil, - dataModel.getTxFee(), - dataModel.getAmount().get(), - btcFormatter, - Coin.ZERO - ); - } - - public String getTxFeePercentage() { - Coin txFeeAsCoin = dataModel.getTxFee(); - return GUIUtil.getPercentage(txFeeAsCoin, dataModel.getAmount().get()); - } - public PaymentAccount getPaymentAccount() { return dataModel.getPaymentAccount(); } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/OfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/OfferDataModel.java index f1f471bb..459f48e7 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/OfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/OfferDataModel.java @@ -41,6 +41,7 @@ import static bisq.core.util.coin.CoinUtil.minCoin; * needed in that UI element. */ public abstract class OfferDataModel extends ActivatableDataModel { + @Getter protected final XmrWalletService xmrWalletService; protected final OfferUtil offerUtil; 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 e8722ad0..b5014277 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 @@ -454,12 +454,11 @@ class TakeOfferDataModel extends OfferDataModel { // The mining fee for the takeOfferFee tx is deducted from the createOfferFee and not visible to the trader final Coin takerFee = getTakerFee(); if (offer != null && amount.get() != null && takerFee != null) { - Coin feeAndSecDeposit = getTotalTxFee().add(securityDeposit).add(takerFee); + Coin feeAndSecDeposit = securityDeposit.add(takerFee); if (isBuyOffer()) totalToPayAsCoin.set(feeAndSecDeposit.add(amount.get())); else totalToPayAsCoin.set(feeAndSecDeposit); - updateBalance(); log.debug("totalToPayAsCoin {}", totalToPayAsCoin.get().toFriendlyString()); } @@ -556,10 +555,6 @@ class TakeOfferDataModel extends OfferDataModel { return CurrencyUtil.getNameByCode(offer.getCurrencyCode()); } - public Coin getTotalTxFee() { - return txFeeFromFeeService.add(getTxFeeForDepositTx()).add(getTxFeeForPayoutTx()); - } - @NotNull private Coin getFundsNeededForTrade() { return getSecurityDeposit().add(getTxFeeForDepositTx()).add(getTxFeeForPayoutTx()); 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 172348a1..d20236b9 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 @@ -23,7 +23,6 @@ import bisq.desktop.common.view.FxmlView; import bisq.desktop.components.AddressTextField; import bisq.desktop.components.AutoTooltipButton; import bisq.desktop.components.AutoTooltipLabel; -import bisq.desktop.components.AutoTooltipSlideToggleButton; import bisq.desktop.components.BalanceTextField; import bisq.desktop.components.BusyAnimation; import bisq.desktop.components.FundsTextField; @@ -88,7 +87,6 @@ import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; @@ -455,8 +453,7 @@ public class TakeOfferView extends ActivatableViewAndModel GUIUtil.showFeeInfoBeforeExecute( - () -> UserThread.runAfter( - () -> new QRCodeWindow(getBitcoinURI()).show(), - 200, TimeUnit.MILLISECONDS))); + qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter( + () -> new QRCodeWindow(getMoneroURI()).show(), + 200, TimeUnit.MILLISECONDS)); GridPane.setRowIndex(qrCodeImageView, gridRow); GridPane.setColumnIndex(qrCodeImageView, 1); GridPane.setRowSpan(qrCodeImageView, 3); @@ -890,7 +886,7 @@ public class TakeOfferView extends ActivatableViewAndModel GUIUtil.showFeeInfoBeforeExecute(this::openWallet)); + fundFromExternalWalletButton.setOnAction(e -> openWallet()); waitingForFundsBusyAnimation = new BusyAnimation(false); waitingForFundsLabel = new AutoTooltipLabel(); waitingForFundsLabel.setPadding(new Insets(5, 0, 0, 0)); @@ -955,7 +951,7 @@ public class TakeOfferView extends ActivatableViewAndModel im return totalToPay; } - public String getTxFee() { - return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil, - dataModel.getTotalTxFee(), - dataModel.getAmount().get(), - btcFormatter, - Coin.ZERO - ); - } - - public String getTxFeePercentage() { - Coin txFeeAsCoin = dataModel.getTotalTxFee(); - return GUIUtil.getPercentage(txFeeAsCoin, dataModel.getAmount().get()); - } - ObservableList getPossiblePaymentAccounts() { return dataModel.getPossiblePaymentAccounts(); } diff --git a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java index bd510cd9..181415ab 100644 --- a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java @@ -42,11 +42,13 @@ import bisq.core.payment.PaymentAccount; import bisq.core.payment.PaymentAccountList; import bisq.core.payment.payload.PaymentMethod; import bisq.core.provider.fee.FeeService; +import bisq.core.trade.HavenoUtils; import bisq.core.trade.txproof.AssetTxProofResult; import bisq.core.user.DontShowAgainLookup; import bisq.core.user.Preferences; import bisq.core.user.User; import bisq.core.util.FormattingUtils; +import bisq.core.util.ParsingUtils; import bisq.core.util.coin.CoinFormatter; import bisq.network.p2p.P2PService; @@ -135,6 +137,9 @@ import java.util.function.Consumer; import lombok.extern.slf4j.Slf4j; import monero.daemon.model.MoneroTx; +import monero.wallet.MoneroWallet; +import monero.wallet.model.MoneroTxConfig; + import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; @@ -190,19 +195,6 @@ public class GUIUtil { }); } - public static void showFeeInfoBeforeExecute(Runnable runnable) { - String key = "miningFeeInfo"; - if (!DevEnv.isDevMode() && DontShowAgainLookup.showAgain(key)) { - new Popup().attention(Res.get("guiUtil.miningFeeInfo", String.valueOf(GUIUtil.feeService.getTxFeePerVbyte().value))) - .onClose(runnable) - .useIUnderstandButton() - .show(); - DontShowAgainLookup.dontShowAgain(key, true); - } else { - runnable.run(); - } - } - public static void exportAccounts(ArrayList accounts, String fileName, Preferences preferences, @@ -715,6 +707,13 @@ public class GUIUtil { .show(); } + public static String getMoneroURI(String address, Coin amount, String label, MoneroWallet wallet) { + return wallet.getPaymentUri(new MoneroTxConfig() + .setAddress(address) + .setAmount(HavenoUtils.coinToAtomicUnits(amount)) + .setNote(label)); + } + public static String getBitcoinURI(String address, Coin amount, String label) { return address != null ? BitcoinURI.convertToBitcoinURI(Address.fromString(Config.baseCurrencyNetworkParameters(),