Repository navigation
Show a fiat value again, from Neoxa's rate for this chain - #81
Conversation
Every exchange source quoted the SHA256d chain, so privkeyio#39 turned fiat off. Neoxa quotes this chain's coin as BTCB2_USDC, and is now the only exchange rate source, used by default. A stored Coinbase, Coingecko or mempool.space choice reads back as Neoxa, so the wrong rate cannot return; None is kept. Other currencies are converted from USD through a second setting, Currency conversion (Coinbase, Coingecko or mempool.space, mempool.space by default). Only the ratio between its price of a bitcoin in two currencies is used, so its price of a bitcoin cancels out. Historical values in the transaction CSV and BIP329 label exports are chosen by block height: a transaction below 961632, the first block not shared with the SHA256d chain, takes that chain's price for the day, and one from 961632 on takes Neoxa's. Neoxa's daily candles start on 2026-09-01, so a transaction between the split and then has no value rather than a wrong one. Closes privkeyio#55
|
Thanks, this works. Please slim it to Neoxa as an opt-in USD-only source: keep the default at None for new and existing installs, and drop the conversion source and HistoricalRates. Older transactions without Neoxa data showing no fiat is fine. |
As asked in review: the default stays None for new and existing installs, and Neoxa is used only when chosen. Any other stored source still reads back as None. Neoxa offers USD only, its BTCB2_USDC price; the currency conversion setting and HistoricalRates are dropped. Exports take Neoxa's daily closes, so a transaction from before 2026-09-01 has no fiat value.
|
Done: Neoxa is now opt-in and USD only, default None; the conversion source and HistoricalRates are gone. |
| for(NeoxaCandle candle : neoxaCandles.candles) { | ||
| Date date = new Date(candle.time * 1000); | ||
| if(candle.close != null && !date.before(DateUtils.truncate(start, Calendar.DAY_OF_MONTH)) && !date.after(end)) { | ||
| rates.put(DateUtils.truncate(date, Calendar.DAY_OF_MONTH), candle.close); |
There was a problem hiding this comment.
Candles are UTC midnight; local truncation shifts every close a day west of UTC. Key by the UTC date.
There was a problem hiding this comment.
Fixed in 9967bf9: each close is now keyed by its candle's UTC date, at local midnight to match how transaction dates are looked up. A test checks four time zones and fails with the old truncation.
| } | ||
|
|
||
| //The candles endpoint takes a count rather than a range, and returns the most recent ones | ||
| long days = ChronoUnit.DAYS.between(start.toInstant(), Instant.now()) + 2; |
There was a problem hiding this comment.
This limit reveals the wallet's first transaction date to Neoxa. Use a fixed limit; data starts Sep 2026.
There was a problem hiding this comment.
Fixed in 9967bf9: the count is now always worked out from 2026-09-01, when Neoxa's data starts, so it depends only on the day and not on the wallet.
|
|
||
| HttpClientService httpClientService = AppServices.getHttpClientService(); | ||
| try { | ||
| return httpClientService.requestJson(url, NeoxaTicker.class, null).ticker.lastPrice; |
There was a problem hiding this comment.
Accept only finite, positive prices here and for candle closes; Infinity breaks rendering, zero or negative corrupts exports.
There was a problem hiding this comment.
Fixed in 9967bf9: toValidRate keeps only finite, positive prices, for the ticker and every candle close.
As asked in review: - A candle opens at UTC midnight, so truncating it in local time moved every close to the previous day west of UTC. Each close is now keyed by its UTC date, at local midnight to match how transaction dates are looked up. - The candle count was worked out from the export's start date, which told Neoxa when the wallet's first transaction was. It is now always counted from 2026-09-01, when Neoxa's data starts, so it depends only on the day. - Only finite, positive prices are used, from the ticker and the candles.
Summary
Adds Neoxa, which quotes BTCB2 against USDC, as an opt-in exchange rate source. With it chosen, a USD value is shown again. #39 turned fiat off because every source quoted the SHA256d chain, which overstated balances by a couple of hundred times. The default stays None for new and existing installs. Closes #55.
Problem
Config.getExchangeSource()returnsNONEunconditionally (src/main/java/com/sparrowwallet/sparrow/io/Config.java:243onf2a00271), and the source dropdown is disabled with a tooltip (src/main/java/com/sparrowwallet/sparrow/settings/GeneralSettingsController.java:165-166). No source quoted this chain's coin:ExchangeSourceonly offers Coinbase, Coingecko and mempool.space, all SHA256d.Reproduction
On
f2a00271, open Settings → General. The exchange rate source and currency are disabled, and no fiat value can be turned on.Neoxa's public API, no authentication:
Before
Change
ExchangeSource.NEOXAsupports USD only. The current rate is the ticker'slastPrice, and historical rates are daily candle closes. Any other currency gets no rate, without a request.Config.getExchangeSource()returnsNEOXAonly if Neoxa was chosen, andNONEotherwise. That includes no choice and a stored Coinbase, Coingecko or mempool.space choice.Infinitybreaks rendering, and a zero or negative price corrupts exports.After
Live, through the app's HTTP client, on 2026-10-17:
Test results
f2a00271. JDK Temurin 25.0.4.1+1../gradlew clean test --continue -Djava.awt.headless=true.ExchangeSourceGateTest: the 2 old tests were replaced by 3 (None by default, stored SHA256d sources read as None, Neoxa when chosen).NeoxaExchangeSourceTest(7): USD only, ticker parsing, daily closes from candles, closes keyed by UTC date in four time zones (fails with local truncation), the candles count, the finite positive check, and invalid closes left out.HeaderCheckpointVerificationTest(4 tests, tagcheckpoint) is excluded bybuild.gradlein both runs. It needs live Electrum servers and runs throughverifyCheckpoint.Caveats
b91f7993). It fills once a server is connected and settings are reopened.