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
5 changes: 5 additions & 0 deletions packages/assets-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Bump `@metamask/transaction-controller` from `^65.3.0` to `^65.4.0` ([#8796](https://github.com/MetaMask/core/pull/8796))

### Fixed

- Non-EVM assets with a `slip44` asset namespace (e.g. Bitcoin, Solana native, TRON) are now correctly typed as `native` instead of `erc20` in `assetsInfo` ([#8811](https://github.com/MetaMask/core/pull/8811))
- Solana SPL tokens (CAIP-19 `solana:.../token:<address>`) are now correctly typed as `spl` instead of `erc20` in `assetsInfo` ([#8811](https://github.com/MetaMask/core/pull/8811))

## [7.1.2]

### Changed
Expand Down
11 changes: 9 additions & 2 deletions packages/assets-controller/src/AssetsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import type { Hex } from '@metamask/utils';
import {
isCaipChainId,
isStrictHexString,
KnownCaipNamespace,
parseCaipAssetType,
parseCaipChainId,
} from '@metamask/utils';
Expand All @@ -82,7 +83,10 @@ import type { AccountsControllerAccountBalancesUpdatedEvent } from './data-sourc
import { SnapDataSource } from './data-sources/SnapDataSource';
import type { StakedBalanceDataSourceConfig } from './data-sources/StakedBalanceDataSource';
import { StakedBalanceDataSource } from './data-sources/StakedBalanceDataSource';
import { TokenDataSource } from './data-sources/TokenDataSource';
import {
CaipAssetNamespace,
TokenDataSource,
} from './data-sources/TokenDataSource';
import {
CHAINS_WITH_DEFAULT_TRACKED_ASSETS,
DEFAULT_TRACKED_ASSETS_BY_CHAIN,
Expand Down Expand Up @@ -1663,7 +1667,10 @@ export class AssetsController extends BaseController<
let tokenType: FungibleAssetMetadata['type'] = 'erc20';
if (this.#isNativeAsset(normalizedAssetId)) {
tokenType = 'native';
} else if (parsed.assetNamespace === 'spl') {
} else if (
parsed.chain.namespace === KnownCaipNamespace.Solana &&
parsed.assetNamespace === CaipAssetNamespace.Token
) {
tokenType = 'spl';
}

Expand Down
2 changes: 1 addition & 1 deletion packages/assets-controller/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ type Caip19AssetId = string;
// - Native ETH: "eip155:1/slip44:60"
// - USDC on Ethereum: "eip155:1/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// - SOL: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501"
// - SPL Token: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/spl:EPjFWdd5..."
// - SPL Token: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5..."

// CAIP-2 chain identifier
type ChainId = string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ const MOCK_ADDRESS = '0x1234567890123456789012345678901234567890';
const MOCK_TOKEN_ASSET =
'eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' as Caip19AssetId;
const MOCK_NATIVE_ASSET = 'eip155:1/slip44:60' as Caip19AssetId;
const MOCK_BTC_ASSET =
'bip122:000000000019d6689c085ae165831e93/slip44:0' as Caip19AssetId;
const MOCK_SOL_NATIVE_ASSET =
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501' as Caip19AssetId;
const MOCK_TRX_ASSET = 'tron:728126428/slip44:195' as Caip19AssetId;
const MOCK_SPL_ASSET =
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/spl:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' as Caip19AssetId;
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' as Caip19AssetId;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intentional?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just making sure. There was an ADR around the naming standardisation for solana
https://github.com/MetaMask/decisions/pull/170/changes

Copy link
Copy Markdown
Contributor Author

@bergarces bergarces May 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


type MockApiClient = {
tokens: {
Expand Down Expand Up @@ -516,6 +521,57 @@ describe('TokenDataSource', () => {
expect(context.response.assetsInfo?.[MOCK_SPL_ASSET]?.type).toBe('spl');
});

it.each([
{
label: 'Bitcoin (bip122/slip44)',
assetId: MOCK_BTC_ASSET,
chainId: 'bip122:000000000019d6689c085ae165831e93',
name: 'Bitcoin',
symbol: 'BTC',
decimals: 8,
},
{
label: 'SOL native (solana/slip44)',
assetId: MOCK_SOL_NATIVE_ASSET,
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
name: 'Solana',
symbol: 'SOL',
decimals: 9,
},
{
label: 'TRX native (tron/slip44)',
assetId: MOCK_TRX_ASSET,
chainId: 'tron:728126428',
name: 'TRON',
symbol: 'TRX',
decimals: 6,
},
])(
'middleware types non-EVM slip44 asset as native: $label',
async ({ assetId, chainId, name, symbol, decimals }) => {
const { controller } = setupController({
messenger: createTestMessenger(),
supportedNetworks: [chainId],
assetsResponse: [
createMockAssetResponse(assetId, { name, symbol, decimals }),
],
});

const next = jest.fn().mockResolvedValue(undefined);
const context = createMiddlewareContext({
response: {
detectedAssets: {
'mock-account-id': [assetId],
},
},
});

await controller.assetsMiddleware(context, next);

expect(context.response.assetsInfo?.[assetId]?.type).toBe('native');
},
);

it('middleware merges metadata into existing response', async () => {
const anotherAsset =
'eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f' as Caip19AssetId;
Expand Down
13 changes: 10 additions & 3 deletions packages/assets-controller/src/data-sources/TokenDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const BULK_SCAN_BATCH_SIZE = 100;
const MIN_TOKEN_OCCURRENCES = 3;

/** CAIP-19 `assetNamespace` segments used across filtering logic. */
enum CaipAssetNamespace {
export enum CaipAssetNamespace {
Slip44 = 'slip44',
Erc20 = 'erc20',
Token = 'token',
Expand Down Expand Up @@ -102,11 +102,18 @@ function transformV3AssetResponseToMetadata(
const parsed = parseCaipAssetType(assetId);
let tokenType: 'native' | 'erc20' | 'spl' = 'erc20';

if (nativeAssetIds.has(assetId.toLowerCase())) {
if (
nativeAssetIds.has(assetId.toLowerCase()) ||
parsed.assetNamespace === CaipAssetNamespace.Slip44
) {
tokenType = 'native';
} else if (parsed.assetNamespace === 'spl') {
} else if (
parsed.chain.namespace === KnownCaipNamespace.Solana &&
parsed.assetNamespace === CaipAssetNamespace.Token
) {
tokenType = 'spl';
}
// TODO: Add support for Tron trc20 standard

const metadata: FungibleAssetMetadata = {
// Type derived from assetId
Expand Down
2 changes: 1 addition & 1 deletion packages/assets-controller/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { CaipAssetType, CaipChainId, Json } from '@metamask/utils';
* - Native: "eip155:1/slip44:60" (ETH)
* - ERC20: "eip155:1/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" (USDC)
* - ERC721: "eip155:1/erc721:0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D/1234" (BAYC #1234)
* - SPL: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/spl:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
* - SPL: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
*/
export type Caip19AssetId = CaipAssetType;

Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-controller/src/selectors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('Bridge Selectors', () => {
exchangeRate: '2.5',
usdExchangeRate: '1.5',
},
'solana:101/spl:456': {
'solana:101/token:456': {
exchangeRate: '3.0',
},
},
Expand Down
Loading