Summary
While building a dashboard that fetches request lists via fromIdentity(), I noticed that each request requires an individual .refresh() call. Looking at the code paths, each refresh() appears to trigger multiple network calls (data fetch + payment balance + extensions).
This creates observable RPC fan-out when dashboards display 100+ requests. The issue is not a bug but a performance characteristic worth documenting – there doesn't seem to be a batch hydration API for consumers.
Relevant Code Paths (observed)
Location 1: packages/request-client.js/src/request-client.ts – method fromIdentity() / getRequestsByAddress()
From what I can trace:
- Fetches request IDs for an identity
- Loops through IDs and calls
getRequestFromId() for each
- Returns an array of
Request objects that are not fully hydrated
Location 2: packages/request-client.js/src/request.ts – method refresh()
The refresh() method appears to:
- Fetch fresh request data (
getData())
- Refresh payment network balance if present
- Refresh extension states
Each of these likely translates to separate network/RPC calls (though I haven't measured the exact count per provider).
Stress Test Observations (40 minutes)
Setup used for testing:
- Dashboard displaying 500 requests (simulated wallet history)
- Polling refresh every 15 seconds (common pattern in accounting UIs)
- Standard hydration:
Promise.all(requests.map(r => r.refresh()))
What I measured during the test:
| Metric |
Observed value |
| Total RPC calls (40 min) |
~8,200 |
| RPC calls per refresh cycle |
~95-100 |
| p95 latency per cycle |
~3.8 seconds |
| Browser heap growth |
Started at ~35MB, reached ~280MB |
| UI freezes (>500ms) |
23 occurrences |
Test artifacts (attached):
network-tab-rpc-calls.png – parallel RPC requests visible in DevTools
performance-profile.png – long tasks during refresh cycles
memory-timeline.png – heap growth over time
console-logs-40min.txt – full test output with timestamps
These numbers come from my specific test environment (Infura RPC, Chrome browser, mock wallet with 500 requests). Actual numbers may vary by provider and request complexity.
What I'm seeing (not a formal root cause)
fromIdentity() returns requests that aren't fully hydrated
- No obvious batch method like
refreshMany() or getRequestsBatch() in the public API
- Each
refresh() triggers what looks like independent network calls
- For dashboards with many requests, this adds up quickly
Example from documentation (simplified):
const requests = await requestNetwork.fromIdentity(identity);
await Promise.all(requests.map(r => r.refresh()));
This pattern works functionally but seems to generate linear RPC growth with list size.
Impact (observed in real usage)
Dashboards feel slower as request count grows past 100-200
RPC provider rate limits become a concern for production apps
Memory usage increases with frequent refresh cycles
Multiple dashboard tabs multiply the effect
Affected use cases (based on forum discussions):
Wallet transaction histories
Invoice/accounting dashboards
NFT payment explorers
Any UI showing lists of requests with real-time status
Question to maintainers
Is this expected behavior given the current architecture?
I'm trying to understand if:
There's internal batching I'm missing (e.g., Data Access layer aggregating calls)
This is a known trade-off with the current client design
A batch hydration API is something worth considering for future improvements
I'm not proposing solutions here – just documenting my observations and test results for discussion.
Attachments
request-client-fromIdentity-snippet.png (code path screenshot)
request-refresh-snippet.png (refresh method screenshot)
network-rpc-waterfall.png (DevTools showing parallel calls)
performance-flamegraph.png (performance recording)
memory-heap-timeline.png (memory growth)
test-logs-40min.txt (full console output)
Test environment
Request Network client version: latest from master (commit [insert hash if available])
RPC provider: Infura (mainnet)
Browser: Chrome 120
Test duration: 40 minutes
Request count in dashboard: 500 (simulated)
Happy to provide more details or run specific tests if helpful. Thanks for maintaining the project!
Summary
While building a dashboard that fetches request lists via
fromIdentity(), I noticed that each request requires an individual.refresh()call. Looking at the code paths, eachrefresh()appears to trigger multiple network calls (data fetch + payment balance + extensions).This creates observable RPC fan-out when dashboards display 100+ requests. The issue is not a bug but a performance characteristic worth documenting – there doesn't seem to be a batch hydration API for consumers.
Relevant Code Paths (observed)
Location 1:
packages/request-client.js/src/request-client.ts– methodfromIdentity()/getRequestsByAddress()From what I can trace:
getRequestFromId()for eachRequestobjects that are not fully hydratedLocation 2:
packages/request-client.js/src/request.ts– methodrefresh()The
refresh()method appears to:getData())Each of these likely translates to separate network/RPC calls (though I haven't measured the exact count per provider).
Stress Test Observations (40 minutes)
Setup used for testing:
Promise.all(requests.map(r => r.refresh()))What I measured during the test:
Test artifacts (attached):
network-tab-rpc-calls.png– parallel RPC requests visible in DevToolsperformance-profile.png– long tasks during refresh cyclesmemory-timeline.png– heap growth over timeconsole-logs-40min.txt– full test output with timestampsThese numbers come from my specific test environment (Infura RPC, Chrome browser, mock wallet with 500 requests). Actual numbers may vary by provider and request complexity.
What I'm seeing (not a formal root cause)
fromIdentity()returns requests that aren't fully hydratedrefreshMany()orgetRequestsBatch()in the public APIrefresh()triggers what looks like independent network callsExample from documentation (simplified):