From 4d60a203363daddfb717fdf6833d26d442f237e4 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Mon, 3 Feb 2025 14:47:23 +0200 Subject: [PATCH] add mempool.space exchange rate source --- lark | 2 +- .../sparrow/net/ExchangeSource.java | 91 +++++++++++++++++++ .../sparrow/settings/general.fxml | 1 + 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/lark b/lark index d7083680..b379338c 160000 --- a/lark +++ b/lark @@ -1 +1 @@ -Subproject commit d7083680d83b97c09433f453e2f61cc15d6cbe77 +Subproject commit b379338cc270d32e4225cb67392fc7c3a7c91b85 diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ExchangeSource.java b/src/main/java/com/sparrowwallet/sparrow/net/ExchangeSource.java index c1847209..a57cbb21 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/ExchangeSource.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/ExchangeSource.java @@ -1,5 +1,7 @@ package com.sparrowwallet.sparrow.net; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.event.ExchangeRatesUpdatedEvent; import com.sparrowwallet.tern.http.client.HttpResponseException; @@ -184,6 +186,75 @@ public enum ExchangeSource { } } + return historicalRates; + } + }, + MEMPOOL_SPACE("mempool.space") { + @Override + public List getSupportedCurrencies() { + return getRates().rates.entrySet().stream().filter(price -> isValidISO4217Code(price.getKey().toUpperCase(Locale.ROOT))) + .map(rate -> Currency.getInstance(rate.getKey().toUpperCase(Locale.ROOT))).collect(Collectors.toList()); + } + + @Override + public Double getExchangeRate(Currency currency) { + String currencyCode = currency.getCurrencyCode(); + OptionalDouble optRate = getRates().rates.entrySet().stream().filter(price -> currencyCode.equalsIgnoreCase(price.getKey())).mapToDouble(Map.Entry::getValue).findFirst(); + if(optRate.isPresent()) { + return optRate.getAsDouble(); + } + + return null; + } + + private MempoolSpaceRates getRates() { + String url = AppServices.isUsingProxy() ? "http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api/v1/prices" : "https://mempool.space/api/v1/prices"; + + if(log.isInfoEnabled()) { + log.info("Requesting exchange rates from " + url); + } + + HttpClientService httpClientService = AppServices.getHttpClientService(); + try { + return httpClientService.requestJson(url, MempoolSpaceRates.class, null); + } catch(Exception e) { + if(log.isDebugEnabled()) { + log.warn("Error retrieving currency rates", e); + } else { + log.warn("Error retrieving currency rates (" + e.getMessage() + ")"); + } + return new MempoolSpaceRates(); + } + } + + @Override + public Map getHistoricalExchangeRates(Currency currency, Date start, Date end) { + String url = AppServices.isUsingProxy() ? "http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/api/v1/historical-price?currency=" + currency.getCurrencyCode() : + "https://mempool.space/api/v1/historical-price?currency=" + currency.getCurrencyCode(); + + if(log.isInfoEnabled()) { + log.info("Requesting historical exchange rates from " + url); + } + + Map historicalRates = new TreeMap<>(); + HttpClientService httpClientService = AppServices.getHttpClientService(); + try { + MempoolSpaceHistoricalRates mempoolSpaceHistoricalRates = httpClientService.requestJson(url, MempoolSpaceHistoricalRates.class, null); + Collections.reverse(mempoolSpaceHistoricalRates.prices); //Use "closing" rates + for(MempoolSpaceRates historicalRate : mempoolSpaceHistoricalRates.prices) { + Date date = new Date(historicalRate.time * 1000); + if(date.after(start) && date.before(end) && historicalRate.rates.containsKey(currency.getCurrencyCode())) { + historicalRates.put(DateUtils.truncate(date, Calendar.DAY_OF_MONTH), historicalRate.rates.get(currency.getCurrencyCode())); + } + } + } catch(Exception e) { + if(log.isDebugEnabled()) { + log.warn("Error retrieving historical currency rates", e); + } else { + log.warn("Error retrieving historical currency rates (" + e.getMessage() + ")"); + } + } + return historicalRates; } }; @@ -279,4 +350,24 @@ public enum ExchangeSource { private static class CoinGeckoHistoricalRates { public List> prices = new ArrayList<>(); } + + private static class MempoolSpaceRates { + public long time; + public final Map rates = new LinkedHashMap<>(); + + // Capture all other fields that Jackson do not match other members + @JsonAnyGetter + public Map getPrices() { + return rates; + } + + @JsonAnySetter + public void setPrice(String name, Double value) { + rates.put(name, value); + } + } + + private static class MempoolSpaceHistoricalRates { + public List prices = new ArrayList<>(); + } } diff --git a/src/main/resources/com/sparrowwallet/sparrow/settings/general.fxml b/src/main/resources/com/sparrowwallet/sparrow/settings/general.fxml index a0f1dbe8..71d86404 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/settings/general.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/settings/general.fxml @@ -58,6 +58,7 @@ +