Skip to content

fix: an unknown exchange rate is unknown, not zero - #8

Merged
marc0olo merged 1 commit into
mainfrom
fix/unknown-exchange-rate-is-not-zero
Aug 22, 2026
Merged

fix: an unknown exchange rate is unknown, not zero#8
marc0olo merged 1 commit into
mainfrom
fix/unknown-exchange-rate-is-not-zero

Conversation

@marc0olo

Copy link
Copy Markdown
Member

Fixes #7.

buildPriceInfo fell back to multiplying by zero when the token was not in the exchange rate cache:

priceUSD = roundTo(priceUSD != null ? priceUSD
    : price * (cache.containsKey(token) ? cache.get(token) : 0));

So a price in a token we have no rate for was published as a confident $0.00 — indistinguishable from a genuinely free item. _PriceInfo consumers already render a null usd as "n/a" (see BaseTransformer), so null is the value this should produce; the convention already exists and this converges onto it.

Scope, measured

49 activity rows in soonmarket_realtime_event carry a non-zero price and a zero usd in a token with no exchange rate at all — FOOBAR (21), EASY (17), CYPHR (6), RDM (3), PIXEL (2).

The other 9737 zero-usd rows are not this bug and must not be "repaired" away. XPR, XUSDC and LOAN all have rates, so the fallback never fired for them. Their zeros come from roundTo defaulting to 2 decimals with amounts genuinely below half a cent — every one of the 9724 XPR rows is between 0.0001 and 5 XPR, and XPR is $0.00286. A bad cached rate would not respect that ceiling, which is what rules it out. This is spelled out in #7 along with two hypotheses that were tested and disproved.

The part that would bite a careless fix

roundTo unboxes its Double argument, so the derived values cannot simply be computed and rounded once priceUSD may be null. Three of them are now guarded:

  • royaltyUSD and marketFeeUSD — were guarded on royalty/marketFee but not on priceUSD
  • sellerReceivedPriceUSDdereferenced priceUSD unguarded, so swapping the : 0 for null without touching this would have turned the fix into a new NPE on exactly the rows it is meant to fix

Also note cache.get(token) replaces containsKey(...) ? get(...) : 0. Besides being the actual fix, that removes an unboxing NPE if the map ever held a null value for a present key.

No repair

The 49 rows cannot be repaired — no rate exists for those tokens, so null is the correct value and this produces it going forward. The other 9737 are already correct.

Verification

This repo has no build of its own (CI checks line endings only), so it was verified through the consumers:

  • event-processor-contractmvn test passes with this source vendored in
  • soon-market-apimvn compile passes. Its mvn test fails locally on a Quarkus startup TimeoutException from the DB connection pool, which is an environment issue unrelated to this change

Consumers need the usual submodule bump afterwards; at minimum event-processor-contract and soon-market-api, which are the two that render prices.

🤖 Generated with Claude Code

buildPriceInfo fell back to multiplying by zero when the token was not in
the exchange rate cache:

    priceUSD = roundTo(priceUSD != null ? priceUSD
        : price * (cache.containsKey(token) ? cache.get(token) : 0));

so a price in a token we have no rate for was published as a confident
$0.00, indistinguishable from a genuinely free item. _PriceInfo
consumers already render a null usd as "n/a" - see BaseTransformer - so
null is the value this should produce.

Measured in soonmarket_realtime_event: 49 activity rows carry a non zero
price and a zero usd in a token with no exchange rate at all, across
FOOBAR, EASY, CYPHR, RDM and PIXEL.

The other 9737 zero usd rows are not this bug and must not be "repaired"
away. XPR, XUSDC and LOAN all have rates, so the fallback never fired
for them. Their zeros come from roundTo defaulting to 2 decimals with
amounts genuinely below half a cent: every one of the 9724 XPR rows is
between 0.0001 and 5 XPR, and XPR is $0.00286. A bad cached rate would
not respect that ceiling, which is what rules it out.

roundTo unboxes its argument, so the derived values cannot simply be
computed and rounded once priceUSD may be null. royaltyUSD, marketFeeUSD
and sellerReceivedPriceUSD are each guarded instead - the last one
dereferenced priceUSD unguarded and would otherwise have turned this fix
into a new NPE.

No repair is possible or needed for the 49 rows: no rate exists for
those tokens, so null is the correct value and this produces it going
forward.

Refs #7

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@marc0olo
marc0olo merged commit 656b50b into main Aug 22, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

buildPriceInfo publishes $0.00 when no exchange rate exists, instead of n/a

1 participant