Skip to content
Merged
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
1 change: 1 addition & 0 deletions packages/assets-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `decimals: 0` is treated as valid; `name` and `symbol` are not required.
- Decimals resolution in `#handleBalanceUpdate` and the manual-fetch path no longer relies on `??` to fall through from state to pipeline metadata. A new `#pickValidDecimals` helper picks the first source whose `decimals` is finite and non-negative, so a stale `decimals: NaN` (or `decimals: -1`) in state can no longer shadow the chain-status stub's `decimals: 18` and silently produce `amount: '0'` while `assetsInfo` reports `decimals: 18`.
- `#convertToHumanReadable` now defensively returns `'0'` when `decimals` isn't a finite non-negative number or when the raw balance can't be parsed, matching the existing safe fallback used in the error path.
- The mUSD (`MetaMask USD`) contract address stored in `defaults.ts` is now EIP-55 checksummed (`0xacA92E438df0B2401fF60dA7E4337B687a2435DA`) ([#8786](https://github.com/MetaMask/core/pull/8786)). Previously the address was all-lowercase, causing the CAIP-19 keys pre-seeded into `assetsInfo` by `buildDefaultAssetsInfo()` to differ from the checksummed keys written by data sources (which always normalise asset IDs via `normalizeAssetId`). The mismatch resulted in two separate `assetsInfo` entries for the same token after the first balance or token-data poll.

## [7.1.1]

Expand Down
17 changes: 17 additions & 0 deletions packages/assets-controller/src/AssetsController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,23 @@ describe('AssetsController', () => {
selectedCurrency: 'usd',
});
});

it('pre-seeds assetsInfo with EIP-55 checksummed CAIP-19 keys', () => {
// Regression: MUSD_ADDRESS was previously all-lowercase, so
// buildDefaultAssetsInfo() produced lowercase CAIP-19 keys while data
// sources (which call normalizeAssetId) wrote checksummed keys.
// After the first balance poll both keys existed in assetsInfo.
const defaultState = getDefaultAssetsControllerState();
const assetIds = Object.keys(defaultState.assetsInfo);
expect(assetIds.length).toBeGreaterThan(0);
// Every erc20 asset ID must contain at least one uppercase hex letter
// (EIP-55 checksum property) so that keys match normalizeAssetId output.
const erc20Ids = assetIds.filter((id) => id.includes('/erc20:'));
expect(erc20Ids.length).toBeGreaterThan(0);
for (const id of erc20Ids) {
expect(id).toMatch(/\/erc20:0x[0-9a-fA-F]*[A-F][0-9a-fA-F]*/u);
}
});
});

describe('constructor', () => {
Expand Down
28 changes: 18 additions & 10 deletions packages/assets-controller/src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ import type {
} from './types';

/**
* Address of MetaMask USD (mUSD) — same canonical contract address
* across every chain we deploy it to.
* EIP-55 checksummed address of MetaMask USD (mUSD) — same canonical contract
* address across every chain we deploy it to.
*
* Must be checksummed so that the CAIP-19 asset IDs produced by
* `musdAssetId()` (and therefore the keys seeded into `assetsInfo` by
* `buildDefaultAssetsInfo()`) match the keys written by data sources, which
* always pass IDs through `normalizeAssetId` → `toChecksumAddress` before
* emitting a `DataResponse`. Using a lowercase address would cause the
* pre-seeded keys to diverge from the data-source keys, leaving a duplicate
* entry in `assetsInfo` after the first balance or token-data poll.
*/
const MUSD_ADDRESS = '0xaca92e438df0b2401ff60da7e4337b687a2435da';
const MUSD_ADDRESS = '0xacA92E438df0B2401fF60dA7E4337B687a2435DA';

/**
* Hardcoded metadata for MetaMask USD. Pre-seeding this in default
Expand Down Expand Up @@ -63,14 +71,14 @@ export const CHAINS_WITH_DEFAULT_TRACKED_ASSETS: ReadonlySet<ChainId> = new Set(

/**
* Pre-seeded metadata for every default tracked asset, keyed by the
* lowercase CAIP-19 id so callers can look up without worrying about
* checksum case.
* checksummed CAIP-19 id. All callers must pass a checksummed asset ID;
* use `normalizeAssetId` to ensure the correct format before looking up.
*/
export const DEFAULT_ASSET_METADATA: ReadonlyMap<string, AssetMetadata> =
new Map<string, AssetMetadata>([
[musdAssetId('eip155:1' as ChainId).toLowerCase(), MUSD_METADATA],
[musdAssetId('eip155:59144' as ChainId).toLowerCase(), MUSD_METADATA],
[musdAssetId('eip155:143' as ChainId).toLowerCase(), MUSD_METADATA],
[musdAssetId('eip155:1' as ChainId), MUSD_METADATA],
[musdAssetId('eip155:59144' as ChainId), MUSD_METADATA],
[musdAssetId('eip155:143' as ChainId), MUSD_METADATA],
]);

/**
Expand All @@ -89,14 +97,14 @@ export function getDefaultTrackedAssetsForChain(
/**
* Look up pre-seeded metadata for a default tracked asset.
*
* @param assetId - CAIP-19 asset id (any case).
* @param assetId - CAIP-19 asset id (must be EIP-55 checksummed for EVM tokens).
* @returns The metadata if the asset is a default tracked asset,
* otherwise `undefined`.
*/
export function getDefaultAssetMetadata(
assetId: Caip19AssetId,
): AssetMetadata | undefined {
return DEFAULT_ASSET_METADATA.get(assetId.toLowerCase());
return DEFAULT_ASSET_METADATA.get(assetId);
}

/**
Expand Down
Loading