Skip to content

Conversation

@sahar-fehri
Copy link
Contributor

@sahar-fehri sahar-fehri commented Dec 9, 2025

Description

Optimizes TokenListController storage to reduce write amplification by persisting tokensChainsCache via StorageService using per-chain files instead of a single monolithic state property.

Related: https://github.com/MetaMask/metamask-mobile/pull/22943/files

Related: https://github.com/MetaMask/decisions/pull/110

Related: #7192

Explanation

The tokensChainsCache (~5MB total, containing token lists for all chains) was persisted as part of the controller state. Every time a single chain's token list was updated (~100-500KB), the entire ~5MB cache was rewritten to disk, causing:

  • Startup performance issues (loading large state on app initialization)
  • Runtime performance degradation (frequent large writes during token fetches)
  • Impacts both extension

Solution

Per-Chain File Storage:
Each chain's cache is now stored in a separate file (e.g., tokensChainsCache:0x1, tokensChainsCache:0x89)
Only the updated chain (~100-500KB) is written on each token fetch, reducing write operations by ~90-95%
All chains are loaded in parallel at startup to maintain compatibility with TokenDetectionController
Key Changes:

  • Set tokensChainsCache metadata to persist: false to prevent framework-managed persistence
  • Added #loadCacheFromStorage() to load all per-chain files in parallel on initialization
  • Added #saveChainCacheToStorage(chainId) to persist only the specific chain that changed
  • Added #migrateStateToStorage() to automatically migrate existing cache data on first launch after upgrade
  • Updated clearingTokenListData() to remove all per-chain files

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

TokenListController now persists tokensChainsCache to StorageService as per‑chain files with initialization load/migration, safer async clearing, and comprehensive tests; adds storage-service dependency and updates metadata/types.

  • Assets Controllers (TokenListController)
    • Persist tokensChainsCache via StorageService using per‑chain keys (tokensChainsCache:<chainId>); stop framework persistence (persist: false).
    • Add init flow: load all chains from storage in parallel and migrate any state-held cache to per‑chain files.
    • Save only the updated chain on fetch; avoid timestamp updates on failed fetches with existing cache.
    • Implement async clearingTokenListData() to remove all per‑chain files (handles partial failures with Promise.allSettled).
    • Add mutex and initialization promise to prevent race conditions; use private fields; refine polling start/stop helpers.
    • Expand messenger allowed actions to StorageService:getItem|setItem|removeItem|getAllKeys.
  • Tests
    • Add extensive StorageService load/save/clear/migration and error‑path coverage; introduce mock storage handlers; await clearingTokenListData(); minor type fixes.
  • Meta/Deps
    • CHANGELOG: mark BREAKING change describing new persistence strategy.
    • Add dependency @metamask/storage-service and TypeScript project references; update yarn.lock.

Written by Cursor Bugbot for commit eb78626. This will update automatically on new commits. Configure here.

tokensChainsCache: {
includeInStateLogs: false,
persist: true,
persist: false, // Persisted separately via StorageService
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Making this false to block disk writes

salimtb
salimtb previously approved these changes Dec 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants