From 438645c19b4325fe01f215f72e1764d2f45903c7 Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Fri, 10 Jul 2026 00:48:48 +0200 Subject: [PATCH 1/2] fix: merge account balances preserve fields --- .../src/AssetsController.test.ts | 34 +++++++++++++++++++ .../assets-controller/src/AssetsController.ts | 11 +++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/assets-controller/src/AssetsController.test.ts b/packages/assets-controller/src/AssetsController.test.ts index 8fbd85f741..f9aa60c4ce 100644 --- a/packages/assets-controller/src/AssetsController.test.ts +++ b/packages/assets-controller/src/AssetsController.test.ts @@ -1807,6 +1807,40 @@ describe('AssetsController', () => { }); }); + it('preserves extra balance fields when merge update changes amount', async () => { + const initialState: Partial = { + assetsBalance: { + [MOCK_ACCOUNT_ID]: { + [MOCK_ASSET_ID]: { + amount: '1', + metadata: { limit: '1000', authorized: true }, + }, + }, + }, + }; + + await withController({ state: initialState }, async ({ controller }) => { + await controller.handleAssetsUpdate( + { + updateMode: 'merge', + assetsBalance: { + [MOCK_ACCOUNT_ID]: { + [MOCK_ASSET_ID]: { amount: '2' }, + }, + }, + }, + 'TestSource', + ); + + expect( + controller.state.assetsBalance[MOCK_ACCOUNT_ID]?.[MOCK_ASSET_ID], + ).toStrictEqual({ + amount: '2', + metadata: { limit: '1000', authorized: true }, + }); + }); + }); + it('preserves existing balances when merge update adds new chain data', async () => { const polygonNative = 'eip155:137/slip44:966' as Caip19AssetId; const initialState: Partial = { diff --git a/packages/assets-controller/src/AssetsController.ts b/packages/assets-controller/src/AssetsController.ts index 3a6612af09..9ef1fc3681 100644 --- a/packages/assets-controller/src/AssetsController.ts +++ b/packages/assets-controller/src/AssetsController.ts @@ -582,7 +582,16 @@ function mergeAccountBalances( replaceCoveredChains: boolean, ): Record { if (!replaceCoveredChains) { - return { ...previousBalances, ...accountBalances }; + // Per-asset shallow merge so amount updates do not drop extra fields + // already on the prior balance (e.g. enrichment metadata). + const next: Record = { ...previousBalances }; + for (const [assetId, balance] of Object.entries(accountBalances)) { + next[assetId] = { + ...(previousBalances[assetId] ?? { amount: '0' }), + ...balance, + }; + } + return next; } const coveredChains = new Set( From c14a99b6e0ccf6210e2f50b2dd8e384a4e2d1f1b Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Fri, 10 Jul 2026 00:50:45 +0200 Subject: [PATCH 2/2] chore: update CHANGELOG --- packages/assets-controller/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/assets-controller/CHANGELOG.md b/packages/assets-controller/CHANGELOG.md index 356907132f..a872917daf 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] +### Fixed + +- Preserve existing per-asset balance fields when applying merge-mode balance updates, so amount-only updates do not drop extra fields already present on the prior balance ([#9453](https://github.com/MetaMask/core/pull/9453)) + ## [10.2.0] ### Added