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 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(