Skip to content

Commit 8e28c9f

Browse files
authored
Merge branch 'main' into update-network-nicknames
2 parents 1ec5477 + f070663 commit 8e28c9f

File tree

11 files changed

+677
-32
lines changed

11 files changed

+677
-32
lines changed

eslint-warning-thresholds.json

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -321,22 +321,6 @@
321321
"packages/name-controller/src/util.ts": {
322322
"jsdoc/require-returns": 1
323323
},
324-
"packages/network-controller/src/NetworkController.ts": {
325-
"@typescript-eslint/prefer-promise-reject-errors": 1
326-
},
327-
"packages/network-controller/tests/NetworkController.test.ts": {
328-
"@typescript-eslint/no-unused-vars": 1,
329-
"@typescript-eslint/prefer-promise-reject-errors": 1
330-
},
331-
"packages/network-controller/tests/create-network-client.test.ts": {
332-
"import-x/order": 1
333-
},
334-
"packages/network-controller/tests/provider-api-tests/helpers.ts": {
335-
"@typescript-eslint/prefer-promise-reject-errors": 1,
336-
"import-x/namespace": 1,
337-
"import-x/no-named-as-default-member": 1,
338-
"promise/catch-or-return": 1
339-
},
340324
"packages/permission-controller/src/PermissionController.test.ts": {
341325
"jest/no-conditional-in-test": 4
342326
},

packages/assets-controllers/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Add `AssetsByAccountGroup` to list of exported types ([#6983](https://github.com/MetaMask/core/pull/6983))
13+
- Added `addAssets` to allow adding multiple assets for non-EVM chains ([#7016](https://github.com/MetaMask/core/pull/7016))
1314

1415
### Changed
1516

@@ -22,6 +23,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2223
- `AccountTreeController:selectedAccountGroupChange` updates DeFi positions for the selected address
2324
- `TransactionController:transactionConfirmed` only updates DeFi positions if the transaction is for the selected address
2425

26+
### Fixed
27+
28+
- Fixed token is not removed from ignored tokens list when added back due to case insensiteivity ([#7016](https://github.com/MetaMask/core/pull/7016))
29+
2530
## [86.0.0]
2631

2732
### Changed

packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.test.ts

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { deriveStateFromMetadata } from '@metamask/base-controller';
22
import type {
33
AccountAssetListUpdatedEventPayload,
4+
CaipAssetType,
45
CaipAssetTypeOrId,
56
} from '@metamask/keyring-api';
67
import {
@@ -921,6 +922,317 @@ describe('MultichainAssetsController', () => {
921922
});
922923
});
923924

925+
describe('addAssets', () => {
926+
it('should add a single asset to account assets list', async () => {
927+
const { controller } = setupController({
928+
state: {
929+
accountsAssets: {
930+
[mockSolanaAccount.id]: [
931+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501',
932+
],
933+
},
934+
assetsMetadata: mockGetMetadataReturnValue.assets,
935+
allIgnoredAssets: {},
936+
} as MultichainAssetsControllerState,
937+
});
938+
939+
const assetToAdd =
940+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/token:Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr';
941+
942+
const result = await controller.addAssets(
943+
[assetToAdd],
944+
mockSolanaAccount.id,
945+
);
946+
947+
expect(result).toStrictEqual([
948+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501',
949+
assetToAdd,
950+
]);
951+
expect(
952+
controller.state.accountsAssets[mockSolanaAccount.id],
953+
).toStrictEqual([
954+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501',
955+
assetToAdd,
956+
]);
957+
});
958+
959+
it('should not add duplicate assets', async () => {
960+
const existingAsset =
961+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501';
962+
const { controller } = setupController({
963+
state: {
964+
accountsAssets: {
965+
[mockSolanaAccount.id]: [existingAsset],
966+
},
967+
assetsMetadata: mockGetMetadataReturnValue.assets,
968+
allIgnoredAssets: {},
969+
} as MultichainAssetsControllerState,
970+
});
971+
972+
const result = await controller.addAssets(
973+
[existingAsset],
974+
mockSolanaAccount.id,
975+
);
976+
977+
expect(result).toStrictEqual([existingAsset]);
978+
expect(
979+
controller.state.accountsAssets[mockSolanaAccount.id],
980+
).toStrictEqual([existingAsset]);
981+
});
982+
983+
it('should remove asset from ignored list when added', async () => {
984+
const assetToAdd = 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501';
985+
const { controller } = setupController({
986+
state: {
987+
accountsAssets: {},
988+
assetsMetadata: mockGetMetadataReturnValue.assets,
989+
allIgnoredAssets: {
990+
[mockSolanaAccount.id]: [assetToAdd],
991+
},
992+
} as MultichainAssetsControllerState,
993+
});
994+
995+
const result = await controller.addAssets(
996+
[assetToAdd],
997+
mockSolanaAccount.id,
998+
);
999+
1000+
expect(result).toStrictEqual([assetToAdd]);
1001+
expect(
1002+
controller.state.accountsAssets[mockSolanaAccount.id],
1003+
).toStrictEqual([assetToAdd]);
1004+
expect(
1005+
controller.state.allIgnoredAssets[mockSolanaAccount.id],
1006+
).toBeUndefined();
1007+
});
1008+
1009+
it('should handle adding asset to account with no existing assets', async () => {
1010+
const assetToAdd = 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501';
1011+
const { controller } = setupController({
1012+
state: {
1013+
accountsAssets: {},
1014+
assetsMetadata: mockGetMetadataReturnValue.assets,
1015+
allIgnoredAssets: {},
1016+
} as MultichainAssetsControllerState,
1017+
});
1018+
1019+
const result = await controller.addAssets(
1020+
[assetToAdd],
1021+
mockSolanaAccount.id,
1022+
);
1023+
1024+
expect(result).toStrictEqual([assetToAdd]);
1025+
expect(
1026+
controller.state.accountsAssets[mockSolanaAccount.id],
1027+
).toStrictEqual([assetToAdd]);
1028+
});
1029+
1030+
it('should publish accountAssetListUpdated event when asset is added', async () => {
1031+
const { controller, messenger } = setupController({
1032+
state: {
1033+
accountsAssets: {},
1034+
assetsMetadata: mockGetMetadataReturnValue.assets,
1035+
allIgnoredAssets: {},
1036+
} as MultichainAssetsControllerState,
1037+
});
1038+
1039+
const assetToAdd = 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501';
1040+
1041+
// Set up event listener to capture the published event
1042+
const eventListener = jest.fn();
1043+
messenger.subscribe(
1044+
'MultichainAssetsController:accountAssetListUpdated',
1045+
eventListener,
1046+
);
1047+
1048+
await controller.addAssets([assetToAdd], mockSolanaAccount.id);
1049+
1050+
expect(eventListener).toHaveBeenCalledWith({
1051+
assets: {
1052+
[mockSolanaAccount.id]: {
1053+
added: [assetToAdd],
1054+
removed: [],
1055+
},
1056+
},
1057+
});
1058+
});
1059+
1060+
it('should add multiple assets from the same chain', async () => {
1061+
const { controller } = setupController({
1062+
state: {
1063+
accountsAssets: {
1064+
[mockSolanaAccount.id]: [
1065+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501',
1066+
],
1067+
},
1068+
assetsMetadata: mockGetMetadataReturnValue.assets,
1069+
allIgnoredAssets: {},
1070+
} as MultichainAssetsControllerState,
1071+
});
1072+
1073+
const assetsToAdd: CaipAssetType[] = [
1074+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/token:Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr',
1075+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/token:AnotherTokenAddress',
1076+
];
1077+
1078+
const result = await controller.addAssets(
1079+
assetsToAdd,
1080+
mockSolanaAccount.id,
1081+
);
1082+
1083+
expect(result).toStrictEqual([
1084+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501',
1085+
...assetsToAdd,
1086+
]);
1087+
expect(
1088+
controller.state.accountsAssets[mockSolanaAccount.id],
1089+
).toStrictEqual([
1090+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501',
1091+
...assetsToAdd,
1092+
]);
1093+
});
1094+
1095+
it('should throw error when assets are from different chains', async () => {
1096+
const { controller } = setupController({
1097+
state: {
1098+
accountsAssets: {},
1099+
assetsMetadata: mockGetMetadataReturnValue.assets,
1100+
allIgnoredAssets: {},
1101+
} as MultichainAssetsControllerState,
1102+
});
1103+
1104+
const assetsFromDifferentChains: CaipAssetType[] = [
1105+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501',
1106+
'eip155:1/slip44:60', // Ethereum asset
1107+
];
1108+
1109+
await expect(
1110+
controller.addAssets(assetsFromDifferentChains, mockSolanaAccount.id),
1111+
).rejects.toThrow(
1112+
'All assets must belong to the same chain. Found assets from chains: solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1, eip155:1',
1113+
);
1114+
});
1115+
1116+
it('should return existing assets when empty array is provided', async () => {
1117+
const existingAsset =
1118+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501';
1119+
const { controller } = setupController({
1120+
state: {
1121+
accountsAssets: {
1122+
[mockSolanaAccount.id]: [existingAsset],
1123+
},
1124+
assetsMetadata: mockGetMetadataReturnValue.assets,
1125+
allIgnoredAssets: {},
1126+
} as MultichainAssetsControllerState,
1127+
});
1128+
1129+
const result = await controller.addAssets([], mockSolanaAccount.id);
1130+
1131+
expect(result).toStrictEqual([existingAsset]);
1132+
});
1133+
1134+
it('should only publish event for newly added assets', async () => {
1135+
const existingAsset =
1136+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501';
1137+
const newAsset = 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/token:NewToken';
1138+
1139+
const { controller, messenger } = setupController({
1140+
state: {
1141+
accountsAssets: {
1142+
[mockSolanaAccount.id]: [existingAsset],
1143+
},
1144+
assetsMetadata: mockGetMetadataReturnValue.assets,
1145+
allIgnoredAssets: {},
1146+
} as MultichainAssetsControllerState,
1147+
});
1148+
1149+
const eventListener = jest.fn();
1150+
messenger.subscribe(
1151+
'MultichainAssetsController:accountAssetListUpdated',
1152+
eventListener,
1153+
);
1154+
1155+
await controller.addAssets(
1156+
[existingAsset, newAsset],
1157+
mockSolanaAccount.id,
1158+
);
1159+
1160+
expect(eventListener).toHaveBeenCalledWith({
1161+
assets: {
1162+
[mockSolanaAccount.id]: {
1163+
added: [newAsset], // Only the new asset should be in the event
1164+
removed: [],
1165+
},
1166+
},
1167+
});
1168+
});
1169+
1170+
it('should not publish event when no new assets are added', async () => {
1171+
const existingAsset =
1172+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501';
1173+
1174+
const { controller, messenger } = setupController({
1175+
state: {
1176+
accountsAssets: {
1177+
[mockSolanaAccount.id]: [existingAsset],
1178+
},
1179+
assetsMetadata: mockGetMetadataReturnValue.assets,
1180+
allIgnoredAssets: {},
1181+
} as MultichainAssetsControllerState,
1182+
});
1183+
1184+
const eventListener = jest.fn();
1185+
messenger.subscribe(
1186+
'MultichainAssetsController:accountAssetListUpdated',
1187+
eventListener,
1188+
);
1189+
1190+
await controller.addAssets([existingAsset], mockSolanaAccount.id);
1191+
1192+
// Event should not be published since no new assets were added
1193+
expect(eventListener).not.toHaveBeenCalled();
1194+
});
1195+
1196+
it('should partially remove assets from ignored list when only some are added', async () => {
1197+
const ignoredAsset1 =
1198+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501';
1199+
const ignoredAsset2 =
1200+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/token:Token1';
1201+
const ignoredAsset3 =
1202+
'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/token:Token2';
1203+
1204+
const { controller } = setupController({
1205+
state: {
1206+
accountsAssets: {},
1207+
assetsMetadata: mockGetMetadataReturnValue.assets,
1208+
allIgnoredAssets: {
1209+
[mockSolanaAccount.id]: [
1210+
ignoredAsset1,
1211+
ignoredAsset2,
1212+
ignoredAsset3,
1213+
],
1214+
},
1215+
} as MultichainAssetsControllerState,
1216+
});
1217+
1218+
// Only add two of the three ignored assets
1219+
await controller.addAssets(
1220+
[ignoredAsset1, ignoredAsset2],
1221+
mockSolanaAccount.id,
1222+
);
1223+
1224+
// Should have added the two assets
1225+
expect(
1226+
controller.state.accountsAssets[mockSolanaAccount.id],
1227+
).toStrictEqual([ignoredAsset1, ignoredAsset2]);
1228+
1229+
// Should have only the third asset remaining in ignored list
1230+
expect(
1231+
controller.state.allIgnoredAssets[mockSolanaAccount.id],
1232+
).toStrictEqual([ignoredAsset3]);
1233+
});
1234+
});
1235+
9241236
describe('asset detection with ignored assets', () => {
9251237
it('should filter out ignored assets when account assets are updated', async () => {
9261238
const ignoredAsset = 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1/slip44:501';

0 commit comments

Comments
 (0)