Skip to content

Modularize and fix various issues#19

Merged
ismaelsadeeq merged 20 commits into
2140-dev:masterfrom
ismaelsadeeq:04-2026-bug-fixes
Apr 15, 2026
Merged

Modularize and fix various issues#19
ismaelsadeeq merged 20 commits into
2140-dev:masterfrom
ismaelsadeeq:04-2026-bug-fixes

Conversation

@ismaelsadeeq

@ismaelsadeeq ismaelsadeeq commented Apr 15, 2026

Copy link
Copy Markdown
Member

Fixes #5 , #6, #8, #10 #16

This pr focuses on fixing correctness issues in mempool health calculations and queries, improving frontend data handling and rendering, and restructuring backend services to make the system easier to reason about and extend

Changes

fix three bugs found in code review affecting correctness and edge cases
remove unused reset_collectors function from collector_service
split rpc_service into rpc_client, rpc_registry, and a thin shim for clearer separation of concerns
parallelise block stats fetching and add gzip compression to improve performance
fix duplicate rows caused by incorrect distinct usage and document conversion logic
adjust mempool health logic by restoring clamp to 1.0 and refining how stats are sourced via estimatesmartfee verbosity 2

extract inline subcomponents and tighten api types for better structure and type safety
remove unused types and fix tsconfig issues impacting ci
add stats cache with prefetching and background auto refresh
improve typography, color tokens, and dark mode chart rendering
fix layout issues including cramped percentile labels and mempool chart axes
update mempool health ratio display and ensure consistency with backend changes
show all three fee targets in a responsive grid
remove redundant labels and clean up api usage by always passing chain explicitly
correctly patching the RPC registry in the test helpers, ensuring tests pass even without a local configuration file. On the
frontend, fixed mobile overflow issues in the navigation bar by adjusting padding and font sizes for smaller screens. Finally, improved the network
dropdown's mobile usability and synchronized the active link colors with the selected network's theme.

add rest api reference to readme

chain was optional in getFeeEstimate so early page loads could fire
the request without it. Make chain a required parameter and always
include ?chain= in the URL, consistent with every other endpoint.
Guard fetchFee with an early return until chain is set so no request
goes out before NetworkContext resolves.

Remove chain_display from the estimate_smart_fee response — the
frontend already has chain_display from NetworkContext (/networks),
so echoing it back in every fee response is redundant.
…sion comment

Three fixes:

1. Replace SELECT DISTINCT with a window function in get_estimates_in_range.
   DISTINCT operates on the full row, so two rows with the same (poll_height,
   target) but different estimate_feerate both survived the dedup — the
   intended behaviour is one estimate per poll_height. Use ROW_NUMBER()
   OVER (PARTITION BY poll_height, target ORDER BY timestamp ASC) and keep
   only rn=1 to always take the earliest poll at each height.

2. Remove min(1.0, ...) clamp from the mempool health ratio.
   Clamping at 1.0 meant a mempool of 8 MWU showed the same ratio as 4 MWU,
   losing information. The ratio (total_mempool_weight / 4_000_000) now
   reflects actual block-equivalents waiting: 0.5 = half a block, 2.0 = two
   blocks worth, etc.

3. Add inline comment explaining the feerate_sat_per_vb conversion factor.
   × 100_000 is non-obvious; the comment makes the BTC/kvB → sat/vB
   arithmetic explicit.
Replace the single-target tab selector with three parallel fee cards
(Next Block / 7 Blocks / 1 Day) in a responsive 1→3 column grid.
All targets are fetched in a single Promise.all alongside getMempoolHealth,
so the page makes one round of requests instead of waiting for a tab click.

Also fix the mempool health section: it was reading
feeData?.mempool_health_statistics which no longer exists after the
/mempool-health split — it now uses the dedicated getMempoolHealth call.
Stale fetches are cancelled via AbortController on mode change or unmount.
ratio is block-equivalents (mempool_weight / 4_000_000), not a 0–1
fraction. Multiplying by 100 and showing % gave nonsense values like
3537%. Display as Nx (e.g. 35.4x = 35 blocks worth of backlog) and
update color thresholds: green < 1 block, orange 1–5 blocks, red > 5.
Removing min(1.0, ...) caused the ratio to exceed 1.0 when the mempool
backlog spans multiple blocks (e.g. 35x → 3537%). Restore the clamp so
ratio stays in [0, 1] and the frontend percentage display is correct.
The previous implementation called getmempoolfeeratediagram to get the
live total mempool weight and applied it uniformly to every block in the
range. This was wrong: the field mempool_txs_weight should reflect how
much of each specific block came from the mempool, which is exactly what
estimatesmartfee verbosity 2 (PR #34075) returns in its
mempool_health_statistics field.

Replace the getmempoolfeeratediagram + 5x getblockstats approach with a
single estimatesmartfee call and read the per-block stats directly.
Ratio is now mempool_txs_weight / block_weight capped at 1.0.
- Introduce statsCache (frontend/src/lib/statsCache.ts) keyed by
  chain:target; prefetchStats() warms all three targets in parallel
  when the chain resolves on the landing page.
- useStats reads from cache on mount so /stats is instant after a
  landing-page visit; writes back to cache after every successful fetch.
- Landing page auto-refreshes fee estimates every 30 s; background
  refresh suppresses the loading spinner when the cache is already warm.
- Cap cache size at 30 entries (10 chains × 3 targets) with LRU-style
  eviction to prevent unbounded growth.
- Add !frontend/src/lib/ negation to .gitignore so the new lib/ tree
  is not silently excluded by the root Python packaging pattern.
- Replace single-pass label spread with a three-pass algorithm (push
  down, pull up from bottom, top-clamp + forward) so labels never
  overlap even on a flat mempool curve where all dots cluster at the
  same y position.
- Labels are placed as a fixed-height block entirely above the dot;
  a leader line is drawn when the block is displaced from its natural
  position.
- Show labels as "p25" / "p95" (feerate percentile) rather than
  "25%" / "95%"; correct weight-fraction inversion so p95 (expensive)
  maps to the 5% weight position in the diagram (highest-feerate first).
- Add x-axis ("CUMULATIVE WEIGHT (MWU)") and y-axis
  ("FEE RATE (sat/vB)") labels to the mempool diagram chart.
- Remove unused lucide-react imports (Activity, Database, Layers,
  TrendingUp, Scale) from mempool/page.tsx.
…fixes

- Replace sequential getblockstats loop with ThreadPoolExecutor (max 8
  workers) in get_performance_data and calculate_local_summary.
  calculate_local_summary collects all needed heights upfront, warms
  the lru_cache in parallel, then classifies rows serially to avoid
  per-row state races.
- Add Flask-Compress for automatic gzip on JSON responses (~80% size
  reduction on large payloads).
- Fix auth credential check: use 'and' instead of 'or' so auth=None
  is correctly sent when both user and password are absent.
- Fix percentile convention in get_mempool_feerate_diagram_analysis:
  the diagram packs transactions highest-feerate first, so weight
  position p corresponds to feerate percentile (1-p).
- Remove dead module-level wrappers get_current_chain() and
  get_block_count() that were never called from app.py or collectors.
…ring

- Add CSS custom properties for chart surfaces (--chart-bg,
  --chart-grid, --chart-text, --chart-axis, --data-blue) and
  surface layers (--surface, --surface-2, --text-secondary); dark
  mode uses Zinc scale for warmer card/bg contrast.
- FeeHistoryChart reads all colors via getComputedStyle at D3 render
  time so the chart adapts correctly to light/dark mode without
  hardcoded hex values.
- Restore p10-p90 band fill-opacity from 0.25 to 0.45; the band
  was invisible after the --muted token was applied over --chart-bg.
- Add x-axis ("BLOCK HEIGHT") and y-axis ("FEE RATE (sat/vB)")
  labels to the stats history chart.
- Normalise font weights across cards and summary panels
  (font-black → font-semibold on labels; tabular-nums on numbers).
- Remove BlockStats interface, BlockStatsMap and FeesStatsMap types
  from types/api.ts; none are referenced anywhere in the codebase.
- Add .next/dev/types/**/*.ts to tsconfig includes so the TypeScript
  compiler picks up Next.js dev-mode generated types (fixes CI tsc run).
… shim

rpc_service.py was a 477-line file mixing three unrelated concerns.
Split into focused modules:

- rpc_client.py  — RpcClient class: transport, node queries, block-stats
                   cache, mempool analysis, and performance analytics.
- rpc_registry.py — RpcRegistry, config-file discovery (_find_config),
                   registry construction (_build_registry), and the
                   lazy module-level singleton.
- rpc_service.py — thin public-API shim: re-exports all symbols used by
                   app.py, collector_service, and tests; defines its own
                   _RegistryProxy so patching services.rpc_service._get_registry
                   in tests continues to intercept the registry without
                   also needing to patch rpc_registry.

No behaviour changes; all 71 tests pass.
- Extract FeeCard, HealthBlock, LoadingSpinner from app/page.tsx into
  components/home/ so the landing page is a composition of named
  components rather than a single 270-line file with embedded functions.
- Extract SummaryCard from app/stats/page.tsx into
  components/stats/SummaryCard.tsx with a typed Props interface
  replacing the previous 'any' annotation.
- Make FeeEstimateResponse.chain non-optional (the backend always sets
  it when result != null) and add inline JSDoc to the affected fields.
- Remove unused lucide-react imports from stats/page.tsx (Search,
  Activity, Scale).
database_service: add id ASC as tiebreaker in ROW_NUMBER ORDER BY so
  that two estimates inserted within the same second produce a
  deterministic result instead of an arbitrary one.

collector_service: floor sleep_time at 1 s instead of 0 so a slow or
  continuously-erroring RPC node cannot cause the collector thread to
  busy-loop without any backoff.

useStats: remove the getMempoolHealth call from the stats hook.
  healthStats was fetched on every stats page load but never consumed
  there — the mempool health data is fetched directly on the landing
  page. Removing it eliminates one unnecessary RPC round-trip per
  stats fetch.
@ismaelsadeeq ismaelsadeeq merged commit 82839d9 into 2140-dev:master Apr 15, 2026
2 checks 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.

Stats Page: 7B, 1 day stats are not reflecting

1 participant