Skip to content

Verbose logging for exchange rate queries#6083

Open
peachbits wants to merge 4 commits into
developfrom
matthew/verbose-rates-logging
Open

Verbose logging for exchange rate queries#6083
peachbits wants to merge 4 commits into
developfrom
matthew/verbose-rates-logging

Conversation

@peachbits

@peachbits peachbits commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Requirements

If you have made any visual changes to the GUI. Make sure you have:

  • Tested on iOS device
  • Tested on Android device
  • Tested on small-screen device (iPod Touch)
  • Tested on large-screen device (tablet)

No visual changes — this PR only adds logging, a support/dev script, and a rate-cache fix.

Description

Asana: https://app.asana.com/1/9976422036640/project/1213843652804305/task/1216799838261197

Adds verbose-gated logging to the bulk exchange-rate query loop, plus a way to re-run a user's rate queries against the rates server — to diagnose cases where the app can reach the rates server but still shows no rates (e.g. a wallet balance rendering $0.00 USD while the price chart, which comes from a different host, works).

Query logging (ExchangeRateActions.ts) — gated by the Settings → Verbose Logging toggle and written through util/logger, so it lands in the captured logs_info.* bundle sent via Send Logs:

  • Each v3/rates request is logged as a condensed JSON string — the exact body that is POSTed, so it can be replayed verbatim.
  • A per-query outcome summary: how many pairs resolved vs. came back no-rate, naming the pairs the server returned without a rate.
  • Non-OK HTTP responses (status + body). These were previously dropped with no output at all, because fetchWaterfall only fails over on thrown errors — an HTTP error response was treated as success and the whole chunk silently discarded.
  • Thrown errors, passed to log as Error values so their stack (and therefore the failing call site) is preserved.

When the setting is off there is no new output and no string-building work.

Cache snapshot (LogActions.tsx): the info log payload now includes a snapshot of the in-memory exchange-rate cache — rates plus the subscribed cryptoPairs / fiatPairs lists and their expirations — so support can tell pairs that were never requested from pairs that failed to resolve.

Replay script (scripts/ratesCacheReplay.ts, npm run rates-cache-replay): rebuilds the v3/rates queries the app would have sent for the pairs in a logged cache blob, sends them, and reports every requested pair as one of:

  • <rate> — the server returned a rate
  • NO RATE — the server returned the pair but declined to price it
  • MISSING — the server omitted the pair from its response entirely

Query construction mirrors convertToRatesParams. Accepts the bare cache JSON or a pasted log excerpt. --server selects the rates host (default rates3; point it at rates4 to compare hosts) and --dry-run prints the query bodies without sending them.

Duplicate rate pairs (ExchangeRateActions.ts): the pair cache matched entries with a linear scan comparing tokenId with ===. Pairs written without a tokenId key read back as undefined, and undefined === null is false, so every refresh appended a second pair for each chain's own asset. A real cache had 202 pairs where only 137 were distinct — inflating each rate query and pushing it over the 100-pair chunk size into an extra request every refresh. The pair lists are now built from keyed maps using the same key loadExchangeRateCache de-duplicates with, so a missing tokenId and an explicit null collapse to one pair; existing duplicates are merged away on the next refresh.

Testing: tsc --noEmit, eslint, and the full jest suite (231 tests / 103 snapshots) pass. New mergePairCache unit tests cover the de-duplication, including the two cases that fail against the previous implementation. The replay script was exercised against a real cache blob — 202 cached pairs, 3 queries, all pairs resolved against rates3.

Runtime was not exercised in-app: develop currently hits an unrelated react-native-worklets installTurboModule init crash under Xcode 26 / iOS 26.5 that blocks launching the simulator build. That crash reproduces on a clean develop and is orthogonal to this JS-only change.

Copilot AI review requested due to automatic review settings July 23, 2026 21:19

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 815a8bb. Configure here.

Comment thread src/actions/ExchangeRateActions.ts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

This PR improves exchange-rate troubleshooting by adding verbose-gated, replayable logging around v3/rates bulk queries, including an exchange-rate cache snapshot in support logs, and introducing a script to convert captured cache data back into rate-server query bodies for replay.

Changes:

  • Added verbose logging in the bulk exchange-rate fetch loop (request body logging, resolved vs no-rate summary, HTTP non-OK + thrown error logging).
  • Included an in-memory exchange-rate cache snapshot in the support “info” log payload.
  • Added scripts/ratesCacheToQueries.ts to transform a logged cache blob (or excerpt) into replayable v3/rates JSON bodies or curl commands, and updated the changelog accordingly.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/actions/ExchangeRateActions.ts Adds verbose-gated logging around rate queries and exports an in-memory cache dump for support logging.
src/actions/LogActions.tsx Appends an exchange-rate cache snapshot to the support log payload.
scripts/ratesCacheToQueries.ts New developer/support script to convert logged cache blobs into replayable rate query bodies / curl commands.
CHANGELOG.md Documents the new verbose rate-query logging, cache snapshot, and replay script.
package.json Updated to support the new replay script (content excluded by policy).
Files excluded by content exclusion policy (1)
  • package.json

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/actions/LogActions.tsx
Copilot AI review requested due to automatic review settings July 23, 2026 21:44
@peachbits
peachbits force-pushed the matthew/verbose-rates-logging branch from 815a8bb to 4329aa0 Compare July 23, 2026 21:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.

Files excluded by content exclusion policy (1)
  • package.json

Comment thread scripts/ratesCacheReplay.ts
Copilot AI review requested due to automatic review settings July 23, 2026 21:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.

Files excluded by content exclusion policy (1)
  • package.json
Comments suppressed due to low confidence (1)

scripts/ratesCacheReplay.ts:226

  • The --server flag accepts an option value, but if the user passes --server without a URL (or as the last argument), server = argv[++i] will set server to undefined and the script will later build requests to undefined/v3/rates, which is a confusing failure mode. Add a simple validation/error for missing --server values.
    const arg = argv[i]
    if (arg === '--dry-run') dryRun = true
    else if (arg === '--server') server = argv[++i]
    else if (!arg.startsWith('--')) file = arg

Comment thread src/actions/ExchangeRateActions.ts
When the Verbose Logging setting is on, the bulk rates loop logs the exact
request body for each query (a condensed JSON string that can be replayed
verbatim against the rates server), a resolved/no-rate outcome summary,
non-OK HTTP responses, and thrown errors. Non-OK responses were previously
dropped with no output at all.
The info log payload sent to the logs server now carries a snapshot of
the in-memory exchange-rate cache, including the subscribed pair lists
and per-rate expirations, so support can distinguish pairs that were
never requested from pairs that failed to resolve.
ratesCacheToQueries.ts takes the exchange-rate cache blob captured in the
support logs and converts its subscribed pair lists into the v3/rates query
bodies the app would send, mirroring convertToRatesParams. Emits JSON bodies
or ready-to-run curl commands so a rate issue seen in a user's logs can be
reproduced directly against the rates server.
The pair cache matched entries with a linear scan comparing tokenId with
===. Pairs written without a tokenId key read back as undefined, and
undefined === null is false, so every refresh appended a second pair for
each chain's own asset. A real cache had 202 pairs where only 137 were
distinct, inflating each rate query and pushing it over the 100-pair chunk
size into an extra request.

Build the pair lists from keyed maps instead, using the same key
loadExchangeRateCache de-duplicates with, so a missing tokenId and an
explicit null collapse to one pair. Existing duplicates are merged away on
the next refresh.
Copilot AI review requested due to automatic review settings July 23, 2026 22:13
@peachbits
peachbits force-pushed the matthew/verbose-rates-logging branch from 9338475 to f80cf3b Compare July 23, 2026 22:13

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (1)
  • package.json
Comments suppressed due to low confidence (1)

scripts/ratesCacheReplay.ts:227

  • --server consumes the next argv entry without validating it exists. If a user runs --server as the last arg (or before another flag), server becomes undefined and the script will attempt requests to undefined/v3/rates, which is hard to diagnose. Add a guard that throws a clear error when --server is missing a value.
  for (let i = 0; i < argv.length; i++) {
    const arg = argv[i]
    if (arg === '--dry-run') dryRun = true
    else if (arg === '--server') server = argv[++i]
    else if (!arg.startsWith('--')) file = arg
  }

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.

3 participants