Skip to content

Conversation

@0xApotheosis
Copy link
Member

@0xApotheosis 0xApotheosis commented Jan 9, 2026

Description

This PR delivers significant performance improvements for the ShapeShift web app across all browsers, with particularly impactful gains for Firefox users. The changes address three main bottlenecks: IndexedDB operations, redux-persist write frequency, and Redux selector memoization.

UAT:

Screenshot 2026-01-14 at 9 25 23 am

Some jams of this branch (left) vs develop (right) on a simulated 4x CPU throttle:

Key Improvements

  1. Web Worker for IndexedDB Operations

    • Moves all IndexedDB read/write operations to a dedicated Web Worker
    • Prevents main thread blocking during state persistence
    • Critical for Firefox where IndexedDB is significantly slower
  2. Redux-Persist Throttling

    • Added 1000ms throttle to all 14 persist configurations
    • Reduces IndexedDB write frequency during rapid state updates
    • Particularly impactful during WebSocket price updates and user interactions
  3. localStorage Migration for Small Slices

    • Moved 4 small slices to localStorage: preferences, localWallet, gridplus, addressBook
    • localStorage is synchronous and much faster than IndexedDB for small data
    • Remaining slices use IndexedDB via Web Worker for large data
  4. fast-deep-equal for Selector Memoization

    • Replaced lodash/isEqual with fast-deep-equal in createDeepEqualOutputSelector
    • 2-7x faster equality checks for JSON-like Redux state
    • Benefits all 157 selectors using deep equality across the codebase
  5. Performance Profiling Infrastructure (dev-only)

    • Visual overlay showing real-time IndexedDB, selector, and search metrics
    • Enables browser comparison testing
    • Controlled via VITE_FEATURE_PERFORMANCE_PROFILER flag

Performance Metrics

User Experience: UI Responsiveness

The main improvement is eliminating "jank" (UI freezing) caused by IndexedDB operations blocking the main thread.

Browser Before (develop) After (feature) User Experience
Firefox UI freezes for ~9.6 seconds total during page load as IndexedDB operations block the main thread UI stays responsive - IndexedDB work happens in background No more frozen UI
Chrome UI freezes for ~1.3 seconds total during page load UI stays responsive - IndexedDB work happens in background No more frozen UI

These freezes occurred in chunks (e.g., 50-200ms at a time) causing stuttering, dropped frames, and unresponsive buttons/inputs.

Page Load: Time to Interactive

Browser Before After Improvement
Firefox 1,707ms 919ms 46% faster - app is usable almost 1 second sooner
Chrome ~500ms ~500ms Already fast, minimal change

Ongoing Responsiveness: State Persistence

When you interact with the app (change settings, get quotes, etc.), state is saved to IndexedDB. Previously this caused micro-freezes.

Scenario Before After
Rapid price updates (WebSocket) UI stutters as each update triggers IndexedDB write Smooth - writes throttled to 1/second, off main thread
Searching assets Brief freeze on each keystroke Smooth - no main thread blocking
Switching between pages Momentary freeze as state persists Smooth - persistence happens in background

Selector Performance (fast-deep-equal)

Metric Improvement
Deep equality checks 2-7x faster
Affected selectors 157 selectors across the codebase

This reduces CPU time spent comparing Redux state, making all UI updates snappier.

Trade-offs

Metric Before After Notes
Memory usage (Chrome) 518 MB 797 MB Higher due to Web Worker + profiler. Acceptable trade-off for responsiveness.

Why Firefox Benefits Most

Chrome's IndexedDB is highly optimized and rarely blocks for long. Firefox's IndexedDB is significantly slower - operations that take 2ms in Chrome can take 50-200ms in Firefox. By moving these operations to a Web Worker, Firefox users see the biggest improvement: from a janky, freezing experience to a smooth, responsive app.

How to Verify

Quick test: Open the app in Firefox on develop branch, then on this branch. Notice:

  • Faster initial load
  • No stuttering when navigating
  • Smooth scrolling in asset lists
  • Responsive button clicks during state updates

DevTools test:

  1. Open Firefox DevTools → Performance tab
  2. Record while navigating the app
  3. On develop: Red "Long Task" bars during IndexedDB operations (UI was frozen)
  4. On this branch: Clean timeline, no long tasks from storage

Issue (if applicable)

N/A - Performance improvement initiative

Risk

Low-Medium Risk - These changes affect core state persistence and selector infrastructure, but:

  • Web Worker has automatic fallback to direct localforage if worker fails
  • fast-deep-equal handles all JSON-serializable data identically to lodash/isEqual
  • Profiling infrastructure is behind a feature flag (dev-only by default)
  • Throttling only affects persist frequency, not data integrity

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

All wallet connections and state persistence. The action slice (pending swaps, limit orders, bridge claims) remains persisted via IndexedDB/Worker to ensure user notifications survive page refresh.

Testing

Engineering

To test locally:

  1. yarn dev and open in browser
  2. Enable profiler: Set VITE_FEATURE_PERFORMANCE_PROFILER=true in .env.development
  3. Verify profiler overlay appears in bottom-right corner
  4. Test state persistence: change preferences, refresh page, verify they persist
  5. Test trade flow: search assets, get quotes
  6. Compare Firefox vs Chrome load times

Verification checklist:

  • App loads correctly in Chrome
  • App loads correctly in Firefox
  • App loads correctly in Safari
  • Redux state survives page refresh
  • User preferences persist across sessions
  • Pending swaps/limit orders (action slice) persist
  • yarn type-check passes
  • yarn lint passes
  • yarn test passes

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

The performance profiler is behind VITE_FEATURE_PERFORMANCE_PROFILER flag (disabled in production). All other changes are infrastructure improvements that don't change user-facing behavior.

Functional testing:

  • Verify app loads and is responsive
  • Test trade/swap flow works correctly
  • Verify wallet connections work
  • Check that settings/preferences persist after refresh

Screenshots (if applicable)

Performance Profiler Overlay (dev-only):

┌─────────────────────────────────────┐
│ Perf Profiler                    ─ │
├─────────────────────────────────────┤
│ Chrome 143.0                        │
│ Session: 12s                        │
├─────────────────────────────────────┤
│ IndexedDB                           │
│ Reads: 9 (avg: 2.13ms, max: 13.3ms) │
│ Writes: 12 (avg: 4.39ms, max: 35ms) │
├─────────────────────────────────────┤
│ Selectors                           │
│ Total calls: 0 | Slow (>16ms): 0    │
├─────────────────────────────────────┤
│ [Export] [Reset]                    │
└─────────────────────────────────────┘

🤖 Generated with Claude Code

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 9, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/firefox-performance-profiling-and-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

0xApotheosis and others added 5 commits January 14, 2026 09:17
- Add performance profiler with visual overlay for tracking IndexedDB,
  selectors, and asset search performance across browsers
- Add VITE_FEATURE_PERFORMANCE_PROFILER feature flag (dev-only)
- Add throttle (1000ms) to all 13 redux-persist configs to reduce
  write frequency and main thread blocking
- Move small slices (preferences, localWallet, gridplus, addressBook)
  to localStorage for faster reads vs IndexedDB
- Add createLocalStorageAdapter and createProfiledStorage utilities
- Instrument useAssetSearchWorker to track search latency

Profiling revealed Firefox IndexedDB reads are ~6x slower than Chrome
(9.5s vs 1.5s avg). These changes reduce IndexedDB operations and
improve runtime performance, though Firefox's core IndexedDB read
performance remains a browser limitation.

Co-Authored-By: Claude <[email protected]>
Add Web Worker for IndexedDB operations to prevent main thread blocking,
especially critical for Firefox where IndexedDB reads were ~6x slower.

- Add indexedDB.worker.ts - handles getItem/setItem/removeItem in worker
- Add workerStorage.ts - adapter that communicates with worker via postMessage
- Update reducer.ts to use worker storage instead of direct localforage

Results:
- Firefox: Read time reduced from 9,628ms to 109ms (99% improvement)
- Chrome: Read time reduced from 1,339ms to 2.18ms (99.8% improvement)

The worker moves IndexedDB operations off the main thread, keeping the UI
responsive while data loads in the background.

Co-Authored-By: Claude <[email protected]>
…ation

Switch from lodash/isEqual to fast-deep-equal for the resultEqualityCheck
in createDeepEqualOutputSelector. fast-deep-equal is 2-7x faster for
JSON-like data comparisons, which improves performance for all 157
selectors using deep equality checks across the codebase.

Co-Authored-By: Claude <[email protected]>
The worker was resolving init() immediately after construction, before
the worker script finished loading and localforage was ready. This caused
a 5-second timeout fallback on every page load.

Now uses a ping/ready handshake to ensure localforage.ready() completes
before processing any storage operations.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
The profiledStorage module was throwing ReferenceError in Node.js test
environment where localStorage is not defined. Added guards to safely
return early when localStorage is unavailable.

Co-Authored-By: Claude <[email protected]>
@0xApotheosis 0xApotheosis force-pushed the feat/firefox-performance-profiling-and-fixes branch from 049891c to 711743c Compare January 13, 2026 22:18
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.

2 participants