mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-08-04 04:34:26 -04:00
support goldback (XGB)
This commit is contained in:
parent
6a49fffb38
commit
9c2308f7a3
23 changed files with 135 additions and 121 deletions
|
@ -56,6 +56,14 @@ public class CurrencyUtil {
|
|||
|
||||
private static String baseCurrencyCode = "XMR";
|
||||
|
||||
private static List<TraditionalCurrency> getTraditionalNonFiatCurrencies() {
|
||||
return Arrays.asList(
|
||||
new TraditionalCurrency("XAG", "Gold"),
|
||||
new TraditionalCurrency("XAU", "Silver"),
|
||||
new TraditionalCurrency("XGB", "Goldback")
|
||||
);
|
||||
}
|
||||
|
||||
// Calls to isTraditionalCurrency and isCryptoCurrency are very frequent so we use a cache of the results.
|
||||
// The main improvement was already achieved with using memoize for the source maps, but
|
||||
// the caching still reduces performance costs by about 20% for isCryptoCurrency (1752 ms vs 2121 ms) and about 50%
|
||||
|
@ -101,7 +109,7 @@ public class CurrencyUtil {
|
|||
|
||||
public static Collection<TraditionalCurrency> getAllSortedTraditionalCurrencies(Comparator comparator) {
|
||||
return (List<TraditionalCurrency>) getAllSortedTraditionalCurrencies().stream()
|
||||
.sorted(comparator) // sorted by comparator param
|
||||
.sorted(comparator)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
@ -109,7 +117,7 @@ public class CurrencyUtil {
|
|||
List<TraditionalCurrency> currencies = CountryUtil.getAllCountries().stream()
|
||||
.map(country -> getCurrencyByCountryCode(country.code))
|
||||
.collect(Collectors.toList());
|
||||
for (String isoCode : nonFiatIsoCodes) currencies.add(new TraditionalCurrency(Currency.getInstance(isoCode)));
|
||||
currencies.addAll(getTraditionalNonFiatCurrencies());
|
||||
return currencies.stream().sorted(TradeCurrency::compareTo)
|
||||
.distinct()
|
||||
.collect(Collectors.toMap(TradeCurrency::getCode, Function.identity(), (x, y) -> x, LinkedHashMap::new));
|
||||
|
@ -131,12 +139,14 @@ public class CurrencyUtil {
|
|||
|
||||
public static List<TraditionalCurrency> getMainTraditionalCurrencies() {
|
||||
List<TraditionalCurrency> list = getMainFiatCurrencies();
|
||||
for (String isoCode : nonFiatIsoCodes) list.add(new TraditionalCurrency(isoCode));
|
||||
list.addAll(getTraditionalNonFiatCurrencies());
|
||||
postProcessTraditionalCurrenciesList(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<String> nonFiatIsoCodes = Arrays.asList("XAG", "XAU");
|
||||
private static boolean isTraditionalNonFiatCurrency(String currencyCode) {
|
||||
return getTraditionalNonFiatCurrencies().stream().anyMatch(c -> c.getCode().equals(currencyCode));
|
||||
}
|
||||
|
||||
private static void postProcessTraditionalCurrenciesList(List<TraditionalCurrency> list) {
|
||||
list.sort(TradeCurrency::compareTo);
|
||||
|
@ -209,7 +219,7 @@ public class CurrencyUtil {
|
|||
|
||||
public static boolean isFiatCurrency(String currencyCode) {
|
||||
if (!isTraditionalCurrency(currencyCode)) return false;
|
||||
if ("XAG".equalsIgnoreCase(currencyCode) || "XAU".equalsIgnoreCase(currencyCode)) return false;
|
||||
if (isTraditionalNonFiatCurrency(currencyCode)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -224,7 +234,7 @@ public class CurrencyUtil {
|
|||
boolean isTraditionalCurrency = currencyCode != null
|
||||
&& !currencyCode.isEmpty()
|
||||
&& !isCryptoCurrency(currencyCode)
|
||||
&& Currency.getInstance(currencyCode) != null;
|
||||
&& (isTraditionalNonFiatCurrency(currencyCode) || Currency.getInstance(currencyCode) != null);
|
||||
|
||||
if (currencyCode != null) {
|
||||
isTraditionalCurrencyMap.put(currencyCode, isTraditionalCurrency);
|
||||
|
@ -237,6 +247,17 @@ public class CurrencyUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean isVolumeRoundedToNearestUnit(String currencyCode) {
|
||||
return isFiatCurrency(currencyCode) ||
|
||||
"XGB".equals(currencyCode.toUpperCase());
|
||||
}
|
||||
|
||||
public static boolean isPricePrecise(String currencyCode) {
|
||||
return isCryptoCurrency(currencyCode) ||
|
||||
"XAU".equals(currencyCode.toUpperCase()) ||
|
||||
"XAG".equals(currencyCode.toUpperCase());
|
||||
}
|
||||
|
||||
public static Optional<TraditionalCurrency> getTraditionalCurrency(String currencyCode) {
|
||||
return Optional.ofNullable(traditionalCurrencyMapSupplier.get().get(currencyCode));
|
||||
}
|
||||
|
|
|
@ -30,15 +30,22 @@ import java.util.Locale;
|
|||
@ToString
|
||||
@Getter
|
||||
public final class TraditionalCurrency extends TradeCurrency {
|
||||
|
||||
// http://boschista.deviantart.com/journal/Cool-ASCII-Symbols-214218618
|
||||
private final static String PREFIX = "★ ";
|
||||
|
||||
private final Currency currency;
|
||||
|
||||
public TraditionalCurrency(String currencyCode) {
|
||||
this(Currency.getInstance(currencyCode), getLocale());
|
||||
}
|
||||
|
||||
public TraditionalCurrency(String currencyCode, String name) {
|
||||
super(currencyCode, name);
|
||||
}
|
||||
|
||||
public TraditionalCurrency(TraditionalCurrency currency) {
|
||||
this(currency.getCode(), currency.getName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public TraditionalCurrency(Currency currency) {
|
||||
this(currency, getLocale());
|
||||
|
@ -47,7 +54,6 @@ public final class TraditionalCurrency extends TradeCurrency {
|
|||
@SuppressWarnings("WeakerAccess")
|
||||
public TraditionalCurrency(Currency currency, Locale locale) {
|
||||
super(currency.getCurrencyCode(), currency.getDisplayName(locale));
|
||||
this.currency = currency;
|
||||
}
|
||||
|
||||
|
||||
|
@ -57,15 +63,15 @@ public final class TraditionalCurrency extends TradeCurrency {
|
|||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
protobuf.Currency.Builder currencyBuilder = protobuf.Currency.newBuilder().setCurrencyCode(currency.getCurrencyCode());
|
||||
protobuf.TraditionalCurrency.Builder traditionalCurrencyBuilder = protobuf.TraditionalCurrency.newBuilder().setCurrency(currencyBuilder);
|
||||
return getTradeCurrencyBuilder()
|
||||
.setTraditionalCurrency(traditionalCurrencyBuilder)
|
||||
.setCode(code)
|
||||
.setName(name)
|
||||
.setTraditionalCurrency(protobuf.TraditionalCurrency.newBuilder())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static TraditionalCurrency fromProto(protobuf.TradeCurrency proto) {
|
||||
return new TraditionalCurrency(proto.getCode());
|
||||
return new TraditionalCurrency(proto.getCode(), proto.getName());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -632,7 +632,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
|
||||
public void setTraditionalCurrencies(List<TraditionalCurrency> currencies) {
|
||||
traditionalCurrenciesAsObservable.setAll(currencies.stream()
|
||||
.map(traditionalCurrency -> new TraditionalCurrency(traditionalCurrency.getCurrency()))
|
||||
.map(traditionalCurrency -> new TraditionalCurrency(traditionalCurrency))
|
||||
.distinct().collect(Collectors.toList()));
|
||||
requestPersistence();
|
||||
}
|
||||
|
|
|
@ -29,9 +29,8 @@ public class FormattingUtils {
|
|||
|
||||
public final static String RANGE_SEPARATOR = " - ";
|
||||
|
||||
private static final MonetaryFormat fiatPriceFormat = new MonetaryFormat().shift(0).minDecimals(4).repeatOptionalDecimals(0, 0);
|
||||
private static final MonetaryFormat nonFiatPriceFormat = new MonetaryFormat().shift(0).minDecimals(8).repeatOptionalDecimals(0, 0);
|
||||
private static final MonetaryFormat traditionalFormat = new MonetaryFormat().shift(0).minDecimals(TraditionalMoney.SMALLEST_UNIT_EXPONENT).repeatOptionalDecimals(0, 0);
|
||||
private static final MonetaryFormat priceFormat4Decimals = new MonetaryFormat().shift(0).minDecimals(4).repeatOptionalDecimals(0, 0);
|
||||
private static final MonetaryFormat priceFormat8Decimals = new MonetaryFormat().shift(0).minDecimals(8).repeatOptionalDecimals(0, 0);
|
||||
private static final MonetaryFormat cryptoFormat = new MonetaryFormat().shift(0).minDecimals(CryptoMoney.SMALLEST_UNIT_EXPONENT).repeatOptionalDecimals(0, 0);
|
||||
private static final DecimalFormat decimalFormat = new DecimalFormat("#.#");
|
||||
|
||||
|
@ -293,11 +292,7 @@ public class FormattingUtils {
|
|||
return formattedNumber;
|
||||
}
|
||||
|
||||
public static MonetaryFormat getMonetaryFormat(String currencyCode) {
|
||||
return CurrencyUtil.isTraditionalCurrency(currencyCode) ? traditionalFormat : cryptoFormat;
|
||||
}
|
||||
|
||||
public static MonetaryFormat getPriceMonetaryFormat(String currencyCode) {
|
||||
return CurrencyUtil.isFiatCurrency(currencyCode) ? fiatPriceFormat : nonFiatPriceFormat;
|
||||
return CurrencyUtil.isPricePrecise(currencyCode) ? priceFormat8Decimals : priceFormat4Decimals;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ import haveno.core.provider.price.MarketPrice;
|
|||
import haveno.core.provider.price.PriceFeedService;
|
||||
import haveno.core.trade.statistics.TradeStatisticsManager;
|
||||
import haveno.core.user.Preferences;
|
||||
import haveno.core.util.validation.NonFiatPriceValidator;
|
||||
import haveno.core.util.validation.FiatPriceValidator;
|
||||
import haveno.core.util.validation.AmountValidator8Decimals;
|
||||
import haveno.core.util.validation.AmountValidator4Decimals;
|
||||
import haveno.core.util.validation.InputValidator;
|
||||
import haveno.core.util.validation.MonetaryValidator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -53,21 +53,21 @@ public class PriceUtil {
|
|||
this.priceFeedService = priceFeedService;
|
||||
}
|
||||
|
||||
public static MonetaryValidator getPriceValidator(boolean isFiatCurrency) {
|
||||
return isFiatCurrency ?
|
||||
new FiatPriceValidator() :
|
||||
new NonFiatPriceValidator();
|
||||
public static MonetaryValidator getPriceValidator(String currencyCode) {
|
||||
return CurrencyUtil.isPricePrecise(currencyCode) ?
|
||||
new AmountValidator4Decimals() :
|
||||
new AmountValidator8Decimals();
|
||||
}
|
||||
|
||||
public static InputValidator.ValidationResult isTriggerPriceValid(String triggerPriceAsString,
|
||||
MarketPrice marketPrice,
|
||||
boolean isSellOffer,
|
||||
boolean isFiatCurrency) {
|
||||
String currencyCode) {
|
||||
if (triggerPriceAsString == null || triggerPriceAsString.isEmpty()) {
|
||||
return new InputValidator.ValidationResult(true);
|
||||
}
|
||||
|
||||
InputValidator.ValidationResult result = getPriceValidator(isFiatCurrency).validate(triggerPriceAsString);
|
||||
InputValidator.ValidationResult result = getPriceValidator(currencyCode).validate(triggerPriceAsString);
|
||||
if (!result.isValid) {
|
||||
return result;
|
||||
}
|
||||
|
@ -76,7 +76,8 @@ public class PriceUtil {
|
|||
long marketPriceAsLong = PriceUtil.getMarketPriceAsLong("" + marketPrice.getPrice(), marketPrice.getCurrencyCode());
|
||||
String marketPriceAsString = FormattingUtils.formatMarketPrice(marketPrice.getPrice(), marketPrice.getCurrencyCode());
|
||||
|
||||
if ((isSellOffer && isFiatCurrency) || (!isSellOffer && !isFiatCurrency)) {
|
||||
boolean isCryptoCurrency = CurrencyUtil.isCryptoCurrency(currencyCode);
|
||||
if ((isSellOffer && !isCryptoCurrency) || (!isSellOffer && isCryptoCurrency)) {
|
||||
if (triggerPriceAsLong >= marketPriceAsLong) {
|
||||
return new InputValidator.ValidationResult(false,
|
||||
Res.get("createOffer.triggerPrice.invalid.tooHigh", marketPriceAsString));
|
||||
|
|
|
@ -38,33 +38,33 @@ import java.util.Locale;
|
|||
|
||||
public class VolumeUtil {
|
||||
|
||||
private static final MonetaryFormat FIAT_VOLUME_FORMAT = new MonetaryFormat().shift(0).minDecimals(0).repeatOptionalDecimals(0, 0);
|
||||
private static final MonetaryFormat TRADITIONAL_VOLUME_FORMAT = new MonetaryFormat().shift(0).minDecimals(4).repeatOptionalDecimals(0, 0);
|
||||
private static final MonetaryFormat VOLUME_FORMAT_UNIT = new MonetaryFormat().shift(0).minDecimals(0).repeatOptionalDecimals(0, 0);
|
||||
private static final MonetaryFormat VOLUME_FORMAT_PRECISE = new MonetaryFormat().shift(0).minDecimals(4).repeatOptionalDecimals(0, 0);
|
||||
|
||||
private static double EXPONENT = Math.pow(10, TraditionalMoney.SMALLEST_UNIT_EXPONENT); // 1000000000000 with precision 8
|
||||
|
||||
public static Volume getAdjustedVolume(Volume volumeByAmount, String paymentMethodId) {
|
||||
if (PaymentMethod.isRoundedForAtmCash(paymentMethodId))
|
||||
return VolumeUtil.getRoundedAtmCashVolume(volumeByAmount);
|
||||
else if (CurrencyUtil.isFiatCurrency(volumeByAmount.getCurrencyCode()))
|
||||
return VolumeUtil.getRoundedFiatVolume(volumeByAmount);
|
||||
else if (CurrencyUtil.isVolumeRoundedToNearestUnit(volumeByAmount.getCurrencyCode()))
|
||||
return VolumeUtil.getRoundedVolumeUnit(volumeByAmount);
|
||||
else if (CurrencyUtil.isTraditionalCurrency(volumeByAmount.getCurrencyCode()))
|
||||
return VolumeUtil.getRoundedTraditionalVolume(volumeByAmount);
|
||||
return VolumeUtil.getRoundedVolumePrecise(volumeByAmount);
|
||||
return volumeByAmount;
|
||||
}
|
||||
|
||||
public static Volume getRoundedFiatVolume(Volume volumeByAmount) {
|
||||
// We want to get rounded to 1 unit of the fiat currency, e.g. 1 EUR.
|
||||
return getAdjustedFiatVolume(volumeByAmount, 1);
|
||||
public static Volume getRoundedVolumeUnit(Volume volumeByAmount) {
|
||||
// We want to get rounded to 1 unit of the currency, e.g. 1 EUR.
|
||||
return getAdjustedVolumeUnit(volumeByAmount, 1);
|
||||
}
|
||||
|
||||
private static Volume getRoundedAtmCashVolume(Volume volumeByAmount) {
|
||||
// EUR has precision TraditionalMoney.SMALLEST_UNIT_EXPONENT and we want multiple of 10 so we divide by EXPONENT then
|
||||
// round and multiply with 10
|
||||
return getAdjustedFiatVolume(volumeByAmount, 10);
|
||||
return getAdjustedVolumeUnit(volumeByAmount, 10);
|
||||
}
|
||||
|
||||
public static Volume getRoundedTraditionalVolume(Volume volumeByAmount) {
|
||||
public static Volume getRoundedVolumePrecise(Volume volumeByAmount) {
|
||||
DecimalFormat decimalFormat = new DecimalFormat("#.####");
|
||||
double roundedVolume = Double.parseDouble(decimalFormat.format(Double.parseDouble(volumeByAmount.toString())));
|
||||
return Volume.parse(String.valueOf(roundedVolume), volumeByAmount.getCurrencyCode());
|
||||
|
@ -77,7 +77,7 @@ public class VolumeUtil {
|
|||
* units of 1 EUR, 10 means rounded to 10 EUR.
|
||||
* @return The adjusted Fiat volume
|
||||
*/
|
||||
public static Volume getAdjustedFiatVolume(Volume volumeByAmount, int factor) {
|
||||
public static Volume getAdjustedVolumeUnit(Volume volumeByAmount, int factor) {
|
||||
// Fiat currencies use precision TraditionalMoney.SMALLEST_UNIT_EXPONENT and we want multiple of factor so we divide
|
||||
// by EXPONENT * factor then round and multiply with factor
|
||||
long roundedVolume = Math.round((double) volumeByAmount.getValue() / (EXPONENT * factor)) * factor;
|
||||
|
@ -168,6 +168,6 @@ public class VolumeUtil {
|
|||
}
|
||||
|
||||
private static MonetaryFormat getMonetaryFormat(String currencyCode) {
|
||||
return CurrencyUtil.isFiatCurrency(currencyCode) ? FIAT_VOLUME_FORMAT : TRADITIONAL_VOLUME_FORMAT;
|
||||
return CurrencyUtil.isVolumeRoundedToNearestUnit(currencyCode) ? VOLUME_FORMAT_UNIT : VOLUME_FORMAT_PRECISE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import java.math.BigInteger;
|
|||
import java.text.DecimalFormat;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static haveno.core.util.VolumeUtil.getAdjustedFiatVolume;
|
||||
import static haveno.core.util.VolumeUtil.getAdjustedVolumeUnit;
|
||||
|
||||
public class CoinUtil {
|
||||
|
||||
|
@ -82,10 +82,10 @@ public class CoinUtil {
|
|||
public static BigInteger getRoundedAmount(BigInteger amount, Price price, long maxTradeLimit, String currencyCode, String paymentMethodId) {
|
||||
if (PaymentMethod.isRoundedForAtmCash(paymentMethodId)) {
|
||||
return getRoundedAtmCashAmount(amount, price, maxTradeLimit);
|
||||
} else if (CurrencyUtil.isFiatCurrency(currencyCode)) {
|
||||
return getRoundedFiatAmount(amount, price, maxTradeLimit);
|
||||
} else if (CurrencyUtil.isVolumeRoundedToNearestUnit(currencyCode)) {
|
||||
return getRoundedAmountUnit(amount, price, maxTradeLimit);
|
||||
} else if (CurrencyUtil.isTraditionalCurrency(currencyCode)) {
|
||||
return getRoundedTraditionalAmount(amount, price, maxTradeLimit);
|
||||
return getRoundedAmountPrecise(amount, price, maxTradeLimit);
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
|
@ -98,16 +98,16 @@ public class CoinUtil {
|
|||
* Calculate the possibly adjusted amount for {@code amount}, taking into account the
|
||||
* {@code price} and {@code maxTradeLimit} and {@code factor}.
|
||||
*
|
||||
* @param amount Bitcoin amount which is a candidate for getting rounded.
|
||||
* @param amount Monero amount which is a candidate for getting rounded.
|
||||
* @param price Price used in relation to that amount.
|
||||
* @param maxTradeLimit The max. trade limit of the users account, in satoshis.
|
||||
* @param maxTradeLimit The max. trade limit of the users account, in atomic units.
|
||||
* @return The adjusted amount
|
||||
*/
|
||||
public static BigInteger getRoundedFiatAmount(BigInteger amount, Price price, long maxTradeLimit) {
|
||||
public static BigInteger getRoundedAmountUnit(BigInteger amount, Price price, long maxTradeLimit) {
|
||||
return getAdjustedAmount(amount, price, maxTradeLimit, 1);
|
||||
}
|
||||
|
||||
public static BigInteger getRoundedTraditionalAmount(BigInteger amount, Price price, long maxTradeLimit) {
|
||||
public static BigInteger getRoundedAmountPrecise(BigInteger amount, Price price, long maxTradeLimit) {
|
||||
DecimalFormat decimalFormat = new DecimalFormat("#.####");
|
||||
double roundedXmrAmount = Double.parseDouble(decimalFormat.format(HavenoUtils.atomicUnitsToXmr(amount)));
|
||||
return HavenoUtils.xmrToAtomicUnits(roundedXmrAmount);
|
||||
|
@ -154,8 +154,8 @@ public class CoinUtil {
|
|||
|
||||
// We get the adjusted volume from our amount
|
||||
Volume volume = useSmallestUnitForAmount
|
||||
? getAdjustedFiatVolume(price.getVolumeByAmount(smallestUnitForAmount), factor)
|
||||
: getAdjustedFiatVolume(price.getVolumeByAmount(amount), factor);
|
||||
? getAdjustedVolumeUnit(price.getVolumeByAmount(smallestUnitForAmount), factor)
|
||||
: getAdjustedVolumeUnit(price.getVolumeByAmount(amount), factor);
|
||||
if (volume.getValue() <= 0)
|
||||
return BigInteger.valueOf(0);
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ package haveno.core.util.validation;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class FiatPriceValidator extends MonetaryValidator {
|
||||
public class AmountValidator4Decimals extends MonetaryValidator {
|
||||
@Override
|
||||
public double getMinValue() {
|
||||
return 0.0001;
|
||||
|
@ -33,6 +33,6 @@ public class FiatPriceValidator extends MonetaryValidator {
|
|||
}
|
||||
|
||||
@Inject
|
||||
public FiatPriceValidator() {
|
||||
public AmountValidator4Decimals() {
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ package haveno.core.util.validation;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class NonFiatPriceValidator extends MonetaryValidator {
|
||||
public class AmountValidator8Decimals extends MonetaryValidator {
|
||||
@Override
|
||||
public double getMinValue() {
|
||||
return 0.00000001;
|
||||
|
@ -32,6 +32,6 @@ public class NonFiatPriceValidator extends MonetaryValidator {
|
|||
}
|
||||
|
||||
@Inject
|
||||
public NonFiatPriceValidator() {
|
||||
public AmountValidator8Decimals() {
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue