From 6c958f942b385d8b03cf557a7594b717742c26b9 Mon Sep 17 00:00:00 2001 From: Kriys94 Date: Mon, 16 Feb 2026 22:02:38 +0100 Subject: [PATCH 1/3] feat(assets-controller): full and merge assets updates --- .../assets-controller/src/AssetsController.ts | 47 +++++++++++++------ .../src/data-sources/AccountsApiDataSource.ts | 1 + .../BackendWebsocketDataSource.ts | 2 +- .../src/data-sources/PriceDataSource.ts | 5 +- .../src/data-sources/RpcDataSource.ts | 2 + .../src/data-sources/SnapDataSource.test.ts | 2 + .../src/data-sources/SnapDataSource.ts | 5 +- packages/assets-controller/src/index.ts | 1 + packages/assets-controller/src/types.ts | 15 ++++++ 9 files changed, 62 insertions(+), 18 deletions(-) diff --git a/packages/assets-controller/src/AssetsController.ts b/packages/assets-controller/src/AssetsController.ts index ba421627e37..25a92beb6e4 100644 --- a/packages/assets-controller/src/AssetsController.ts +++ b/packages/assets-controller/src/AssetsController.ts @@ -76,6 +76,7 @@ import { DetectionMiddleware } from './middlewares/DetectionMiddleware'; import type { AccountId, AssetPreferences, + AssetsUpdateMode, ChainId, Caip19AssetId, AssetMetadata, @@ -418,6 +419,10 @@ function normalizeResponse(response: DataResponse): DataResponse { normalized.errors = { ...response.errors }; } + if (response.updateMode) { + normalized.updateMode = response.updateMode; + } + return normalized; } @@ -924,7 +929,7 @@ export class AssetsController extends BaseController< sources, request, ); - await this.#updateState(response); + await this.#updateState({ ...response, updateMode: 'full' }); if (this.#trackMetaMetricsEvent && !this.#firstInitFetchReported) { this.#firstInitFetchReported = true; const durationMs = Date.now() - startTime; @@ -1199,8 +1204,8 @@ export class AssetsController extends BaseController< // ============================================================================ async #updateState(response: DataResponse): Promise { - // Normalize asset IDs (checksum EVM addresses) before storing in state const normalizedResponse = normalizeResponse(response); + const mode: AssetsUpdateMode = normalizedResponse.updateMode ?? 'merge'; const releaseLock = await this.#controllerMutex.acquire(); @@ -1248,20 +1253,35 @@ export class AssetsController extends BaseController< )) { const previousBalances = previousState.assetsBalance[accountId] ?? {}; - - if (!balances[accountId]) { - balances[accountId] = {}; - } - - for (const [assetId, balance] of Object.entries(accountBalances)) { + const customAssetIds = + (state.customAssets as Record)[ + accountId + ] ?? []; + + // Full: response is authoritative; preserve custom assets not in response. Merge: response overlays previous. + const effective: Record = + mode === 'full' + ? ((): Record => { + const next: Record = { + ...accountBalances, + }; + for (const customId of customAssetIds) { + if (!(customId in next)) { + const prev = previousBalances[customId]; + next[customId] = + prev ?? ({ amount: '0' } as AssetBalance); + } + } + return next; + })() + : { ...previousBalances, ...accountBalances }; + + for (const [assetId, balance] of Object.entries(effective)) { const previousBalance = previousBalances[ assetId as Caip19AssetId ] as { amount: string } | undefined; - const balanceData = balance as { amount: string }; - const newAmount = balanceData.amount; + const newAmount = (balance as { amount: string }).amount; const oldAmount = previousBalance?.amount; - - // Track if balance actually changed if (oldAmount !== newAmount) { changedBalances.push({ accountId, @@ -1271,8 +1291,7 @@ export class AssetsController extends BaseController< }); } } - - Object.assign(balances[accountId], accountBalances); + balances[accountId] = effective; } } diff --git a/packages/assets-controller/src/data-sources/AccountsApiDataSource.ts b/packages/assets-controller/src/data-sources/AccountsApiDataSource.ts index a0846e390eb..69a48da275b 100644 --- a/packages/assets-controller/src/data-sources/AccountsApiDataSource.ts +++ b/packages/assets-controller/src/data-sources/AccountsApiDataSource.ts @@ -325,6 +325,7 @@ export class AccountsApiDataSource extends AbstractDataSource< ); response.assetsBalance = assetsBalance; + response.updateMode = 'full'; } catch (error) { log('Fetch FAILED', { error, chains: chainsToFetch }); diff --git a/packages/assets-controller/src/data-sources/BackendWebsocketDataSource.ts b/packages/assets-controller/src/data-sources/BackendWebsocketDataSource.ts index 6d38ac55bb7..9d6c617109d 100644 --- a/packages/assets-controller/src/data-sources/BackendWebsocketDataSource.ts +++ b/packages/assets-controller/src/data-sources/BackendWebsocketDataSource.ts @@ -624,7 +624,7 @@ export class BackendWebsocketDataSource extends AbstractDataSource< }; } - const response: DataResponse = {}; + const response: DataResponse = { updateMode: 'merge' }; if (Object.keys(assetsBalance[accountId]).length > 0) { response.assetsBalance = assetsBalance; response.assetsInfo = assetsMetadata; diff --git a/packages/assets-controller/src/data-sources/PriceDataSource.ts b/packages/assets-controller/src/data-sources/PriceDataSource.ts index 3d635acf440..2560ed049a4 100644 --- a/packages/assets-controller/src/data-sources/PriceDataSource.ts +++ b/packages/assets-controller/src/data-sources/PriceDataSource.ts @@ -393,7 +393,10 @@ export class PriceDataSource { fetchResponse.assetsPrice && Object.keys(fetchResponse.assetsPrice).length > 0 ) { - await subscription.onAssetsUpdate(fetchResponse); + await subscription.onAssetsUpdate({ + ...fetchResponse, + updateMode: 'merge', + }); } } catch (error) { log('Subscription poll failed', { subscriptionId, error }); diff --git a/packages/assets-controller/src/data-sources/RpcDataSource.ts b/packages/assets-controller/src/data-sources/RpcDataSource.ts index 922f0bb40d8..78816a45c58 100644 --- a/packages/assets-controller/src/data-sources/RpcDataSource.ts +++ b/packages/assets-controller/src/data-sources/RpcDataSource.ts @@ -411,6 +411,7 @@ export class RpcDataSource extends AbstractDataSource< [result.accountId]: newBalances, }, assetsInfo, + updateMode: 'full', }; log('Balance update response', { @@ -483,6 +484,7 @@ export class RpcDataSource extends AbstractDataSource< assetsBalance: { [result.accountId]: newBalances, }, + updateMode: 'full', }; for (const subscription of this.#activeSubscriptions.values()) { diff --git a/packages/assets-controller/src/data-sources/SnapDataSource.test.ts b/packages/assets-controller/src/data-sources/SnapDataSource.test.ts index 561cfd440da..180c8e1ed8b 100644 --- a/packages/assets-controller/src/data-sources/SnapDataSource.test.ts +++ b/packages/assets-controller/src/data-sources/SnapDataSource.test.ts @@ -435,6 +435,7 @@ describe('SnapDataSource', () => { expect(response).toStrictEqual({ assetsBalance: {}, assetsInfo: {}, + updateMode: 'full', }); cleanup(); @@ -469,6 +470,7 @@ describe('SnapDataSource', () => { expect(response).toStrictEqual({ assetsBalance: {}, assetsInfo: {}, + updateMode: 'full', }); cleanup(); diff --git a/packages/assets-controller/src/data-sources/SnapDataSource.ts b/packages/assets-controller/src/data-sources/SnapDataSource.ts index 823daae02bd..b6e7adc8b41 100644 --- a/packages/assets-controller/src/data-sources/SnapDataSource.ts +++ b/packages/assets-controller/src/data-sources/SnapDataSource.ts @@ -298,7 +298,7 @@ export class SnapDataSource extends AbstractDataSource< // Only report if we have snap-related updates if (assetsBalance) { - const response: DataResponse = { assetsBalance }; + const response: DataResponse = { assetsBalance, updateMode: 'merge' }; for (const subscription of this.activeSubscriptions.values()) { subscription.onAssetsUpdate(response)?.catch(console.error); } @@ -439,12 +439,13 @@ export class SnapDataSource extends AbstractDataSource< return {}; } if (!request?.accountsWithSupportedChains?.length) { - return { assetsBalance: {}, assetsInfo: {} }; + return { assetsBalance: {}, assetsInfo: {}, updateMode: 'full' }; } const results: DataResponse = { assetsBalance: {}, assetsInfo: {}, + updateMode: 'full', }; // Fetch balances for each account using its snap ID from metadata diff --git a/packages/assets-controller/src/index.ts b/packages/assets-controller/src/index.ts index be2857c1b4c..ac43554d6e9 100644 --- a/packages/assets-controller/src/index.ts +++ b/packages/assets-controller/src/index.ts @@ -66,6 +66,7 @@ export type { DataType, DataRequest, DataResponse, + AssetsUpdateMode, // Middleware types Context, NextFunction, diff --git a/packages/assets-controller/src/types.ts b/packages/assets-controller/src/types.ts index 5365db54c1c..8a63f91e1e8 100644 --- a/packages/assets-controller/src/types.ts +++ b/packages/assets-controller/src/types.ts @@ -360,8 +360,23 @@ export type DataResponse = { errors?: Record; /** Detected assets (assets that do not have metadata) */ detectedAssets?: Record; + /** + * How to apply this response to state. See {@link AssetsUpdateMode}. + * Defaults to `'merge'` if omitted. + */ + updateMode?: AssetsUpdateMode; }; +/** + * Type of {@link DataResponse.updateMode}: how the controller applies the response to state. + * + * - **full**: Response is the full set for the scope. Assets in state but not in the + * response are cleared (except custom assets). Use for initial fetch or full refresh. + * - **merge**: Only assets present in the response are updated; nothing is removed. + * Use for event-driven or incremental updates. + */ +export type AssetsUpdateMode = 'full' | 'merge'; + // ============================================================================ // DATA SOURCE <-> CONTROLLER (DIRECT CALLS, NO MESSENGER PER SOURCE) // ============================================================================ From 21f9115d65147faa8b34a8f375d039cd9483118f Mon Sep 17 00:00:00 2001 From: Kriys94 Date: Tue, 17 Feb 2026 11:14:20 +0100 Subject: [PATCH 2/3] feat(assets-controller): parallel balances and cleaning --- .../assets-controller/src/AssetsController.ts | 11 +- .../src/middlewares/index.ts | 5 + .../middlewares/parallelBalanceMiddleware.ts | 222 ++++++++++++++++++ 3 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 packages/assets-controller/src/middlewares/parallelBalanceMiddleware.ts diff --git a/packages/assets-controller/src/AssetsController.ts b/packages/assets-controller/src/AssetsController.ts index 25a92beb6e4..11b76c4891e 100644 --- a/packages/assets-controller/src/AssetsController.ts +++ b/packages/assets-controller/src/AssetsController.ts @@ -73,6 +73,7 @@ import { StakedBalanceDataSource } from './data-sources/StakedBalanceDataSource' import { TokenDataSource } from './data-sources/TokenDataSource'; import { projectLogger, createModuleLogger } from './logger'; import { DetectionMiddleware } from './middlewares/DetectionMiddleware'; +import { createParallelBalanceMiddleware } from './middlewares/parallelBalanceMiddleware'; import type { AccountId, AssetPreferences, @@ -912,10 +913,12 @@ export class AssetsController extends BaseController< }); const sources = this.#isBasicFunctionality() ? [ - this.#accountsApiDataSource, - this.#snapDataSource, - this.#rpcDataSource, - this.#stakedBalanceDataSource, + createParallelBalanceMiddleware([ + this.#accountsApiDataSource, + this.#snapDataSource, + this.#rpcDataSource, + this.#stakedBalanceDataSource, + ]), this.#detectionMiddleware, this.#tokenDataSource, this.#priceDataSource, diff --git a/packages/assets-controller/src/middlewares/index.ts b/packages/assets-controller/src/middlewares/index.ts index 2c54fa8b313..a2f9c7f7a6e 100644 --- a/packages/assets-controller/src/middlewares/index.ts +++ b/packages/assets-controller/src/middlewares/index.ts @@ -1 +1,6 @@ export { DetectionMiddleware } from './DetectionMiddleware'; +export { + createParallelBalanceMiddleware, + mergeDataResponses, +} from './parallelBalanceMiddleware'; +export type { BalanceSource } from './parallelBalanceMiddleware'; \ No newline at end of file diff --git a/packages/assets-controller/src/middlewares/parallelBalanceMiddleware.ts b/packages/assets-controller/src/middlewares/parallelBalanceMiddleware.ts new file mode 100644 index 00000000000..be6f468e827 --- /dev/null +++ b/packages/assets-controller/src/middlewares/parallelBalanceMiddleware.ts @@ -0,0 +1,222 @@ +import type { ChainId, DataRequest, DataResponse, Middleware } from '../types'; + +// ============================================================================ +// MERGE HELPER +// ============================================================================ + +/** + * Deep-merge multiple DataResponses into one. + * Used when running balance data sources in parallel. + * + * @param responses - Array of DataResponse from each source. + * @returns Single merged DataResponse. + */ +export function mergeDataResponses(responses: DataResponse[]): DataResponse { + const merged: DataResponse = {}; + + for (const r of responses) { + if (r.assetsBalance) { + merged.assetsBalance ??= {}; + for (const [accountId, accountBalances] of Object.entries( + r.assetsBalance, + )) { + merged.assetsBalance[accountId] = { + ...(merged.assetsBalance[accountId] ?? {}), + ...accountBalances, + }; + } + } + if (r.assetsInfo) { + merged.assetsInfo = { + ...(merged.assetsInfo ?? {}), + ...r.assetsInfo, + }; + } + if (r.assetsPrice) { + merged.assetsPrice = { + ...(merged.assetsPrice ?? {}), + ...r.assetsPrice, + }; + } + if (r.errors) { + merged.errors = { + ...(merged.errors ?? {}), + ...r.errors, + }; + } + if (r.detectedAssets) { + merged.detectedAssets = { + ...(merged.detectedAssets ?? {}), + ...r.detectedAssets, + }; + } + if (r.updateMode === 'full') { + merged.updateMode = 'full'; + } + } + if (merged.updateMode === undefined) { + merged.updateMode = 'merge'; + } + + return merged; +} + +// ============================================================================ +// PARALLEL BALANCE MIDDLEWARE +// ============================================================================ + +const PARALLEL_BALANCE_MIDDLEWARE_NAME = 'ParallelBalanceMiddleware'; + +export type BalanceSource = { + getName(): string; + /** Chains this source can fetch (e.g. from getActiveChainsSync()). Used to partition chains with no overlap. */ + getActiveChainsSync(): ChainId[]; + assetsMiddleware: Middleware; +}; + +/** + * Partition request.chainIds so each chain is assigned to exactly one source + * (by source order: first source that supports the chain gets it). Ensures no + * chain overlap across data source calls. + * + * @param request - The data request with chainIds to partition. + * @param sources - Balance sources in priority order (e.g. AccountsAPI, Snap, Rpc). + * @returns Array of requests, one per source, each with only that source's assigned chainIds. + */ +function partitionChainsBySource( + request: DataRequest, + sources: BalanceSource[], +): DataRequest[] { + const chainIds = request.chainIds; + const assigned = new Set(); + + return sources.map((source) => { + const supported = new Set(source.getActiveChainsSync()); + const chainsForSource = chainIds.filter( + (id) => supported.has(id) && !assigned.has(id), + ); + chainsForSource.forEach((id) => assigned.add(id)); + + return { + ...request, + chainIds: chainsForSource, + }; + }); +} + +/** + * Collect chain IDs that failed in the first round (present in response.errors). + * Used to run a fallback round with remaining sources. + */ +function getFailedChainIds( + requests: DataRequest[], + results: { response: DataResponse }[], +): Set { + const failed = new Set(); + for (let i = 0; i < results.length; i++) { + const errors = results[i].response.errors ?? {}; + for (const chainId of requests[i].chainIds) { + if (errors[chainId]) { + failed.add(chainId); + } + } + } + return failed; +} + +/** + * Middleware that runs multiple balance data source middlewares in parallel, + * with no chain overlap. Chains that fail (response.errors) are re-partitioned + * and fetched again in a fallback round so lower-priority sources can try them. + * + * @param sources - Array of balance sources in priority order (each with getName(), getActiveChainsSync(), assetsMiddleware). + * @returns A single middleware that runs all sources in parallel and merges responses. + */ +export function createParallelBalanceMiddleware( + sources: BalanceSource[], +): { getName(): string; assetsMiddleware: Middleware } { + return { + getName(): string { + return PARALLEL_BALANCE_MIDDLEWARE_NAME; + }, + + assetsMiddleware: async (context, next) => { + if (sources.length === 0) { + return next(context); + } + + const noopNext = async (ctx: typeof context): Promise => + ctx; + + // Round 1: partition chains (no overlap), run all in parallel + const requests = partitionChainsBySource(context.request, sources); + const results = await Promise.all( + sources.map((source, i) => + source.assetsMiddleware( + { + request: requests[i], + response: {}, + getAssetsState: context.getAssetsState, + }, + noopNext, + ), + ), + ); + + let mergedResponse = mergeDataResponses( + results.map((r) => r.response), + ); + + // Fallback: chains that failed (in errors) get re-partitioned and tried again + const failedChainIds = getFailedChainIds(requests, results); + if (failedChainIds.size > 0) { + const fallbackRequest: DataRequest = { + ...context.request, + chainIds: [...failedChainIds], + }; + const fallbackRequests = partitionChainsBySource( + fallbackRequest, + sources, + ); + const fallbackResults = await Promise.all( + sources.map((source, i) => + source.assetsMiddleware( + { + request: fallbackRequests[i], + response: {}, + getAssetsState: context.getAssetsState, + }, + noopNext, + ), + ), + ); + const fallbackMerged = mergeDataResponses( + fallbackResults.map((r) => r.response), + ); + mergedResponse = mergeDataResponses([mergedResponse, fallbackMerged]); + // Remove errors for chains we successfully got balance for in fallback + if (mergedResponse.errors && mergedResponse.assetsBalance) { + const chainsWithBalance = new Set(); + for (const accountBalances of Object.values( + mergedResponse.assetsBalance, + )) { + for (const assetId of Object.keys(accountBalances)) { + const chainId = assetId.split('/')[0] as ChainId; + chainsWithBalance.add(chainId); + } + } + for (const chainId of failedChainIds) { + if (chainsWithBalance.has(chainId)) { + delete mergedResponse.errors[chainId]; + } + } + } + } + + return next({ + ...context, + response: mergeDataResponses([context.response, mergedResponse]), + }); + }, + }; +} From 94338fd492b22b4fda18d694c69f431d7b81fa8e Mon Sep 17 00:00:00 2001 From: Kriys94 Date: Wed, 18 Feb 2026 15:42:26 +0100 Subject: [PATCH 3/3] add ClientController --- packages/assets-controller/CHANGELOG.md | 4 ++ packages/assets-controller/package.json | 1 + .../assets-controller/src/AssetsController.ts | 50 ++++++++++++++++--- .../assets-controller/tsconfig.build.json | 5 +- packages/assets-controller/tsconfig.json | 5 +- yarn.lock | 3 +- 6 files changed, 57 insertions(+), 11 deletions(-) diff --git a/packages/assets-controller/CHANGELOG.md b/packages/assets-controller/CHANGELOG.md index 45b88bf92ff..a10f0255789 100644 --- a/packages/assets-controller/CHANGELOG.md +++ b/packages/assets-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `@metamask/client-controller` dependency and subscribe to `ClientController:stateChange`. Asset tracking runs only when the UI is open (ClientController) and the keyring is unlocked (KeyringController), and stops when either the UI closes or the keyring locks (Client + Keyring lifecycle). + ### Changed - Refactor data source tests to use shared `MockAssetControllerMessenger` fixture ([#7958](https://github.com/MetaMask/core/pull/7958)) diff --git a/packages/assets-controller/package.json b/packages/assets-controller/package.json index c86f09b8002..21b7cb04285 100644 --- a/packages/assets-controller/package.json +++ b/packages/assets-controller/package.json @@ -54,6 +54,7 @@ "@metamask/account-tree-controller": "^4.1.1", "@metamask/assets-controllers": "^99.4.0", "@metamask/base-controller": "^9.0.0", + "@metamask/client-controller": "^1.0.0", "@metamask/controller-utils": "^11.18.0", "@metamask/core-backend": "^5.1.1", "@metamask/keyring-api": "^21.5.0", diff --git a/packages/assets-controller/src/AssetsController.ts b/packages/assets-controller/src/AssetsController.ts index 11b76c4891e..0af3bac2be9 100644 --- a/packages/assets-controller/src/AssetsController.ts +++ b/packages/assets-controller/src/AssetsController.ts @@ -14,6 +14,8 @@ import type { BackendWebSocketServiceActions, BackendWebSocketServiceEvents, } from '@metamask/core-backend'; +import type { ClientControllerStateChangeEvent } from '@metamask/client-controller'; +import { clientControllerSelectors } from '@metamask/client-controller'; import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent, @@ -227,6 +229,7 @@ type AllowedActions = type AllowedEvents = // AssetsController | AccountTreeControllerSelectedAccountGroupChangeEvent + | ClientControllerStateChangeEvent | KeyringControllerLockEvent | KeyringControllerUnlockEvent | PreferencesControllerStateChangeEvent @@ -447,8 +450,10 @@ function normalizeResponse(response: DataResponse): DataResponse { * based on which chains they support. When active chains change, the controller * dynamically adjusts subscriptions. * - * 4. **Keyring Lifecycle**: Listens to KeyringController unlock/lock events to - * start/stop subscriptions when the wallet is unlocked or locked. + * 4. **Client + Keyring Lifecycle**: Starts subscriptions only when both the UI is + * open (ClientController) and the wallet is unlocked (KeyringController). + * Stops when either the UI closes or the keyring locks. See client-controller + * README for the combined pattern. * * ## Architecture * @@ -478,6 +483,12 @@ export class AssetsController extends BaseController< /** Whether we have already reported first init fetch for this session (reset on #stop). */ #firstInitFetchReported = false; + /** Whether the client (UI) is open. Combined with #keyringUnlocked for #updateActive. */ + #uiOpen = false; + + /** Whether the keyring is unlocked. Combined with #uiOpen for #updateActive. */ + #keyringUnlocked = false; + readonly #controllerMutex = new Mutex(); /** @@ -627,7 +638,7 @@ export class AssetsController extends BaseController< this.#initializeState(); this.#subscribeToEvents(); this.#registerActionHandlers(); - // Subscriptions start only on KeyringController:unlock -> #start(), not here. + // Subscriptions start only when both UI is open and keyring unlocked -> #updateActive(). // Subscribe to basic-functionality changes after construction so a synchronous // onChange during subscribe cannot run before data sources are initialized. @@ -728,9 +739,36 @@ export class AssetsController extends BaseController< }, ); - // Keyring lifecycle: start when unlocked, stop when locked - this.messenger.subscribe('KeyringController:unlock', () => this.#start()); - this.messenger.subscribe('KeyringController:lock', () => this.#stop()); + // Client + Keyring lifecycle: only run when UI is open AND keyring is unlocked + this.messenger.subscribe( + 'ClientController:stateChange', + (isUiOpen: boolean) => { + this.#uiOpen = isUiOpen; + this.#updateActive(); + }, + clientControllerSelectors.selectIsUiOpen, + ); + this.messenger.subscribe('KeyringController:unlock', () => { + this.#keyringUnlocked = true; + this.#updateActive(); + }); + this.messenger.subscribe('KeyringController:lock', () => { + this.#keyringUnlocked = false; + this.#updateActive(); + }); + } + + /** + * Start or stop asset tracking based on client (UI) open state and keyring + * unlock state. Only runs when both UI is open and keyring is unlocked. + */ + #updateActive(): void { + const shouldRun = this.#uiOpen && this.#keyringUnlocked; + if (shouldRun) { + this.#start(); + } else { + this.#stop(); + } } #registerActionHandlers(): void { diff --git a/packages/assets-controller/tsconfig.build.json b/packages/assets-controller/tsconfig.build.json index 3dd2cab2e64..e826cd3f491 100644 --- a/packages/assets-controller/tsconfig.build.json +++ b/packages/assets-controller/tsconfig.build.json @@ -7,14 +7,15 @@ }, "references": [ { "path": "../account-tree-controller/tsconfig.build.json" }, + { "path": "../assets-controllers/tsconfig.build.json" }, { "path": "../base-controller/tsconfig.build.json" }, + { "path": "../client-controller/tsconfig.build.json" }, { "path": "../core-backend/tsconfig.build.json" }, { "path": "../keyring-controller/tsconfig.build.json" }, { "path": "../messenger/tsconfig.build.json" }, { "path": "../network-enablement-controller/tsconfig.build.json" }, { "path": "../permission-controller/tsconfig.build.json" }, - { "path": "../preferences-controller/tsconfig.build.json" }, - { "path": "../assets-controllers/tsconfig.build.json" } + { "path": "../preferences-controller/tsconfig.build.json" } ], "include": ["../../types", "./src"], "exclude": ["**/*.test.ts", "**/__fixtures__/"] diff --git a/packages/assets-controller/tsconfig.json b/packages/assets-controller/tsconfig.json index 5fa36386931..0b58b6c2916 100644 --- a/packages/assets-controller/tsconfig.json +++ b/packages/assets-controller/tsconfig.json @@ -5,13 +5,14 @@ }, "references": [ { "path": "../account-tree-controller" }, + { "path": "../assets-controllers" }, { "path": "../base-controller" }, + { "path": "../client-controller" }, { "path": "../core-backend" }, { "path": "../keyring-controller" }, { "path": "../messenger" }, { "path": "../network-enablement-controller" }, - { "path": "../preferences-controller" }, - { "path": "../assets-controllers" } + { "path": "../preferences-controller" } ], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index 8573cca7a31..a584786a945 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2517,6 +2517,7 @@ __metadata: "@metamask/assets-controllers": "npm:^99.4.0" "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^9.0.0" + "@metamask/client-controller": "npm:^1.0.0" "@metamask/controller-utils": "npm:^11.18.0" "@metamask/core-backend": "npm:^5.1.1" "@metamask/keyring-api": "npm:^21.5.0" @@ -2859,7 +2860,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/client-controller@workspace:packages/client-controller": +"@metamask/client-controller@npm:^1.0.0, @metamask/client-controller@workspace:packages/client-controller": version: 0.0.0-use.local resolution: "@metamask/client-controller@workspace:packages/client-controller" dependencies: