Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/assets-controllers/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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 () => {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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'));
});

Expand All @@ -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 () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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) {
Expand Down
Loading