switch to xmr atomic units as native units

This commit is contained in:
woodser 2023-03-05 14:09:14 -05:00
parent ab94b2d6fa
commit 9b4f8046b7
124 changed files with 1155 additions and 1283 deletions

View File

@ -20,6 +20,7 @@ package bisq.core.account.sign;
import bisq.core.account.witness.AccountAgeWitness;
import bisq.core.filter.FilterManager;
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
import bisq.core.trade.HavenoUtils;
import bisq.core.user.User;
import bisq.network.p2p.BootstrapListener;
@ -43,6 +44,7 @@ import javax.inject.Inject;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.SignatureException;
@ -68,7 +70,7 @@ import lombok.extern.slf4j.Slf4j;
public class SignedWitnessService {
public static final long SIGNER_AGE_DAYS = 30;
private static final long SIGNER_AGE = SIGNER_AGE_DAYS * ChronoUnit.DAYS.getDuration().toMillis();
public static final Coin MINIMUM_TRADE_AMOUNT_FOR_SIGNING = Coin.parseCoin("0.0025");
public static final BigInteger MINIMUM_TRADE_AMOUNT_FOR_SIGNING = HavenoUtils.coinToAtomicUnits(Coin.parseCoin("0.0025"));
private final KeyRing keyRing;
private final P2PService p2PService;
@ -237,7 +239,7 @@ public class SignedWitnessService {
}
// Arbitrators sign with EC key
public void signAndPublishAccountAgeWitness(Coin tradeAmount,
public void signAndPublishAccountAgeWitness(BigInteger tradeAmount,
AccountAgeWitness accountAgeWitness,
ECKey key,
PublicKey peersPubKey) {
@ -263,7 +265,7 @@ public class SignedWitnessService {
}
// Arbitrators sign with EC key
private String signAndPublishAccountAgeWitness(Coin tradeAmount,
private String signAndPublishAccountAgeWitness(BigInteger tradeAmount,
AccountAgeWitness accountAgeWitness,
ECKey key,
byte[] peersPubKey,
@ -287,7 +289,7 @@ public class SignedWitnessService {
key.getPubKey(),
peersPubKey,
time,
tradeAmount.value);
tradeAmount.longValueExact());
publishSignedWitness(signedWitness);
log.info("Arbitrator signed witness {}", signedWitness.toString());
return "";
@ -300,7 +302,7 @@ public class SignedWitnessService {
}
// Any peer can sign with DSA key
public Optional<SignedWitness> signAndPublishAccountAgeWitness(Coin tradeAmount,
public Optional<SignedWitness> signAndPublishAccountAgeWitness(BigInteger tradeAmount,
AccountAgeWitness accountAgeWitness,
PublicKey peersPubKey) throws CryptoException {
if (isSignedAccountAgeWitness(accountAgeWitness)) {
@ -320,7 +322,7 @@ public class SignedWitnessService {
keyRing.getSignatureKeyPair().getPublic().getEncoded(),
peersPubKey.getEncoded(),
new Date().getTime(),
tradeAmount.value);
tradeAmount.longValueExact());
publishSignedWitness(signedWitness);
log.info("Trader signed witness {}", signedWitness.toString());
return Optional.of(signedWitness);
@ -438,8 +440,8 @@ public class SignedWitnessService {
return isSignerAccountAgeWitness(accountAgeWitness, new Date().getTime());
}
public boolean isSufficientTradeAmountForSigning(Coin tradeAmount) {
return !tradeAmount.isLessThan(MINIMUM_TRADE_AMOUNT_FOR_SIGNING);
public boolean isSufficientTradeAmountForSigning(BigInteger tradeAmount) {
return tradeAmount.compareTo(MINIMUM_TRADE_AMOUNT_FOR_SIGNING) >= 0;
}
private boolean verifySigner(SignedWitness signedWitness) {

View File

@ -53,7 +53,6 @@ import bisq.common.util.MathUtils;
import bisq.common.util.Tuple2;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Utils;
@ -61,6 +60,7 @@ import javax.inject.Inject;
import com.google.common.annotations.VisibleForTesting;
import java.math.BigInteger;
import java.security.PublicKey;
import java.time.Clock;
@ -423,7 +423,7 @@ public class AccountAgeWitnessService {
// Non fiat always has max limit
// Account types that can get signed will use time since signing, other methods use time since account age creation
// when measuring account age
private long getTradeLimit(Coin maxTradeLimit,
private BigInteger getTradeLimit(BigInteger maxTradeLimit,
String currencyCode,
AccountAgeWitness accountAgeWitness,
AccountAge accountAgeCategory,
@ -432,17 +432,17 @@ public class AccountAgeWitnessService {
if (CurrencyUtil.isCryptoCurrency(currencyCode) ||
!PaymentMethod.hasChargebackRisk(paymentMethod, currencyCode) ||
direction == OfferDirection.SELL) {
return maxTradeLimit.value;
return maxTradeLimit;
}
long limit = OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value;
BigInteger limit = OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT;
var factor = signedBuyFactor(accountAgeCategory);
if (factor > 0) {
limit = MathUtils.roundDoubleToLong(maxTradeLimit.value * factor);
limit = BigInteger.valueOf(MathUtils.roundDoubleToLong(maxTradeLimit.longValueExact() * factor));
}
log.debug("limit={}, factor={}, accountAgeWitnessHash={}",
Coin.valueOf(limit).toFriendlyString(),
limit,
factor,
Utilities.bytesAsHexString(accountAgeWitness.getHash()));
return limit;
@ -511,9 +511,9 @@ public class AccountAgeWitnessService {
return 0;
AccountAgeWitness accountAgeWitness = getMyWitness(paymentAccount.getPaymentAccountPayload());
Coin maxTradeLimit = paymentAccount.getPaymentMethod().getMaxTradeLimitAsCoin(currencyCode);
BigInteger maxTradeLimit = paymentAccount.getPaymentMethod().getMaxTradeLimit(currencyCode);
if (hasTradeLimitException(accountAgeWitness)) {
return maxTradeLimit.value;
return maxTradeLimit.longValueExact();
}
final long accountSignAge = getWitnessSignAge(accountAgeWitness, new Date());
AccountAge accountAgeCategory = getAccountAgeCategory(accountSignAge);
@ -523,7 +523,7 @@ public class AccountAgeWitnessService {
accountAgeWitness,
accountAgeCategory,
direction,
paymentAccount.getPaymentMethod());
paymentAccount.getPaymentMethod()).longValueExact();
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -582,7 +582,7 @@ public class AccountAgeWitnessService {
}
public boolean verifyPeersTradeAmount(Offer offer,
Coin tradeAmount,
BigInteger tradeAmount,
ErrorMessageHandler errorMessageHandler) {
checkNotNull(offer);
@ -593,8 +593,8 @@ public class AccountAgeWitnessService {
.orElse(isToleratedSmalleAmount(tradeAmount));
}
private boolean isToleratedSmalleAmount(Coin tradeAmount) {
return tradeAmount.value <= OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value;
private boolean isToleratedSmalleAmount(BigInteger tradeAmount) {
return tradeAmount.longValueExact() <= OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.longValueExact();
}
@ -642,14 +642,14 @@ public class AccountAgeWitnessService {
}
private boolean verifyPeersTradeLimit(Offer offer,
Coin tradeAmount,
BigInteger tradeAmount,
AccountAgeWitness peersWitness,
Date peersCurrentDate,
ErrorMessageHandler errorMessageHandler) {
checkNotNull(offer);
final String currencyCode = offer.getCurrencyCode();
final Coin defaultMaxTradeLimit = offer.getPaymentMethod().getMaxTradeLimitAsCoin(currencyCode);
long peersCurrentTradeLimit = defaultMaxTradeLimit.value;
final BigInteger defaultMaxTradeLimit = offer.getPaymentMethod().getMaxTradeLimit(currencyCode);
BigInteger peersCurrentTradeLimit = defaultMaxTradeLimit;
if (!hasTradeLimitException(peersWitness)) {
final long accountSignAge = getWitnessSignAge(peersWitness, peersCurrentDate);
AccountAge accountAgeCategory = getPeersAccountAgeCategory(accountSignAge);
@ -659,11 +659,11 @@ public class AccountAgeWitnessService {
accountAgeCategory, direction, offer.getPaymentMethod());
}
// Makers current trade limit cannot be smaller than that in the offer
boolean result = tradeAmount.value <= peersCurrentTradeLimit;
boolean result = tradeAmount.longValueExact() <= peersCurrentTradeLimit.longValueExact();
if (!result) {
String msg = "The peers trade limit is less than the traded amount.\n" +
"tradeAmount=" + tradeAmount.toFriendlyString() +
"\nPeers trade limit=" + Coin.valueOf(peersCurrentTradeLimit).toFriendlyString() +
"tradeAmount=" + tradeAmount +
"\nPeers trade limit=" + peersCurrentTradeLimit +
"\nOffer ID=" + offer.getShortId() +
"\nPaymentMethod=" + offer.getPaymentMethod().getId() +
"\nCurrencyCode=" + offer.getCurrencyCode();
@ -698,7 +698,7 @@ public class AccountAgeWitnessService {
// Witness signing
///////////////////////////////////////////////////////////////////////////////////////////
public void arbitratorSignAccountAgeWitness(Coin tradeAmount,
public void arbitratorSignAccountAgeWitness(BigInteger tradeAmount,
AccountAgeWitness accountAgeWitness,
ECKey key,
PublicKey peersPubKey) {
@ -737,7 +737,7 @@ public class AccountAgeWitnessService {
public Optional<SignedWitness> traderSignAndPublishPeersAccountAgeWitness(Trade trade) {
AccountAgeWitness peersWitness = findTradePeerWitness(trade).orElse(null);
Coin tradeAmount = trade.getAmount();
BigInteger tradeAmount = trade.getAmount();
checkNotNull(trade.getTradePeer().getPubKeyRing(), "Peer must have a keyring");
PublicKey peersPubKey = trade.getTradePeer().getPubKeyRing().getSignaturePubKey();
checkNotNull(peersWitness, "Not able to find peers witness, unable to sign for trade {}",
@ -800,7 +800,7 @@ public class AccountAgeWitnessService {
}
private Stream<TraderDataItem> getTraderData(Dispute dispute) {
Coin tradeAmount = dispute.getContract().getTradeAmount();
BigInteger tradeAmount = dispute.getContract().getTradeAmount();
PubKeyRing buyerPubKeyRing = dispute.getContract().getBuyerPubKeyRing();
PubKeyRing sellerPubKeyRing = dispute.getContract().getSellerPubKeyRing();
@ -841,7 +841,7 @@ public class AccountAgeWitnessService {
return signedWitnessService.isSignerAccountAgeWitness(accountAgeWitness);
}
public boolean tradeAmountIsSufficient(Coin tradeAmount) {
public boolean tradeAmountIsSufficient(BigInteger tradeAmount) {
return signedWitnessService.isSufficientTradeAmountForSigning(tradeAmount);
}

View File

@ -48,7 +48,6 @@ import bisq.common.handlers.ResultHandler;
import bisq.proto.grpc.NotificationMessage;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import javax.inject.Inject;
@ -58,7 +57,7 @@ import com.google.common.util.concurrent.FutureCallback;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@ -446,8 +445,8 @@ public class CoreApi {
Price price,
boolean useMarketBasedPrice,
double marketPriceMargin,
Coin amount,
Coin minAmount,
BigInteger amount,
BigInteger minAmount,
double buyerSecurityDeposit,
PaymentAccount paymentAccount) {
return coreOffersService.editOffer(offerId,
@ -552,10 +551,6 @@ public class CoreApi {
coreTradesService.closeTrade(tradeId);
}
public void withdrawFunds(String tradeId, String address, String memo) {
coreTradesService.withdrawFunds(tradeId, address, memo);
}
public Trade getTrade(String tradeId) {
return coreTradesService.getTrade(tradeId);
}

View File

@ -24,8 +24,6 @@ import bisq.common.crypto.PubKeyRing;
import bisq.common.handlers.FaultHandler;
import bisq.common.handlers.ResultHandler;
import org.bitcoinj.core.Coin;
import com.google.inject.name.Named;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -40,6 +38,8 @@ import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
import java.math.BigInteger;
@Singleton
@Slf4j
@ -217,33 +217,32 @@ public class CoreDisputesService {
public void applyPayoutAmountsToDisputeResult(DisputePayout payout, Dispute dispute, DisputeResult disputeResult, long customWinnerAmount) {
Contract contract = dispute.getContract();
Trade trade = tradeManager.getTrade(dispute.getTradeId());
Coin buyerSecurityDeposit = trade.getBuyerSecurityDeposit();
Coin sellerSecurityDeposit = trade.getSellerSecurityDeposit();
Coin tradeAmount = contract.getTradeAmount();
BigInteger buyerSecurityDeposit = trade.getBuyerSecurityDeposit();
BigInteger sellerSecurityDeposit = trade.getSellerSecurityDeposit();
BigInteger tradeAmount = contract.getTradeAmount();
if (payout == DisputePayout.BUYER_GETS_TRADE_AMOUNT) {
disputeResult.setBuyerPayoutAmount(tradeAmount.add(buyerSecurityDeposit));
disputeResult.setSellerPayoutAmount(sellerSecurityDeposit);
} else if (payout == DisputePayout.BUYER_GETS_ALL) {
disputeResult.setBuyerPayoutAmount(tradeAmount
.add(buyerSecurityDeposit)
.add(sellerSecurityDeposit)); // TODO (woodser): apply min payout to incentivize loser (see post v1.1.7)
disputeResult.setSellerPayoutAmount(Coin.ZERO);
.add(sellerSecurityDeposit)); // TODO (woodser): apply min payout to incentivize loser? (see post v1.1.7)
disputeResult.setSellerPayoutAmount(BigInteger.valueOf(0));
} else if (payout == DisputePayout.SELLER_GETS_TRADE_AMOUNT) {
disputeResult.setBuyerPayoutAmount(buyerSecurityDeposit);
disputeResult.setSellerPayoutAmount(tradeAmount.add(sellerSecurityDeposit));
} else if (payout == DisputePayout.SELLER_GETS_ALL) {
disputeResult.setBuyerPayoutAmount(Coin.ZERO);
disputeResult.setBuyerPayoutAmount(BigInteger.valueOf(0));
disputeResult.setSellerPayoutAmount(tradeAmount
.add(sellerSecurityDeposit)
.add(buyerSecurityDeposit));
} else if (payout == DisputePayout.CUSTOM) {
Coin winnerAmount = Coin.valueOf(customWinnerAmount);
if (winnerAmount.compareTo(HavenoUtils.atomicUnitsToCoin(trade.getWallet().getBalance())) > 0) {
if (customWinnerAmount > trade.getWallet().getBalance().longValueExact()) {
throw new RuntimeException("The custom winner payout amount is more than the trade wallet's balance");
}
Coin loserAmount = tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit).minus(winnerAmount);
disputeResult.setBuyerPayoutAmount(disputeResult.getWinner() == DisputeResult.Winner.BUYER ? winnerAmount : loserAmount);
disputeResult.setSellerPayoutAmount(disputeResult.getWinner() == DisputeResult.Winner.BUYER ? loserAmount : winnerAmount);
long loserAmount = tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit).subtract(BigInteger.valueOf(customWinnerAmount)).longValueExact();
disputeResult.setBuyerPayoutAmount(BigInteger.valueOf(disputeResult.getWinner() == DisputeResult.Winner.BUYER ? customWinnerAmount : loserAmount));
disputeResult.setSellerPayoutAmount(BigInteger.valueOf(disputeResult.getWinner() == DisputeResult.Winner.BUYER ? loserAmount : customWinnerAmount));
}
}
@ -254,7 +253,7 @@ public class CoreDisputesService {
String agentNodeAddress = checkNotNull(disputeManager.getAgentNodeAddress(dispute)).getFullAddress();
Contract contract = dispute.getContract();
String currencyCode = contract.getOfferPayload().getCurrencyCode();
String amount = formatter.formatCoinWithCode(contract.getTradeAmount());
String amount = HavenoUtils.formatToXmrWithCode(contract.getTradeAmount());
String textToSign = Res.get("disputeSummaryWindow.close.msg",
FormattingUtils.formatDateTime(disputeResult.getCloseDate(), true),
@ -264,8 +263,8 @@ public class CoreDisputesService {
currencyCode,
Res.get("disputeSummaryWindow.reason." + reason.name()),
amount,
formatter.formatCoinWithCode(disputeResult.getBuyerPayoutAmount()),
formatter.formatCoinWithCode(disputeResult.getSellerPayoutAmount()),
HavenoUtils.formatToXmrWithCode(disputeResult.getBuyerPayoutAmount()),
HavenoUtils.formatToXmrWithCode(disputeResult.getSellerPayoutAmount()),
disputeResult.summaryNotesProperty().get()
);

View File

@ -41,6 +41,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
@ -181,8 +182,8 @@ public class CoreOffersService {
String offerId = createOfferService.getRandomOfferId();
OfferDirection direction = OfferDirection.valueOf(directionAsString.toUpperCase());
Price price = priceAsString.isEmpty() ? null : Price.valueOf(upperCaseCurrencyCode, priceStringToLong(priceAsString, upperCaseCurrencyCode));
Coin amount = Coin.valueOf(amountAsLong);
Coin minAmount = Coin.valueOf(minAmountAsLong);
BigInteger amount = BigInteger.valueOf(amountAsLong);
BigInteger minAmount = BigInteger.valueOf(minAmountAsLong);
Coin useDefaultTxFee = Coin.ZERO;
Offer offer = createOfferService.createAndGetOffer(offerId,
direction,
@ -214,8 +215,8 @@ public class CoreOffersService {
Price price,
boolean useMarketBasedPrice,
double marketPriceMargin,
Coin amount,
Coin minAmount,
BigInteger amount,
BigInteger minAmount,
double buyerSecurityDeposit,
PaymentAccount paymentAccount) {
Coin useDefaultTxFee = Coin.ZERO;

View File

@ -25,6 +25,7 @@ import bisq.core.offer.Offer;
import bisq.core.offer.OfferBookService;
import bisq.core.offer.OfferDirection;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.HavenoUtils;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -111,7 +112,7 @@ class CorePriceService {
for(Offer offer: buyOffers) {
Price price = offer.getPrice();
if (price != null) {
double amount = (double) offer.getAmount().value / LongMath.pow(10, offer.getAmount().smallestUnitExponent());
double amount = (double) offer.getAmount().longValueExact() / LongMath.pow(10, HavenoUtils.XMR_SMALLEST_UNIT_EXPONENT);
accumulatedAmount += amount;
double priceAsDouble = (double) price.getValue() / LongMath.pow(10, price.smallestUnitExponent());
buyTM.put(mapPriceFeedServicePrice(priceAsDouble, currencyCode), accumulatedAmount);
@ -124,7 +125,7 @@ class CorePriceService {
for(Offer offer: sellOffers){
Price price = offer.getPrice();
if (price != null) {
double amount = (double) offer.getAmount().value / LongMath.pow(10, offer.getAmount().smallestUnitExponent());
double amount = (double) offer.getAmount().longValueExact() / LongMath.pow(10, HavenoUtils.XMR_SMALLEST_UNIT_EXPONENT);
accumulatedAmount += amount;
double priceAsDouble = (double) price.getValue() / LongMath.pow(10, price.smallestUnitExponent());
sellTM.put(mapPriceFeedServicePrice(priceAsDouble, currencyCode), accumulatedAmount);

View File

@ -50,6 +50,8 @@ import lombok.extern.slf4j.Slf4j;
import static bisq.core.btc.model.AddressEntry.Context.TRADE_PAYOUT;
import static java.lang.String.format;
import java.math.BigInteger;
@Singleton
@Slf4j
class CoreTradesService {
@ -103,8 +105,8 @@ class CoreTradesService {
var useSavingsWallet = true;
// synchronize access to take offer model // TODO (woodser): to avoid synchronizing, don't use stateful model
Coin takerFee;
Coin fundsNeededForTrade;
BigInteger takerFee;
BigInteger fundsNeededForTrade;
synchronized (takeOfferModel) {
takeOfferModel.initModel(offer, paymentAccount, useSavingsWallet);
takerFee = takeOfferModel.getTakerFee();
@ -164,47 +166,6 @@ class CoreTradesService {
tradeManager.onTradeCompleted(trade);
}
void withdrawFunds(String tradeId, String toAddress, String memo) {
coreWalletsService.verifyWalletsAreAvailable();
coreWalletsService.verifyEncryptedWalletIsUnlocked();
verifyTradeIsNotClosed(tradeId);
var trade = getOpenTrade(tradeId).orElseThrow(() ->
new IllegalArgumentException(format("trade with id '%s' not found", tradeId)));
verifyIsValidBTCAddress(toAddress);
var fromAddressEntry = btcWalletService.getOrCreateAddressEntry(trade.getId(), TRADE_PAYOUT);
verifyFundsNotWithdrawn(fromAddressEntry);
var amount = trade.getPayoutAmount();
var fee = getEstimatedTxFee(fromAddressEntry.getAddressString(), toAddress, amount);
var receiverAmount = amount.subtract(fee);
log.info(format("Withdrawing funds received from trade %s:"
+ "%n From %s%n To %s%n Amt %s%n Tx Fee %s%n Receiver Amt %s%n Memo %s%n",
tradeId,
fromAddressEntry.getAddressString(),
toAddress,
amount.toFriendlyString(),
fee.toFriendlyString(),
receiverAmount.toFriendlyString(),
memo));
tradeManager.onWithdrawRequest(
toAddress,
amount,
fee,
coreWalletsService.getKey(),
trade,
memo.isEmpty() ? null : memo,
() -> {
},
(errorMessage, throwable) -> {
log.error(errorMessage, throwable);
throw new IllegalStateException(errorMessage, throwable);
});
}
String getTradeRole(String tradeId) {
coreWalletsService.verifyWalletsAreAvailable();
coreWalletsService.verifyEncryptedWalletIsUnlocked();

View File

@ -21,7 +21,6 @@ import bisq.core.api.model.builder.OfferInfoBuilder;
import bisq.core.monetary.Price;
import bisq.core.offer.Offer;
import bisq.core.offer.OpenOffer;
import bisq.core.trade.HavenoUtils;
import bisq.common.Payload;
import bisq.common.proto.ProtoUtil;
import java.util.Optional;
@ -151,14 +150,14 @@ public class OfferInfo implements Payload {
.withPrice(preciseOfferPrice)
.withUseMarketBasedPrice(offer.isUseMarketBasedPrice())
.withMarketPriceMarginPct(marketPriceMarginAsPctLiteral)
.withAmount(HavenoUtils.centinerosToAtomicUnits(offer.getAmount().value).longValueExact())
.withMinAmount(HavenoUtils.centinerosToAtomicUnits(offer.getMinAmount().value).longValueExact())
.withAmount(offer.getAmount().longValueExact())
.withMinAmount(offer.getMinAmount().longValueExact())
.withVolume(roundedVolume)
.withMinVolume(roundedMinVolume)
.withMakerFee(HavenoUtils.centinerosToAtomicUnits(offer.getMakerFee().value).longValueExact())
.withMakerFee(offer.getMakerFee().longValueExact())
.withOfferFeePaymentTxId(offer.getOfferFeePaymentTxId())
.withBuyerSecurityDeposit(HavenoUtils.centinerosToAtomicUnits(offer.getBuyerSecurityDeposit().value).longValueExact())
.withSellerSecurityDeposit(HavenoUtils.centinerosToAtomicUnits(offer.getSellerSecurityDeposit().value).longValueExact())
.withBuyerSecurityDeposit(offer.getBuyerSecurityDeposit().longValueExact())
.withSellerSecurityDeposit(offer.getSellerSecurityDeposit().longValueExact())
.withPaymentAccountId(offer.getMakerPaymentAccountId())
.withPaymentMethodId(offer.getPaymentMethod().getId())
.withPaymentMethodShortName(offer.getPaymentMethod().getShortName())

View File

@ -161,8 +161,8 @@ public class TradeInfo implements Payload {
.withTakerDepositTxId(trade.getTaker().getDepositTxHash())
.withPayoutTxId(trade.getPayoutTxId())
.withAmountAsLong(trade.getAmountAsLong())
.withBuyerSecurityDeposit(trade.getBuyerSecurityDeposit() == null ? -1 : trade.getBuyerSecurityDeposit().value)
.withSellerSecurityDeposit(trade.getSellerSecurityDeposit() == null ? -1 : trade.getSellerSecurityDeposit().value)
.withBuyerSecurityDeposit(trade.getBuyerSecurityDeposit() == null ? -1 : trade.getBuyerSecurityDeposit().longValueExact())
.withSellerSecurityDeposit(trade.getSellerSecurityDeposit() == null ? -1 : trade.getSellerSecurityDeposit().longValueExact())
.withPrice(toPreciseTradePrice.apply(trade))
.withVolume(toRoundedVolume.apply(trade))
.withArbitratorNodeAddress(toArbitratorNodeAddress.apply(trade))

View File

@ -18,18 +18,21 @@
package bisq.core.btc.wallet;
import bisq.common.config.Config;
import bisq.core.trade.HavenoUtils;
import java.math.BigInteger;
import org.bitcoinj.core.Coin;
public class Restrictions {
private static Coin MIN_TRADE_AMOUNT;
private static Coin MIN_BUYER_SECURITY_DEPOSIT;
private static BigInteger MIN_TRADE_AMOUNT;
private static BigInteger MIN_BUYER_SECURITY_DEPOSIT;
// For the seller we use a fixed one as there is no way the seller can cancel the trade
// To make it editable would just increase complexity.
private static Coin SELLER_SECURITY_DEPOSIT;
private static BigInteger SELLER_SECURITY_DEPOSIT;
// At mediation we require a min. payout to the losing party to keep incentive for the trader to accept the
// mediated payout. For Refund agent cases we do not have that restriction.
private static Coin MIN_REFUND_AT_MEDIATED_DISPUTE;
private static BigInteger MIN_REFUND_AT_MEDIATED_DISPUTE;
public static Coin getMinNonDustOutput() {
if (minNonDustOutput == null)
@ -47,9 +50,9 @@ public class Restrictions {
return !isAboveDust(amount);
}
public static Coin getMinTradeAmount() {
public static BigInteger getMinTradeAmount() {
if (MIN_TRADE_AMOUNT == null)
MIN_TRADE_AMOUNT = Coin.valueOf(10_000); // 0,7 USD @ 7000 USD/BTC
MIN_TRADE_AMOUNT = HavenoUtils.centinerosToAtomicUnits(10000); // TODO: increase for xmr
return MIN_TRADE_AMOUNT;
}
@ -67,9 +70,9 @@ public class Restrictions {
// We use MIN_BUYER_SECURITY_DEPOSIT as well as lower bound in case of small trade amounts.
// So 0.0005 BTC is the min. buyer security deposit even with amount of 0.0001 BTC and 0.05% percentage value.
public static Coin getMinBuyerSecurityDepositAsCoin() {
public static BigInteger getMinBuyerSecurityDeposit() {
if (MIN_BUYER_SECURITY_DEPOSIT == null)
MIN_BUYER_SECURITY_DEPOSIT = Coin.parseCoin("0.001"); // 0.001 BTC is 60 USD @ 60000 USD/BTC
MIN_BUYER_SECURITY_DEPOSIT = HavenoUtils.xmrToAtomicUnits(0.001); // TODO: increase for xmr
return MIN_BUYER_SECURITY_DEPOSIT;
}
@ -78,16 +81,16 @@ public class Restrictions {
return 0.15; // 15% of trade amount.
}
public static Coin getMinSellerSecurityDepositAsCoin() {
public static BigInteger getMinSellerSecurityDeposit() {
if (SELLER_SECURITY_DEPOSIT == null)
SELLER_SECURITY_DEPOSIT = Coin.parseCoin("0.001"); // 0.001 BTC is 60 USD @ 60000 USD/BTC
SELLER_SECURITY_DEPOSIT = HavenoUtils.xmrToAtomicUnits(0.001);
return SELLER_SECURITY_DEPOSIT;
}
// This value must be lower than MIN_BUYER_SECURITY_DEPOSIT and SELLER_SECURITY_DEPOSIT
public static Coin getMinRefundAtMediatedDispute() {
public static BigInteger getMinRefundAtMediatedDispute() {
if (MIN_REFUND_AT_MEDIATED_DISPUTE == null)
MIN_REFUND_AT_MEDIATED_DISPUTE = Coin.parseCoin("0.0005"); // 0.0005 BTC is 30 USD @ 60000 USD/BTC
MIN_REFUND_AT_MEDIATED_DISPUTE = HavenoUtils.xmrToAtomicUnits(0.0005);
return MIN_REFUND_AT_MEDIATED_DISPUTE;
}

View File

@ -66,7 +66,6 @@ import monero.wallet.model.MoneroTxWallet;
import monero.wallet.model.MoneroWalletConfig;
import monero.wallet.model.MoneroWalletListener;
import monero.wallet.model.MoneroWalletListenerI;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -314,9 +313,9 @@ public class XmrWalletService {
public MoneroTxWallet createDepositTx(Trade trade) {
Offer offer = trade.getProcessModel().getOffer();
String multisigAddress = trade.getProcessModel().getMultisigAddress();
BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(trade instanceof MakerTrade ? trade.getOffer().getMakerFee() : trade.getTakerFee());
BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(trade instanceof BuyerTrade ? Coin.ZERO : offer.getAmount());
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(trade instanceof BuyerTrade ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
BigInteger tradeFee = trade instanceof MakerTrade ? trade.getOffer().getMakerFee() : trade.getTakerFee();
BigInteger sendAmount = trade instanceof BuyerTrade ? BigInteger.valueOf(0) : offer.getAmount();
BigInteger securityDeposit = trade instanceof BuyerTrade ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit();
// thaw reserved outputs then create deposit tx
MoneroWallet wallet = getWallet();
@ -683,13 +682,13 @@ public class XmrWalletService {
private void notifyBalanceListeners() {
for (XmrBalanceListener balanceListener : balanceListeners) {
Coin balance;
BigInteger balance;
if (balanceListener.getSubaddressIndex() != null && balanceListener.getSubaddressIndex() != 0) balance = getBalanceForSubaddress(balanceListener.getSubaddressIndex());
else balance = getAvailableBalance();
UserThread.execute(new Runnable() { // TODO (woodser): don't execute on UserThread
@Override
public void run() {
balanceListener.onBalanceChanged(BigInteger.valueOf(balance.value));
balanceListener.onBalanceChanged(balance);
}
});
}
@ -847,7 +846,7 @@ public class XmrWalletService {
}
public List<XmrAddressEntry> getFundedAvailableAddressEntries() {
return getAvailableAddressEntries().stream().filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex()).isPositive()).collect(Collectors.toList());
return getAvailableAddressEntries().stream().filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex()).compareTo(BigInteger.valueOf(0)) > 0).collect(Collectors.toList());
}
public List<XmrAddressEntry> getAddressEntryListAsImmutableList() {
@ -895,31 +894,31 @@ public class XmrWalletService {
.setIncludeOutputs(true));
}
public Coin getBalanceForAddress(String address) {
public BigInteger getBalanceForAddress(String address) {
return getBalanceForSubaddress(wallet.getAddressIndex(address).getIndex());
}
public Coin getBalanceForSubaddress(int subaddressIndex) {
return HavenoUtils.atomicUnitsToCoin(wallet.getBalance(0, subaddressIndex));
public BigInteger getBalanceForSubaddress(int subaddressIndex) {
return wallet.getBalance(0, subaddressIndex);
}
public Coin getAvailableBalanceForSubaddress(int subaddressIndex) {
return HavenoUtils.atomicUnitsToCoin(wallet.getUnlockedBalance(0, subaddressIndex));
public BigInteger getAvailableBalanceForSubaddress(int subaddressIndex) {
return wallet.getUnlockedBalance(0, subaddressIndex);
}
public Coin getBalance() {
return wallet != null ? HavenoUtils.atomicUnitsToCoin(wallet.getBalance(0)) : Coin.ZERO;
public BigInteger getBalance() {
return wallet != null ? wallet.getBalance(0) : BigInteger.valueOf(0);
}
public Coin getAvailableBalance() {
return wallet != null ? HavenoUtils.atomicUnitsToCoin(wallet.getUnlockedBalance(0)) : Coin.ZERO;
public BigInteger getAvailableBalance() {
return wallet != null ? wallet.getUnlockedBalance(0) : BigInteger.valueOf(0);
}
public Stream<XmrAddressEntry> getAddressEntriesForAvailableBalanceStream() {
Stream<XmrAddressEntry> availableAndPayout = Stream.concat(getAddressEntries(XmrAddressEntry.Context.TRADE_PAYOUT).stream(), getFundedAvailableAddressEntries().stream());
Stream<XmrAddressEntry> available = Stream.concat(availableAndPayout, getAddressEntries(XmrAddressEntry.Context.ARBITRATOR).stream());
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.OFFER_FUNDING).stream());
return available.filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex()).isPositive());
return available.filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex()).compareTo(BigInteger.valueOf(0)) > 0);
}
public void addWalletListener(MoneroWalletListenerI listener) {

View File

@ -18,11 +18,12 @@
package bisq.core.monetary;
import bisq.core.locale.CurrencyUtil;
import bisq.core.trade.HavenoUtils;
import bisq.core.util.ParsingUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Monetary;
import org.bitcoinj.utils.ExchangeRate;
import org.bitcoinj.utils.Fiat;
@ -33,7 +34,7 @@ import org.slf4j.LoggerFactory;
import org.jetbrains.annotations.NotNull;
/**
* Bitcoin price value with variable precision.
* Monero price value with variable precision.
* <p>
* <br/>
* We wrap an object implementing the {@link Monetary} interface from bitcoinj. We respect the
@ -82,23 +83,23 @@ public class Price extends MonetaryWrapper implements Comparable<Price> {
}
}
public Volume getVolumeByAmount(Coin amount) {
public Volume getVolumeByAmount(BigInteger amount) {
if (monetary instanceof Fiat)
return new Volume(new ExchangeRate((Fiat) monetary).coinToFiat(amount));
return new Volume(new ExchangeRate((Fiat) monetary).coinToFiat(HavenoUtils.atomicUnitsToCoin(amount)));
else if (monetary instanceof Altcoin)
return new Volume(new AltcoinExchangeRate((Altcoin) monetary).coinToAltcoin(amount));
return new Volume(new AltcoinExchangeRate((Altcoin) monetary).coinToAltcoin(HavenoUtils.atomicUnitsToCoin(amount)));
else
throw new IllegalStateException("Monetary must be either of type Fiat or Altcoin");
}
public Coin getAmountByVolume(Volume volume) {
public BigInteger getAmountByVolume(Volume volume) {
Monetary monetary = volume.getMonetary();
if (monetary instanceof Fiat && this.monetary instanceof Fiat)
return new ExchangeRate((Fiat) this.monetary).fiatToCoin((Fiat) monetary);
return HavenoUtils.coinToAtomicUnits(new ExchangeRate((Fiat) this.monetary).fiatToCoin((Fiat) monetary));
else if (monetary instanceof Altcoin && this.monetary instanceof Altcoin)
return new AltcoinExchangeRate((Altcoin) this.monetary).altcoinToCoin((Altcoin) monetary);
return HavenoUtils.coinToAtomicUnits(new AltcoinExchangeRate((Altcoin) this.monetary).altcoinToCoin((Altcoin) monetary));
else
return Coin.ZERO;
return BigInteger.valueOf(0);
}
public String getCurrencyCode() {

View File

@ -45,6 +45,7 @@ import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;
import java.util.Map;
@ -104,8 +105,8 @@ public class CreateOfferService {
public Offer createAndGetOffer(String offerId,
OfferDirection direction,
String currencyCode,
Coin amount,
Coin minAmount,
BigInteger amount,
BigInteger minAmount,
Price price,
Coin txFee,
boolean useMarketBasedPrice,
@ -128,8 +129,8 @@ public class CreateOfferService {
price == null ? null : price.getValue(),
useMarketBasedPrice,
marketPriceMargin,
amount.value,
minAmount.value,
amount,
minAmount,
buyerSecurityDepositAsDouble);
long creationTime = new Date().getTime();
@ -146,8 +147,8 @@ public class CreateOfferService {
long priceAsLong = price != null ? price.getValue() : 0L;
double marketPriceMarginParam = useMarketBasedPriceValue ? marketPriceMargin : 0;
long amountAsLong = amount != null ? amount.getValue() : 0L;
long minAmountAsLong = minAmount != null ? minAmount.getValue() : 0L;
long amountAsLong = amount != null ? amount.longValueExact() : 0L;
long minAmountAsLong = minAmount != null ? minAmount.longValueExact() : 0L;
boolean isCryptoCurrency = CurrencyUtil.isCryptoCurrency(currencyCode);
String baseCurrencyCode = isCryptoCurrency ? currencyCode : Res.getBaseCurrencyCode();
String counterCurrencyCode = isCryptoCurrency ? Res.getBaseCurrencyCode() : currencyCode;
@ -155,10 +156,10 @@ public class CreateOfferService {
List<String> acceptedCountryCodes = PaymentAccountUtil.getAcceptedCountryCodes(paymentAccount);
String bankId = PaymentAccountUtil.getBankId(paymentAccount);
List<String> acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount);
double sellerSecurityDeposit = getSellerSecurityDepositAsDouble(buyerSecurityDepositAsDouble);
Coin makerFeeAsCoin = HavenoUtils.getMakerFee(amount);
Coin buyerSecurityDepositAsCoin = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble);
Coin sellerSecurityDepositAsCoin = getSellerSecurityDeposit(amount, sellerSecurityDeposit);
double sellerSecurityDepositAsDouble = getSellerSecurityDepositAsDouble(buyerSecurityDepositAsDouble);
BigInteger makerFee = HavenoUtils.getMakerFee(amount);
BigInteger buyerSecurityDeposit = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble);
BigInteger sellerSecurityDeposit = getSellerSecurityDeposit(amount, sellerSecurityDepositAsDouble);
long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction);
long maxTradePeriod = paymentAccount.getMaxTradePeriod();
@ -178,7 +179,7 @@ public class CreateOfferService {
buyerSecurityDepositAsDouble,
paymentAccount,
currencyCode,
makerFeeAsCoin);
makerFee);
OfferPayload offerPayload = new OfferPayload(offerId,
creationTime,
@ -201,9 +202,9 @@ public class CreateOfferService {
acceptedBanks,
Version.VERSION,
xmrWalletService.getWallet().getHeight(),
makerFeeAsCoin.value,
buyerSecurityDepositAsCoin.value,
sellerSecurityDepositAsCoin.value,
makerFee.longValueExact(),
buyerSecurityDeposit.longValueExact(),
sellerSecurityDeposit.longValueExact(),
maxTradeLimit,
maxTradePeriod,
useAutoClose,
@ -222,12 +223,12 @@ public class CreateOfferService {
return offer;
}
public Coin getReservedFundsForOffer(OfferDirection direction,
Coin amount,
public BigInteger getReservedFundsForOffer(OfferDirection direction,
BigInteger amount,
double buyerSecurityDeposit,
double sellerSecurityDeposit) {
Coin reservedFundsForOffer = getSecurityDeposit(direction,
BigInteger reservedFundsForOffer = getSecurityDeposit(direction,
amount,
buyerSecurityDeposit,
sellerSecurityDeposit);
@ -237,8 +238,8 @@ public class CreateOfferService {
return reservedFundsForOffer;
}
public Coin getSecurityDeposit(OfferDirection direction,
Coin amount,
public BigInteger getSecurityDeposit(OfferDirection direction,
BigInteger amount,
double buyerSecurityDeposit,
double sellerSecurityDeposit) {
return offerUtil.isBuyOffer(direction) ?
@ -260,26 +261,25 @@ public class CreateOfferService {
return marketPrice != null && marketPrice.isExternallyProvidedPrice();
}
private Coin getBuyerSecurityDeposit(Coin amount, double buyerSecurityDeposit) {
Coin percentOfAmountAsCoin = CoinUtil.getPercentOfAmountAsCoin(buyerSecurityDeposit, amount);
return getBoundedBuyerSecurityDeposit(percentOfAmountAsCoin);
private BigInteger getBuyerSecurityDeposit(BigInteger amount, double buyerSecurityDeposit) {
BigInteger percentOfAmount = CoinUtil.getPercentOfAmount(buyerSecurityDeposit, amount);
return getBoundedBuyerSecurityDeposit(percentOfAmount);
}
private Coin getSellerSecurityDeposit(Coin amount, double sellerSecurityDeposit) {
Coin amountAsCoin = (amount == null) ? Coin.ZERO : amount;
Coin percentOfAmountAsCoin = CoinUtil.getPercentOfAmountAsCoin(sellerSecurityDeposit, amountAsCoin);
return getBoundedSellerSecurityDeposit(percentOfAmountAsCoin);
private BigInteger getSellerSecurityDeposit(BigInteger amount, double sellerSecurityDeposit) {
BigInteger percentOfAmount = CoinUtil.getPercentOfAmount(sellerSecurityDeposit, amount);
return getBoundedSellerSecurityDeposit(percentOfAmount);
}
private Coin getBoundedBuyerSecurityDeposit(Coin value) {
private BigInteger getBoundedBuyerSecurityDeposit(BigInteger value) {
// We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the
// MinBuyerSecurityDepositAsCoin from Restrictions.
return Coin.valueOf(Math.max(Restrictions.getMinBuyerSecurityDepositAsCoin().value, value.value));
// MinBuyerSecurityDeposit from Restrictions.
return Restrictions.getMinBuyerSecurityDeposit().max(value);
}
private Coin getBoundedSellerSecurityDeposit(Coin value) {
private BigInteger getBoundedSellerSecurityDeposit(BigInteger value) {
// We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the
// MinSellerSecurityDepositAsCoin from Restrictions.
return Coin.valueOf(Math.max(Restrictions.getMinSellerSecurityDepositAsCoin().value, value.value));
// MinSellerSecurityDeposit from Restrictions.
return Restrictions.getMinSellerSecurityDeposit().max(value);
}
}

View File

@ -40,7 +40,6 @@ import bisq.common.util.JsonExclude;
import bisq.common.util.MathUtils;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat;
import javafx.beans.property.ObjectProperty;
@ -49,6 +48,7 @@ import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import java.math.BigInteger;
import java.security.PublicKey;
import java.util.Date;
@ -246,7 +246,7 @@ public class Offer implements NetworkPayload, PersistablePayload {
}
@Nullable
public Volume getVolumeByAmount(Coin amount) {
public Volume getVolumeByAmount(BigInteger amount) {
Price price = getPrice();
if (price == null || amount == null) {
return null;
@ -291,34 +291,34 @@ public class Offer implements NetworkPayload, PersistablePayload {
///////////////////////////////////////////////////////////////////////////////////////////
// get the amount needed for the maker to reserve the offer
public Coin getReserveAmount() {
Coin reserveAmount = getDirection() == OfferDirection.BUY ? getBuyerSecurityDeposit() : getSellerSecurityDeposit();
public BigInteger getReserveAmount() {
BigInteger reserveAmount = getDirection() == OfferDirection.BUY ? getBuyerSecurityDeposit() : getSellerSecurityDeposit();
if (getDirection() == OfferDirection.SELL) reserveAmount = reserveAmount.add(getAmount());
return reserveAmount;
}
public Coin getMakerFee() {
return Coin.valueOf(offerPayload.getMakerFee());
public BigInteger getMakerFee() {
return BigInteger.valueOf(offerPayload.getMakerFee());
}
public Coin getBuyerSecurityDeposit() {
return Coin.valueOf(offerPayload.getBuyerSecurityDeposit());
public BigInteger getBuyerSecurityDeposit() {
return BigInteger.valueOf(offerPayload.getBuyerSecurityDeposit());
}
public Coin getSellerSecurityDeposit() {
return Coin.valueOf(offerPayload.getSellerSecurityDeposit());
public BigInteger getSellerSecurityDeposit() {
return BigInteger.valueOf(offerPayload.getSellerSecurityDeposit());
}
public Coin getMaxTradeLimit() {
return Coin.valueOf(offerPayload.getMaxTradeLimit());
public BigInteger getMaxTradeLimit() {
return BigInteger.valueOf(offerPayload.getMaxTradeLimit());
}
public Coin getAmount() {
return Coin.valueOf(offerPayload.getAmount());
public BigInteger getAmount() {
return BigInteger.valueOf(offerPayload.getAmount());
}
public Coin getMinAmount() {
return Coin.valueOf(offerPayload.getMinAmount());
public BigInteger getMinAmount() {
return BigInteger.valueOf(offerPayload.getMinAmount());
}
public boolean isRange() {

View File

@ -208,7 +208,7 @@ public class OfferFilterService {
.map(paymentAccount -> accountAgeWitnessService.getMyTradeLimit(paymentAccount,
offer.getCurrencyCode(), offer.getMirroredDirection()))
.orElse(0L);
long offerMinAmount = offer.getMinAmount().value;
long offerMinAmount = offer.getMinAmount().longValueExact();
log.debug("isInsufficientTradeLimit accountOptional={}, myTradeLimit={}, offerMinAmount={}, ",
accountOptional.isPresent() ? accountOptional.get().getAccountName() : "null",
Coin.valueOf(myTradeLimit).toFriendlyString(),

View File

@ -22,14 +22,14 @@ import bisq.core.locale.Res;
import bisq.core.monetary.Price;
import bisq.core.monetary.Volume;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.trade.HavenoUtils;
import bisq.common.util.MathUtils;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.MonetaryFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.math.BigInteger;
import java.util.Date;
import org.slf4j.Logger;
@ -77,8 +77,8 @@ public class OfferForJson {
public OfferForJson(OfferDirection direction,
String currencyCode,
Coin minAmount,
Coin amount,
BigInteger minAmount,
BigInteger amount,
@Nullable Price price,
Date date,
String id,
@ -88,8 +88,8 @@ public class OfferForJson {
this.direction = direction;
this.currencyCode = currencyCode;
this.minAmount = minAmount.value;
this.amount = amount.value;
this.minAmount = minAmount.longValueExact();
this.amount = amount.longValueExact();
this.price = price.getValue();
this.date = date.getTime();
this.id = id;
@ -114,21 +114,21 @@ public class OfferForJson {
priceDisplayString = altcoinFormat.noCode().format(price.getMonetary()).toString();
primaryMarketMinAmountDisplayString = altcoinFormat.noCode().format(getMinVolume().getMonetary()).toString();
primaryMarketAmountDisplayString = altcoinFormat.noCode().format(getVolume().getMonetary()).toString();
primaryMarketMinVolumeDisplayString = coinFormat.noCode().format(getMinAmountAsCoin()).toString();
primaryMarketVolumeDisplayString = coinFormat.noCode().format(getAmountAsCoin()).toString();
primaryMarketMinVolumeDisplayString = HavenoUtils.formatToXmr(getMinAmount()).toString();
primaryMarketVolumeDisplayString = HavenoUtils.formatToXmr(getAmount()).toString();
primaryMarketPrice = price.getValue();
primaryMarketMinAmount = getMinVolume().getValue();
primaryMarketAmount = getVolume().getValue();
primaryMarketMinVolume = getMinAmountAsCoin().getValue();
primaryMarketVolume = getAmountAsCoin().getValue();
primaryMarketMinVolume = getMinAmount().longValueExact();
primaryMarketVolume = getAmount().longValueExact();
} else {
primaryMarketDirection = direction;
currencyPair = Res.getBaseCurrencyCode() + "/" + currencyCode;
priceDisplayString = fiatFormat.noCode().format(price.getMonetary()).toString();
primaryMarketMinAmountDisplayString = coinFormat.noCode().format(getMinAmountAsCoin()).toString();
primaryMarketAmountDisplayString = coinFormat.noCode().format(getAmountAsCoin()).toString();
primaryMarketMinAmountDisplayString = HavenoUtils.formatToXmr(getMinAmount()).toString();
primaryMarketAmountDisplayString = HavenoUtils.formatToXmr(getAmount()).toString();
primaryMarketMinVolumeDisplayString = fiatFormat.noCode().format(getMinVolume().getMonetary()).toString();
primaryMarketVolumeDisplayString = fiatFormat.noCode().format(getVolume().getMonetary()).toString();
@ -137,8 +137,8 @@ public class OfferForJson {
primaryMarketMinVolume = (long) MathUtils.scaleUpByPowerOf10(getMinVolume().getValue(), 4);
primaryMarketVolume = (long) MathUtils.scaleUpByPowerOf10(getVolume().getValue(), 4);
primaryMarketMinAmount = getMinAmountAsCoin().getValue();
primaryMarketAmount = getAmountAsCoin().getValue();
primaryMarketMinAmount = getMinAmount().longValueExact();
primaryMarketAmount = getAmount().longValueExact();
}
} catch (Throwable t) {
@ -150,19 +150,19 @@ public class OfferForJson {
return Price.valueOf(currencyCode, price);
}
private Coin getAmountAsCoin() {
return Coin.valueOf(amount);
private BigInteger getAmount() {
return BigInteger.valueOf(amount);
}
private Coin getMinAmountAsCoin() {
return Coin.valueOf(minAmount);
private BigInteger getMinAmount() {
return BigInteger.valueOf(minAmount);
}
private Volume getVolume() {
return getPrice().getVolumeByAmount(getAmountAsCoin());
return getPrice().getVolumeByAmount(getAmount());
}
private Volume getMinVolume() {
return getPrice().getVolumeByAmount(getMinAmountAsCoin());
return getPrice().getVolumeByAmount(getMinAmount());
}
}

View File

@ -21,9 +21,9 @@ import bisq.common.app.Capabilities;
import bisq.common.app.Capability;
import bisq.common.config.Config;
import bisq.common.util.Utilities;
import bisq.core.trade.HavenoUtils;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Map;
@ -37,7 +37,7 @@ public class OfferRestrictions {
return new Date().after(REQUIRE_TOR_NODE_ADDRESS_V3_DATE) && Config.baseCurrencyNetwork().isMainnet();
}
public static Coin TOLERATED_SMALL_TRADE_AMOUNT = Coin.parseCoin("2.0");
public static BigInteger TOLERATED_SMALL_TRADE_AMOUNT = HavenoUtils.xmrToAtomicUnits(2.0);
static boolean hasOfferMandatoryCapability(Offer offer, Capability mandatoryCapability) {
Map<String, String> extraDataMap = offer.getExtraDataMap();

View File

@ -40,7 +40,6 @@ import bisq.common.app.Capabilities;
import bisq.common.app.Version;
import bisq.common.util.MathUtils;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutput;
@ -49,6 +48,7 @@ import org.bitcoinj.utils.Fiat;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@ -129,7 +129,7 @@ public class OfferUtil {
* @param balance a wallet balance
* @return true if balance >= cost
*/
public boolean isBalanceSufficient(Coin cost, Coin balance) {
public boolean isBalanceSufficient(BigInteger cost, BigInteger balance) {
return cost != null && balance.compareTo(cost) >= 0;
}
@ -141,16 +141,15 @@ public class OfferUtil {
* @param balance a wallet balance
* @return the wallet balance shortage for the given cost, else zero.
*/
public Coin getBalanceShortage(Coin cost, Coin balance) {
public BigInteger getBalanceShortage(BigInteger cost, BigInteger balance) {
if (cost != null) {
Coin shortage = cost.subtract(balance);
return shortage.isNegative() ? Coin.ZERO : shortage;
BigInteger shortage = cost.subtract(balance);
return shortage.compareTo(BigInteger.valueOf(0)) < 0 ? BigInteger.valueOf(0) : shortage;
} else {
return Coin.ZERO;
return BigInteger.valueOf(0);
}
}
public double calculateManualPrice(double volumeAsDouble, double amountAsDouble) {
return volumeAsDouble / amountAsDouble;
}
@ -163,7 +162,7 @@ public class OfferUtil {
return offer != null && offer.getPaymentMethod().isBlockchain();
}
public Optional<Volume> getFeeInUserFiatCurrency(Coin makerFee,
public Optional<Volume> getFeeInUserFiatCurrency(BigInteger makerFee,
CoinFormatter formatter) {
String userCurrencyCode = preferences.getPreferredTradeCurrency().getCode();
if (CurrencyUtil.isCryptoCurrency(userCurrencyCode)) {
@ -216,8 +215,8 @@ public class OfferUtil {
public void validateOfferData(double buyerSecurityDeposit,
PaymentAccount paymentAccount,
String currencyCode,
Coin makerFeeAsCoin) {
checkNotNull(makerFeeAsCoin, "makerFee must not be null");
BigInteger makerFee) {
checkNotNull(makerFee, "makerFee must not be null");
checkNotNull(p2PService.getAddress(), "Address must not be null");
checkArgument(buyerSecurityDeposit <= getMaxBuyerSecurityDepositAsPercent(),
"securityDeposit must not exceed " +
@ -231,7 +230,7 @@ public class OfferUtil {
Res.get("offerbook.warning.paymentMethodBanned"));
}
private Optional<Volume> getFeeInUserFiatCurrency(Coin makerFee, String userCurrencyCode, CoinFormatter formatter) {
private Optional<Volume> getFeeInUserFiatCurrency(BigInteger makerFee, String userCurrencyCode, CoinFormatter formatter) {
MarketPrice marketPrice = priceFeedService.getMarketPrice(userCurrencyCode);
if (marketPrice != null && makerFee != null) {
long marketPriceAsLong = roundDoubleToLong(scaleUpByPowerOf10(marketPrice.getPrice(), Fiat.SMALLEST_UNIT_EXPONENT));

View File

@ -73,7 +73,6 @@ import bisq.common.persistence.PersistenceManager;
import bisq.common.proto.network.NetworkEnvelope;
import bisq.common.proto.persistable.PersistedDataHost;
import bisq.common.util.Tuple2;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
@ -800,8 +799,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
}
// get offer reserve amount
Coin offerReserveAmountCoin = openOffer.getOffer().getReserveAmount();
BigInteger offerReserveAmount = HavenoUtils.centinerosToAtomicUnits(offerReserveAmountCoin.value);
BigInteger offerReserveAmount = openOffer.getOffer().getReserveAmount();
// handle sufficient available balance
if (xmrWalletService.getWallet().getUnlockedBalance(0).compareTo(offerReserveAmount) >= 0) {
@ -814,7 +812,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
// otherwise sign and post offer
else {
signAndPostOffer(openOffer, offerReserveAmountCoin, true, resultHandler, errorMessageHandler);
signAndPostOffer(openOffer, offerReserveAmount, true, resultHandler, errorMessageHandler);
}
return;
}
@ -887,7 +885,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
}
private void signAndPostOffer(OpenOffer openOffer,
Coin offerReserveAmount,
BigInteger offerReserveAmount,
boolean useSavingsWallet, // TODO: remove this
TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
log.info("Signing and posting offer " + openOffer.getId());
@ -980,8 +978,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
// 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()))) {
BigInteger tradeFee = HavenoUtils.getMakerFee(offer.getAmount());
if (!tradeFee.equals(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);
@ -989,8 +987,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
}
// verify maker's reserve tx (double spend, trade fee, trade amount, mining fee)
BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount());
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
BigInteger sendAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.valueOf(0) : offer.getAmount();
BigInteger securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit();
MoneroTx reserveTx = xmrWalletService.verifyTradeTx(
tradeFee,
sendAmount,
@ -1011,8 +1009,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
SignedOffer signedOffer = new SignedOffer(
System.currentTimeMillis(),
signedOfferPayload.getId(),
HavenoUtils.coinToAtomicUnits(offer.getAmount()).longValueExact(),
HavenoUtils.coinToAtomicUnits(HavenoUtils.getMakerFee(offer.getAmount())).longValueExact(),
offer.getAmount().longValueExact(),
HavenoUtils.getMakerFee(offer.getAmount()).longValueExact(),
request.getReserveTxHash(),
request.getReserveTxHex(),
request.getReserveTxKeyImages(),

View File

@ -21,7 +21,6 @@ import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.monetary.Price;
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;
@ -72,9 +71,9 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
offer.getId(),
P2PService.getMyNodeAddress(),
p2PService.getKeyRing().getPubKeyRing(),
offer.getAmount().value,
offer.getAmount().longValueExact(),
price.getValue(),
HavenoUtils.getTakerFee(offer.getAmount()).value,
HavenoUtils.getTakerFee(offer.getAmount()).longValueExact(),
user.getAccountId(),
paymentAccountId,
paymentMethodId,

View File

@ -34,7 +34,8 @@ import bisq.common.taskrunner.Model;
import bisq.network.p2p.P2PService;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import org.bitcoinj.core.Transaction;
import lombok.Getter;
@ -50,7 +51,7 @@ import monero.wallet.model.MoneroTxWallet;
public class PlaceOfferModel implements Model {
// Immutable
private final Offer offer;
private final Coin reservedFundsForOffer;
private final BigInteger reservedFundsForOffer;
private final boolean useSavingsWallet;
private final P2PService p2PService;
private final BtcWalletService walletService;
@ -78,7 +79,7 @@ public class PlaceOfferModel implements Model {
private SignOfferResponse signOfferResponse;
public PlaceOfferModel(Offer offer,
Coin reservedFundsForOffer,
BigInteger reservedFundsForOffer,
boolean useSavingsWallet,
P2PService p2PService,
BtcWalletService walletService,

View File

@ -23,14 +23,12 @@ 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.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;
@ -54,9 +52,9 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
model.getXmrWalletService().getConnectionsService().verifyConnection();
// create reserve tx
BigInteger makerFee = HavenoUtils.coinToAtomicUnits(offer.getMakerFee());
BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount());
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
BigInteger makerFee = offer.getMakerFee();
BigInteger sendAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.valueOf(0) : offer.getAmount();
BigInteger securityDeposit = 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, sendAmount, securityDeposit, returnAddress);

View File

@ -30,6 +30,8 @@ import org.bitcoinj.core.Coin;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.math.BigInteger;
public class ValidateOffer extends Task<PlaceOfferModel> {
public ValidateOffer(TaskRunner<PlaceOfferModel> taskHandler, PlaceOfferModel model) {
super(taskHandler, model);
@ -42,13 +44,13 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
runInterceptHook();
// Coins
checkCoinNotNullOrZero(offer.getAmount(), "Amount");
checkCoinNotNullOrZero(offer.getMinAmount(), "MinAmount");
checkCoinNotNullOrZero(offer.getMakerFee(), "MakerFee");
checkCoinNotNullOrZero(offer.getBuyerSecurityDeposit(), "buyerSecurityDeposit");
checkCoinNotNullOrZero(offer.getSellerSecurityDeposit(), "sellerSecurityDeposit");
checkBINotNullOrZero(offer.getAmount(), "Amount");
checkBINotNullOrZero(offer.getMinAmount(), "MinAmount");
checkBINotNullOrZero(offer.getMakerFee(), "MakerFee");
checkBINotNullOrZero(offer.getBuyerSecurityDeposit(), "buyerSecurityDeposit");
checkBINotNullOrZero(offer.getSellerSecurityDeposit(), "sellerSecurityDeposit");
//checkCoinNotNullOrZero(offer.getTxFee(), "txFee"); // TODO: remove from data model
checkCoinNotNullOrZero(offer.getMaxTradeLimit(), "MaxTradeLimit");
checkBINotNullOrZero(offer.getMaxTradeLimit(), "MaxTradeLimit");
// We remove those checks to be more flexible with future changes.
/*checkArgument(offer.getMakerFee().value >= FeeService.getMinMakerFee(offer.isCurrencyForMakerFeeBtc()).value,
@ -67,8 +69,8 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
"MinAmount is less than " + ProposalConsensus.getMinTradeAmount().toFriendlyString());*/
long maxAmount = model.getAccountAgeWitnessService().getMyTradeLimit(model.getUser().getPaymentAccount(offer.getMakerPaymentAccountId()), offer.getCurrencyCode(), offer.getDirection());
checkArgument(offer.getAmount().longValue() <= maxAmount,
"Amount is larger than " + HavenoUtils.coinToXmr(offer.getPaymentMethod().getMaxTradeLimitAsCoin(offer.getCurrencyCode())) + " XMR");
checkArgument(offer.getAmount().longValueExact() <= maxAmount,
"Amount is larger than " + HavenoUtils.atomicUnitsToXmr(offer.getPaymentMethod().getMaxTradeLimit(offer.getCurrencyCode())) + " XMR");
checkArgument(offer.getAmount().compareTo(offer.getMinAmount()) >= 0, "MinAmount is larger than Amount");
checkNotNull(offer.getPrice(), "Price is null");
@ -100,6 +102,12 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
}
}
public static void checkBINotNullOrZero(BigInteger value, String name) {
checkNotNull(value, name + " is null");
checkArgument(value.compareTo(BigInteger.valueOf(0)) > 0,
name + " must be positive. " + name + "=" + value);
}
public static void checkCoinNotNullOrZero(Coin value, String name) {
checkNotNull(value, name + " is null");
checkArgument(value.isPositive(),

View File

@ -31,10 +31,9 @@ import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.HavenoUtils;
import bisq.common.taskrunner.Model;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import java.math.BigInteger;
import java.util.Objects;
import lombok.Getter;
@ -46,11 +45,8 @@ import static bisq.core.btc.model.XmrAddressEntry.Context.OFFER_FUNDING;
import static bisq.core.offer.OfferDirection.SELL;
import static bisq.core.util.VolumeUtil.getAdjustedVolumeForHalCash;
import static bisq.core.util.VolumeUtil.getRoundedFiatVolume;
import static bisq.core.util.coin.CoinUtil.minCoin;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.bitcoinj.core.Coin.ZERO;
import static org.bitcoinj.core.Coin.valueOf;
@Slf4j
public class TakeOfferModel implements Model {
@ -64,23 +60,23 @@ public class TakeOfferModel implements Model {
@Getter
private XmrAddressEntry addressEntry;
@Getter
private Coin amount;
private BigInteger amount;
private Offer offer;
private PaymentAccount paymentAccount;
@Getter
private Coin securityDeposit;
private BigInteger securityDeposit;
private boolean useSavingsWallet;
@Getter
private Coin takerFee;
private BigInteger takerFee;
@Getter
private Coin totalToPayAsCoin;
private BigInteger totalToPay;
@Getter
private Coin missingCoin = ZERO;
private BigInteger missingCoin = BigInteger.valueOf(0);
@Getter
private Coin totalAvailableBalance;
private BigInteger totalAvailableBalance;
@Getter
private Coin availableBalance;
private BigInteger availableBalance;
@Getter
private boolean isXmrWalletFunded;
@Getter
@ -107,7 +103,7 @@ public class TakeOfferModel implements Model {
validateModelInputs();
this.useSavingsWallet = useSavingsWallet;
this.amount = valueOf(Math.min(offer.getAmount().value, getMaxTradeLimit()));
this.amount = offer.getAmount().min(BigInteger.valueOf(getMaxTradeLimit()));
this.securityDeposit = offer.getDirection() == SELL
? offer.getBuyerSecurityDeposit()
: offer.getSellerSecurityDeposit();
@ -130,9 +126,9 @@ public class TakeOfferModel implements Model {
// maker created the offer and reserved his funds, so that would not work well
// with dynamic fees. The mining fee for the takeOfferFee tx is deducted from
// the createOfferFee and not visible to the trader.
Coin feeAndSecDeposit = securityDeposit.add(takerFee);
BigInteger feeAndSecDeposit = securityDeposit.add(takerFee);
totalToPayAsCoin = offer.isBuyOffer()
totalToPay = offer.isBuyOffer()
? feeAndSecDeposit.add(amount)
: feeAndSecDeposit;
@ -155,9 +151,9 @@ public class TakeOfferModel implements Model {
private void updateBalance() {
totalAvailableBalance = xmrWalletService.getAvailableBalance();
if (totalToPayAsCoin != null) availableBalance = minCoin(totalToPayAsCoin, totalAvailableBalance);
missingCoin = offerUtil.getBalanceShortage(totalToPayAsCoin, availableBalance);
isXmrWalletFunded = offerUtil.isBalanceSufficient(totalToPayAsCoin, availableBalance);
if (totalToPay != null) availableBalance = totalToPay.min(totalAvailableBalance);
missingCoin = offerUtil.getBalanceShortage(totalToPay, availableBalance);
isXmrWalletFunded = offerUtil.isBalanceSufficient(totalToPay, availableBalance);
}
private long getMaxTradeLimit() {
@ -167,15 +163,15 @@ public class TakeOfferModel implements Model {
}
@NotNull
public Coin getFundsNeededForTrade() {
public BigInteger getFundsNeededForTrade() {
// If taking a buy offer, taker needs to reserve the offer.amt too.
return securityDeposit.add(offer.isBuyOffer() ? amount : ZERO);
return securityDeposit.add(offer.isBuyOffer() ? amount : BigInteger.valueOf(0));
}
private void validateModelInputs() {
checkNotNull(offer, "offer must not be null");
checkNotNull(offer.getAmount(), "offer amount must not be null");
checkArgument(offer.getAmount().value > 0, "offer amount must not be zero");
checkArgument(offer.getAmount().longValueExact() > 0, "offer amount must not be zero");
checkNotNull(offer.getPrice(), "offer price must not be null");
checkNotNull(paymentAccount, "payment account must not be null");
checkNotNull(addressEntry, "address entry must not be null");
@ -186,13 +182,13 @@ public class TakeOfferModel implements Model {
this.amount = null;
this.availableBalance = null;
this.isXmrWalletFunded = false;
this.missingCoin = ZERO;
this.missingCoin = BigInteger.valueOf(0);
this.offer = null;
this.paymentAccount = null;
this.securityDeposit = null;
this.takerFee = null;
this.totalAvailableBalance = null;
this.totalToPayAsCoin = null;
this.totalToPay = null;
this.useSavingsWallet = true;
this.volume = null;
}
@ -209,7 +205,7 @@ public class TakeOfferModel implements Model {
", amount=" + amount + "\n" +
", securityDeposit=" + securityDeposit + "\n" +
", takerFee=" + takerFee + "\n" +
", totalToPayAsCoin=" + totalToPayAsCoin + "\n" +
", totalToPay=" + totalToPay + "\n" +
", missingCoin=" + missingCoin + "\n" +
", totalAvailableBalance=" + totalAvailableBalance + "\n" +
", availableBalance=" + availableBalance + "\n" +

View File

@ -71,7 +71,7 @@ public class PaymentAccountUtil {
AccountAgeWitnessService accountAgeWitnessService) {
boolean hasChargebackRisk = hasChargebackRisk(offer.getPaymentMethod(), offer.getCurrencyCode());
boolean hasValidAccountAgeWitness = accountAgeWitnessService.getMyTradeLimit(paymentAccount,
offer.getCurrencyCode(), offer.getMirroredDirection()) >= offer.getMinAmount().value;
offer.getCurrencyCode(), offer.getMirroredDirection()) >= offer.getMinAmount().longValueExact();
return !hasChargebackRisk || hasValidAccountAgeWitness;
}

View File

@ -18,8 +18,7 @@
package bisq.core.payment;
import bisq.common.util.MathUtils;
import org.bitcoinj.core.Coin;
import bisq.core.trade.HavenoUtils;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -29,12 +28,14 @@ import com.google.common.annotations.VisibleForTesting;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.math.BigInteger;
import javax.annotation.Nullable;
@Slf4j
@Singleton
public class TradeLimits {
private static final Coin MAX_TRADE_LIMIT = Coin.parseCoin("20"); // max trade limit for lowest risk payment method. Others will get derived from that.
private static final BigInteger MAX_TRADE_LIMIT = HavenoUtils.xmrToAtomicUnits(20.0); // max trade limit for lowest risk payment method. Others will get derived from that.
@Nullable
@Getter
private static TradeLimits INSTANCE;
@ -57,7 +58,7 @@ public class TradeLimits {
* @see bisq.core.payment.payload.PaymentMethod
* @return the maximum trade limit
*/
public Coin getMaxTradeLimit() {
public BigInteger getMaxTradeLimit() {
return MAX_TRADE_LIMIT;
}

View File

@ -77,8 +77,7 @@ import bisq.common.config.BaseCurrencyNetwork;
import bisq.common.config.Config;
import bisq.common.proto.persistable.PersistablePayload;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -113,10 +112,10 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
// risk factor so the relation between the risk categories stays the same as with the default values.
// We must not change those values as it could lead to invalid offers if amount becomes lower then new trade limit.
// Increasing might be ok, but needs more thought as well...
private static final Coin DEFAULT_TRADE_LIMIT_VERY_LOW_RISK = Coin.parseCoin("100");
private static final Coin DEFAULT_TRADE_LIMIT_LOW_RISK = Coin.parseCoin("50");
private static final Coin DEFAULT_TRADE_LIMIT_MID_RISK = Coin.parseCoin("25");
private static final Coin DEFAULT_TRADE_LIMIT_HIGH_RISK = Coin.parseCoin("12.5");
private static final BigInteger DEFAULT_TRADE_LIMIT_VERY_LOW_RISK = HavenoUtils.xmrToAtomicUnits(100);
private static final BigInteger DEFAULT_TRADE_LIMIT_LOW_RISK = HavenoUtils.xmrToAtomicUnits(50);
private static final BigInteger DEFAULT_TRADE_LIMIT_MID_RISK = HavenoUtils.xmrToAtomicUnits(25);
private static final BigInteger DEFAULT_TRADE_LIMIT_HIGH_RISK = HavenoUtils.xmrToAtomicUnits(12.5);
public static final String UPHOLD_ID = "UPHOLD";
public static final String MONEY_BEAM_ID = "MONEY_BEAM";
@ -291,19 +290,19 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
TRANSFERWISE_USD = new PaymentMethod(TRANSFERWISE_USD_ID, 4 * DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(TransferwiseUsdAccount.SUPPORTED_CURRENCIES)),
PAYSERA = new PaymentMethod(PAYSERA_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(PayseraAccount.SUPPORTED_CURRENCIES)),
PAXUM = new PaymentMethod(PAXUM_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(PaxumAccount.SUPPORTED_CURRENCIES)),
NEFT = new PaymentMethod(NEFT_ID, DAY, Coin.parseCoin("0.02"), getAssetCodes(NeftAccount.SUPPORTED_CURRENCIES)),
NEFT = new PaymentMethod(NEFT_ID, DAY, HavenoUtils.xmrToAtomicUnits(0.02), getAssetCodes(NeftAccount.SUPPORTED_CURRENCIES)),
RTGS = new PaymentMethod(RTGS_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(RtgsAccount.SUPPORTED_CURRENCIES)),
IMPS = new PaymentMethod(IMPS_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(ImpsAccount.SUPPORTED_CURRENCIES)),
UPI = new PaymentMethod(UPI_ID, DAY, Coin.parseCoin("0.05"), getAssetCodes(UpiAccount.SUPPORTED_CURRENCIES)),
PAYTM = new PaymentMethod(PAYTM_ID, DAY, Coin.parseCoin("0.05"), getAssetCodes(PaytmAccount.SUPPORTED_CURRENCIES)),
UPI = new PaymentMethod(UPI_ID, DAY, HavenoUtils.xmrToAtomicUnits(0.05), getAssetCodes(UpiAccount.SUPPORTED_CURRENCIES)),
PAYTM = new PaymentMethod(PAYTM_ID, DAY, HavenoUtils.xmrToAtomicUnits(0.05), getAssetCodes(PaytmAccount.SUPPORTED_CURRENCIES)),
NEQUI = new PaymentMethod(NEQUI_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(NequiAccount.SUPPORTED_CURRENCIES)),
BIZUM = new PaymentMethod(BIZUM_ID, DAY, Coin.parseCoin("0.04"), getAssetCodes(BizumAccount.SUPPORTED_CURRENCIES)),
BIZUM = new PaymentMethod(BIZUM_ID, DAY, HavenoUtils.xmrToAtomicUnits(0.04), getAssetCodes(BizumAccount.SUPPORTED_CURRENCIES)),
PIX = new PaymentMethod(PIX_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(PixAccount.SUPPORTED_CURRENCIES)),
CAPITUAL = new PaymentMethod(CAPITUAL_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(CapitualAccount.SUPPORTED_CURRENCIES)),
CELPAY = new PaymentMethod(CELPAY_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(CelPayAccount.SUPPORTED_CURRENCIES)),
MONESE = new PaymentMethod(MONESE_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(MoneseAccount.SUPPORTED_CURRENCIES)),
SATISPAY = new PaymentMethod(SATISPAY_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(SatispayAccount.SUPPORTED_CURRENCIES)),
TIKKIE = new PaymentMethod(TIKKIE_ID, DAY, Coin.parseCoin("0.05"), getAssetCodes(TikkieAccount.SUPPORTED_CURRENCIES)),
TIKKIE = new PaymentMethod(TIKKIE_ID, DAY, HavenoUtils.xmrToAtomicUnits(0.05), getAssetCodes(TikkieAccount.SUPPORTED_CURRENCIES)),
VERSE = new PaymentMethod(VERSE_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(VerseAccount.SUPPORTED_CURRENCIES)),
STRIKE = new PaymentMethod(STRIKE_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK, getAssetCodes(StrikeAccount.SUPPORTED_CURRENCIES)),
SWIFT = new PaymentMethod(SWIFT_ID, 7 * DAY, DEFAULT_TRADE_LIMIT_MID_RISK, getAssetCodes(SwiftAccount.SUPPORTED_CURRENCIES)),
@ -365,7 +364,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
}
public static PaymentMethod getDummyPaymentMethod(String id) {
return new PaymentMethod(id, 0, Coin.ZERO, Arrays.asList());
return new PaymentMethod(id, 0, BigInteger.valueOf(0), Arrays.asList());
}
@ -405,16 +404,16 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
* @param maxTradeLimit The max. allowed trade amount in Bitcoin for that payment method (depending on charge back risk)
* @param supportedAssetCodes Supported asset codes.
*/
private PaymentMethod(String id, long maxTradePeriod, Coin maxTradeLimit, List<String> supportedAssetCodes) {
private PaymentMethod(String id, long maxTradePeriod, BigInteger maxTradeLimit, List<String> supportedAssetCodes) {
this.id = id;
this.maxTradePeriod = maxTradePeriod;
this.maxTradeLimit = maxTradeLimit.value;
this.maxTradeLimit = maxTradeLimit.longValueExact();
this.supportedAssetCodes = supportedAssetCodes;
}
// Used for dummy entries in payment methods list (SHOW_ALL)
private PaymentMethod(String id) {
this(id, 0, Coin.ZERO, new ArrayList<String>());
this(id, 0, BigInteger.valueOf(0), new ArrayList<String>());
}
@ -435,7 +434,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
public static PaymentMethod fromProto(protobuf.PaymentMethod proto) {
return new PaymentMethod(proto.getId(),
proto.getMaxTradePeriod(),
Coin.valueOf(proto.getMaxTradeLimit()),
BigInteger.valueOf(proto.getMaxTradeLimit()),
proto.getSupportedAssetCodesList());
}
@ -456,44 +455,43 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
.findFirst();
}
public Coin getMaxTradeLimitAsCoin(String currencyCode) {
public BigInteger getMaxTradeLimit(String currencyCode) {
// Hack for SF as the smallest unit is 1 SF ;-( and price is about 3 BTC!
if (currencyCode.equals("SF"))
return Coin.parseCoin("4");
return HavenoUtils.xmrToAtomicUnits(4);
// payment methods which define their own trade limits
if (id.equals(NEFT_ID) || id.equals(UPI_ID) || id.equals(PAYTM_ID) || id.equals(BIZUM_ID) || id.equals(TIKKIE_ID)) {
return Coin.valueOf(maxTradeLimit);
return BigInteger.valueOf(maxTradeLimit);
}
// We use the class field maxTradeLimit only for mapping the risk factor.
long riskFactor;
if (maxTradeLimit == DEFAULT_TRADE_LIMIT_VERY_LOW_RISK.value)
if (maxTradeLimit == DEFAULT_TRADE_LIMIT_VERY_LOW_RISK.longValueExact())
riskFactor = 1;
else if (maxTradeLimit == DEFAULT_TRADE_LIMIT_LOW_RISK.value)
else if (maxTradeLimit == DEFAULT_TRADE_LIMIT_LOW_RISK.longValueExact())
riskFactor = 2;
else if (maxTradeLimit == DEFAULT_TRADE_LIMIT_MID_RISK.value)
else if (maxTradeLimit == DEFAULT_TRADE_LIMIT_MID_RISK.longValueExact())
riskFactor = 4;
else if (maxTradeLimit == DEFAULT_TRADE_LIMIT_HIGH_RISK.value)
else if (maxTradeLimit == DEFAULT_TRADE_LIMIT_HIGH_RISK.longValueExact())
riskFactor = 8;
else {
riskFactor = 8;
log.warn("maxTradeLimit is not matching one of our default values. We use highest risk factor. " +
"maxTradeLimit={}. PaymentMethod={}",
Coin.valueOf(maxTradeLimit).toFriendlyString(), this);
"maxTradeLimit={}. PaymentMethod={}", maxTradeLimit, this);
}
// get risk based trade limit
TradeLimits tradeLimits = new TradeLimits();
long maxTradeLimit = tradeLimits.getMaxTradeLimit().value;
long riskBasedTradeLimit = tradeLimits.getRoundedRiskBasedTradeLimit(maxTradeLimit, riskFactor); // as centineros
long maxTradeLimit = tradeLimits.getMaxTradeLimit().longValueExact();
long riskBasedTradeLimit = tradeLimits.getRoundedRiskBasedTradeLimit(maxTradeLimit, riskFactor);
// if fiat and stagenet, cap offer amounts to avoid offers which cannot be taken
boolean isFiat = CurrencyUtil.isFiatCurrency(currencyCode);
boolean isStagenet = Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_STAGENET;
if (isFiat && isStagenet && HavenoUtils.centinerosToXmr(riskBasedTradeLimit) > OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value) {
riskBasedTradeLimit = HavenoUtils.xmrToCentineros(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value);
if (isFiat && isStagenet && riskBasedTradeLimit > OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.longValueExact()) {
riskBasedTradeLimit = OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.longValueExact();
}
return Coin.valueOf(riskBasedTradeLimit);
return BigInteger.valueOf(riskBasedTradeLimit);
}
public String getShortName() {

View File

@ -17,47 +17,39 @@
package bisq.core.payment.validation;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.locale.Res;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import bisq.core.trade.HavenoUtils;
import bisq.core.util.validation.NumberValidator;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javax.inject.Named;
import java.math.BigDecimal;
import java.math.BigInteger;
import lombok.Getter;
import lombok.Setter;
import javax.annotation.Nullable;
public class BtcValidator extends NumberValidator {
protected final CoinFormatter formatter;
public class XmrValidator extends NumberValidator {
@Nullable
@Setter
protected Coin minValue;
protected BigInteger minValue;
@Nullable
@Setter
protected Coin maxValue;
protected BigInteger maxValue;
@Nullable
@Setter
@Getter
protected Coin maxTradeLimit;
protected BigInteger maxTradeLimit;
@Inject
public BtcValidator(@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter) {
this.formatter = formatter;
public XmrValidator() {
}
@Override
public ValidationResult validate(String input) {
ValidationResult result = validateIfNotEmpty(input);
@ -70,34 +62,20 @@ public class BtcValidator extends NumberValidator {
result = result.andValidation(input,
this::validateIfNotZero,
this::validateIfNotNegative,
this::validateIfNotFractionalBtcValue,
this::validateIfNotFractionalXmrValue,
this::validateIfNotExceedsMaxTradeLimit,
this::validateIfNotExceedsMaxBtcValue,
this::validateIfNotUnderMinValue,
this::validateIfAboveDust);
this::validateIfNotExceedsMaxValue,
this::validateIfNotUnderMinValue);
}
return result;
}
protected ValidationResult validateIfAboveDust(String input) {
try {
final Coin coin = Coin.parseCoin(input);
if (Restrictions.isAboveDust(coin))
return new ValidationResult(true);
else
return new ValidationResult(false, Res.get("validation.amountBelowDust",
formatter.formatCoinWithCode(Restrictions.getMinNonDustOutput())));
} catch (Throwable t) {
return new ValidationResult(false, Res.get("validation.invalidInput", t.getMessage()));
}
}
protected ValidationResult validateIfNotFractionalBtcValue(String input) {
protected ValidationResult validateIfNotFractionalXmrValue(String input) {
try {
BigDecimal bd = new BigDecimal(input);
final BigDecimal satoshis = bd.movePointRight(8);
if (satoshis.scale() > 0)
final BigDecimal atomicUnits = bd.movePointRight(HavenoUtils.XMR_SMALLEST_UNIT_EXPONENT);
if (atomicUnits.scale() > 0)
return new ValidationResult(false, Res.get("validation.btc.fraction"));
else
return new ValidationResult(true);
@ -106,11 +84,11 @@ public class BtcValidator extends NumberValidator {
}
}
protected ValidationResult validateIfNotExceedsMaxBtcValue(String input) {
protected ValidationResult validateIfNotExceedsMaxValue(String input) {
try {
final Coin coin = Coin.parseCoin(input);
if (maxValue != null && coin.compareTo(maxValue) > 0)
return new ValidationResult(false, Res.get("validation.btc.toLarge", formatter.formatCoinWithCode(maxValue)));
final BigInteger amount = HavenoUtils.parseXmr(input);
if (maxValue != null && amount.compareTo(maxValue) > 0)
return new ValidationResult(false, Res.get("validation.btc.toLarge", HavenoUtils.formatToXmrWithCode(maxValue)));
else
return new ValidationResult(true);
} catch (Throwable t) {
@ -120,9 +98,9 @@ public class BtcValidator extends NumberValidator {
protected ValidationResult validateIfNotExceedsMaxTradeLimit(String input) {
try {
final Coin coin = Coin.parseCoin(input);
if (maxTradeLimit != null && coin.compareTo(maxTradeLimit) > 0)
return new ValidationResult(false, Res.get("validation.btc.exceedsMaxTradeLimit", formatter.formatCoinWithCode(maxTradeLimit)));
final BigInteger amount = HavenoUtils.parseXmr(input);
if (maxTradeLimit != null && amount.compareTo(maxTradeLimit) > 0)
return new ValidationResult(false, Res.get("validation.btc.exceedsMaxTradeLimit", HavenoUtils.formatToXmrWithCode(maxTradeLimit)));
else
return new ValidationResult(true);
} catch (Throwable t) {
@ -132,9 +110,9 @@ public class BtcValidator extends NumberValidator {
protected ValidationResult validateIfNotUnderMinValue(String input) {
try {
final Coin coin = Coin.parseCoin(input);
if (minValue != null && coin.compareTo(minValue) < 0)
return new ValidationResult(false, Res.get("validation.btc.toSmall", formatter.formatCoinWithCode(minValue)));
final BigInteger amount = HavenoUtils.parseXmr(input);
if (minValue != null && amount.compareTo(minValue) < 0)
return new ValidationResult(false, Res.get("validation.btc.toSmall", HavenoUtils.formatToXmr(minValue)));
else
return new ValidationResult(true);
} catch (Throwable t) {

View File

@ -56,7 +56,6 @@ import bisq.common.handlers.ResultHandler;
import bisq.common.util.MathUtils;
import bisq.common.util.Tuple2;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat;
import javafx.beans.property.IntegerProperty;
@ -844,8 +843,8 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> 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 = HavenoUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getBuyerPayoutAmount() : disputeResult.getSellerPayoutAmount());
BigInteger loserPayoutAmount = HavenoUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getSellerPayoutAmount() : disputeResult.getBuyerPayoutAmount());
BigInteger winnerPayoutAmount = disputeResult.getWinner() == Winner.BUYER ? disputeResult.getBuyerPayoutAmount() : disputeResult.getSellerPayoutAmount();
BigInteger loserPayoutAmount = disputeResult.getWinner() == Winner.BUYER ? disputeResult.getSellerPayoutAmount() : disputeResult.getBuyerPayoutAmount();
// check sufficient balance
if (winnerPayoutAmount.compareTo(BigInteger.ZERO) < 0) throw new RuntimeException("Winner payout cannot be negative");
@ -1039,26 +1038,26 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
}
// The amount we would get if we do a new trade with current price
Coin potentialAmountAtDisputeOpening = priceAtDisputeOpening.getAmountByVolume(contract.getTradeVolume());
Coin buyerSecurityDeposit = Coin.valueOf(offerPayload.getBuyerSecurityDeposit());
Coin minRefundAtMediatedDispute = Restrictions.getMinRefundAtMediatedDispute();
BigInteger potentialAmountAtDisputeOpening = priceAtDisputeOpening.getAmountByVolume(contract.getTradeVolume());
BigInteger buyerSecurityDeposit = BigInteger.valueOf(offerPayload.getBuyerSecurityDeposit());
BigInteger minRefundAtMediatedDispute = Restrictions.getMinRefundAtMediatedDispute();
// minRefundAtMediatedDispute is always larger as buyerSecurityDeposit at mediated payout, we ignore refund agent case here as there it can be 0.
Coin maxLossSecDeposit = buyerSecurityDeposit.subtract(minRefundAtMediatedDispute);
Coin tradeAmount = contract.getTradeAmount();
Coin potentialGain = potentialAmountAtDisputeOpening.subtract(tradeAmount).subtract(maxLossSecDeposit);
BigInteger maxLossSecDeposit = buyerSecurityDeposit.subtract(minRefundAtMediatedDispute);
BigInteger tradeAmount = contract.getTradeAmount();
BigInteger potentialGain = potentialAmountAtDisputeOpening.subtract(tradeAmount).subtract(maxLossSecDeposit);
String optionTradeDetails;
// We don't translate those strings (yet) as it is only displayed to mediators/arbitrators.
String headline;
if (potentialGain.isPositive()) {
if (potentialGain.compareTo(BigInteger.valueOf(0)) > 0) {
headline = "This might be a potential option trade!";
optionTradeDetails = "\nBTC amount calculated with price at dispute opening: " + potentialAmountAtDisputeOpening.toFriendlyString() +
"\nMax loss of security deposit is: " + maxLossSecDeposit.toFriendlyString() +
"\nPossible gain from an option trade is: " + potentialGain.toFriendlyString();
optionTradeDetails = "\nBTC amount calculated with price at dispute opening: " + HavenoUtils.formatToXmrWithCode(potentialAmountAtDisputeOpening) +
"\nMax loss of security deposit is: " + HavenoUtils.formatToXmrWithCode(maxLossSecDeposit) +
"\nPossible gain from an option trade is: " + HavenoUtils.formatToXmrWithCode(potentialGain);
} else {
headline = "It does not appear to be an option trade.";
optionTradeDetails = "\nBTC amount calculated with price at dispute opening: " + potentialAmountAtDisputeOpening.toFriendlyString() +
"\nMax loss of security deposit is: " + maxLossSecDeposit.toFriendlyString() +
"\nPossible loss from an option trade is: " + potentialGain.multiply(-1).toFriendlyString();
optionTradeDetails = "\nBTC amount calculated with price at dispute opening: " + HavenoUtils.formatToXmrWithCode(potentialAmountAtDisputeOpening) +
"\nMax loss of security deposit is: " + HavenoUtils.formatToXmrWithCode(maxLossSecDeposit) +
"\nPossible loss from an option trade is: " + HavenoUtils.formatToXmrWithCode(potentialGain.multiply(BigInteger.valueOf(-1)));
}
String percentagePriceDetails = offerPayload.isUseMarketBasedPrice() ?
@ -1067,7 +1066,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
String priceInfoText = "System message: " + headline +
"\n\nTrade price: " + contract.getPrice().toFriendlyString() + percentagePriceDetails +
"\nTrade amount: " + tradeAmount.toFriendlyString() +
"\nTrade amount: " + HavenoUtils.formatToXmrWithCode(tradeAmount) +
"\nPrice at dispute opening: " + priceAtDisputeOpening.toFriendlyString() +
optionTradeDetails;

View File

@ -23,8 +23,6 @@ import bisq.common.proto.ProtoUtil;
import bisq.common.proto.network.NetworkPayload;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import com.google.protobuf.ByteString;
import javafx.beans.property.BooleanProperty;
@ -32,6 +30,7 @@ import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import java.math.BigInteger;
import java.util.Date;
import java.util.Optional;
@ -206,20 +205,20 @@ public final class DisputeResult implements NetworkPayload {
return summaryNotesProperty;
}
public void setBuyerPayoutAmount(Coin buyerPayoutAmount) {
this.buyerPayoutAmount = buyerPayoutAmount.value;
public void setBuyerPayoutAmount(BigInteger buyerPayoutAmount) {
this.buyerPayoutAmount = buyerPayoutAmount.longValueExact();
}
public Coin getBuyerPayoutAmount() {
return Coin.valueOf(buyerPayoutAmount);
public BigInteger getBuyerPayoutAmount() {
return BigInteger.valueOf(buyerPayoutAmount);
}
public void setSellerPayoutAmount(Coin sellerPayoutAmount) {
this.sellerPayoutAmount = sellerPayoutAmount.value;
public void setSellerPayoutAmount(BigInteger sellerPayoutAmount) {
this.sellerPayoutAmount = sellerPayoutAmount.longValueExact();
}
public Coin getSellerPayoutAmount() {
return Coin.valueOf(sellerPayoutAmount);
public BigInteger getSellerPayoutAmount() {
return BigInteger.valueOf(sellerPayoutAmount);
}
public void setCloseDate(Date closeDate) {

View File

@ -386,13 +386,13 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
// verify payouts sum to unlocked balance within loss of precision due to conversion to centineros
BigInteger txCost = arbitratorSignedPayoutTx.getFee().add(arbitratorSignedPayoutTx.getChangeAmount()); // fee + lost dust change
if (trade.getWallet().getUnlockedBalance().subtract(actualWinnerAmount.add(actualLoserAmount).add(txCost)).compareTo(HavenoUtils.CENTINEROS_AU_MULTIPLIER) > 0) {
if (trade.getWallet().getUnlockedBalance().subtract(actualWinnerAmount.add(actualLoserAmount).add(txCost)).compareTo(BigInteger.valueOf(0)) > 0) {
throw new RuntimeException("The dispute payout amounts do not sum to the wallet's unlocked balance while verifying the dispute payout tx, unlocked balance=" + trade.getWallet().getUnlockedBalance() + " vs sum payout amount=" + actualWinnerAmount.add(actualLoserAmount) + ", winner payout=" + actualWinnerAmount + ", loser payout=" + actualLoserAmount);
}
// get expected payout amounts
BigInteger expectedWinnerAmount = HavenoUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getBuyerPayoutAmount() : disputeResult.getSellerPayoutAmount());
BigInteger expectedLoserAmount = HavenoUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getSellerPayoutAmount() : disputeResult.getBuyerPayoutAmount());
BigInteger expectedWinnerAmount = disputeResult.getWinner() == Winner.BUYER ? disputeResult.getBuyerPayoutAmount() : disputeResult.getSellerPayoutAmount();
BigInteger expectedLoserAmount = disputeResult.getWinner() == Winner.BUYER ? disputeResult.getSellerPayoutAmount() : disputeResult.getBuyerPayoutAmount();
// add any loss of precision to winner amount
expectedWinnerAmount = expectedWinnerAmount.add(trade.getWallet().getUnlockedBalance().subtract(expectedWinnerAmount.add(expectedLoserAmount)));

View File

@ -20,8 +20,7 @@ package bisq.core.support.dispute.arbitration;
import bisq.core.account.witness.AccountAgeWitness;
import bisq.core.payment.payload.PaymentAccountPayload;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import java.security.PublicKey;
import lombok.EqualsAndHashCode;
@ -34,12 +33,12 @@ public class TraderDataItem {
private final PaymentAccountPayload paymentAccountPayload;
@EqualsAndHashCode.Include
private final AccountAgeWitness accountAgeWitness;
private final Coin tradeAmount;
private final BigInteger tradeAmount;
private final PublicKey peersPubKey;
public TraderDataItem(PaymentAccountPayload paymentAccountPayload,
AccountAgeWitness accountAgeWitness,
Coin tradeAmount,
BigInteger tradeAmount,
PublicKey peersPubKey) {
this.paymentAccountPayload = paymentAccountPayload;
this.accountAgeWitness = accountAgeWitness;

View File

@ -51,11 +51,10 @@ import bisq.common.crypto.KeyRing;
import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ResultHandler;
import org.bitcoinj.core.Coin;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.math.BigInteger;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
@ -194,8 +193,8 @@ public final class MediationManager extends DisputeManager<MediationDisputeList>
Trade trade = tradeOptional.get();
if (trade.getDisputeState() == Trade.DisputeState.MEDIATION_REQUESTED ||
trade.getDisputeState() == Trade.DisputeState.MEDIATION_STARTED_BY_PEER) {
trade.getProcessModel().setBuyerPayoutAmountFromMediation(disputeResult.getBuyerPayoutAmount().value);
trade.getProcessModel().setSellerPayoutAmountFromMediation(disputeResult.getSellerPayoutAmount().value);
trade.getProcessModel().setBuyerPayoutAmountFromMediation(disputeResult.getBuyerPayoutAmount().longValueExact());
trade.getProcessModel().setSellerPayoutAmountFromMediation(disputeResult.getSellerPayoutAmount().longValueExact());
trade.setDisputeState(Trade.DisputeState.MEDIATION_CLOSED);
@ -228,11 +227,11 @@ public final class MediationManager extends DisputeManager<MediationDisputeList>
Optional<Dispute> optionalDispute = findDispute(tradeId);
checkArgument(optionalDispute.isPresent(), "dispute must be present");
DisputeResult disputeResult = optionalDispute.get().getDisputeResultProperty().get();
Coin buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
Coin sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
BigInteger buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
BigInteger sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
ProcessModel processModel = trade.getProcessModel();
processModel.setBuyerPayoutAmountFromMediation(buyerPayoutAmount.value);
processModel.setSellerPayoutAmountFromMediation(sellerPayoutAmount.value);
processModel.setBuyerPayoutAmountFromMediation(buyerPayoutAmount.longValueExact());
processModel.setSellerPayoutAmountFromMediation(sellerPayoutAmount.longValueExact());
DisputeProtocol tradeProtocol = (DisputeProtocol) tradeManager.getTradeProtocol(trade);
trade.setMediationResultState(MediationResultState.MEDIATION_RESULT_ACCEPTED);

View File

@ -9,8 +9,7 @@ import bisq.network.p2p.NodeAddress;
import bisq.common.proto.ProtoUtil;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
@ -22,8 +21,8 @@ import lombok.extern.slf4j.Slf4j;
public class ArbitratorTrade extends Trade {
public ArbitratorTrade(Offer offer,
Coin tradeAmount,
Coin takerFee,
BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
@ -35,7 +34,7 @@ public class ArbitratorTrade extends Trade {
}
@Override
public Coin getPayoutAmount() {
public BigInteger getPayoutAmount() {
throw new RuntimeException("Arbitrator does not have a payout amount");
}
@ -62,8 +61,8 @@ public class ArbitratorTrade extends Trade {
}
return fromProto(new ArbitratorTrade(
Offer.fromProto(proto.getOffer()),
Coin.valueOf(proto.getAmountAsLong()),
Coin.valueOf(proto.getTakerFeeAsLong()),
BigInteger.valueOf(proto.getAmountAsLong()),
BigInteger.valueOf(proto.getTakerFeeAsLong()),
proto.getPrice(),
xmrWalletService,
processModel,

View File

@ -26,8 +26,7 @@ import bisq.network.p2p.NodeAddress;
import bisq.common.proto.ProtoUtil;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
@ -40,8 +39,8 @@ public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade {
///////////////////////////////////////////////////////////////////////////////////////////
public BuyerAsMakerTrade(Offer offer,
Coin tradeAmount,
Coin takeOfferFee,
BigInteger tradeAmount,
BigInteger takeOfferFee,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
@ -84,8 +83,8 @@ public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade {
}
BuyerAsMakerTrade trade = new BuyerAsMakerTrade(
Offer.fromProto(proto.getOffer()),
Coin.valueOf(proto.getAmountAsLong()),
Coin.valueOf(proto.getTakerFeeAsLong()),
BigInteger.valueOf(proto.getAmountAsLong()),
BigInteger.valueOf(proto.getTakerFeeAsLong()),
proto.getPrice(),
xmrWalletService,
processModel,

View File

@ -26,8 +26,7 @@ import bisq.network.p2p.NodeAddress;
import bisq.common.proto.ProtoUtil;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
@ -42,8 +41,8 @@ public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade {
///////////////////////////////////////////////////////////////////////////////////////////
public BuyerAsTakerTrade(Offer offer,
Coin tradeAmount,
Coin takerFee,
BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
@ -87,8 +86,8 @@ public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade {
}
return fromProto(new BuyerAsTakerTrade(
Offer.fromProto(proto.getOffer()),
Coin.valueOf(proto.getAmountAsLong()),
Coin.valueOf(proto.getTakerFeeAsLong()),
BigInteger.valueOf(proto.getAmountAsLong()),
BigInteger.valueOf(proto.getTakerFeeAsLong()),
proto.getPrice(),
xmrWalletService,
processModel,

View File

@ -23,19 +23,19 @@ import bisq.core.trade.protocol.ProcessModel;
import bisq.network.p2p.NodeAddress;
import org.bitcoinj.core.Coin;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
import java.math.BigInteger;
@Slf4j
public abstract class BuyerTrade extends Trade {
BuyerTrade(Offer offer,
Coin tradeAmount,
Coin takerFee,
BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
@ -56,7 +56,7 @@ public abstract class BuyerTrade extends Trade {
}
@Override
public Coin getPayoutAmount() {
public BigInteger getPayoutAmount() {
checkNotNull(getAmount(), "Invalid state: getTradeAmount() = null");
return checkNotNull(getOffer()).getBuyerSecurityDeposit().add(getAmount());
}

View File

@ -48,8 +48,6 @@ import static bisq.core.util.FormattingUtils.formatPercentagePrice;
import static bisq.core.util.FormattingUtils.formatToPercentWithSymbol;
import static bisq.core.util.VolumeUtil.formatVolume;
import static bisq.core.util.VolumeUtil.formatVolumeWithCode;
import static org.bitcoinj.core.TransactionConfidence.ConfidenceType.BUILDING;
import static org.bitcoinj.core.TransactionConfidence.ConfidenceType.PENDING;
@Slf4j
@Singleton
@ -59,7 +57,6 @@ public class ClosedTradableFormatter {
private static final String I18N_KEY_TOTAL_AMOUNT = "closedTradesSummaryWindow.totalAmount.value";
private static final String I18N_KEY_TOTAL_TX_FEE = "closedTradesSummaryWindow.totalMinerFee.value";
private static final String I18N_KEY_TOTAL_TRADE_FEE_BTC = "closedTradesSummaryWindow.totalTradeFeeInBtc.value";
private static final String I18N_KEY_TOTAL_TRADE_FEE_BSQ = "closedTradesSummaryWindow.totalTradeFeeInBsq.value";
private final CoinFormatter btcFormatter;
private final ClosedTradableManager closedTradableManager;
@ -72,7 +69,7 @@ public class ClosedTradableFormatter {
}
public String getAmountAsString(Tradable tradable) {
return tradable.getOptionalAmount().map(btcFormatter::formatCoin).orElse("");
return tradable.getOptionalAmount().map(HavenoUtils::formatToXmr).orElse("");
}
public String getTotalAmountWithVolumeAsString(Coin totalTradeAmount, Volume volume) {
@ -93,11 +90,11 @@ public class ClosedTradableFormatter {
}
public String getBuyerSecurityDepositAsString(Tradable tradable) {
return btcFormatter.formatCoin(tradable.getOffer().getBuyerSecurityDeposit());
return HavenoUtils.formatToXmr(tradable.getOffer().getBuyerSecurityDeposit());
}
public String getSellerSecurityDepositAsString(Tradable tradable) {
return btcFormatter.formatCoin(tradable.getOffer().getSellerSecurityDeposit());
return HavenoUtils.formatToXmr(tradable.getOffer().getSellerSecurityDeposit());
}
public String getTradeFeeAsString(Tradable tradable, boolean appendCode) {

View File

@ -41,6 +41,7 @@ import com.google.common.collect.ImmutableList;
import javafx.collections.ObservableList;
import java.math.BigInteger;
import java.time.Instant;
import java.util.ArrayList;
@ -229,8 +230,8 @@ public class ClosedTradableManager implements PersistedDataHost {
public long getBtcTradeFee(Tradable tradable) {
return isMaker(tradable) ?
tradable.getOptionalMakerFee().orElse(Coin.ZERO).value :
tradable.getOptionalTakerFee().orElse(Coin.ZERO).value;
tradable.getOptionalMakerFee().orElse(BigInteger.valueOf(0)).longValueExact() :
tradable.getOptionalTakerFee().orElse(BigInteger.valueOf(0)).longValueExact();
}
public boolean isMaker(Tradable tradable) {

View File

@ -33,8 +33,6 @@ import bisq.common.proto.network.NetworkPayload;
import bisq.common.util.JsonExclude;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import org.apache.commons.lang3.StringUtils;
import lombok.Value;
@ -44,6 +42,8 @@ import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
import java.math.BigInteger;
@Slf4j
@Value
public final class Contract implements NetworkPayload {
@ -209,8 +209,8 @@ public final class Contract implements NetworkPayload {
return makerPaymentMethodId;
}
public Coin getTradeAmount() {
return Coin.valueOf(tradeAmount);
public BigInteger getTradeAmount() {
return BigInteger.valueOf(tradeAmount);
}
public Volume getTradeVolume() {

View File

@ -25,8 +25,6 @@ import bisq.core.trade.messages.InitTradeRequest;
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 bisq.network.p2p.NodeAddress;
import bisq.common.config.Config;
@ -36,7 +34,6 @@ import bisq.common.crypto.Sig;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.MonetaryFormat;
import com.google.common.base.CaseFormat;
import com.google.common.base.Charsets;
@ -68,9 +65,10 @@ import javax.annotation.Nullable;
@Slf4j
public class HavenoUtils {
public static int XMR_SMALLEST_UNIT_EXPONENT = 12;
public static final String LOOPBACK_HOST = "127.0.0.1"; // local loopback address to host Monero node
public static final String LOCALHOST = "localhost";
public static final BigInteger CENTINEROS_AU_MULTIPLIER = new BigInteger("10000");
private static final long CENTINEROS_AU_MULTIPLIER = 10000;
private static final BigInteger XMR_AU_MULTIPLIER = new BigInteger("1000000000000");
public static final DecimalFormat XMR_FORMATTER = new DecimalFormat("0.000000000000");
public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
@ -79,12 +77,15 @@ public class HavenoUtils {
public static ArbitrationManager arbitrationManager; // TODO: better way to share reference?
// ----------------------- CONVERSION UTILS -------------------------------
public static BigInteger coinToAtomicUnits(Coin coin) {
return centinerosToAtomicUnits(coin.value);
}
public static BigInteger centinerosToAtomicUnits(long centineros) {
return BigInteger.valueOf(centineros).multiply(CENTINEROS_AU_MULTIPLIER);
return BigInteger.valueOf(centineros).multiply(BigInteger.valueOf(CENTINEROS_AU_MULTIPLIER));
}
public static double centinerosToXmr(long centineros) {
@ -95,12 +96,12 @@ public class HavenoUtils {
return atomicUnitsToCoin(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(long atomicUnits) {
return atomicUnits / CENTINEROS_AU_MULTIPLIER;
}
public static long atomicUnitsToCentineros(BigInteger atomicUnits) {
return atomicUnits.divide(CENTINEROS_AU_MULTIPLIER).longValueExact();
return atomicUnits.divide(BigInteger.valueOf(CENTINEROS_AU_MULTIPLIER)).longValueExact();
}
public static Coin atomicUnitsToCoin(long atomicUnits) {
@ -131,11 +132,19 @@ public class HavenoUtils {
return atomicUnitsToXmr(coinToAtomicUnits(coin));
}
public static String formatXmrWithCode(Coin coin) {
return formatXmrWithCode(coinToAtomicUnits(coin).longValueExact());
// ------------------------- FORMAT UTILS ---------------------------------
public static String formatToXmr(BigInteger atomicUnits) {
if (atomicUnits == null) return "";
return formatToXmr(atomicUnits.longValueExact());
}
public static String formatXmrWithCode(long atomicUnits) {
public static String formatToXmr(BigInteger atomicUnits, int decimalPlaces) {
return applyDecimals(formatToXmr(atomicUnits), decimalPlaces);
}
public static String formatToXmr(long atomicUnits) {
String formatted = XMR_FORMATTER.format(atomicUnitsToXmr(atomicUnits));
// strip trailing 0s
@ -144,55 +153,87 @@ public class HavenoUtils {
formatted = formatted.substring(0, formatted.length() - 1);
}
}
return formatted.concat(" ").concat("XMR");
return applyDecimals(formatted, 2);
}
private static final MonetaryFormat xmrCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat();
private static String applyDecimals(String decimalStr, int decimalPlaces) {
if (decimalStr.contains(".")) return decimalStr + getNumZeros(decimalPlaces - (decimalStr.length() - decimalStr.indexOf(".") - 1));
else return decimalStr + "." + getNumZeros(decimalPlaces);
}
private static String getNumZeros(int numZeros) {
String zeros = "";
for (int i = 0; i < numZeros; i++) zeros += "0";
return zeros;
}
public static String formatToXmrWithCode(BigInteger atomicUnits) {
if (atomicUnits == null) return "";
return formatToXmrWithCode(atomicUnits.longValueExact());
}
public static String formatToXmrWithCode(long atomicUnits) {
return formatToXmr(atomicUnits).concat(" XMR");
}
public static BigInteger parseXmr(String input) {
if (input == null || input.length() == 0) return BigInteger.valueOf(0);
try {
return xmrToAtomicUnits(new BigDecimal(input).doubleValue());
} catch (Exception e) {
log.warn("Exception at parseXmr: " + e.toString());
return BigInteger.valueOf(0);
}
}
// ------------------------------ FEE UTILS -------------------------------
@Nullable
public static Coin getMakerFee(@Nullable Coin amount) {
public static BigInteger getMakerFee(@Nullable BigInteger amount) {
if (amount != null) {
Coin feePerXmr = getFeePerXmr(HavenoUtils.getMakerFeePerXmr(), amount);
return CoinUtil.maxCoin(feePerXmr, HavenoUtils.getMinMakerFee());
BigInteger feePerXmr = getFeePerXmr(HavenoUtils.getMakerFeePerXmr(), amount);
return feePerXmr.max(HavenoUtils.getMinMakerFee());
} else {
return null;
}
}
@Nullable
public static Coin getTakerFee(@Nullable Coin amount) {
public static BigInteger getTakerFee(@Nullable BigInteger amount) {
if (amount != null) {
Coin feePerXmr = HavenoUtils.getFeePerXmr(HavenoUtils.getTakerFeePerXmr(), amount);
return CoinUtil.maxCoin(feePerXmr, HavenoUtils.getMinTakerFee());
BigInteger feePerXmr = HavenoUtils.getFeePerXmr(HavenoUtils.getTakerFeePerXmr(), amount);
return feePerXmr.max(HavenoUtils.getMinTakerFee());
} else {
return null;
}
}
private static Coin getMakerFeePerXmr() {
return ParsingUtils.parseToCoin("0.001", xmrCoinFormat);
private static BigInteger getMakerFeePerXmr() {
return HavenoUtils.xmrToAtomicUnits(0.001);
}
public static Coin getMinMakerFee() {
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
public static BigInteger getMinMakerFee() {
return HavenoUtils.xmrToAtomicUnits(0.00005);
}
private static Coin getTakerFeePerXmr() {
return ParsingUtils.parseToCoin("0.003", xmrCoinFormat);
private static BigInteger getTakerFeePerXmr() {
return HavenoUtils.xmrToAtomicUnits(0.003);
}
public static Coin getMinTakerFee() {
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
public static BigInteger getMinTakerFee() {
return HavenoUtils.xmrToAtomicUnits(0.00005);
}
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));
public static BigInteger getFeePerXmr(BigInteger feePerXmr, BigInteger amount) {
BigDecimal feePerXmrAsDecimal = feePerXmr == null ? BigDecimal.valueOf(0) : new BigDecimal(feePerXmr);
BigDecimal amountAsDecimal = amount == null ? BigDecimal.valueOf(0) : new BigDecimal(amount);
BigDecimal xmrAsDecimal = new BigDecimal(HavenoUtils.xmrToAtomicUnits(1.0));
return feePerXmrAsDecimal.multiply(amountAsDecimal.divide(xmrAsDecimal)).toBigInteger();
}
// ----------------------------- OTHER UTILS ------------------------------
/**
* Get address to collect trade fees.
*

View File

@ -26,8 +26,7 @@ import bisq.network.p2p.NodeAddress;
import bisq.common.proto.ProtoUtil;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
@ -42,8 +41,8 @@ public final class SellerAsMakerTrade extends SellerTrade implements MakerTrade
///////////////////////////////////////////////////////////////////////////////////////////
public SellerAsMakerTrade(Offer offer,
Coin tradeAmount,
Coin takerFee,
BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
@ -87,8 +86,8 @@ public final class SellerAsMakerTrade extends SellerTrade implements MakerTrade
}
SellerAsMakerTrade trade = new SellerAsMakerTrade(
Offer.fromProto(proto.getOffer()),
Coin.valueOf(proto.getAmountAsLong()),
Coin.valueOf(proto.getTakerFeeAsLong()),
BigInteger.valueOf(proto.getAmountAsLong()),
BigInteger.valueOf(proto.getTakerFeeAsLong()),
proto.getPrice(),
xmrWalletService,
processModel,

View File

@ -26,8 +26,7 @@ import bisq.network.p2p.NodeAddress;
import bisq.common.proto.ProtoUtil;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
@ -42,8 +41,8 @@ public final class SellerAsTakerTrade extends SellerTrade implements TakerTrade
///////////////////////////////////////////////////////////////////////////////////////////
public SellerAsTakerTrade(Offer offer,
Coin tradeAmount,
Coin takerFee,
BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
@ -87,8 +86,8 @@ public final class SellerAsTakerTrade extends SellerTrade implements TakerTrade
}
return fromProto(new SellerAsTakerTrade(
Offer.fromProto(proto.getOffer()),
Coin.valueOf(proto.getAmountAsLong()),
Coin.valueOf(proto.getTakerFeeAsLong()),
BigInteger.valueOf(proto.getAmountAsLong()),
BigInteger.valueOf(proto.getTakerFeeAsLong()),
proto.getPrice(),
xmrWalletService,
processModel,

View File

@ -18,25 +18,24 @@
package bisq.core.trade;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.CurrencyUtil;
import bisq.core.offer.Offer;
import bisq.core.trade.protocol.ProcessModel;
import bisq.network.p2p.NodeAddress;
import org.bitcoinj.core.Coin;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
import java.math.BigInteger;
@Slf4j
public abstract class SellerTrade extends Trade {
SellerTrade(Offer offer,
Coin tradeAmount,
Coin takerFee,
BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
@ -57,7 +56,7 @@ public abstract class SellerTrade extends Trade {
}
@Override
public Coin getPayoutAmount() {
public BigInteger getPayoutAmount() {
return checkNotNull(getOffer()).getSellerSecurityDeposit();
}

View File

@ -26,6 +26,7 @@ import bisq.common.proto.persistable.PersistablePayload;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import java.util.Date;
import java.util.Optional;
@ -54,7 +55,7 @@ public interface Tradable extends PersistablePayload {
return asTradeModel().map(Trade::getPrice).or(() -> Optional.ofNullable(getOffer().getPrice()));
}
default Optional<Coin> getOptionalAmount() {
default Optional<BigInteger> getOptionalAmount() {
return asTradeModel().map(Trade::getAmount);
}
@ -66,11 +67,11 @@ public interface Tradable extends PersistablePayload {
return asTradeModel().map(Trade::getTxFee);
}
default Optional<Coin> getOptionalTakerFee() {
default Optional<BigInteger> getOptionalTakerFee() {
return asTradeModel().map(Trade::getTakerFee);
}
default Optional<Coin> getOptionalMakerFee() {
default Optional<BigInteger> getOptionalMakerFee() {
return asTradeModel().map(Trade::getOffer).map(Offer::getMakerFee).or(() -> Optional.ofNullable(getOffer().getMakerFee()));
}

View File

@ -375,7 +375,7 @@ public abstract class Trade implements Tradable, Model {
@Getter
transient final private Coin txFee;
@Getter
transient final private Coin takerFee;
transient final private BigInteger takerFee;
@Getter
transient final private XmrWalletService xmrWalletService;
@ -402,9 +402,9 @@ public abstract class Trade implements Tradable, Model {
// Added in v1.2.0
@Nullable
transient private Coin tradeAmount;
transient private BigInteger tradeAmount;
transient private ObjectProperty<Coin> tradeAmountProperty;
transient private ObjectProperty<BigInteger> tradeAmountProperty;
transient private ObjectProperty<Volume> tradeVolumeProperty;
// Added in v1.1.6
@ -469,8 +469,8 @@ public abstract class Trade implements Tradable, Model {
// maker
protected Trade(Offer offer,
Coin tradeAmount,
Coin takerFee, // TODO (woodser): makerFee, takerFee, but not given one during construction
BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
@ -487,7 +487,7 @@ public abstract class Trade implements Tradable, Model {
this.processModel = processModel;
this.uid = uid;
this.takerFeeAsLong = takerFee.value;
this.takerFeeAsLong = takerFee.longValueExact();
this.takeOfferDate = new Date().getTime();
this.tradeListeners = new ArrayList<TradeListener>();
@ -503,9 +503,9 @@ public abstract class Trade implements Tradable, Model {
// taker
@SuppressWarnings("NullableProblems")
protected Trade(Offer offer,
Coin tradeAmount,
Coin txFee,
Coin takerFee,
BigInteger tradeAmount,
BigInteger txFee,
BigInteger takerFee,
long tradePrice,
@Nullable NodeAddress mediatorNodeAddress, // TODO (woodser): remove mediator, refund agent from trade
@Nullable NodeAddress refundAgentNodeAddress,
@ -532,9 +532,9 @@ public abstract class Trade implements Tradable, Model {
// arbitrator
@SuppressWarnings("NullableProblems")
protected Trade(Offer offer,
Coin tradeAmount,
BigInteger tradeAmount,
Coin txFee,
Coin takerFee,
BigInteger takerFee,
long tradePrice,
NodeAddress makerNodeAddress,
NodeAddress takerNodeAddress,
@ -860,7 +860,7 @@ public abstract class Trade implements Tradable, Model {
boolean isBuyerMakerAndSellerTaker = getOffer().getDirection() == OfferDirection.BUY;
Contract contract = new Contract(
getOffer().getOfferPayload(),
checkNotNull(getAmount()).value,
checkNotNull(getAmount()).longValueExact(),
getPrice().getValue(),
(isBuyerMakerAndSellerTaker ? getMaker() : getTaker()).getNodeAddress(), // buyer node address // TODO (woodser): use maker and taker node address instead of buyer and seller node address for consistency
(isBuyerMakerAndSellerTaker ? getTaker() : getMaker()).getNodeAddress(), // seller node address
@ -905,7 +905,7 @@ public abstract class Trade implements Tradable, Model {
Preconditions.checkNotNull(buyerPayoutAddress, "Buyer payout address must not be null");
BigInteger sellerDepositAmount = multisigWallet.getTx(this.getSeller().getDepositTxHash()).getIncomingAmount();
BigInteger buyerDepositAmount = multisigWallet.getTx(this.getBuyer().getDepositTxHash()).getIncomingAmount();
BigInteger tradeAmount = HavenoUtils.coinToAtomicUnits(this.getAmount());
BigInteger tradeAmount = getAmount();
BigInteger buyerPayoutAmount = buyerDepositAmount.add(tradeAmount);
BigInteger sellerPayoutAmount = sellerDepositAmount.subtract(tradeAmount);
@ -956,7 +956,7 @@ public abstract class Trade implements Tradable, Model {
Contract contract = getContract();
BigInteger sellerDepositAmount = wallet.getTx(getSeller().getDepositTxHash()).getIncomingAmount(); // TODO (woodser): redundancy of processModel.getPreparedDepositTxId() vs this.getDepositTxId() necessary or avoidable?
BigInteger buyerDepositAmount = wallet.getTx(getBuyer().getDepositTxHash()).getIncomingAmount();
BigInteger tradeAmount = HavenoUtils.coinToAtomicUnits(getAmount());
BigInteger tradeAmount = getAmount();
// describe payout tx
MoneroTxSet describedTxSet = wallet.describeTxSet(new MoneroTxSet().setMultisigTxHex(payoutTxHex));
@ -1111,7 +1111,7 @@ public abstract class Trade implements Tradable, Model {
// by mediators and we keep the confirm disabled to avoid that the seller can complete the trade
// without the penalty.
long payoutAmountFromMediation = processModel.getSellerPayoutAmountFromMediation();
long normalPayoutAmount = getSellerSecurityDeposit().value;
long normalPayoutAmount = getSellerSecurityDeposit().longValueExact();
return payoutAmountFromMediation < normalPayoutAmount;
}
@ -1157,7 +1157,7 @@ public abstract class Trade implements Tradable, Model {
// Abstract
///////////////////////////////////////////////////////////////////////////////////////////
public abstract Coin getPayoutAmount();
public abstract BigInteger getPayoutAmount();
public abstract boolean confirmPermitted();
@ -1263,9 +1263,9 @@ public abstract class Trade implements Tradable, Model {
tradePeriodStateProperty.set(tradePeriodState);
}
public void setAmount(Coin tradeAmount) {
public void setAmount(BigInteger tradeAmount) {
this.tradeAmount = tradeAmount;
amountAsLong = tradeAmount.value;
amountAsLong = tradeAmount.longValueExact();
getAmountProperty().set(tradeAmount);
getVolumeProperty().set(getVolume());
}
@ -1535,7 +1535,7 @@ public abstract class Trade implements Tradable, Model {
return tradePeriodStateProperty;
}
public ReadOnlyObjectProperty<Coin> tradeAmountProperty() {
public ReadOnlyObjectProperty<BigInteger> tradeAmountProperty() {
return tradeAmountProperty;
}
@ -1567,24 +1567,24 @@ public abstract class Trade implements Tradable, Model {
}
@Nullable
public Coin getAmount() {
public BigInteger getAmount() {
if (tradeAmount == null)
tradeAmount = Coin.valueOf(amountAsLong);
tradeAmount = BigInteger.valueOf(amountAsLong);
return tradeAmount;
}
public Coin getMakerFee() {
public BigInteger getMakerFee() {
return offer.getMakerFee();
}
public Coin getBuyerSecurityDeposit() {
public BigInteger getBuyerSecurityDeposit() {
if (getBuyer().getDepositTxHash() == null) return null;
return HavenoUtils.centinerosToCoin(getBuyer().getSecurityDeposit());
return BigInteger.valueOf(getBuyer().getSecurityDeposit());
}
public Coin getSellerSecurityDeposit() {
public BigInteger getSellerSecurityDeposit() {
if (getSeller().getDepositTxHash() == null) return null;
return HavenoUtils.centinerosToCoin(getSeller().getSecurityDeposit());
return BigInteger.valueOf(getSeller().getSecurityDeposit());
}
@Nullable
@ -1627,7 +1627,7 @@ public abstract class Trade implements Tradable, Model {
///////////////////////////////////////////////////////////////////////////////////////////
// lazy initialization
private ObjectProperty<Coin> getAmountProperty() {
private ObjectProperty<BigInteger> getAmountProperty() {
if (tradeAmountProperty == null)
tradeAmountProperty = getAmount() != null ? new SimpleObjectProperty<>(getAmount()) : new SimpleObjectProperty<>();
@ -1730,9 +1730,9 @@ public abstract class Trade implements Tradable, Model {
// set security deposits
if (getBuyer().getSecurityDeposit() == 0) {
BigInteger buyerSecurityDeposit = ((MoneroTxWallet) getBuyer().getDepositTx()).getIncomingAmount();
BigInteger sellerSecurityDeposit = ((MoneroTxWallet) getSeller().getDepositTx()).getIncomingAmount().subtract(HavenoUtils.coinToAtomicUnits(getAmount()));
getBuyer().setSecurityDeposit(HavenoUtils.atomicUnitsToCentineros(buyerSecurityDeposit));
getSeller().setSecurityDeposit(HavenoUtils.atomicUnitsToCentineros(sellerSecurityDeposit));
BigInteger sellerSecurityDeposit = ((MoneroTxWallet) getSeller().getDepositTx()).getIncomingAmount().subtract(getAmount());
getBuyer().setSecurityDeposit(buyerSecurityDeposit.longValueExact());
getSeller().setSecurityDeposit(sellerSecurityDeposit.longValueExact());
}
// set deposits published state

View File

@ -22,12 +22,13 @@ import bisq.core.offer.Offer;
import bisq.core.support.dispute.Dispute;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutPoint;
import org.bitcoinj.core.TransactionOutput;
import java.math.BigInteger;
import java.util.function.Consumer;
import lombok.Getter;
@ -130,7 +131,7 @@ public class TradeDataValidation {
// Check amount
TransactionOutput output = delayedPayoutTx.getOutput(0);
Offer offer = checkNotNull(trade.getOffer());
Coin msOutputAmount = offer.getBuyerSecurityDeposit()
BigInteger msOutputAmount = offer.getBuyerSecurityDeposit()
.add(offer.getSellerSecurityDeposit())
.add(checkNotNull(trade.getAmount()));

View File

@ -88,6 +88,8 @@ import javafx.collections.ObservableList;
import org.bouncycastle.crypto.params.KeyParameter;
import org.fxmisc.easybind.EasyBind;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
@ -505,11 +507,11 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
// get expected taker fee
Coin takerFee = HavenoUtils.getTakerFee(Coin.valueOf(offer.getOfferPayload().getAmount()));
BigInteger takerFee = HavenoUtils.getTakerFee(BigInteger.valueOf(offer.getOfferPayload().getAmount()));
// create arbitrator trade
trade = new ArbitratorTrade(offer,
Coin.valueOf(offer.getOfferPayload().getAmount()),
BigInteger.valueOf(offer.getOfferPayload().getAmount()),
takerFee,
offer.getOfferPayload().getPrice(),
xmrWalletService,
@ -577,12 +579,12 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
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()));
BigInteger takerFee = HavenoUtils.getTakerFee(BigInteger.valueOf(offer.getOfferPayload().getAmount()));
Trade trade;
if (offer.isBuyOffer())
trade = new BuyerAsMakerTrade(offer,
Coin.valueOf(offer.getOfferPayload().getAmount()),
BigInteger.valueOf(offer.getOfferPayload().getAmount()),
takerFee,
offer.getOfferPayload().getPrice(),
xmrWalletService,
@ -593,7 +595,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
request.getArbitratorNodeAddress());
else
trade = new SellerAsMakerTrade(offer,
Coin.valueOf(offer.getOfferPayload().getAmount()),
BigInteger.valueOf(offer.getOfferPayload().getAmount()),
takerFee,
offer.getOfferPayload().getPrice(),
xmrWalletService,
@ -742,9 +744,9 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
// First we check if offer is still available then we create the trade with the protocol
public void onTakeOffer(Coin amount,
Coin takerFee,
Coin fundsNeededForTrade,
public void onTakeOffer(BigInteger amount,
BigInteger takerFee,
BigInteger fundsNeededForTrade,
Offer offer,
String paymentAccountId,
boolean useSavingsWallet,
@ -786,7 +788,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
trade.getProcessModel().setTradeMessage(model.getTradeRequest());
trade.getProcessModel().setMakerSignature(model.getMakerSignature());
trade.getProcessModel().setUseSavingsWallet(useSavingsWallet);
trade.getProcessModel().setFundsNeededForTradeAsLong(fundsNeededForTrade.value);
trade.getProcessModel().setFundsNeededForTradeAsLong(fundsNeededForTrade.longValueExact());
trade.getMaker().setPubKeyRing(trade.getOffer().getPubKeyRing());
trade.getSelf().setPubKeyRing(model.getPubKeyRing());
trade.getSelf().setPaymentAccountId(paymentAccountId);

View File

@ -23,7 +23,6 @@ import bisq.common.crypto.PubKeyRing;
import bisq.common.crypto.Sig;
import bisq.common.taskrunner.TaskRunner;
import bisq.core.offer.Offer;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.trade.messages.DepositRequest;
import bisq.core.trade.messages.DepositResponse;
@ -37,8 +36,6 @@ import java.util.Arrays;
import java.util.Date;
import java.util.UUID;
import org.bitcoinj.core.Coin;
import lombok.extern.slf4j.Slf4j;
import monero.daemon.MoneroDaemon;
import monero.daemon.model.MoneroSubmitTxResult;
@ -79,9 +76,9 @@ public class ArbitratorProcessDepositRequest extends TradeTask {
Offer offer = trade.getOffer();
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());
BigInteger tradeFee = isFromTaker ? trade.getTakerFee() : trade.getMakerFee();
BigInteger sendAmount = isFromBuyer ? BigInteger.valueOf(0) : offer.getAmount();
BigInteger securityDeposit = isFromBuyer ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit();
String depositAddress = processModel.getMultisigAddress();
// verify deposit tx

View File

@ -20,14 +20,11 @@ package bisq.core.trade.protocol.tasks;
import bisq.common.taskrunner.TaskRunner;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.trade.messages.InitTradeRequest;
import bisq.core.trade.protocol.TradePeer;
import java.math.BigInteger;
import org.bitcoinj.core.Coin;
import lombok.extern.slf4j.Slf4j;
/**
@ -55,9 +52,9 @@ public class ArbitratorProcessReserveTx extends TradeTask {
// TODO (woodser): if signer online, should never be called by maker
// 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());
BigInteger tradeFee = isFromTaker ? trade.getTakerFee() : trade.getMakerFee();
BigInteger sendAmount = isFromBuyer ? BigInteger.valueOf(0) : offer.getAmount();
BigInteger securityDeposit = isFromBuyer ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit();
try {
trade.getXmrWalletService().verifyTradeTx(
tradeFee,

View File

@ -57,9 +57,9 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
processModel.getOfferId(),
request.getSenderNodeAddress(),
request.getPubKeyRing(),
trade.getAmount().value,
trade.getAmount().longValueExact(),
trade.getPrice().getValue(),
trade.getTakerFee().getValue(),
trade.getTakerFee().longValueExact(),
request.getAccountId(),
request.getPaymentAccountId(),
request.getPaymentMethodId(),

View File

@ -24,11 +24,8 @@ import bisq.core.trade.messages.InitTradeRequest;
import bisq.network.p2p.SendDirectMessageListener;
import bisq.common.app.Version;
import bisq.common.crypto.Sig;
import bisq.common.taskrunner.TaskRunner;
import com.google.common.base.Charsets;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
@ -59,9 +56,9 @@ public class MakerSendInitTradeRequest extends TradeTask {
offer.getId(),
processModel.getMyNodeAddress(),
processModel.getPubKeyRing(),
offer.getAmount().value,
offer.getAmount().longValueExact(),
trade.getPrice().getValue(),
offer.getMakerFee().value,
offer.getMakerFee().longValueExact(),
trade.getProcessModel().getAccountId(),
offer.getMakerPaymentAccountId(),
offer.getOfferPayload().getPaymentMethodId(),

View File

@ -27,7 +27,6 @@ import bisq.core.trade.messages.InitTradeRequest;
import bisq.core.trade.protocol.TradePeer;
import bisq.common.taskrunner.TaskRunner;
import org.bitcoinj.core.Coin;
import com.google.common.base.Charsets;
@ -38,6 +37,7 @@ import static bisq.core.util.Validator.nonEmptyStringOf;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.math.BigInteger;
import java.util.Date;
@Slf4j
@ -134,7 +134,7 @@ public class ProcessInitTradeRequest extends TradeTask {
// check trade amount
checkArgument(request.getTradeAmount() > 0);
trade.setAmount(Coin.valueOf(request.getTradeAmount()));
trade.setAmount(BigInteger.valueOf(request.getTradeAmount()));
// persist trade
processModel.getTradeManager().requestPersistence();

View File

@ -20,14 +20,11 @@ package bisq.core.trade.protocol.tasks;
import bisq.common.taskrunner.TaskRunner;
import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.offer.OfferDirection;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
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;
@ -43,9 +40,9 @@ public class TakerReserveTradeFunds extends TradeTask {
runInterceptHook();
// create reserve tx
BigInteger takerFee = HavenoUtils.coinToAtomicUnits(trade.getTakerFee());
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());
BigInteger takerFee = trade.getTakerFee();
BigInteger sendAmount = trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getOffer().getAmount() : BigInteger.valueOf(0);
BigInteger securityDeposit = 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, sendAmount, securityDeposit, returnAddress);

View File

@ -24,6 +24,7 @@ import bisq.core.monetary.Volume;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.offer.OfferPayload;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.util.JsonUtil;
import bisq.core.util.VolumeUtil;
@ -44,12 +45,12 @@ import bisq.common.util.Utilities;
import com.google.protobuf.ByteString;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.ExchangeRate;
import org.bitcoinj.utils.Fiat;
import com.google.common.base.Charsets;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@ -144,7 +145,7 @@ public final class TradeStatistics2 implements ProcessOncePersistableNetworkPayl
public TradeStatistics2(OfferPayload offerPayload,
Price tradePrice,
Coin tradeAmount,
BigInteger tradeAmount,
Date tradeDate,
String makerDepositTxId,
String takerDepositTxId,
@ -160,7 +161,7 @@ public final class TradeStatistics2 implements ProcessOncePersistableNetworkPayl
offerPayload.getMinAmount(),
offerPayload.getId(),
tradePrice.getValue(),
tradeAmount.value,
tradeAmount.longValueExact(),
tradeDate.getTime(),
makerDepositTxId,
takerDepositTxId,
@ -312,15 +313,15 @@ public final class TradeStatistics2 implements ProcessOncePersistableNetworkPayl
return baseCurrency.equals("XMR") ? counterCurrency : baseCurrency;
}
public Coin getTradeAmount() {
return Coin.valueOf(tradeAmount);
public BigInteger getTradeAmount() {
return BigInteger.valueOf(tradeAmount);
}
public Volume getTradeVolume() {
if (getPrice().getMonetary() instanceof Altcoin) {
return new Volume(new AltcoinExchangeRate((Altcoin) getPrice().getMonetary()).coinToAltcoin(getTradeAmount()));
return new Volume(new AltcoinExchangeRate((Altcoin) getPrice().getMonetary()).coinToAltcoin(HavenoUtils.atomicUnitsToCoin(getTradeAmount())));
} else {
Volume volume = new Volume(new ExchangeRate((Fiat) getPrice().getMonetary()).coinToFiat(getTradeAmount()));
Volume volume = new Volume(new ExchangeRate((Fiat) getPrice().getMonetary()).coinToFiat(HavenoUtils.atomicUnitsToCoin(getTradeAmount())));
return VolumeUtil.getRoundedFiatVolume(volume);
}
}

View File

@ -168,7 +168,7 @@ public class TradeStatisticsConverter {
byte[] hash = tradeStatistics2.getHash();
return new TradeStatistics3(tradeStatistics2.getCurrencyCode(),
tradeStatistics2.getPrice().getValue(),
tradeStatistics2.getTradeAmount().getValue(),
tradeStatistics2.getTradeAmount().longValueExact(),
tradeStatistics2.getOfferPaymentMethod(),
time,
mediator,

View File

@ -24,12 +24,12 @@ import bisq.core.monetary.Volume;
import bisq.common.util.MathUtils;
import org.bitcoinj.core.Coin;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import java.math.BigInteger;
import javax.annotation.concurrent.Immutable;
@Immutable
@ -65,12 +65,12 @@ public final class TradeStatisticsForJson {
primaryMarketTradeAmount = getTradeVolume() != null ?
getTradeVolume().getValue() :
0;
primaryMarketTradeVolume = getTradeAmount().getValue();
primaryMarketTradeVolume = getTradeAmount().longValueExact();
} else {
currencyPair = Res.getBaseCurrencyCode() + "/" + currency;
// we use precision 4 for fiat based price but on the markets api we use precision 8 so we scale up by 10000
primaryMarketTradePrice = (long) MathUtils.scaleUpByPowerOf10(tradePrice.getValue(), 4);
primaryMarketTradeAmount = getTradeAmount().getValue();
primaryMarketTradeAmount = getTradeAmount().longValueExact();
// we use precision 4 for fiat but on the markets api we use precision 8 so we scale up by 10000
primaryMarketTradeVolume = getTradeVolume() != null ?
(long) MathUtils.scaleUpByPowerOf10(getTradeVolume().getValue(), 4) :
@ -86,8 +86,8 @@ public final class TradeStatisticsForJson {
return Price.valueOf(currency, tradePrice);
}
public Coin getTradeAmount() {
return Coin.valueOf(tradeAmount);
public BigInteger getTradeAmount() {
return BigInteger.valueOf(tradeAmount);
}
public Volume getTradeVolume() {

View File

@ -22,6 +22,7 @@ import bisq.core.locale.Res;
import bisq.core.support.dispute.Dispute;
import bisq.core.support.dispute.mediation.MediationManager;
import bisq.core.support.dispute.refund.RefundManager;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.trade.txproof.AssetTxProofRequestsPerTrade;
import bisq.core.trade.txproof.AssetTxProofResult;
@ -31,13 +32,12 @@ import bisq.network.Socks5ProxyProvider;
import bisq.common.handlers.FaultHandler;
import org.bitcoinj.core.Coin;
import javafx.beans.value.ChangeListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -327,11 +327,11 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade {
///////////////////////////////////////////////////////////////////////////////////////////
private boolean isTradeAmountAboveLimit(Trade trade) {
Coin tradeAmount = trade.getAmount();
Coin tradeLimit = Coin.valueOf(autoConfirmSettings.getTradeLimit());
if (tradeAmount != null && tradeAmount.isGreaterThan(tradeLimit)) {
BigInteger tradeAmount = trade.getAmount();
BigInteger tradeLimit = BigInteger.valueOf(autoConfirmSettings.getTradeLimit());
if (tradeAmount != null && tradeAmount.compareTo(tradeLimit) > 0) {
log.warn("Trade amount {} is higher than limit from auto-conf setting {}.",
tradeAmount.toFriendlyString(), tradeLimit.toFriendlyString());
HavenoUtils.formatToXmrWithCode(tradeAmount), HavenoUtils.formatToXmrWithCode(tradeLimit));
return true;
}
return false;

View File

@ -20,6 +20,7 @@ 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;
@ -29,6 +30,9 @@ import com.google.common.annotations.VisibleForTesting;
import static bisq.core.util.VolumeUtil.getAdjustedFiatVolume;
import static com.google.common.base.Preconditions.checkArgument;
import java.math.BigDecimal;
import java.math.BigInteger;
public class CoinUtil {
@ -49,8 +53,8 @@ public class CoinUtil {
* @param value Btc amount to be converted to percent value. E.g. 0.01 BTC is 1% (of 1 BTC)
* @return The percentage value as double (e.g. 1% is 0.01)
*/
public static double getAsPercentPerBtc(Coin value) {
return getAsPercentPerBtc(value, Coin.COIN);
public static double getAsPercentPerBtc(BigInteger value) {
return getAsPercentPerBtc(value, HavenoUtils.xmrToAtomicUnits(1.0));
}
/**
@ -60,20 +64,20 @@ public class CoinUtil {
*
* @return The percentage value as double (e.g. 1% is 0.01)
*/
public static double getAsPercentPerBtc(Coin part, Coin total) {
double asDouble = part != null ? (double) part.value : 0;
double btcAsDouble = total != null ? (double) total.value : 1;
return MathUtils.roundDouble(asDouble / btcAsDouble, 4);
public static double getAsPercentPerBtc(BigInteger part, BigInteger total) {
BigDecimal partDecimal = part == null ? BigDecimal.valueOf(0) : new BigDecimal(part);
BigDecimal totalDecimal = total == null ? BigDecimal.valueOf(1) : new BigDecimal(total);
return MathUtils.roundDouble(partDecimal.divide(totalDecimal).doubleValue(), 4);
}
/**
* @param percent The percentage value as double (e.g. 1% is 0.01)
* @param amount The amount as Coin for the percentage calculation
* @return The percentage as Coin (e.g. 1% of 1 BTC is 0.01 BTC)
* @param amount The amount as atomic units for the percentage calculation
* @return The percentage as atomic units (e.g. 1% of 1 BTC is 0.01 BTC)
*/
public static Coin getPercentOfAmountAsCoin(double percent, Coin amount) {
double amountAsDouble = amount != null ? (double) amount.value : 0;
return Coin.valueOf(Math.round(percent * amountAsDouble));
public static BigInteger getPercentOfAmount(double percent, BigInteger amount) {
if (amount == null) amount = BigInteger.valueOf(0);
return BigDecimal.valueOf(percent).multiply(new BigDecimal(amount)).toBigInteger();
}
/**
@ -85,11 +89,11 @@ public class CoinUtil {
* @param maxTradeLimit The max. trade limit of the users account, in satoshis.
* @return The adjusted amount
*/
public static Coin getRoundedFiatAmount(Coin amount, Price price, long maxTradeLimit) {
public static BigInteger getRoundedFiatAmount(BigInteger amount, Price price, long maxTradeLimit) {
return getAdjustedAmount(amount, price, maxTradeLimit, 1);
}
public static Coin getAdjustedAmountForHalCash(Coin amount, Price price, long maxTradeLimit) {
public static BigInteger getAdjustedAmountForHalCash(BigInteger amount, Price price, long maxTradeLimit) {
return getAdjustedAmount(amount, price, maxTradeLimit, 10);
}
@ -97,7 +101,7 @@ public class CoinUtil {
* Calculate the possibly adjusted amount for {@code amount}, taking into account the
* {@code price} and {@code maxTradeLimit} and {@code factor}.
*
* @param amount Bitcoin amount which is a candidate for getting rounded.
* @param amount amount which is a candidate for getting rounded.
* @param price Price used in relation to that amount.
* @param maxTradeLimit The max. trade limit of the users account, in satoshis.
* @param factor The factor used for rounding. E.g. 1 means rounded to units of
@ -105,10 +109,10 @@ public class CoinUtil {
* @return The adjusted amount
*/
@VisibleForTesting
static Coin getAdjustedAmount(Coin amount, Price price, long maxTradeLimit, int factor) {
static BigInteger getAdjustedAmount(BigInteger amount, Price price, long maxTradeLimit, int factor) {
checkArgument(
amount.getValue() >= 10_000,
"amount needs to be above minimum of 10k satoshis"
amount.longValueExact() >= HavenoUtils.xmrToAtomicUnits(0.0001).longValueExact(),
"amount needs to be above minimum of 0.0001 xmr" // TODO: update amount for XMR
);
checkArgument(
factor > 0,
@ -118,17 +122,17 @@ public class CoinUtil {
// 10 EUR in case of HalCash.
Volume smallestUnitForVolume = Volume.parse(String.valueOf(factor), price.getCurrencyCode());
if (smallestUnitForVolume.getValue() <= 0)
return Coin.ZERO;
return BigInteger.valueOf(0);
Coin smallestUnitForAmount = price.getAmountByVolume(smallestUnitForVolume);
long minTradeAmount = Restrictions.getMinTradeAmount().value;
BigInteger smallestUnitForAmount = price.getAmountByVolume(smallestUnitForVolume);
long minTradeAmount = Restrictions.getMinTradeAmount().longValueExact();
// We use 10 000 satoshi as min allowed amount
checkArgument(
minTradeAmount >= 10_000,
"MinTradeAmount must be at least 10k satoshis"
minTradeAmount >= HavenoUtils.xmrToAtomicUnits(0.0001).longValueExact(),
"MinTradeAmount must be at least 0.0001 xmr" // TODO: update amount for XMR
);
smallestUnitForAmount = Coin.valueOf(Math.max(minTradeAmount, smallestUnitForAmount.value));
smallestUnitForAmount = BigInteger.valueOf(Math.max(minTradeAmount, smallestUnitForAmount.longValueExact()));
// We don't allow smaller amount values than smallestUnitForAmount
boolean useSmallestUnitForAmount = amount.compareTo(smallestUnitForAmount) < 0;
@ -137,21 +141,22 @@ public class CoinUtil {
? getAdjustedFiatVolume(price.getVolumeByAmount(smallestUnitForAmount), factor)
: getAdjustedFiatVolume(price.getVolumeByAmount(amount), factor);
if (volume.getValue() <= 0)
return Coin.ZERO;
return BigInteger.valueOf(0);
// From that adjusted volume we calculate back the amount. It might be a bit different as
// the amount used as input before due rounding.
Coin amountByVolume = price.getAmountByVolume(volume);
BigInteger amountByVolume = price.getAmountByVolume(volume);
// For the amount we allow only 4 decimal places
long adjustedAmount = Math.round((double) amountByVolume.value / 10000d) * 10000;
// TODO: remove rounding for XMR?
long adjustedAmount = HavenoUtils.centinerosToAtomicUnits(Math.round((double) HavenoUtils.atomicUnitsToCentineros(amountByVolume) / 10000d) * 10000).longValueExact();
// If we are above our trade limit we reduce the amount by the smallestUnitForAmount
while (adjustedAmount > maxTradeLimit) {
adjustedAmount -= smallestUnitForAmount.value;
adjustedAmount -= smallestUnitForAmount.longValueExact();
}
adjustedAmount = Math.max(minTradeAmount, adjustedAmount);
adjustedAmount = Math.min(maxTradeLimit, adjustedAmount);
return Coin.valueOf(adjustedAmount);
return BigInteger.valueOf(adjustedAmount);
}
}

View File

@ -31,11 +31,11 @@ import bisq.common.crypto.KeyRing;
import bisq.common.crypto.Sig;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import com.google.common.base.Charsets;
import java.math.BigInteger;
import java.security.KeyPair;
import java.time.Instant;
@ -354,7 +354,7 @@ public class SignedWitnessServiceTest {
when(keyRing.getSignatureKeyPair()).thenReturn(signerKeyPair);
AccountAgeWitness accountAgeWitness = new AccountAgeWitness(account1DataHash, accountCreationTime);
signedWitnessService.signAndPublishAccountAgeWitness(Coin.ZERO, accountAgeWitness, peerKeyPair.getPublic());
signedWitnessService.signAndPublishAccountAgeWitness(BigInteger.valueOf(0), accountAgeWitness, peerKeyPair.getPublic());
verify(p2pService, never()).addPersistableNetworkPayload(any(PersistableNetworkPayload.class), anyBoolean());
}

View File

@ -32,7 +32,7 @@ import bisq.core.support.dispute.DisputeResult;
import bisq.core.support.dispute.arbitration.TraderDataItem;
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
import bisq.core.trade.Contract;
import bisq.core.trade.HavenoUtils;
import bisq.network.p2p.P2PService;
import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService;
@ -44,7 +44,6 @@ import bisq.common.crypto.PubKeyRing;
import bisq.common.crypto.Sig;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import java.security.KeyPair;
@ -52,7 +51,6 @@ import java.security.PublicKey;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
@ -227,7 +225,7 @@ public class AccountAgeWitnessServiceTest {
when(chargeBackRisk.hasChargebackRisk(any(), any())).thenReturn(true);
when(contract.getPaymentMethodId()).thenReturn(PaymentMethod.SEPA_ID);
when(contract.getTradeAmount()).thenReturn(Coin.parseCoin("0.01"));
when(contract.getTradeAmount()).thenReturn(HavenoUtils.xmrToAtomicUnits(0.01));
when(contract.getBuyerPubKeyRing()).thenReturn(buyerPubKeyRing);
when(contract.getSellerPubKeyRing()).thenReturn(sellerPubKeyRing);
when(contract.getOfferPayload()).thenReturn(mock(OfferPayload.class));
@ -357,7 +355,7 @@ public class AccountAgeWitnessServiceTest {
signerKeyRing.getSignatureKeyPair().getPublic().getEncoded(),
witnessOwnerPubKey.getEncoded(),
time,
SignedWitnessService.MINIMUM_TRADE_AMOUNT_FOR_SIGNING.value);
SignedWitnessService.MINIMUM_TRADE_AMOUNT_FOR_SIGNING.longValueExact());
signedWitnessService.addToMap(signedWitness);
}

View File

@ -4,8 +4,7 @@ import bisq.core.account.witness.AccountAgeWitness;
import bisq.core.support.dispute.arbitration.TraderDataItem;
import bisq.core.payment.payload.PaymentAccountPayload;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import java.security.PublicKey;
import org.junit.Before;
@ -44,11 +43,11 @@ public class TraderDataItemTest {
public void setup() {
accountAgeWitness1 = new AccountAgeWitness(hash1, 123);
accountAgeWitness2 = new AccountAgeWitness(hash2, 124);
traderDataItem1 = new TraderDataItem(mock(PaymentAccountPayload.class), accountAgeWitness1, Coin.valueOf(546),
traderDataItem1 = new TraderDataItem(mock(PaymentAccountPayload.class), accountAgeWitness1, BigInteger.valueOf(546),
mock(PublicKey.class));
traderDataItem2 = new TraderDataItem(mock(PaymentAccountPayload.class), accountAgeWitness1, Coin.valueOf(547),
traderDataItem2 = new TraderDataItem(mock(PaymentAccountPayload.class), accountAgeWitness1, BigInteger.valueOf(547),
mock(PublicKey.class));
traderDataItem3 = new TraderDataItem(mock(PaymentAccountPayload.class), accountAgeWitness2, Coin.valueOf(548),
traderDataItem3 = new TraderDataItem(mock(PaymentAccountPayload.class), accountAgeWitness2, BigInteger.valueOf(548),
mock(PublicKey.class));
}

View File

@ -27,14 +27,16 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.math.BigInteger;
public class CoinUtilTest {
@Test
public void testGetFeePerBtc() {
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")));
assertEquals(HavenoUtils.xmrToAtomicUnits(1), HavenoUtils.getFeePerXmr(HavenoUtils.xmrToAtomicUnits(1), HavenoUtils.xmrToAtomicUnits(1)));
assertEquals(HavenoUtils.xmrToAtomicUnits(0.1), HavenoUtils.getFeePerXmr(HavenoUtils.xmrToAtomicUnits(0.1), HavenoUtils.xmrToAtomicUnits(1)));
assertEquals(HavenoUtils.xmrToAtomicUnits(0.01), HavenoUtils.getFeePerXmr(HavenoUtils.xmrToAtomicUnits(0.1), HavenoUtils.xmrToAtomicUnits(0.1)));
assertEquals(HavenoUtils.xmrToAtomicUnits(0.015), HavenoUtils.getFeePerXmr(HavenoUtils.xmrToAtomicUnits(0.3), HavenoUtils.xmrToAtomicUnits(0.05)));
}
@Test
@ -57,52 +59,52 @@ public class CoinUtilTest {
@Test
public void testGetAdjustedAmount() {
Coin result = CoinUtil.getAdjustedAmount(
Coin.valueOf(100_000),
BigInteger result = CoinUtil.getAdjustedAmount(
HavenoUtils.xmrToAtomicUnits(0.001),
Price.valueOf("USD", 1000_0000),
20_000_000,
HavenoUtils.xmrToAtomicUnits(0.2).longValueExact(),
1);
assertEquals(
"Minimum trade amount allowed should be adjusted to the smallest trade allowed.",
"0.001 BTC",
result.toFriendlyString()
"0.001 XMR",
HavenoUtils.formatToXmrWithCode(result)
);
try {
CoinUtil.getAdjustedAmount(
Coin.ZERO,
BigInteger.valueOf(0),
Price.valueOf("USD", 1000_0000),
20_000_000,
HavenoUtils.xmrToAtomicUnits(0.2).longValueExact(),
1);
fail("Expected IllegalArgumentException to be thrown when amount is too low.");
} catch (IllegalArgumentException iae) {
assertEquals(
"Unexpected exception message.",
"amount needs to be above minimum of 10k satoshis",
"amount needs to be above minimum of 0.0001 xmr",
iae.getMessage()
);
}
result = CoinUtil.getAdjustedAmount(
Coin.valueOf(1_000_000),
HavenoUtils.xmrToAtomicUnits(0.01),
Price.valueOf("USD", 1000_0000),
20_000_000,
HavenoUtils.xmrToAtomicUnits(0.2).longValueExact(),
1);
assertEquals(
"Minimum allowed trade amount should not be adjusted.",
"0.01 BTC",
result.toFriendlyString()
"0.01 XMR",
HavenoUtils.formatToXmrWithCode(result)
);
result = CoinUtil.getAdjustedAmount(
Coin.valueOf(100_000),
HavenoUtils.xmrToAtomicUnits(0.001),
Price.valueOf("USD", 1000_0000),
1_000_000,
HavenoUtils.xmrToAtomicUnits(0.1).longValueExact(),
1);
assertEquals(
"Minimum trade amount allowed should respect maxTradeLimit and factor, if possible.",
"0.001 BTC",
result.toFriendlyString()
"0.001 XMR",
HavenoUtils.formatToXmrWithCode(result)
);
// TODO(chirhonul): The following seems like it should raise an exception or otherwise fail.
@ -111,14 +113,14 @@ public class CoinUtilTest {
// 0.05 USD worth, which is below the factor of 1 USD, but does respect the maxTradeLimit.
// Basically the given constraints (maxTradeLimit vs factor) are impossible to both fulfill..
result = CoinUtil.getAdjustedAmount(
Coin.valueOf(100_000),
HavenoUtils.xmrToAtomicUnits(0.001),
Price.valueOf("USD", 1000_0000),
5_000,
HavenoUtils.xmrToAtomicUnits(0.00005).longValueExact(),
1);
assertEquals(
"Minimum trade amount allowed with low maxTradeLimit should still respect that limit, even if result does not respect the factor specified.",
"0.00005 BTC",
result.toFriendlyString()
"0.00005 XMR",
HavenoUtils.formatToXmrWithCode(result)
);
}
}

View File

@ -3,7 +3,6 @@ package bisq.daemon.grpc;
import bisq.core.api.CoreApi;
import bisq.core.support.dispute.Attachment;
import bisq.core.support.dispute.DisputeResult;
import bisq.core.trade.HavenoUtils;
import bisq.common.proto.ProtoUtil;
@ -108,9 +107,7 @@ public class GrpcDisputesService extends DisputesImplBase {
try {
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 = HavenoUtils.atomicUnitsToCentineros(req.getCustomPayoutAmount());
coreApi.resolveDispute(req.getTradeId(), winner, reason, req.getSummaryNotes(), customPayoutAmount);
coreApi.resolveDispute(req.getTradeId(), winner, reason, req.getSummaryNotes(), req.getCustomPayoutAmount());
var reply = ResolveDisputeReply.newBuilder().build();
responseObserver.onNext(reply);
responseObserver.onCompleted();

View File

@ -21,7 +21,6 @@ import bisq.core.api.CoreApi;
import bisq.core.api.model.OfferInfo;
import bisq.core.offer.Offer;
import bisq.core.offer.OpenOffer;
import bisq.core.trade.HavenoUtils;
import bisq.proto.grpc.CancelOfferReply;
import bisq.proto.grpc.CancelOfferRequest;
import bisq.proto.grpc.PostOfferReply;
@ -154,8 +153,8 @@ class GrpcOffersService extends OffersImplBase {
req.getPrice(),
req.getUseMarketBasedPrice(),
req.getMarketPriceMarginPct(),
HavenoUtils.atomicUnitsToCentineros(req.getAmount()), // scale atomic unit to centineros for consistency TODO switch base to atomic units?
HavenoUtils.atomicUnitsToCentineros(req.getMinAmount()),
req.getAmount(),
req.getMinAmount(),
req.getBuyerSecurityDepositPct(),
req.getTriggerPrice(),
req.getPaymentAccountId(),

View File

@ -37,8 +37,6 @@ import bisq.proto.grpc.SendChatMessageReply;
import bisq.proto.grpc.SendChatMessageRequest;
import bisq.proto.grpc.TakeOfferReply;
import bisq.proto.grpc.TakeOfferRequest;
import bisq.proto.grpc.WithdrawFundsReply;
import bisq.proto.grpc.WithdrawFundsRequest;
import io.grpc.ServerInterceptor;
import io.grpc.stub.StreamObserver;
@ -191,19 +189,6 @@ class GrpcTradesService extends TradesImplBase {
}
}
@Override
public void withdrawFunds(WithdrawFundsRequest req,
StreamObserver<WithdrawFundsReply> responseObserver) {
try {
coreApi.withdrawFunds(req.getTradeId(), req.getAddress(), req.getMemo());
var reply = WithdrawFundsReply.newBuilder().build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
} catch (Throwable cause) {
exceptionHandler.handleException(log, cause, responseObserver);
}
}
@Override
public void getChatMessages(GetChatMessagesRequest req,
StreamObserver<GetChatMessagesReply> responseObserver) {

View File

@ -25,7 +25,7 @@ import bisq.desktop.util.GUIUtil;
import bisq.core.account.sign.SignedWitnessService;
import bisq.core.locale.Res;
import bisq.core.offer.OfferRestrictions;
import bisq.core.util.coin.CoinFormatter;
import bisq.core.trade.HavenoUtils;
import bisq.common.UserThread;
@ -54,14 +54,13 @@ public class AccountStatusTooltipLabel extends AutoTooltipLabel {
private PopOver popOver;
private boolean keepPopOverVisible = false;
public AccountStatusTooltipLabel(OfferBookListItem.WitnessAgeData witnessAgeData,
CoinFormatter formatter) {
public AccountStatusTooltipLabel(OfferBookListItem.WitnessAgeData witnessAgeData) {
super(witnessAgeData.getDisplayString());
this.witnessAgeData = witnessAgeData;
this.textIcon = FormBuilder.getIcon(witnessAgeData.getIcon());
this.popupTitle = witnessAgeData.isLimitLifted()
? Res.get("offerbook.timeSinceSigning.tooltip.accountLimitLifted")
: Res.get("offerbook.timeSinceSigning.tooltip.accountLimit", formatter.formatCoinWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT));
: Res.get("offerbook.timeSinceSigning.tooltip.accountLimit", HavenoUtils.formatToXmrWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT));
positionAndActivateIcon();
}

View File

@ -21,11 +21,9 @@ import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.GUIUtil;
import bisq.core.locale.Res;
import bisq.core.trade.HavenoUtils;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
@ -40,6 +38,7 @@ import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import java.math.BigInteger;
import java.net.URI;
import org.slf4j.Logger;
@ -50,7 +49,7 @@ public class AddressTextField extends AnchorPane {
private final StringProperty address = new SimpleStringProperty();
private final StringProperty paymentLabel = new SimpleStringProperty();
private final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>(Coin.ZERO);
private final ObjectProperty<BigInteger> amount = new SimpleObjectProperty<>(BigInteger.ZERO);
private boolean wasPrimaryButtonDown;
@ -128,16 +127,16 @@ public class AddressTextField extends AnchorPane {
return address;
}
public Coin getAmountAsCoin() {
return amountAsCoin.get();
public BigInteger getAmount() {
return amount.get();
}
public ObjectProperty<Coin> amountAsCoinProperty() {
return amountAsCoin;
public ObjectProperty<BigInteger> amountAsProperty() {
return amount;
}
public void setAmountAsCoin(Coin amountAsCoin) {
this.amountAsCoin.set(amountAsCoin);
public void setAmount(BigInteger amount) {
this.amount.set(amount);
}
public String getPaymentLabel() {
@ -158,11 +157,11 @@ public class AddressTextField extends AnchorPane {
///////////////////////////////////////////////////////////////////////////////////////////
private String getBitcoinURI() {
if (amountAsCoin.get().isNegative()) {
if (amount.get().compareTo(BigInteger.valueOf(0)) < 0) {
log.warn("Amount must not be negative");
setAmountAsCoin(Coin.ZERO);
setAmount(BigInteger.valueOf(0));
}
return GUIUtil.getBitcoinURI(address.get(), amountAsCoin.get(),
return GUIUtil.getBitcoinURI(address.get(), HavenoUtils.atomicUnitsToCoin(amount.get()),
paymentLabel.get());
}
}

View File

@ -17,10 +17,9 @@
package bisq.desktop.components;
import bisq.core.trade.HavenoUtils;
import bisq.core.util.coin.CoinFormatter;
import org.bitcoinj.core.Coin;
import com.jfoenix.controls.JFXTextField;
import javafx.scene.effect.BlurType;
@ -29,17 +28,19 @@ import javafx.scene.effect.Effect;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import java.math.BigInteger;
import javax.annotation.Nullable;
public class BalanceTextField extends AnchorPane {
private Coin targetAmount;
private BigInteger targetAmount;
private final JFXTextField textField;
private final Effect fundedEffect = new DropShadow(BlurType.THREE_PASS_BOX, Color.GREEN, 4, 0.0, 0, 0);
private final Effect notFundedEffect = new DropShadow(BlurType.THREE_PASS_BOX, Color.ORANGERED, 4, 0.0, 0, 0);
private CoinFormatter formatter;
@Nullable
private Coin balance;
private BigInteger balance;
///////////////////////////////////////////////////////////////////////////////////////////
@ -64,13 +65,13 @@ public class BalanceTextField extends AnchorPane {
this.formatter = formatter;
}
public void setBalance(Coin balance) {
public void setBalance(BigInteger balance) {
this.balance = balance;
updateBalance(balance);
}
public void setTargetAmount(Coin targetAmount) {
public void setTargetAmount(BigInteger targetAmount) {
this.targetAmount = targetAmount;
if (this.balance != null)
@ -81,9 +82,9 @@ public class BalanceTextField extends AnchorPane {
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
private void updateBalance(Coin balance) {
private void updateBalance(BigInteger balance) {
if (formatter != null)
textField.setText(formatter.formatCoinWithCode(balance));
textField.setText(HavenoUtils.formatToXmrWithCode(balance));
//TODO: replace with new validation logic
// if (targetAmount != null) {

View File

@ -238,7 +238,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
Tuple2<Label, VBox> availableBalanceBox = getBalanceBox(Res.get("mainView.balance.available"));
availableBalanceBox.first.textProperty().bind(model.getAvailableBalance());
availableBalanceBox.first.setPrefWidth(100);
availableBalanceBox.first.setPrefWidth(105);
availableBalanceBox.first.tooltipProperty().bind(new ObjectBinding<>() {
{
bind(model.getAvailableBalance());

View File

@ -115,6 +115,7 @@ import bisq.core.payment.validation.TransferwiseValidator;
import bisq.core.payment.validation.USPostalMoneyOrderValidator;
import bisq.core.payment.validation.UpholdValidator;
import bisq.core.payment.validation.WeChatPayValidator;
import bisq.core.trade.HavenoUtils;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
@ -123,8 +124,6 @@ import bisq.common.util.Tuple2;
import bisq.common.util.Tuple3;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javax.inject.Named;
@ -142,6 +141,7 @@ import javafx.collections.ObservableList;
import javafx.util.StringConverter;
import java.math.BigInteger;
import java.util.List;
import java.util.stream.Collectors;
@ -257,9 +257,9 @@ public class FiatAccountsView extends PaymentAccountsView<GridPane, FiatAccounts
///////////////////////////////////////////////////////////////////////////////////////////
private void onSaveNewAccount(PaymentAccount paymentAccount) {
Coin maxTradeLimitAsCoin = paymentAccount.getPaymentMethod().getMaxTradeLimitAsCoin("USD");
Coin maxTradeLimitSecondMonth = maxTradeLimitAsCoin.divide(2L);
Coin maxTradeLimitFirstMonth = maxTradeLimitAsCoin.divide(4L);
BigInteger maxTradeLimit = paymentAccount.getPaymentMethod().getMaxTradeLimit("USD");
BigInteger maxTradeLimitSecondMonth = maxTradeLimit.divide(BigInteger.valueOf(2L));
BigInteger maxTradeLimitFirstMonth = maxTradeLimit.divide(BigInteger.valueOf(4L));
if (paymentAccount instanceof F2FAccount) {
new Popup().information(Res.get("payment.f2f.info"))
.width(700)
@ -287,17 +287,17 @@ public class FiatAccountsView extends PaymentAccountsView<GridPane, FiatAccounts
} else {
String limitsInfoKey = "payment.limits.info";
String initialLimit = formatter.formatCoinWithCode(maxTradeLimitFirstMonth);
String initialLimit = HavenoUtils.formatToXmrWithCode(maxTradeLimitFirstMonth);
if (PaymentMethod.hasChargebackRisk(paymentAccount.getPaymentMethod(), paymentAccount.getTradeCurrencies())) {
limitsInfoKey = "payment.limits.info.withSigning";
initialLimit = formatter.formatCoinWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT);
initialLimit = HavenoUtils.formatToXmrWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT);
}
new Popup().information(Res.get(limitsInfoKey,
initialLimit,
formatter.formatCoinWithCode(maxTradeLimitSecondMonth),
formatter.formatCoinWithCode(maxTradeLimitAsCoin)))
HavenoUtils.formatToXmrWithCode(maxTradeLimitSecondMonth),
HavenoUtils.formatToXmrWithCode(maxTradeLimit)))
.width(700)
.closeButtonText(Res.get("shared.cancel"))
.actionButtonText(Res.get("shared.iUnderstand"))

View File

@ -37,14 +37,13 @@ import monero.daemon.model.MoneroTx;
import monero.wallet.model.MoneroTransferQuery;
import monero.wallet.model.MoneroTxQuery;
import monero.wallet.model.MoneroTxWallet;
import org.bitcoinj.core.Coin;
@Slf4j
class DepositListItem {
private final StringProperty balance = new SimpleStringProperty();
private final XmrAddressEntry addressEntry;
private final XmrWalletService xmrWalletService;
private Coin balanceAsCoin;
private BigInteger balanceAsBI;
private String usage = "-";
private XmrBalanceListener balanceListener;
private int numTxOutputs = 0;
@ -66,15 +65,15 @@ class DepositListItem {
balanceListener = new XmrBalanceListener(addressEntry.getSubaddressIndex()) {
@Override
public void onBalanceChanged(BigInteger balance) {
DepositListItem.this.balanceAsCoin = HavenoUtils.atomicUnitsToCoin(balance);
DepositListItem.this.balance.set(formatter.formatCoin(balanceAsCoin));
DepositListItem.this.balanceAsBI = balance;
DepositListItem.this.balance.set(HavenoUtils.formatToXmr(balanceAsBI));
updateUsage(addressEntry.getSubaddressIndex(), null);
}
};
xmrWalletService.addBalanceListener(balanceListener);
balanceAsCoin = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex());
balance.set(formatter.formatCoin(balanceAsCoin));
balanceAsBI = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex());
balance.set(HavenoUtils.formatToXmr(balanceAsBI));
updateUsage(addressEntry.getSubaddressIndex(), cachedTxs);
@ -124,8 +123,8 @@ class DepositListItem {
return balance.get();
}
public Coin getBalanceAsCoin() {
return balanceAsCoin;
public BigInteger getBalanceAsBI() {
return balanceAsBI;
}
public int getNumTxOutputs() {

View File

@ -157,7 +157,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
setConfidenceColumnCellFactory();
addressColumn.setComparator(Comparator.comparing(DepositListItem::getAddressString));
balanceColumn.setComparator(Comparator.comparing(DepositListItem::getBalanceAsCoin));
balanceColumn.setComparator(Comparator.comparing(DepositListItem::getBalanceAsBI));
confirmationsColumn.setComparator(Comparator.comparingLong(o -> o.getNumConfirmationsSinceFirstUsed()));
usageColumn.setComparator(Comparator.comparingInt(DepositListItem::getNumTxOutputs));
tableView.getSortOrder().add(usageColumn);
@ -237,7 +237,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
xmrWalletService.addBalanceListener(balanceListener);
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
addressTextField.setAmountAsCoin(ParsingUtils.parseToCoin(t, formatter));
addressTextField.setAmount(HavenoUtils.parseXmr(t));
updateQRCode();
});
@ -306,7 +306,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
.forEach(e -> observableList.add(new DepositListItem(e, xmrWalletService, formatter, incomingTxs)));
}
private Coin getAmountAsCoin() {
private Coin getAmount() {
return ParsingUtils.parseToCoin(amountTextField.getText(), formatter);
}
@ -314,7 +314,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
private String getPaymentUri() {
return xmrWalletService.getWallet().getPaymentUri(new MoneroTxConfig()
.setAddress(addressTextField.getAddress())
.setAmount(HavenoUtils.coinToAtomicUnits(getAmountAsCoin()))
.setAmount(HavenoUtils.coinToAtomicUnits(getAmount()))
.setNote(paymentLabelString));
}

View File

@ -51,8 +51,6 @@ public class TransactionListItemFactory {
TransactionsListItem create(MoneroTxWallet transaction, @Nullable TransactionAwareTradable tradable) {
return new TransactionsListItem(transaction,
xmrWalletService,
tradable,
formatter,
preferences.getIgnoreDustThreshold());
tradable);
}
}

View File

@ -24,7 +24,6 @@ import bisq.core.offer.OpenOffer;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Tradable;
import bisq.core.trade.Trade;
import bisq.core.util.coin.CoinFormatter;
import bisq.desktop.components.indicator.TxConfidenceIndicator;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.GUIUtil;
@ -41,11 +40,9 @@ import monero.wallet.model.MoneroIncomingTransfer;
import monero.wallet.model.MoneroOutgoingTransfer;
import monero.wallet.model.MoneroTxWallet;
import monero.wallet.model.MoneroWalletListener;
import org.bitcoinj.core.Coin;
@Slf4j
class TransactionsListItem {
private final CoinFormatter formatter;
private String dateString;
private final Date date;
private final String txId;
@ -56,11 +53,10 @@ class TransactionsListItem {
private String direction = "";
private boolean received;
private boolean detailsAvailable;
private Coin amountAsCoin = Coin.ZERO;
private BigInteger amount = BigInteger.valueOf(0);
private String memo = "";
private long confirmations = 0;
@Getter
private final boolean isDustAttackTx;
private boolean initialTxConfidenceVisibility = true;
private final Supplier<LazyFields> lazyFieldsSupplier;
@ -77,25 +73,20 @@ class TransactionsListItem {
TransactionsListItem() {
date = null;
txId = null;
formatter = null;
isDustAttackTx = false;
lazyFieldsSupplier = null;
}
TransactionsListItem(MoneroTxWallet tx,
XmrWalletService xmrWalletService,
TransactionAwareTradable transactionAwareTradable,
CoinFormatter formatter,
long ignoreDustThreshold) {
this.formatter = formatter;
TransactionAwareTradable transactionAwareTradable) {
this.memo = tx.getNote();
this.txId = tx.getHash();
Optional<Tradable> optionalTradable = Optional.ofNullable(transactionAwareTradable)
.map(TransactionAwareTradable::asTradable);
Coin valueSentToMe = HavenoUtils.atomicUnitsToCoin(tx.getIncomingAmount() == null ? new BigInteger("0") : tx.getIncomingAmount());
Coin valueSentFromMe = HavenoUtils.atomicUnitsToCoin(tx.getOutgoingAmount() == null ? new BigInteger("0") : tx.getOutgoingAmount());
BigInteger valueSentToMe = tx.getIncomingAmount() == null ? new BigInteger("0") : tx.getIncomingAmount();
BigInteger valueSentFromMe = tx.getOutgoingAmount() == null ? new BigInteger("0") : tx.getOutgoingAmount();
if (tx.getTransfers().get(0).isIncoming()) {
addressString = ((MoneroIncomingTransfer) tx.getTransfers().get(0)).getAddress();
@ -105,12 +96,12 @@ class TransactionsListItem {
else addressString = "unavailable";
}
if (valueSentFromMe.isZero()) {
amountAsCoin = valueSentToMe;
if (valueSentFromMe.compareTo(BigInteger.valueOf(0)) == 0) {
amount = valueSentToMe;
direction = Res.get("funds.tx.direction.receivedWith");
received = true;
} else {
amountAsCoin = valueSentFromMe.multiply(-1);
amount = valueSentFromMe.multiply(BigInteger.valueOf(-1));
received = false;
direction = Res.get("funds.tx.direction.sentTo");
}
@ -134,13 +125,13 @@ class TransactionsListItem {
} else if (trade.getPayoutTxId() != null &&
trade.getPayoutTxId().equals(txId)) {
details = Res.get("funds.tx.multiSigPayout", tradeId);
if (amountAsCoin.isZero()) {
if (amount.compareTo(BigInteger.valueOf(0)) == 0) {
initialTxConfidenceVisibility = false;
}
} else {
Trade.DisputeState disputeState = trade.getDisputeState();
if (disputeState == Trade.DisputeState.DISPUTE_CLOSED) {
if (valueSentToMe.isPositive()) {
if (valueSentToMe.compareTo(BigInteger.valueOf(0)) > 0) {
details = Res.get("funds.tx.disputePayout", tradeId);
} else {
details = Res.get("funds.tx.disputeLost", tradeId);
@ -148,7 +139,7 @@ class TransactionsListItem {
} else if (disputeState == Trade.DisputeState.REFUND_REQUEST_CLOSED ||
disputeState == Trade.DisputeState.REFUND_REQUESTED ||
disputeState == Trade.DisputeState.REFUND_REQUEST_STARTED_BY_PEER) {
if (valueSentToMe.isPositive()) {
if (valueSentToMe.compareTo(BigInteger.valueOf(0)) > 0) {
details = Res.get("funds.tx.refund", tradeId);
} else {
// We have spent the deposit tx outputs to the Bisq donation address to enable
@ -156,7 +147,7 @@ class TransactionsListItem {
// already when funding the deposit tx we show 0 BTC as amount.
// Confirmation is not known from the BitcoinJ side (not 100% clear why) as no funds
// left our wallet nor we received funds. So we set indicator invisible.
amountAsCoin = Coin.ZERO;
amount = BigInteger.valueOf(0);
details = Res.get("funds.tx.collateralForRefund", tradeId);
initialTxConfidenceVisibility = false;
}
@ -166,7 +157,7 @@ class TransactionsListItem {
}
}
} else {
if (amountAsCoin.isZero()) {
if (amount.compareTo(BigInteger.valueOf(0)) == 0) {
details = Res.get("funds.tx.noFundsFromDispute");
}
}
@ -176,11 +167,6 @@ class TransactionsListItem {
this.date = new Date(timestamp);
dateString = DisplayUtils.formatDateTime(date);
isDustAttackTx = received && valueSentToMe.value < ignoreDustThreshold;
if (isDustAttackTx) {
details = Res.get("funds.tx.dustAttackTx");
}
// confidence
lazyFieldsSupplier = Suppliers.memoize(() -> new LazyFields() {{
txConfidenceIndicator = new TxConfidenceIndicator();
@ -217,16 +203,14 @@ class TransactionsListItem {
return dateString;
}
public String getAmount() {
return formatter.formatCoin(amountAsCoin);
public String getAmountStr() {
return HavenoUtils.formatToXmr(amount);
}
public Coin getAmountAsCoin() {
return amountAsCoin;
public BigInteger getAmount() {
return amount;
}
public String getAddressString() {
return addressString;
}

View File

@ -24,7 +24,6 @@ import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.ExternalHyperlink;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.OfferDetailsWindow;
import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
import bisq.desktop.util.GUIUtil;
@ -168,7 +167,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
});
addressColumn.setComparator(Comparator.comparing(item -> item.getDirection() + item.getAddressString()));
transactionColumn.setComparator(Comparator.comparing(TransactionsListItem::getTxId));
amountColumn.setComparator(Comparator.comparing(TransactionsListItem::getAmountAsCoin));
amountColumn.setComparator(Comparator.comparing(TransactionsListItem::getAmount));
confidenceColumn.setComparator(Comparator.comparingLong(item -> item.getNumConfirmations()));
memoColumn.setComparator(Comparator.comparing(TransactionsListItem::getMemo));
@ -221,7 +220,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
columns[1] = item.getDetails();
columns[2] = item.getDirection() + " " + item.getAddressString();
columns[3] = item.getTxId();
columns[4] = item.getAmount();
columns[4] = item.getAmountStr();
columns[5] = item.getMemo() == null ? "" : item.getMemo();
columns[6] = String.valueOf(item.getNumConfirmations());
return columns;
@ -320,13 +319,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
setGraphic(hyperlinkWithIcon);
// If details are available its a trade tx and we don't expect any dust attack tx
} else {
if (item.isDustAttackTx()) {
hyperlinkWithIcon = new HyperlinkWithIcon(item.getDetails(), AwesomeIcon.WARNING_SIGN);
hyperlinkWithIcon.setOnAction(event -> new Popup().warning(Res.get("funds.tx.dustAttackTx.popup")).show());
setGraphic(hyperlinkWithIcon);
} else {
setGraphic(new AutoTooltipLabel(item.getDetails()));
}
setGraphic(new AutoTooltipLabel(item.getDetails()));
}
} else {
setGraphic(null);
@ -423,7 +416,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
super.updateItem(item, empty);
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getAmount()));
setGraphic(new AutoTooltipLabel(item.getAmountStr()));
} else {
setGraphic(null);
}

View File

@ -23,11 +23,9 @@ 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 org.bitcoinj.core.Coin;
import javafx.scene.control.Label;
import java.math.BigInteger;
@ -41,7 +39,7 @@ class WithdrawalListItem {
private final XmrAddressEntry addressEntry;
private final XmrWalletService walletService;
private final CoinFormatter formatter;
private Coin balance;
private BigInteger balance;
private final String addressString;
@Setter
@Getter
@ -74,7 +72,7 @@ class WithdrawalListItem {
private void updateBalance() {
balance = walletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex());
if (balance != null)
balanceLabel.setText(formatter.formatCoin(this.balance));
balanceLabel.setText(HavenoUtils.formatToXmr(this.balance));
}
public final String getLabel() {
@ -111,7 +109,7 @@ class WithdrawalListItem {
return balanceLabel;
}
public Coin getBalance() {
public BigInteger getBalance() {
return balance;
}

View File

@ -35,18 +35,12 @@ import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.trade.TradeManager;
import bisq.core.user.DontShowAgainLookup;
import bisq.core.util.FormattingUtils;
import bisq.core.util.ParsingUtils;
import bisq.core.util.coin.CoinFormatter;
import bisq.core.util.validation.BtcAddressValidator;
import bisq.network.p2p.P2PService;
import bisq.common.util.Tuple2;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javax.inject.Named;
import monero.wallet.model.MoneroTxConfig;
import monero.wallet.model.MoneroTxWallet;
@ -78,9 +72,8 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
private final XmrWalletService xmrWalletService;
private final TradeManager tradeManager;
private final P2PService p2PService;
private final CoinFormatter formatter;
private XmrBalanceListener balanceListener;
private Coin amountAsCoin = Coin.ZERO;
private BigInteger amount = BigInteger.valueOf(0);
private ChangeListener<String> amountListener;
private ChangeListener<Boolean> amountFocusListener;
private int rowIndex = 0;
@ -94,13 +87,11 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
TradeManager tradeManager,
P2PService p2PService,
WalletsSetup walletsSetup,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
BtcAddressValidator btcAddressValidator,
WalletPasswordWindow walletPasswordWindow) {
this.xmrWalletService = xmrWalletService;
this.tradeManager = tradeManager;
this.p2PService = p2PService;
this.formatter = formatter;
}
@Override
@ -136,7 +127,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
amountListener = (observable, oldValue, newValue) -> {
if (amountTextField.focusedProperty().get()) {
try {
amountAsCoin = ParsingUtils.parseToCoin(amountTextField.getText(), formatter);
amount = HavenoUtils.parseXmr(amountTextField.getText());
} catch (Throwable t) {
log.error("Error at amountTextField input. " + t.toString());
}
@ -144,8 +135,8 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
};
amountFocusListener = (observable, oldValue, newValue) -> {
if (oldValue && !newValue) {
if (amountAsCoin.isPositive())
amountTextField.setText(formatter.formatCoin(amountAsCoin));
if (amount.compareTo(BigInteger.valueOf(0)) > 0)
amountTextField.setText(HavenoUtils.formatToXmr(amount));
else
amountTextField.setText("");
}
@ -184,23 +175,23 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
final String withdrawToAddress = withdrawToTextField.getText();
// get receiver amount
Coin receiverAmount = amountAsCoin;
if (!receiverAmount.isPositive()) throw new RuntimeException(Res.get("portfolio.pending.step5_buyer.amountTooLow"));
BigInteger receiverAmount = amount;
if (receiverAmount.compareTo(BigInteger.valueOf(0)) <= 0) throw new RuntimeException(Res.get("portfolio.pending.step5_buyer.amountTooLow"));
// create tx
MoneroTxWallet tx = xmrWalletService.getWallet().createTx(new MoneroTxConfig()
.setAccountIndex(0)
.setAmount(HavenoUtils.coinToAtomicUnits(receiverAmount)) // TODO: rename to centinerosToAtomicUnits()?
.setAmount(receiverAmount)
.setAddress(withdrawToAddress));
// create confirmation message
Coin sendersAmount = receiverAmount;
Coin fee = HavenoUtils.atomicUnitsToCoin(tx.getFee());
BigInteger sendersAmount = receiverAmount;
BigInteger fee = tx.getFee();
String messageText = Res.get("shared.sendFundsDetailsWithFee",
formatter.formatCoinWithCode(sendersAmount),
HavenoUtils.formatToXmrWithCode(sendersAmount),
withdrawToAddress,
formatter.formatCoinWithCode(fee),
formatter.formatCoinWithCode(receiverAmount));
HavenoUtils.formatToXmrWithCode(fee),
HavenoUtils.formatToXmrWithCode(receiverAmount));
// popup confirmation message
new Popup().headLine(Res.get("funds.withdrawal.confirmWithdrawalRequest"))
@ -214,7 +205,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
xmrWalletService.getWallet().setTxNote(tx.getHash(), withdrawMemoTextField.getText()); // TODO (monero-java): tx note does not persist when tx created then relayed
String key = "showTransactionSent";
if (DontShowAgainLookup.showAgain(key)) {
new TxDetails(tx.getHash(), withdrawToAddress, formatter.formatCoinWithCode(sendersAmount), formatter.formatCoinWithCode(fee), xmrWalletService.getWallet().getTxNote(tx.getHash()))
new TxDetails(tx.getHash(), withdrawToAddress, HavenoUtils.formatToXmrWithCode(sendersAmount), HavenoUtils.formatToXmrWithCode(fee), xmrWalletService.getWallet().getTxNote(tx.getHash()))
.dontShowAgainId(key)
.show();
}
@ -225,7 +216,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
.filter(Trade::isPayoutPublished)
.forEach(trade -> xmrWalletService.getAddressEntry(trade.getId(), XmrAddressEntry.Context.TRADE_PAYOUT)
.ifPresent(addressEntry -> {
if (xmrWalletService.getBalanceForAddress(addressEntry.getAddressString()).isZero())
if (xmrWalletService.getBalanceForAddress(addressEntry.getAddressString()).compareTo(BigInteger.valueOf(0)) == 0)
tradeManager.onTradeCompleted(trade);
}));
} catch (Exception e) {
@ -251,7 +242,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
///////////////////////////////////////////////////////////////////////////////////////////
private void reset() {
amountAsCoin = Coin.ZERO;
amount = BigInteger.valueOf(0);
amountTextField.setText("");
amountTextField.setPromptText(Res.get("funds.withdrawal.setAmount"));

View File

@ -32,7 +32,6 @@ import bisq.desktop.util.CurrencyList;
import bisq.desktop.util.CurrencyListItem;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.GUIUtil;
import bisq.common.UserThread;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.GlobalSettings;
@ -67,7 +66,6 @@ import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
class OfferBookChartViewModel extends ActivatableViewModel {
@ -402,7 +400,7 @@ class OfferBookChartViewModel extends ActivatableViewModel {
for (Offer offer : sortedList) {
Price price = offer.getPrice();
if (price != null) {
double amount = (double) offer.getAmount().value / LongMath.pow(10, offer.getAmount().smallestUnitExponent());
double amount = (double) offer.getAmount().longValueExact() / LongMath.pow(10, HavenoUtils.XMR_SMALLEST_UNIT_EXPONENT);
accumulatedAmount += amount;
offerTableListTemp.add(new OfferListItem(offer, accumulatedAmount));

View File

@ -19,11 +19,11 @@ package bisq.desktop.main.market.spread;
import bisq.core.monetary.Price;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import javax.annotation.Nullable;
public class SpreadItem {
@ -36,10 +36,10 @@ public class SpreadItem {
public final Price priceSpread;
public final String percentage;
public final double percentageValue;
public final Coin totalAmount;
public final BigInteger totalAmount;
public SpreadItem(String currencyCode, int numberOfBuyOffers, int numberOfSellOffers, int numberOfOffers,
@Nullable Price priceSpread, String percentage, double percentageValue, Coin totalAmount) {
@Nullable Price priceSpread, String percentage, double percentageValue, BigInteger totalAmount) {
this.currencyCode = currencyCode;
this.numberOfBuyOffers = numberOfBuyOffers;
this.numberOfSellOffers = numberOfSellOffers;

View File

@ -26,11 +26,10 @@ import bisq.desktop.util.GUIUtil;
import bisq.common.UserThread;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.trade.HavenoUtils;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javax.inject.Named;
@ -48,6 +47,7 @@ import javafx.collections.transformation.SortedList;
import javafx.util.Callback;
import java.math.BigInteger;
import java.util.Comparator;
@FxmlView
@ -127,7 +127,10 @@ public class SpreadView extends ActivatableViewAndModel<GridPane, SpreadViewMode
int numberOfOffers = sortedList.stream().mapToInt(item -> item.numberOfOffers).sum();
int numberOfBuyOffers = sortedList.stream().mapToInt(item -> item.numberOfBuyOffers).sum();
int numberOfSellOffers = sortedList.stream().mapToInt(item -> item.numberOfSellOffers).sum();
String total = formatter.formatCoin(Coin.valueOf(sortedList.stream().mapToLong(item -> item.totalAmount.value).sum()));
BigInteger totalAmount = BigInteger.valueOf(0);
for (SpreadItem item : sortedList) totalAmount = totalAmount.add(item.totalAmount);
String total = HavenoUtils.formatToXmr(totalAmount);
UserThread.execute(() -> {
numberOfOffersColumn.setGraphic(new AutoTooltipLabel(Res.get("market.spread.numberOfOffersColumn", numberOfOffers)));

View File

@ -30,10 +30,10 @@ import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.provider.price.MarketPrice;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.HavenoUtils;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat;
import com.google.inject.Inject;
@ -48,6 +48,7 @@ import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
@ -135,7 +136,7 @@ class SpreadViewModel extends ActivatableViewModel {
}
spreadItems.clear();
Coin totalAmount = null;
BigInteger totalAmount = BigInteger.valueOf(0);
for (String key : offersByCurrencyMap.keySet()) {
List<Offer> offers = offersByCurrencyMap.get(key);
@ -235,7 +236,7 @@ class SpreadViewModel extends ActivatableViewModel {
}
}
totalAmount = Coin.valueOf(offers.stream().mapToLong(offer -> offer.getAmount().getValue()).sum());
for (Offer offer : offers) totalAmount = totalAmount.add(offer.getAmount());
spreadItems.add(new SpreadItem(key, buyOffers.size(), sellOffers.size(),
uniqueOffers.size(), spread, percentage, percentageValue, totalAmount));
}
@ -243,11 +244,11 @@ class SpreadViewModel extends ActivatableViewModel {
maxPlacesForAmount.set(formatAmount(totalAmount, false).length());
}
public String getAmount(Coin amount) {
public String getAmount(BigInteger amount) {
return formatAmount(amount, true);
}
private String formatAmount(Coin amount, boolean decimalAligned) {
return formatter.formatCoin(amount, GUIUtil.AMOUNT_DECIMALS, decimalAligned, maxPlacesForAmount.get());
private String formatAmount(BigInteger amount, boolean decimalAligned) {
return formatter.formatCoin(HavenoUtils.atomicUnitsToCoin(amount), GUIUtil.AMOUNT_DECIMALS, decimalAligned, maxPlacesForAmount.get());
}
}

View File

@ -113,27 +113,26 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
protected TradeCurrency tradeCurrency;
protected final StringProperty tradeCurrencyCode = new SimpleStringProperty();
protected final BooleanProperty useMarketBasedPrice = new SimpleBooleanProperty();
protected final ObjectProperty<Coin> amount = new SimpleObjectProperty<>();
protected final ObjectProperty<Coin> minAmount = new SimpleObjectProperty<>();
protected final ObjectProperty<BigInteger> amount = new SimpleObjectProperty<>();
protected final ObjectProperty<BigInteger> minAmount = new SimpleObjectProperty<>();
protected final ObjectProperty<Price> price = new SimpleObjectProperty<>();
protected final ObjectProperty<Volume> volume = new SimpleObjectProperty<>();
protected final ObjectProperty<Volume> minVolume = new SimpleObjectProperty<>();
// Percentage value of buyer security deposit. E.g. 0.01 means 1% of trade amount
protected final DoubleProperty buyerSecurityDeposit = new SimpleDoubleProperty();
protected final DoubleProperty buyerSecurityDepositPct = new SimpleDoubleProperty();
protected final ObservableList<PaymentAccount> paymentAccounts = FXCollections.observableArrayList();
protected PaymentAccount paymentAccount;
boolean isTabSelected;
protected double marketPriceMargin = 0;
private Coin txFeeFromFeeService = Coin.ZERO;
@Getter
private boolean marketPriceAvailable;
protected boolean allowAmountUpdate = true;
private final TradeStatisticsManager tradeStatisticsManager;
private final Predicate<ObjectProperty<Coin>> isNonZeroAmount = (c) -> c.get() != null && !c.get().isZero();
private final Predicate<ObjectProperty<BigInteger>> isNonZeroAmount = (c) -> c.get() != null && c.get().compareTo(BigInteger.valueOf(0)) != 0;
private final Predicate<ObjectProperty<Price>> isNonZeroPrice = (p) -> p.get() != null && !p.get().isZero();
private final Predicate<ObjectProperty<Volume>> isNonZeroVolume = (v) -> v.get() != null && !v.get().isZero();
@Getter
@ -176,7 +175,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
addressEntry = xmrWalletService.getOrCreateAddressEntry(offerId, XmrAddressEntry.Context.OFFER_FUNDING);
useMarketBasedPrice.set(preferences.isUsePercentageBasedPrice());
buyerSecurityDeposit.set(Restrictions.getMinBuyerSecurityDepositAsPercent());
buyerSecurityDepositPct.set(Restrictions.getMinBuyerSecurityDepositAsPercent());
xmrBalanceListener = new XmrBalanceListener(getAddressEntry().getSubaddressIndex()) {
@Override
@ -295,10 +294,10 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
amount.get(),
minAmount.get(),
price.get(),
txFeeFromFeeService,
Coin.ZERO,
useMarketBasedPrice.get(),
marketPriceMargin,
buyerSecurityDeposit.get(),
buyerSecurityDepositPct.get(),
paymentAccount);
}
@ -319,7 +318,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
setSuggestedSecurityDeposit(getPaymentAccount());
if (amount.get() != null && this.allowAmountUpdate)
this.amount.set(Coin.valueOf(Math.min(amount.get().value, getMaxTradeLimit())));
this.amount.set(amount.get().min(BigInteger.valueOf(getMaxTradeLimit())));
}
}
@ -356,10 +355,10 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
// Suggested deposit is double the trade range over the previous lock time period, bounded by min/max deposit
var suggestedSecurityDeposit =
Math.min(2 * (max - min) / max, Restrictions.getMaxBuyerSecurityDepositAsPercent());
buyerSecurityDeposit.set(Math.max(suggestedSecurityDeposit, minSecurityDeposit));
buyerSecurityDepositPct.set(Math.max(suggestedSecurityDeposit, minSecurityDeposit));
} catch (Throwable t) {
log.error(t.toString());
buyerSecurityDeposit.set(minSecurityDeposit);
buyerSecurityDepositPct.set(minSecurityDeposit);
}
}
@ -421,7 +420,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
boolean isMinAmountLessOrEqualAmount() {
//noinspection SimplifiableIfStatement
if (minAmount.get() != null && amount.get() != null)
return !minAmount.get().isGreaterThan(amount.get());
return minAmount.get().compareTo(amount.get()) <= 0;
return true;
}
@ -512,7 +511,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
}
}
private Volume calculateVolumeForAmount(ObjectProperty<Coin> minAmount) {
private Volume calculateVolumeForAmount(ObjectProperty<BigInteger> minAmount) {
Volume volumeByAmount = price.get().getVolumeByAmount(minAmount.get());
// For HalCash we want multiple of 10 EUR
@ -526,7 +525,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
void calculateAmount() {
if (isNonZeroPrice.test(price) && isNonZeroVolume.test(volume) && allowAmountUpdate) {
try {
Coin value = DisplayUtils.reduceTo4Decimals(price.get().getAmountByVolume(volume.get()), btcFormatter);
BigInteger value = HavenoUtils.coinToAtomicUnits(DisplayUtils.reduceTo4Decimals(HavenoUtils.atomicUnitsToCoin(price.get().getAmountByVolume(volume.get())), btcFormatter));
if (isUsingHalCashAccount())
value = CoinUtil.getAdjustedAmountForHalCash(value, price.get(), getMaxTradeLimit());
else if (CurrencyUtil.isFiatCurrency(tradeCurrencyCode.get()))
@ -546,17 +545,17 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
// Maker does not pay the mining fee for the trade txs because the mining fee might be different when maker
// created the offer and reserved his funds, so that would not work well with dynamic fees.
// The mining fee for the createOfferFee tx is deducted from the createOfferFee and not visible to the trader
final Coin makerFee = getMakerFee();
final BigInteger makerFee = getMakerFee();
if (direction != null && amount.get() != null && makerFee != null) {
Coin feeAndSecDeposit = getSecurityDeposit().add(makerFee);
Coin total = isBuyOffer() ? feeAndSecDeposit : feeAndSecDeposit.add(amount.get());
totalToPayAsCoin.set(total);
BigInteger feeAndSecDeposit = getSecurityDeposit().add(makerFee);
BigInteger total = isBuyOffer() ? feeAndSecDeposit : feeAndSecDeposit.add(amount.get());
totalToPay.set(total);
updateBalance();
}
}
Coin getSecurityDeposit() {
return isBuyOffer() ? getBuyerSecurityDepositAsCoin() : getSellerSecurityDepositAsCoin();
BigInteger getSecurityDeposit() {
return isBuyOffer() ? getBuyerSecurityDeposit() : getSellerSecurityDeposit();
}
void swapTradeToSavings() {
@ -571,7 +570,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
protected abstract Set<PaymentAccount> getUserPaymentAccounts();
protected void setAmount(Coin amount) {
protected void setAmount(BigInteger amount) {
this.amount.set(amount);
}
@ -584,18 +583,18 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
}
protected void setBuyerSecurityDeposit(double value) {
this.buyerSecurityDeposit.set(value);
this.buyerSecurityDepositPct.set(value);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
protected ReadOnlyObjectProperty<Coin> getAmount() {
protected ReadOnlyObjectProperty<BigInteger> getAmount() {
return amount;
}
protected ReadOnlyObjectProperty<Coin> getMinAmount() {
protected ReadOnlyObjectProperty<BigInteger> getMinAmount() {
return minAmount;
}
@ -611,7 +610,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
return minVolume;
}
protected void setMinAmount(Coin minAmount) {
protected void setMinAmount(BigInteger minAmount) {
this.minAmount.set(minAmount);
}
@ -635,46 +634,46 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
return useMarketBasedPrice;
}
ReadOnlyDoubleProperty getBuyerSecurityDeposit() {
return buyerSecurityDeposit;
ReadOnlyDoubleProperty getBuyerSecurityDepositPct() {
return buyerSecurityDepositPct;
}
protected Coin getBuyerSecurityDepositAsCoin() {
Coin percentOfAmountAsCoin = CoinUtil.getPercentOfAmountAsCoin(buyerSecurityDeposit.get(), amount.get());
return getBoundedBuyerSecurityDepositAsCoin(percentOfAmountAsCoin);
protected BigInteger getBuyerSecurityDeposit() {
BigInteger percentOfAmount = CoinUtil.getPercentOfAmount(buyerSecurityDepositPct.get(), amount.get());
return getBoundedBuyerSecurityDeposit(percentOfAmount);
}
private Coin getSellerSecurityDepositAsCoin() {
Coin amountAsCoin = this.amount.get();
if (amountAsCoin == null)
amountAsCoin = Coin.ZERO;
private BigInteger getSellerSecurityDeposit() {
BigInteger amount = this.amount.get();
if (amount == null)
amount = BigInteger.valueOf(0);
Coin percentOfAmountAsCoin = CoinUtil.getPercentOfAmountAsCoin(
createOfferService.getSellerSecurityDepositAsDouble(buyerSecurityDeposit.get()), amountAsCoin);
return getBoundedSellerSecurityDepositAsCoin(percentOfAmountAsCoin);
BigInteger percentOfAmount = CoinUtil.getPercentOfAmount(
createOfferService.getSellerSecurityDepositAsDouble(buyerSecurityDepositPct.get()), amount);
return getBoundedSellerSecurityDeposit(percentOfAmount);
}
protected Coin getBoundedBuyerSecurityDepositAsCoin(Coin value) {
protected BigInteger getBoundedBuyerSecurityDeposit(BigInteger value) {
// We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the
// MinBuyerSecurityDepositAsCoin from Restrictions.
return Coin.valueOf(Math.max(Restrictions.getMinBuyerSecurityDepositAsCoin().value, value.value));
// MinBuyerSecurityDeposit from Restrictions.
return Restrictions.getMinBuyerSecurityDeposit().max(value);
}
private Coin getBoundedSellerSecurityDepositAsCoin(Coin value) {
private BigInteger getBoundedSellerSecurityDeposit(BigInteger value) {
// We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the
// MinSellerSecurityDepositAsCoin from Restrictions.
return Coin.valueOf(Math.max(Restrictions.getMinSellerSecurityDepositAsCoin().value, value.value));
// MinSellerSecurityDeposit from Restrictions.
return Restrictions.getMinSellerSecurityDeposit().max(value);
}
ReadOnlyObjectProperty<Coin> totalToPayAsCoinProperty() {
return totalToPayAsCoin;
ReadOnlyObjectProperty<BigInteger> totalToPayAsProperty() {
return totalToPay;
}
public void setMarketPriceAvailable(boolean marketPriceAvailable) {
this.marketPriceAvailable = marketPriceAvailable;
}
public Coin getMakerFee() {
public BigInteger getMakerFee() {
return HavenoUtils.getMakerFee(amount.get());
}
@ -684,7 +683,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
}
public boolean isMinBuyerSecurityDeposit() {
return !getBuyerSecurityDepositAsCoin().isGreaterThan(Restrictions.getMinBuyerSecurityDepositAsCoin());
return getBuyerSecurityDeposit().compareTo(Restrictions.getMinBuyerSecurityDeposit()) <= 0;
}
public void setTriggerPrice(long triggerPrice) {

View File

@ -48,6 +48,7 @@ import bisq.core.offer.OfferDirection;
import bisq.core.payment.FasterPaymentsAccount;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.trade.HavenoUtils;
import bisq.core.user.DontShowAgainLookup;
import bisq.core.user.Preferences;
import bisq.core.util.coin.CoinFormatter;
@ -58,8 +59,6 @@ import bisq.common.util.Tuple2;
import bisq.common.util.Tuple3;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import net.glxn.qrgen.QRCode;
import net.glxn.qrgen.image.ImageType;
@ -105,7 +104,7 @@ import javafx.util.StringConverter;
import java.net.URI;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -157,7 +156,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
buyerSecurityDepositFocusedListener, priceFocusedListener, placeOfferCompletedListener,
priceAsPercentageFocusedListener, getShowWalletFundedNotificationListener,
isMinBuyerSecurityDepositListener, triggerPriceFocusedListener;
private ChangeListener<Coin> missingCoinListener;
private ChangeListener<BigInteger> missingCoinListener;
private ChangeListener<String> tradeCurrencyCodeListener, errorMessageListener,
marketPriceMarginListener, volumeListener, buyerSecurityDepositInBTCListener;
private ChangeListener<Number> marketPriceAvailableListener;
@ -248,7 +247,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
onPaymentAccountsComboBoxSelected();
balanceTextField.setTargetAmount(model.getDataModel().totalToPayAsCoinProperty().get());
balanceTextField.setTargetAmount(model.getDataModel().totalToPayAsProperty().get());
updatePriceToggle();
Label popOverLabel = OfferViewUtil.createPopOverLabel(Res.get("createOffer.triggerPrice.tooltip"));
@ -331,7 +330,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
// called from parent as the view does not get notified when the tab is closed
public void onClose() {
// we use model.placeOfferCompleted to not react on close which was triggered by a successful placeOffer
if (model.getDataModel().getBalance().get().isPositive() && !model.placeOfferCompleted.get()) {
if (model.getDataModel().getBalance().get().compareTo(BigInteger.valueOf(0)) > 0 && !model.placeOfferCompleted.get()) {
model.getDataModel().swapTradeToSavings();
}
}
@ -411,7 +410,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
triggerPriceVBox.setVisible(false);
}
balanceTextField.setTargetAmount(model.getDataModel().totalToPayAsCoinProperty().get());
balanceTextField.setTargetAmount(model.getDataModel().totalToPayAsProperty().get());
// temporarily disabled due to high CPU usage (per issue #4649)
// waitingForFundsSpinner.play();
@ -550,7 +549,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
volumeTextField.textProperty().bindBidirectional(model.volume);
volumeTextField.promptTextProperty().bind(model.volumePromptLabel);
totalToPayTextField.textProperty().bind(model.totalToPay);
addressTextField.amountAsCoinProperty().bind(model.getDataModel().getMissingCoin());
addressTextField.amountAsProperty().bind(model.getDataModel().getMissingCoin());
buyerSecurityDepositInputTextField.textProperty().bindBidirectional(model.buyerSecurityDeposit);
buyerSecurityDepositLabel.textProperty().bind(model.buyerSecurityDepositLabel);
tradeFeeInBtcLabel.textProperty().bind(model.tradeFeeInBtcWithFiat);
@ -598,7 +597,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
volumeTextField.textProperty().unbindBidirectional(model.volume);
volumeTextField.promptTextProperty().unbindBidirectional(model.volume);
totalToPayTextField.textProperty().unbind();
addressTextField.amountAsCoinProperty().unbind();
addressTextField.amountAsProperty().unbind();
buyerSecurityDepositInputTextField.textProperty().unbindBidirectional(model.buyerSecurityDeposit);
buyerSecurityDepositLabel.textProperty().unbind();
tradeFeeInBtcLabel.textProperty().unbind();
@ -736,7 +735,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
if (newValue) {
Notification walletFundedNotification = new Notification()
.headLine(Res.get("notification.walletUpdate.headline"))
.notification(Res.get("notification.walletUpdate.msg", xmrFormatter.formatCoinWithCode(model.getDataModel().getTotalToPayAsCoin().get())))
.notification(Res.get("notification.walletUpdate.msg", HavenoUtils.formatToXmrWithCode(model.getDataModel().getTotalToPay().get())))
.autoClose();
walletFundedNotification.show();

View File

@ -41,7 +41,7 @@ import bisq.core.offer.OfferRestrictions;
import bisq.core.offer.OfferUtil;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.payment.validation.BtcValidator;
import bisq.core.payment.validation.XmrValidator;
import bisq.core.payment.validation.FiatVolumeValidator;
import bisq.core.payment.validation.SecurityDepositValidator;
import bisq.core.provider.price.MarketPrice;
@ -84,6 +84,7 @@ import javafx.beans.value.ChangeListener;
import javafx.util.Callback;
import java.math.BigInteger;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
@ -92,7 +93,7 @@ import static javafx.beans.binding.Bindings.createStringBinding;
@Slf4j
public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> extends ActivatableWithDataModel<M> {
private final BtcValidator btcValidator;
private final XmrValidator xmrValidator;
protected final SecurityDepositValidator securityDepositValidator;
protected final PriceFeedService priceFeedService;
private final AccountAgeWitnessService accountAgeWitnessService;
@ -163,8 +164,8 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
private ChangeListener<String> volumeStringListener;
private ChangeListener<String> securityDepositStringListener;
private ChangeListener<Coin> amountAsCoinListener;
private ChangeListener<Coin> minAmountAsCoinListener;
private ChangeListener<BigInteger> amountListener;
private ChangeListener<BigInteger> minAmountListener;
private ChangeListener<Price> priceListener;
private ChangeListener<Volume> volumeListener;
private ChangeListener<Number> securityDepositAsDoubleListener;
@ -190,7 +191,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
FiatVolumeValidator fiatVolumeValidator,
FiatPriceValidator fiatPriceValidator,
AltcoinValidator altcoinValidator,
BtcValidator btcValidator,
XmrValidator btcValidator,
SecurityDepositValidator securityDepositValidator,
PriceFeedService priceFeedService,
AccountAgeWitnessService accountAgeWitnessService,
@ -203,7 +204,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
this.fiatVolumeValidator = fiatVolumeValidator;
this.fiatPriceValidator = fiatPriceValidator;
this.altcoinValidator = altcoinValidator;
this.btcValidator = btcValidator;
this.xmrValidator = btcValidator;
this.securityDepositValidator = securityDepositValidator;
this.priceFeedService = priceFeedService;
this.accountAgeWitnessService = accountAgeWitnessService;
@ -271,10 +272,10 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
() -> Res.get("createOffer.volume.prompt", dataModel.getTradeCurrencyCode().get()),
dataModel.getTradeCurrencyCode()));
totalToPay.bind(createStringBinding(() -> btcFormatter.formatCoinWithCode(dataModel.totalToPayAsCoinProperty().get()),
dataModel.totalToPayAsCoinProperty()));
totalToPay.bind(createStringBinding(() -> HavenoUtils.formatToXmrWithCode(dataModel.totalToPayAsProperty().get()),
dataModel.totalToPayAsProperty()));
tradeAmount.bind(createStringBinding(() -> btcFormatter.formatCoinWithCode(dataModel.getAmount().get()),
tradeAmount.bind(createStringBinding(() -> HavenoUtils.formatToXmrWithCode(dataModel.getAmount().get()),
dataModel.getAmount()));
tradeCurrencyCode.bind(dataModel.getTradeCurrencyCode());
@ -298,7 +299,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
private void createListeners() {
amountStringListener = (ov, oldValue, newValue) -> {
if (!ignoreAmountStringListener) {
if (isBtcInputValid(newValue).isValid) {
if (isXmrInputValid(newValue).isValid) {
setAmountToModel();
dataModel.calculateVolume();
dataModel.calculateTotalToPay();
@ -308,7 +309,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
}
};
minAmountStringListener = (ov, oldValue, newValue) -> {
if (isBtcInputValid(newValue).isValid)
if (isXmrInputValid(newValue).isValid)
setMinAmountToModel();
updateButtonDisableState();
};
@ -431,10 +432,10 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
};
amountAsCoinListener = (ov, oldValue, newValue) -> {
amountListener = (ov, oldValue, newValue) -> {
if (newValue != null) {
amount.set(btcFormatter.formatCoin(newValue));
buyerSecurityDepositInBTC.set(btcFormatter.formatCoinWithCode(dataModel.getBuyerSecurityDepositAsCoin()));
amount.set(HavenoUtils.formatToXmr(newValue));
buyerSecurityDepositInBTC.set(HavenoUtils.formatToXmrWithCode(dataModel.getBuyerSecurityDeposit()));
} else {
amount.set("");
buyerSecurityDepositInBTC.set("");
@ -442,9 +443,9 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
applyMakerFee();
};
minAmountAsCoinListener = (ov, oldValue, newValue) -> {
minAmountListener = (ov, oldValue, newValue) -> {
if (newValue != null)
minAmount.set(btcFormatter.formatCoin(newValue));
minAmount.set(HavenoUtils.formatToXmr(newValue));
else
minAmount.set("");
};
@ -473,7 +474,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
if (newValue != null) {
buyerSecurityDeposit.set(FormattingUtils.formatToPercent((double) newValue));
if (dataModel.getAmount().get() != null) {
buyerSecurityDepositInBTC.set(btcFormatter.formatCoinWithCode(dataModel.getBuyerSecurityDepositAsCoin()));
buyerSecurityDepositInBTC.set(HavenoUtils.formatToXmrWithCode(dataModel.getBuyerSecurityDeposit()));
}
updateBuyerSecurityDeposit();
} else {
@ -498,13 +499,13 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
tradeFeeCurrencyCode.set(Res.getBaseCurrencyCode());
tradeFeeDescription.set(Res.get("createOffer.tradeFee.descriptionBTCOnly"));
Coin makerFeeAsCoin = dataModel.getMakerFee();
if (makerFeeAsCoin == null) {
BigInteger makerFee = dataModel.getMakerFee();
if (makerFee == null) {
return;
}
isTradeFeeVisible.setValue(true);
tradeFee.set(getFormatterForMakerFee().formatCoin(makerFeeAsCoin));
tradeFee.set(HavenoUtils.formatToXmr(makerFee));
tradeFeeInBtcWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil,
dataModel.getMakerFee(),
btcFormatter));
@ -529,11 +530,11 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
buyerSecurityDeposit.addListener(securityDepositStringListener);
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
dataModel.getAmount().addListener(amountAsCoinListener);
dataModel.getMinAmount().addListener(minAmountAsCoinListener);
dataModel.getAmount().addListener(amountListener);
dataModel.getMinAmount().addListener(minAmountListener);
dataModel.getPrice().addListener(priceListener);
dataModel.getVolume().addListener(volumeListener);
dataModel.getBuyerSecurityDeposit().addListener(securityDepositAsDoubleListener);
dataModel.getBuyerSecurityDepositPct().addListener(securityDepositAsDoubleListener);
// dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
dataModel.getIsXmrWalletFunded().addListener(isWalletFundedListener);
@ -551,11 +552,11 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
buyerSecurityDeposit.removeListener(securityDepositStringListener);
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
dataModel.getAmount().removeListener(amountAsCoinListener);
dataModel.getMinAmount().removeListener(minAmountAsCoinListener);
dataModel.getAmount().removeListener(amountListener);
dataModel.getMinAmount().removeListener(minAmountListener);
dataModel.getPrice().removeListener(priceListener);
dataModel.getVolume().removeListener(volumeListener);
dataModel.getBuyerSecurityDeposit().removeListener(securityDepositAsDoubleListener);
dataModel.getBuyerSecurityDepositPct().removeListener(securityDepositAsDoubleListener);
//dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
dataModel.getIsXmrWalletFunded().removeListener(isWalletFundedListener);
@ -574,9 +575,9 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
boolean initWithData(OfferDirection direction, TradeCurrency tradeCurrency) {
boolean result = dataModel.initWithData(direction, tradeCurrency);
if (dataModel.paymentAccount != null)
btcValidator.setMaxValue(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimitAsCoin(dataModel.getTradeCurrencyCode().get()));
btcValidator.setMaxTradeLimit(Coin.valueOf(dataModel.getMaxTradeLimit()));
btcValidator.setMinValue(Restrictions.getMinTradeAmount());
xmrValidator.setMaxValue(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimit(dataModel.getTradeCurrencyCode().get()));
xmrValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit()));
xmrValidator.setMinValue(Restrictions.getMinTradeAmount());
final boolean isBuy = dataModel.getDirection() == OfferDirection.BUY;
@ -592,7 +593,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
securityDepositValidator.setPaymentAccount(dataModel.paymentAccount);
validateAndSetBuyerSecurityDepositToModel();
buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDeposit().get()));
buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDepositPct().get()));
buyerSecurityDepositLabel.set(getSecurityDepositLabel());
applyMakerFee();
@ -630,10 +631,10 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
dataModel.onPaymentAccountSelected(paymentAccount);
if (amount.get() != null)
amountValidationResult.set(isBtcInputValid(amount.get()));
amountValidationResult.set(isXmrInputValid(amount.get()));
btcValidator.setMaxValue(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimitAsCoin(dataModel.getTradeCurrencyCode().get()));
btcValidator.setMaxTradeLimit(Coin.valueOf(dataModel.getMaxTradeLimit()));
xmrValidator.setMaxValue(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimit(dataModel.getTradeCurrencyCode().get()));
xmrValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit()));
maybeShowMakeOfferToUnsignedAccountWarning();
securityDepositValidator.setPaymentAccount(paymentAccount);
@ -659,8 +660,8 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
updateButtonDisableState();
} else {
new Popup().warning(Res.get("shared.notEnoughFunds",
btcFormatter.formatCoinWithCode(dataModel.totalToPayAsCoinProperty().get()),
btcFormatter.formatCoinWithCode(dataModel.getTotalBalance())))
HavenoUtils.formatToXmrWithCode(dataModel.totalToPayAsProperty().get()),
HavenoUtils.formatToXmrWithCode(dataModel.getTotalBalance())))
.actionButtonTextWithGoTo("navigation.funds.depositFunds")
.onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, DepositView.class))
.show();
@ -675,12 +676,15 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
// On focus out we do validation and apply the data to the model
void onFocusOutAmountTextField(boolean oldValue, boolean newValue) {
if (oldValue && !newValue) {
InputValidator.ValidationResult result = isBtcInputValid(amount.get());
System.out.println("Checking if input valid: " + amount.get());
InputValidator.ValidationResult result = isXmrInputValid(amount.get());
amountValidationResult.set(result);
System.out.println("Result is valid: " + result.isValid);
System.out.println("Result error: " + result.errorMessage);
if (result.isValid) {
setAmountToModel();
ignoreAmountStringListener = true;
amount.set(btcFormatter.formatCoin(dataModel.getAmount().get()));
amount.set(HavenoUtils.formatToXmr(dataModel.getAmount().get()));
ignoreAmountStringListener = false;
dataModel.calculateVolume();
@ -690,12 +694,12 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
amountValidationResult.set(result);
if (minAmount.get() != null)
minAmountValidationResult.set(isBtcInputValid(minAmount.get()));
} else if (amount.get() != null && btcValidator.getMaxTradeLimit() != null && btcValidator.getMaxTradeLimit().value == OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value) {
amount.set(btcFormatter.formatCoin(btcValidator.getMaxTradeLimit()));
minAmountValidationResult.set(isXmrInputValid(minAmount.get()));
} else if (amount.get() != null && xmrValidator.getMaxTradeLimit() != null && xmrValidator.getMaxTradeLimit().longValueExact() == OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.longValueExact()) {
amount.set(HavenoUtils.formatToXmr(xmrValidator.getMaxTradeLimit()));
boolean isBuy = dataModel.getDirection() == OfferDirection.BUY;
new Popup().information(Res.get(isBuy ? "popup.warning.tradeLimitDueAccountAgeRestriction.buyer" : "popup.warning.tradeLimitDueAccountAgeRestriction.seller",
btcFormatter.formatCoinWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT),
HavenoUtils.formatToXmrWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT),
Res.get("offerbook.warning.newVersionAnnouncement")))
.width(900)
.show();
@ -714,11 +718,11 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
public void onFocusOutMinAmountTextField(boolean oldValue, boolean newValue) {
if (oldValue && !newValue) {
InputValidator.ValidationResult result = isBtcInputValid(minAmount.get());
InputValidator.ValidationResult result = isXmrInputValid(minAmount.get());
minAmountValidationResult.set(result);
if (result.isValid) {
Coin minAmountAsCoin = dataModel.getMinAmount().get();
syncMinAmountWithAmount = minAmountAsCoin != null && minAmountAsCoin.equals(dataModel.getAmount().get());
BigInteger minAmount = dataModel.getMinAmount().get();
syncMinAmountWithAmount = minAmount != null && minAmount.equals(dataModel.getAmount().get());
setMinAmountToModel();
dataModel.calculateMinVolume();
@ -732,14 +736,14 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
updateButtonDisableState();
}
this.minAmount.set(btcFormatter.formatCoin(minAmountAsCoin));
this.minAmount.set(HavenoUtils.formatToXmr(minAmount));
if (!dataModel.isMinAmountLessOrEqualAmount()) {
this.amount.set(this.minAmount.get());
} else {
minAmountValidationResult.set(result);
if (this.amount.get() != null)
amountValidationResult.set(isBtcInputValid(this.amount.get()));
amountValidationResult.set(isXmrInputValid(this.amount.get()));
}
} else {
syncMinAmountWithAmount = true;
@ -864,12 +868,12 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
minAmount.set(amount.getValue());
} else {
if (amount.get() != null)
amountValidationResult.set(isBtcInputValid(amount.get()));
amountValidationResult.set(isXmrInputValid(amount.get()));
// We only check minAmountValidationResult if amountValidationResult is valid, otherwise we would get
// triggered a close of the popup when the minAmountValidationResult is applied
if (amountValidationResult.getValue() != null && amountValidationResult.getValue().isValid && minAmount.get() != null)
minAmountValidationResult.set(isBtcInputValid(minAmount.get()));
minAmountValidationResult.set(isXmrInputValid(minAmount.get()));
}
}
@ -899,7 +903,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
.onAction(() -> {
dataModel.setBuyerSecurityDeposit(defaultSecurityDeposit);
ignoreSecurityDepositStringListener = true;
buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDeposit().get()));
buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDepositPct().get()));
ignoreSecurityDepositStringListener = false;
})
.closeButtonText(Res.get("createOffer.useLowerValue"))
@ -916,7 +920,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
private void applyBuyerSecurityDepositOnFocusOut() {
setBuyerSecurityDepositToModel();
ignoreSecurityDepositStringListener = true;
buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDeposit().get()));
buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDepositPct().get()));
ignoreSecurityDepositStringListener = false;
}
@ -985,12 +989,12 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
dataModel.getSecurityDeposit(),
dataModel.getAmount().get(),
btcFormatter,
Restrictions.getMinBuyerSecurityDepositAsCoin()
Restrictions.getMinBuyerSecurityDeposit()
);
}
public String getSecurityDepositWithCode() {
return btcFormatter.formatCoinWithCode(dataModel.getSecurityDeposit());
return HavenoUtils.formatToXmrWithCode(dataModel.getSecurityDeposit());
}
@ -1003,13 +1007,13 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
}
public String getMakerFeePercentage() {
final Coin makerFeeAsCoin = dataModel.getMakerFee();
return GUIUtil.getPercentage(makerFeeAsCoin, dataModel.getAmount().get());
final BigInteger makerFee = dataModel.getMakerFee();
return GUIUtil.getPercentage(makerFee, dataModel.getAmount().get());
}
public String getTotalToPayInfo() {
return OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil,
dataModel.totalToPayAsCoin.get(),
dataModel.totalToPay.get(),
btcFormatter);
}
@ -1083,7 +1087,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
private void setAmountToModel() {
if (amount.get() != null && !amount.get().isEmpty()) {
Coin amount = DisplayUtils.parseToCoinWith4Decimals(this.amount.get(), btcFormatter);
BigInteger amount = HavenoUtils.coinToAtomicUnits(DisplayUtils.parseToCoinWith4Decimals(this.amount.get(), btcFormatter));
long maxTradeLimit = dataModel.getMaxTradeLimit();
Price price = dataModel.getPrice().get();
@ -1107,7 +1111,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
private void setMinAmountToModel() {
if (minAmount.get() != null && !minAmount.get().isEmpty()) {
Coin minAmount = DisplayUtils.parseToCoinWith4Decimals(this.minAmount.get(), btcFormatter);
BigInteger minAmount = HavenoUtils.coinToAtomicUnits(DisplayUtils.parseToCoinWith4Decimals(this.minAmount.get(), btcFormatter));
Price price = dataModel.getPrice().get();
long maxTradeLimit = dataModel.getMaxTradeLimit();
@ -1158,7 +1162,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
private void validateAndSetBuyerSecurityDepositToModel() {
// If the security deposit in the model is not valid percent
String value = FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDeposit().get());
String value = FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDepositPct().get());
if (!securityDepositValidator.validate(value).isValid) {
dataModel.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent());
}
@ -1168,15 +1172,15 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
if (!makeOfferFromUnsignedAccountWarningDisplayed &&
dataModel.getDirection() == OfferDirection.SELL &&
PaymentMethod.hasChargebackRisk(dataModel.getPaymentAccount().getPaymentMethod(), dataModel.getTradeCurrency().getCode())) {
Coin checkAmount = dataModel.getMinAmount().get() == null ? dataModel.getAmount().get() : dataModel.getMinAmount().get();
if (checkAmount != null && !checkAmount.isGreaterThan(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT)) {
BigInteger checkAmount = dataModel.getMinAmount().get() == null ? dataModel.getAmount().get() : dataModel.getMinAmount().get();
if (checkAmount != null && checkAmount.compareTo(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT) <= 0) {
makeOfferFromUnsignedAccountWarningDisplayed = true;
}
}
}
private InputValidator.ValidationResult isBtcInputValid(String input) {
return btcValidator.validate(input);
private InputValidator.ValidationResult isXmrInputValid(String input) {
return xmrValidator.validate("" + HavenoUtils.atomicUnitsToXmr(HavenoUtils.parseXmr(input)));
}
private InputValidator.ValidationResult isPriceInputValid(String input) {
@ -1219,16 +1223,16 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
if (dataModel.isMinBuyerSecurityDeposit()) {
buyerSecurityDepositLabel.set(Res.get("createOffer.minSecurityDepositUsed"));
buyerSecurityDeposit.set(btcFormatter.formatCoin(Restrictions.getMinBuyerSecurityDepositAsCoin()));
buyerSecurityDeposit.set(HavenoUtils.formatToXmr(Restrictions.getMinBuyerSecurityDeposit()));
} else {
buyerSecurityDepositLabel.set(getSecurityDepositLabel());
buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDeposit().get()));
buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDepositPct().get()));
}
}
void updateButtonDisableState() {
boolean inputDataValid = isBtcInputValid(amount.get()).isValid &&
isBtcInputValid(minAmount.get()).isValid &&
boolean inputDataValid = isXmrInputValid(amount.get()).isValid &&
isXmrInputValid(minAmount.get()).isValid &&
isPriceInputValid(price.get()).isValid &&
dataModel.getPrice().get() != null &&
dataModel.getPrice().get().getValue() != 0 &&
@ -1249,10 +1253,6 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
isPlaceOfferButtonDisabled.set(createOfferRequested || !inputDataValid || !dataModel.getIsXmrWalletFunded().get());
}
private CoinFormatter getFormatterForMakerFee() {
return btcFormatter;
}
private void updateMarketPriceToManual() {
final String currencyCode = dataModel.getTradeCurrencyCode().get();
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);

View File

@ -23,8 +23,6 @@ import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.offer.OfferUtil;
import org.bitcoinj.core.Coin;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
@ -32,7 +30,7 @@ import javafx.beans.property.SimpleObjectProperty;
import lombok.Getter;
import static bisq.core.util.coin.CoinUtil.minCoin;
import java.math.BigInteger;
/**
* Domain for that UI element.
@ -48,19 +46,19 @@ public abstract class OfferDataModel extends ActivatableDataModel {
@Getter
protected final BooleanProperty isXmrWalletFunded = new SimpleBooleanProperty();
@Getter
protected final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
protected final ObjectProperty<BigInteger> totalToPay = new SimpleObjectProperty<>();
@Getter
protected final ObjectProperty<Coin> balance = new SimpleObjectProperty<>();
protected final ObjectProperty<BigInteger> balance = new SimpleObjectProperty<>();
@Getter
protected final ObjectProperty<Coin> availableBalance = new SimpleObjectProperty<>();
protected final ObjectProperty<BigInteger> availableBalance = new SimpleObjectProperty<>();
@Getter
protected final ObjectProperty<Coin> missingCoin = new SimpleObjectProperty<>(Coin.ZERO);
protected final ObjectProperty<BigInteger> missingCoin = new SimpleObjectProperty<>(BigInteger.valueOf(0));
@Getter
protected final BooleanProperty showWalletFundedNotification = new SimpleBooleanProperty();
@Getter
protected Coin totalBalance;
protected BigInteger totalBalance;
@Getter
protected Coin totalAvailableBalance;
protected BigInteger totalAvailableBalance;
protected XmrAddressEntry addressEntry;
protected boolean useSavingsWallet;
@ -70,37 +68,37 @@ public abstract class OfferDataModel extends ActivatableDataModel {
}
protected void updateBalance() {
Coin tradeWalletBalance = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex());
BigInteger tradeWalletBalance = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex());
if (useSavingsWallet) {
Coin walletBalance = xmrWalletService.getBalance();
BigInteger walletBalance = xmrWalletService.getBalance();
totalBalance = walletBalance.add(tradeWalletBalance);
if (totalToPayAsCoin.get() != null) {
balance.set(minCoin(totalToPayAsCoin.get(), totalBalance));
if (totalToPay.get() != null) {
balance.set(totalToPay.get().min(totalBalance));
}
} else {
balance.set(tradeWalletBalance);
}
missingCoin.set(offerUtil.getBalanceShortage(totalToPayAsCoin.get(), balance.get()));
isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPayAsCoin.get(), balance.get()));
if (totalToPayAsCoin.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) {
missingCoin.set(offerUtil.getBalanceShortage(totalToPay.get(), balance.get()));
isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPay.get(), balance.get()));
if (totalToPay.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) {
showWalletFundedNotification.set(true);
}
}
protected void updateAvailableBalance() {
Coin tradeWalletBalance = xmrWalletService.getAvailableBalanceForSubaddress(addressEntry.getSubaddressIndex());
BigInteger tradeWalletBalance = xmrWalletService.getAvailableBalanceForSubaddress(addressEntry.getSubaddressIndex());
if (useSavingsWallet) {
Coin walletAvailableBalance = xmrWalletService.getAvailableBalance();
BigInteger walletAvailableBalance = xmrWalletService.getAvailableBalance();
totalAvailableBalance = walletAvailableBalance.add(tradeWalletBalance);
if (totalToPayAsCoin.get() != null) {
availableBalance.set(minCoin(totalToPayAsCoin.get(), totalAvailableBalance));
if (totalToPay.get() != null) {
availableBalance.set(totalToPay.get().min(totalAvailableBalance));
}
} else {
availableBalance.set(tradeWalletBalance);
}
missingCoin.set(offerUtil.getBalanceShortage(totalToPayAsCoin.get(), availableBalance.get()));
isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPayAsCoin.get(), availableBalance.get()));
if (totalToPayAsCoin.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) {
missingCoin.set(offerUtil.getBalanceShortage(totalToPay.get(), availableBalance.get()));
isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPay.get(), availableBalance.get()));
if (totalToPay.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) {
showWalletFundedNotification.set(true);
}
}

View File

@ -23,19 +23,17 @@ import bisq.desktop.util.GUIUtil;
import bisq.core.locale.Res;
import bisq.core.monetary.Volume;
import bisq.core.offer.OfferUtil;
import bisq.core.trade.HavenoUtils;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.CoinFormatter;
import bisq.common.app.DevEnv;
import org.bitcoinj.core.Coin;
import java.math.BigInteger;
import java.util.Optional;
// Shared utils for ViewModels
public class OfferViewModelUtil {
public static String getTradeFeeWithFiatEquivalent(OfferUtil offerUtil,
Coin tradeFee,
BigInteger tradeFee,
CoinFormatter formatter) {
Optional<Volume> optionalBtcFeeInFiat = offerUtil.getFeeInUserFiatCurrency(tradeFee,
@ -45,13 +43,13 @@ public class OfferViewModelUtil {
}
public static String getTradeFeeWithFiatEquivalentAndPercentage(OfferUtil offerUtil,
Coin tradeFee,
Coin tradeAmount,
BigInteger tradeFee,
BigInteger tradeAmount,
CoinFormatter formatter,
Coin minTradeFee) {
String feeAsBtc = formatter.formatCoinWithCode(tradeFee);
BigInteger minTradeFee) {
String feeAsBtc = HavenoUtils.formatToXmrWithCode(tradeFee);
String percentage;
if (!tradeFee.isGreaterThan(minTradeFee)) {
if (tradeFee.compareTo(minTradeFee) <= 0) {
percentage = Res.get("guiUtil.requiredMinimum")
.replace("(", "")
.replace(")", "");

View File

@ -22,7 +22,7 @@ import bisq.desktop.common.model.ViewModel;
import bisq.desktop.main.offer.MutableOfferViewModel;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.offer.OfferUtil;
import bisq.core.payment.validation.BtcValidator;
import bisq.core.payment.validation.XmrValidator;
import bisq.core.payment.validation.FiatVolumeValidator;
import bisq.core.payment.validation.SecurityDepositValidator;
import bisq.core.provider.price.PriceFeedService;
@ -42,7 +42,7 @@ class CreateOfferViewModel extends MutableOfferViewModel<CreateOfferDataModel> i
FiatVolumeValidator fiatVolumeValidator,
FiatPriceValidator fiatPriceValidator,
AltcoinValidator altcoinValidator,
BtcValidator btcValidator,
XmrValidator btcValidator,
SecurityDepositValidator securityDepositValidator,
PriceFeedService priceFeedService,
AccountAgeWitnessService accountAgeWitnessService,

View File

@ -61,6 +61,7 @@ import bisq.core.offer.OfferRestrictions;
import bisq.core.offer.OpenOffer;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.trade.HavenoUtils;
import bisq.core.user.DontShowAgainLookup;
import bisq.core.util.coin.CoinFormatter;
@ -110,6 +111,8 @@ import javafx.collections.ListChangeListener;
import javafx.util.Callback;
import javafx.util.StringConverter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Comparator;
import java.util.Map;
import java.util.Optional;
@ -299,15 +302,15 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
avatarColumn.setComparator(Comparator.comparing(o -> model.getNumTrades(o.getOffer())));
depositColumn.setComparator(Comparator.comparing(item -> {
boolean isSellOffer = item.getOffer().getDirection() == OfferDirection.SELL;
Coin deposit = isSellOffer ?
BigInteger deposit = isSellOffer ?
item.getOffer().getBuyerSecurityDeposit() :
item.getOffer().getSellerSecurityDeposit();
double amountValue = item.getOffer().getAmount().getValue();
long amountValue = item.getOffer().getAmount().longValueExact();
if ((deposit == null || amountValue == 0)) {
return 0d;
} else {
return deposit.getValue() / amountValue;
return BigDecimal.valueOf(deposit.longValueExact()).divide(BigDecimal.valueOf(amountValue)).doubleValue();
}
}, Comparator.nullsFirst(Comparator.naturalOrder())));
@ -715,7 +718,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
if (model.isBootstrappedOrShowPopup()) {
String key = "RemoveOfferWarning";
if (DontShowAgainLookup.showAgain(key)) {
String message = offer.getMakerFee().isPositive() ?
String message = offer.getMakerFee().compareTo(BigInteger.valueOf(0)) > 0 ?
Res.get("popup.warning.removeOffer", model.getMakerFeeAsString(offer)) :
Res.get("popup.warning.removeNoFeeOffer");
new Popup().warning(message)
@ -1034,7 +1037,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
} else {
setText("");
setGraphic(new ColoredDecimalPlacesWithZerosText(model.formatDepositString(
deposit, item.getOffer().getAmount().getValue()),
deposit, item.getOffer().getAmount().longValueExact()),
GUIUtil.AMOUNT_DECIMALS_WITH_ZEROS));
}
} else {
@ -1186,7 +1189,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
Res.get("offerbook.timeSinceSigning"),
Res.get("offerbook.timeSinceSigning.help",
SignedWitnessService.SIGNER_AGE_DAYS,
formatter.formatCoinWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT))) {
HavenoUtils.formatToXmrWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT))) {
{
setMinWidth(60);
setSortable(true);
@ -1206,7 +1209,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
if (item != null && !empty) {
var witnessAgeData = item.getWitnessAgeData(accountAgeWitnessService, signedWitnessService);
var label = witnessAgeData.isSigningRequired()
? new AccountStatusTooltipLabel(witnessAgeData, formatter)
? new AccountStatusTooltipLabel(witnessAgeData)
: new InfoAutoTooltipLabel(witnessAgeData.getDisplayString(), witnessAgeData.getIcon(), ContentDisplay.RIGHT, witnessAgeData.getInfo());
setGraphic(label);
} else {

View File

@ -47,6 +47,7 @@ import bisq.core.payment.PaymentAccountUtil;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.ClosedTradableManager;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.user.Preferences;
import bisq.core.user.User;
@ -61,8 +62,6 @@ import bisq.network.p2p.P2PService;
import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ResultHandler;
import org.bitcoinj.core.Coin;
import com.google.common.base.Joiner;
import javafx.beans.property.BooleanProperty;
@ -78,6 +77,8 @@ import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.Comparator;
@ -181,7 +182,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
filterItemsListener = c -> {
final Optional<OfferBookListItem> highestAmountOffer = filteredItems.stream()
.max(Comparator.comparingLong(o -> o.getOffer().getAmount().getValue()));
.max(Comparator.comparingLong(o -> o.getOffer().getAmount().longValueExact()));
final boolean containsRangeAmount = filteredItems.stream().anyMatch(o -> o.getOffer().isRange());
@ -196,7 +197,6 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
maxPlacesForAmount.set(formatAmount(item.getOffer(), false).length());
maxPlacesForVolume.set(formatVolume(item.getOffer(), false).length());
}
}
final Optional<OfferBookListItem> highestPriceOffer = filteredItems.stream()
@ -627,7 +627,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
}
public String getMakerFeeAsString(Offer offer) {
return btcFormatter.formatCoinWithCode(offer.getMakerFee());
return HavenoUtils.formatToXmrWithCode(offer.getMakerFee());
}
private static String getDirectionWithCodeDetailed(OfferDirection direction, String currencyCode) {
@ -637,9 +637,9 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
return (direction == OfferDirection.SELL) ? Res.get("shared.buyingCurrency", currencyCode) : Res.get("shared.sellingCurrency", currencyCode);
}
public String formatDepositString(Coin deposit, long amount) {
var percentage = FormattingUtils.formatToRoundedPercentWithSymbol(deposit.getValue() / (double) amount);
return btcFormatter.formatCoin(deposit) + " (" + percentage + ")";
public String formatDepositString(BigInteger deposit, long amount) {
var percentage = FormattingUtils.formatToRoundedPercentWithSymbol(BigDecimal.valueOf(deposit.longValueExact()).divide(BigDecimal.valueOf(amount)).doubleValue());
return HavenoUtils.formatToXmr(deposit) + " (" + percentage + ")";
}
PaymentMethod getShowAllEntryForPaymentMethod() {

View File

@ -26,7 +26,6 @@ import bisq.common.handlers.ErrorMessageHandler;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.btc.listeners.XmrBalanceListener;
import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.filter.FilterManager;
import bisq.core.locale.CurrencyUtil;
@ -47,12 +46,9 @@ import bisq.core.trade.handlers.TradeResultHandler;
import bisq.core.user.Preferences;
import bisq.core.user.User;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.CoinUtil;
import bisq.network.p2p.P2PService;
import org.bitcoinj.core.Coin;
import com.google.inject.Inject;
import javafx.beans.property.IntegerProperty;
@ -94,13 +90,13 @@ class TakeOfferDataModel extends OfferDataModel {
private final Navigation navigation;
private final P2PService p2PService;
private Coin securityDeposit;
private BigInteger securityDeposit;
private Offer offer;
// final BooleanProperty isFeeFromFundingTxSufficient = new SimpleBooleanProperty();
// final BooleanProperty isMainNet = new SimpleBooleanProperty();
private final ObjectProperty<Coin> amount = new SimpleObjectProperty<>();
private final ObjectProperty<BigInteger> amount = new SimpleObjectProperty<>();
final ObjectProperty<Volume> volume = new SimpleObjectProperty<>();
private XmrBalanceListener balanceListener;
@ -198,7 +194,7 @@ class TakeOfferDataModel extends OfferDataModel {
checkArgument(!possiblePaymentAccounts.isEmpty(), "possiblePaymentAccounts.isEmpty()");
paymentAccount = getLastSelectedPaymentAccount();
this.amount.set(Coin.valueOf(Math.min(offer.getAmount().value, getMaxTradeLimit())));
this.amount.set(offer.getAmount().min(BigInteger.valueOf(getMaxTradeLimit())));
securityDeposit = offer.getDirection() == OfferDirection.SELL ?
getBuyerSecurityDeposit() :
@ -263,7 +259,7 @@ class TakeOfferDataModel extends OfferDataModel {
void onTakeOffer(TradeResultHandler tradeResultHandler, ErrorMessageHandler errorMessageHandler) {
checkNotNull(getTakerFee(), "takerFee must not be null");
Coin fundsNeededForTrade = getFundsNeededForTrade();
BigInteger fundsNeededForTrade = getFundsNeededForTrade();
if (isBuyOffer())
fundsNeededForTrade = fundsNeededForTrade.add(amount.get());
@ -301,7 +297,7 @@ class TakeOfferDataModel extends OfferDataModel {
this.paymentAccount = paymentAccount;
long myLimit = getMaxTradeLimit();
this.amount.set(Coin.valueOf(Math.max(offer.getMinAmount().value, Math.min(amount.get().value, myLimit))));
this.amount.set(offer.getMinAmount().max(amount.get().min(BigInteger.valueOf(myLimit))));
preferences.setTakeOfferSelectedPaymentAccountId(paymentAccount.getId());
}
@ -385,7 +381,7 @@ class TakeOfferDataModel extends OfferDataModel {
void calculateVolume() {
if (tradePrice != null && offer != null &&
amount.get() != null &&
!amount.get().isZero()) {
amount.get().compareTo(BigInteger.valueOf(0)) != 0) {
Volume volumeByAmount = tradePrice.getVolumeByAmount(amount.get());
if (offer.getPaymentMethod().getId().equals(PaymentMethod.HAL_CASH_ID))
volumeByAmount = VolumeUtil.getAdjustedVolumeForHalCash(volumeByAmount);
@ -398,8 +394,8 @@ class TakeOfferDataModel extends OfferDataModel {
}
}
void applyAmount(Coin amount) {
this.amount.set(Coin.valueOf(Math.min(amount.value, getMaxTradeLimit())));
void applyAmount(BigInteger amount) {
this.amount.set(amount.min(BigInteger.valueOf(getMaxTradeLimit())));
calculateTotalToPay();
}
@ -408,15 +404,15 @@ class TakeOfferDataModel extends OfferDataModel {
// Taker pays 2 times the tx fee because the mining fee might be different when maker created the offer
// and reserved his funds, so that would not work well with dynamic fees.
// The mining fee for the takeOfferFee tx is deducted from the createOfferFee and not visible to the trader
final Coin takerFee = getTakerFee();
final BigInteger takerFee = getTakerFee();
if (offer != null && amount.get() != null && takerFee != null) {
Coin feeAndSecDeposit = securityDeposit.add(takerFee);
BigInteger feeAndSecDeposit = securityDeposit.add(takerFee);
if (isBuyOffer())
totalToPayAsCoin.set(feeAndSecDeposit.add(amount.get()));
totalToPay.set(feeAndSecDeposit.add(amount.get()));
else
totalToPayAsCoin.set(feeAndSecDeposit);
totalToPay.set(feeAndSecDeposit);
updateAvailableBalance();
log.debug("totalToPayAsCoin {}", totalToPayAsCoin.get().toFriendlyString());
log.debug("totalToPay {}", totalToPay.get());
}
}
@ -433,7 +429,7 @@ class TakeOfferDataModel extends OfferDataModel {
}
@Nullable
Coin getTakerFee() {
BigInteger getTakerFee() {
return HavenoUtils.getTakerFee(this.amount.get());
}
@ -450,33 +446,22 @@ class TakeOfferDataModel extends OfferDataModel {
boolean isMinAmountLessOrEqualAmount() {
//noinspection SimplifiableIfStatement
if (offer != null && amount.get() != null)
return !offer.getMinAmount().isGreaterThan(amount.get());
return offer.getMinAmount().compareTo(amount.get()) <= 0;
return true;
}
boolean isAmountLargerThanOfferAmount() {
//noinspection SimplifiableIfStatement
if (amount.get() != null && offer != null)
return amount.get().isGreaterThan(offer.getAmount());
return amount.get().compareTo(offer.getAmount()) > 0;
return true;
}
boolean wouldCreateDustForMaker() {
//noinspection SimplifiableIfStatement
boolean result;
if (amount.get() != null && offer != null) {
Coin customAmount = offer.getAmount().subtract(amount.get());
result = customAmount.isPositive() && customAmount.isLessThan(Restrictions.getMinNonDustOutput());
if (result)
log.info("would create dust for maker, customAmount={}, Restrictions.getMinNonDustOutput()={}", customAmount, Restrictions.getMinNonDustOutput());
} else {
result = true;
}
return result;
return false; // TODO: update for XMR?
}
ReadOnlyObjectProperty<Coin> getAmount() {
ReadOnlyObjectProperty<BigInteger> getAmount() {
return amount;
}
@ -493,7 +478,7 @@ class TakeOfferDataModel extends OfferDataModel {
}
@NotNull
private Coin getFundsNeededForTrade() {
private BigInteger getFundsNeededForTrade() {
return getSecurityDeposit();
}
@ -501,15 +486,15 @@ class TakeOfferDataModel extends OfferDataModel {
return addressEntry;
}
public Coin getSecurityDeposit() {
public BigInteger getSecurityDeposit() {
return securityDeposit;
}
public Coin getBuyerSecurityDeposit() {
public BigInteger getBuyerSecurityDeposit() {
return offer.getBuyerSecurityDeposit();
}
public Coin getSellerSecurityDeposit() {
public BigInteger getSellerSecurityDeposit() {
return offer.getSellerSecurityDeposit();
}

View File

@ -54,6 +54,7 @@ import bisq.core.offer.Offer;
import bisq.core.payment.FasterPaymentsAccount;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.trade.HavenoUtils;
import bisq.core.user.DontShowAgainLookup;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
@ -65,8 +66,6 @@ import bisq.common.util.Tuple3;
import bisq.common.util.Tuple4;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import net.glxn.qrgen.QRCode;
import net.glxn.qrgen.image.ImageType;
@ -108,6 +107,7 @@ import javafx.beans.value.ChangeListener;
import java.net.URI;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
@ -208,7 +208,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
if (newValue) {
Notification walletFundedNotification = new Notification()
.headLine(Res.get("notification.walletUpdate.headline"))
.notification(Res.get("notification.walletUpdate.msg", formatter.formatCoinWithCode(model.dataModel.getTotalToPayAsCoin().get())))
.notification(Res.get("notification.walletUpdate.msg", HavenoUtils.formatToXmrWithCode(model.dataModel.getTotalToPay().get())))
.autoClose();
walletFundedNotification.show();
@ -262,7 +262,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
model.onPaymentAccountSelected(lastPaymentAccount);
}
balanceTextField.setTargetAmount(model.dataModel.getTotalToPayAsCoin().get());
balanceTextField.setTargetAmount(model.dataModel.getTotalToPay().get());
maybeShowTakeOfferFromUnsignedAccountWarning(model.dataModel.getOffer());
maybeShowClearXchangeWarning(lastPaymentAccount);
@ -359,8 +359,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
// Called from parent as the view does not get notified when the tab is closed
public void onClose() {
Coin availableBalance = model.dataModel.getAvailableBalance().get();
if (availableBalance != null && availableBalance.isPositive() && !model.takeOfferCompleted.get() && !DevEnv.isDevMode()) {
BigInteger availableBalance = model.dataModel.getAvailableBalance().get();
if (availableBalance != null && availableBalance.compareTo(BigInteger.valueOf(0)) > 0 && !model.takeOfferCompleted.get() && !DevEnv.isDevMode()) {
model.dataModel.swapTradeToSavings();
}
}
@ -445,7 +445,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
updateOfferElementsStyle();
balanceTextField.setTargetAmount(model.dataModel.getTotalToPayAsCoin().get());
balanceTextField.setTargetAmount(model.dataModel.getTotalToPay().get());
if (!DevEnv.isDevMode()) {
String tradeAmountText = model.isSeller() ? Res.get("takeOffer.takeOfferFundWalletInfo.tradeAmount", model.getTradeAmount()) : "";
@ -481,7 +481,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
if (walletFundedNotification == null) {
walletFundedNotification = new Notification()
.headLine(Res.get("notification.walletUpdate.headline"))
.notification(Res.get("notification.takeOffer.walletUpdate.msg", formatter.formatCoinWithCode(model.dataModel.getTotalToPayAsCoin().get())))
.notification(Res.get("notification.takeOffer.walletUpdate.msg", HavenoUtils.formatToXmrWithCode(model.dataModel.getTotalToPay().get())))
.autoClose();
walletFundedNotification.show();
}
@ -542,7 +542,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
amountTextField.textProperty().bindBidirectional(model.amount);
volumeTextField.textProperty().bindBidirectional(model.volume);
totalToPayTextField.textProperty().bind(model.totalToPay);
addressTextField.amountAsCoinProperty().bind(model.dataModel.getMissingCoin());
addressTextField.amountAsProperty().bind(model.dataModel.getMissingCoin());
amountTextField.validationResultProperty().bind(model.amountValidationResult);
priceCurrencyLabel.textProperty().bind(createStringBinding(() -> CurrencyUtil.getCounterCurrency(model.dataModel.getCurrencyCode())));
priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty());
@ -566,7 +566,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
amountTextField.textProperty().unbindBidirectional(model.amount);
volumeTextField.textProperty().unbindBidirectional(model.volume);
totalToPayTextField.textProperty().unbind();
addressTextField.amountAsCoinProperty().unbind();
addressTextField.amountAsProperty().unbind();
amountTextField.validationResultProperty().unbind();
priceCurrencyLabel.textProperty().unbind();
priceAsPercentageLabel.prefWidthProperty().unbind();

View File

@ -37,7 +37,7 @@ import bisq.core.offer.OfferRestrictions;
import bisq.core.offer.OfferUtil;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.payment.validation.BtcValidator;
import bisq.core.payment.validation.XmrValidator;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.util.FormattingUtils;
@ -52,8 +52,6 @@ import bisq.network.p2p.network.Connection;
import bisq.network.p2p.network.ConnectionListener;
import bisq.common.UserThread;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javax.inject.Named;
@ -78,10 +76,12 @@ import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
import static javafx.beans.binding.Bindings.createStringBinding;
import java.math.BigInteger;
class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> implements ViewModel {
final TakeOfferDataModel dataModel;
private final OfferUtil offerUtil;
private final BtcValidator btcValidator;
private final XmrValidator btcValidator;
private final P2PService p2PService;
private final AccountAgeWitnessService accountAgeWitnessService;
private final Navigation navigation;
@ -118,8 +118,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>();
private ChangeListener<String> amountListener;
private ChangeListener<Coin> amountAsCoinListener;
private ChangeListener<String> amountStrListener;
private ChangeListener<BigInteger> amountListener;
private ChangeListener<Boolean> isWalletFundedListener;
private ChangeListener<Trade.State> tradeStateListener;
private ChangeListener<Offer.State> offerStateListener;
@ -136,7 +136,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
@Inject
public TakeOfferViewModel(TakeOfferDataModel dataModel,
OfferUtil offerUtil,
BtcValidator btcValidator,
XmrValidator btcValidator,
P2PService p2PService,
AccountAgeWitnessService accountAgeWitnessService,
Navigation navigation,
@ -168,7 +168,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
volumeDescriptionLabel.set(Res.get(sellVolumeDescriptionKey, dataModel.getCurrencyCode()));
}
amount.set(xmrFormatter.formatCoin(dataModel.getAmount().get()));
amount.set(HavenoUtils.formatToXmr(dataModel.getAmount().get()));
showTransactionPublishedScreen.set(false);
// when getting back to an open screen we want to re-check again
@ -209,7 +209,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
? Res.get(buyAmountDescriptionKey)
: Res.get(sellAmountDescriptionKey);
amountRange = xmrFormatter.formatCoin(offer.getMinAmount()) + " - " + xmrFormatter.formatCoin(offer.getAmount());
amountRange = HavenoUtils.formatToXmr(offer.getMinAmount()) + " - " + HavenoUtils.formatToXmr(offer.getAmount());
price = FormattingUtils.formatPrice(dataModel.tradePrice);
marketPriceMargin = FormattingUtils.formatToPercent(offer.getMarketPriceMarginPct());
paymentLabel = Res.get("takeOffer.fundsBox.paymentLabel", offer.getShortId());
@ -219,7 +219,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
errorMessage.set(offer.getErrorMessage());
btcValidator.setMaxValue(offer.getAmount());
btcValidator.setMaxTradeLimit(Coin.valueOf(Math.min(dataModel.getMaxTradeLimit(), offer.getAmount().value)));
btcValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit()).min(offer.getAmount()));
btcValidator.setMinValue(offer.getMinAmount());
}
@ -248,7 +248,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
dataModel.onPaymentAccountSelected(paymentAccount);
btcValidator.setMaxTradeLimit(Coin.valueOf(Math.min(dataModel.getMaxTradeLimit(), offer.getAmount().value)));
btcValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit()).min(offer.getAmount()));
updateButtonDisableState();
}
@ -265,8 +265,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
return true;
} else {
new Popup().warning(Res.get("shared.notEnoughFunds",
xmrFormatter.formatCoinWithCode(dataModel.getTotalToPayAsCoin().get()),
xmrFormatter.formatCoinWithCode(dataModel.getTotalAvailableBalance())))
HavenoUtils.formatToXmrWithCode(dataModel.getTotalToPay().get()),
HavenoUtils.formatToXmrWithCode(dataModel.getTotalAvailableBalance())))
.actionButtonTextWithGoTo("navigation.funds.depositFunds")
.onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, DepositView.class))
.show();
@ -276,13 +276,13 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
private void applyTakerFee() {
tradeFeeDescription.set(Res.get("createOffer.tradeFee.descriptionBTCOnly"));
Coin takerFeeAsCoin = dataModel.getTakerFee();
if (takerFeeAsCoin == null) {
BigInteger takerFee = dataModel.getTakerFee();
if (takerFee == null) {
return;
}
isTradeFeeVisible.setValue(true);
tradeFee.set(getFormatterForTakerFee().formatCoin(takerFeeAsCoin));
tradeFee.set(HavenoUtils.formatToXmr(takerFee));
tradeFeeInXmrWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil,
dataModel.getTakerFee(),
xmrFormatter));
@ -303,27 +303,27 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
// only allow max 4 decimal places for btc values
setAmountToModel();
// reformat input
amount.set(xmrFormatter.formatCoin(dataModel.getAmount().get()));
amount.set(HavenoUtils.formatToXmr(dataModel.getAmount().get()));
calculateVolume();
Price tradePrice = dataModel.tradePrice;
long maxTradeLimit = dataModel.getMaxTradeLimit();
if (dataModel.getPaymentMethod().getId().equals(PaymentMethod.HAL_CASH_ID)) {
Coin adjustedAmountForHalCash = CoinUtil.getAdjustedAmountForHalCash(dataModel.getAmount().get(),
BigInteger adjustedAmountForHalCash = CoinUtil.getAdjustedAmountForHalCash(dataModel.getAmount().get(),
tradePrice,
maxTradeLimit);
dataModel.applyAmount(adjustedAmountForHalCash);
amount.set(xmrFormatter.formatCoin(dataModel.getAmount().get()));
amount.set(HavenoUtils.formatToXmr(dataModel.getAmount().get()));
} else if (dataModel.getOffer().isFiatOffer()) {
if (!isAmountEqualMinAmount(dataModel.getAmount().get()) && (!isAmountEqualMaxAmount(dataModel.getAmount().get()))) {
// We only apply the rounding if the amount is variable (minAmount is lower as amount).
// Otherwise we could get an amount lower then the minAmount set by rounding
Coin roundedAmount = CoinUtil.getRoundedFiatAmount(dataModel.getAmount().get(), tradePrice,
BigInteger roundedAmount = CoinUtil.getRoundedFiatAmount(dataModel.getAmount().get(), tradePrice,
maxTradeLimit);
dataModel.applyAmount(roundedAmount);
}
amount.set(xmrFormatter.formatCoin(dataModel.getAmount().get()));
amount.set(HavenoUtils.formatToXmr(dataModel.getAmount().get()));
}
if (!dataModel.isMinAmountLessOrEqualAmount())
@ -337,16 +337,16 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
if (dataModel.wouldCreateDustForMaker())
amountValidationResult.set(new InputValidator.ValidationResult(false,
Res.get("takeOffer.validation.amountLargerThanOfferAmountMinusFee")));
} else if (btcValidator.getMaxTradeLimit() != null && btcValidator.getMaxTradeLimit().value == OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value) {
} else if (btcValidator.getMaxTradeLimit() != null && btcValidator.getMaxTradeLimit().equals(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT)) {
if (dataModel.getDirection() == OfferDirection.BUY) {
new Popup().information(Res.get("popup.warning.tradeLimitDueAccountAgeRestriction.seller",
xmrFormatter.formatCoinWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT),
HavenoUtils.formatToXmrWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT),
Res.get("offerbook.warning.newVersionAnnouncement")))
.width(900)
.show();
} else {
new Popup().information(Res.get("popup.warning.tradeLimitDueAccountAgeRestriction.buyer",
xmrFormatter.formatCoinWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT),
HavenoUtils.formatToXmrWithCode(OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT),
Res.get("offerbook.warning.newVersionAnnouncement")))
.width(900)
.show();
@ -468,7 +468,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
private void addBindings() {
volume.bind(createStringBinding(() -> VolumeUtil.formatVolume(dataModel.volume.get()), dataModel.volume));
totalToPay.bind(createStringBinding(() -> xmrFormatter.formatCoinWithCode(dataModel.getTotalToPayAsCoin().get()), dataModel.getTotalToPayAsCoin()));
totalToPay.bind(createStringBinding(() -> HavenoUtils.formatToXmrWithCode(dataModel.getTotalToPay().get()), dataModel.getTotalToPay()));
}
@ -479,7 +479,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
}
private void createListeners() {
amountListener = (ov, oldValue, newValue) -> {
amountStrListener = (ov, oldValue, newValue) -> {
if (isBtcInputValid(newValue).isValid) {
setAmountToModel();
calculateVolume();
@ -488,8 +488,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
}
updateButtonDisableState();
};
amountAsCoinListener = (ov, oldValue, newValue) -> {
amount.set(xmrFormatter.formatCoin(newValue));
amountListener = (ov, oldValue, newValue) -> {
amount.set(HavenoUtils.formatToXmr(newValue));
applyTakerFee();
};
isWalletFundedListener = (ov, oldValue, newValue) -> updateButtonDisableState();
@ -548,10 +548,10 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
private void addListeners() {
// Bidirectional bindings are used for all input fields: amount, price, volume and minAmount
// We do volume/amount calculation during input, so user has immediate feedback
amount.addListener(amountListener);
amount.addListener(amountStrListener);
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
dataModel.getAmount().addListener(amountAsCoinListener);
dataModel.getAmount().addListener(amountListener);
dataModel.getIsXmrWalletFunded().addListener(isWalletFundedListener);
dataModel.getMempoolStatus().addListener(getMempoolStatusListener);
@ -563,10 +563,10 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
}
private void removeListeners() {
amount.removeListener(amountListener);
amount.removeListener(amountStrListener);
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
dataModel.getAmount().removeListener(amountAsCoinListener);
dataModel.getAmount().removeListener(amountListener);
dataModel.getMempoolStatus().removeListener(getMempoolStatusListener);
dataModel.getIsXmrWalletFunded().removeListener(isWalletFundedListener);
@ -592,7 +592,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
private void setAmountToModel() {
if (amount.get() != null && !amount.get().isEmpty()) {
Coin amount = DisplayUtils.parseToCoinWith4Decimals(this.amount.get(), xmrFormatter);
BigInteger amount = HavenoUtils.coinToAtomicUnits(DisplayUtils.parseToCoinWith4Decimals(this.amount.get(), xmrFormatter));
long maxTradeLimit = dataModel.getMaxTradeLimit();
Price price = dataModel.tradePrice;
if (price != null) {
@ -609,12 +609,12 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
}
}
private boolean isAmountEqualMinAmount(Coin amount) {
return amount.value == offer.getMinAmount().value;
private boolean isAmountEqualMinAmount(BigInteger amount) {
return offer.getMinAmount().equals(amount);
}
private boolean isAmountEqualMaxAmount(Coin amount) {
return amount.value == offer.getAmount().value;
private boolean isAmountEqualMaxAmount(BigInteger amount) {
return offer.getAmount().equals(amount);
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -679,12 +679,12 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
dataModel.getSecurityDeposit(),
dataModel.getAmount().get(),
xmrFormatter,
Restrictions.getMinBuyerSecurityDepositAsCoin()
Restrictions.getMinBuyerSecurityDeposit()
);
}
public String getSecurityDepositWithCode() {
return xmrFormatter.formatCoinWithCode(dataModel.getSecurityDeposit());
return HavenoUtils.formatToXmrWithCode(dataModel.getSecurityDeposit());
}
public String getTradeFee() {
@ -696,8 +696,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
}
public String getTakerFeePercentage() {
final Coin takerFeeAsCoin = dataModel.getTakerFee();
return takerFeeAsCoin != null ? GUIUtil.getPercentage(takerFeeAsCoin, dataModel.getAmount().get()) : Res.get("shared.na");
final BigInteger takerFee = dataModel.getTakerFee();
return takerFee != null ? GUIUtil.getPercentage(takerFee, dataModel.getAmount().get()) : Res.get("shared.na");
}
public String getTotalToPayInfo() {

View File

@ -25,10 +25,8 @@ import bisq.desktop.util.Layout;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.locale.CountryUtil;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.offer.Offer;
import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.support.dispute.Dispute;
import bisq.core.support.dispute.DisputeList;
@ -37,19 +35,17 @@ import bisq.core.support.dispute.arbitration.ArbitrationManager;
import bisq.core.support.dispute.mediation.MediationManager;
import bisq.core.support.dispute.refund.RefundManager;
import bisq.core.trade.Contract;
import bisq.core.trade.HavenoUtils;
import bisq.core.util.FormattingUtils;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.CoinFormatter;
import bisq.network.p2p.NodeAddress;
import bisq.common.UserThread;
import bisq.common.crypto.PubKeyRing;
import org.bitcoinj.core.Utils;
import javax.inject.Inject;
import javax.inject.Named;
import com.google.common.base.Joiner;
@ -80,7 +76,6 @@ public class ContractWindow extends Overlay<ContractWindow> {
private final MediationManager mediationManager;
private final RefundManager refundManager;
private final AccountAgeWitnessService accountAgeWitnessService;
private final CoinFormatter formatter;
private Dispute dispute;
@ -92,13 +87,11 @@ public class ContractWindow extends Overlay<ContractWindow> {
public ContractWindow(ArbitrationManager arbitrationManager,
MediationManager mediationManager,
RefundManager refundManager,
AccountAgeWitnessService accountAgeWitnessService,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter) {
AccountAgeWitnessService accountAgeWitnessService) {
this.arbitrationManager = arbitrationManager;
this.mediationManager = mediationManager;
this.refundManager = refundManager;
this.accountAgeWitnessService = accountAgeWitnessService;
this.formatter = formatter;
type = Type.Confirmation;
}
@ -160,18 +153,18 @@ public class ContractWindow extends Overlay<ContractWindow> {
addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.tradePrice"),
FormattingUtils.formatPrice(contract.getPrice()));
addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.tradeAmount"),
formatter.formatCoinWithCode(contract.getTradeAmount()));
HavenoUtils.formatToXmrWithCode(contract.getTradeAmount()));
addConfirmationLabelTextField(gridPane,
++rowIndex,
VolumeUtil.formatVolumeLabel(currencyCode, ":"),
VolumeUtil.formatVolumeWithCode(contract.getTradeVolume()));
String securityDeposit = Res.getWithColAndCap("shared.buyer") +
" " +
formatter.formatCoinWithCode(offer.getBuyerSecurityDeposit()) +
HavenoUtils.formatToXmrWithCode(offer.getBuyerSecurityDeposit()) +
" / " +
Res.getWithColAndCap("shared.seller") +
" " +
formatter.formatCoinWithCode(offer.getSellerSecurityDeposit());
HavenoUtils.formatToXmrWithCode(offer.getSellerSecurityDeposit());
addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), securityDeposit);
addConfirmationLabelTextField(gridPane,
++rowIndex,

View File

@ -43,7 +43,6 @@ import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.trade.TradeManager;
import bisq.core.util.FormattingUtils;
import bisq.core.util.ParsingUtils;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.CoinFormatter;
@ -76,6 +75,7 @@ import javafx.geometry.Insets;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import java.math.BigInteger;
import java.util.Date;
import java.util.Optional;
@ -278,26 +278,26 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
}
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("disputeSummaryWindow.role"), role);
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.tradeAmount"),
formatter.formatCoinWithCode(contract.getTradeAmount()));
HavenoUtils.formatToXmrWithCode(contract.getTradeAmount()));
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.tradePrice"),
FormattingUtils.formatPrice(contract.getPrice()));
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.tradeVolume"),
VolumeUtil.formatVolumeWithCode(contract.getTradeVolume()));
String tradeFee = Res.getWithColAndCap("shared.buyer") +
" " +
formatter.formatCoinWithCode(trade.getBuyer() == trade.getMaker() ? trade.getMakerFee() : trade.getTakerFee()) +
HavenoUtils.formatToXmrWithCode(trade.getBuyer() == trade.getMaker() ? trade.getMakerFee() : trade.getTakerFee()) +
" / " +
Res.getWithColAndCap("shared.seller") +
" " +
formatter.formatCoinWithCode(trade.getSeller() == trade.getMaker() ? trade.getMakerFee() : trade.getTakerFee());
HavenoUtils.formatToXmrWithCode(trade.getSeller() == trade.getMaker() ? trade.getMakerFee() : trade.getTakerFee());
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.tradeFee"), tradeFee);
String securityDeposit = Res.getWithColAndCap("shared.buyer") +
" " +
formatter.formatCoinWithCode(trade.getBuyerSecurityDeposit()) +
HavenoUtils.formatToXmrWithCode(trade.getBuyerSecurityDeposit()) +
" / " +
Res.getWithColAndCap("shared.seller") +
" " +
formatter.formatCoinWithCode(trade.getSellerSecurityDeposit());
HavenoUtils.formatToXmrWithCode(trade.getSellerSecurityDeposit());
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), securityDeposit);
}
@ -356,14 +356,14 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
}
private boolean isPayoutAmountValid() {
Coin buyerAmount = ParsingUtils.parseToCoin(buyerPayoutAmountInputTextField.getText(), formatter);
Coin sellerAmount = ParsingUtils.parseToCoin(sellerPayoutAmountInputTextField.getText(), formatter);
BigInteger buyerAmount = HavenoUtils.parseXmr(buyerPayoutAmountInputTextField.getText());
BigInteger sellerAmount = HavenoUtils.parseXmr(sellerPayoutAmountInputTextField.getText());
Contract contract = dispute.getContract();
Coin tradeAmount = contract.getTradeAmount();
Coin available = tradeAmount
BigInteger tradeAmount = contract.getTradeAmount();
BigInteger available = tradeAmount
.add(trade.getBuyerSecurityDeposit())
.add(trade.getSellerSecurityDeposit());
Coin totalAmount = buyerAmount.add(sellerAmount);
BigInteger totalAmount = buyerAmount.add(sellerAmount);
boolean isRefundAgent = getDisputeManager(dispute) instanceof RefundManager;
if (isRefundAgent) {
@ -371,7 +371,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
// be made
return totalAmount.compareTo(available) <= 0;
} else {
if (!totalAmount.isPositive()) {
if (totalAmount.compareTo(BigInteger.valueOf(0)) <= 0) {
return false;
}
return totalAmount.compareTo(available) == 0;
@ -386,26 +386,26 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
}
Contract contract = dispute.getContract();
Coin available = contract.getTradeAmount()
BigInteger available = contract.getTradeAmount()
.add(trade.getBuyerSecurityDeposit())
.add(trade.getSellerSecurityDeposit());
Coin enteredAmount = ParsingUtils.parseToCoin(inputTextField.getText(), formatter);
BigInteger enteredAmount = HavenoUtils.parseXmr(inputTextField.getText());
if (enteredAmount.compareTo(available) > 0) {
enteredAmount = available;
Coin finalEnteredAmount = enteredAmount;
inputTextField.setText(formatter.formatCoin(finalEnteredAmount));
BigInteger finalEnteredAmount = enteredAmount;
inputTextField.setText(HavenoUtils.formatToXmr(finalEnteredAmount));
}
Coin counterPartAsCoin = available.subtract(enteredAmount);
String formattedCounterPartAmount = formatter.formatCoin(counterPartAsCoin);
Coin buyerAmount;
Coin sellerAmount;
BigInteger counterPart = available.subtract(enteredAmount);
String formattedCounterPartAmount = HavenoUtils.formatToXmr(counterPart);
BigInteger buyerAmount;
BigInteger sellerAmount;
if (inputTextField == buyerPayoutAmountInputTextField) {
buyerAmount = enteredAmount;
sellerAmount = counterPartAsCoin;
sellerAmount = counterPart;
sellerPayoutAmountInputTextField.setText(formattedCounterPartAmount);
} else {
sellerAmount = enteredAmount;
buyerAmount = counterPartAsCoin;
buyerAmount = counterPart;
buyerPayoutAmountInputTextField.setText(formattedCounterPartAmount);
}
@ -619,28 +619,28 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
}
private void showPayoutTxConfirmation(Contract contract, DisputeResult disputeResult, MoneroTxWallet payoutTx, ResultHandler resultHandler) {
Coin buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
BigInteger buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
String buyerPayoutAddressString = contract.getBuyerPayoutAddressString();
Coin sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
BigInteger sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
String sellerPayoutAddressString = contract.getSellerPayoutAddressString();
Coin outputAmount = buyerPayoutAmount.add(sellerPayoutAmount);
BigInteger outputAmount = buyerPayoutAmount.add(sellerPayoutAmount);
String buyerDetails = "";
if (buyerPayoutAmount.isPositive()) {
if (buyerPayoutAmount.compareTo(BigInteger.valueOf(0)) > 0) {
buyerDetails = Res.get("disputeSummaryWindow.close.txDetails.buyer",
formatter.formatCoinWithCode(buyerPayoutAmount),
HavenoUtils.formatToXmrWithCode(buyerPayoutAmount),
buyerPayoutAddressString);
}
String sellerDetails = "";
if (sellerPayoutAmount.isPositive()) {
if (sellerPayoutAmount.compareTo(BigInteger.valueOf(0)) > 0) {
sellerDetails = Res.get("disputeSummaryWindow.close.txDetails.seller",
formatter.formatCoinWithCode(sellerPayoutAmount),
HavenoUtils.formatToXmrWithCode(sellerPayoutAmount),
sellerPayoutAddressString);
}
if (outputAmount.isPositive()) {
if (outputAmount.compareTo(BigInteger.valueOf(0)) > 0) {
new Popup().width(900)
.headLine(Res.get("disputeSummaryWindow.close.txDetails.headline"))
.confirmation(Res.get("disputeSummaryWindow.close.txDetails",
formatter.formatCoinWithCode(outputAmount),
HavenoUtils.formatToXmrWithCode(outputAmount),
buyerDetails,
sellerDetails,
formatter.formatCoinWithCode(HavenoUtils.atomicUnitsToCoin(payoutTx.getFee()))))
@ -716,21 +716,21 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
throw new IllegalStateException("Unknown radio button");
}
disputesService.applyPayoutAmountsToDisputeResult(payout, dispute, disputeResult, -1);
buyerPayoutAmountInputTextField.setText(formatter.formatCoin(disputeResult.getBuyerPayoutAmount()));
sellerPayoutAmountInputTextField.setText(formatter.formatCoin(disputeResult.getSellerPayoutAmount()));
buyerPayoutAmountInputTextField.setText(HavenoUtils.formatToXmr(disputeResult.getBuyerPayoutAmount()));
sellerPayoutAmountInputTextField.setText(HavenoUtils.formatToXmr(disputeResult.getSellerPayoutAmount()));
}
private void applyTradeAmountRadioButtonStates() {
Contract contract = dispute.getContract();
Coin buyerSecurityDeposit = trade.getBuyerSecurityDeposit();
Coin sellerSecurityDeposit = trade.getSellerSecurityDeposit();
Coin tradeAmount = contract.getTradeAmount();
BigInteger buyerSecurityDeposit = trade.getBuyerSecurityDeposit();
BigInteger sellerSecurityDeposit = trade.getSellerSecurityDeposit();
BigInteger tradeAmount = contract.getTradeAmount();
Coin buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
Coin sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
BigInteger buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
BigInteger sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
buyerPayoutAmountInputTextField.setText(formatter.formatCoin(buyerPayoutAmount));
sellerPayoutAmountInputTextField.setText(formatter.formatCoin(sellerPayoutAmount));
buyerPayoutAmountInputTextField.setText(HavenoUtils.formatToXmr(buyerPayoutAmount));
sellerPayoutAmountInputTextField.setText(HavenoUtils.formatToXmr(sellerPayoutAmount));
if (buyerPayoutAmount.equals(tradeAmount.add(buyerSecurityDeposit)) &&
sellerPayoutAmount.equals(sellerSecurityDeposit)) {

View File

@ -32,6 +32,7 @@ import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.trade.TradeManager;
import bisq.core.trade.Trade.State;
@ -44,7 +45,6 @@ import bisq.common.crypto.KeyRing;
import bisq.common.util.Tuple2;
import bisq.common.util.Tuple4;
import org.bitcoinj.core.Coin;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
@ -64,6 +64,7 @@ import javafx.scene.layout.HBox;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
@ -80,7 +81,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
private final KeyRing keyRing;
private final Navigation navigation;
private Offer offer;
private Coin tradeAmount;
private BigInteger tradeAmount;
private Price tradePrice;
private Optional<Runnable> placeOfferHandlerOptional = Optional.empty();
private Optional<Runnable> takeOfferHandlerOptional = Optional.empty();
@ -106,7 +107,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
type = Type.Confirmation;
}
public void show(Offer offer, Coin tradeAmount, Price tradePrice) {
public void show(Offer offer, BigInteger tradeAmount, Price tradePrice) {
this.offer = offer;
this.tradeAmount = tradeAmount;
this.tradePrice = tradePrice;
@ -208,14 +209,14 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
String btcAmount = Res.get("shared.btcAmount");
if (takeOfferHandlerOptional.isPresent()) {
addConfirmationLabelLabel(gridPane, ++rowIndex, btcAmount + btcDirectionInfo,
formatter.formatCoinWithCode(tradeAmount));
HavenoUtils.formatToXmrWithCode(tradeAmount));
addConfirmationLabelLabel(gridPane, ++rowIndex, VolumeUtil.formatVolumeLabel(currencyCode) + fiatDirectionInfo,
VolumeUtil.formatVolumeWithCode(offer.getVolumeByAmount(tradeAmount)));
} else {
addConfirmationLabelLabel(gridPane, ++rowIndex, btcAmount + btcDirectionInfo,
formatter.formatCoinWithCode(offer.getAmount()));
HavenoUtils.formatToXmrWithCode(offer.getAmount()));
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.minBtcAmount"),
formatter.formatCoinWithCode(offer.getMinAmount()));
HavenoUtils.formatToXmrWithCode(offer.getMinAmount()));
String volume = VolumeUtil.formatVolumeWithCode(offer.getVolume());
String minVolume = "";
if (offer.getVolume() != null && offer.getMinVolume() != null &&
@ -324,11 +325,11 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
DisplayUtils.formatDateTime(offer.getDate()));
String value = Res.getWithColAndCap("shared.buyer") +
" " +
formatter.formatCoinWithCode(offer.getBuyerSecurityDeposit()) +
HavenoUtils.formatToXmrWithCode(offer.getBuyerSecurityDeposit()) +
" / " +
Res.getWithColAndCap("shared.seller") +
" " +
formatter.formatCoinWithCode(offer.getSellerSecurityDeposit());
HavenoUtils.formatToXmrWithCode(offer.getSellerSecurityDeposit());
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), value);
if (countryCode != null && !isF2F)

View File

@ -31,6 +31,7 @@ import bisq.core.offer.Offer;
import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.support.dispute.arbitration.ArbitrationManager;
import bisq.core.trade.Contract;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.trade.TradeManager;
import bisq.core.trade.txproof.AssetTxProofResult;
@ -162,7 +163,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
}
addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.btcAmount") + btcDirectionInfo,
formatter.formatCoinWithCode(trade.getAmount()));
HavenoUtils.formatToXmrWithCode(trade.getAmount()));
addConfirmationLabelTextField(gridPane, ++rowIndex,
VolumeUtil.formatVolumeLabel(offer.getCurrencyCode()) + fiatDirectionInfo,
VolumeUtil.formatVolumeWithCode(trade.getVolume()));
@ -215,11 +216,11 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
DisplayUtils.formatDateTime(trade.getDate()));
String securityDeposit = Res.getWithColAndCap("shared.buyer") +
" " +
formatter.formatCoinWithCode(offer.getBuyerSecurityDeposit()) +
HavenoUtils.formatToXmrWithCode(offer.getBuyerSecurityDeposit()) +
" / " +
Res.getWithColAndCap("shared.seller") +
" " +
formatter.formatCoinWithCode(offer.getSellerSecurityDeposit());
HavenoUtils.formatToXmrWithCode(offer.getSellerSecurityDeposit());
addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), securityDeposit);
NodeAddress arbitratorNodeAddress = trade.getArbitratorNodeAddress();

View File

@ -29,10 +29,10 @@ import bisq.core.trade.ClosedTradableFormatter;
import bisq.core.trade.ClosedTradableManager;
import bisq.core.trade.Tradable;
import bisq.core.trade.Trade;
import org.bitcoinj.core.Coin;
import org.apache.commons.lang3.StringUtils;
import java.math.BigInteger;
import java.util.Date;
import lombok.Getter;
@ -57,7 +57,7 @@ public class ClosedTradesListItem implements FilterableListItem {
return tradable.getShortId();
}
public Coin getAmount() {
public BigInteger getAmount() {
return tradable.getOptionalAmount().orElse(null);
}

View File

@ -41,12 +41,11 @@ import bisq.core.util.coin.CoinUtil;
import bisq.network.p2p.P2PService;
import org.bitcoinj.core.Coin;
import com.google.inject.Inject;
import javax.inject.Named;
import java.math.BigInteger;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@ -100,7 +99,7 @@ class DuplicateOfferDataModel extends MutableOfferDataModel {
}
private double getBuyerSecurityAsPercent(Offer offer) {
Coin offerBuyerSecurityDeposit = getBoundedBuyerSecurityDepositAsCoin(offer.getBuyerSecurityDeposit());
BigInteger offerBuyerSecurityDeposit = getBoundedBuyerSecurityDeposit(offer.getBuyerSecurityDeposit());
double offerBuyerSecurityDepositAsPercent = CoinUtil.getAsPercentPerBtc(offerBuyerSecurityDeposit,
offer.getAmount());
return Math.min(offerBuyerSecurityDepositAsPercent,

View File

@ -23,7 +23,7 @@ import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferPayload;
import bisq.core.offer.OfferUtil;
import bisq.core.payment.validation.BtcValidator;
import bisq.core.payment.validation.XmrValidator;
import bisq.core.payment.validation.FiatVolumeValidator;
import bisq.core.payment.validation.SecurityDepositValidator;
import bisq.core.provider.price.PriceFeedService;
@ -46,7 +46,7 @@ class DuplicateOfferViewModel extends MutableOfferViewModel<DuplicateOfferDataMo
FiatVolumeValidator fiatVolumeValidator,
FiatPriceValidator fiatPriceValidator,
AltcoinValidator altcoinValidator,
BtcValidator btcValidator,
XmrValidator btcValidator,
SecurityDepositValidator securityDepositValidator,
PriceFeedService priceFeedService,
AccountAgeWitnessService accountAgeWitnessService,

Some files were not shown because too many files have changed in this diff Show More