Modularize and fix various issues#19
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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