feat(authenticated-user-storage): add watchlist support#8836
Open
Prithpal-Sooriya wants to merge 1 commit into
Open
feat(authenticated-user-storage): add watchlist support#8836Prithpal-Sooriya wants to merge 1 commit into
Prithpal-Sooriya wants to merge 1 commit into
Conversation
e4e6ad4 to
c4d9e42
Compare
c4d9e42 to
bd2b5db
Compare
10 tasks
pull Bot
pushed a commit
to Dustin4444/metamask-mobile
that referenced
this pull request
May 18, 2026
…SETS-3115] (MetaMask#30337) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## **Description** Adds the initial **Storage Integration** layer for the WatchList feature, as scoped by [ASSETS-3115](https://consensyssoftware.atlassian.net/browse/ASSETS-3115) and the [WatchList Tech Spec](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/401467637802/WatchList+Tech+Spec+-+Technical+Breakdown+Tasks#block-ca5ff63649b3). The [AUS SDK changes](MetaMask/core#8836) are not yet available, so this PR introduces a temporary local-device implementation backed by the existing MMKV-based `StorageWrapper`. The async API and schema-validation contract are designed to remain stable, so the body of the two exported functions can later be swapped to delegate to the AUS SDK without changes for callers. New module: `app/components/UI/assets/watchlist/storage.ts` - `WatchlistBlobSchema` — `@metamask/superstruct` schema describing the persisted blob: - `assets`: defaulted `array(string())` - `version`: defaulted `literal(1)` - `WatchlistBlob` — type inferred from the schema. - `WATCHLIST_STORAGE_PATH = 'watchlistV1.tokens'` and `EMPTY_BLOB = { assets: [], version: 1 }` constants (matching the tech-spec snippet). - `readFromTokenWatchList(): Promise<WatchlistBlob>` — reads from `StorageWrapper`, returns `EMPTY_BLOB` when no value is stored, otherwise `JSON.parse`s and validates through `WatchlistBlobSchema` (applying schema defaults) before returning. Per ticket AC: _"must parse through the defined schema before client can use it"_. - `writeToTokenWatchList(blob): Promise<void>` — validates the input through `WatchlistBlobSchema` first, then `JSON.stringify`s and writes via `StorageWrapper`. Per ticket AC: _"must validate input before stringify and store"_. Note on file path: the ticket asks for `app/components/UI/assets/watchlist/storage.ts` (lowercase `assets/`). There is also an existing `app/components/UI/Assets/` (capital `A`). On case-insensitive filesystems (macOS APFS default, Windows NTFS default) these two folders will collide. Flagging for awareness — happy to rename to `Assets/watchlist/` (or another path) if preferred. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: [ASSETS-3115](https://consensyssoftware.atlassian.net/browse/ASSETS-3115) Parent story: [ASSETS-2912 — \[Mobile\]\[WatchList\] Allow users to add, remove, and view their watchlisted tokens](https://consensyssoftware.atlassian.net/browse/ASSETS-2912) Tech Spec: [WatchList Tech Spec — Technical Breakdown Tasks](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/401467637802/WatchList+Tech+Spec+-+Technical+Breakdown+Tasks#block-ca5ff63649b3) Related SDK PR (future swap-in): MetaMask/core#8836 ## **Manual testing steps** This PR introduces internal utility functions with no UI surface yet, so there is nothing to test manually in-app. Verified via unit tests: ```gherkin Feature: WatchList local-device storage utilities Scenario: read with empty storage Given the WATCHLIST_STORAGE_PATH key is unset When the caller invokes readFromTokenWatchList() Then the result is EMPTY_BLOB ({ assets: [], version: 1 }) Scenario: read with valid stored blob Given a valid JSON-serialized WatchlistBlob is stored at WATCHLIST_STORAGE_PATH When the caller invokes readFromTokenWatchList() Then the parsed blob is returned with schema defaults applied where fields are missing Scenario: read with invalid stored data Given the stored value has the wrong shape (e.g. non-string asset entries, or version !== 1) When the caller invokes readFromTokenWatchList() Then the promise rejects (no silently-malformed data reaches callers) Scenario: write with a valid blob Given a valid WatchlistBlob When the caller invokes writeToTokenWatchList(blob) Then StorageWrapper.setItem is called with WATCHLIST_STORAGE_PATH and the JSON-stringified blob Scenario: write with an invalid blob Given a malformed blob (wrong asset type, wrong version, or wrong shape) When the caller invokes writeToTokenWatchList(blob) Then the promise rejects and StorageWrapper.setItem is NOT called ``` ## **Screenshots/Recordings** ### **Before** N/A — new utility module, no UI. ### **After** N/A — new utility module, no UI. ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable (`storage.test.ts`, 17 cases, all passing) - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - [ ] I've tested with a power user scenario - [ ] I've instrumented key operations with Sentry traces for production performance metrics Not applicable — internal storage helper, no perf-critical UI path or new in-app surface. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-0ed5d6b3-936f-4ff8-aa9f-c7ab0f588eea"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-0ed5d6b3-936f-4ff8-aa9f-c7ab0f588eea"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div> [ASSETS-3115]: https://consensyssoftware.atlassian.net/browse/ASSETS-3115?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [ASSETS-3115]: https://consensyssoftware.atlassian.net/browse/ASSETS-3115?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
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.
Explanation
https://consensyssoftware.atlassian.net/browse/ASSETS-3219
Add watchlist support to the AUS SDK.
References
Checklist
Note
Medium Risk
Adds a new persisted user-data domain with new network requests, caching, and runtime validation; risk is mainly around API contract/validation mismatches and cache invalidation behavior.
Overview
Adds assets watchlist support to the authenticated-user-storage SDK via new
getAssetsWatchlist(returnsnullon 404) andsetAssetsWatchlistmethods plus corresponding messenger actions.Introduces an
AssetsWatchlistBlobschema/type and exportsASSETS_WATCHLIST_MAX_ASSETS(100), enforcing the cap on writes via superstruct while keeping reads lenient. Updates tests/fixtures/mocks to cover headers, error handling, caching, and invalidation, and refreshes README/CHANGELOG to document the new domain.Reviewed by Cursor Bugbot for commit bd2b5db. Bugbot is set up for automated code reviews on this repo. Configure here.