remove inverted crypto prices; always use price per XMR (base/quote) (#1821)

This commit is contained in:
woodser 2025-07-21 09:56:57 -04:00 committed by GitHub
parent 51d40d73a7
commit a4d744aa53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
89 changed files with 588 additions and 728 deletions

View file

@ -654,7 +654,7 @@ public class AccountAgeWitnessService {
Date peersCurrentDate,
ErrorMessageHandler errorMessageHandler) {
checkNotNull(offer);
final String currencyCode = offer.getCurrencyCode();
final String currencyCode = offer.getCounterCurrencyCode();
final BigInteger defaultMaxTradeLimit = offer.getPaymentMethod().getMaxTradeLimit(currencyCode);
BigInteger peersCurrentTradeLimit = defaultMaxTradeLimit;
if (!hasTradeLimitException(peersWitness)) {
@ -673,7 +673,7 @@ public class AccountAgeWitnessService {
"\nPeers trade limit=" + peersCurrentTradeLimit +
"\nOffer ID=" + offer.getShortId() +
"\nPaymentMethod=" + offer.getPaymentMethod().getId() +
"\nCurrencyCode=" + offer.getCurrencyCode();
"\nCurrencyCode=" + offer.getCounterCurrencyCode();
log.warn(msg);
errorMessageHandler.handleErrorMessage(msg);
}

View file

@ -149,7 +149,7 @@ public class CoreOffersService {
List<OpenOffer> getMyOffers(String direction, String currencyCode) {
return getMyOffers().stream()
.filter(o -> offerMatchesDirectionAndCurrency(o.getOffer(), direction, currencyCode))
.sorted(openOfferPriceComparator(direction, CurrencyUtil.isTraditionalCurrency(currencyCode)))
.sorted(openOfferPriceComparator(direction))
.collect(Collectors.toList());
}
@ -336,7 +336,7 @@ public class CoreOffersService {
String sourceOfferId,
Consumer<Transaction> resultHandler,
ErrorMessageHandler errorMessageHandler) {
long triggerPriceAsLong = PriceUtil.getMarketPriceAsLong(triggerPriceAsString, offer.getCurrencyCode());
long triggerPriceAsLong = PriceUtil.getMarketPriceAsLong(triggerPriceAsString, offer.getCounterCurrencyCode());
openOfferManager.placeOffer(offer,
useSavingsWallet,
triggerPriceAsLong,
@ -353,8 +353,7 @@ public class CoreOffersService {
if ("".equals(direction)) direction = null;
if ("".equals(currencyCode)) currencyCode = null;
var offerOfWantedDirection = direction == null || offer.getDirection().name().equalsIgnoreCase(direction);
var counterAssetCode = CurrencyUtil.isCryptoCurrency(currencyCode) ? offer.getOfferPayload().getBaseCurrencyCode() : offer.getOfferPayload().getCounterCurrencyCode();
var offerInWantedCurrency = currencyCode == null || counterAssetCode.equalsIgnoreCase(currencyCode);
var offerInWantedCurrency = currencyCode == null || offer.getCounterCurrencyCode().equalsIgnoreCase(currencyCode);
return offerOfWantedDirection && offerInWantedCurrency;
}
@ -366,17 +365,12 @@ public class CoreOffersService {
: priceComparator.get();
}
private Comparator<OpenOffer> openOfferPriceComparator(String direction, boolean isTraditional) {
private Comparator<OpenOffer> openOfferPriceComparator(String direction) {
// A buyer probably wants to see sell orders in price ascending order.
// A seller probably wants to see buy orders in price descending order.
if (isTraditional)
return direction.equalsIgnoreCase(OfferDirection.BUY.name())
? openOfferPriceComparator.get().reversed()
: openOfferPriceComparator.get();
else
return direction.equalsIgnoreCase(OfferDirection.SELL.name())
? openOfferPriceComparator.get().reversed()
: openOfferPriceComparator.get();
return direction.equalsIgnoreCase(OfferDirection.BUY.name())
? openOfferPriceComparator.get().reversed()
: openOfferPriceComparator.get();
}
private long priceStringToLong(String priceAsString, String currencyCode) {

View file

@ -78,7 +78,7 @@ class CorePriceService {
} else if (!marketPrice.isExternallyProvidedPrice()) {
throw new IllegalArgumentException("Price is not available externally: " + currencyCode); // TODO: return more complex Price type including price double and isExternal boolean
}
return mapPriceFeedServicePrice(marketPrice.getPrice(), marketPrice.getCurrencyCode());
return marketPrice.getPrice();
}
/**
@ -87,8 +87,7 @@ class CorePriceService {
public List<MarketPriceInfo> getMarketPrices() throws ExecutionException, InterruptedException, TimeoutException {
return priceFeedService.requestAllPrices().values().stream()
.map(marketPrice -> {
double mappedPrice = mapPriceFeedServicePrice(marketPrice.getPrice(), marketPrice.getCurrencyCode());
return new MarketPriceInfo(marketPrice.getCurrencyCode(), mappedPrice);
return new MarketPriceInfo(marketPrice.getCurrencyCode(), marketPrice.getPrice());
})
.collect(Collectors.toList());
}
@ -102,12 +101,13 @@ class CorePriceService {
// Offer price can be null (if price feed unavailable), thus a null-tolerant comparator is used.
Comparator<Offer> offerPriceComparator = Comparator.comparing(Offer::getPrice, Comparator.nullsLast(Comparator.naturalOrder()));
// TODO: remove this!!!
// Trading xmr-traditional is considered as buying/selling XMR, but trading xmr-crypto is
// considered as buying/selling crypto. Because of this, when viewing a xmr-crypto pair,
// the buy column is actually the sell column and vice versa. To maintain the expected
// ordering, we have to reverse the price comparator.
boolean isCrypto = CurrencyUtil.isCryptoCurrency(currencyCode);
if (isCrypto) offerPriceComparator = offerPriceComparator.reversed();
//boolean isCrypto = CurrencyUtil.isCryptoCurrency(currencyCode);
//if (isCrypto) offerPriceComparator = offerPriceComparator.reversed();
// Offer amounts are used for the secondary sort. They are sorted from high to low.
Comparator<Offer> offerAmountComparator = Comparator.comparing(Offer::getAmount).reversed();
@ -130,11 +130,11 @@ class CorePriceService {
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);
buyTM.put(priceAsDouble, accumulatedAmount);
}
};
// Create buyer hashmap {key:price, value:count}, uses TreeMap to sort by key (asc)
// Create seller hashmap {key:price, value:count}, uses TreeMap to sort by key (asc)
accumulatedAmount = 0;
LinkedHashMap<Double,Double> sellTM = new LinkedHashMap<Double,Double>();
for(Offer offer: sellOffers){
@ -143,7 +143,7 @@ class CorePriceService {
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);
sellTM.put(priceAsDouble, accumulatedAmount);
}
};
@ -157,20 +157,5 @@ class CorePriceService {
return new MarketDepthInfo(currencyCode, buyPrices, buyDepth, sellPrices, sellDepth);
}
/**
* PriceProvider returns different values for crypto and traditional,
* e.g. 1 XMR = X USD
* but 1 DOGE = X XMR
* Here we convert all to:
* 1 XMR = X (FIAT or CRYPTO)
*/
private double mapPriceFeedServicePrice(double price, String currencyCode) {
if (CurrencyUtil.isTraditionalCurrency(currencyCode)) {
return price;
}
return price == 0 ? 0 : 1 / price;
// TODO PriceProvider.getAll() could provide these values directly when the original values are not needed for the 'desktop' UI anymore
}
}

View file

@ -123,7 +123,7 @@ class CoreTradesService {
BigInteger amount = amountAsLong == 0 ? offer.getAmount() : BigInteger.valueOf(amountAsLong);
// adjust amount for fixed-price offer (based on TakeOfferViewModel)
String currencyCode = offer.getCurrencyCode();
String currencyCode = offer.getCounterCurrencyCode();
OfferDirection direction = offer.getOfferPayload().getDirection();
BigInteger maxAmount = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction, offer.hasBuyerAsTakerWithoutDeposit());
if (offer.getPrice() != null) {

View file

@ -129,7 +129,7 @@ public class OfferInfo implements Payload {
public static OfferInfo toMyOfferInfo(OpenOffer openOffer) {
// An OpenOffer is always my offer.
var offer = openOffer.getOffer();
var currencyCode = offer.getCurrencyCode();
var currencyCode = offer.getCounterCurrencyCode();
var isActivated = !openOffer.isDeactivated();
Optional<Price> optionalTriggerPrice = openOffer.getTriggerPrice() > 0
? Optional.of(Price.valueOf(currencyCode, openOffer.getTriggerPrice()))
@ -150,7 +150,7 @@ public class OfferInfo implements Payload {
private static OfferInfoBuilder getBuilder(Offer offer) {
// OfferInfo protos are passed to API client, and some field
// values are converted to displayable, unambiguous form.
var currencyCode = offer.getCurrencyCode();
var currencyCode = offer.getCounterCurrencyCode();
var preciseOfferPrice = reformatMarketPrice(
requireNonNull(offer.getPrice()).toPlainString(),
currencyCode);

View file

@ -57,7 +57,7 @@ public class TradeInfo implements Payload {
private static final Function<Trade, String> toPreciseTradePrice = (trade) ->
reformatMarketPrice(requireNonNull(trade.getPrice()).toPlainString(),
trade.getOffer().getCurrencyCode());
trade.getOffer().getCounterCurrencyCode());
// Haveno v1 trade protocol fields (some are in common with the BSQ Swap protocol).
private final OfferInfo offer;

View file

@ -284,7 +284,7 @@ public class CurrencyUtil {
}
/**
* We return true if it is BTC or any of our currencies available in the assetRegistry.
* We return true if it is XMR or any of our currencies available in the assetRegistry.
* For removed assets it would fail as they are not found but we don't want to conclude that they are traditional then.
* As the caller might not deal with the case that a currency can be neither a cryptoCurrency nor Traditional if not found
* we return true as well in case we have no traditional currency for the code.
@ -514,17 +514,11 @@ public class CurrencyUtil {
}
public static String getCurrencyPair(String currencyCode) {
if (isTraditionalCurrency(currencyCode))
return Res.getBaseCurrencyCode() + "/" + currencyCode;
else
return currencyCode + "/" + Res.getBaseCurrencyCode();
return Res.getBaseCurrencyCode() + "/" + currencyCode;
}
public static String getCounterCurrency(String currencyCode) {
if (isTraditionalCurrency(currencyCode))
return currencyCode;
else
return Res.getBaseCurrencyCode();
return currencyCode;
}
public static String getPriceWithCurrencyCode(String currencyCode) {
@ -532,10 +526,7 @@ public class CurrencyUtil {
}
public static String getPriceWithCurrencyCode(String currencyCode, String translationKey) {
if (isCryptoCurrency(currencyCode))
return Res.get(translationKey, Res.getBaseCurrencyCode(), currencyCode);
else
return Res.get(translationKey, currencyCode, Res.getBaseCurrencyCode());
return Res.get(translationKey, currencyCode, Res.getBaseCurrencyCode());
}
public static String getOfferVolumeCode(String currencyCode) {

View file

@ -32,7 +32,7 @@ public class CryptoExchangeRate {
*/
public final Coin coin;
public final CryptoMoney crypto;
public final CryptoMoney cryptoMoney;
/**
* Construct exchange rate. This amount of coin is worth that amount of crypto.
@ -43,7 +43,7 @@ public class CryptoExchangeRate {
checkArgument(crypto.isPositive());
checkArgument(crypto.currencyCode != null, "currency code required");
this.coin = coin;
this.crypto = crypto;
this.cryptoMoney = crypto;
}
/**
@ -59,13 +59,13 @@ public class CryptoExchangeRate {
* @throws ArithmeticException if the converted crypto amount is too high or too low.
*/
public CryptoMoney coinToCrypto(Coin convertCoin) {
BigInteger converted = BigInteger.valueOf(coin.value)
.multiply(BigInteger.valueOf(convertCoin.value))
.divide(BigInteger.valueOf(crypto.value));
final BigInteger converted = BigInteger.valueOf(convertCoin.value)
.multiply(BigInteger.valueOf(cryptoMoney.value))
.divide(BigInteger.valueOf(coin.value));
if (converted.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0
|| converted.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0)
throw new ArithmeticException("Overflow");
return CryptoMoney.valueOf(crypto.currencyCode, converted.longValue());
return CryptoMoney.valueOf(cryptoMoney.currencyCode, converted.longValue());
}
/**
@ -74,12 +74,11 @@ public class CryptoExchangeRate {
* @throws ArithmeticException if the converted coin amount is too high or too low.
*/
public Coin cryptoToCoin(CryptoMoney convertCrypto) {
checkArgument(convertCrypto.currencyCode.equals(crypto.currencyCode), "Currency mismatch: %s vs %s",
convertCrypto.currencyCode, crypto.currencyCode);
checkArgument(convertCrypto.currencyCode.equals(cryptoMoney.currencyCode), "Currency mismatch: %s vs %s",
convertCrypto.currencyCode, cryptoMoney.currencyCode);
// Use BigInteger because it's much easier to maintain full precision without overflowing.
BigInteger converted = BigInteger.valueOf(crypto.value)
.multiply(BigInteger.valueOf(convertCrypto.value))
.divide(BigInteger.valueOf(coin.value));
final BigInteger converted = BigInteger.valueOf(convertCrypto.value).multiply(BigInteger.valueOf(coin.value))
.divide(BigInteger.valueOf(cryptoMoney.value));
if (converted.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0
|| converted.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0)
throw new ArithmeticException("Overflow");

View file

@ -136,7 +136,7 @@ public class Price extends MonetaryWrapper implements Comparable<Price> {
public String toFriendlyString() {
return monetary instanceof CryptoMoney ?
((CryptoMoney) monetary).toFriendlyString() + "/XMR" :
((CryptoMoney) monetary).toFriendlyString().replace(((CryptoMoney) monetary).currencyCode, "") + "XMR/" + ((CryptoMoney) monetary).currencyCode :
((TraditionalMoney) monetary).toFriendlyString().replace(((TraditionalMoney) monetary).currencyCode, "") + "XMR/" + ((TraditionalMoney) monetary).currencyCode;
}

View file

@ -15,84 +15,84 @@
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package haveno.core.monetary;
package haveno.core.monetary;
import static com.google.common.base.Preconditions.checkArgument;
import java.io.Serializable;
import java.math.BigInteger;
import org.bitcoinj.core.Coin;
import com.google.common.base.Objects;
/**
* An exchange rate is expressed as a ratio of a {@link Coin} and a traditional money amount.
*/
public class TraditionalExchangeRate implements Serializable {
public final Coin coin;
public final TraditionalMoney traditionalMoney;
/** Construct exchange rate. This amount of coin is worth that amount of money. */
public TraditionalExchangeRate(Coin coin, TraditionalMoney traditionalMoney) {
checkArgument(coin.isPositive());
checkArgument(traditionalMoney.isPositive());
checkArgument(traditionalMoney.currencyCode != null, "currency code required");
this.coin = coin;
this.traditionalMoney = traditionalMoney;
}
/** Construct exchange rate. One coin is worth this amount of traditional money. */
public TraditionalExchangeRate(TraditionalMoney traditionalMoney) {
this(Coin.COIN, traditionalMoney);
}
/**
* Convert a coin amount to a traditional money amount using this exchange rate.
* @throws ArithmeticException if the converted amount is too high or too low.
*/
public TraditionalMoney coinToTraditionalMoney(Coin convertCoin) {
// Use BigInteger because it's much easier to maintain full precision without overflowing.
final BigInteger converted = BigInteger.valueOf(convertCoin.value).multiply(BigInteger.valueOf(traditionalMoney.value))
.divide(BigInteger.valueOf(coin.value));
if (converted.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0
|| converted.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0)
throw new ArithmeticException("Overflow");
return TraditionalMoney.valueOf(traditionalMoney.currencyCode, converted.longValue());
}
/**
* Convert a traditional money amount to a coin amount using this exchange rate.
* @throws ArithmeticException if the converted coin amount is too high or too low.
*/
public Coin traditionalMoneyToCoin(TraditionalMoney convertTraditionalMoney) {
checkArgument(convertTraditionalMoney.currencyCode.equals(traditionalMoney.currencyCode), "Currency mismatch: %s vs %s",
convertTraditionalMoney.currencyCode, traditionalMoney.currencyCode);
// Use BigInteger because it's much easier to maintain full precision without overflowing.
final BigInteger converted = BigInteger.valueOf(convertTraditionalMoney.value).multiply(BigInteger.valueOf(coin.value))
.divide(BigInteger.valueOf(traditionalMoney.value));
if (converted.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0
|| converted.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0)
throw new ArithmeticException("Overflow");
try {
return Coin.valueOf(converted.longValue());
} catch (IllegalArgumentException x) {
throw new ArithmeticException("Overflow: " + x.getMessage());
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TraditionalExchangeRate other = (TraditionalExchangeRate) o;
return Objects.equal(this.coin, other.coin) && Objects.equal(this.traditionalMoney, other.traditionalMoney);
}
@Override
public int hashCode() {
return Objects.hashCode(coin, traditionalMoney);
}
}
import static com.google.common.base.Preconditions.checkArgument;
import java.io.Serializable;
import java.math.BigInteger;
import org.bitcoinj.core.Coin;
import com.google.common.base.Objects;
/**
* An exchange rate is expressed as a ratio of a {@link Coin} and a traditional money amount.
*/
public class TraditionalExchangeRate implements Serializable {
public final Coin coin;
public final TraditionalMoney traditionalMoney;
/** Construct exchange rate. This amount of coin is worth that amount of money. */
public TraditionalExchangeRate(Coin coin, TraditionalMoney traditionalMoney) {
checkArgument(coin.isPositive());
checkArgument(traditionalMoney.isPositive());
checkArgument(traditionalMoney.currencyCode != null, "currency code required");
this.coin = coin;
this.traditionalMoney = traditionalMoney;
}
/** Construct exchange rate. One coin is worth this amount of traditional money. */
public TraditionalExchangeRate(TraditionalMoney traditionalMoney) {
this(Coin.COIN, traditionalMoney);
}
/**
* Convert a coin amount to a traditional money amount using this exchange rate.
* @throws ArithmeticException if the converted amount is too high or too low.
*/
public TraditionalMoney coinToTraditionalMoney(Coin convertCoin) {
// Use BigInteger because it's much easier to maintain full precision without overflowing.
final BigInteger converted = BigInteger.valueOf(convertCoin.value)
.multiply(BigInteger.valueOf(traditionalMoney.value))
.divide(BigInteger.valueOf(coin.value));
if (converted.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0
|| converted.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0)
throw new ArithmeticException("Overflow");
return TraditionalMoney.valueOf(traditionalMoney.currencyCode, converted.longValue());
}
/**
* Convert a traditional money amount to a coin amount using this exchange rate.
* @throws ArithmeticException if the converted coin amount is too high or too low.
*/
public Coin traditionalMoneyToCoin(TraditionalMoney convertTraditionalMoney) {
checkArgument(convertTraditionalMoney.currencyCode.equals(traditionalMoney.currencyCode), "Currency mismatch: %s vs %s",
convertTraditionalMoney.currencyCode, traditionalMoney.currencyCode);
// Use BigInteger because it's much easier to maintain full precision without overflowing.
final BigInteger converted = BigInteger.valueOf(convertTraditionalMoney.value).multiply(BigInteger.valueOf(coin.value))
.divide(BigInteger.valueOf(traditionalMoney.value));
if (converted.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0
|| converted.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0)
throw new ArithmeticException("Overflow");
try {
return Coin.valueOf(converted.longValue());
} catch (IllegalArgumentException x) {
throw new ArithmeticException("Overflow: " + x.getMessage());
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TraditionalExchangeRate other = (TraditionalExchangeRate) o;
return Objects.equal(this.coin, other.coin) && Objects.equal(this.traditionalMoney, other.traditionalMoney);
}
@Override
public int hashCode() {
return Objects.hashCode(coin, traditionalMoney);
}
}

View file

@ -110,13 +110,12 @@ public class MarketAlerts {
}
private void onOfferAdded(Offer offer) {
String currencyCode = offer.getCurrencyCode();
String currencyCode = offer.getCounterCurrencyCode();
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
Price offerPrice = offer.getPrice();
if (marketPrice != null && offerPrice != null) {
boolean isSellOffer = offer.getDirection() == OfferDirection.SELL;
String shortOfferId = offer.getShortId();
boolean isTraditionalCurrency = CurrencyUtil.isTraditionalCurrency(currencyCode);
String alertId = getAlertId(offer);
user.getMarketAlertFilters().stream()
.filter(marketAlertFilter -> !offer.isMyOffer(keyRing))
@ -133,9 +132,7 @@ public class MarketAlerts {
double offerPriceValue = offerPrice.getValue();
double ratio = offerPriceValue / marketPriceAsDouble;
ratio = 1 - ratio;
if (isTraditionalCurrency && isSellOffer)
ratio *= -1;
else if (!isTraditionalCurrency && !isSellOffer)
if (isSellOffer)
ratio *= -1;
ratio = ratio * 10000;
@ -148,26 +145,14 @@ public class MarketAlerts {
if (isTriggerForBuyOfferAndTriggered || isTriggerForSellOfferAndTriggered) {
String direction = isSellOffer ? Res.get("shared.sell") : Res.get("shared.buy");
String marketDir;
if (isTraditionalCurrency) {
if (isSellOffer) {
marketDir = ratio > 0 ?
Res.get("account.notifications.marketAlert.message.msg.above") :
Res.get("account.notifications.marketAlert.message.msg.below");
} else {
marketDir = ratio < 0 ?
Res.get("account.notifications.marketAlert.message.msg.above") :
Res.get("account.notifications.marketAlert.message.msg.below");
}
if (isSellOffer) {
marketDir = ratio > 0 ?
Res.get("account.notifications.marketAlert.message.msg.above") :
Res.get("account.notifications.marketAlert.message.msg.below");
} else {
if (isSellOffer) {
marketDir = ratio < 0 ?
Res.get("account.notifications.marketAlert.message.msg.above") :
Res.get("account.notifications.marketAlert.message.msg.below");
} else {
marketDir = ratio > 0 ?
Res.get("account.notifications.marketAlert.message.msg.above") :
Res.get("account.notifications.marketAlert.message.msg.below");
}
marketDir = ratio < 0 ?
Res.get("account.notifications.marketAlert.message.msg.above") :
Res.get("account.notifications.marketAlert.message.msg.below");
}
ratio = Math.abs(ratio);

View file

@ -22,7 +22,6 @@ import com.google.inject.Singleton;
import haveno.common.app.Version;
import haveno.common.crypto.PubKeyRingProvider;
import haveno.common.util.Utilities;
import haveno.core.locale.CurrencyUtil;
import haveno.core.locale.Res;
import haveno.core.monetary.Price;
import haveno.core.payment.PaymentAccount;
@ -176,9 +175,8 @@ public class CreateOfferService {
double marketPriceMarginParam = useMarketBasedPriceValue ? marketPriceMargin : 0;
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;
String baseCurrencyCode = Res.getBaseCurrencyCode();
String counterCurrencyCode = currencyCode;
String countryCode = PaymentAccountUtil.getCountryCode(paymentAccount);
List<String> acceptedCountryCodes = PaymentAccountUtil.getAcceptedCountryCodes(paymentAccount);
String bankId = PaymentAccountUtil.getBankId(paymentAccount);

View file

@ -39,6 +39,7 @@ import haveno.core.payment.payload.PaymentMethod;
import haveno.core.provider.price.MarketPrice;
import haveno.core.provider.price.PriceFeedService;
import haveno.core.trade.HavenoUtils;
import haveno.core.util.PriceUtil;
import haveno.core.util.VolumeUtil;
import haveno.core.util.coin.CoinUtil;
import haveno.network.p2p.NodeAddress;
@ -174,32 +175,27 @@ public class Offer implements NetworkPayload, PersistablePayload {
@Nullable
public Price getPrice() {
String currencyCode = getCurrencyCode();
String counterCurrencyCode = getCounterCurrencyCode();
if (!offerPayload.isUseMarketBasedPrice()) {
return Price.valueOf(currencyCode, offerPayload.getPrice());
return Price.valueOf(counterCurrencyCode, isInverted() ? PriceUtil.invertLongPrice(offerPayload.getPrice(), counterCurrencyCode) : offerPayload.getPrice());
}
checkNotNull(priceFeedService, "priceFeed must not be null");
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
MarketPrice marketPrice = priceFeedService.getMarketPrice(counterCurrencyCode);
if (marketPrice != null && marketPrice.isRecentExternalPriceAvailable()) {
double factor;
double marketPriceMargin = offerPayload.getMarketPriceMarginPct();
if (CurrencyUtil.isCryptoCurrency(currencyCode)) {
factor = getDirection() == OfferDirection.SELL ?
1 - marketPriceMargin : 1 + marketPriceMargin;
} else {
factor = getDirection() == OfferDirection.BUY ?
1 - marketPriceMargin : 1 + marketPriceMargin;
}
factor = getDirection() == OfferDirection.BUY ?
1 - marketPriceMargin : 1 + marketPriceMargin;
double marketPriceAsDouble = marketPrice.getPrice();
double targetPriceAsDouble = marketPriceAsDouble * factor;
try {
int precision = CurrencyUtil.isTraditionalCurrency(currencyCode) ?
int precision = CurrencyUtil.isTraditionalCurrency(counterCurrencyCode) ?
TraditionalMoney.SMALLEST_UNIT_EXPONENT :
CryptoMoney.SMALLEST_UNIT_EXPONENT;
double scaled = MathUtils.scaleUpByPowerOf10(targetPriceAsDouble, precision);
final long roundedToLong = MathUtils.roundDoubleToLong(scaled);
return Price.valueOf(currencyCode, roundedToLong);
return Price.valueOf(counterCurrencyCode, roundedToLong);
} catch (Exception e) {
log.error("Exception at getPrice / parseToFiat: " + e + "\n" +
"That case should never happen.");
@ -225,7 +221,7 @@ public class Offer implements NetworkPayload, PersistablePayload {
return;
}
Price tradePrice = Price.valueOf(getCurrencyCode(), price);
Price tradePrice = Price.valueOf(getCounterCurrencyCode(), price);
Price offerPrice = getPrice();
if (offerPrice == null)
throw new MarketPriceNotAvailableException("Market price required for calculating trade price is not available.");
@ -240,7 +236,7 @@ public class Offer implements NetworkPayload, PersistablePayload {
double deviation = Math.abs(1 - relation);
log.info("Price at take-offer time: id={}, currency={}, takersPrice={}, makersPrice={}, deviation={}",
getShortId(), getCurrencyCode(), price, offerPrice.getValue(),
getShortId(), getCounterCurrencyCode(), price, offerPrice.getValue(),
deviation * 100 + "%");
if (deviation > PRICE_TOLERANCE) {
String msg = "Taker's trade price is too far away from our calculated price based on the market price.\n" +
@ -509,23 +505,18 @@ public class Offer implements NetworkPayload, PersistablePayload {
return offerPayload.getCountryCode();
}
public String getCurrencyCode() {
if (currencyCode != null) {
return currencyCode;
}
currencyCode = offerPayload.getBaseCurrencyCode().equals("XMR") ?
offerPayload.getCounterCurrencyCode() :
offerPayload.getBaseCurrencyCode();
return currencyCode;
public String getBaseCurrencyCode() {
return isInverted() ? offerPayload.getCounterCurrencyCode() : offerPayload.getBaseCurrencyCode(); // legacy offers inverted crypto
}
public String getCounterCurrencyCode() {
return offerPayload.getCounterCurrencyCode();
if (currencyCode != null) return currencyCode;
currencyCode = isInverted() ? offerPayload.getBaseCurrencyCode() : offerPayload.getCounterCurrencyCode(); // legacy offers inverted crypto
return currencyCode;
}
public String getBaseCurrencyCode() {
return offerPayload.getBaseCurrencyCode();
public boolean isInverted() {
return !offerPayload.getBaseCurrencyCode().equals("XMR");
}
public String getPaymentMethodId() {
@ -586,21 +577,6 @@ public class Offer implements NetworkPayload, PersistablePayload {
return offerPayload.isUseReOpenAfterAutoClose();
}
public boolean isXmrAutoConf() {
if (!isXmr()) {
return false;
}
if (getExtraDataMap() == null || !getExtraDataMap().containsKey(OfferPayload.XMR_AUTO_CONF)) {
return false;
}
return getExtraDataMap().get(OfferPayload.XMR_AUTO_CONF).equals(OfferPayload.XMR_AUTO_CONF_ENABLED_VALUE);
}
public boolean isXmr() {
return getCurrencyCode().equals("XMR");
}
public boolean isTraditionalOffer() {
return CurrencyUtil.isTraditionalCurrency(currencyCode);
}

View file

@ -271,7 +271,7 @@ public class OfferBookService {
public List<Offer> getOffersByCurrency(String direction, String currencyCode) {
return getOffers().stream()
.filter(o -> o.getOfferPayload().getBaseCurrencyCode().equalsIgnoreCase(currencyCode) && o.getDirection().name() == direction)
.filter(o -> o.getOfferPayload().getCounterCurrencyCode().equalsIgnoreCase(currencyCode) && o.getDirection().name() == direction)
.collect(Collectors.toList());
}
@ -445,11 +445,11 @@ public class OfferBookService {
// We filter the case that it is a MarketBasedPrice but the price is not available
// That should only be possible if the price feed provider is not available
final List<OfferForJson> offerForJsonList = getOffers().stream()
.filter(offer -> !offer.isUseMarketBasedPrice() || priceFeedService.getMarketPrice(offer.getCurrencyCode()) != null)
.filter(offer -> !offer.isUseMarketBasedPrice() || priceFeedService.getMarketPrice(offer.getCounterCurrencyCode()) != null)
.map(offer -> {
try {
return new OfferForJson(offer.getDirection(),
offer.getCurrencyCode(),
offer.getCounterCurrencyCode(),
offer.getMinAmount(),
offer.getAmount(),
offer.getPrice(),

View file

@ -162,7 +162,7 @@ public class OfferFilterService {
}
public boolean isCurrencyBanned(Offer offer) {
return filterManager.isCurrencyBanned(offer.getCurrencyCode());
return filterManager.isCurrencyBanned(offer.getCounterCurrencyCode());
}
public boolean isPaymentMethodBanned(Offer offer) {
@ -204,7 +204,7 @@ public class OfferFilterService {
accountAgeWitnessService);
long myTradeLimit = accountOptional
.map(paymentAccount -> accountAgeWitnessService.getMyTradeLimit(paymentAccount,
offer.getCurrencyCode(), offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit()))
offer.getCounterCurrencyCode(), offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit()))
.orElse(0L);
long offerMinAmount = offer.getMinAmount().longValueExact();
log.debug("isInsufficientTradeLimit accountOptional={}, myTradeLimit={}, offerMinAmount={}, ",

View file

@ -100,14 +100,8 @@ public class OfferForJson {
private void setDisplayStrings() {
try {
final Price price = getPrice();
if (CurrencyUtil.isCryptoCurrency(currencyCode)) {
primaryMarketDirection = direction == OfferDirection.BUY ? OfferDirection.SELL : OfferDirection.BUY;
currencyPair = currencyCode + "/" + Res.getBaseCurrencyCode();
} else {
primaryMarketDirection = direction;
currencyPair = Res.getBaseCurrencyCode() + "/" + currencyCode;
}
primaryMarketDirection = direction;
currencyPair = Res.getBaseCurrencyCode() + "/" + currencyCode;
if (CurrencyUtil.isTraditionalCurrency(currencyCode)) {
priceDisplayString = traditionalFormat.noCode().format(price.getMonetary()).toString();
@ -116,7 +110,6 @@ public class OfferForJson {
primaryMarketMinVolumeDisplayString = traditionalFormat.noCode().format(getMinVolume().getMonetary()).toString();
primaryMarketVolumeDisplayString = traditionalFormat.noCode().format(getVolume().getMonetary()).toString();
} else {
// amount and volume is inverted for json
priceDisplayString = cryptoFormat.noCode().format(price.getMonetary()).toString();
primaryMarketMinAmountDisplayString = cryptoFormat.noCode().format(getMinVolume().getMonetary()).toString();
primaryMarketAmountDisplayString = cryptoFormat.noCode().format(getVolume().getMonetary()).toString();

View file

@ -264,11 +264,11 @@ public class OfferUtil {
}
public static boolean isTraditionalOffer(Offer offer) {
return offer.getBaseCurrencyCode().equals("XMR");
return CurrencyUtil.isTraditionalCurrency(offer.getCounterCurrencyCode());
}
public static boolean isCryptoOffer(Offer offer) {
return offer.getCounterCurrencyCode().equals("XMR");
return CurrencyUtil.isCryptoCurrency(offer.getCounterCurrencyCode());
}
public BigInteger getMaxTradeLimitForRelease(PaymentAccount paymentAccount,

View file

@ -78,6 +78,7 @@ import haveno.core.trade.statistics.TradeStatisticsManager;
import haveno.core.user.Preferences;
import haveno.core.user.User;
import haveno.core.util.JsonUtil;
import haveno.core.util.PriceUtil;
import haveno.core.util.Validator;
import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.wallet.BtcWalletService;
@ -638,7 +639,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
private void applyTriggerState(OpenOffer openOffer) {
if (openOffer.getState() != OpenOffer.State.AVAILABLE) return;
if (TriggerPriceService.isTriggered(priceFeedService.getMarketPrice(openOffer.getOffer().getCurrencyCode()), openOffer)) {
if (TriggerPriceService.isTriggered(priceFeedService.getMarketPrice(openOffer.getOffer().getCounterCurrencyCode()), openOffer)) {
openOffer.deactivate(true);
}
}
@ -2015,12 +2016,13 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
log.info("Updated the owner nodeaddress of offer id={}", originalOffer.getId());
}
long normalizedPrice = originalOffer.isInverted() ? PriceUtil.invertLongPrice(originalOfferPayload.getPrice(), originalOffer.getCounterCurrencyCode()) : originalOfferPayload.getPrice();
OfferPayload updatedPayload = new OfferPayload(originalOfferPayload.getId(),
originalOfferPayload.getDate(),
ownerNodeAddress,
originalOfferPayload.getPubKeyRing(),
originalOfferPayload.getDirection(),
originalOfferPayload.getPrice(),
normalizedPrice,
originalOfferPayload.getMarketPriceMarginPct(),
originalOfferPayload.isUseMarketBasedPrice(),
originalOfferPayload.getAmount(),
@ -2030,8 +2032,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
originalOfferPayload.getPenaltyFeePct(),
originalOfferPayload.getBuyerSecurityDepositPct(),
originalOfferPayload.getSellerSecurityDepositPct(),
originalOfferPayload.getBaseCurrencyCode(),
originalOfferPayload.getCounterCurrencyCode(),
originalOffer.getBaseCurrencyCode(),
originalOffer.getCounterCurrencyCode(),
originalOfferPayload.getPaymentMethodId(),
originalOfferPayload.getMakerPaymentAccountId(),
originalOfferPayload.getCountryCode(),
@ -2063,7 +2065,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
Offer updatedOffer = new Offer(updatedPayload);
updatedOffer.setPriceFeedService(priceFeedService);
OpenOffer updatedOpenOffer = new OpenOffer(updatedOffer, originalOpenOffer.getTriggerPrice());
long normalizedTriggerPrice = originalOffer.isInverted() ? PriceUtil.invertLongPrice(originalOpenOffer.getTriggerPrice(), originalOffer.getCounterCurrencyCode()) : originalOpenOffer.getTriggerPrice();
OpenOffer updatedOpenOffer = new OpenOffer(updatedOffer, normalizedTriggerPrice);
updatedOpenOffer.setChallenge(originalOpenOffer.getChallenge());
addOpenOffer(updatedOpenOffer);
requestPersistence();

View file

@ -102,7 +102,7 @@ public class TriggerPriceService {
return false;
}
String currencyCode = openOffer.getOffer().getCurrencyCode();
String currencyCode = openOffer.getOffer().getCounterCurrencyCode();
boolean traditionalCurrency = CurrencyUtil.isTraditionalCurrency(currencyCode);
int smallestUnitExponent = traditionalCurrency ?
TraditionalMoney.SMALLEST_UNIT_EXPONENT :
@ -116,15 +116,13 @@ public class TriggerPriceService {
OfferDirection direction = openOffer.getOffer().getDirection();
boolean isSellOffer = direction == OfferDirection.SELL;
boolean cryptoCurrency = CurrencyUtil.isCryptoCurrency(currencyCode);
boolean condition = isSellOffer && !cryptoCurrency || !isSellOffer && cryptoCurrency;
return condition ?
return isSellOffer ?
marketPriceAsLong < triggerPrice :
marketPriceAsLong > triggerPrice;
}
private void checkPriceThreshold(MarketPrice marketPrice, OpenOffer openOffer) {
String currencyCode = openOffer.getOffer().getCurrencyCode();
String currencyCode = openOffer.getOffer().getCounterCurrencyCode();
int smallestUnitExponent = CurrencyUtil.isTraditionalCurrency(currencyCode) ?
TraditionalMoney.SMALLEST_UNIT_EXPONENT :
CryptoMoney.SMALLEST_UNIT_EXPONENT;
@ -162,11 +160,11 @@ public class TriggerPriceService {
private void onAddedOpenOffers(List<? extends OpenOffer> openOffers) {
openOffers.forEach(openOffer -> {
String currencyCode = openOffer.getOffer().getCurrencyCode();
String currencyCode = openOffer.getOffer().getCounterCurrencyCode();
openOffersByCurrency.putIfAbsent(currencyCode, new HashSet<>());
openOffersByCurrency.get(currencyCode).add(openOffer);
MarketPrice marketPrice = priceFeedService.getMarketPrice(openOffer.getOffer().getCurrencyCode());
MarketPrice marketPrice = priceFeedService.getMarketPrice(openOffer.getOffer().getCounterCurrencyCode());
if (marketPrice != null) {
checkPriceThreshold(marketPrice, openOffer);
}
@ -175,7 +173,7 @@ public class TriggerPriceService {
private void onRemovedOpenOffers(List<? extends OpenOffer> openOffers) {
openOffers.forEach(openOffer -> {
String currencyCode = openOffer.getOffer().getCurrencyCode();
String currencyCode = openOffer.getOffer().getCounterCurrencyCode();
if (openOffersByCurrency.containsKey(currencyCode)) {
Set<OpenOffer> set = openOffersByCurrency.get(currencyCode);
set.remove(openOffer);

View file

@ -100,7 +100,7 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
PaymentAccount paymentAccount = user.getPaymentAccount(offer.getMakerPaymentAccountId());
checkArgument(paymentAccount != null, "Payment account is null. makerPaymentAccountId=" + offer.getMakerPaymentAccountId());
long maxAmount = accountAgeWitnessService.getMyTradeLimit(user.getPaymentAccount(offer.getMakerPaymentAccountId()), offer.getCurrencyCode(), offer.getDirection(), offer.hasBuyerAsTakerWithoutDeposit());
long maxAmount = accountAgeWitnessService.getMyTradeLimit(user.getPaymentAccount(offer.getMakerPaymentAccountId()), offer.getCounterCurrencyCode(), offer.getDirection(), offer.hasBuyerAsTakerWithoutDeposit());
checkArgument(offer.getAmount().longValueExact() <= maxAmount,
"Amount is larger than " + HavenoUtils.atomicUnitsToXmr(maxAmount) + " XMR");
checkArgument(offer.getAmount().compareTo(offer.getMinAmount()) >= 0, "MinAmount is larger than Amount");
@ -112,7 +112,7 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
checkArgument(offer.getDate().getTime() > 0,
"Date must not be 0. date=" + offer.getDate().toString());
checkNotNull(offer.getCurrencyCode(), "Currency is null");
checkNotNull(offer.getCounterCurrencyCode(), "Currency is null");
checkNotNull(offer.getDirection(), "Direction is null");
checkNotNull(offer.getId(), "Id is null");
checkNotNull(offer.getPubKeyRing(), "pubKeyRing is null");

View file

@ -106,7 +106,7 @@ public class TakeOfferModel implements Model {
calculateTotalToPay();
offer.resetState();
priceFeedService.setCurrencyCode(offer.getCurrencyCode());
priceFeedService.setCurrencyCode(offer.getCounterCurrencyCode());
}
@Override
@ -147,7 +147,7 @@ public class TakeOfferModel implements Model {
private long getMaxTradeLimit() {
return accountAgeWitnessService.getMyTradeLimit(paymentAccount,
offer.getCurrencyCode(),
offer.getCounterCurrencyCode(),
offer.getMirroredDirection(),
offer.hasBuyerAsTakerWithoutDeposit());
}

View file

@ -122,9 +122,9 @@ public class PaymentAccountUtil {
public static boolean isAmountValidForOffer(Offer offer,
PaymentAccount paymentAccount,
AccountAgeWitnessService accountAgeWitnessService) {
boolean hasChargebackRisk = hasChargebackRisk(offer.getPaymentMethod(), offer.getCurrencyCode());
boolean hasChargebackRisk = hasChargebackRisk(offer.getPaymentMethod(), offer.getCounterCurrencyCode());
boolean hasValidAccountAgeWitness = accountAgeWitnessService.getMyTradeLimit(paymentAccount,
offer.getCurrencyCode(), offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit()) >= offer.getMinAmount().longValueExact();
offer.getCounterCurrencyCode(), offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit()) >= offer.getMinAmount().longValueExact();
return !hasChargebackRisk || hasValidAccountAgeWitness;
}

View file

@ -95,7 +95,7 @@ class ReceiptPredicates {
.map(TradeCurrency::getCode)
.collect(Collectors.toSet());
return codes.contains(offer.getCurrencyCode());
return codes.contains(offer.getCounterCurrencyCode());
}
boolean isMatchingSepaOffer(Offer offer, PaymentAccount account) {

View file

@ -296,13 +296,13 @@ public class PriceFeedService {
}
}
private void setHavenoMarketPrice(String currencyCode, Price price) {
private void setHavenoMarketPrice(String counterCurrencyCode, Price price) {
UserThread.execute(() -> {
String currencyCodeBase = CurrencyUtil.getCurrencyCodeBase(currencyCode);
String counterCurrencyCodeBase = CurrencyUtil.getCurrencyCodeBase(counterCurrencyCode);
synchronized (cache) {
if (!cache.containsKey(currencyCodeBase) || !cache.get(currencyCodeBase).isExternallyProvidedPrice()) {
cache.put(currencyCodeBase, new MarketPrice(currencyCodeBase,
MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(currencyCode) ? CryptoMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT),
if (!cache.containsKey(counterCurrencyCodeBase) || !cache.get(counterCurrencyCodeBase).isExternallyProvidedPrice()) {
cache.put(counterCurrencyCodeBase, new MarketPrice(counterCurrencyCodeBase,
MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(counterCurrencyCode) ? CryptoMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT),
0,
false));
}
@ -371,9 +371,7 @@ public class PriceFeedService {
}
/**
* Returns prices for all available currencies.
* For crypto currencies the value is XMR price for 1 unit of given crypto currency (e.g. 1 DOGE = X XMR).
* For traditional currencies the value is price in the given traditional currency per 1 XMR (e.g. 1 XMR = X USD).
* Returns prices for all available currencies. The base currency is always XMR.
*
* TODO: instrument requestPrices() result and fault handlers instead of using CountDownLatch and timeout
*/

View file

@ -28,6 +28,8 @@ import haveno.network.p2p.P2PService;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -63,16 +65,20 @@ public class PriceProvider extends HttpClientProvider {
LinkedTreeMap<?, ?> treeMap = (LinkedTreeMap<?, ?>) obj;
String baseCurrencyCode = (String) treeMap.get("baseCurrencyCode");
String counterCurrencyCode = (String) treeMap.get("counterCurrencyCode");
String currencyCode = baseCurrencyCode.equals("XMR") ? counterCurrencyCode : baseCurrencyCode;
currencyCode = CurrencyUtil.getCurrencyCodeBase(currencyCode);
boolean isInverted = !"XMR".equalsIgnoreCase(baseCurrencyCode);
if (isInverted) {
String temp = baseCurrencyCode;
baseCurrencyCode = counterCurrencyCode;
counterCurrencyCode = temp;
}
counterCurrencyCode = CurrencyUtil.getCurrencyCodeBase(counterCurrencyCode);
double price = (Double) treeMap.get("price");
// json uses double for our timestampSec long value...
if (isInverted) price = BigDecimal.ONE.divide(BigDecimal.valueOf(price), 10, RoundingMode.HALF_UP).doubleValue(); // XMR is always base currency, so invert price if applicable
long timestampSec = MathUtils.doubleToLong((Double) treeMap.get("timestampSec"));
marketPriceMap.put(currencyCode, new MarketPrice(currencyCode, price, timestampSec, true));
marketPriceMap.put(counterCurrencyCode, new MarketPrice(counterCurrencyCode, price, timestampSec, true));
} catch (Throwable t) {
log.error("Error getting all prices: {}\n", t.getMessage(), t);
}
});
return marketPriceMap;
}

View file

@ -65,6 +65,7 @@ import haveno.core.trade.protocol.ProcessModelServiceProvider;
import haveno.core.trade.protocol.TradeListener;
import haveno.core.trade.protocol.TradePeer;
import haveno.core.trade.protocol.TradeProtocol;
import haveno.core.util.PriceUtil;
import haveno.core.util.VolumeUtil;
import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.wallet.XmrWalletBase;
@ -151,6 +152,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
private boolean restartInProgress;
private Subscription protocolErrorStateSubscription;
private Subscription protocolErrorHeightSubscription;
public static final String PROTOCOL_VERSION = "protocolVersion"; // key for extraDataMap in trade statistics
///////////////////////////////////////////////////////////////////////////////////////////
// Enums
@ -2377,8 +2379,16 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
return isBuyer() ? getBuyer().getSecurityDeposit() : getAmount().add(getSeller().getSecurityDeposit());
}
/**
* Returns the price as XMR/QUOTE.
*/
public Price getPrice() {
return Price.valueOf(offer.getCurrencyCode(), price);
boolean isInverted = getOffer().isInverted(); // return uninverted price
return Price.valueOf(offer.getCounterCurrencyCode(), isInverted ? PriceUtil.invertLongPrice(price, offer.getCounterCurrencyCode()) : price);
}
public Price getRawPrice() {
return Price.valueOf(offer.getCounterCurrencyCode(), price);
}
@Nullable

View file

@ -23,7 +23,6 @@ import com.google.inject.Singleton;
import haveno.common.crypto.KeyRing;
import haveno.common.util.Tuple2;
import static haveno.core.locale.CurrencyUtil.getCurrencyPair;
import static haveno.core.locale.CurrencyUtil.isTraditionalCurrency;
import haveno.core.locale.Res;
import haveno.core.offer.Offer;
import static haveno.core.util.FormattingUtils.formatDurationAsWords;
@ -153,8 +152,8 @@ public class TradeUtil {
return "";
checkNotNull(trade.getOffer());
checkNotNull(trade.getOffer().getCurrencyCode());
return getCurrencyPair(trade.getOffer().getCurrencyCode());
checkNotNull(trade.getOffer().getCounterCurrencyCode());
return getCurrencyPair(trade.getOffer().getCounterCurrencyCode());
}
public String getPaymentMethodNameWithCountryCode(Trade trade) {
@ -180,7 +179,7 @@ public class TradeUtil {
return (trade.isArbitrator() ? "Arbitrator for " : "") + // TODO: use Res.get()
getRole(trade.getBuyer() == trade.getMaker(),
trade.isArbitrator() ? true : trade.isMaker(), // arbitrator role in context of maker
offer.getCurrencyCode());
offer.getCounterCurrencyCode());
}
/**
@ -192,25 +191,14 @@ public class TradeUtil {
* @return String describing a trader's role
*/
private static String getRole(boolean isBuyerMakerAndSellerTaker, boolean isMaker, String currencyCode) {
if (isTraditionalCurrency(currencyCode)) {
String baseCurrencyCode = Res.getBaseCurrencyCode();
if (isBuyerMakerAndSellerTaker)
return isMaker
? Res.get("formatter.asMaker", baseCurrencyCode, Res.get("shared.buyer"))
: Res.get("formatter.asTaker", baseCurrencyCode, Res.get("shared.seller"));
else
return isMaker
? Res.get("formatter.asMaker", baseCurrencyCode, Res.get("shared.seller"))
: Res.get("formatter.asTaker", baseCurrencyCode, Res.get("shared.buyer"));
} else {
if (isBuyerMakerAndSellerTaker)
return isMaker
? Res.get("formatter.asMaker", currencyCode, Res.get("shared.seller"))
: Res.get("formatter.asTaker", currencyCode, Res.get("shared.buyer"));
else
return isMaker
? Res.get("formatter.asMaker", currencyCode, Res.get("shared.buyer"))
: Res.get("formatter.asTaker", currencyCode, Res.get("shared.seller"));
}
String baseCurrencyCode = Res.getBaseCurrencyCode();
if (isBuyerMakerAndSellerTaker)
return isMaker
? Res.get("formatter.asMaker", baseCurrencyCode, Res.get("shared.buyer"))
: Res.get("formatter.asTaker", baseCurrencyCode, Res.get("shared.seller"));
else
return isMaker
? Res.get("formatter.asMaker", baseCurrencyCode, Res.get("shared.seller"))
: Res.get("formatter.asTaker", baseCurrencyCode, Res.get("shared.buyer"));
}
}

View file

@ -45,9 +45,9 @@ public class ApplyFilter extends TradeTask {
} else if (filterManager.isOfferIdBanned(trade.getId())) {
failed("Offer ID is banned.\n" +
"Offer ID=" + trade.getId());
} else if (trade.getOffer() != null && filterManager.isCurrencyBanned(trade.getOffer().getCurrencyCode())) {
} else if (trade.getOffer() != null && filterManager.isCurrencyBanned(trade.getOffer().getCounterCurrencyCode())) {
failed("Currency is banned.\n" +
"Currency code=" + trade.getOffer().getCurrencyCode());
"Currency code=" + trade.getOffer().getCounterCurrencyCode());
} else if (filterManager.isPaymentMethodBanned(checkNotNull(trade.getOffer()).getPaymentMethod())) {
failed("Payment method is banned.\n" +
"Payment method=" + trade.getOffer().getPaymentMethod().getId());

View file

@ -63,7 +63,7 @@ public class VerifyPeersAccountAgeWitness extends TradeTask {
// only verify traditional offer
Offer offer = checkNotNull(trade.getOffer());
if (CurrencyUtil.isCryptoCurrency(offer.getCurrencyCode())) {
if (CurrencyUtil.isCryptoCurrency(offer.getCounterCurrencyCode())) {
complete();
return;
}

View file

@ -28,6 +28,7 @@ import haveno.common.util.CollectionUtils;
import haveno.common.util.ExtraDataMapValidator;
import haveno.common.util.JsonExclude;
import haveno.common.util.Utilities;
import haveno.core.locale.CurrencyUtil;
import haveno.core.monetary.CryptoMoney;
import haveno.core.monetary.Price;
import haveno.core.monetary.Volume;
@ -35,6 +36,7 @@ import haveno.core.offer.Offer;
import haveno.core.offer.OfferPayload;
import haveno.core.trade.Trade;
import haveno.core.util.JsonUtil;
import haveno.core.util.PriceUtil;
import haveno.core.util.VolumeUtil;
import haveno.network.p2p.NodeAddress;
import haveno.network.p2p.storage.payload.CapabilityRequiringPayload;
@ -67,6 +69,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload,
CapabilityRequiringPayload, DateSortedTruncatablePayload {
private static final String VERSION_KEY = "v"; // single character key for versioning
@JsonExclude
private transient static final ZoneId ZONE_ID = ZoneId.systemDefault();
@ -94,6 +98,13 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
extraDataMap.put(OfferPayload.REFERRAL_ID, referralId);
}
// Store the trade protocol version to denote that the crypto price is not inverted starting with v3.
// This can be removed in the future after all stats are expected to not be inverted,
// then only stats which are missing this field prior to then need to be uninverted.
if (!trade.getOffer().isInverted() && CurrencyUtil.isCryptoCurrency(trade.getOffer().getCounterCurrencyCode())) {
extraDataMap.put(VERSION_KEY, trade.getOffer().getOfferPayload().getProtocolVersion() + "");
}
NodeAddress arbitratorNodeAddress = checkNotNull(trade.getArbitrator().getNodeAddress(), "Arbitrator address is null", trade.getClass().getSimpleName(), trade.getId());
// The first 4 chars are sufficient to identify an arbitrator.
@ -104,7 +115,7 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
arbitratorNodeAddress.getFullAddress();
Offer offer = checkNotNull(trade.getOffer());
return new TradeStatistics3(offer.getCurrencyCode(),
return new TradeStatistics3(offer.getCounterCurrencyCode(),
fuzzTradePriceReproducibly(trade, fuzzPricePct),
fuzzTradeAmountReproducibly(trade, fuzzAmountPct),
offer.getPaymentMethod().getId(),
@ -114,10 +125,10 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
}
private static long fuzzTradePriceReproducibly(Trade trade, double fuzzPricePct) {
if (fuzzPricePct == 0.0) return trade.getPrice().getValue();
if (fuzzPricePct == 0.0) return trade.getRawPrice().getValue();
long originalTimestamp = trade.getTakeOfferDate().getTime();
Random random = new Random(originalTimestamp); // pseudo random generator seeded from take offer datestamp
long exactPrice = trade.getPrice().getValue();
long exactPrice = trade.getRawPrice().getValue();
long adjustedPrice = (long) random.nextDouble(exactPrice * (1.0 - fuzzPricePct), exactPrice * (1.0 + fuzzPricePct));
log.debug("trade {} fuzzed trade price for tradeStatistics is {}", trade.getShortId(), adjustedPrice);
return adjustedPrice;
@ -209,12 +220,10 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
@Getter
private final String currency;
@Getter
private final long price;
@Getter
private final long amount; // BTC amount
private final long amount; // XMR amount
private final String paymentMethod;
// As only seller is publishing it is the sellers trade date
private final long date;
// Old converted trade stat objects might not have it set
@ -228,7 +237,7 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
// Hash get set in constructor from json of all the other data fields (with hash = null).
@JsonExclude
private final byte[] hash;
// Should be only used in emergency case if we need to add data but do not want to break backward compatibility
// Should be only used in exceptional case if we need to add data but do not want to break backward compatibility
// at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new
// field in a class would break that hash and therefore break the storage mechanism.
@Nullable
@ -314,8 +323,6 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
public byte[] createHash() {
// We create hash from all fields excluding hash itself. We use json as simple data serialisation.
// TradeDate is different for both peers so we ignore it for hash. ExtraDataMap is ignored as well as at
// software updates we might have different entries which would cause a different hash.
return Hash.getSha256Ripemd160hash(JsonUtil.objectToJson(this).getBytes(Charsets.UTF_8));
}
@ -411,11 +418,26 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
public Price getTradePrice() {
if (priceObj == null) {
priceObj = Price.valueOf(currency, price);
priceObj = Price.valueOf(currency, getNormalizedPrice());
}
return priceObj;
}
/**
* Returns the price as XMR/QUOTE.
*
* Note: Cannot override getPrice() because it's used for gson serialization, nor do we want expose it publicly.
*/
public long getNormalizedPrice() {
return isInverted() ? PriceUtil.invertLongPrice(price, currency) : price;
}
private boolean isInverted() {
return CurrencyUtil.isCryptoCurrency(currency) &&
(extraDataMap == null ||
!extraDataMap.containsKey(VERSION_KEY)); // crypto price is inverted if missing key
}
public BigInteger getTradeAmount() {
return BigInteger.valueOf(amount);
}
@ -472,7 +494,8 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
public String toString() {
return "TradeStatistics3{" +
"\n currency='" + currency + '\'' +
",\n price=" + price +
",\n rawPrice=" + price +
",\n normalizedPrice=" + getNormalizedPrice() +
",\n amount=" + amount +
",\n paymentMethod='" + paymentMethod + '\'' +
",\n date=" + date +

View file

@ -17,7 +17,6 @@
package haveno.core.trade.statistics;
import haveno.core.locale.CurrencyUtil;
import haveno.core.locale.Res;
import haveno.core.monetary.Price;
import haveno.core.monetary.Volume;
@ -39,7 +38,7 @@ public final class TradeStatisticsForJson {
public final long tradeDate;
public final String paymentMethod;
// primaryMarket fields are based on industry standard where primaryMarket is always in the focus (in the app BTC is always in the focus - will be changed in a larger refactoring once)
// primaryMarket fields are based on industry standard where primaryMarket is always in the focus (in the app XMR is always in the focus)
public String currencyPair;
public long primaryMarketTradePrice;
@ -49,27 +48,18 @@ public final class TradeStatisticsForJson {
public TradeStatisticsForJson(TradeStatistics3 tradeStatistics) {
this.currency = tradeStatistics.getCurrency();
this.paymentMethod = tradeStatistics.getPaymentMethodId();
this.tradePrice = tradeStatistics.getPrice();
this.tradePrice = tradeStatistics.getNormalizedPrice();
this.tradeAmount = tradeStatistics.getAmount();
this.tradeDate = tradeStatistics.getDateAsLong();
try {
Price tradePrice = getPrice();
if (CurrencyUtil.isCryptoCurrency(currency)) {
currencyPair = currency + "/" + Res.getBaseCurrencyCode();
primaryMarketTradePrice = tradePrice.getValue();
primaryMarketTradeAmount = getTradeVolume() != null ?
getTradeVolume().getValue() :
0;
primaryMarketTradeVolume = getTradeAmount().longValueExact();
} else {
currencyPair = Res.getBaseCurrencyCode() + "/" + currency;
primaryMarketTradePrice = tradePrice.getValue();
primaryMarketTradeAmount = getTradeAmount().longValueExact();
primaryMarketTradeVolume = getTradeVolume() != null ?
getTradeVolume().getValue() :
0;
}
currencyPair = Res.getBaseCurrencyCode() + "/" + currency;
primaryMarketTradePrice = tradePrice.getValue();
primaryMarketTradeAmount = getTradeAmount().longValueExact();
primaryMarketTradeVolume = getTradeVolume() != null ?
getTradeVolume().getValue() :
0;
} catch (Throwable t) {
log.error(t.getMessage());
t.printStackTrace();

View file

@ -101,8 +101,8 @@ public class TradeStatisticsManager {
.collect(Collectors.toSet());
// remove duplicates in early trades due to bug
deduplicateEarlyTradeStatistics(set);
// remove duplicates in early trade stats due to bugs
removeDuplicateStats(set);
synchronized (observableTradeStatisticsSet) {
observableTradeStatisticsSet.addAll(set);
@ -111,7 +111,42 @@ public class TradeStatisticsManager {
maybeDumpStatistics();
}
private void deduplicateEarlyTradeStatistics(Set<TradeStatistics3> tradeStats) {
private void removeDuplicateStats(Set<TradeStatistics3> tradeStats) {
removeEarlyDuplicateStats(tradeStats);
removeEarlyDuplicateStatsFuzzy(tradeStats);
}
private void removeEarlyDuplicateStats(Set<TradeStatistics3> tradeStats) {
// collect trades before September 30, 2024
Set<TradeStatistics3> earlyTrades = tradeStats.stream()
.filter(e -> e.getDate().toInstant().isBefore(Instant.parse("2024-09-30T00:00:00Z")))
.collect(Collectors.toSet());
// collect stats with duplicated timestamp, currency, and payment method
Set<TradeStatistics3> duplicates = new HashSet<>();
Set<TradeStatistics3> deduplicates = new HashSet<>();
for (TradeStatistics3 tradeStatistic : earlyTrades) {
TradeStatistics3 duplicate = findDuplicate(tradeStatistic, deduplicates);
if (duplicate == null) deduplicates.add(tradeStatistic);
else duplicates.add(tradeStatistic);
}
// remove duplicated stats
tradeStats.removeAll(duplicates);
}
private TradeStatistics3 findDuplicate(TradeStatistics3 tradeStatistics, Set<TradeStatistics3> set) {
return set.stream().filter(e -> isDuplicate(tradeStatistics, e)).findFirst().orElse(null);
}
private boolean isDuplicate(TradeStatistics3 tradeStatistics1, TradeStatistics3 tradeStatistics2) {
if (!tradeStatistics1.getPaymentMethodId().equals(tradeStatistics2.getPaymentMethodId())) return false;
if (!tradeStatistics1.getCurrency().equals(tradeStatistics2.getCurrency())) return false;
return tradeStatistics1.getDateAsLong() == tradeStatistics2.getDateAsLong();
}
private void removeEarlyDuplicateStatsFuzzy(Set<TradeStatistics3> tradeStats) {
// collect trades before August 7, 2024
Set<TradeStatistics3> earlyTrades = tradeStats.stream()
@ -127,7 +162,7 @@ public class TradeStatisticsManager {
else duplicates.add(tradeStatistic);
}
// remove duplicated trades
// remove duplicated stats
tradeStats.removeAll(duplicates);
}
@ -138,7 +173,7 @@ public class TradeStatisticsManager {
private boolean isFuzzyDuplicate(TradeStatistics3 tradeStatistics1, TradeStatistics3 tradeStatistics2) {
if (!tradeStatistics1.getPaymentMethodId().equals(tradeStatistics2.getPaymentMethodId())) return false;
if (!tradeStatistics1.getCurrency().equals(tradeStatistics2.getCurrency())) return false;
if (tradeStatistics1.getPrice() != tradeStatistics2.getPrice()) return false;
if (tradeStatistics1.getNormalizedPrice() != tradeStatistics2.getNormalizedPrice()) return false;
return isFuzzyDuplicateV1(tradeStatistics1, tradeStatistics2) || isFuzzyDuplicateV2(tradeStatistics1, tradeStatistics2);
}

View file

@ -66,15 +66,15 @@ public class AveragePriceUtil {
private static List<TradeStatistics3> removeOutliers(List<TradeStatistics3> list, double percentToTrim) {
List<Double> yValues = list.stream()
.filter(TradeStatistics3::isValid)
.map(e -> (double) e.getPrice())
.map(e -> (double) e.getNormalizedPrice())
.collect(Collectors.toList());
Tuple2<Double, Double> tuple = InlierUtil.findInlierRange(yValues, percentToTrim, HOW_MANY_STD_DEVS_CONSTITUTE_OUTLIER);
double lowerBound = tuple.first;
double upperBound = tuple.second;
return list.stream()
.filter(e -> e.getPrice() > lowerBound)
.filter(e -> e.getPrice() < upperBound)
.filter(e -> e.getNormalizedPrice() > lowerBound)
.filter(e -> e.getNormalizedPrice() < upperBound)
.collect(Collectors.toList());
}

View file

@ -35,6 +35,9 @@ import haveno.core.util.validation.AmountValidator4Decimals;
import haveno.core.util.validation.AmountValidator8Decimals;
import haveno.core.util.validation.InputValidator;
import haveno.core.util.validation.MonetaryValidator;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
@ -84,7 +87,7 @@ public class PriceUtil {
}
public boolean hasMarketPrice(Offer offer) {
String currencyCode = offer.getCurrencyCode();
String currencyCode = offer.getCounterCurrencyCode();
checkNotNull(priceFeedService, "priceFeed must not be null");
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
Price price = offer.getPrice();
@ -103,7 +106,7 @@ public class PriceUtil {
return Optional.empty();
}
String currencyCode = offer.getCurrencyCode();
String currencyCode = offer.getCounterCurrencyCode();
checkNotNull(priceFeedService, "priceFeed must not be null");
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
double marketPriceAsDouble = checkNotNull(marketPrice).getPrice();
@ -114,7 +117,7 @@ public class PriceUtil {
double marketPrice,
OfferDirection direction) {
// If the offer did not use % price we calculate % from current market price
String currencyCode = offer.getCurrencyCode();
String currencyCode = offer.getCounterCurrencyCode();
Price price = offer.getPrice();
int precision = CurrencyUtil.isTraditionalCurrency(currencyCode) ?
TraditionalMoney.SMALLEST_UNIT_EXPONENT :
@ -123,29 +126,15 @@ public class PriceUtil {
double scaled = MathUtils.scaleDownByPowerOf10(priceAsLong, precision);
double value;
if (direction == OfferDirection.SELL) {
if (CurrencyUtil.isTraditionalCurrency(currencyCode)) {
if (marketPrice == 0) {
return Optional.empty();
}
value = 1 - scaled / marketPrice;
} else {
if (marketPrice == 1) {
return Optional.empty();
}
value = scaled / marketPrice - 1;
if (marketPrice == 0) {
return Optional.empty();
}
value = 1 - scaled / marketPrice;
} else {
if (CurrencyUtil.isTraditionalCurrency(currencyCode)) {
if (marketPrice == 1) {
return Optional.empty();
}
value = scaled / marketPrice - 1;
} else {
if (marketPrice == 0) {
return Optional.empty();
}
value = 1 - scaled / marketPrice;
if (marketPrice == 1) {
return Optional.empty();
}
value = scaled / marketPrice - 1;
}
return Optional.of(value);
}
@ -183,4 +172,13 @@ public class PriceUtil {
return CurrencyUtil.isTraditionalCurrency(currencyCode) ?
TraditionalMoney.SMALLEST_UNIT_EXPONENT : CryptoMoney.SMALLEST_UNIT_EXPONENT;
}
public static long invertLongPrice(long price, String currencyCode) {
if (price == 0) return 0;
int precision = CurrencyUtil.isTraditionalCurrency(currencyCode) ? TraditionalMoney.SMALLEST_UNIT_EXPONENT : CryptoMoney.SMALLEST_UNIT_EXPONENT;
double priceDouble = MathUtils.scaleDownByPowerOf10(price, precision);
double priceDoubleInverted = BigDecimal.ONE.divide(BigDecimal.valueOf(priceDouble), precision, RoundingMode.HALF_UP).doubleValue();
double scaled = MathUtils.scaleUpByPowerOf10(priceDoubleInverted, precision);
return MathUtils.roundDoubleToLong(scaled);
}
}

View file

@ -474,12 +474,8 @@ createOffer.amount.prompt=Enter amount in XMR
createOffer.price.prompt=Enter price
createOffer.volume.prompt=Enter amount in {0}
createOffer.amountPriceBox.amountDescription=Amount of XMR to {0}
createOffer.amountPriceBox.buy.amountDescriptionCrypto=Amount of XMR to sell
createOffer.amountPriceBox.sell.amountDescriptionCrypto=Amount of XMR to buy
createOffer.amountPriceBox.buy.volumeDescription=Amount in {0} to spend
createOffer.amountPriceBox.sell.volumeDescription=Amount in {0} to receive
createOffer.amountPriceBox.buy.volumeDescriptionCrypto=Amount in {0} to sell
createOffer.amountPriceBox.sell.volumeDescriptionCrypto=Amount in {0} to buy
createOffer.amountPriceBox.minAmountDescription=Minimum amount of XMR
createOffer.securityDeposit.prompt=Security deposit
createOffer.fundsBox.title=Fund your offer
@ -508,8 +504,8 @@ createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
createOffer.extraInfo.invalid.tooLong=Must not exceed {0} characters.
# new entries
createOffer.placeOfferButton=Review: Place offer to {0} monero
createOffer.placeOfferButtonCrypto=Review: Place offer to {0} {1}
createOffer.placeOfferButton.buy=Review: Place offer to buy XMR with {0}
createOffer.placeOfferButton.sell=Review: Place offer to sell XMR for {0}
createOffer.createOfferFundWalletInfo.headline=Fund your offer
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Trade amount: {0} \n
@ -579,8 +575,8 @@ takeOffer.success.info=You can see the status of your trade at \"Portfolio/Open
takeOffer.error.message=An error occurred when taking the offer.\n\n{0}
# new entries
takeOffer.takeOfferButton=Review: Take offer to {0} monero
takeOffer.takeOfferButtonCrypto=Review: Take offer to {0} {1}
takeOffer.takeOfferButton.buy=Review: Take offer to buy XMR with {0}
takeOffer.takeOfferButton.sell=Review: Take offer to sell XMR for {0}
takeOffer.noPriceFeedAvailable=You cannot take that offer as it uses a percentage price based on the market price but there is no price feed available.
takeOffer.takeOfferFundWalletInfo.headline=Fund your trade
# suppress inspection "TrailingSpacesInProperty"
@ -2008,10 +2004,10 @@ offerDetailsWindow.countryBank=Maker's country of bank
offerDetailsWindow.commitment=Commitment
offerDetailsWindow.agree=I agree
offerDetailsWindow.tac=Terms and conditions
offerDetailsWindow.confirm.maker=Confirm: Place offer to {0} monero
offerDetailsWindow.confirm.makerCrypto=Confirm: Place offer to {0} {1}
offerDetailsWindow.confirm.taker=Confirm: Take offer to {0} monero
offerDetailsWindow.confirm.takerCrypto=Confirm: Take offer to {0} {1}
offerDetailsWindow.confirm.maker.buy=Confirm: Place offer to buy XMR with {0}
offerDetailsWindow.confirm.maker.sell=Confirm: Place offer to sell XMR for {0}
offerDetailsWindow.confirm.taker.buy=Confirm: Take offer to buy XMR with {0}
offerDetailsWindow.confirm.taker.sell=Confirm: Take offer to sell XMR for {0}
offerDetailsWindow.creationDate=Creation date
offerDetailsWindow.makersOnion=Maker's onion address
offerDetailsWindow.challenge=Offer passphrase

View file

@ -457,12 +457,8 @@ createOffer.amount.prompt=Zadejte množství v XMR
createOffer.price.prompt=Zadejte cenu
createOffer.volume.prompt=Zadejte množství v {0}
createOffer.amountPriceBox.amountDescription=Množství XMR, které chcete {0}
createOffer.amountPriceBox.buy.amountDescriptionCrypto=Množství XMR k prodeji
createOffer.amountPriceBox.sell.amountDescriptionCrypto=Množství XMR k nákupu
createOffer.amountPriceBox.buy.volumeDescription=Množství {0}, které odešlete
createOffer.amountPriceBox.sell.volumeDescription=Množství {0}, které přijmete
createOffer.amountPriceBox.buy.volumeDescriptionCrypto=Množství {0} k prodání
createOffer.amountPriceBox.sell.volumeDescriptionCrypto=Množství {0} k nakoupení
createOffer.amountPriceBox.minAmountDescription=Minimální množství XMR
createOffer.securityDeposit.prompt=Kauce
createOffer.fundsBox.title=Financovat nabídku
@ -490,8 +486,8 @@ createOffer.triggerPrice.invalid.tooLow=Hodnota musí být vyšší než {0}
createOffer.triggerPrice.invalid.tooHigh=Hodnota musí být nižší než {0}
# new entries
createOffer.placeOfferButton=Zkontrolovat vytvoření nabídky {0} monero
createOffer.placeOfferButtonCrypto=Zkontrolovat vytvoření nabídky {0} {1}
createOffer.placeOfferButton.buy=Zkontrolovat: Vytvořit nabídku na nákup XMR za {0}
createOffer.placeOfferButton.sell=Zkontrolovat: Vytvořit nabídku na prodej XMR za {0}
createOffer.createOfferFundWalletInfo.headline=Financovat nabídku
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Výše obchodu: {0}\n
@ -561,8 +557,8 @@ takeOffer.success.info=Stav vašeho obchodu můžete vidět v \"Portfolio/Otevř
takeOffer.error.message=Při převzetí nabídky došlo k chybě.\n\n{0}
# new entries
takeOffer.takeOfferButton=Zkontrolovat přijetí nabídky {0} monero
takeOffer.takeOfferButtonCrypto=Zkontrolovat přijetí nabídky {0} {1}
takeOffer.takeOfferButton.buy=Zkontrolovat: Přijmout nabídku na nákup XMR za {0}
takeOffer.takeOfferButton.sell=Zkontrolovat: Přijmout nabídku na prodej XMR za {0}
takeOffer.noPriceFeedAvailable=Tuto nabídku nemůžete vzít, protože používá procentuální cenu založenou na tržní ceně, ale není k dispozici žádný zdroj cen.
takeOffer.takeOfferFundWalletInfo.headline=Financovat obchod
# suppress inspection "TrailingSpacesInProperty"
@ -1964,10 +1960,10 @@ offerDetailsWindow.countryBank=Země původu banky tvůrce
offerDetailsWindow.commitment=Závazek
offerDetailsWindow.agree=Souhlasím
offerDetailsWindow.tac=Pravidla a podmínky
offerDetailsWindow.confirm.maker=Potvrďte: Přidat nabídku {0} monero
offerDetailsWindow.confirm.makerCrypto=Potvrďte: Přidat nabídku do {0} {1}
offerDetailsWindow.confirm.taker=Potvrďte: Přijmout nabídku {0} monero
offerDetailsWindow.confirm.takerCrypto=Potvrďte: Přijmout nabídku {0} {1}
offerDetailsWindow.confirm.maker.buy=Potvrdit: Vytvořit nabídku na nákup XMR za {0}
offerDetailsWindow.confirm.maker.sell=Potvrdit: Vytvořit nabídku na prodej XMR za {0}
offerDetailsWindow.confirm.taker.buy=Potvrdit: Přijmout nabídku na nákup XMR za {0}
offerDetailsWindow.confirm.taker.sell=Potvrdit: Přijmout nabídku na prodej XMR za {0}
offerDetailsWindow.creationDate=Datum vzniku
offerDetailsWindow.makersOnion=Onion adresa tvůrce
offerDetailsWindow.challenge=Passphrase nabídky

View file

@ -455,7 +455,8 @@ createOffer.triggerPrice.invalid.tooLow=Wert muss höher sein als {0}
createOffer.triggerPrice.invalid.tooHigh=Wert muss niedriger sein als {0}
# new entries
createOffer.placeOfferButton=Überprüfung: Anbieten moneros zu {0}
createOffer.placeOfferButton.buy=Überprüfen: Angebot zum Kauf von XMR mit {0} erstellen
createOffer.placeOfferButton.sell=Überprüfen: Angebot zum Verkauf von XMR für {0} erstellen
createOffer.createOfferFundWalletInfo.headline=Ihr Angebot finanzieren
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Handelsbetrag: {0} \n
@ -520,7 +521,8 @@ takeOffer.success.info=Sie können den Status Ihres Trades unter \"Portfolio/Off
takeOffer.error.message=Bei der Angebotsannahme trat ein Fehler auf.\n\n{0}
# new entries
takeOffer.takeOfferButton=Überprüfung: Angebot annehmen moneros zu {0}
takeOffer.takeOfferButton.buy=Überprüfen: Angebot zum Kauf von XMR mit {0} annehmen
takeOffer.takeOfferButton.sell=Überprüfen: Angebot zum Verkauf von XMR für {0} annehmen
takeOffer.noPriceFeedAvailable=Sie können dieses Angebot nicht annehmen, da es auf einem Prozentsatz vom Marktpreis basiert, jedoch keiner verfügbar ist.
takeOffer.takeOfferFundWalletInfo.headline=Ihren Handel finanzieren
# suppress inspection "TrailingSpacesInProperty"
@ -1469,8 +1471,10 @@ offerDetailsWindow.countryBank=Land der Bank des Erstellers
offerDetailsWindow.commitment=Verpflichtung
offerDetailsWindow.agree=Ich stimme zu
offerDetailsWindow.tac=Geschäftsbedingungen
offerDetailsWindow.confirm.maker=Bestätigen: Anbieten monero zu {0}
offerDetailsWindow.confirm.taker=Bestätigen: Angebot annehmen monero zu {0}
offerDetailsWindow.confirm.maker.buy=Bestätigen: Angebot zum Kauf von XMR mit {0} erstellen
offerDetailsWindow.confirm.maker.sell=Bestätigen: Angebot zum Verkauf von XMR für {0} erstellen
offerDetailsWindow.confirm.taker.buy=Bestätigen: Angebot zum Kauf von XMR mit {0} annehmen
offerDetailsWindow.confirm.taker.sell=Bestätigen: Angebot zum Verkauf von XMR für {0} annehmen
offerDetailsWindow.creationDate=Erstellungsdatum
offerDetailsWindow.makersOnion=Onion-Adresse des Erstellers
offerDetailsWindow.challenge=Angebots-Passphrase

View file

@ -455,7 +455,8 @@ createOffer.triggerPrice.invalid.tooLow=El valor debe ser superior a {0}
createOffer.triggerPrice.invalid.tooHigh=El valor debe ser inferior a {0}
# new entries
createOffer.placeOfferButton=Revisar: Poner oferta para {0} monero
createOffer.placeOfferButton.buy=Revisar: Crear oferta para comprar XMR con {0}
createOffer.placeOfferButton.sell=Revisar: Crear oferta para vender XMR por {0}
createOffer.createOfferFundWalletInfo.headline=Dote de fondos su trato.
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Cantidad a intercambiar: {0}\n
@ -520,7 +521,8 @@ takeOffer.success.info=Puede ver el estado de su intercambio en \"Portafolio/Int
takeOffer.error.message=Un error ocurrió al tomar la oferta.\n\n{0}
# new entries
takeOffer.takeOfferButton=Revisión: Tomar oferta a {0} monero
takeOffer.takeOfferButton.buy=Revisar: Aceptar oferta para comprar XMR con {0}
takeOffer.takeOfferButton.sell=Revisar: Aceptar oferta para vender XMR por {0}
takeOffer.noPriceFeedAvailable=No puede tomar esta oferta porque utiliza un precio porcentual basado en el precio de mercado y no hay fuentes de precio disponibles.
takeOffer.takeOfferFundWalletInfo.headline=Dotar de fondos su intercambio
# suppress inspection "TrailingSpacesInProperty"
@ -1470,8 +1472,10 @@ offerDetailsWindow.countryBank=País del banco del creador
offerDetailsWindow.commitment=Compromiso
offerDetailsWindow.agree=Estoy de acuerdo
offerDetailsWindow.tac=Términos y condiciones:
offerDetailsWindow.confirm.maker=Confirmar: Poner oferta para {0} monero
offerDetailsWindow.confirm.taker=Confirmar: Tomar oferta {0} monero
offerDetailsWindow.confirm.maker.buy=Confirmar: Crear oferta para comprar XMR con {0}
offerDetailsWindow.confirm.maker.sell=Confirmar: Crear oferta para vender XMR por {0}
offerDetailsWindow.confirm.taker.buy=Confirmar: Aceptar oferta para comprar XMR con {0}
offerDetailsWindow.confirm.taker.sell=Confirmar: Aceptar oferta para vender XMR por {0}
offerDetailsWindow.creationDate=Fecha de creación
offerDetailsWindow.makersOnion=Dirección onion del creador
offerDetailsWindow.challenge=Frase de contraseña de la oferta

View file

@ -454,7 +454,8 @@ createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=بررسی: پیشنهاد را برای {0} بیتکوین بگذارید
createOffer.placeOfferButton.buy=بررسی: ایجاد پیشنهاد خرید XMR با {0}
createOffer.placeOfferButton.sell=بررسی: ایجاد پیشنهاد فروش XMR به ازای {0}
createOffer.createOfferFundWalletInfo.headline=پیشنهاد خود را تامین وجه نمایید
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=مقدار معامله:{0}\n
@ -519,7 +520,8 @@ takeOffer.success.info=شما می‌توانید وضعیت معامله‌ی
takeOffer.error.message=هنگام قبول کردن پیشنهاد، اتفاقی رخ داده است.\n\n{0}
# new entries
takeOffer.takeOfferButton=بررسی: برای {0} بیتکوین پیشنهاد بگذارید.
takeOffer.takeOfferButton.buy=بررسی: قبول پیشنهاد خرید XMR با {0}
takeOffer.takeOfferButton.sell=بررسی: قبول پیشنهاد فروش XMR به ازای {0}
takeOffer.noPriceFeedAvailable=امکان پذیرفتن پیشنهاد وجود ندارد. پیشنهاد از قیمت درصدی مبتنی بر قیمت روز بازار استفاده می‌کند و قیمت‌های بازار هم‌اکنون در دسترس نیست.
takeOffer.takeOfferFundWalletInfo.headline=معامله خود را تأمین وجه نمایید
# suppress inspection "TrailingSpacesInProperty"
@ -1465,8 +1467,10 @@ offerDetailsWindow.countryBank=کشور بانک سفارش‌گذار
offerDetailsWindow.commitment=تعهد
offerDetailsWindow.agree=من موافقم
offerDetailsWindow.tac=شرایط و الزامات
offerDetailsWindow.confirm.maker=تأیید: پیشنهاد را به {0} بگذارید
offerDetailsWindow.confirm.taker=تأیید: پیشنهاد را به {0} بپذیرید
offerDetailsWindow.confirm.maker.buy=تأیید: ایجاد پیشنهاد خرید XMR با {0}
offerDetailsWindow.confirm.maker.sell=تأیید: ایجاد پیشنهاد فروش XMR به ازای {0}
offerDetailsWindow.confirm.taker.buy=تأیید: قبول پیشنهاد خرید XMR با {0}
offerDetailsWindow.confirm.taker.sell=تأیید: قبول پیشنهاد فروش XMR به ازای {0}
offerDetailsWindow.creationDate=تاریخ ایجاد
offerDetailsWindow.makersOnion=آدرس Onion سفارش گذار
offerDetailsWindow.challenge=Passphrase de l'offre

View file

@ -455,7 +455,8 @@ createOffer.triggerPrice.invalid.tooLow=La valeur doit être supérieure à {0}
createOffer.triggerPrice.invalid.tooHigh=La valuer doit être inférieure à {0]
# new entries
createOffer.placeOfferButton=Review: Placer un ordre de {0} monero
createOffer.placeOfferButton.buy=Vérifier : Créer une offre pour acheter XMR avec {0}
createOffer.placeOfferButton.sell=Vérifier : Créer une offre pour vendre XMR contre {0}
createOffer.createOfferFundWalletInfo.headline=Financer votre ordre
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=Montant du trade: {0}\n\n
@ -520,7 +521,8 @@ takeOffer.success.info=Vous pouvez voir vos transactions dans \"Portfolio/Échan
takeOffer.error.message=Une erreur s'est produite pendant l'acceptation de l'ordre.\n\n{0}
# new entries
takeOffer.takeOfferButton=Vérifier: Accepter l'ordre de {0} Monero
takeOffer.takeOfferButton.buy=Vérifier : Accepter une offre pour acheter XMR avec {0}
takeOffer.takeOfferButton.sell=Vérifier : Accepter une offre pour vendre XMR contre {0}
takeOffer.noPriceFeedAvailable=Vous ne pouvez pas accepter cet ordre, car celui-ci utilise un prix en pourcentage basé sur le prix du marché, mais il n'y a pas de prix de référence de disponible.
takeOffer.takeOfferFundWalletInfo.headline=Provisionner votre trade
# suppress inspection "TrailingSpacesInProperty"
@ -1471,8 +1473,10 @@ offerDetailsWindow.countryBank=Pays de la banque du Maker
offerDetailsWindow.commitment=Engagement
offerDetailsWindow.agree=J'accepte
offerDetailsWindow.tac=Conditions d'utilisation
offerDetailsWindow.confirm.maker=Confirmer: Placer un ordre de {0} monero
offerDetailsWindow.confirm.taker=Confirmer: Acceptez l'ordre de {0} monero
offerDetailsWindow.confirm.maker.buy=Confirmer : Créer une offre pour acheter XMR avec {0}
offerDetailsWindow.confirm.maker.sell=Confirmer : Créer une offre pour vendre XMR contre {0}
offerDetailsWindow.confirm.taker.buy=Confirmer : Accepter une offre pour acheter XMR avec {0}
offerDetailsWindow.confirm.taker.sell=Confirmer : Accepter une offre pour vendre XMR contre {0}
offerDetailsWindow.creationDate=Date de création
offerDetailsWindow.makersOnion=Adresse onion du maker
offerDetailsWindow.challenge=Phrase secrète de l'offre

View file

@ -454,7 +454,8 @@ createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=Revisione: piazza l'offerta a {0} monero
createOffer.placeOfferButton.buy=Revisiona: Crea offerta per acquistare XMR con {0}
createOffer.placeOfferButton.sell=Revisiona: Crea offerta per vendere XMR in cambio di {0}
createOffer.createOfferFundWalletInfo.headline=Finanzia la tua offerta
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Importo di scambio: {0} \n
@ -519,7 +520,8 @@ takeOffer.success.info=Puoi vedere lo stato del tuo scambio su \"Portafoglio/Sca
takeOffer.error.message=Si è verificato un errore durante l'accettazione dell'offerta.\n\n{0}
# new entries
takeOffer.takeOfferButton=Rivedi: Accetta l'offerta per {0} monero
takeOffer.takeOfferButton.buy=Revisiona: Accetta offerta per acquistare XMR con {0}
takeOffer.takeOfferButton.sell=Revisiona: Accetta offerta per vendere XMR in cambio di {0}
takeOffer.noPriceFeedAvailable=Non puoi accettare questa offerta poiché utilizza un prezzo in percentuale basato sul prezzo di mercato ma non è disponibile alcun feed di prezzi.
takeOffer.takeOfferFundWalletInfo.headline=Finanzia il tuo scambio
# suppress inspection "TrailingSpacesInProperty"
@ -1468,8 +1470,10 @@ offerDetailsWindow.countryBank=Paese della banca del maker
offerDetailsWindow.commitment=Impegno
offerDetailsWindow.agree=Accetto
offerDetailsWindow.tac=Termini e condizioni
offerDetailsWindow.confirm.maker=Conferma: Piazza l'offerta a {0} monero
offerDetailsWindow.confirm.taker=Conferma: Accetta l'offerta a {0} monero
offerDetailsWindow.confirm.maker.buy=Conferma: Crea offerta per acquistare XMR con {0}
offerDetailsWindow.confirm.maker.sell=Conferma: Crea offerta per vendere XMR in cambio di {0}
offerDetailsWindow.confirm.taker.buy=Conferma: Accetta offerta per acquistare XMR con {0}
offerDetailsWindow.confirm.taker.sell=Conferma: Accetta offerta per vendere XMR in cambio di {0}
offerDetailsWindow.creationDate=Data di creazione
offerDetailsWindow.makersOnion=Indirizzo .onion del maker
offerDetailsWindow.challenge=Passphrase dell'offerta

View file

@ -455,7 +455,8 @@ createOffer.triggerPrice.invalid.tooLow=価値は{0}より高くなければな
createOffer.triggerPrice.invalid.tooHigh=価値は{0}より低くなければなりません
# new entries
createOffer.placeOfferButton=再確認: ビットコインを{0}オファーを出す
createOffer.placeOfferButton.buy=確認:{0}でXMRを購入するオファーを作成
createOffer.placeOfferButton.sell=確認:{0}でXMRを売却するオファーを作成
createOffer.createOfferFundWalletInfo.headline=あなたのオファーへ入金
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- 取引額: {0}\n
@ -520,7 +521,8 @@ takeOffer.success.info=あなたのトレード状態は「ポートフォリオ
takeOffer.error.message=オファーの受け入れ時にエラーが発生しました。\n\n{0}
# new entries
takeOffer.takeOfferButton=再確認: ビットコインを{0}オファーを申し込む
takeOffer.takeOfferButton.buy=確認:{0}でXMRを購入するオファーを受け入れ
takeOffer.takeOfferButton.sell=確認:{0}でXMRを売却するオファーを受け入れ
takeOffer.noPriceFeedAvailable=そのオファーは市場価格に基づくパーセント値を使用していますが、使用可能な価格フィードがないため、利用することはできません。
takeOffer.takeOfferFundWalletInfo.headline=あなたのオファーへ入金
# suppress inspection "TrailingSpacesInProperty"
@ -1469,8 +1471,10 @@ offerDetailsWindow.countryBank=メイカーの銀行の国名
offerDetailsWindow.commitment=約束
offerDetailsWindow.agree=同意します
offerDetailsWindow.tac=取引条件
offerDetailsWindow.confirm.maker=承認: ビットコインを{0}オファーを出す
offerDetailsWindow.confirm.taker=承認: ビットコインを{0}オファーを受ける
offerDetailsWindow.confirm.maker.buy=確定:{0}でXMRを購入するオファーを作成
offerDetailsWindow.confirm.maker.sell=確定:{0}でXMRを売却するオファーを作成
offerDetailsWindow.confirm.taker.buy=確定:{0}でXMRを購入するオファーを受け入れ
offerDetailsWindow.confirm.taker.sell=確定:{0}でXMRを売却するオファーを受け入れ
offerDetailsWindow.creationDate=作成日
offerDetailsWindow.makersOnion=メイカーのonionアドレス
offerDetailsWindow.challenge=オファーパスフレーズ

View file

@ -457,7 +457,8 @@ createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=Revisar: Criar oferta para {0} monero
createOffer.placeOfferButton.buy=Revisar: Criar oferta para comprar XMR com {0}
createOffer.placeOfferButton.sell=Revisar: Criar oferta para vender XMR por {0}
createOffer.createOfferFundWalletInfo.headline=Financiar sua oferta
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Quantia da negociação: {0} \n
@ -522,7 +523,8 @@ takeOffer.success.info=Você pode ver o status de sua negociação em \"Portfoli
takeOffer.error.message=Ocorreu um erro ao aceitar a oferta.\n\n{0}
# new entries
takeOffer.takeOfferButton=Revisar: Aceitar oferta para {0} monero
takeOffer.takeOfferButton.buy=Revisar: Aceitar oferta para comprar XMR com {0}
takeOffer.takeOfferButton.sell=Revisar: Aceitar oferta para vender XMR por {0}
takeOffer.noPriceFeedAvailable=Você não pode aceitar essa oferta pois ela usa uma porcentagem do preço baseada no preço de mercado, mas o canal de preços está indisponível no momento.
takeOffer.takeOfferFundWalletInfo.headline=Financiar sua negociação
# suppress inspection "TrailingSpacesInProperty"
@ -1472,8 +1474,10 @@ offerDetailsWindow.countryBank=País do banco do ofertante
offerDetailsWindow.commitment=Compromisso
offerDetailsWindow.agree=Eu concordo
offerDetailsWindow.tac=Termos e condições
offerDetailsWindow.confirm.maker=Criar oferta para {0} monero
offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} monero
offerDetailsWindow.confirm.maker.buy=Confirmar: Criar oferta para comprar XMR com {0}
offerDetailsWindow.confirm.maker.sell=Confirmar: Criar oferta para vender XMR por {0}
offerDetailsWindow.confirm.taker.buy=Confirmar: Aceitar oferta para comprar XMR com {0}
offerDetailsWindow.confirm.taker.sell=Confirmar: Aceitar oferta para vender XMR por {0}
offerDetailsWindow.creationDate=Criada em
offerDetailsWindow.makersOnion=Endereço onion do ofertante
offerDetailsWindow.challenge=Passphrase da oferta

View file

@ -454,7 +454,8 @@ createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=Rever: Colocar oferta para {0} monero
createOffer.placeOfferButton.buy=Revisar: Criar oferta para comprar XMR com {0}
createOffer.placeOfferButton.sell=Revisar: Criar oferta para vender XMR por {0}
createOffer.createOfferFundWalletInfo.headline=Financiar sua oferta
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Quantia de negócio: {0} \n
@ -519,7 +520,8 @@ takeOffer.success.info=Você pode ver o estado de seu negócio em \"Portefólio/
takeOffer.error.message=Ocorreu um erro ao aceitar a oferta .\n\n{0}
# new entries
takeOffer.takeOfferButton=Rever: Colocar oferta para {0} monero
takeOffer.takeOfferButton.buy=Revisar: Aceitar oferta para comprar XMR com {0}
takeOffer.takeOfferButton.sell=Revisar: Aceitar oferta para vender XMR por {0}
takeOffer.noPriceFeedAvailable=Você não pode aceitar aquela oferta pois ela utiliza uma percentagem do preço baseada no preço de mercado, mas o feed de preços está indisponível no momento.
takeOffer.takeOfferFundWalletInfo.headline=Financiar seu negócio
# suppress inspection "TrailingSpacesInProperty"
@ -1465,8 +1467,10 @@ offerDetailsWindow.countryBank=País do banco do ofertante
offerDetailsWindow.commitment=Compromisso
offerDetailsWindow.agree=Eu concordo
offerDetailsWindow.tac=Termos e condições
offerDetailsWindow.confirm.maker=Confirmar: Criar oferta para {0} monero
offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} monero
offerDetailsWindow.confirm.maker.buy=Confirmar: Criar oferta para comprar XMR com {0}
offerDetailsWindow.confirm.maker.sell=Confirmar: Criar oferta para vender XMR por {0}
offerDetailsWindow.confirm.taker.buy=Confirmar: Aceitar oferta para comprar XMR com {0}
offerDetailsWindow.confirm.taker.sell=Confirmar: Aceitar oferta para vender XMR por {0}
offerDetailsWindow.creationDate=Data de criação
offerDetailsWindow.makersOnion=Endereço onion do ofertante
offerDetailsWindow.challenge=Passphrase da oferta

View file

@ -454,7 +454,8 @@ createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=Проверка: разместить предложение {0} биткойн
createOffer.placeOfferButton.buy=Проверка: Создать предложение на покупку XMR за {0}
createOffer.placeOfferButton.sell=Проверка: Создать предложение на продажу XMR за {0}
createOffer.createOfferFundWalletInfo.headline=Обеспечить своё предложение
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Сумма сделки: {0} \n
@ -519,7 +520,8 @@ takeOffer.success.info=Статус вашей сделки отображает
takeOffer.error.message=Ошибка при принятии предложения:\n\n{0}
# new entries
takeOffer.takeOfferButton=Проверка: принять предложение {0} биткойн
takeOffer.takeOfferButton.buy=Проверка: Принять предложение на покупку XMR за {0}
takeOffer.takeOfferButton.sell=Проверка: Принять предложение на продажу XMR за {0}
takeOffer.noPriceFeedAvailable=Нельзя принять это предложение, поскольку в нем используется процентный курс на основе рыночного курса, источник которого недоступен.
takeOffer.takeOfferFundWalletInfo.headline=Обеспечьте свою сделку
# suppress inspection "TrailingSpacesInProperty"
@ -1466,8 +1468,10 @@ offerDetailsWindow.countryBank=Страна банка мейкера
offerDetailsWindow.commitment=Обязательство
offerDetailsWindow.agree=Подтверждаю
offerDetailsWindow.tac=Пользовательское соглашение
offerDetailsWindow.confirm.maker=Подтвердите: разместить предложение {0} биткойн
offerDetailsWindow.confirm.taker=Подтвердите: принять предложение {0} биткойн
offerDetailsWindow.confirm.maker.buy=Подтверждение: Создать предложение на покупку XMR за {0}
offerDetailsWindow.confirm.maker.sell=Подтверждение: Создать предложение на продажу XMR за {0}
offerDetailsWindow.confirm.taker.buy=Подтверждение: Принять предложение на покупку XMR за {0}
offerDetailsWindow.confirm.taker.sell=Подтверждение: Принять предложение на продажу XMR за {0}
offerDetailsWindow.creationDate=Дата создания
offerDetailsWindow.makersOnion=Onion-адрес мейкера
offerDetailsWindow.challenge=Пароль предложения

View file

@ -454,7 +454,8 @@ createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=รีวิว: ใส่ข้อเสนอไปยัง {0} บิตคอย
createOffer.placeOfferButton.buy=ตรวจสอบ: สร้างข้อเสนอเพื่อซื้อ XMR ด้วย {0}
createOffer.placeOfferButton.sell=ตรวจสอบ: สร้างข้อเสนอเพื่อขาย XMR เป็น {0}
createOffer.createOfferFundWalletInfo.headline=เงินทุนสำหรับข้อเสนอของคุณ
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- ปริมาณการซื้อขาย: {0}
@ -519,7 +520,8 @@ takeOffer.success.info=คุณสามารถดูสถานะการ
takeOffer.error.message=เกิดข้อผิดพลาดขณะรับข้อเสนอ\n\n{0}
# new entries
takeOffer.takeOfferButton=รีวิว: รับข้อเสนอจาก {0} monero
takeOffer.takeOfferButton.buy=ตรวจสอบ: ยอมรับข้อเสนอเพื่อซื้อ XMR ด้วย {0}
takeOffer.takeOfferButton.sell=ตรวจสอบ: ยอมรับข้อเสนอเพื่อขาย XMR เป็น {0}
takeOffer.noPriceFeedAvailable=คุณไม่สามารถรับข้อเสนอดังกล่าวเนื่องจากใช้ราคาร้อยละตามราคาตลาด แต่ไม่มีฟีดราคาที่พร้อมใช้งาน
takeOffer.takeOfferFundWalletInfo.headline=ทุนการซื้อขายของคุณ
# suppress inspection "TrailingSpacesInProperty"
@ -1466,8 +1468,10 @@ offerDetailsWindow.countryBank=ประเทศของธนาคารข
offerDetailsWindow.commitment=ข้อผูกมัด
offerDetailsWindow.agree=ฉันเห็นด้วย
offerDetailsWindow.tac=ข้อตกลงและเงื่อนไข
offerDetailsWindow.confirm.maker=ยืนยัน: ยื่นข้อเสนอไปยัง{0} บิทคอยน์
offerDetailsWindow.confirm.taker=ยืนยัน: รับข้อเสนอไปยัง {0} บิทคอยน์
offerDetailsWindow.confirm.maker.buy=ยืนยัน: สร้างข้อเสนอเพื่อซื้อ XMR ด้วย {0}
offerDetailsWindow.confirm.maker.sell=ยืนยัน: สร้างข้อเสนอเพื่อขาย XMR เป็น {0}
offerDetailsWindow.confirm.taker.buy=ยืนยัน: ยอมรับข้อเสนอเพื่อซื้อ XMR ด้วย {0}
offerDetailsWindow.confirm.taker.sell=ยืนยัน: ยอมรับข้อเสนอเพื่อขาย XMR เป็น {0}
offerDetailsWindow.creationDate=วันที่สร้าง
offerDetailsWindow.makersOnion=ที่อยู่ onion ของผู้สร้าง
offerDetailsWindow.challenge=รหัสผ่านสำหรับข้อเสนอ

View file

@ -455,12 +455,8 @@ createOffer.amount.prompt=XMR miktarını girin
createOffer.price.prompt=Fiyatı girin
createOffer.volume.prompt={0} cinsinden miktar girin
createOffer.amountPriceBox.amountDescription={0} XMR miktarı
createOffer.amountPriceBox.buy.amountDescriptionCrypto=Satılacak XMR miktarı
createOffer.amountPriceBox.sell.amountDescriptionCrypto=Satın alınacak XMR miktarı
createOffer.amountPriceBox.buy.volumeDescription=Harcanacak {0} miktarı
createOffer.amountPriceBox.sell.volumeDescription=Alınacak {0} miktarı
createOffer.amountPriceBox.buy.volumeDescriptionCrypto=Satılacak {0} miktarı
createOffer.amountPriceBox.sell.volumeDescriptionCrypto=Satın alınacak {0} miktarı
createOffer.amountPriceBox.minAmountDescription=Minimum XMR miktarı
createOffer.securityDeposit.prompt=Güvenlik teminatı
createOffer.fundsBox.title=Teklifinizi finanse edin
@ -488,8 +484,8 @@ createOffer.triggerPrice.invalid.tooLow=Değer {0}'den yüksek olmalıdır
createOffer.triggerPrice.invalid.tooHigh=Değer {0}'den düşük olmalıdır
# new entries
createOffer.placeOfferButton=Gözden Geçir: Teklif ver {0} monero
createOffer.placeOfferButtonCrypto=Gözden Geçir: Teklif ver {0} {1}
createOffer.placeOfferButton.buy=Gözden Geçir: {0} ile XMR satın almak için teklif oluştur
createOffer.placeOfferButton.sell=Gözden Geçir: {0} karşılığında XMR satmak için teklif oluştur
createOffer.createOfferFundWalletInfo.headline=Teklifinizi finanse edin
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Ticaret miktarı: {0} \n
@ -559,8 +555,8 @@ takeOffer.success.info=İşleminizin durumunu \"Portföy/Açık işlemler\" kıs
takeOffer.error.message=Teklif alımı sırasında bir hata oluştu.\n\n{0}
# new entries
takeOffer.takeOfferButton=İncele: {0} monero için teklifi al
takeOffer.takeOfferButtonCrypto=İncele: {0} {1} için teklifi al
takeOffer.takeOfferButton.buy=Gözden Geçir: {0} ile XMR satın alma teklifini kabul et
takeOffer.takeOfferButton.sell=Gözden Geçir: {0} karşılığında XMR satma teklifini kabul et
takeOffer.noPriceFeedAvailable=Bu teklifi alamazsınız çünkü piyasa fiyatına dayalı yüzdelik bir fiyat kullanıyor ancak fiyat beslemesi mevcut değil.
takeOffer.takeOfferFundWalletInfo.headline=İşleminizi finanse edin
# suppress inspection "TrailingSpacesInProperty"
@ -1960,10 +1956,10 @@ offerDetailsWindow.countryBank=Yapıcı'nın banka ülkesi
offerDetailsWindow.commitment=Taahhüt
offerDetailsWindow.agree=Kabul ediyorum
offerDetailsWindow.tac=Şartlar ve koşullar
offerDetailsWindow.confirm.maker=Onayla: {0} monero teklifi yerleştir
offerDetailsWindow.confirm.makerCrypto=Onayla: {0} {1} teklifi yerleştir
offerDetailsWindow.confirm.taker=Onayla: {0} monero teklifi al
offerDetailsWindow.confirm.takerCrypto=Onayla: {0} {1} teklifi al
offerDetailsWindow.confirm.maker.buy=Onayla: {0} ile XMR satın almak için teklif oluştur
offerDetailsWindow.confirm.maker.sell=Onayla: {0} karşılığında XMR satmak için teklif oluştur
offerDetailsWindow.confirm.taker.buy=Onayla: {0} ile XMR satın alma teklifini kabul et
offerDetailsWindow.confirm.taker.sell=Onayla: {0} karşılığında XMR satma teklifini kabul et
offerDetailsWindow.creationDate=Oluşturma tarihi
offerDetailsWindow.makersOnion=Yapıcı'nın onion adresi
offerDetailsWindow.challenge=Teklif şifresi

View file

@ -454,7 +454,8 @@ createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=Kiểm tra:: Đặt báo giá cho {0} monero
createOffer.placeOfferButton.buy=Xem lại: Tạo đề nghị mua XMR bằng {0}
createOffer.placeOfferButton.sell=Xem lại: Tạo đề nghị bán XMR lấy {0}
createOffer.createOfferFundWalletInfo.headline=Nộp tiền cho báo giá của bạn
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Khoản tiền giao dịch: {0} \n
@ -519,7 +520,8 @@ takeOffer.success.info=Bạn có thể xem trạng thái giao dịch của bạn
takeOffer.error.message=Có lỗi xảy ra khi nhận báo giá.\n\n{0}
# new entries
takeOffer.takeOfferButton=Rà soát: Nhận báo giá cho {0} monero
takeOffer.takeOfferButton.buy=Xem lại: Chấp nhận đề nghị mua XMR bằng {0}
takeOffer.takeOfferButton.sell=Xem lại: Chấp nhận đề nghị bán XMR lấy {0}
takeOffer.noPriceFeedAvailable=Bạn không thể nhận báo giá này do sử dụng giá phần trăm dựa trên giá thị trường nhưng không có giá cung cấp.
takeOffer.takeOfferFundWalletInfo.headline=Nộp tiền cho giao dịch của bạn
# suppress inspection "TrailingSpacesInProperty"
@ -1468,8 +1470,10 @@ offerDetailsWindow.countryBank=Quốc gia ngân hàng của người tạo
offerDetailsWindow.commitment=Cam kết
offerDetailsWindow.agree=Tôi đồng ý
offerDetailsWindow.tac=Điều khoản và điều kiện
offerDetailsWindow.confirm.maker=Xác nhận: Đặt chào giá cho {0} monero
offerDetailsWindow.confirm.taker=Xác nhận: Nhận chào giáo cho {0} monero
offerDetailsWindow.confirm.maker.buy=Xác nhận: Tạo đề nghị mua XMR bằng {0}
offerDetailsWindow.confirm.maker.sell=Xác nhận: Tạo đề nghị bán XMR lấy {0}
offerDetailsWindow.confirm.taker.buy=Xác nhận: Chấp nhận đề nghị mua XMR bằng {0}
offerDetailsWindow.confirm.taker.sell=Xác nhận: Chấp nhận đề nghị bán XMR lấy {0}
offerDetailsWindow.creationDate=Ngày tạo
offerDetailsWindow.makersOnion=Địa chỉ onion của người tạo
offerDetailsWindow.challenge=Mã bảo vệ giao dịch

View file

@ -455,7 +455,8 @@ createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=复审:报价挂单 {0} 比特币
createOffer.placeOfferButton.buy=审核:创建以 {0} 买入 XMR 的报价
createOffer.placeOfferButton.sell=审核:创建以 {0} 卖出 XMR 的报价
createOffer.createOfferFundWalletInfo.headline=为您的报价充值
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- 交易数量:{0}\n
@ -520,7 +521,8 @@ takeOffer.success.info=你可以在“业务/未完成交易”页面内查看
takeOffer.error.message=下单时发生了一个错误。\n\n{0}
# new entries
takeOffer.takeOfferButton=复审:报价下单 {0} 比特币
takeOffer.takeOfferButton.buy=审核:接受以 {0} 买入 XMR 的报价
takeOffer.takeOfferButton.sell=审核:接受以 {0} 卖出 XMR 的报价
takeOffer.noPriceFeedAvailable=您不能对这笔报价下单,因为它使用交易所价格百分比定价,但是您没有获得可用的价格。
takeOffer.takeOfferFundWalletInfo.headline=为交易充值
# suppress inspection "TrailingSpacesInProperty"
@ -1470,8 +1472,10 @@ offerDetailsWindow.countryBank=卖家银行所在国家或地区
offerDetailsWindow.commitment=承诺
offerDetailsWindow.agree=我同意
offerDetailsWindow.tac=条款和条件
offerDetailsWindow.confirm.maker=确定:发布报价 {0} 比特币
offerDetailsWindow.confirm.taker=确定:下单买入 {0} 比特币
offerDetailsWindow.confirm.maker.buy=确认:创建以 {0} 买入 XMR 的报价
offerDetailsWindow.confirm.maker.sell=确认:创建以 {0} 卖出 XMR 的报价
offerDetailsWindow.confirm.taker.buy=确认:接受以 {0} 买入 XMR 的报价
offerDetailsWindow.confirm.taker.sell=确认:接受以 {0} 卖出 XMR 的报价
offerDetailsWindow.creationDate=创建时间
offerDetailsWindow.makersOnion=卖家的匿名地址
offerDetailsWindow.challenge=提供密码

View file

@ -455,7 +455,8 @@ createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=複審:報價掛單 {0} 比特幣
createOffer.placeOfferButton.buy=審核:建立以 {0} 買入 XMR 的報價
createOffer.placeOfferButton.sell=審核:建立以 {0} 賣出 XMR 的報價
createOffer.createOfferFundWalletInfo.headline=為您的報價充值
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- 交易數量:{0}\n
@ -520,7 +521,8 @@ takeOffer.success.info=你可以在“業務/未完成交易”頁面內查看
takeOffer.error.message=下單時發生了一個錯誤。\n\n{0}
# new entries
takeOffer.takeOfferButton=複審:報價下單 {0} 比特幣
takeOffer.takeOfferButton.buy=審核:接受以 {0} 買入 XMR 的報價
takeOffer.takeOfferButton.sell=審核:接受以 {0} 賣出 XMR 的報價
takeOffer.noPriceFeedAvailable=您不能對這筆報價下單,因為它使用交易所價格百分比定價,但是您沒有獲得可用的價格。
takeOffer.takeOfferFundWalletInfo.headline=為交易充值
# suppress inspection "TrailingSpacesInProperty"
@ -1470,8 +1472,10 @@ offerDetailsWindow.countryBank=賣家銀行所在國家或地區
offerDetailsWindow.commitment=承諾
offerDetailsWindow.agree=我同意
offerDetailsWindow.tac=條款和條件
offerDetailsWindow.confirm.maker=確定:發佈報價 {0} 比特幣
offerDetailsWindow.confirm.taker=確定:下單買入 {0} 比特幣
offerDetailsWindow.confirm.maker.buy=確認:建立以 {0} 買入 XMR 的報價
offerDetailsWindow.confirm.maker.sell=確認:建立以 {0} 賣出 XMR 的報價
offerDetailsWindow.confirm.taker.buy=確認:接受以 {0} 買入 XMR 的報價
offerDetailsWindow.confirm.taker.sell=確認:接受以 {0} 賣出 XMR 的報價
offerDetailsWindow.creationDate=創建時間
offerDetailsWindow.makersOnion=賣家的匿名地址
offerDetailsWindow.challenge=提供密碼

View file

@ -59,13 +59,13 @@ public class PriceTest {
);
assertEquals(
"10000.2346 LTC/XMR",
"10000.2346 XMR/LTC",
parse("LTC", "10000,23456789").toFriendlyString(),
"Too many decimals should get rounded up properly."
);
assertEquals(
"10000.2345 LTC/XMR",
"10000.2345 XMR/LTC",
parse("LTC", "10000,23454999").toFriendlyString(),
"Too many decimals should get rounded down properly."
);
@ -108,13 +108,13 @@ public class PriceTest {
);
assertEquals(
"10000.2346 LTC/XMR",
"10000.2346 XMR/LTC",
valueOf("LTC", 1000023456789L).toFriendlyString(),
"Too many decimals should get rounded up properly."
);
assertEquals(
"10000.2345 LTC/XMR",
"10000.2345 XMR/LTC",
valueOf("LTC", 1000023454999L).toFriendlyString(),
"Too many decimals should get rounded down properly."
);

View file

@ -34,7 +34,7 @@ public class ReceiptPredicatesTest {
@Test
public void testIsMatchingCurrency() {
Offer offer = mock(Offer.class);
when(offer.getCurrencyCode()).thenReturn("USD");
when(offer.getCounterCurrencyCode()).thenReturn("USD");
PaymentAccount account = mock(PaymentAccount.class);
when(account.getTradeCurrencies()).thenReturn(Lists.newArrayList(

View file

@ -48,7 +48,6 @@ import haveno.proto.grpc.MarketPriceReply;
import haveno.proto.grpc.MarketPriceRequest;
import haveno.proto.grpc.MarketPricesReply;
import haveno.proto.grpc.MarketPricesRequest;
import static haveno.proto.grpc.PriceGrpc.PriceImplBase;
import static haveno.proto.grpc.PriceGrpc.getGetMarketPriceMethod;
import haveno.proto.grpc.PriceGrpc.PriceImplBase;
import io.grpc.ServerInterceptor;
@ -57,8 +56,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import static haveno.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
import static haveno.proto.grpc.PriceGrpc.getGetMarketPriceMethod;
import static java.util.concurrent.TimeUnit.SECONDS;
import lombok.extern.slf4j.Slf4j;

View file

@ -198,6 +198,6 @@ public class PeerInfoIconTrading extends PeerInfoIcon {
Offer offerToCheck = Trade != null ? Trade.getOffer() : offer;
return offerToCheck != null &&
PaymentMethod.hasChargebackRisk(offerToCheck.getPaymentMethod(), offerToCheck.getCurrencyCode());
PaymentMethod.hasChargebackRisk(offerToCheck.getPaymentMethod(), offerToCheck.getCounterCurrencyCode());
}
}

View file

@ -209,7 +209,7 @@ public class MarketView extends ActivatableView<TabPane, Void> {
StringBuilder sb = new StringBuilder();
sb.append("Offer ID: ").append(offer.getId()).append("\n")
.append("Type: ").append(offer.getDirection().name()).append("\n")
.append("Market: ").append(CurrencyUtil.getCurrencyPair(offer.getCurrencyCode())).append("\n")
.append("Market: ").append(CurrencyUtil.getCurrencyPair(offer.getCounterCurrencyCode())).append("\n")
.append("Price: ").append(FormattingUtils.formatPrice(offer.getPrice())).append("\n")
.append("Amount: ").append(DisplayUtils.formatAmount(offer, formatter)).append(" BTC\n")
.append("Payment method: ").append(Res.get(offer.getPaymentMethod().getId())).append("\n")

View file

@ -236,19 +236,19 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
}
});
String viewBaseCurrencyCode = model.isCrypto() ? code : Res.getBaseCurrencyCode();
String viewPriceCurrencyCode = model.isCrypto() ? Res.getBaseCurrencyCode() : code;
String viewBaseCurrencyCode = Res.getBaseCurrencyCode();
String viewPriceCurrencyCode = code;
sellHeaderLabel.setText(Res.get("market.offerBook.sellOffersHeaderLabel", viewBaseCurrencyCode));
sellButton.updateText(Res.get("shared.sellCurrency", viewBaseCurrencyCode));
sellButton.setGraphic(GUIUtil.getCurrencyIconWithBorder(viewBaseCurrencyCode));
sellButton.setOnAction(e -> model.goToOfferView(model.isCrypto() ? OfferDirection.SELL : OfferDirection.BUY));
sellButton.setOnAction(e -> model.goToOfferView(OfferDirection.BUY));
sellButton.setId("sell-button-big");
buyHeaderLabel.setText(Res.get("market.offerBook.buyOffersHeaderLabel", viewBaseCurrencyCode));
buyButton.updateText(Res.get( "shared.buyCurrency", viewBaseCurrencyCode));
buyButton.setGraphic(GUIUtil.getCurrencyIconWithBorder(viewBaseCurrencyCode));
buyButton.setOnAction(e -> model.goToOfferView(model.isCrypto() ? OfferDirection.BUY : OfferDirection.SELL));
buyButton.setOnAction(e -> model.goToOfferView(OfferDirection.SELL));
buyButton.setId("buy-button-big");
priceColumnLabel.set(Res.get("shared.priceWithCur", viewPriceCurrencyCode));
@ -358,8 +358,8 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
private synchronized void updateChartData() {
// update volume headers
Volume volumeSell = model.getTotalVolume(model.isCrypto() ? OfferDirection.BUY : OfferDirection.SELL);
Volume volumeBuy = model.getTotalVolume(model.isCrypto() ? OfferDirection.SELL : OfferDirection.BUY);
Volume volumeSell = model.getTotalVolume(OfferDirection.SELL);
Volume volumeBuy = model.getTotalVolume(OfferDirection.BUY);
String formattedVolumeSell = volumeSell == null ? null : VolumeUtil.formatVolume(volumeSell);
String formattedVolumeBuy = volumeBuy == null ? null : VolumeUtil.formatVolume(volumeBuy);
if (model.getSellData().isEmpty()) formattedVolumeSell = "0.0";
@ -368,8 +368,8 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
volumeBuyColumnLabel.set(Res.get("offerbook.volumeTotal", model.getCurrencyCode(), formattedVolumeBuy == null ? "" : "(" + formattedVolumeBuy + ")"));
// update amount headers
amountSellColumnLabel.set(Res.get("offerbook.XMRTotal", "" + model.getTotalAmount(model.isCrypto() ? OfferDirection.BUY : OfferDirection.SELL)));
amountBuyColumnLabel.set(Res.get("offerbook.XMRTotal", "" + model.getTotalAmount(model.isCrypto() ? OfferDirection.SELL : OfferDirection.BUY)));
amountSellColumnLabel.set(Res.get("offerbook.XMRTotal", "" + model.getTotalAmount(OfferDirection.SELL)));
amountBuyColumnLabel.set(Res.get("offerbook.XMRTotal", "" + model.getTotalAmount(OfferDirection.BUY)));
seriesSell.getData().clear();
seriesBuy.getData().clear();

View file

@ -130,7 +130,7 @@ class OfferBookChartViewModel extends ActivatableViewModel {
list.addAll(c.getAddedSubList());
if (list.stream()
.map(OfferBookListItem::getOffer)
.anyMatch(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())))
.anyMatch(e -> e.getCounterCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())))
updateChartData();
}
@ -156,7 +156,7 @@ class OfferBookChartViewModel extends ActivatableViewModel {
synchronized (offerBookListItems) {
List<TradeCurrency> tradeCurrencyList = offerBookListItems.stream()
.map(e -> {
String currencyCode = e.getOffer().getCurrencyCode();
String currencyCode = e.getOffer().getCounterCurrencyCode();
Optional<TradeCurrency> tradeCurrencyOptional = CurrencyUtil.getTradeCurrency(currencyCode);
return tradeCurrencyOptional.orElse(null);
})
@ -221,7 +221,7 @@ class OfferBookChartViewModel extends ActivatableViewModel {
synchronized (offerBookListItems) {
List<Offer> offerList = offerBookListItems.stream()
.map(OfferBookListItem::getOffer)
.filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
.filter(e -> e.getCounterCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
&& e.getDirection().equals(direction))
.collect(Collectors.toList());
BigInteger sum = BigInteger.ZERO;
@ -234,7 +234,7 @@ class OfferBookChartViewModel extends ActivatableViewModel {
synchronized (offerBookListItems) {
List<Volume> volumes = offerBookListItems.stream()
.map(OfferBookListItem::getOffer)
.filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
.filter(e -> e.getCounterCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
&& e.getDirection().equals(direction))
.map(Offer::getVolume)
.collect(Collectors.toList());
@ -300,13 +300,13 @@ class OfferBookChartViewModel extends ActivatableViewModel {
}
public int getMaxNumberOfPriceZeroDecimalsToColorize(Offer offer) {
return CurrencyUtil.isVolumeRoundedToNearestUnit(offer.getCurrencyCode())
return CurrencyUtil.isVolumeRoundedToNearestUnit(offer.getCounterCurrencyCode())
? GUIUtil.NUM_DECIMALS_UNIT
: GUIUtil.NUM_DECIMALS_PRECISE;
}
public int getZeroDecimalsForPrice(Offer offer) {
return CurrencyUtil.isPricePrecise(offer.getCurrencyCode())
return CurrencyUtil.isPricePrecise(offer.getCounterCurrencyCode())
? GUIUtil.NUM_DECIMALS_PRECISE
: GUIUtil.NUM_DECIMALS_PRICE_LESS_PRECISE;
}
@ -350,13 +350,6 @@ class OfferBookChartViewModel extends ActivatableViewModel {
// Offer price can be null (if price feed unavailable), thus a null-tolerant comparator is used.
Comparator<Offer> offerPriceComparator = Comparator.comparing(Offer::getPrice, Comparator.nullsLast(Comparator.naturalOrder()));
// Trading xmr-traditional is considered as buying/selling XMR, but trading xmr-crypto is
// considered as buying/selling Crypto. Because of this, when viewing a xmr-crypto pair,
// the buy column is actually the sell column and vice versa. To maintain the expected
// ordering, we have to reverse the price comparator.
boolean isCrypto = CurrencyUtil.isCryptoCurrency(getCurrencyCode());
// if (isCrypto) offerPriceComparator = offerPriceComparator.reversed();
// Offer amounts are used for the secondary sort. They are sorted from high to low.
Comparator<Offer> offerAmountComparator = Comparator.comparing(Offer::getAmount).reversed();
@ -367,11 +360,11 @@ class OfferBookChartViewModel extends ActivatableViewModel {
offerPriceComparator
.thenComparing(offerAmountComparator);
OfferDirection buyOfferDirection = isCrypto ? OfferDirection.SELL : OfferDirection.BUY;
OfferDirection buyOfferDirection = OfferDirection.BUY;
List<Offer> allBuyOffers = offerBookListItems.stream()
.map(OfferBookListItem::getOffer)
.filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
.filter(e -> e.getCounterCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
&& e.getDirection().equals(buyOfferDirection))
.sorted(buyOfferSortComparator)
.collect(Collectors.toList());
@ -398,11 +391,11 @@ class OfferBookChartViewModel extends ActivatableViewModel {
buildChartAndTableEntries(allBuyOffers, OfferDirection.BUY, buyData, topBuyOfferList);
OfferDirection sellOfferDirection = isCrypto ? OfferDirection.BUY : OfferDirection.SELL;
OfferDirection sellOfferDirection = OfferDirection.SELL;
List<Offer> allSellOffers = offerBookListItems.stream()
.map(OfferBookListItem::getOffer)
.filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
.filter(e -> e.getCounterCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
&& e.getDirection().equals(sellOfferDirection))
.sorted(sellOfferSortComparator)
.collect(Collectors.toList());

View file

@ -116,11 +116,11 @@ class SpreadViewModel extends ActivatableViewModel {
synchronized (offerBookListItems) {
for (OfferBookListItem offerBookListItem : offerBookListItems) {
Offer offer = offerBookListItem.getOffer();
String key = offer.getCurrencyCode();
String key = offer.getCounterCurrencyCode();
if (includePaymentMethod) {
key = offer.getPaymentMethod().getShortName();
if (expandedView) {
key += ":" + offer.getCurrencyCode();
key += ":" + offer.getCounterCurrencyCode();
}
}
if (!offersByCurrencyMap.containsKey(key))
@ -134,8 +134,6 @@ class SpreadViewModel extends ActivatableViewModel {
for (String key : offersByCurrencyMap.keySet()) {
List<Offer> offers = offersByCurrencyMap.get(key);
boolean iTraditionalCurrency = (offers.size() > 0 && offers.get(0).getPaymentMethod().isTraditional());
List<Offer> uniqueOffers = offers.stream().filter(distinctByKey(Offer::getId)).collect(Collectors.toList());
List<Offer> buyOffers = uniqueOffers
@ -145,11 +143,7 @@ class SpreadViewModel extends ActivatableViewModel {
long a = o1.getPrice() != null ? o1.getPrice().getValue() : 0;
long b = o2.getPrice() != null ? o2.getPrice().getValue() : 0;
if (a != b) {
if (iTraditionalCurrency) {
return a < b ? 1 : -1;
} else {
return a < b ? -1 : 1;
}
return a < b ? 1 : -1;
}
return 0;
})
@ -162,11 +156,7 @@ class SpreadViewModel extends ActivatableViewModel {
long a = o1.getPrice() != null ? o1.getPrice().getValue() : 0;
long b = o2.getPrice() != null ? o2.getPrice().getValue() : 0;
if (a != b) {
if (iTraditionalCurrency) {
return a > b ? 1 : -1;
} else {
return a > b ? -1 : 1;
}
return a > b ? 1 : -1;
}
return 0;
})
@ -178,24 +168,22 @@ class SpreadViewModel extends ActivatableViewModel {
Price bestSellOfferPrice = sellOffers.isEmpty() ? null : sellOffers.get(0).getPrice();
Price bestBuyOfferPrice = buyOffers.isEmpty() ? null : buyOffers.get(0).getPrice();
if (bestBuyOfferPrice != null && bestSellOfferPrice != null &&
sellOffers.get(0).getCurrencyCode().equals(buyOffers.get(0).getCurrencyCode())) {
MarketPrice marketPrice = priceFeedService.getMarketPrice(sellOffers.get(0).getCurrencyCode());
sellOffers.get(0).getCounterCurrencyCode().equals(buyOffers.get(0).getCounterCurrencyCode())) {
MarketPrice marketPrice = priceFeedService.getMarketPrice(sellOffers.get(0).getCounterCurrencyCode());
// There have been some bug reports that an offer caused an overflow exception.
// We never found out which offer it was. So add here a try/catch to get better info if it
// happens again
try {
if (iTraditionalCurrency)
spread = bestSellOfferPrice.subtract(bestBuyOfferPrice);
else
spread = bestBuyOfferPrice.subtract(bestSellOfferPrice);
spread = bestSellOfferPrice.subtract(bestBuyOfferPrice);
// TODO maybe show extra columns with spread and use real amount diff
// not % based. e.g. diff between best buy and sell offer (of small amounts its a smaller gain)
if (spread != null && marketPrice != null && marketPrice.isPriceAvailable()) {
double marketPriceAsDouble = marketPrice.getPrice();
final double precision = iTraditionalCurrency ?
boolean isTraditionalCurrency = (offers.size() > 0 && offers.get(0).getPaymentMethod().isTraditional());
final double precision = isTraditionalCurrency ?
Math.pow(10, TraditionalMoney.SMALLEST_UNIT_EXPONENT) :
Math.pow(10, CryptoMoney.SMALLEST_UNIT_EXPONENT);
@ -217,8 +205,8 @@ class SpreadViewModel extends ActivatableViewModel {
"Details of offer data: \n" +
"bestSellOfferPrice: " + bestSellOfferPrice.getValue() + "\n" +
"bestBuyOfferPrice: " + bestBuyOfferPrice.getValue() + "\n" +
"sellOffer getCurrencyCode: " + sellOffers.get(0).getCurrencyCode() + "\n" +
"buyOffer getCurrencyCode: " + buyOffers.get(0).getCurrencyCode() + "\n\n" +
"sellOffer getCurrencyCode: " + sellOffers.get(0).getCounterCurrencyCode() + "\n" +
"buyOffer getCurrencyCode: " + buyOffers.get(0).getCounterCurrencyCode() + "\n\n" +
"Please copy and paste this data and send it to the developers so they can investigate the issue.";
new Popup().error(msg).show();
log.error(t.toString());

View file

@ -263,16 +263,10 @@ public class ChartCalculations {
Long[] prices = new Long[tradePrices.size()];
tradePrices.toArray(prices);
long medianPrice = MathUtils.getMedian(prices);
boolean isBullish;
if (CurrencyUtil.isCryptoCurrency(currencyCode)) {
isBullish = close < open;
BigInteger accumulatedAmountAsBI = MathUtils.scaleUpByPowerOf10(accumulatedAmount, CryptoMoney.SMALLEST_UNIT_EXPONENT - 4);
averagePrice = MathUtils.roundDoubleToLong(HavenoUtils.divide(accumulatedAmountAsBI, accumulatedVolume));
} else {
isBullish = close > open;
BigInteger accumulatedVolumeAsBI = MathUtils.scaleUpByPowerOf10(accumulatedVolume, TraditionalMoney.SMALLEST_UNIT_EXPONENT + 4);
averagePrice = MathUtils.roundDoubleToLong(HavenoUtils.divide(accumulatedVolumeAsBI, accumulatedAmount));
}
boolean isBullish = close > open;
int smallestUnitExponent = CurrencyUtil.isCryptoCurrency(currencyCode) ? CryptoMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT;
BigInteger accumulatedVolumeAsBI = MathUtils.scaleUpByPowerOf10(accumulatedVolume, smallestUnitExponent + 4);
averagePrice = MathUtils.roundDoubleToLong(HavenoUtils.divide(accumulatedVolumeAsBI, accumulatedAmount));
Date dateFrom = new Date(getTimeFromTickIndex(tick, itemsPerInterval));
Date dateTo = new Date(getTimeFromTickIndex(tick + 1, itemsPerInterval));
@ -281,10 +275,10 @@ public class ChartCalculations {
DisplayUtils.formatDate(dateFrom) + " - " + DisplayUtils.formatDate(dateTo);
// We do not need precision, so we scale down before multiplication otherwise we could get an overflow.
averageUsdPrice = (long) MathUtils.scaleDownByPowerOf10((double) averageUsdPrice, TraditionalMoney.SMALLEST_UNIT_EXPONENT);
averageUsdPrice = (long) MathUtils.scaleDownByPowerOf10((double) averageUsdPrice, smallestUnitExponent);
long volumeInUsd = averageUsdPrice * MathUtils.scaleDownByPowerOf10(accumulatedAmount, 4).longValue();
// We store USD value without decimals as its only total volume, no precision is needed.
volumeInUsd = (long) MathUtils.scaleDownByPowerOf10((double) volumeInUsd, TraditionalMoney.SMALLEST_UNIT_EXPONENT);
volumeInUsd = (long) MathUtils.scaleDownByPowerOf10((double) volumeInUsd, smallestUnitExponent);
return new CandleData(tick, open, close, high, low, averagePrice, medianPrice, accumulatedAmount.longValueExact(), accumulatedVolume.longValueExact(),
numTrades, isBullish, dateString, volumeInUsd);
}

View file

@ -265,7 +265,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
return;
}
if (throwable != null) {
log.error("Error at applyAsyncChartData. {}", throwable.toString());
log.error("Error at applyAsyncChartData. {}", throwable);
return;
}
UserThread.execute(() -> {

View file

@ -321,20 +321,12 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
if (OfferViewUtil.isShownAsBuyOffer(direction, tradeCurrency)) {
placeOfferButton.setId("buy-button-big");
if (CurrencyUtil.isTraditionalCurrency(tradeCurrency.getCode())) {
placeOfferButtonLabel = Res.get("createOffer.placeOfferButton", Res.get("shared.buy"));
} else {
placeOfferButtonLabel = Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.sell"), tradeCurrency.getCode());
}
placeOfferButtonLabel = Res.get("createOffer.placeOfferButton.buy", tradeCurrency.getCode());
nextButton.setId("buy-button");
fundFromSavingsWalletButton.setId("buy-button");
} else {
placeOfferButton.setId("sell-button-big");
if (CurrencyUtil.isTraditionalCurrency(tradeCurrency.getCode())) {
placeOfferButtonLabel = Res.get("createOffer.placeOfferButton", Res.get("shared.sell"));
} else {
placeOfferButtonLabel = Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.buy"), tradeCurrency.getCode());
}
placeOfferButtonLabel = Res.get("createOffer.placeOfferButton.sell", tradeCurrency.getCode());
nextButton.setId("sell-button");
fundFromSavingsWalletButton.setId("sell-button");
}
@ -736,15 +728,6 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
marketBasedPriceTextField.clear();
volumeTextField.clear();
triggerPriceInputTextField.clear();
if (!CurrencyUtil.isTraditionalCurrency(newValue)) {
if (model.isShownAsBuyOffer()) {
placeOfferButton.updateText(Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.sell"),
model.getTradeCurrency().getCode()));
} else {
placeOfferButton.updateText(Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.buy"),
model.getTradeCurrency().getCode()));
}
}
};
placeOfferCompletedListener = (o, oldValue, newValue) -> {

View file

@ -261,15 +261,11 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
private void addBindings() {
if (dataModel.getDirection() == OfferDirection.BUY) {
volumeDescriptionLabel.bind(createStringBinding(
() -> Res.get(CurrencyUtil.isTraditionalCurrency(dataModel.getTradeCurrencyCode().get()) ?
"createOffer.amountPriceBox.buy.volumeDescription" :
"createOffer.amountPriceBox.buy.volumeDescriptionCrypto", dataModel.getTradeCurrencyCode().get()),
() -> Res.get("createOffer.amountPriceBox.buy.volumeDescription", dataModel.getTradeCurrencyCode().get()),
dataModel.getTradeCurrencyCode()));
} else {
volumeDescriptionLabel.bind(createStringBinding(
() -> Res.get(CurrencyUtil.isTraditionalCurrency(dataModel.getTradeCurrencyCode().get()) ?
"createOffer.amountPriceBox.sell.volumeDescription" :
"createOffer.amountPriceBox.sell.volumeDescriptionCrypto", dataModel.getTradeCurrencyCode().get()),
() -> Res.get("createOffer.amountPriceBox.sell.volumeDescription", dataModel.getTradeCurrencyCode().get()),
dataModel.getTradeCurrencyCode()));
}
volumePromptLabel.bind(createStringBinding(
@ -319,7 +315,6 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
};
priceStringListener = (ov, oldValue, newValue) -> {
updateMarketPriceAvailable();
final String currencyCode = dataModel.getTradeCurrencyCode().get();
if (!ignorePriceStringListener) {
if (isPriceInputValid(newValue).isValid) {
setPriceToModel();
@ -332,9 +327,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
try {
double priceAsDouble = ParsingUtils.parseNumberStringToDouble(price.get());
double relation = priceAsDouble / marketPriceAsDouble;
final OfferDirection compareDirection = CurrencyUtil.isCryptoCurrency(currencyCode) ?
OfferDirection.SELL :
OfferDirection.BUY;
final OfferDirection compareDirection = OfferDirection.BUY;
double percentage = dataModel.getDirection() == compareDirection ? 1 - relation : relation - 1;
percentage = MathUtils.roundDouble(percentage, 4);
dataModel.setMarketPriceMarginPct(percentage);
@ -367,9 +360,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
if (marketPrice != null && marketPrice.isRecentExternalPriceAvailable()) {
percentage = MathUtils.roundDouble(percentage, 4);
double marketPriceAsDouble = marketPrice.getPrice();
final OfferDirection compareDirection = CurrencyUtil.isCryptoCurrency(currencyCode) ?
OfferDirection.SELL :
OfferDirection.BUY;
final OfferDirection compareDirection = OfferDirection.BUY;
double factor = dataModel.getDirection() == compareDirection ?
1 - percentage :
1 + percentage;
@ -614,15 +605,8 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
final boolean isBuy = dataModel.getDirection() == OfferDirection.BUY;
boolean isTraditionalCurrency = CurrencyUtil.isTraditionalCurrency(tradeCurrency.getCode());
if (isTraditionalCurrency) {
amountDescription = Res.get("createOffer.amountPriceBox.amountDescription",
isBuy ? Res.get("shared.buy") : Res.get("shared.sell"));
} else {
amountDescription = Res.get(isBuy ? "createOffer.amountPriceBox.sell.amountDescriptionCrypto" :
"createOffer.amountPriceBox.buy.amountDescriptionCrypto");
}
amountDescription = Res.get("createOffer.amountPriceBox.amountDescription",
isBuy ? Res.get("shared.buy") : Res.get("shared.sell"));
securityDepositValidator.setPaymentAccount(dataModel.paymentAccount);
validateAndSetSecurityDepositToModel();
@ -1164,29 +1148,14 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
}
String getTriggerPriceDescriptionLabel() {
String details;
if (dataModel.isBuyOffer()) {
details = dataModel.isCryptoCurrency() ?
Res.get("account.notifications.marketAlert.message.msg.below") :
Res.get("account.notifications.marketAlert.message.msg.above");
} else {
details = dataModel.isCryptoCurrency() ?
Res.get("account.notifications.marketAlert.message.msg.above") :
Res.get("account.notifications.marketAlert.message.msg.below");
}
String details = dataModel.isBuyOffer() ?
Res.get("account.notifications.marketAlert.message.msg.above") :
Res.get("account.notifications.marketAlert.message.msg.below");
return Res.get("createOffer.triggerPrice.label", details);
}
String getPercentagePriceDescription() {
if (dataModel.isBuyOffer()) {
return dataModel.isCryptoCurrency() ?
Res.get("shared.aboveInPercent") :
Res.get("shared.belowInPercent");
} else {
return dataModel.isCryptoCurrency() ?
Res.get("shared.belowInPercent") :
Res.get("shared.aboveInPercent");
}
return dataModel.isBuyOffer() ? Res.get("shared.belowInPercent") : Res.get("shared.aboveInPercent");
}

View file

@ -138,7 +138,7 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
@Override
public void onTakeOffer(Offer offer) {
Optional<TradeCurrency> optionalTradeCurrency = CurrencyUtil.getTradeCurrency(offer.getCurrencyCode());
Optional<TradeCurrency> optionalTradeCurrency = CurrencyUtil.getTradeCurrency(offer.getCounterCurrencyCode());
if (optionalTradeCurrency.isPresent() && canCreateOrTakeOffer(optionalTradeCurrency.get())) {
showTakeOffer(offer);
}
@ -192,12 +192,10 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
loadTakeViewClass(viewClass, childViewClass, cryptoOfferBookTab);
} else {
// add sanity check in case of app restart
if (CurrencyUtil.isTraditionalCurrency(tradeCurrency.getCode())) {
Optional<TradeCurrency> tradeCurrencyOptional = (this.direction == OfferDirection.SELL) ?
CurrencyUtil.getTradeCurrency(preferences.getSellScreenCryptoCurrencyCode()) :
CurrencyUtil.getTradeCurrency(preferences.getBuyScreenCryptoCurrencyCode());
tradeCurrency = tradeCurrencyOptional.isEmpty() ? OfferViewUtil.getAnyOfMainCryptoCurrencies() : tradeCurrencyOptional.get();
}
Optional<TradeCurrency> tradeCurrencyOptional = (this.direction == OfferDirection.SELL) ?
CurrencyUtil.getTradeCurrency(preferences.getSellScreenCryptoCurrencyCode()) :
CurrencyUtil.getTradeCurrency(preferences.getBuyScreenCryptoCurrencyCode());
tradeCurrency = tradeCurrencyOptional.isEmpty() ? OfferViewUtil.getAnyOfMainCryptoCurrencies() : tradeCurrencyOptional.get();
loadCreateViewClass(cryptoOfferBookView, viewClass, childViewClass, cryptoOfferBookTab, (PaymentMethod) data);
}
tabPane.getSelectionModel().select(cryptoOfferBookTab);
@ -316,7 +314,7 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
private void showTakeOffer(Offer offer) {
this.offer = offer;
Class<? extends OfferBookView<?, ?>> offerBookViewClass = getOfferBookViewClassFor(offer.getCurrencyCode());
Class<? extends OfferBookView<?, ?>> offerBookViewClass = getOfferBookViewClassFor(offer.getCounterCurrencyCode());
navigation.navigateTo(MainView.class, this.getClass(), offerBookViewClass, TakeOfferView.class);
}

View file

@ -100,7 +100,7 @@ public class OfferViewUtil {
}
public static boolean isShownAsSellOffer(Offer offer) {
return isShownAsSellOffer(offer.getCurrencyCode(), offer.getDirection());
return isShownAsSellOffer(offer.getCounterCurrencyCode(), offer.getDirection());
}
public static boolean isShownAsSellOffer(TradeCurrency tradeCurrency, OfferDirection direction) {

View file

@ -106,9 +106,9 @@ public class CryptoOfferBookViewModel extends OfferBookViewModel {
return offerBookListItem -> {
Offer offer = offerBookListItem.getOffer();
boolean directionResult = offer.getDirection() != direction; // offer to buy xmr appears as offer to sell in peer's offer book and vice versa
boolean currencyResult = CurrencyUtil.isCryptoCurrency(offer.getCurrencyCode()) &&
boolean currencyResult = CurrencyUtil.isCryptoCurrency(offer.getCounterCurrencyCode()) &&
(showAllTradeCurrenciesProperty.get() ||
offer.getCurrencyCode().equals(selectedTradeCurrency.getCode()));
offer.getCounterCurrencyCode().equals(selectedTradeCurrency.getCode()));
boolean paymentMethodResult = showAllPaymentMethods ||
offer.getPaymentMethod().equals(selectedPaymentMethod);
boolean notMyOfferOrShowMyOffersActivated = !isMyOffer(offerBookListItem.getOffer()) || preferences.isShowOwnOffersInOfferBook();

View file

@ -116,7 +116,7 @@ public class FiatOfferBookViewModel extends OfferBookViewModel {
Offer offer = offerBookListItem.getOffer();
boolean directionResult = offer.getDirection() != direction;
boolean currencyResult = (showAllTradeCurrenciesProperty.get() && offer.isFiatOffer()) ||
offer.getCurrencyCode().equals(selectedTradeCurrency.getCode());
offer.getCounterCurrencyCode().equals(selectedTradeCurrency.getCode());
boolean paymentMethodResult = showAllPaymentMethods ||
offer.getPaymentMethod().equals(selectedPaymentMethod);
boolean notMyOfferOrShowMyOffersActivated = !isMyOffer(offerBookListItem.getOffer()) || preferences.isShowOwnOffersInOfferBook();

View file

@ -234,7 +234,7 @@ public class OfferBook {
final String[] ccyCode = new String[1];
final int[] offerCount = new int[1];
offerBookListItems.forEach(o -> {
ccyCode[0] = o.getOffer().getCurrencyCode();
ccyCode[0] = o.getOffer().getCounterCurrencyCode();
if (o.getOffer().getDirection() == BUY) {
offerCount[0] = buyOfferCountMap.getOrDefault(ccyCode[0], 0) + 1;
buyOfferCountMap.put(ccyCode[0], offerCount[0]);

View file

@ -67,9 +67,9 @@ public class OfferBookListItem {
public WitnessAgeData getWitnessAgeData(AccountAgeWitnessService accountAgeWitnessService,
SignedWitnessService signedWitnessService) {
if (witnessAgeData == null) {
if (CurrencyUtil.isCryptoCurrency(offer.getCurrencyCode())) {
if (CurrencyUtil.isCryptoCurrency(offer.getCounterCurrencyCode())) {
witnessAgeData = new WitnessAgeData(WitnessAgeData.TYPE_CRYPTOS);
} else if (PaymentMethod.hasChargebackRisk(offer.getPaymentMethod(), offer.getCurrencyCode())) {
} else if (PaymentMethod.hasChargebackRisk(offer.getPaymentMethod(), offer.getCounterCurrencyCode())) {
// Fiat and signed witness required
Optional<AccountAgeWitness> optionalWitness = accountAgeWitnessService.findWitness(offer);
AccountAgeWitnessService.SignState signState = optionalWitness

View file

@ -263,7 +263,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
tableView.setPlaceholder(placeholder);
marketColumn.setComparator(Comparator.comparing(
o -> CurrencyUtil.getCurrencyPair(o.getOffer().getCurrencyCode()),
o -> CurrencyUtil.getCurrencyPair(o.getOffer().getCounterCurrencyCode()),
Comparator.nullsFirst(Comparator.naturalOrder())
));
@ -677,7 +677,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
Optional<PaymentAccount> account = model.getMostMaturePaymentAccountForOffer(offer);
if (account.isPresent()) {
long tradeLimit = model.accountAgeWitnessService.getMyTradeLimit(account.get(),
offer.getCurrencyCode(), offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit());
offer.getCounterCurrencyCode(), offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit());
new Popup()
.warning(Res.get("popup.warning.tradeLimitDueAccountAgeRestriction.buyer",
HavenoUtils.formatXmr(tradeLimit, true),
@ -824,7 +824,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
public void updateItem(final OfferBookListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty)
setText(CurrencyUtil.getCurrencyPair(item.getOffer().getCurrencyCode()));
setText(CurrencyUtil.getCurrencyPair(item.getOffer().getCounterCurrencyCode()));
else
setText("");
}
@ -947,7 +947,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
} else {
setText("");
ColoredDecimalPlacesWithZerosText volumeBox = new ColoredDecimalPlacesWithZerosText(model.getVolumeAmount(item), model.getNumberOfDecimalsForVolume(item));
if (model.showAllTradeCurrenciesProperty.get()) volumeBox.getChildren().add(new Label(" " + item.getOffer().getCurrencyCode()));
if (model.showAllTradeCurrenciesProperty.get()) volumeBox.getChildren().add(new Label(" " + item.getOffer().getCounterCurrencyCode()));
setGraphic(volumeBox);
}
} else {
@ -987,11 +987,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
if (model.isOfferBanned(offer)) {
setGraphic(new AutoTooltipLabel(model.getPaymentMethod(item)));
} else {
if (offer.isXmrAutoConf()) {
field = new HyperlinkWithIcon(model.getPaymentMethod(item), AwesomeIcon.ROCKET);
} else {
field = new HyperlinkWithIcon(model.getPaymentMethod(item));
}
field = new HyperlinkWithIcon(model.getPaymentMethod(item));
field.setOnAction(event -> {
offerDetailsWindow.show(offer);
});

View file

@ -448,7 +448,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
Volume offerVolume = offer.getVolume();
Volume minOfferVolume = offer.getMinVolume();
if (offerVolume != null && minOfferVolume != null) {
String postFix = appendCurrencyCode ? " " + offer.getCurrencyCode() : "";
String postFix = appendCurrencyCode ? " " + offer.getCounterCurrencyCode() : "";
decimalAligned = decimalAligned && !showAllTradeCurrenciesProperty.get();
return VolumeUtil.formatVolume(offer, decimalAligned, maxPlacesForVolume.get()) + postFix;
} else {
@ -457,7 +457,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
}
int getNumberOfDecimalsForVolume(OfferBookListItem item) {
return CurrencyUtil.isVolumeRoundedToNearestUnit(item.getOffer().getCurrencyCode()) ? GUIUtil.NUM_DECIMALS_UNIT : GUIUtil.NUM_DECIMALS_PRECISE;
return CurrencyUtil.isVolumeRoundedToNearestUnit(item.getOffer().getCounterCurrencyCode()) ? GUIUtil.NUM_DECIMALS_UNIT : GUIUtil.NUM_DECIMALS_PRECISE;
}
String getPaymentMethod(OfferBookListItem item) {
@ -484,14 +484,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
if (item != null) {
Offer offer = item.getOffer();
result = Res.getWithCol("shared.paymentMethod") + " " + Res.get(offer.getPaymentMethod().getId());
result += "\n" + Res.getWithCol("shared.currency") + " " + CurrencyUtil.getNameAndCode(offer.getCurrencyCode());
if (offer.isXmr()) {
String isAutoConf = offer.isXmrAutoConf() ?
Res.get("shared.yes") :
Res.get("shared.no");
result += "\n" + Res.getWithCol("offerbook.xmrAutoConf") + " " + isAutoConf;
}
result += "\n" + Res.getWithCol("shared.currency") + " " + CurrencyUtil.getNameAndCode(offer.getCounterCurrencyCode());
String countryCode = offer.getCountryCode();
if (isF2F(offer)) {
@ -538,7 +531,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
}
String getDirectionLabelTooltip(Offer offer) {
return getDirectionWithCodeDetailed(offer.getMirroredDirection(), offer.getCurrencyCode());
return getDirectionWithCodeDetailed(offer.getMirroredDirection(), offer.getCounterCurrencyCode());
}
Optional<PaymentAccount> getMostMaturePaymentAccountForOffer(Offer offer) {
@ -623,9 +616,9 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
// filter currencies
nextPredicate = nextPredicate.or(offerBookListItem -> {
return offerBookListItem.getOffer().getCurrencyCode().toLowerCase().contains(filterText.toLowerCase()) ||
return offerBookListItem.getOffer().getCounterCurrencyCode().toLowerCase().contains(filterText.toLowerCase()) ||
offerBookListItem.getOffer().getBaseCurrencyCode().toLowerCase().contains(filterText.toLowerCase()) ||
CurrencyUtil.getNameAndCode(offerBookListItem.getOffer().getCurrencyCode()).toLowerCase().contains(filterText.toLowerCase()) ||
CurrencyUtil.getNameAndCode(offerBookListItem.getOffer().getCounterCurrencyCode()).toLowerCase().contains(filterText.toLowerCase()) ||
CurrencyUtil.getNameAndCode(offerBookListItem.getOffer().getBaseCurrencyCode()).toLowerCase().contains(filterText.toLowerCase());
});
@ -691,10 +684,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
}
private static String getDirectionWithCodeDetailed(OfferDirection direction, String currencyCode) {
if (CurrencyUtil.isTraditionalCurrency(currencyCode))
return (direction == OfferDirection.BUY) ? Res.get("shared.buyingXMRWith", currencyCode) : Res.get("shared.sellingXMRFor", currencyCode);
else
return (direction == OfferDirection.SELL) ? Res.get("shared.buyingCurrency", currencyCode) : Res.get("shared.sellingCurrency", currencyCode);
return (direction == OfferDirection.BUY) ? Res.get("shared.buyingXMRWith", currencyCode) : Res.get("shared.sellingXMRFor", currencyCode);
}
public String formatDepositString(BigInteger deposit, long amount) {

View file

@ -112,8 +112,8 @@ public class OtherOfferBookViewModel extends OfferBookViewModel {
return offerBookListItem -> {
Offer offer = offerBookListItem.getOffer();
boolean directionResult = offer.getDirection() != direction;
boolean currencyResult = CurrencyUtil.isTraditionalCurrency(offer.getCurrencyCode()) && !CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
(showAllTradeCurrenciesProperty.get() || offer.getCurrencyCode().equals(selectedTradeCurrency.getCode()));
boolean currencyResult = CurrencyUtil.isTraditionalCurrency(offer.getCounterCurrencyCode()) && !CurrencyUtil.isFiatCurrency(offer.getCounterCurrencyCode()) &&
(showAllTradeCurrenciesProperty.get() || offer.getCounterCurrencyCode().equals(selectedTradeCurrency.getCode()));
boolean paymentMethodResult = showAllPaymentMethods ||
offer.getPaymentMethod().equals(selectedPaymentMethod);
boolean notMyOfferOrShowMyOffersActivated = !isMyOffer(offerBookListItem.getOffer()) || preferences.isShowOwnOffersInOfferBook();

View file

@ -146,7 +146,7 @@ class TakeOfferDataModel extends OfferDataModel {
// feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx());
if (isTabSelected)
priceFeedService.setCurrencyCode(offer.getCurrencyCode());
priceFeedService.setCurrencyCode(offer.getCounterCurrencyCode());
if (canTakeOffer()) {
tradeManager.checkOfferAvailability(offer,
@ -204,7 +204,7 @@ class TakeOfferDataModel extends OfferDataModel {
offer.resetState();
priceFeedService.setCurrencyCode(offer.getCurrencyCode());
priceFeedService.setCurrencyCode(offer.getCounterCurrencyCode());
}
// We don't want that the fee gets updated anymore after we show the funding screen.
@ -215,7 +215,7 @@ class TakeOfferDataModel extends OfferDataModel {
void onTabSelected(boolean isSelected) {
this.isTabSelected = isSelected;
if (isTabSelected)
priceFeedService.setCurrencyCode(offer.getCurrencyCode());
priceFeedService.setCurrencyCode(offer.getCounterCurrencyCode());
}
public void onClose(boolean removeOffer) {
@ -259,7 +259,7 @@ class TakeOfferDataModel extends OfferDataModel {
fundsNeededForTrade = fundsNeededForTrade.add(amount.get());
String errorMsg = null;
if (filterManager.isCurrencyBanned(offer.getCurrencyCode())) {
if (filterManager.isCurrencyBanned(offer.getCounterCurrencyCode())) {
errorMsg = Res.get("offerbook.warning.currencyBanned");
} else if (filterManager.isPaymentMethodBanned(offer.getPaymentMethod())) {
errorMsg = Res.get("offerbook.warning.paymentMethodBanned");
@ -501,11 +501,11 @@ class TakeOfferDataModel extends OfferDataModel {
}
public String getCurrencyCode() {
return offer.getCurrencyCode();
return offer.getCounterCurrencyCode();
}
public String getCurrencyNameAndCode() {
return CurrencyUtil.getNameByCode(offer.getCurrencyCode());
return CurrencyUtil.getNameByCode(offer.getCounterCurrencyCode());
}
@NotNull

View file

@ -1266,12 +1266,6 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
@NotNull
private String getTakeOfferLabel(Offer offer, boolean isBuyOffer) {
return offer.isTraditionalOffer() ?
Res.get("takeOffer.takeOfferButton", isBuyOffer ? Res.get("shared.sell") : Res.get("shared.buy")) :
Res.get("takeOffer.takeOfferButtonCrypto",
isBuyOffer ? Res.get("shared.buy") : Res.get("shared.sell"),
offer.getCurrencyCode());
return Res.get(isBuyOffer ? "takeOffer.takeOfferButton.sell" : "takeOffer.takeOfferButton.buy", offer.getCounterCurrencyCode());
}
}

View file

@ -164,10 +164,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
addBindings();
addListeners();
String buyVolumeDescriptionKey = offer.isTraditionalOffer() ? "createOffer.amountPriceBox.buy.volumeDescription" :
"createOffer.amountPriceBox.buy.volumeDescriptionCrypto";
String sellVolumeDescriptionKey = offer.isTraditionalOffer() ? "createOffer.amountPriceBox.sell.volumeDescription" :
"createOffer.amountPriceBox.sell.volumeDescriptionCrypto";
String buyVolumeDescriptionKey = "createOffer.amountPriceBox.buy.volumeDescription";
String sellVolumeDescriptionKey = "createOffer.amountPriceBox.sell.volumeDescription";
if (dataModel.getDirection() == OfferDirection.SELL) {
volumeDescriptionLabel.set(Res.get(buyVolumeDescriptionKey, dataModel.getCurrencyCode()));
@ -695,7 +693,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
public boolean isSellingToAnUnsignedAccount(Offer offer) {
if (offer.getDirection() == OfferDirection.BUY &&
PaymentMethod.hasChargebackRisk(offer.getPaymentMethod(), offer.getCurrencyCode())) {
PaymentMethod.hasChargebackRisk(offer.getPaymentMethod(), offer.getCounterCurrencyCode())) {
// considered risky when either UNSIGNED, PEER_INITIAL, or BANNED (see #5343)
return accountAgeWitnessService.getSignState(offer) == AccountAgeWitnessService.SignState.UNSIGNED ||
accountAgeWitnessService.getSignState(offer) == AccountAgeWitnessService.SignState.PEER_INITIAL ||
@ -792,14 +790,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
}
String getPercentagePriceDescription() {
if (dataModel.isBuyOffer()) {
return dataModel.isCryptoCurrency() ?
Res.get("shared.aboveInPercent") :
Res.get("shared.belowInPercent");
} else {
return dataModel.isCryptoCurrency() ?
Res.get("shared.belowInPercent") :
Res.get("shared.aboveInPercent");
}
return dataModel.isBuyOffer() ? Res.get("shared.belowInPercent") : Res.get("shared.aboveInPercent");
}
}

View file

@ -141,7 +141,7 @@ public class ContractWindow extends Overlay<ContractWindow> {
addSeparator(gridPane, ++rowIndex);
addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("contractWindow.dates"),
DisplayUtils.formatDateTime(offer.getDate()) + " / " + DisplayUtils.formatDateTime(dispute.getTradeDate()));
String currencyCode = offer.getCurrencyCode();
String currencyCode = offer.getCounterCurrencyCode();
addSeparator(gridPane, ++rowIndex);
addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.offerType"),
DisplayUtils.getDirectionBothSides(offer.getDirection(), offer.isPrivateOffer()));

View file

@ -202,17 +202,12 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
if (isF2F)
rows++;
boolean showXmrAutoConf = offer.isXmr() && offer.getDirection() == OfferDirection.SELL;
if (showXmrAutoConf) {
rows++;
}
addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.Offer"));
String counterCurrencyDirectionInfo = "";
String xmrDirectionInfo = "";
OfferDirection direction = offer.getDirection();
String currencyCode = offer.getCurrencyCode();
String currencyCode = offer.getCounterCurrencyCode();
String offerTypeLabel = Res.get("shared.offerType");
String toReceive = " " + Res.get("shared.toReceive");
String toSpend = " " + Res.get("shared.toSpend");
@ -291,14 +286,6 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.paymentMethod"), method);
}
if (showXmrAutoConf) {
addSeparator(gridPane, ++rowIndex);
String isAutoConf = offer.isXmrAutoConf() ?
Res.get("shared.yes") :
Res.get("shared.no");
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerbook.xmrAutoConf"), isAutoConf);
}
if (showAcceptedBanks) {
if (paymentMethod.equals(PaymentMethod.SAME_BANK)) {
addSeparator(gridPane, ++rowIndex);
@ -474,11 +461,11 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
boolean isBuyOffer = offer.isBuyOffer();
boolean isBuyerRole = isPlaceOffer == isBuyOffer;
String placeOfferButtonText = isBuyerRole ?
Res.get("offerDetailsWindow.confirm.maker", Res.get("shared.buy")) :
Res.get("offerDetailsWindow.confirm.maker", Res.get("shared.sell"));
Res.get("offerDetailsWindow.confirm.maker.buy", offer.getCounterCurrencyCode()) :
Res.get("offerDetailsWindow.confirm.maker.sell", offer.getCounterCurrencyCode());
String takeOfferButtonText = isBuyerRole ?
Res.get("offerDetailsWindow.confirm.taker", Res.get("shared.buy")) :
Res.get("offerDetailsWindow.confirm.taker", Res.get("shared.sell"));
Res.get("offerDetailsWindow.confirm.taker.buy", offer.getCounterCurrencyCode()) :
Res.get("offerDetailsWindow.confirm.taker.sell", offer.getCounterCurrencyCode());
Tuple4<Button, BusyAnimation, Label, HBox> placeOfferTuple = addButtonBusyAnimationLabelAfterGroup(gridPane,
++rowIndex, 1,

View file

@ -144,12 +144,12 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
String offerType = Res.get("shared.offerType");
if (tradeManager.isBuyer(offer)) {
addConfirmationLabelTextField(gridPane, rowIndex, offerType,
DisplayUtils.getDirectionForBuyer(myOffer, offer.getCurrencyCode()), Layout.TWICE_FIRST_ROW_DISTANCE);
DisplayUtils.getDirectionForBuyer(myOffer, offer.getCounterCurrencyCode()), Layout.TWICE_FIRST_ROW_DISTANCE);
counterCurrencyDirectionInfo = toSpend;
xmrDirectionInfo = toReceive;
} else {
addConfirmationLabelTextField(gridPane, rowIndex, offerType,
DisplayUtils.getDirectionForSeller(myOffer, offer.getCurrencyCode()), Layout.TWICE_FIRST_ROW_DISTANCE);
DisplayUtils.getDirectionForSeller(myOffer, offer.getCounterCurrencyCode()), Layout.TWICE_FIRST_ROW_DISTANCE);
counterCurrencyDirectionInfo = toReceive;
xmrDirectionInfo = toSpend;
}
@ -159,7 +159,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
HavenoUtils.formatXmr(trade.getAmount(), true));
addSeparator(gridPane, ++rowIndex);
addConfirmationLabelTextField(gridPane, ++rowIndex,
VolumeUtil.formatVolumeLabel(offer.getCurrencyCode()) + counterCurrencyDirectionInfo,
VolumeUtil.formatVolumeLabel(offer.getCounterCurrencyCode()) + counterCurrencyDirectionInfo,
VolumeUtil.formatVolumeWithCode(trade.getVolume()));
addSeparator(gridPane, ++rowIndex);
addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.tradePrice"),

View file

@ -108,12 +108,12 @@ class CloneOfferDataModel extends MutableOfferDataModel {
Offer offer = openOffer.getOffer();
direction = offer.getDirection();
CurrencyUtil.getTradeCurrency(offer.getCurrencyCode())
CurrencyUtil.getTradeCurrency(offer.getCounterCurrencyCode())
.ifPresent(c -> this.tradeCurrency = c);
tradeCurrencyCode.set(offer.getCurrencyCode());
tradeCurrencyCode.set(offer.getCounterCurrencyCode());
PaymentAccount tmpPaymentAccount = user.getPaymentAccount(openOffer.getOffer().getMakerPaymentAccountId());
Optional<TradeCurrency> optionalTradeCurrency = CurrencyUtil.getTradeCurrency(openOffer.getOffer().getCurrencyCode());
Optional<TradeCurrency> optionalTradeCurrency = CurrencyUtil.getTradeCurrency(openOffer.getOffer().getCounterCurrencyCode());
if (optionalTradeCurrency.isPresent() && tmpPaymentAccount != null) {
TradeCurrency selectedTradeCurrency = optionalTradeCurrency.get();
this.paymentAccount = PaymentAccount.fromProto(tmpPaymentAccount.toProtoMessage(), corePersistenceProtoResolver);

View file

@ -144,7 +144,7 @@ public class CloneOfferView extends MutableOfferView<CloneOfferViewModel> {
model.applyOpenOffer(openOffer);
initWithData(openOffer.getOffer().getDirection(),
CurrencyUtil.getTradeCurrency(openOffer.getOffer().getCurrencyCode()).get(),
CurrencyUtil.getTradeCurrency(openOffer.getOffer().getCounterCurrencyCode()).get(),
false,
null);

View file

@ -104,7 +104,7 @@ public class ClosedTradesListItem implements FilterableListItem {
OfferDirection direction = closedTradableManager.wasMyOffer(offer) || tradable instanceof ArbitratorTrade
? offer.getDirection()
: offer.getMirroredDirection();
String currencyCode = tradable.getOffer().getCurrencyCode();
String currencyCode = tradable.getOffer().getCounterCurrencyCode();
return DisplayUtils.getDirectionWithCode(direction, currencyCode, offer.isPrivateOffer());
}
@ -117,7 +117,7 @@ public class ClosedTradesListItem implements FilterableListItem {
}
public String getMarketLabel() {
return CurrencyUtil.getCurrencyPair(tradable.getOffer().getCurrencyCode());
return CurrencyUtil.getCurrencyPair(tradable.getOffer().getCounterCurrencyCode());
}
public String getState() {

View file

@ -108,13 +108,13 @@ class EditOfferDataModel extends MutableOfferDataModel {
Offer offer = openOffer.getOffer();
direction = offer.getDirection();
CurrencyUtil.getTradeCurrency(offer.getCurrencyCode())
CurrencyUtil.getTradeCurrency(offer.getCounterCurrencyCode())
.ifPresent(c -> this.tradeCurrency = c);
tradeCurrencyCode.set(offer.getCurrencyCode());
tradeCurrencyCode.set(offer.getCounterCurrencyCode());
this.initialState = openOffer.getState();
PaymentAccount tmpPaymentAccount = user.getPaymentAccount(openOffer.getOffer().getMakerPaymentAccountId());
Optional<TradeCurrency> optionalTradeCurrency = CurrencyUtil.getTradeCurrency(openOffer.getOffer().getCurrencyCode());
Optional<TradeCurrency> optionalTradeCurrency = CurrencyUtil.getTradeCurrency(openOffer.getOffer().getCounterCurrencyCode());
if (optionalTradeCurrency.isPresent() && tmpPaymentAccount != null) {
TradeCurrency selectedTradeCurrency = optionalTradeCurrency.get();
this.paymentAccount = PaymentAccount.fromProto(tmpPaymentAccount.toProtoMessage(), corePersistenceProtoResolver);

View file

@ -141,7 +141,7 @@ public class EditOfferView extends MutableOfferView<EditOfferViewModel> {
model.applyOpenOffer(openOffer);
initWithData(openOffer.getOffer().getDirection(),
CurrencyUtil.getTradeCurrency(openOffer.getOffer().getCurrencyCode()).get(),
CurrencyUtil.getTradeCurrency(openOffer.getOffer().getCounterCurrencyCode()).get(),
false,
null);

View file

@ -68,14 +68,14 @@ class FailedTradesViewModel extends ActivatableWithDataModel<FailedTradesDataMod
}
String getDirectionLabel(FailedTradesListItem item) {
return (item != null) ? DisplayUtils.getDirectionWithCode(dataModel.getDirection(item.getTrade().getOffer()), item.getTrade().getOffer().getCurrencyCode(), item.getTrade().getOffer().isPrivateOffer()) : "";
return (item != null) ? DisplayUtils.getDirectionWithCode(dataModel.getDirection(item.getTrade().getOffer()), item.getTrade().getOffer().getCounterCurrencyCode(), item.getTrade().getOffer().isPrivateOffer()) : "";
}
String getMarketLabel(FailedTradesListItem item) {
if ((item == null))
return "";
return CurrencyUtil.getCurrencyPair(item.getTrade().getOffer().getCurrencyCode());
return CurrencyUtil.getCurrencyPair(item.getTrade().getOffer().getCounterCurrencyCode());
}
String getDate(FailedTradesListItem item) {

View file

@ -95,6 +95,6 @@ class OpenOffersDataModel extends ActivatableDataModel {
}
boolean isTriggered(OpenOffer openOffer) {
return TriggerPriceService.isTriggered(priceFeedService.getMarketPrice(openOffer.getOffer().getCurrencyCode()), openOffer);
return TriggerPriceService.isTriggered(priceFeedService.getMarketPrice(openOffer.getOffer().getCounterCurrencyCode()), openOffer);
}
}

View file

@ -118,21 +118,21 @@ class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel>
}
String getVolume(OpenOfferListItem item) {
return (item != null) ? VolumeUtil.formatVolume(item.getOffer(), false, 0) + " " + item.getOffer().getCurrencyCode() : "";
return (item != null) ? VolumeUtil.formatVolume(item.getOffer(), false, 0) + " " + item.getOffer().getCounterCurrencyCode() : "";
}
String getDirectionLabel(OpenOfferListItem item) {
if ((item == null))
return "";
return DisplayUtils.getDirectionWithCode(dataModel.getDirection(item.getOffer()), item.getOffer().getCurrencyCode(), item.getOffer().isPrivateOffer());
return DisplayUtils.getDirectionWithCode(dataModel.getDirection(item.getOffer()), item.getOffer().getCounterCurrencyCode(), item.getOffer().isPrivateOffer());
}
String getMarketLabel(OpenOfferListItem item) {
if ((item == null))
return "";
return CurrencyUtil.getCurrencyPair(item.getOffer().getCurrencyCode());
return CurrencyUtil.getCurrencyPair(item.getOffer().getCounterCurrencyCode());
}
String getPaymentMethod(OpenOfferListItem item) {
@ -168,7 +168,7 @@ class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel>
if (!offer.isUseMarketBasedPrice() || triggerPrice <= 0) {
return Res.get("shared.na");
} else {
return PriceUtil.formatMarketPrice(triggerPrice, offer.getCurrencyCode());
return PriceUtil.formatMarketPrice(triggerPrice, offer.getCounterCurrencyCode());
}
}
}

View file

@ -63,7 +63,7 @@ public class PendingTradesListItem implements FilterableListItem {
}
public String getMarketDescription() {
return getCurrencyPair(trade.getOffer().getCurrencyCode());
return getCurrencyPair(trade.getOffer().getCounterCurrencyCode());
}
@Override

View file

@ -743,7 +743,7 @@ public abstract class TradeStepView extends AnchorPane {
}
protected String getCurrencyCode(Trade trade) {
return checkNotNull(trade.getOffer()).getCurrencyCode();
return checkNotNull(trade.getOffer()).getCounterCurrencyCode();
}
protected boolean isXmrTrade() {

View file

@ -3,7 +3,6 @@ package haveno.desktop.util;
import haveno.common.crypto.PubKeyRing;
import haveno.core.account.witness.AccountAgeWitness;
import haveno.core.account.witness.AccountAgeWitnessService;
import haveno.core.locale.CurrencyUtil;
import haveno.core.locale.GlobalSettings;
import haveno.core.locale.Res;
import haveno.core.monetary.Price;
@ -118,10 +117,7 @@ public class DisplayUtils {
///////////////////////////////////////////////////////////////////////////////////////////
public static String getDirectionWithCode(OfferDirection direction, String currencyCode, boolean isPrivate) {
if (CurrencyUtil.isTraditionalCurrency(currencyCode))
return (direction == OfferDirection.BUY) ? Res.get("shared.buyCurrency" + (isPrivate ? LOCKED : ""), Res.getBaseCurrencyCode()) : Res.get("shared.sellCurrency" + (isPrivate ? LOCKED : ""), Res.getBaseCurrencyCode());
else
return (direction == OfferDirection.SELL) ? Res.get("shared.buyCurrency" + (isPrivate ? LOCKED : ""), currencyCode) : Res.get("shared.sellCurrency" + (isPrivate ? LOCKED : ""), currencyCode);
return (direction == OfferDirection.BUY) ? Res.get("shared.buyCurrency" + (isPrivate ? LOCKED : ""), Res.getBaseCurrencyCode()) : Res.get("shared.sellCurrency" + (isPrivate ? LOCKED : ""), Res.getBaseCurrencyCode());
}
public static String getDirectionBothSides(OfferDirection direction, boolean isPrivate) {
@ -132,56 +128,31 @@ public class DisplayUtils {
}
public static String getDirectionForBuyer(boolean isMyOffer, String currencyCode) {
if (CurrencyUtil.isTraditionalCurrency(currencyCode)) {
String code = Res.getBaseCurrencyCode();
return isMyOffer ?
Res.get("formatter.youAreAsMaker", Res.get("shared.buyer"), code, Res.get("shared.seller"), code) :
Res.get("formatter.youAreAsTaker", Res.get("shared.buyer"), code, Res.get("shared.seller"), code);
} else {
return isMyOffer ?
Res.get("formatter.youAreAsMaker", Res.get("shared.seller"), currencyCode, Res.get("shared.buyer"), currencyCode) :
Res.get("formatter.youAreAsTaker", Res.get("shared.seller"), currencyCode, Res.get("shared.buyer"), currencyCode);
}
String code = Res.getBaseCurrencyCode();
return isMyOffer ?
Res.get("formatter.youAreAsMaker", Res.get("shared.buyer"), code, Res.get("shared.seller"), code) :
Res.get("formatter.youAreAsTaker", Res.get("shared.buyer"), code, Res.get("shared.seller"), code);
}
public static String getDirectionForSeller(boolean isMyOffer, String currencyCode) {
if (CurrencyUtil.isTraditionalCurrency(currencyCode)) {
String code = Res.getBaseCurrencyCode();
return isMyOffer ?
Res.get("formatter.youAreAsMaker", Res.get("shared.seller"), code, Res.get("shared.buyer"), code) :
Res.get("formatter.youAreAsTaker", Res.get("shared.seller"), code, Res.get("shared.buyer"), code);
} else {
return isMyOffer ?
Res.get("formatter.youAreAsMaker", Res.get("shared.buyer"), currencyCode, Res.get("shared.seller"), currencyCode) :
Res.get("formatter.youAreAsTaker", Res.get("shared.buyer"), currencyCode, Res.get("shared.seller"), currencyCode);
}
String code = Res.getBaseCurrencyCode();
return isMyOffer ?
Res.get("formatter.youAreAsMaker", Res.get("shared.seller"), code, Res.get("shared.buyer"), code) :
Res.get("formatter.youAreAsTaker", Res.get("shared.seller"), code, Res.get("shared.buyer"), code);
}
public static String getDirectionForTakeOffer(OfferDirection direction, String currencyCode) {
String baseCurrencyCode = Res.getBaseCurrencyCode();
if (CurrencyUtil.isTraditionalCurrency(currencyCode)) {
return direction == OfferDirection.BUY ?
Res.get("formatter.youAre", Res.get("shared.selling"), baseCurrencyCode, Res.get("shared.buying"), currencyCode) :
Res.get("formatter.youAre", Res.get("shared.buying"), baseCurrencyCode, Res.get("shared.selling"), currencyCode);
} else {
return direction == OfferDirection.SELL ?
Res.get("formatter.youAre", Res.get("shared.selling"), currencyCode, Res.get("shared.buying"), baseCurrencyCode) :
Res.get("formatter.youAre", Res.get("shared.buying"), currencyCode, Res.get("shared.selling"), baseCurrencyCode);
}
return direction == OfferDirection.BUY ?
Res.get("formatter.youAre", Res.get("shared.selling"), baseCurrencyCode, Res.get("shared.buying"), currencyCode) :
Res.get("formatter.youAre", Res.get("shared.buying"), baseCurrencyCode, Res.get("shared.selling"), currencyCode);
}
public static String getOfferDirectionForCreateOffer(OfferDirection direction, String currencyCode, boolean isPrivate) {
String baseCurrencyCode = Res.getBaseCurrencyCode();
if (CurrencyUtil.isTraditionalCurrency(currencyCode)) {
return direction == OfferDirection.BUY ?
Res.get("formatter.youAreCreatingAnOffer.traditional" + (isPrivate ? LOCKED : ""), Res.get("shared.buy"), baseCurrencyCode) :
Res.get("formatter.youAreCreatingAnOffer.traditional" + (isPrivate ? LOCKED : ""), Res.get("shared.sell"), baseCurrencyCode);
} else {
return direction == OfferDirection.SELL ?
Res.get("formatter.youAreCreatingAnOffer.crypto" + (isPrivate ? LOCKED : ""), Res.get("shared.buy"), currencyCode, Res.get("shared.selling"), baseCurrencyCode) :
Res.get("formatter.youAreCreatingAnOffer.crypto" + (isPrivate ? LOCKED : ""), Res.get("shared.sell"), currencyCode, Res.get("shared.buying"), baseCurrencyCode);
}
return direction == OfferDirection.BUY ?
Res.get("formatter.youAreCreatingAnOffer.traditional" + (isPrivate ? LOCKED : ""), Res.get("shared.buy"), baseCurrencyCode) :
Res.get("formatter.youAreCreatingAnOffer.traditional" + (isPrivate ? LOCKED : ""), Res.get("shared.sell"), baseCurrencyCode);
}
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -217,7 +217,7 @@ public class CreateOfferViewModelTest {
model.amount.set("0.01");
model.onFocusOutPriceAsPercentageTextField(true, false); //leave focus without changing
assertEquals("0.00", model.marketPriceMargin.get());
assertEquals("0.00000078", model.volume.get());
assertEquals("126.84045000", model.volume.get());
assertEquals("12684.04500000", model.price.get());
}
}