diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index b2211ffd559..f7c8d09dd0f 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -20,6 +20,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING:** Standardize names of `CurrencyRateController` messenger action types ([#8561](https://github.com/MetaMask/core/pull/8561)) - The `GetCurrencyRateState` messenger action has been renamed to `CurrencyRateControllerGetStateAction` to follow the convention. You will need to update imports appropriately. - These changes only affect the types. The action type strings themselves have not changed, so you do not need to update the list of actions you pass when initializing `CurrencyRateController` messenger. +- `MultichainAssetsController`: Change Blockaid spam token filter to fail-open behavior ([#8580](https://github.com/MetaMask/core/pull/8580)) + - When Blockaid bulk token scan API calls fail or are rejected, tokens are now allowed through rather than being blocked + - This prevents legitimate tokens from being blocked due to API outages or network issues + - Tokens are still filtered when the API succeeds and marks them as malicious ## [104.3.0] diff --git a/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.test.ts b/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.test.ts index b1ed3f34485..af0094b5040 100644 --- a/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.test.ts +++ b/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.test.ts @@ -1514,7 +1514,7 @@ describe('MultichainAssetsController', () => { ]); }); - it('does not add tokens when bulkScanTokens throws (fail closed)', async () => { + it('adds tokens when bulkScanTokens throws (fail open)', async () => { const mockAccountId = 'account1'; const token = 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/token:SomeAddr'; @@ -1536,7 +1536,9 @@ describe('MultichainAssetsController', () => { await jestAdvanceTime({ duration: 1 }); - expect(controller.state.accountsAssets[mockAccountId]).toStrictEqual([]); + expect(controller.state.accountsAssets[mockAccountId]).toStrictEqual([ + token, + ]); }); it('does not add tokens when bulkScanTokens returns empty (API error handled internally)', async () => { @@ -1765,7 +1767,7 @@ describe('MultichainAssetsController', () => { ).toBeUndefined(); }); - it('drops tokens from batches that fail (partial fail closed)', async () => { + it('keeps tokens from batches that fail (partial fail open)', async () => { const mockAccountId = 'account1'; // 120 tokens = batch 1 (100) + batch 2 (20) const tokens = Array.from( @@ -1800,7 +1802,7 @@ describe('MultichainAssetsController', () => { } return Promise.resolve(results); } - // Second batch fails — its tokens must not be added + // Second batch fails — its tokens are allowed through (fail open) return Promise.reject(new Error('API timeout')); }); @@ -1822,12 +1824,14 @@ describe('MultichainAssetsController', () => { ), ).toBeUndefined(); + // Tokens from the failed second batch (100-119) should be added (fail open) for (let i = 100; i < 120; i++) { const tokenCaip = `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/token:Token${String(i).padStart(3, '0')}`; - expect(storedAssets).not.toContain(tokenCaip); + expect(storedAssets).toContain(tokenCaip); } - expect(storedAssets).toHaveLength(99); + // 99 from batch 1 (excluding Token099) + 20 from batch 2 = 119 total + expect(storedAssets).toHaveLength(119); }); it('periodic rescan ignores SPL tokens that Blockaid later marks malicious', async () => { diff --git a/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.ts b/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.ts index 4030d6b3c57..ee802dd0d68 100644 --- a/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.ts +++ b/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.ts @@ -826,7 +826,7 @@ export class MultichainAssetsController extends StaticIntervalPollingController< } /** - * Fail-closed Blockaid filter for newly detected `token:` assets (native/other namespaces unchanged). + * Fail-open Blockaid filter for newly detected `token:` assets (native/other namespaces unchanged). * * @param assets - CAIP assets to filter. * @returns Filtered list, original order preserved. @@ -850,6 +850,10 @@ export class MultichainAssetsController extends StaticIntervalPollingController< for (const outcome of batchOutcomes) { if (outcome.status === 'rejected') { + // Fail-open: if API fails, allow all tokens in this batch through + for (const entry of outcome.entries) { + keptTokenAssets.add(entry.asset); + } continue; } for (const entry of outcome.entries) {