Skip to content

Commit 0e0cb25

Browse files
chore(runway): cherry-pick fix(perps): fix HIP-3 asset ID lookup failure from dual-cache desync cp-7.70.1 (#27860)
- fix(perps): fix HIP-3 asset ID lookup failure from dual-cache desync cp-7.70.1 (#27854) ## **Description** Fix HIP-3 asset ID lookup failure (`"Asset ID not found for xyz:BRENTOIL"`) that blocked trading on HIP-3 markets when navigating via the old Perps tab layout. **Root cause**: Dual-cache desync between `#cachedValidatedDexs` (string DEX names) and `#cachedAllPerpDexs` (raw API objects for `perpDexIndex` computation). The standalone preload path (`#getStandaloneValidatedDexs`) populated one cache but not the other. When `#buildAssetMapping` later ran, it found "xyz" in `dexsToMap` but couldn't compute its `perpDexIndex` because `#cachedAllPerpDexs` was null. **Why old Perps tab vs new Homepage Sections**: Both layouts sit inside `Wallet/index.tsx`, which calls `startMarketDataPreload()` on mount. This fires standalone HTTP calls that populate `#cachedValidatedDexs` but not `#cachedAllPerpDexs`. - **New homepage sections**: `PerpsSectionWithProvider` mounts immediately. Stream hooks fire `ensureReady()` before or concurrently with the standalone preload. Since `#cachedValidatedDexs` is often still null, `fetchValidatedDexsInternal` runs fresh and sets **both** caches correctly. - **Old tab layout**: The Perps tab doesn't mount until the user taps it. By that time, `startMarketDataPreload()` has already completed → `#cachedValidatedDexs` is populated by standalone. When the tab mounts → `getValidatedDexs()` → **cache hit** → `fetchValidatedDexsInternal` is never called → `#cachedAllPerpDexs` stays null → `buildAssetMapping` can't find "xyz". **Changes (1 file, 3 sites)**: 1. **Root cause fix**: `#getStandaloneValidatedDexs` now sets `this.#cachedAllPerpDexs = allDexs` after a successful `perpDexs()` call, keeping both caches in sync. 2. **Cache poisoning fix**: Removed `this.#cachedAllPerpDexs = this.#cachedAllPerpDexs ?? [null]` from the catch block in `#buildAssetMapping`. 3. **Cache poisoning fix**: Replaced persistent `if (!cache) { cache = [null] }` with local `const allPerpDexs = cache ?? [null]` — consumers read the cache, only the owner writes it. ## **Changelog** CHANGELOG entry: Fixed a bug where closing positions on HIP-3 markets (e.g., xyz:BRENTOIL) failed with "Asset ID not found" when navigating via the Perps tab ## **Related issues** Fixes: HIP-3 asset ID lookup failure on old Perps tab layout ## **Manual testing steps** ```gherkin Feature: HIP-3 position management via Perps tab Scenario: user closes a HIP-3 position from the old Perps tab Given user has an open position on a HIP-3 market (e.g., xyz:BRENTOIL) And user is using the old tab layout (homepage redesign v1 disabled) When user navigates to the Perps tab And user taps close on the xyz:BRENTOIL position Then the position closes successfully without "Asset ID not found" error Scenario: user opens a HIP-3 position from the old Perps tab Given user is on the Perps tab (old layout) When user navigates to xyz:BRENTOIL market and places a market order Then the order executes successfully with correct asset ID routing ``` ## **Screenshots/Recordings** ### **Before** Metro logs show the desync: ``` getValidatedDexs CACHE HIT {"cachedAllNull": true, "dexs": [null, "xyz"]} buildAssetMapping state {"allPerpDexsLen": 1, "cachedAllNull": true} Could not find perpDexIndex for DEX xyz Asset ID not found for xyz:BRENTOIL ``` ### **After** Metro logs show both caches in sync: ``` buildAssetMapping state {"allPerpDexsLen": 8, "cachedAllNull": false, "dexsToMap": [null, "xyz"]} Asset map state at order time {"assetExistsInMap": true, "hip3AssetsCount": 54, "totalAssetsInMap": 283} Resolved DEX-specific asset ID {"assetId": 110049, "coin": "xyz:BRENTOIL"} usePerpsClosePosition: Close result {"success": true, "orderId": "359617825254"} ``` ## **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 - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] 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. ## **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_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches HIP-3 market routing/asset-ID mapping in `HyperLiquidProvider`, so a mistake could break trading on some perps markets; scope is small and localized to cache population/fallback behavior. > > **Overview** > Fixes a HIP-3 asset mapping failure where `#cachedValidatedDexs` could be populated via the standalone preload path while `#cachedAllPerpDexs` stayed `null`, leading to missing `perpDexIndex` during `#buildAssetMapping`. > > `#getStandaloneValidatedDexs()` now also populates `#cachedAllPerpDexs` after a successful `perpDexs()` call, and `#buildAssetMapping()` no longer “poisons” the shared cache with a persistent `[null]` fallback (it uses a local fallback instead). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit c925609. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> [2898ec8](2898ec8) Co-authored-by: abretonc7s <107169956+abretonc7s@users.noreply.github.com>
1 parent 8c73c19 commit 0e0cb25

1 file changed

Lines changed: 10 additions & 7 deletions

File tree

app/controllers/perps/providers/HyperLiquidProvider.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,16 +2119,13 @@ export class HyperLiquidProvider implements PerpsProvider {
21192119
'[buildAssetMapping] getValidatedDexs failed, falling back to main DEX',
21202120
{ error: String(dexError) },
21212121
);
2122-
this.#cachedAllPerpDexs = this.#cachedAllPerpDexs ?? [null];
21232122
dexsToMap = [null];
21242123
}
21252124

2126-
// Use cached perpDexs array (populated by getValidatedDexs)
2127-
// Defensive: ensure non-null even if getValidatedDexs had an unexpected issue
2128-
if (!this.#cachedAllPerpDexs) {
2129-
this.#cachedAllPerpDexs = [null];
2130-
}
2131-
const allPerpDexs = this.#cachedAllPerpDexs;
2125+
// Local fallback only — never write [null] into #cachedAllPerpDexs here.
2126+
// That cache is owned exclusively by #fetchValidatedDexsInternal; writing a
2127+
// fallback here would prevent subsequent callers from retrying perpDexs().
2128+
const allPerpDexs = this.#cachedAllPerpDexs ?? [null];
21322129

21332130
this.#deps.debugLogger.log(
21342131
'HyperLiquidProvider: Starting asset mapping rebuild',
@@ -4772,6 +4769,12 @@ export class HyperLiquidProvider implements PerpsProvider {
47724769
return [null];
47734770
}
47744771

4772+
// Populate #cachedAllPerpDexs so buildAssetMapping can compute perpDexIndex.
4773+
// Without this, getValidatedDexs returns from #cachedValidatedDexs (string names)
4774+
// but #cachedAllPerpDexs (raw objects for index computation) stays null,
4775+
// causing "Could not find perpDexIndex for DEX xyz" failures.
4776+
this.#cachedAllPerpDexs = allDexs;
4777+
47754778
// Extract HIP-3 DEX names (filter out null which represents main DEX)
47764779
const availableHip3Dexs: string[] = [];
47774780
allDexs.forEach((dex) => {

0 commit comments

Comments
 (0)