@metamask-previews/assets-controllers 93.1.0-preview-f557eade → 93.1.0-preview-aabe1c65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
17
17
 
18
18
  ### Fixed
19
19
 
20
+ - **BREAKING:** `NftDetectionController` now calls a new function `NftController:addNfts` that reduces API calls to bulk-scan to batch multiple NFTs URLs ([#7411](https://github.com/MetaMask/core/pull/7411))
20
21
  - Added decimal precision (default 9dp) for `CurrencyRateController` `conversionRate` and `conversionRate` properties. ([#7324](https://github.com/MetaMask/core/pull/7324))
21
22
  - This fixes any BigNumber conversion errors due to exceeding the 15 significant digit limit
22
23
 
@@ -253,6 +253,42 @@ class NftController extends base_controller_1.BaseController {
253
253
  await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_addIndividualNft).call(this, checksumHexAddress, tokenId, nftMetadata, nftContract, chainId, addressToSearch, source);
254
254
  }
255
255
  }
256
+ /**
257
+ * Adds multiple NFTs and respective NFT contracts to the stored NFT and NFT contracts lists.
258
+ *
259
+ * @param nfts - An array of NFT objects to add.
260
+ * @param nfts[].tokenAddress - Hex address of the NFT contract.
261
+ * @param nfts[].tokenId - The NFT identifier.
262
+ * @param nfts[].nftMetadata - NFT metadata including chainId.
263
+ * @param userAddress - The address of the current user.
264
+ * @param source - Whether the NFT was detected, added manually or suggested by a dapp. Defaults to Source.Custom.
265
+ * @returns Promise resolving to the current NFT list.
266
+ */
267
+ async addNfts(nfts, userAddress, source = constants_1.Source.Custom) {
268
+ const addressToSearch = __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getAddressOrSelectedAddress).call(this, userAddress);
269
+ if (!addressToSearch) {
270
+ return;
271
+ }
272
+ // Remember max number of urls this allows is 250
273
+ const sanitizedNftMetadata = await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_bulkSanitizeNftMetadata).call(this, nfts.map((nft) => nft.nftMetadata));
274
+ for (const [index, nft] of nfts.entries()) {
275
+ const checksumHexAddress = (0, controller_utils_1.toChecksumHexAddress)(nft.tokenAddress);
276
+ const hexChainId = (0, controller_utils_1.toHex)(nft.nftMetadata.chainId);
277
+ const networkClientId = this.messenger.call('NetworkController:findNetworkClientIdByChainId', hexChainId);
278
+ const newNftContracts = await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_addNftContract).call(this, networkClientId, {
279
+ tokenAddress: checksumHexAddress,
280
+ userAddress: addressToSearch,
281
+ source,
282
+ nftMetadata: sanitizedNftMetadata[index],
283
+ });
284
+ // If NFT contract was not added, do not add individual NFT
285
+ const nftContract = newNftContracts.find((contract) => contract.address.toLowerCase() === checksumHexAddress.toLowerCase());
286
+ // If NFT contract information, add individual NFT
287
+ if (nftContract) {
288
+ await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_addIndividualNft).call(this, checksumHexAddress, nft.tokenId, sanitizedNftMetadata[index], nftContract, hexChainId, addressToSearch, source);
289
+ }
290
+ }
291
+ }
256
292
  /**
257
293
  * Refetches NFT metadata and updates the state
258
294
  *
@@ -1216,27 +1252,40 @@ async function _NftController_bulkSanitizeNftMetadata(metadataList) {
1216
1252
  return sanitizedMetadataList;
1217
1253
  }
1218
1254
  try {
1219
- // Use bulkScanUrls to check all URLs at once
1220
- const bulkScanResponse = await this.messenger.call('PhishingController:bulkScanUrls', urlsToCheck);
1221
- // Apply scan results to all metadata objects
1222
- Object.entries(bulkScanResponse.results).forEach(([url, result]) => {
1223
- if (result.recommendedAction === phishing_controller_1.RecommendedAction.Block) {
1224
- // Remove this URL from all metadata objects where it appears
1225
- urlMap[url].forEach(({ metadataIndex, fieldName }) => {
1226
- if (fieldName === 'collection.externalLink' &&
1227
- sanitizedMetadataList[metadataIndex].collection // Check if collection exists
1228
- ) {
1229
- const { collection } = sanitizedMetadataList[metadataIndex];
1230
- // Ensure collection is not undefined again just to be safe before using 'in'
1231
- if (collection && 'externalLink' in collection) {
1232
- delete collection.externalLink;
1233
- }
1234
- }
1235
- else {
1236
- delete sanitizedMetadataList[metadataIndex][fieldName];
1255
+ // PhishingController has a 250 URL limit, so batch if needed
1256
+ const MAX_URLS_PER_BATCH = 250;
1257
+ // Process URLs in batches serially
1258
+ const blockedUrls = await (0, assetsUtil_1.reduceInBatchesSerially)({
1259
+ values: urlsToCheck,
1260
+ batchSize: MAX_URLS_PER_BATCH,
1261
+ eachBatch: async (workingBlockedUrls, batch) => {
1262
+ // Use bulkScanUrls to check this batch
1263
+ const bulkScanResponse = await this.messenger.call('PhishingController:bulkScanUrls', batch);
1264
+ // Collect blocked URLs from this batch
1265
+ Object.entries(bulkScanResponse.results).forEach(([url, result]) => {
1266
+ if (result.recommendedAction === phishing_controller_1.RecommendedAction.Block) {
1267
+ // Type assertion is safe here as we always initialize with a Set and return a Set
1268
+ workingBlockedUrls.add(url);
1237
1269
  }
1238
1270
  });
1239
- }
1271
+ return workingBlockedUrls;
1272
+ },
1273
+ initialResult: new Set(),
1274
+ });
1275
+ // Apply scan results to all metadata objects
1276
+ blockedUrls.forEach((url) => {
1277
+ urlMap[url].forEach(({ metadataIndex, fieldName }) => {
1278
+ if (fieldName === 'collection.externalLink' &&
1279
+ sanitizedMetadataList[metadataIndex].collection) {
1280
+ const { collection } = sanitizedMetadataList[metadataIndex];
1281
+ if (collection && 'externalLink' in collection) {
1282
+ delete collection.externalLink;
1283
+ }
1284
+ }
1285
+ else {
1286
+ delete sanitizedMetadataList[metadataIndex][fieldName];
1287
+ }
1288
+ });
1240
1289
  });
1241
1290
  }
1242
1291
  catch (error) {