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

View file

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

View file

@ -78,7 +78,7 @@ class CorePriceService {
} else if (!marketPrice.isExternallyProvidedPrice()) { } 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 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 { public List<MarketPriceInfo> getMarketPrices() throws ExecutionException, InterruptedException, TimeoutException {
return priceFeedService.requestAllPrices().values().stream() return priceFeedService.requestAllPrices().values().stream()
.map(marketPrice -> { .map(marketPrice -> {
double mappedPrice = mapPriceFeedServicePrice(marketPrice.getPrice(), marketPrice.getCurrencyCode()); return new MarketPriceInfo(marketPrice.getCurrencyCode(), marketPrice.getPrice());
return new MarketPriceInfo(marketPrice.getCurrencyCode(), mappedPrice);
}) })
.collect(Collectors.toList()); .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. // 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())); 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 // 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, // 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 // the buy column is actually the sell column and vice versa. To maintain the expected
// ordering, we have to reverse the price comparator. // ordering, we have to reverse the price comparator.
boolean isCrypto = CurrencyUtil.isCryptoCurrency(currencyCode); //boolean isCrypto = CurrencyUtil.isCryptoCurrency(currencyCode);
if (isCrypto) offerPriceComparator = offerPriceComparator.reversed(); //if (isCrypto) offerPriceComparator = offerPriceComparator.reversed();
// Offer amounts are used for the secondary sort. They are sorted from high to low. // Offer amounts are used for the secondary sort. They are sorted from high to low.
Comparator<Offer> offerAmountComparator = Comparator.comparing(Offer::getAmount).reversed(); 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); double amount = (double) offer.getAmount().longValueExact() / LongMath.pow(10, HavenoUtils.XMR_SMALLEST_UNIT_EXPONENT);
accumulatedAmount += amount; accumulatedAmount += amount;
double priceAsDouble = (double) price.getValue() / LongMath.pow(10, price.smallestUnitExponent()); 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; accumulatedAmount = 0;
LinkedHashMap<Double,Double> sellTM = new LinkedHashMap<Double,Double>(); LinkedHashMap<Double,Double> sellTM = new LinkedHashMap<Double,Double>();
for(Offer offer: sellOffers){ for(Offer offer: sellOffers){
@ -143,7 +143,7 @@ class CorePriceService {
double amount = (double) offer.getAmount().longValueExact() / LongMath.pow(10, HavenoUtils.XMR_SMALLEST_UNIT_EXPONENT); double amount = (double) offer.getAmount().longValueExact() / LongMath.pow(10, HavenoUtils.XMR_SMALLEST_UNIT_EXPONENT);
accumulatedAmount += amount; accumulatedAmount += amount;
double priceAsDouble = (double) price.getValue() / LongMath.pow(10, price.smallestUnitExponent()); 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); 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); BigInteger amount = amountAsLong == 0 ? offer.getAmount() : BigInteger.valueOf(amountAsLong);
// adjust amount for fixed-price offer (based on TakeOfferViewModel) // adjust amount for fixed-price offer (based on TakeOfferViewModel)
String currencyCode = offer.getCurrencyCode(); String currencyCode = offer.getCounterCurrencyCode();
OfferDirection direction = offer.getOfferPayload().getDirection(); OfferDirection direction = offer.getOfferPayload().getDirection();
BigInteger maxAmount = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction, offer.hasBuyerAsTakerWithoutDeposit()); BigInteger maxAmount = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction, offer.hasBuyerAsTakerWithoutDeposit());
if (offer.getPrice() != null) { if (offer.getPrice() != null) {

View file

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

View file

@ -57,7 +57,7 @@ public class TradeInfo implements Payload {
private static final Function<Trade, String> toPreciseTradePrice = (trade) -> private static final Function<Trade, String> toPreciseTradePrice = (trade) ->
reformatMarketPrice(requireNonNull(trade.getPrice()).toPlainString(), 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). // Haveno v1 trade protocol fields (some are in common with the BSQ Swap protocol).
private final OfferInfo offer; 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. * 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 * 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. * 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) { public static String getCurrencyPair(String currencyCode) {
if (isTraditionalCurrency(currencyCode)) return Res.getBaseCurrencyCode() + "/" + currencyCode;
return Res.getBaseCurrencyCode() + "/" + currencyCode;
else
return currencyCode + "/" + Res.getBaseCurrencyCode();
} }
public static String getCounterCurrency(String currencyCode) { public static String getCounterCurrency(String currencyCode) {
if (isTraditionalCurrency(currencyCode)) return currencyCode;
return currencyCode;
else
return Res.getBaseCurrencyCode();
} }
public static String getPriceWithCurrencyCode(String currencyCode) { public static String getPriceWithCurrencyCode(String currencyCode) {
@ -532,10 +526,7 @@ public class CurrencyUtil {
} }
public static String getPriceWithCurrencyCode(String currencyCode, String translationKey) { public static String getPriceWithCurrencyCode(String currencyCode, String translationKey) {
if (isCryptoCurrency(currencyCode)) return Res.get(translationKey, currencyCode, Res.getBaseCurrencyCode());
return Res.get(translationKey, Res.getBaseCurrencyCode(), currencyCode);
else
return Res.get(translationKey, currencyCode, Res.getBaseCurrencyCode());
} }
public static String getOfferVolumeCode(String currencyCode) { public static String getOfferVolumeCode(String currencyCode) {

View file

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

View file

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

View file

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

View file

@ -22,7 +22,6 @@ import com.google.inject.Singleton;
import haveno.common.app.Version; import haveno.common.app.Version;
import haveno.common.crypto.PubKeyRingProvider; import haveno.common.crypto.PubKeyRingProvider;
import haveno.common.util.Utilities; import haveno.common.util.Utilities;
import haveno.core.locale.CurrencyUtil;
import haveno.core.locale.Res; import haveno.core.locale.Res;
import haveno.core.monetary.Price; import haveno.core.monetary.Price;
import haveno.core.payment.PaymentAccount; import haveno.core.payment.PaymentAccount;
@ -176,9 +175,8 @@ public class CreateOfferService {
double marketPriceMarginParam = useMarketBasedPriceValue ? marketPriceMargin : 0; double marketPriceMarginParam = useMarketBasedPriceValue ? marketPriceMargin : 0;
long amountAsLong = amount != null ? amount.longValueExact() : 0L; long amountAsLong = amount != null ? amount.longValueExact() : 0L;
long minAmountAsLong = minAmount != null ? minAmount.longValueExact() : 0L; long minAmountAsLong = minAmount != null ? minAmount.longValueExact() : 0L;
boolean isCryptoCurrency = CurrencyUtil.isCryptoCurrency(currencyCode); String baseCurrencyCode = Res.getBaseCurrencyCode();
String baseCurrencyCode = isCryptoCurrency ? currencyCode : Res.getBaseCurrencyCode(); String counterCurrencyCode = currencyCode;
String counterCurrencyCode = isCryptoCurrency ? Res.getBaseCurrencyCode() : currencyCode;
String countryCode = PaymentAccountUtil.getCountryCode(paymentAccount); String countryCode = PaymentAccountUtil.getCountryCode(paymentAccount);
List<String> acceptedCountryCodes = PaymentAccountUtil.getAcceptedCountryCodes(paymentAccount); List<String> acceptedCountryCodes = PaymentAccountUtil.getAcceptedCountryCodes(paymentAccount);
String bankId = PaymentAccountUtil.getBankId(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.MarketPrice;
import haveno.core.provider.price.PriceFeedService; import haveno.core.provider.price.PriceFeedService;
import haveno.core.trade.HavenoUtils; import haveno.core.trade.HavenoUtils;
import haveno.core.util.PriceUtil;
import haveno.core.util.VolumeUtil; import haveno.core.util.VolumeUtil;
import haveno.core.util.coin.CoinUtil; import haveno.core.util.coin.CoinUtil;
import haveno.network.p2p.NodeAddress; import haveno.network.p2p.NodeAddress;
@ -174,32 +175,27 @@ public class Offer implements NetworkPayload, PersistablePayload {
@Nullable @Nullable
public Price getPrice() { public Price getPrice() {
String currencyCode = getCurrencyCode(); String counterCurrencyCode = getCounterCurrencyCode();
if (!offerPayload.isUseMarketBasedPrice()) { 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"); checkNotNull(priceFeedService, "priceFeed must not be null");
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); MarketPrice marketPrice = priceFeedService.getMarketPrice(counterCurrencyCode);
if (marketPrice != null && marketPrice.isRecentExternalPriceAvailable()) { if (marketPrice != null && marketPrice.isRecentExternalPriceAvailable()) {
double factor; double factor;
double marketPriceMargin = offerPayload.getMarketPriceMarginPct(); double marketPriceMargin = offerPayload.getMarketPriceMarginPct();
if (CurrencyUtil.isCryptoCurrency(currencyCode)) { factor = getDirection() == OfferDirection.BUY ?
factor = getDirection() == OfferDirection.SELL ? 1 - marketPriceMargin : 1 + marketPriceMargin;
1 - marketPriceMargin : 1 + marketPriceMargin;
} else {
factor = getDirection() == OfferDirection.BUY ?
1 - marketPriceMargin : 1 + marketPriceMargin;
}
double marketPriceAsDouble = marketPrice.getPrice(); double marketPriceAsDouble = marketPrice.getPrice();
double targetPriceAsDouble = marketPriceAsDouble * factor; double targetPriceAsDouble = marketPriceAsDouble * factor;
try { try {
int precision = CurrencyUtil.isTraditionalCurrency(currencyCode) ? int precision = CurrencyUtil.isTraditionalCurrency(counterCurrencyCode) ?
TraditionalMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT :
CryptoMoney.SMALLEST_UNIT_EXPONENT; CryptoMoney.SMALLEST_UNIT_EXPONENT;
double scaled = MathUtils.scaleUpByPowerOf10(targetPriceAsDouble, precision); double scaled = MathUtils.scaleUpByPowerOf10(targetPriceAsDouble, precision);
final long roundedToLong = MathUtils.roundDoubleToLong(scaled); final long roundedToLong = MathUtils.roundDoubleToLong(scaled);
return Price.valueOf(currencyCode, roundedToLong); return Price.valueOf(counterCurrencyCode, roundedToLong);
} catch (Exception e) { } catch (Exception e) {
log.error("Exception at getPrice / parseToFiat: " + e + "\n" + log.error("Exception at getPrice / parseToFiat: " + e + "\n" +
"That case should never happen."); "That case should never happen.");
@ -225,7 +221,7 @@ public class Offer implements NetworkPayload, PersistablePayload {
return; return;
} }
Price tradePrice = Price.valueOf(getCurrencyCode(), price); Price tradePrice = Price.valueOf(getCounterCurrencyCode(), price);
Price offerPrice = getPrice(); Price offerPrice = getPrice();
if (offerPrice == null) if (offerPrice == null)
throw new MarketPriceNotAvailableException("Market price required for calculating trade price is not available."); 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); double deviation = Math.abs(1 - relation);
log.info("Price at take-offer time: id={}, currency={}, takersPrice={}, makersPrice={}, deviation={}", log.info("Price at take-offer time: id={}, currency={}, takersPrice={}, makersPrice={}, deviation={}",
getShortId(), getCurrencyCode(), price, offerPrice.getValue(), getShortId(), getCounterCurrencyCode(), price, offerPrice.getValue(),
deviation * 100 + "%"); deviation * 100 + "%");
if (deviation > PRICE_TOLERANCE) { if (deviation > PRICE_TOLERANCE) {
String msg = "Taker's trade price is too far away from our calculated price based on the market price.\n" + 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(); return offerPayload.getCountryCode();
} }
public String getCurrencyCode() { public String getBaseCurrencyCode() {
if (currencyCode != null) { return isInverted() ? offerPayload.getCounterCurrencyCode() : offerPayload.getBaseCurrencyCode(); // legacy offers inverted crypto
return currencyCode;
}
currencyCode = offerPayload.getBaseCurrencyCode().equals("XMR") ?
offerPayload.getCounterCurrencyCode() :
offerPayload.getBaseCurrencyCode();
return currencyCode;
} }
public String getCounterCurrencyCode() { 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() { public boolean isInverted() {
return offerPayload.getBaseCurrencyCode(); return !offerPayload.getBaseCurrencyCode().equals("XMR");
} }
public String getPaymentMethodId() { public String getPaymentMethodId() {
@ -586,21 +577,6 @@ public class Offer implements NetworkPayload, PersistablePayload {
return offerPayload.isUseReOpenAfterAutoClose(); 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() { public boolean isTraditionalOffer() {
return CurrencyUtil.isTraditionalCurrency(currencyCode); return CurrencyUtil.isTraditionalCurrency(currencyCode);
} }

View file

@ -271,7 +271,7 @@ public class OfferBookService {
public List<Offer> getOffersByCurrency(String direction, String currencyCode) { public List<Offer> getOffersByCurrency(String direction, String currencyCode) {
return getOffers().stream() 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()); .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 // 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 // That should only be possible if the price feed provider is not available
final List<OfferForJson> offerForJsonList = getOffers().stream() 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 -> { .map(offer -> {
try { try {
return new OfferForJson(offer.getDirection(), return new OfferForJson(offer.getDirection(),
offer.getCurrencyCode(), offer.getCounterCurrencyCode(),
offer.getMinAmount(), offer.getMinAmount(),
offer.getAmount(), offer.getAmount(),
offer.getPrice(), offer.getPrice(),

View file

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

View file

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

View file

@ -264,11 +264,11 @@ public class OfferUtil {
} }
public static boolean isTraditionalOffer(Offer offer) { public static boolean isTraditionalOffer(Offer offer) {
return offer.getBaseCurrencyCode().equals("XMR"); return CurrencyUtil.isTraditionalCurrency(offer.getCounterCurrencyCode());
} }
public static boolean isCryptoOffer(Offer offer) { public static boolean isCryptoOffer(Offer offer) {
return offer.getCounterCurrencyCode().equals("XMR"); return CurrencyUtil.isCryptoCurrency(offer.getCounterCurrencyCode());
} }
public BigInteger getMaxTradeLimitForRelease(PaymentAccount paymentAccount, 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.Preferences;
import haveno.core.user.User; import haveno.core.user.User;
import haveno.core.util.JsonUtil; import haveno.core.util.JsonUtil;
import haveno.core.util.PriceUtil;
import haveno.core.util.Validator; import haveno.core.util.Validator;
import haveno.core.xmr.model.XmrAddressEntry; import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.wallet.BtcWalletService; import haveno.core.xmr.wallet.BtcWalletService;
@ -638,7 +639,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
private void applyTriggerState(OpenOffer openOffer) { private void applyTriggerState(OpenOffer openOffer) {
if (openOffer.getState() != OpenOffer.State.AVAILABLE) return; 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); 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()); 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(), OfferPayload updatedPayload = new OfferPayload(originalOfferPayload.getId(),
originalOfferPayload.getDate(), originalOfferPayload.getDate(),
ownerNodeAddress, ownerNodeAddress,
originalOfferPayload.getPubKeyRing(), originalOfferPayload.getPubKeyRing(),
originalOfferPayload.getDirection(), originalOfferPayload.getDirection(),
originalOfferPayload.getPrice(), normalizedPrice,
originalOfferPayload.getMarketPriceMarginPct(), originalOfferPayload.getMarketPriceMarginPct(),
originalOfferPayload.isUseMarketBasedPrice(), originalOfferPayload.isUseMarketBasedPrice(),
originalOfferPayload.getAmount(), originalOfferPayload.getAmount(),
@ -2030,8 +2032,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
originalOfferPayload.getPenaltyFeePct(), originalOfferPayload.getPenaltyFeePct(),
originalOfferPayload.getBuyerSecurityDepositPct(), originalOfferPayload.getBuyerSecurityDepositPct(),
originalOfferPayload.getSellerSecurityDepositPct(), originalOfferPayload.getSellerSecurityDepositPct(),
originalOfferPayload.getBaseCurrencyCode(), originalOffer.getBaseCurrencyCode(),
originalOfferPayload.getCounterCurrencyCode(), originalOffer.getCounterCurrencyCode(),
originalOfferPayload.getPaymentMethodId(), originalOfferPayload.getPaymentMethodId(),
originalOfferPayload.getMakerPaymentAccountId(), originalOfferPayload.getMakerPaymentAccountId(),
originalOfferPayload.getCountryCode(), originalOfferPayload.getCountryCode(),
@ -2063,7 +2065,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
Offer updatedOffer = new Offer(updatedPayload); Offer updatedOffer = new Offer(updatedPayload);
updatedOffer.setPriceFeedService(priceFeedService); 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()); updatedOpenOffer.setChallenge(originalOpenOffer.getChallenge());
addOpenOffer(updatedOpenOffer); addOpenOffer(updatedOpenOffer);
requestPersistence(); requestPersistence();

View file

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

View file

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

View file

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

View file

@ -122,9 +122,9 @@ public class PaymentAccountUtil {
public static boolean isAmountValidForOffer(Offer offer, public static boolean isAmountValidForOffer(Offer offer,
PaymentAccount paymentAccount, PaymentAccount paymentAccount,
AccountAgeWitnessService accountAgeWitnessService) { AccountAgeWitnessService accountAgeWitnessService) {
boolean hasChargebackRisk = hasChargebackRisk(offer.getPaymentMethod(), offer.getCurrencyCode()); boolean hasChargebackRisk = hasChargebackRisk(offer.getPaymentMethod(), offer.getCounterCurrencyCode());
boolean hasValidAccountAgeWitness = accountAgeWitnessService.getMyTradeLimit(paymentAccount, 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; return !hasChargebackRisk || hasValidAccountAgeWitness;
} }

View file

@ -95,7 +95,7 @@ class ReceiptPredicates {
.map(TradeCurrency::getCode) .map(TradeCurrency::getCode)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
return codes.contains(offer.getCurrencyCode()); return codes.contains(offer.getCounterCurrencyCode());
} }
boolean isMatchingSepaOffer(Offer offer, PaymentAccount account) { 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(() -> { UserThread.execute(() -> {
String currencyCodeBase = CurrencyUtil.getCurrencyCodeBase(currencyCode); String counterCurrencyCodeBase = CurrencyUtil.getCurrencyCodeBase(counterCurrencyCode);
synchronized (cache) { synchronized (cache) {
if (!cache.containsKey(currencyCodeBase) || !cache.get(currencyCodeBase).isExternallyProvidedPrice()) { if (!cache.containsKey(counterCurrencyCodeBase) || !cache.get(counterCurrencyCodeBase).isExternallyProvidedPrice()) {
cache.put(currencyCodeBase, new MarketPrice(currencyCodeBase, cache.put(counterCurrencyCodeBase, new MarketPrice(counterCurrencyCodeBase,
MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(currencyCode) ? CryptoMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT), MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(counterCurrencyCode) ? CryptoMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT),
0, 0,
false)); false));
} }
@ -371,9 +371,7 @@ public class PriceFeedService {
} }
/** /**
* Returns prices for all available currencies. * Returns prices for all available currencies. The base currency is always XMR.
* 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).
* *
* TODO: instrument requestPrices() result and fault handlers instead of using CountDownLatch and timeout * 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 lombok.extern.slf4j.Slf4j;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -63,16 +65,20 @@ public class PriceProvider extends HttpClientProvider {
LinkedTreeMap<?, ?> treeMap = (LinkedTreeMap<?, ?>) obj; LinkedTreeMap<?, ?> treeMap = (LinkedTreeMap<?, ?>) obj;
String baseCurrencyCode = (String) treeMap.get("baseCurrencyCode"); String baseCurrencyCode = (String) treeMap.get("baseCurrencyCode");
String counterCurrencyCode = (String) treeMap.get("counterCurrencyCode"); String counterCurrencyCode = (String) treeMap.get("counterCurrencyCode");
String currencyCode = baseCurrencyCode.equals("XMR") ? counterCurrencyCode : baseCurrencyCode; boolean isInverted = !"XMR".equalsIgnoreCase(baseCurrencyCode);
currencyCode = CurrencyUtil.getCurrencyCodeBase(currencyCode); if (isInverted) {
String temp = baseCurrencyCode;
baseCurrencyCode = counterCurrencyCode;
counterCurrencyCode = temp;
}
counterCurrencyCode = CurrencyUtil.getCurrencyCodeBase(counterCurrencyCode);
double price = (Double) treeMap.get("price"); 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")); 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) { } catch (Throwable t) {
log.error("Error getting all prices: {}\n", t.getMessage(), t); log.error("Error getting all prices: {}\n", t.getMessage(), t);
} }
}); });
return marketPriceMap; 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.TradeListener;
import haveno.core.trade.protocol.TradePeer; import haveno.core.trade.protocol.TradePeer;
import haveno.core.trade.protocol.TradeProtocol; import haveno.core.trade.protocol.TradeProtocol;
import haveno.core.util.PriceUtil;
import haveno.core.util.VolumeUtil; import haveno.core.util.VolumeUtil;
import haveno.core.xmr.model.XmrAddressEntry; import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.wallet.XmrWalletBase; import haveno.core.xmr.wallet.XmrWalletBase;
@ -151,6 +152,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
private boolean restartInProgress; private boolean restartInProgress;
private Subscription protocolErrorStateSubscription; private Subscription protocolErrorStateSubscription;
private Subscription protocolErrorHeightSubscription; private Subscription protocolErrorHeightSubscription;
public static final String PROTOCOL_VERSION = "protocolVersion"; // key for extraDataMap in trade statistics
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Enums // Enums
@ -2377,8 +2379,16 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
return isBuyer() ? getBuyer().getSecurityDeposit() : getAmount().add(getSeller().getSecurityDeposit()); return isBuyer() ? getBuyer().getSecurityDeposit() : getAmount().add(getSeller().getSecurityDeposit());
} }
/**
* Returns the price as XMR/QUOTE.
*/
public Price getPrice() { 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 @Nullable

View file

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

View file

@ -45,9 +45,9 @@ public class ApplyFilter extends TradeTask {
} else if (filterManager.isOfferIdBanned(trade.getId())) { } else if (filterManager.isOfferIdBanned(trade.getId())) {
failed("Offer ID is banned.\n" + failed("Offer ID is banned.\n" +
"Offer ID=" + trade.getId()); "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" + failed("Currency is banned.\n" +
"Currency code=" + trade.getOffer().getCurrencyCode()); "Currency code=" + trade.getOffer().getCounterCurrencyCode());
} else if (filterManager.isPaymentMethodBanned(checkNotNull(trade.getOffer()).getPaymentMethod())) { } else if (filterManager.isPaymentMethodBanned(checkNotNull(trade.getOffer()).getPaymentMethod())) {
failed("Payment method is banned.\n" + failed("Payment method is banned.\n" +
"Payment method=" + trade.getOffer().getPaymentMethod().getId()); "Payment method=" + trade.getOffer().getPaymentMethod().getId());

View file

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

View file

@ -28,6 +28,7 @@ import haveno.common.util.CollectionUtils;
import haveno.common.util.ExtraDataMapValidator; import haveno.common.util.ExtraDataMapValidator;
import haveno.common.util.JsonExclude; import haveno.common.util.JsonExclude;
import haveno.common.util.Utilities; import haveno.common.util.Utilities;
import haveno.core.locale.CurrencyUtil;
import haveno.core.monetary.CryptoMoney; import haveno.core.monetary.CryptoMoney;
import haveno.core.monetary.Price; import haveno.core.monetary.Price;
import haveno.core.monetary.Volume; import haveno.core.monetary.Volume;
@ -35,6 +36,7 @@ import haveno.core.offer.Offer;
import haveno.core.offer.OfferPayload; import haveno.core.offer.OfferPayload;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.util.JsonUtil; import haveno.core.util.JsonUtil;
import haveno.core.util.PriceUtil;
import haveno.core.util.VolumeUtil; import haveno.core.util.VolumeUtil;
import haveno.network.p2p.NodeAddress; import haveno.network.p2p.NodeAddress;
import haveno.network.p2p.storage.payload.CapabilityRequiringPayload; 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, public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload,
CapabilityRequiringPayload, DateSortedTruncatablePayload { CapabilityRequiringPayload, DateSortedTruncatablePayload {
private static final String VERSION_KEY = "v"; // single character key for versioning
@JsonExclude @JsonExclude
private transient static final ZoneId ZONE_ID = ZoneId.systemDefault(); 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); 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()); 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. // The first 4 chars are sufficient to identify an arbitrator.
@ -104,7 +115,7 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
arbitratorNodeAddress.getFullAddress(); arbitratorNodeAddress.getFullAddress();
Offer offer = checkNotNull(trade.getOffer()); Offer offer = checkNotNull(trade.getOffer());
return new TradeStatistics3(offer.getCurrencyCode(), return new TradeStatistics3(offer.getCounterCurrencyCode(),
fuzzTradePriceReproducibly(trade, fuzzPricePct), fuzzTradePriceReproducibly(trade, fuzzPricePct),
fuzzTradeAmountReproducibly(trade, fuzzAmountPct), fuzzTradeAmountReproducibly(trade, fuzzAmountPct),
offer.getPaymentMethod().getId(), offer.getPaymentMethod().getId(),
@ -114,10 +125,10 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
} }
private static long fuzzTradePriceReproducibly(Trade trade, double fuzzPricePct) { 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(); long originalTimestamp = trade.getTakeOfferDate().getTime();
Random random = new Random(originalTimestamp); // pseudo random generator seeded from take offer datestamp 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)); 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); log.debug("trade {} fuzzed trade price for tradeStatistics is {}", trade.getShortId(), adjustedPrice);
return adjustedPrice; return adjustedPrice;
@ -209,12 +220,10 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
@Getter @Getter
private final String currency; private final String currency;
@Getter
private final long price; private final long price;
@Getter @Getter
private final long amount; // BTC amount private final long amount; // XMR amount
private final String paymentMethod; private final String paymentMethod;
// As only seller is publishing it is the sellers trade date
private final long date; private final long date;
// Old converted trade stat objects might not have it set // 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). // Hash get set in constructor from json of all the other data fields (with hash = null).
@JsonExclude @JsonExclude
private final byte[] hash; 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 // 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. // field in a class would break that hash and therefore break the storage mechanism.
@Nullable @Nullable
@ -314,8 +323,6 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
public byte[] createHash() { public byte[] createHash() {
// We create hash from all fields excluding hash itself. We use json as simple data serialisation. // 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)); return Hash.getSha256Ripemd160hash(JsonUtil.objectToJson(this).getBytes(Charsets.UTF_8));
} }
@ -411,11 +418,26 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
public Price getTradePrice() { public Price getTradePrice() {
if (priceObj == null) { if (priceObj == null) {
priceObj = Price.valueOf(currency, price); priceObj = Price.valueOf(currency, getNormalizedPrice());
} }
return priceObj; 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() { public BigInteger getTradeAmount() {
return BigInteger.valueOf(amount); return BigInteger.valueOf(amount);
} }
@ -472,7 +494,8 @@ public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayl
public String toString() { public String toString() {
return "TradeStatistics3{" + return "TradeStatistics3{" +
"\n currency='" + currency + '\'' + "\n currency='" + currency + '\'' +
",\n price=" + price + ",\n rawPrice=" + price +
",\n normalizedPrice=" + getNormalizedPrice() +
",\n amount=" + amount + ",\n amount=" + amount +
",\n paymentMethod='" + paymentMethod + '\'' + ",\n paymentMethod='" + paymentMethod + '\'' +
",\n date=" + date + ",\n date=" + date +

View file

@ -17,7 +17,6 @@
package haveno.core.trade.statistics; package haveno.core.trade.statistics;
import haveno.core.locale.CurrencyUtil;
import haveno.core.locale.Res; import haveno.core.locale.Res;
import haveno.core.monetary.Price; import haveno.core.monetary.Price;
import haveno.core.monetary.Volume; import haveno.core.monetary.Volume;
@ -39,7 +38,7 @@ public final class TradeStatisticsForJson {
public final long tradeDate; public final long tradeDate;
public final String paymentMethod; 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 String currencyPair;
public long primaryMarketTradePrice; public long primaryMarketTradePrice;
@ -49,27 +48,18 @@ public final class TradeStatisticsForJson {
public TradeStatisticsForJson(TradeStatistics3 tradeStatistics) { public TradeStatisticsForJson(TradeStatistics3 tradeStatistics) {
this.currency = tradeStatistics.getCurrency(); this.currency = tradeStatistics.getCurrency();
this.paymentMethod = tradeStatistics.getPaymentMethodId(); this.paymentMethod = tradeStatistics.getPaymentMethodId();
this.tradePrice = tradeStatistics.getPrice(); this.tradePrice = tradeStatistics.getNormalizedPrice();
this.tradeAmount = tradeStatistics.getAmount(); this.tradeAmount = tradeStatistics.getAmount();
this.tradeDate = tradeStatistics.getDateAsLong(); this.tradeDate = tradeStatistics.getDateAsLong();
try { try {
Price tradePrice = getPrice(); Price tradePrice = getPrice();
if (CurrencyUtil.isCryptoCurrency(currency)) { currencyPair = Res.getBaseCurrencyCode() + "/" + currency;
currencyPair = currency + "/" + Res.getBaseCurrencyCode(); primaryMarketTradePrice = tradePrice.getValue();
primaryMarketTradePrice = tradePrice.getValue(); primaryMarketTradeAmount = getTradeAmount().longValueExact();
primaryMarketTradeAmount = getTradeVolume() != null ? primaryMarketTradeVolume = getTradeVolume() != null ?
getTradeVolume().getValue() : getTradeVolume().getValue() :
0; 0;
primaryMarketTradeVolume = getTradeAmount().longValueExact();
} else {
currencyPair = Res.getBaseCurrencyCode() + "/" + currency;
primaryMarketTradePrice = tradePrice.getValue();
primaryMarketTradeAmount = getTradeAmount().longValueExact();
primaryMarketTradeVolume = getTradeVolume() != null ?
getTradeVolume().getValue() :
0;
}
} catch (Throwable t) { } catch (Throwable t) {
log.error(t.getMessage()); log.error(t.getMessage());
t.printStackTrace(); t.printStackTrace();

View file

@ -101,8 +101,8 @@ public class TradeStatisticsManager {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
// remove duplicates in early trades due to bug // remove duplicates in early trade stats due to bugs
deduplicateEarlyTradeStatistics(set); removeDuplicateStats(set);
synchronized (observableTradeStatisticsSet) { synchronized (observableTradeStatisticsSet) {
observableTradeStatisticsSet.addAll(set); observableTradeStatisticsSet.addAll(set);
@ -111,7 +111,42 @@ public class TradeStatisticsManager {
maybeDumpStatistics(); 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 // collect trades before August 7, 2024
Set<TradeStatistics3> earlyTrades = tradeStats.stream() Set<TradeStatistics3> earlyTrades = tradeStats.stream()
@ -127,7 +162,7 @@ public class TradeStatisticsManager {
else duplicates.add(tradeStatistic); else duplicates.add(tradeStatistic);
} }
// remove duplicated trades // remove duplicated stats
tradeStats.removeAll(duplicates); tradeStats.removeAll(duplicates);
} }
@ -138,7 +173,7 @@ public class TradeStatisticsManager {
private boolean isFuzzyDuplicate(TradeStatistics3 tradeStatistics1, TradeStatistics3 tradeStatistics2) { private boolean isFuzzyDuplicate(TradeStatistics3 tradeStatistics1, TradeStatistics3 tradeStatistics2) {
if (!tradeStatistics1.getPaymentMethodId().equals(tradeStatistics2.getPaymentMethodId())) return false; if (!tradeStatistics1.getPaymentMethodId().equals(tradeStatistics2.getPaymentMethodId())) return false;
if (!tradeStatistics1.getCurrency().equals(tradeStatistics2.getCurrency())) 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); 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) { private static List<TradeStatistics3> removeOutliers(List<TradeStatistics3> list, double percentToTrim) {
List<Double> yValues = list.stream() List<Double> yValues = list.stream()
.filter(TradeStatistics3::isValid) .filter(TradeStatistics3::isValid)
.map(e -> (double) e.getPrice()) .map(e -> (double) e.getNormalizedPrice())
.collect(Collectors.toList()); .collect(Collectors.toList());
Tuple2<Double, Double> tuple = InlierUtil.findInlierRange(yValues, percentToTrim, HOW_MANY_STD_DEVS_CONSTITUTE_OUTLIER); Tuple2<Double, Double> tuple = InlierUtil.findInlierRange(yValues, percentToTrim, HOW_MANY_STD_DEVS_CONSTITUTE_OUTLIER);
double lowerBound = tuple.first; double lowerBound = tuple.first;
double upperBound = tuple.second; double upperBound = tuple.second;
return list.stream() return list.stream()
.filter(e -> e.getPrice() > lowerBound) .filter(e -> e.getNormalizedPrice() > lowerBound)
.filter(e -> e.getPrice() < upperBound) .filter(e -> e.getNormalizedPrice() < upperBound)
.collect(Collectors.toList()); .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.AmountValidator8Decimals;
import haveno.core.util.validation.InputValidator; import haveno.core.util.validation.InputValidator;
import haveno.core.util.validation.MonetaryValidator; import haveno.core.util.validation.MonetaryValidator;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Optional; import java.util.Optional;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -84,7 +87,7 @@ public class PriceUtil {
} }
public boolean hasMarketPrice(Offer offer) { public boolean hasMarketPrice(Offer offer) {
String currencyCode = offer.getCurrencyCode(); String currencyCode = offer.getCounterCurrencyCode();
checkNotNull(priceFeedService, "priceFeed must not be null"); checkNotNull(priceFeedService, "priceFeed must not be null");
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
Price price = offer.getPrice(); Price price = offer.getPrice();
@ -103,7 +106,7 @@ public class PriceUtil {
return Optional.empty(); return Optional.empty();
} }
String currencyCode = offer.getCurrencyCode(); String currencyCode = offer.getCounterCurrencyCode();
checkNotNull(priceFeedService, "priceFeed must not be null"); checkNotNull(priceFeedService, "priceFeed must not be null");
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
double marketPriceAsDouble = checkNotNull(marketPrice).getPrice(); double marketPriceAsDouble = checkNotNull(marketPrice).getPrice();
@ -114,7 +117,7 @@ public class PriceUtil {
double marketPrice, double marketPrice,
OfferDirection direction) { OfferDirection direction) {
// If the offer did not use % price we calculate % from current market price // 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(); Price price = offer.getPrice();
int precision = CurrencyUtil.isTraditionalCurrency(currencyCode) ? int precision = CurrencyUtil.isTraditionalCurrency(currencyCode) ?
TraditionalMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT :
@ -123,29 +126,15 @@ public class PriceUtil {
double scaled = MathUtils.scaleDownByPowerOf10(priceAsLong, precision); double scaled = MathUtils.scaleDownByPowerOf10(priceAsLong, precision);
double value; double value;
if (direction == OfferDirection.SELL) { if (direction == OfferDirection.SELL) {
if (CurrencyUtil.isTraditionalCurrency(currencyCode)) { if (marketPrice == 0) {
if (marketPrice == 0) { return Optional.empty();
return Optional.empty();
}
value = 1 - scaled / marketPrice;
} else {
if (marketPrice == 1) {
return Optional.empty();
}
value = scaled / marketPrice - 1;
} }
value = 1 - scaled / marketPrice;
} else { } else {
if (CurrencyUtil.isTraditionalCurrency(currencyCode)) { if (marketPrice == 1) {
if (marketPrice == 1) { return Optional.empty();
return Optional.empty();
}
value = scaled / marketPrice - 1;
} else {
if (marketPrice == 0) {
return Optional.empty();
}
value = 1 - scaled / marketPrice;
} }
value = scaled / marketPrice - 1;
} }
return Optional.of(value); return Optional.of(value);
} }
@ -183,4 +172,13 @@ public class PriceUtil {
return CurrencyUtil.isTraditionalCurrency(currencyCode) ? return CurrencyUtil.isTraditionalCurrency(currencyCode) ?
TraditionalMoney.SMALLEST_UNIT_EXPONENT : CryptoMoney.SMALLEST_UNIT_EXPONENT; 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.price.prompt=Enter price
createOffer.volume.prompt=Enter amount in {0} createOffer.volume.prompt=Enter amount in {0}
createOffer.amountPriceBox.amountDescription=Amount of XMR to {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.buy.volumeDescription=Amount in {0} to spend
createOffer.amountPriceBox.sell.volumeDescription=Amount in {0} to receive 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.amountPriceBox.minAmountDescription=Minimum amount of XMR
createOffer.securityDeposit.prompt=Security deposit createOffer.securityDeposit.prompt=Security deposit
createOffer.fundsBox.title=Fund your offer 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. createOffer.extraInfo.invalid.tooLong=Must not exceed {0} characters.
# new entries # new entries
createOffer.placeOfferButton=Review: Place offer to {0} monero createOffer.placeOfferButton.buy=Review: Place offer to buy XMR with {0}
createOffer.placeOfferButtonCrypto=Review: Place offer to {0} {1} createOffer.placeOfferButton.sell=Review: Place offer to sell XMR for {0}
createOffer.createOfferFundWalletInfo.headline=Fund your offer createOffer.createOfferFundWalletInfo.headline=Fund your offer
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Trade amount: {0} \n 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} takeOffer.error.message=An error occurred when taking the offer.\n\n{0}
# new entries # new entries
takeOffer.takeOfferButton=Review: Take offer to {0} monero takeOffer.takeOfferButton.buy=Review: Take offer to buy XMR with {0}
takeOffer.takeOfferButtonCrypto=Review: Take offer to {0} {1} 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.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 takeOffer.takeOfferFundWalletInfo.headline=Fund your trade
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -2008,10 +2004,10 @@ offerDetailsWindow.countryBank=Maker's country of bank
offerDetailsWindow.commitment=Commitment offerDetailsWindow.commitment=Commitment
offerDetailsWindow.agree=I agree offerDetailsWindow.agree=I agree
offerDetailsWindow.tac=Terms and conditions offerDetailsWindow.tac=Terms and conditions
offerDetailsWindow.confirm.maker=Confirm: Place offer to {0} monero offerDetailsWindow.confirm.maker.buy=Confirm: Place offer to buy XMR with {0}
offerDetailsWindow.confirm.makerCrypto=Confirm: Place offer to {0} {1} offerDetailsWindow.confirm.maker.sell=Confirm: Place offer to sell XMR for {0}
offerDetailsWindow.confirm.taker=Confirm: Take offer to {0} monero offerDetailsWindow.confirm.taker.buy=Confirm: Take offer to buy XMR with {0}
offerDetailsWindow.confirm.takerCrypto=Confirm: Take offer to {0} {1} offerDetailsWindow.confirm.taker.sell=Confirm: Take offer to sell XMR for {0}
offerDetailsWindow.creationDate=Creation date offerDetailsWindow.creationDate=Creation date
offerDetailsWindow.makersOnion=Maker's onion address offerDetailsWindow.makersOnion=Maker's onion address
offerDetailsWindow.challenge=Offer passphrase offerDetailsWindow.challenge=Offer passphrase

View file

@ -457,12 +457,8 @@ createOffer.amount.prompt=Zadejte množství v XMR
createOffer.price.prompt=Zadejte cenu createOffer.price.prompt=Zadejte cenu
createOffer.volume.prompt=Zadejte množství v {0} createOffer.volume.prompt=Zadejte množství v {0}
createOffer.amountPriceBox.amountDescription=Množství XMR, které chcete {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.buy.volumeDescription=Množství {0}, které odešlete
createOffer.amountPriceBox.sell.volumeDescription=Množství {0}, které přijmete 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.amountPriceBox.minAmountDescription=Minimální množství XMR
createOffer.securityDeposit.prompt=Kauce createOffer.securityDeposit.prompt=Kauce
createOffer.fundsBox.title=Financovat nabídku 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} createOffer.triggerPrice.invalid.tooHigh=Hodnota musí být nižší než {0}
# new entries # new entries
createOffer.placeOfferButton=Zkontrolovat vytvoření nabídky {0} monero createOffer.placeOfferButton.buy=Zkontrolovat: Vytvořit nabídku na nákup XMR za {0}
createOffer.placeOfferButtonCrypto=Zkontrolovat vytvoření nabídky {0} {1} createOffer.placeOfferButton.sell=Zkontrolovat: Vytvořit nabídku na prodej XMR za {0}
createOffer.createOfferFundWalletInfo.headline=Financovat nabídku createOffer.createOfferFundWalletInfo.headline=Financovat nabídku
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Výše obchodu: {0}\n 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} takeOffer.error.message=Při převzetí nabídky došlo k chybě.\n\n{0}
# new entries # new entries
takeOffer.takeOfferButton=Zkontrolovat přijetí nabídky {0} monero takeOffer.takeOfferButton.buy=Zkontrolovat: Přijmout nabídku na nákup XMR za {0}
takeOffer.takeOfferButtonCrypto=Zkontrolovat přijetí nabídky {0} {1} 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.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 takeOffer.takeOfferFundWalletInfo.headline=Financovat obchod
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1964,10 +1960,10 @@ offerDetailsWindow.countryBank=Země původu banky tvůrce
offerDetailsWindow.commitment=Závazek offerDetailsWindow.commitment=Závazek
offerDetailsWindow.agree=Souhlasím offerDetailsWindow.agree=Souhlasím
offerDetailsWindow.tac=Pravidla a podmínky offerDetailsWindow.tac=Pravidla a podmínky
offerDetailsWindow.confirm.maker=Potvrďte: Přidat nabídku {0} monero offerDetailsWindow.confirm.maker.buy=Potvrdit: Vytvořit nabídku na nákup XMR za {0}
offerDetailsWindow.confirm.makerCrypto=Potvrďte: Přidat nabídku do {0} {1} offerDetailsWindow.confirm.maker.sell=Potvrdit: Vytvořit nabídku na prodej XMR za {0}
offerDetailsWindow.confirm.taker=Potvrďte: Přijmout nabídku {0} monero offerDetailsWindow.confirm.taker.buy=Potvrdit: Přijmout nabídku na nákup XMR za {0}
offerDetailsWindow.confirm.takerCrypto=Potvrďte: Přijmout nabídku {0} {1} offerDetailsWindow.confirm.taker.sell=Potvrdit: Přijmout nabídku na prodej XMR za {0}
offerDetailsWindow.creationDate=Datum vzniku offerDetailsWindow.creationDate=Datum vzniku
offerDetailsWindow.makersOnion=Onion adresa tvůrce offerDetailsWindow.makersOnion=Onion adresa tvůrce
offerDetailsWindow.challenge=Passphrase nabídky 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} createOffer.triggerPrice.invalid.tooHigh=Wert muss niedriger sein als {0}
# new entries # 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 createOffer.createOfferFundWalletInfo.headline=Ihr Angebot finanzieren
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Handelsbetrag: {0} \n 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} takeOffer.error.message=Bei der Angebotsannahme trat ein Fehler auf.\n\n{0}
# new entries # 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.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 takeOffer.takeOfferFundWalletInfo.headline=Ihren Handel finanzieren
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1469,8 +1471,10 @@ offerDetailsWindow.countryBank=Land der Bank des Erstellers
offerDetailsWindow.commitment=Verpflichtung offerDetailsWindow.commitment=Verpflichtung
offerDetailsWindow.agree=Ich stimme zu offerDetailsWindow.agree=Ich stimme zu
offerDetailsWindow.tac=Geschäftsbedingungen offerDetailsWindow.tac=Geschäftsbedingungen
offerDetailsWindow.confirm.maker=Bestätigen: Anbieten monero zu {0} offerDetailsWindow.confirm.maker.buy=Bestätigen: Angebot zum Kauf von XMR mit {0} erstellen
offerDetailsWindow.confirm.taker=Bestätigen: Angebot annehmen monero zu {0} 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.creationDate=Erstellungsdatum
offerDetailsWindow.makersOnion=Onion-Adresse des Erstellers offerDetailsWindow.makersOnion=Onion-Adresse des Erstellers
offerDetailsWindow.challenge=Angebots-Passphrase 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} createOffer.triggerPrice.invalid.tooHigh=El valor debe ser inferior a {0}
# new entries # 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. createOffer.createOfferFundWalletInfo.headline=Dote de fondos su trato.
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Cantidad a intercambiar: {0}\n 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} takeOffer.error.message=Un error ocurrió al tomar la oferta.\n\n{0}
# new entries # 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.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 takeOffer.takeOfferFundWalletInfo.headline=Dotar de fondos su intercambio
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1470,8 +1472,10 @@ offerDetailsWindow.countryBank=País del banco del creador
offerDetailsWindow.commitment=Compromiso offerDetailsWindow.commitment=Compromiso
offerDetailsWindow.agree=Estoy de acuerdo offerDetailsWindow.agree=Estoy de acuerdo
offerDetailsWindow.tac=Términos y condiciones: offerDetailsWindow.tac=Términos y condiciones:
offerDetailsWindow.confirm.maker=Confirmar: Poner oferta para {0} monero offerDetailsWindow.confirm.maker.buy=Confirmar: Crear oferta para comprar XMR con {0}
offerDetailsWindow.confirm.taker=Confirmar: Tomar oferta {0} monero 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.creationDate=Fecha de creación
offerDetailsWindow.makersOnion=Dirección onion del creador offerDetailsWindow.makersOnion=Dirección onion del creador
offerDetailsWindow.challenge=Frase de contraseña de la oferta 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} createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries # new entries
createOffer.placeOfferButton=بررسی: پیشنهاد را برای {0} بیتکوین بگذارید createOffer.placeOfferButton.buy=بررسی: ایجاد پیشنهاد خرید XMR با {0}
createOffer.placeOfferButton.sell=بررسی: ایجاد پیشنهاد فروش XMR به ازای {0}
createOffer.createOfferFundWalletInfo.headline=پیشنهاد خود را تامین وجه نمایید createOffer.createOfferFundWalletInfo.headline=پیشنهاد خود را تامین وجه نمایید
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=مقدار معامله:{0}\n createOffer.createOfferFundWalletInfo.tradeAmount=مقدار معامله:{0}\n
@ -519,7 +520,8 @@ takeOffer.success.info=شما می‌توانید وضعیت معامله‌ی
takeOffer.error.message=هنگام قبول کردن پیشنهاد، اتفاقی رخ داده است.\n\n{0} takeOffer.error.message=هنگام قبول کردن پیشنهاد، اتفاقی رخ داده است.\n\n{0}
# new entries # new entries
takeOffer.takeOfferButton=بررسی: برای {0} بیتکوین پیشنهاد بگذارید. takeOffer.takeOfferButton.buy=بررسی: قبول پیشنهاد خرید XMR با {0}
takeOffer.takeOfferButton.sell=بررسی: قبول پیشنهاد فروش XMR به ازای {0}
takeOffer.noPriceFeedAvailable=امکان پذیرفتن پیشنهاد وجود ندارد. پیشنهاد از قیمت درصدی مبتنی بر قیمت روز بازار استفاده می‌کند و قیمت‌های بازار هم‌اکنون در دسترس نیست. takeOffer.noPriceFeedAvailable=امکان پذیرفتن پیشنهاد وجود ندارد. پیشنهاد از قیمت درصدی مبتنی بر قیمت روز بازار استفاده می‌کند و قیمت‌های بازار هم‌اکنون در دسترس نیست.
takeOffer.takeOfferFundWalletInfo.headline=معامله خود را تأمین وجه نمایید takeOffer.takeOfferFundWalletInfo.headline=معامله خود را تأمین وجه نمایید
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1465,8 +1467,10 @@ offerDetailsWindow.countryBank=کشور بانک سفارش‌گذار
offerDetailsWindow.commitment=تعهد offerDetailsWindow.commitment=تعهد
offerDetailsWindow.agree=من موافقم offerDetailsWindow.agree=من موافقم
offerDetailsWindow.tac=شرایط و الزامات offerDetailsWindow.tac=شرایط و الزامات
offerDetailsWindow.confirm.maker=تأیید: پیشنهاد را به {0} بگذارید offerDetailsWindow.confirm.maker.buy=تأیید: ایجاد پیشنهاد خرید XMR با {0}
offerDetailsWindow.confirm.taker=تأیید: پیشنهاد را به {0} بپذیرید offerDetailsWindow.confirm.maker.sell=تأیید: ایجاد پیشنهاد فروش XMR به ازای {0}
offerDetailsWindow.confirm.taker.buy=تأیید: قبول پیشنهاد خرید XMR با {0}
offerDetailsWindow.confirm.taker.sell=تأیید: قبول پیشنهاد فروش XMR به ازای {0}
offerDetailsWindow.creationDate=تاریخ ایجاد offerDetailsWindow.creationDate=تاریخ ایجاد
offerDetailsWindow.makersOnion=آدرس Onion سفارش گذار offerDetailsWindow.makersOnion=آدرس Onion سفارش گذار
offerDetailsWindow.challenge=Passphrase de l'offre 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] createOffer.triggerPrice.invalid.tooHigh=La valuer doit être inférieure à {0]
# new entries # 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 createOffer.createOfferFundWalletInfo.headline=Financer votre ordre
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=Montant du trade: {0}\n\n 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} takeOffer.error.message=Une erreur s'est produite pendant l'acceptation de l'ordre.\n\n{0}
# new entries # 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.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 takeOffer.takeOfferFundWalletInfo.headline=Provisionner votre trade
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1471,8 +1473,10 @@ offerDetailsWindow.countryBank=Pays de la banque du Maker
offerDetailsWindow.commitment=Engagement offerDetailsWindow.commitment=Engagement
offerDetailsWindow.agree=J'accepte offerDetailsWindow.agree=J'accepte
offerDetailsWindow.tac=Conditions d'utilisation offerDetailsWindow.tac=Conditions d'utilisation
offerDetailsWindow.confirm.maker=Confirmer: Placer un ordre de {0} monero offerDetailsWindow.confirm.maker.buy=Confirmer : Créer une offre pour acheter XMR avec {0}
offerDetailsWindow.confirm.taker=Confirmer: Acceptez l'ordre de {0} monero 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.creationDate=Date de création
offerDetailsWindow.makersOnion=Adresse onion du maker offerDetailsWindow.makersOnion=Adresse onion du maker
offerDetailsWindow.challenge=Phrase secrète de l'offre 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} createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries # 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 createOffer.createOfferFundWalletInfo.headline=Finanzia la tua offerta
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Importo di scambio: {0} \n 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} takeOffer.error.message=Si è verificato un errore durante l'accettazione dell'offerta.\n\n{0}
# new entries # 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.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 takeOffer.takeOfferFundWalletInfo.headline=Finanzia il tuo scambio
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1468,8 +1470,10 @@ offerDetailsWindow.countryBank=Paese della banca del maker
offerDetailsWindow.commitment=Impegno offerDetailsWindow.commitment=Impegno
offerDetailsWindow.agree=Accetto offerDetailsWindow.agree=Accetto
offerDetailsWindow.tac=Termini e condizioni offerDetailsWindow.tac=Termini e condizioni
offerDetailsWindow.confirm.maker=Conferma: Piazza l'offerta a {0} monero offerDetailsWindow.confirm.maker.buy=Conferma: Crea offerta per acquistare XMR con {0}
offerDetailsWindow.confirm.taker=Conferma: Accetta l'offerta a {0} monero 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.creationDate=Data di creazione
offerDetailsWindow.makersOnion=Indirizzo .onion del maker offerDetailsWindow.makersOnion=Indirizzo .onion del maker
offerDetailsWindow.challenge=Passphrase dell'offerta offerDetailsWindow.challenge=Passphrase dell'offerta

View file

@ -455,7 +455,8 @@ createOffer.triggerPrice.invalid.tooLow=価値は{0}より高くなければな
createOffer.triggerPrice.invalid.tooHigh=価値は{0}より低くなければなりません createOffer.triggerPrice.invalid.tooHigh=価値は{0}より低くなければなりません
# new entries # new entries
createOffer.placeOfferButton=再確認: ビットコインを{0}オファーを出す createOffer.placeOfferButton.buy=確認:{0}でXMRを購入するオファーを作成
createOffer.placeOfferButton.sell=確認:{0}でXMRを売却するオファーを作成
createOffer.createOfferFundWalletInfo.headline=あなたのオファーへ入金 createOffer.createOfferFundWalletInfo.headline=あなたのオファーへ入金
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- 取引額: {0}\n createOffer.createOfferFundWalletInfo.tradeAmount=- 取引額: {0}\n
@ -520,7 +521,8 @@ takeOffer.success.info=あなたのトレード状態は「ポートフォリオ
takeOffer.error.message=オファーの受け入れ時にエラーが発生しました。\n\n{0} takeOffer.error.message=オファーの受け入れ時にエラーが発生しました。\n\n{0}
# new entries # new entries
takeOffer.takeOfferButton=再確認: ビットコインを{0}オファーを申し込む takeOffer.takeOfferButton.buy=確認:{0}でXMRを購入するオファーを受け入れ
takeOffer.takeOfferButton.sell=確認:{0}でXMRを売却するオファーを受け入れ
takeOffer.noPriceFeedAvailable=そのオファーは市場価格に基づくパーセント値を使用していますが、使用可能な価格フィードがないため、利用することはできません。 takeOffer.noPriceFeedAvailable=そのオファーは市場価格に基づくパーセント値を使用していますが、使用可能な価格フィードがないため、利用することはできません。
takeOffer.takeOfferFundWalletInfo.headline=あなたのオファーへ入金 takeOffer.takeOfferFundWalletInfo.headline=あなたのオファーへ入金
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1469,8 +1471,10 @@ offerDetailsWindow.countryBank=メイカーの銀行の国名
offerDetailsWindow.commitment=約束 offerDetailsWindow.commitment=約束
offerDetailsWindow.agree=同意します offerDetailsWindow.agree=同意します
offerDetailsWindow.tac=取引条件 offerDetailsWindow.tac=取引条件
offerDetailsWindow.confirm.maker=承認: ビットコインを{0}オファーを出す offerDetailsWindow.confirm.maker.buy=確定:{0}でXMRを購入するオファーを作成
offerDetailsWindow.confirm.taker=承認: ビットコインを{0}オファーを受ける offerDetailsWindow.confirm.maker.sell=確定:{0}でXMRを売却するオファーを作成
offerDetailsWindow.confirm.taker.buy=確定:{0}でXMRを購入するオファーを受け入れ
offerDetailsWindow.confirm.taker.sell=確定:{0}でXMRを売却するオファーを受け入れ
offerDetailsWindow.creationDate=作成日 offerDetailsWindow.creationDate=作成日
offerDetailsWindow.makersOnion=メイカーのonionアドレス offerDetailsWindow.makersOnion=メイカーのonionアドレス
offerDetailsWindow.challenge=オファーパスフレーズ 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} createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries # 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 createOffer.createOfferFundWalletInfo.headline=Financiar sua oferta
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Quantia da negociação: {0} \n 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} takeOffer.error.message=Ocorreu um erro ao aceitar a oferta.\n\n{0}
# new entries # 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.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 takeOffer.takeOfferFundWalletInfo.headline=Financiar sua negociação
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1472,8 +1474,10 @@ offerDetailsWindow.countryBank=País do banco do ofertante
offerDetailsWindow.commitment=Compromisso offerDetailsWindow.commitment=Compromisso
offerDetailsWindow.agree=Eu concordo offerDetailsWindow.agree=Eu concordo
offerDetailsWindow.tac=Termos e condições offerDetailsWindow.tac=Termos e condições
offerDetailsWindow.confirm.maker=Criar oferta para {0} monero offerDetailsWindow.confirm.maker.buy=Confirmar: Criar oferta para comprar XMR com {0}
offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} monero 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.creationDate=Criada em
offerDetailsWindow.makersOnion=Endereço onion do ofertante offerDetailsWindow.makersOnion=Endereço onion do ofertante
offerDetailsWindow.challenge=Passphrase da oferta 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} createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries # 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 createOffer.createOfferFundWalletInfo.headline=Financiar sua oferta
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Quantia de negócio: {0} \n 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} takeOffer.error.message=Ocorreu um erro ao aceitar a oferta .\n\n{0}
# new entries # 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.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 takeOffer.takeOfferFundWalletInfo.headline=Financiar seu negócio
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1465,8 +1467,10 @@ offerDetailsWindow.countryBank=País do banco do ofertante
offerDetailsWindow.commitment=Compromisso offerDetailsWindow.commitment=Compromisso
offerDetailsWindow.agree=Eu concordo offerDetailsWindow.agree=Eu concordo
offerDetailsWindow.tac=Termos e condições offerDetailsWindow.tac=Termos e condições
offerDetailsWindow.confirm.maker=Confirmar: Criar oferta para {0} monero offerDetailsWindow.confirm.maker.buy=Confirmar: Criar oferta para comprar XMR com {0}
offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} monero 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.creationDate=Data de criação
offerDetailsWindow.makersOnion=Endereço onion do ofertante offerDetailsWindow.makersOnion=Endereço onion do ofertante
offerDetailsWindow.challenge=Passphrase da oferta 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} createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries # new entries
createOffer.placeOfferButton=Проверка: разместить предложение {0} биткойн createOffer.placeOfferButton.buy=Проверка: Создать предложение на покупку XMR за {0}
createOffer.placeOfferButton.sell=Проверка: Создать предложение на продажу XMR за {0}
createOffer.createOfferFundWalletInfo.headline=Обеспечить своё предложение createOffer.createOfferFundWalletInfo.headline=Обеспечить своё предложение
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Сумма сделки: {0} \n createOffer.createOfferFundWalletInfo.tradeAmount=- Сумма сделки: {0} \n
@ -519,7 +520,8 @@ takeOffer.success.info=Статус вашей сделки отображает
takeOffer.error.message=Ошибка при принятии предложения:\n\n{0} takeOffer.error.message=Ошибка при принятии предложения:\n\n{0}
# new entries # new entries
takeOffer.takeOfferButton=Проверка: принять предложение {0} биткойн takeOffer.takeOfferButton.buy=Проверка: Принять предложение на покупку XMR за {0}
takeOffer.takeOfferButton.sell=Проверка: Принять предложение на продажу XMR за {0}
takeOffer.noPriceFeedAvailable=Нельзя принять это предложение, поскольку в нем используется процентный курс на основе рыночного курса, источник которого недоступен. takeOffer.noPriceFeedAvailable=Нельзя принять это предложение, поскольку в нем используется процентный курс на основе рыночного курса, источник которого недоступен.
takeOffer.takeOfferFundWalletInfo.headline=Обеспечьте свою сделку takeOffer.takeOfferFundWalletInfo.headline=Обеспечьте свою сделку
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1466,8 +1468,10 @@ offerDetailsWindow.countryBank=Страна банка мейкера
offerDetailsWindow.commitment=Обязательство offerDetailsWindow.commitment=Обязательство
offerDetailsWindow.agree=Подтверждаю offerDetailsWindow.agree=Подтверждаю
offerDetailsWindow.tac=Пользовательское соглашение offerDetailsWindow.tac=Пользовательское соглашение
offerDetailsWindow.confirm.maker=Подтвердите: разместить предложение {0} биткойн offerDetailsWindow.confirm.maker.buy=Подтверждение: Создать предложение на покупку XMR за {0}
offerDetailsWindow.confirm.taker=Подтвердите: принять предложение {0} биткойн offerDetailsWindow.confirm.maker.sell=Подтверждение: Создать предложение на продажу XMR за {0}
offerDetailsWindow.confirm.taker.buy=Подтверждение: Принять предложение на покупку XMR за {0}
offerDetailsWindow.confirm.taker.sell=Подтверждение: Принять предложение на продажу XMR за {0}
offerDetailsWindow.creationDate=Дата создания offerDetailsWindow.creationDate=Дата создания
offerDetailsWindow.makersOnion=Onion-адрес мейкера offerDetailsWindow.makersOnion=Onion-адрес мейкера
offerDetailsWindow.challenge=Пароль предложения 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} createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries # new entries
createOffer.placeOfferButton=รีวิว: ใส่ข้อเสนอไปยัง {0} บิตคอย createOffer.placeOfferButton.buy=ตรวจสอบ: สร้างข้อเสนอเพื่อซื้อ XMR ด้วย {0}
createOffer.placeOfferButton.sell=ตรวจสอบ: สร้างข้อเสนอเพื่อขาย XMR เป็น {0}
createOffer.createOfferFundWalletInfo.headline=เงินทุนสำหรับข้อเสนอของคุณ createOffer.createOfferFundWalletInfo.headline=เงินทุนสำหรับข้อเสนอของคุณ
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- ปริมาณการซื้อขาย: {0} createOffer.createOfferFundWalletInfo.tradeAmount=- ปริมาณการซื้อขาย: {0}
@ -519,7 +520,8 @@ takeOffer.success.info=คุณสามารถดูสถานะการ
takeOffer.error.message=เกิดข้อผิดพลาดขณะรับข้อเสนอ\n\n{0} takeOffer.error.message=เกิดข้อผิดพลาดขณะรับข้อเสนอ\n\n{0}
# new entries # new entries
takeOffer.takeOfferButton=รีวิว: รับข้อเสนอจาก {0} monero takeOffer.takeOfferButton.buy=ตรวจสอบ: ยอมรับข้อเสนอเพื่อซื้อ XMR ด้วย {0}
takeOffer.takeOfferButton.sell=ตรวจสอบ: ยอมรับข้อเสนอเพื่อขาย XMR เป็น {0}
takeOffer.noPriceFeedAvailable=คุณไม่สามารถรับข้อเสนอดังกล่าวเนื่องจากใช้ราคาร้อยละตามราคาตลาด แต่ไม่มีฟีดราคาที่พร้อมใช้งาน takeOffer.noPriceFeedAvailable=คุณไม่สามารถรับข้อเสนอดังกล่าวเนื่องจากใช้ราคาร้อยละตามราคาตลาด แต่ไม่มีฟีดราคาที่พร้อมใช้งาน
takeOffer.takeOfferFundWalletInfo.headline=ทุนการซื้อขายของคุณ takeOffer.takeOfferFundWalletInfo.headline=ทุนการซื้อขายของคุณ
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1466,8 +1468,10 @@ offerDetailsWindow.countryBank=ประเทศของธนาคารข
offerDetailsWindow.commitment=ข้อผูกมัด offerDetailsWindow.commitment=ข้อผูกมัด
offerDetailsWindow.agree=ฉันเห็นด้วย offerDetailsWindow.agree=ฉันเห็นด้วย
offerDetailsWindow.tac=ข้อตกลงและเงื่อนไข offerDetailsWindow.tac=ข้อตกลงและเงื่อนไข
offerDetailsWindow.confirm.maker=ยืนยัน: ยื่นข้อเสนอไปยัง{0} บิทคอยน์ offerDetailsWindow.confirm.maker.buy=ยืนยัน: สร้างข้อเสนอเพื่อซื้อ XMR ด้วย {0}
offerDetailsWindow.confirm.taker=ยืนยัน: รับข้อเสนอไปยัง {0} บิทคอยน์ offerDetailsWindow.confirm.maker.sell=ยืนยัน: สร้างข้อเสนอเพื่อขาย XMR เป็น {0}
offerDetailsWindow.confirm.taker.buy=ยืนยัน: ยอมรับข้อเสนอเพื่อซื้อ XMR ด้วย {0}
offerDetailsWindow.confirm.taker.sell=ยืนยัน: ยอมรับข้อเสนอเพื่อขาย XMR เป็น {0}
offerDetailsWindow.creationDate=วันที่สร้าง offerDetailsWindow.creationDate=วันที่สร้าง
offerDetailsWindow.makersOnion=ที่อยู่ onion ของผู้สร้าง offerDetailsWindow.makersOnion=ที่อยู่ onion ของผู้สร้าง
offerDetailsWindow.challenge=รหัสผ่านสำหรับข้อเสนอ offerDetailsWindow.challenge=รหัสผ่านสำหรับข้อเสนอ

View file

@ -455,12 +455,8 @@ createOffer.amount.prompt=XMR miktarını girin
createOffer.price.prompt=Fiyatı girin createOffer.price.prompt=Fiyatı girin
createOffer.volume.prompt={0} cinsinden miktar girin createOffer.volume.prompt={0} cinsinden miktar girin
createOffer.amountPriceBox.amountDescription={0} XMR miktarı 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.buy.volumeDescription=Harcanacak {0} miktarı
createOffer.amountPriceBox.sell.volumeDescription=Alınacak {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.amountPriceBox.minAmountDescription=Minimum XMR miktarı
createOffer.securityDeposit.prompt=Güvenlik teminatı createOffer.securityDeposit.prompt=Güvenlik teminatı
createOffer.fundsBox.title=Teklifinizi finanse edin 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 createOffer.triggerPrice.invalid.tooHigh=Değer {0}'den düşük olmalıdır
# new entries # new entries
createOffer.placeOfferButton=Gözden Geçir: Teklif ver {0} monero createOffer.placeOfferButton.buy=Gözden Geçir: {0} ile XMR satın almak için teklif oluştur
createOffer.placeOfferButtonCrypto=Gözden Geçir: Teklif ver {0} {1} createOffer.placeOfferButton.sell=Gözden Geçir: {0} karşılığında XMR satmak için teklif oluştur
createOffer.createOfferFundWalletInfo.headline=Teklifinizi finanse edin createOffer.createOfferFundWalletInfo.headline=Teklifinizi finanse edin
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Ticaret miktarı: {0} \n 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} takeOffer.error.message=Teklif alımı sırasında bir hata oluştu.\n\n{0}
# new entries # new entries
takeOffer.takeOfferButton=İncele: {0} monero için teklifi al takeOffer.takeOfferButton.buy=Gözden Geçir: {0} ile XMR satın alma teklifini kabul et
takeOffer.takeOfferButtonCrypto=İncele: {0} {1} için teklifi al 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.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 takeOffer.takeOfferFundWalletInfo.headline=İşleminizi finanse edin
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1960,10 +1956,10 @@ offerDetailsWindow.countryBank=Yapıcı'nın banka ülkesi
offerDetailsWindow.commitment=Taahhüt offerDetailsWindow.commitment=Taahhüt
offerDetailsWindow.agree=Kabul ediyorum offerDetailsWindow.agree=Kabul ediyorum
offerDetailsWindow.tac=Şartlar ve koşullar offerDetailsWindow.tac=Şartlar ve koşullar
offerDetailsWindow.confirm.maker=Onayla: {0} monero teklifi yerleştir offerDetailsWindow.confirm.maker.buy=Onayla: {0} ile XMR satın almak için teklif oluştur
offerDetailsWindow.confirm.makerCrypto=Onayla: {0} {1} teklifi yerleştir offerDetailsWindow.confirm.maker.sell=Onayla: {0} karşılığında XMR satmak için teklif oluştur
offerDetailsWindow.confirm.taker=Onayla: {0} monero teklifi al offerDetailsWindow.confirm.taker.buy=Onayla: {0} ile XMR satın alma teklifini kabul et
offerDetailsWindow.confirm.takerCrypto=Onayla: {0} {1} teklifi al offerDetailsWindow.confirm.taker.sell=Onayla: {0} karşılığında XMR satma teklifini kabul et
offerDetailsWindow.creationDate=Oluşturma tarihi offerDetailsWindow.creationDate=Oluşturma tarihi
offerDetailsWindow.makersOnion=Yapıcı'nın onion adresi offerDetailsWindow.makersOnion=Yapıcı'nın onion adresi
offerDetailsWindow.challenge=Teklif şifresi 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} createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries # 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 createOffer.createOfferFundWalletInfo.headline=Nộp tiền cho báo giá của bạn
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Khoản tiền giao dịch: {0} \n 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} takeOffer.error.message=Có lỗi xảy ra khi nhận báo giá.\n\n{0}
# new entries # 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.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 takeOffer.takeOfferFundWalletInfo.headline=Nộp tiền cho giao dịch của bạn
# suppress inspection "TrailingSpacesInProperty" # 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.commitment=Cam kết
offerDetailsWindow.agree=Tôi đồng ý offerDetailsWindow.agree=Tôi đồng ý
offerDetailsWindow.tac=Điều khoản và điều kiện 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.maker.buy=Xác nhận: Tạo đề nghị mua XMR bằng {0}
offerDetailsWindow.confirm.taker=Xác nhận: Nhận chào giáo cho {0} monero 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.creationDate=Ngày tạo
offerDetailsWindow.makersOnion=Địa chỉ onion của người tạo offerDetailsWindow.makersOnion=Địa chỉ onion của người tạo
offerDetailsWindow.challenge=Mã bảo vệ giao dịch 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} createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries # new entries
createOffer.placeOfferButton=复审:报价挂单 {0} 比特币 createOffer.placeOfferButton.buy=审核:创建以 {0} 买入 XMR 的报价
createOffer.placeOfferButton.sell=审核:创建以 {0} 卖出 XMR 的报价
createOffer.createOfferFundWalletInfo.headline=为您的报价充值 createOffer.createOfferFundWalletInfo.headline=为您的报价充值
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- 交易数量:{0}\n createOffer.createOfferFundWalletInfo.tradeAmount=- 交易数量:{0}\n
@ -520,7 +521,8 @@ takeOffer.success.info=你可以在“业务/未完成交易”页面内查看
takeOffer.error.message=下单时发生了一个错误。\n\n{0} takeOffer.error.message=下单时发生了一个错误。\n\n{0}
# new entries # new entries
takeOffer.takeOfferButton=复审:报价下单 {0} 比特币 takeOffer.takeOfferButton.buy=审核:接受以 {0} 买入 XMR 的报价
takeOffer.takeOfferButton.sell=审核:接受以 {0} 卖出 XMR 的报价
takeOffer.noPriceFeedAvailable=您不能对这笔报价下单,因为它使用交易所价格百分比定价,但是您没有获得可用的价格。 takeOffer.noPriceFeedAvailable=您不能对这笔报价下单,因为它使用交易所价格百分比定价,但是您没有获得可用的价格。
takeOffer.takeOfferFundWalletInfo.headline=为交易充值 takeOffer.takeOfferFundWalletInfo.headline=为交易充值
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1470,8 +1472,10 @@ offerDetailsWindow.countryBank=卖家银行所在国家或地区
offerDetailsWindow.commitment=承诺 offerDetailsWindow.commitment=承诺
offerDetailsWindow.agree=我同意 offerDetailsWindow.agree=我同意
offerDetailsWindow.tac=条款和条件 offerDetailsWindow.tac=条款和条件
offerDetailsWindow.confirm.maker=确定:发布报价 {0} 比特币 offerDetailsWindow.confirm.maker.buy=确认:创建以 {0} 买入 XMR 的报价
offerDetailsWindow.confirm.taker=确定:下单买入 {0} 比特币 offerDetailsWindow.confirm.maker.sell=确认:创建以 {0} 卖出 XMR 的报价
offerDetailsWindow.confirm.taker.buy=确认:接受以 {0} 买入 XMR 的报价
offerDetailsWindow.confirm.taker.sell=确认:接受以 {0} 卖出 XMR 的报价
offerDetailsWindow.creationDate=创建时间 offerDetailsWindow.creationDate=创建时间
offerDetailsWindow.makersOnion=卖家的匿名地址 offerDetailsWindow.makersOnion=卖家的匿名地址
offerDetailsWindow.challenge=提供密码 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} createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries # new entries
createOffer.placeOfferButton=複審:報價掛單 {0} 比特幣 createOffer.placeOfferButton.buy=審核:建立以 {0} 買入 XMR 的報價
createOffer.placeOfferButton.sell=審核:建立以 {0} 賣出 XMR 的報價
createOffer.createOfferFundWalletInfo.headline=為您的報價充值 createOffer.createOfferFundWalletInfo.headline=為您的報價充值
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- 交易數量:{0}\n createOffer.createOfferFundWalletInfo.tradeAmount=- 交易數量:{0}\n
@ -520,7 +521,8 @@ takeOffer.success.info=你可以在“業務/未完成交易”頁面內查看
takeOffer.error.message=下單時發生了一個錯誤。\n\n{0} takeOffer.error.message=下單時發生了一個錯誤。\n\n{0}
# new entries # new entries
takeOffer.takeOfferButton=複審:報價下單 {0} 比特幣 takeOffer.takeOfferButton.buy=審核:接受以 {0} 買入 XMR 的報價
takeOffer.takeOfferButton.sell=審核:接受以 {0} 賣出 XMR 的報價
takeOffer.noPriceFeedAvailable=您不能對這筆報價下單,因為它使用交易所價格百分比定價,但是您沒有獲得可用的價格。 takeOffer.noPriceFeedAvailable=您不能對這筆報價下單,因為它使用交易所價格百分比定價,但是您沒有獲得可用的價格。
takeOffer.takeOfferFundWalletInfo.headline=為交易充值 takeOffer.takeOfferFundWalletInfo.headline=為交易充值
# suppress inspection "TrailingSpacesInProperty" # suppress inspection "TrailingSpacesInProperty"
@ -1470,8 +1472,10 @@ offerDetailsWindow.countryBank=賣家銀行所在國家或地區
offerDetailsWindow.commitment=承諾 offerDetailsWindow.commitment=承諾
offerDetailsWindow.agree=我同意 offerDetailsWindow.agree=我同意
offerDetailsWindow.tac=條款和條件 offerDetailsWindow.tac=條款和條件
offerDetailsWindow.confirm.maker=確定:發佈報價 {0} 比特幣 offerDetailsWindow.confirm.maker.buy=確認:建立以 {0} 買入 XMR 的報價
offerDetailsWindow.confirm.taker=確定:下單買入 {0} 比特幣 offerDetailsWindow.confirm.maker.sell=確認:建立以 {0} 賣出 XMR 的報價
offerDetailsWindow.confirm.taker.buy=確認:接受以 {0} 買入 XMR 的報價
offerDetailsWindow.confirm.taker.sell=確認:接受以 {0} 賣出 XMR 的報價
offerDetailsWindow.creationDate=創建時間 offerDetailsWindow.creationDate=創建時間
offerDetailsWindow.makersOnion=賣家的匿名地址 offerDetailsWindow.makersOnion=賣家的匿名地址
offerDetailsWindow.challenge=提供密碼 offerDetailsWindow.challenge=提供密碼

View file

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

View file

@ -34,7 +34,7 @@ public class ReceiptPredicatesTest {
@Test @Test
public void testIsMatchingCurrency() { public void testIsMatchingCurrency() {
Offer offer = mock(Offer.class); Offer offer = mock(Offer.class);
when(offer.getCurrencyCode()).thenReturn("USD"); when(offer.getCounterCurrencyCode()).thenReturn("USD");
PaymentAccount account = mock(PaymentAccount.class); PaymentAccount account = mock(PaymentAccount.class);
when(account.getTradeCurrencies()).thenReturn(Lists.newArrayList( 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.MarketPriceRequest;
import haveno.proto.grpc.MarketPricesReply; import haveno.proto.grpc.MarketPricesReply;
import haveno.proto.grpc.MarketPricesRequest; import haveno.proto.grpc.MarketPricesRequest;
import static haveno.proto.grpc.PriceGrpc.PriceImplBase;
import static haveno.proto.grpc.PriceGrpc.getGetMarketPriceMethod; import static haveno.proto.grpc.PriceGrpc.getGetMarketPriceMethod;
import haveno.proto.grpc.PriceGrpc.PriceImplBase; import haveno.proto.grpc.PriceGrpc.PriceImplBase;
import io.grpc.ServerInterceptor; import io.grpc.ServerInterceptor;
@ -57,8 +56,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Optional; 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 static java.util.concurrent.TimeUnit.SECONDS;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;

View file

@ -198,6 +198,6 @@ public class PeerInfoIconTrading extends PeerInfoIcon {
Offer offerToCheck = Trade != null ? Trade.getOffer() : offer; Offer offerToCheck = Trade != null ? Trade.getOffer() : offer;
return offerToCheck != null && 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(); StringBuilder sb = new StringBuilder();
sb.append("Offer ID: ").append(offer.getId()).append("\n") sb.append("Offer ID: ").append(offer.getId()).append("\n")
.append("Type: ").append(offer.getDirection().name()).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("Price: ").append(FormattingUtils.formatPrice(offer.getPrice())).append("\n")
.append("Amount: ").append(DisplayUtils.formatAmount(offer, formatter)).append(" BTC\n") .append("Amount: ").append(DisplayUtils.formatAmount(offer, formatter)).append(" BTC\n")
.append("Payment method: ").append(Res.get(offer.getPaymentMethod().getId())).append("\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 viewBaseCurrencyCode = Res.getBaseCurrencyCode();
String viewPriceCurrencyCode = model.isCrypto() ? Res.getBaseCurrencyCode() : code; String viewPriceCurrencyCode = code;
sellHeaderLabel.setText(Res.get("market.offerBook.sellOffersHeaderLabel", viewBaseCurrencyCode)); sellHeaderLabel.setText(Res.get("market.offerBook.sellOffersHeaderLabel", viewBaseCurrencyCode));
sellButton.updateText(Res.get("shared.sellCurrency", viewBaseCurrencyCode)); sellButton.updateText(Res.get("shared.sellCurrency", viewBaseCurrencyCode));
sellButton.setGraphic(GUIUtil.getCurrencyIconWithBorder(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"); sellButton.setId("sell-button-big");
buyHeaderLabel.setText(Res.get("market.offerBook.buyOffersHeaderLabel", viewBaseCurrencyCode)); buyHeaderLabel.setText(Res.get("market.offerBook.buyOffersHeaderLabel", viewBaseCurrencyCode));
buyButton.updateText(Res.get( "shared.buyCurrency", viewBaseCurrencyCode)); buyButton.updateText(Res.get( "shared.buyCurrency", viewBaseCurrencyCode));
buyButton.setGraphic(GUIUtil.getCurrencyIconWithBorder(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"); buyButton.setId("buy-button-big");
priceColumnLabel.set(Res.get("shared.priceWithCur", viewPriceCurrencyCode)); priceColumnLabel.set(Res.get("shared.priceWithCur", viewPriceCurrencyCode));
@ -358,8 +358,8 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
private synchronized void updateChartData() { private synchronized void updateChartData() {
// update volume headers // update volume headers
Volume volumeSell = model.getTotalVolume(model.isCrypto() ? OfferDirection.BUY : OfferDirection.SELL); Volume volumeSell = model.getTotalVolume(OfferDirection.SELL);
Volume volumeBuy = model.getTotalVolume(model.isCrypto() ? OfferDirection.SELL : OfferDirection.BUY); Volume volumeBuy = model.getTotalVolume(OfferDirection.BUY);
String formattedVolumeSell = volumeSell == null ? null : VolumeUtil.formatVolume(volumeSell); String formattedVolumeSell = volumeSell == null ? null : VolumeUtil.formatVolume(volumeSell);
String formattedVolumeBuy = volumeBuy == null ? null : VolumeUtil.formatVolume(volumeBuy); String formattedVolumeBuy = volumeBuy == null ? null : VolumeUtil.formatVolume(volumeBuy);
if (model.getSellData().isEmpty()) formattedVolumeSell = "0.0"; 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 + ")")); volumeBuyColumnLabel.set(Res.get("offerbook.volumeTotal", model.getCurrencyCode(), formattedVolumeBuy == null ? "" : "(" + formattedVolumeBuy + ")"));
// update amount headers // update amount headers
amountSellColumnLabel.set(Res.get("offerbook.XMRTotal", "" + model.getTotalAmount(model.isCrypto() ? OfferDirection.BUY : OfferDirection.SELL))); amountSellColumnLabel.set(Res.get("offerbook.XMRTotal", "" + model.getTotalAmount(OfferDirection.SELL)));
amountBuyColumnLabel.set(Res.get("offerbook.XMRTotal", "" + model.getTotalAmount(model.isCrypto() ? OfferDirection.SELL : OfferDirection.BUY))); amountBuyColumnLabel.set(Res.get("offerbook.XMRTotal", "" + model.getTotalAmount(OfferDirection.BUY)));
seriesSell.getData().clear(); seriesSell.getData().clear();
seriesBuy.getData().clear(); seriesBuy.getData().clear();

View file

@ -130,7 +130,7 @@ class OfferBookChartViewModel extends ActivatableViewModel {
list.addAll(c.getAddedSubList()); list.addAll(c.getAddedSubList());
if (list.stream() if (list.stream()
.map(OfferBookListItem::getOffer) .map(OfferBookListItem::getOffer)
.anyMatch(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode()))) .anyMatch(e -> e.getCounterCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())))
updateChartData(); updateChartData();
} }
@ -156,7 +156,7 @@ class OfferBookChartViewModel extends ActivatableViewModel {
synchronized (offerBookListItems) { synchronized (offerBookListItems) {
List<TradeCurrency> tradeCurrencyList = offerBookListItems.stream() List<TradeCurrency> tradeCurrencyList = offerBookListItems.stream()
.map(e -> { .map(e -> {
String currencyCode = e.getOffer().getCurrencyCode(); String currencyCode = e.getOffer().getCounterCurrencyCode();
Optional<TradeCurrency> tradeCurrencyOptional = CurrencyUtil.getTradeCurrency(currencyCode); Optional<TradeCurrency> tradeCurrencyOptional = CurrencyUtil.getTradeCurrency(currencyCode);
return tradeCurrencyOptional.orElse(null); return tradeCurrencyOptional.orElse(null);
}) })
@ -221,7 +221,7 @@ class OfferBookChartViewModel extends ActivatableViewModel {
synchronized (offerBookListItems) { synchronized (offerBookListItems) {
List<Offer> offerList = offerBookListItems.stream() List<Offer> offerList = offerBookListItems.stream()
.map(OfferBookListItem::getOffer) .map(OfferBookListItem::getOffer)
.filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode()) .filter(e -> e.getCounterCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
&& e.getDirection().equals(direction)) && e.getDirection().equals(direction))
.collect(Collectors.toList()); .collect(Collectors.toList());
BigInteger sum = BigInteger.ZERO; BigInteger sum = BigInteger.ZERO;
@ -234,7 +234,7 @@ class OfferBookChartViewModel extends ActivatableViewModel {
synchronized (offerBookListItems) { synchronized (offerBookListItems) {
List<Volume> volumes = offerBookListItems.stream() List<Volume> volumes = offerBookListItems.stream()
.map(OfferBookListItem::getOffer) .map(OfferBookListItem::getOffer)
.filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode()) .filter(e -> e.getCounterCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
&& e.getDirection().equals(direction)) && e.getDirection().equals(direction))
.map(Offer::getVolume) .map(Offer::getVolume)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -300,13 +300,13 @@ class OfferBookChartViewModel extends ActivatableViewModel {
} }
public int getMaxNumberOfPriceZeroDecimalsToColorize(Offer offer) { public int getMaxNumberOfPriceZeroDecimalsToColorize(Offer offer) {
return CurrencyUtil.isVolumeRoundedToNearestUnit(offer.getCurrencyCode()) return CurrencyUtil.isVolumeRoundedToNearestUnit(offer.getCounterCurrencyCode())
? GUIUtil.NUM_DECIMALS_UNIT ? GUIUtil.NUM_DECIMALS_UNIT
: GUIUtil.NUM_DECIMALS_PRECISE; : GUIUtil.NUM_DECIMALS_PRECISE;
} }
public int getZeroDecimalsForPrice(Offer offer) { public int getZeroDecimalsForPrice(Offer offer) {
return CurrencyUtil.isPricePrecise(offer.getCurrencyCode()) return CurrencyUtil.isPricePrecise(offer.getCounterCurrencyCode())
? GUIUtil.NUM_DECIMALS_PRECISE ? GUIUtil.NUM_DECIMALS_PRECISE
: GUIUtil.NUM_DECIMALS_PRICE_LESS_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. // 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())); 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. // Offer amounts are used for the secondary sort. They are sorted from high to low.
Comparator<Offer> offerAmountComparator = Comparator.comparing(Offer::getAmount).reversed(); Comparator<Offer> offerAmountComparator = Comparator.comparing(Offer::getAmount).reversed();
@ -367,11 +360,11 @@ class OfferBookChartViewModel extends ActivatableViewModel {
offerPriceComparator offerPriceComparator
.thenComparing(offerAmountComparator); .thenComparing(offerAmountComparator);
OfferDirection buyOfferDirection = isCrypto ? OfferDirection.SELL : OfferDirection.BUY; OfferDirection buyOfferDirection = OfferDirection.BUY;
List<Offer> allBuyOffers = offerBookListItems.stream() List<Offer> allBuyOffers = offerBookListItems.stream()
.map(OfferBookListItem::getOffer) .map(OfferBookListItem::getOffer)
.filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode()) .filter(e -> e.getCounterCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
&& e.getDirection().equals(buyOfferDirection)) && e.getDirection().equals(buyOfferDirection))
.sorted(buyOfferSortComparator) .sorted(buyOfferSortComparator)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -398,11 +391,11 @@ class OfferBookChartViewModel extends ActivatableViewModel {
buildChartAndTableEntries(allBuyOffers, OfferDirection.BUY, buyData, topBuyOfferList); buildChartAndTableEntries(allBuyOffers, OfferDirection.BUY, buyData, topBuyOfferList);
OfferDirection sellOfferDirection = isCrypto ? OfferDirection.BUY : OfferDirection.SELL; OfferDirection sellOfferDirection = OfferDirection.SELL;
List<Offer> allSellOffers = offerBookListItems.stream() List<Offer> allSellOffers = offerBookListItems.stream()
.map(OfferBookListItem::getOffer) .map(OfferBookListItem::getOffer)
.filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode()) .filter(e -> e.getCounterCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode())
&& e.getDirection().equals(sellOfferDirection)) && e.getDirection().equals(sellOfferDirection))
.sorted(sellOfferSortComparator) .sorted(sellOfferSortComparator)
.collect(Collectors.toList()); .collect(Collectors.toList());

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -100,7 +100,7 @@ public class OfferViewUtil {
} }
public static boolean isShownAsSellOffer(Offer offer) { 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) { public static boolean isShownAsSellOffer(TradeCurrency tradeCurrency, OfferDirection direction) {

View file

@ -106,9 +106,9 @@ public class CryptoOfferBookViewModel extends OfferBookViewModel {
return offerBookListItem -> { return offerBookListItem -> {
Offer offer = offerBookListItem.getOffer(); 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 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() || (showAllTradeCurrenciesProperty.get() ||
offer.getCurrencyCode().equals(selectedTradeCurrency.getCode())); offer.getCounterCurrencyCode().equals(selectedTradeCurrency.getCode()));
boolean paymentMethodResult = showAllPaymentMethods || boolean paymentMethodResult = showAllPaymentMethods ||
offer.getPaymentMethod().equals(selectedPaymentMethod); offer.getPaymentMethod().equals(selectedPaymentMethod);
boolean notMyOfferOrShowMyOffersActivated = !isMyOffer(offerBookListItem.getOffer()) || preferences.isShowOwnOffersInOfferBook(); boolean notMyOfferOrShowMyOffersActivated = !isMyOffer(offerBookListItem.getOffer()) || preferences.isShowOwnOffersInOfferBook();

View file

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

View file

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

View file

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

View file

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

View file

@ -448,7 +448,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
Volume offerVolume = offer.getVolume(); Volume offerVolume = offer.getVolume();
Volume minOfferVolume = offer.getMinVolume(); Volume minOfferVolume = offer.getMinVolume();
if (offerVolume != null && minOfferVolume != null) { if (offerVolume != null && minOfferVolume != null) {
String postFix = appendCurrencyCode ? " " + offer.getCurrencyCode() : ""; String postFix = appendCurrencyCode ? " " + offer.getCounterCurrencyCode() : "";
decimalAligned = decimalAligned && !showAllTradeCurrenciesProperty.get(); decimalAligned = decimalAligned && !showAllTradeCurrenciesProperty.get();
return VolumeUtil.formatVolume(offer, decimalAligned, maxPlacesForVolume.get()) + postFix; return VolumeUtil.formatVolume(offer, decimalAligned, maxPlacesForVolume.get()) + postFix;
} else { } else {
@ -457,7 +457,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
} }
int getNumberOfDecimalsForVolume(OfferBookListItem item) { 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) { String getPaymentMethod(OfferBookListItem item) {
@ -484,14 +484,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
if (item != null) { if (item != null) {
Offer offer = item.getOffer(); Offer offer = item.getOffer();
result = Res.getWithCol("shared.paymentMethod") + " " + Res.get(offer.getPaymentMethod().getId()); result = Res.getWithCol("shared.paymentMethod") + " " + Res.get(offer.getPaymentMethod().getId());
result += "\n" + Res.getWithCol("shared.currency") + " " + CurrencyUtil.getNameAndCode(offer.getCurrencyCode()); result += "\n" + Res.getWithCol("shared.currency") + " " + CurrencyUtil.getNameAndCode(offer.getCounterCurrencyCode());
if (offer.isXmr()) {
String isAutoConf = offer.isXmrAutoConf() ?
Res.get("shared.yes") :
Res.get("shared.no");
result += "\n" + Res.getWithCol("offerbook.xmrAutoConf") + " " + isAutoConf;
}
String countryCode = offer.getCountryCode(); String countryCode = offer.getCountryCode();
if (isF2F(offer)) { if (isF2F(offer)) {
@ -538,7 +531,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
} }
String getDirectionLabelTooltip(Offer offer) { String getDirectionLabelTooltip(Offer offer) {
return getDirectionWithCodeDetailed(offer.getMirroredDirection(), offer.getCurrencyCode()); return getDirectionWithCodeDetailed(offer.getMirroredDirection(), offer.getCounterCurrencyCode());
} }
Optional<PaymentAccount> getMostMaturePaymentAccountForOffer(Offer offer) { Optional<PaymentAccount> getMostMaturePaymentAccountForOffer(Offer offer) {
@ -623,9 +616,9 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
// filter currencies // filter currencies
nextPredicate = nextPredicate.or(offerBookListItem -> { 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()) || 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()); 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) { 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);
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);
} }
public String formatDepositString(BigInteger deposit, long amount) { public String formatDepositString(BigInteger deposit, long amount) {

View file

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

View file

@ -146,7 +146,7 @@ class TakeOfferDataModel extends OfferDataModel {
// feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx()); // feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx());
if (isTabSelected) if (isTabSelected)
priceFeedService.setCurrencyCode(offer.getCurrencyCode()); priceFeedService.setCurrencyCode(offer.getCounterCurrencyCode());
if (canTakeOffer()) { if (canTakeOffer()) {
tradeManager.checkOfferAvailability(offer, tradeManager.checkOfferAvailability(offer,
@ -204,7 +204,7 @@ class TakeOfferDataModel extends OfferDataModel {
offer.resetState(); 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. // 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) { void onTabSelected(boolean isSelected) {
this.isTabSelected = isSelected; this.isTabSelected = isSelected;
if (isTabSelected) if (isTabSelected)
priceFeedService.setCurrencyCode(offer.getCurrencyCode()); priceFeedService.setCurrencyCode(offer.getCounterCurrencyCode());
} }
public void onClose(boolean removeOffer) { public void onClose(boolean removeOffer) {
@ -259,7 +259,7 @@ class TakeOfferDataModel extends OfferDataModel {
fundsNeededForTrade = fundsNeededForTrade.add(amount.get()); fundsNeededForTrade = fundsNeededForTrade.add(amount.get());
String errorMsg = null; String errorMsg = null;
if (filterManager.isCurrencyBanned(offer.getCurrencyCode())) { if (filterManager.isCurrencyBanned(offer.getCounterCurrencyCode())) {
errorMsg = Res.get("offerbook.warning.currencyBanned"); errorMsg = Res.get("offerbook.warning.currencyBanned");
} else if (filterManager.isPaymentMethodBanned(offer.getPaymentMethod())) { } else if (filterManager.isPaymentMethodBanned(offer.getPaymentMethod())) {
errorMsg = Res.get("offerbook.warning.paymentMethodBanned"); errorMsg = Res.get("offerbook.warning.paymentMethodBanned");
@ -501,11 +501,11 @@ class TakeOfferDataModel extends OfferDataModel {
} }
public String getCurrencyCode() { public String getCurrencyCode() {
return offer.getCurrencyCode(); return offer.getCounterCurrencyCode();
} }
public String getCurrencyNameAndCode() { public String getCurrencyNameAndCode() {
return CurrencyUtil.getNameByCode(offer.getCurrencyCode()); return CurrencyUtil.getNameByCode(offer.getCounterCurrencyCode());
} }
@NotNull @NotNull

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -68,14 +68,14 @@ class FailedTradesViewModel extends ActivatableWithDataModel<FailedTradesDataMod
} }
String getDirectionLabel(FailedTradesListItem item) { 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) { String getMarketLabel(FailedTradesListItem item) {
if ((item == null)) if ((item == null))
return ""; return "";
return CurrencyUtil.getCurrencyPair(item.getTrade().getOffer().getCurrencyCode()); return CurrencyUtil.getCurrencyPair(item.getTrade().getOffer().getCounterCurrencyCode());
} }
String getDate(FailedTradesListItem item) { String getDate(FailedTradesListItem item) {

View file

@ -95,6 +95,6 @@ class OpenOffersDataModel extends ActivatableDataModel {
} }
boolean isTriggered(OpenOffer openOffer) { 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) { 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) { String getDirectionLabel(OpenOfferListItem item) {
if ((item == null)) if ((item == null))
return ""; 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) { String getMarketLabel(OpenOfferListItem item) {
if ((item == null)) if ((item == null))
return ""; return "";
return CurrencyUtil.getCurrencyPair(item.getOffer().getCurrencyCode()); return CurrencyUtil.getCurrencyPair(item.getOffer().getCounterCurrencyCode());
} }
String getPaymentMethod(OpenOfferListItem item) { String getPaymentMethod(OpenOfferListItem item) {
@ -168,7 +168,7 @@ class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel>
if (!offer.isUseMarketBasedPrice() || triggerPrice <= 0) { if (!offer.isUseMarketBasedPrice() || triggerPrice <= 0) {
return Res.get("shared.na"); return Res.get("shared.na");
} else { } 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() { public String getMarketDescription() {
return getCurrencyPair(trade.getOffer().getCurrencyCode()); return getCurrencyPair(trade.getOffer().getCounterCurrencyCode());
} }
@Override @Override

View file

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

View file

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

View file

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