@metamask/assets-controllers 16.0.0 → 17.0.0
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 +7 -1
- package/dist/NftController.d.ts +1 -1
- package/dist/NftController.js +1 -1
- package/dist/NftController.js.map +1 -1
- package/dist/NftDetectionController.d.ts +16 -5
- package/dist/NftDetectionController.d.ts.map +1 -1
- package/dist/NftDetectionController.js +26 -5
- package/dist/NftDetectionController.js.map +1 -1
- package/dist/TokenBalancesController.js +4 -4
- package/dist/TokenBalancesController.js.map +1 -1
- package/dist/TokenDetectionController.d.ts +19 -5
- package/dist/TokenDetectionController.d.ts.map +1 -1
- package/dist/TokenDetectionController.js +27 -7
- package/dist/TokenDetectionController.js.map +1 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [17.0.0]
|
|
10
|
+
### Changed
|
|
11
|
+
- **BREAKING:** Bump dependency on `@metamask/polling-controller` to ^1.0.0
|
|
12
|
+
- Bump dependency and peer dependency on `@metamask/network-controller` to ^15.1.0
|
|
13
|
+
|
|
9
14
|
## [16.0.0]
|
|
10
15
|
### Added
|
|
11
16
|
- Add way to start and stop different polling sessions for the same network client ID by providing extra scoping data ([#1776](https://github.com/MetaMask/core/pull/1776))
|
|
@@ -334,7 +339,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
334
339
|
### Changed
|
|
335
340
|
- Use Ethers for AssetsContractController ([#845](https://github.com/MetaMask/core/pull/845))
|
|
336
341
|
|
|
337
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@
|
|
342
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@17.0.0...HEAD
|
|
343
|
+
[17.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@16.0.0...@metamask/assets-controllers@17.0.0
|
|
338
344
|
[16.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@15.0.0...@metamask/assets-controllers@16.0.0
|
|
339
345
|
[15.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@14.0.0...@metamask/assets-controllers@15.0.0
|
|
340
346
|
[14.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@13.0.0...@metamask/assets-controllers@14.0.0
|
package/dist/NftController.d.ts
CHANGED
|
@@ -480,7 +480,7 @@ export declare class NftController extends BaseController<NftConfig, NftState> {
|
|
|
480
480
|
* @param transactionId - NFT transaction id.
|
|
481
481
|
* @param selectedAddress - Hex address of the user account.
|
|
482
482
|
* @param chainId - Id of the current network.
|
|
483
|
-
* @returns a boolean indicating if the reset was well
|
|
483
|
+
* @returns a boolean indicating if the reset was well succeeded or not
|
|
484
484
|
*/
|
|
485
485
|
resetNftTransactionStatusByTransactionId(transactionId: string, selectedAddress: string, chainId: Hex): boolean;
|
|
486
486
|
_requestApproval(suggestedNftMeta: SuggestedNftMeta): Promise<unknown>;
|
package/dist/NftController.js
CHANGED
|
@@ -929,7 +929,7 @@ class NftController extends base_controller_1.BaseController {
|
|
|
929
929
|
* @param transactionId - NFT transaction id.
|
|
930
930
|
* @param selectedAddress - Hex address of the user account.
|
|
931
931
|
* @param chainId - Id of the current network.
|
|
932
|
-
* @returns a boolean indicating if the reset was well
|
|
932
|
+
* @returns a boolean indicating if the reset was well succeeded or not
|
|
933
933
|
*/
|
|
934
934
|
resetNftTransactionStatusByTransactionId(transactionId, selectedAddress, chainId) {
|
|
935
935
|
var _a;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NftController.js","sourceRoot":"","sources":["../src/NftController.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAddress } from '@ethersproject/address';\nimport type { AddApprovalRequest } from '@metamask/approval-controller';\nimport type {\n BaseConfig,\n BaseState,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n safelyExecute,\n handleFetch,\n toChecksumHexAddress,\n BNToHex,\n fetchWithErrorHandling,\n IPFS_DEFAULT_GATEWAY_URL,\n ERC721,\n ERC1155,\n OPENSEA_PROXY_URL,\n ApprovalType,\n} from '@metamask/controller-utils';\nimport type {\n NetworkClientId,\n NetworkController,\n NetworkState,\n} from '@metamask/network-controller';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type { Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { BN, stripHexPrefix } from 'ethereumjs-util';\nimport { EventEmitter } from 'events';\nimport { v4 as random } from 'uuid';\n\nimport type { AssetsContractController } from './AssetsContractController';\nimport { compareNftMetadata, getFormattedIpfsUrl } from './assetsUtil';\nimport { Source } from './constants';\nimport type {\n ApiNft,\n ApiNftCreator,\n ApiNftContract,\n ApiNftLastSale,\n} from './NftDetectionController';\n\ntype NFTStandardType = 'ERC721' | 'ERC1155';\n\ntype SuggestedNftMeta = {\n asset: { address: string; tokenId: string } & NftMetadata;\n id: string;\n time: number;\n type: NFTStandardType;\n interactingAddress: string;\n origin: string;\n};\n\n/**\n * @type Nft\n *\n * NFT representation\n * @property address - Hex address of a ERC721 contract\n * @property description - The NFT description\n * @property image - URI of custom NFT image associated with this tokenId\n * @property name - Name associated with this tokenId and contract address\n * @property tokenId - The NFT identifier\n * @property numberOfSales - Number of sales\n * @property backgroundColor - The background color to be displayed with the item\n * @property imagePreview - URI of a smaller image associated with this NFT\n * @property imageThumbnail - URI of a thumbnail image associated with this NFT\n * @property imageOriginal - URI of the original image associated with this NFT\n * @property animation - URI of a animation associated with this NFT\n * @property animationOriginal - URI of the original animation associated with this NFT\n * @property externalLink - External link containing additional information\n * @property creator - The NFT owner information object\n * @property isCurrentlyOwned - Boolean indicating whether the address/chainId combination where it's currently stored currently owns this NFT\n * @property transactionId - Transaction Id associated with the NFT\n */\nexport interface Nft extends NftMetadata {\n tokenId: string;\n address: string;\n isCurrentlyOwned?: boolean;\n}\n\n/**\n * @type NftContract\n *\n * NFT contract information representation\n * @property name - Contract name\n * @property logo - Contract logo\n * @property address - Contract address\n * @property symbol - Contract symbol\n * @property description - Contract description\n * @property totalSupply - Total supply of NFTs\n * @property assetContractType - The NFT type, it could be `semi-fungible` or `non-fungible`\n * @property createdDate - Creation date\n * @property schemaName - The schema followed by the contract, it could be `ERC721` or `ERC1155`\n * @property externalLink - External link containing additional information\n */\nexport interface NftContract {\n name?: string;\n logo?: string;\n address: string;\n symbol?: string;\n description?: string;\n totalSupply?: string;\n assetContractType?: string;\n createdDate?: string;\n schemaName?: string;\n externalLink?: string;\n}\n\n/**\n * @type NftMetadata\n *\n * NFT custom information\n * @property name - NFT custom name\n * @property description - The NFT description\n * @property numberOfSales - Number of sales\n * @property backgroundColor - The background color to be displayed with the item\n * @property image - Image custom image URI\n * @property imagePreview - URI of a smaller image associated with this NFT\n * @property imageThumbnail - URI of a thumbnail image associated with this NFT\n * @property imageOriginal - URI of the original image associated with this NFT\n * @property animation - URI of a animation associated with this NFT\n * @property animationOriginal - URI of the original animation associated with this NFT\n * @property externalLink - External link containing additional information\n * @property creator - The NFT owner information object\n * @property standard - NFT standard name for the NFT, e.g., ERC-721 or ERC-1155\n */\nexport interface NftMetadata {\n name: string | null;\n description: string | null;\n image: string | null;\n standard: string | null;\n favorite?: boolean;\n numberOfSales?: number;\n backgroundColor?: string;\n imagePreview?: string;\n imageThumbnail?: string;\n imageOriginal?: string;\n animation?: string;\n animationOriginal?: string;\n externalLink?: string;\n creator?: ApiNftCreator;\n lastSale?: ApiNftLastSale;\n transactionId?: string;\n tokenURI?: string | null;\n}\n\n/**\n * @type NftConfig\n *\n * NFT controller configuration\n * @property selectedAddress - Vault selected address\n */\nexport interface NftConfig extends BaseConfig {\n selectedAddress: string;\n chainId: Hex;\n ipfsGateway: string;\n openSeaEnabled: boolean;\n useIPFSSubdomains: boolean;\n isIpfsGatewayEnabled: boolean;\n}\n\n/**\n * @type NftState\n *\n * NFT controller state\n * @property allNftContracts - Object containing NFT contract information\n * @property allNfts - Object containing NFTs per account and network\n * @property ignoredNfts - List of NFTs that should be ignored\n */\nexport interface NftState extends BaseState {\n allNftContracts: {\n [key: string]: { [chainId: Hex]: NftContract[] };\n };\n allNfts: { [key: string]: { [chainId: Hex]: Nft[] } };\n ignoredNfts: Nft[];\n}\n\nconst ALL_NFTS_STATE_KEY = 'allNfts';\nconst ALL_NFTS_CONTRACTS_STATE_KEY = 'allNftContracts';\n\ninterface NftAsset {\n address: string;\n tokenId: string;\n}\n\n/**\n * The name of the {@link NftController}.\n */\nconst controllerName = 'NftController';\n\n/**\n * The external actions available to the {@link NftController}.\n */\ntype AllowedActions = AddApprovalRequest;\n\n/**\n * The messenger of the {@link NftController}.\n */\nexport type NftControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n AllowedActions,\n never,\n AllowedActions['type'],\n never\n>;\n\n/**\n * Controller that stores assets and exposes convenience methods\n */\nexport class NftController extends BaseController<NftConfig, NftState> {\n private readonly mutex = new Mutex();\n\n private readonly messagingSystem: NftControllerMessenger;\n\n private getNftApi({\n contractAddress,\n tokenId,\n }: {\n contractAddress: string;\n tokenId: string;\n }) {\n return `${OPENSEA_PROXY_URL}/asset/${contractAddress}/${tokenId}`;\n }\n\n private getNftContractInformationApi({\n contractAddress,\n }: {\n contractAddress: string;\n }) {\n return `${OPENSEA_PROXY_URL}/asset_contract/${contractAddress}`;\n }\n\n /**\n * Helper method to update nested state for allNfts and allNftContracts.\n *\n * @param newCollection - the modified piece of state to update in the controller's store\n * @param baseStateKey - The root key in the store to update.\n * @param passedConfig - An object containing the selectedAddress and chainId that are passed through the auto-detection flow.\n * @param passedConfig.userAddress - the address passed through the NFT detection flow to ensure assets are stored to the correct account\n * @param passedConfig.chainId - the chainId passed through the NFT detection flow to ensure assets are stored to the correct account\n */\n private updateNestedNftState(\n newCollection: Nft[] | NftContract[],\n baseStateKey: 'allNfts' | 'allNftContracts',\n { userAddress, chainId } = {\n userAddress: this.config.selectedAddress,\n chainId: this.config.chainId,\n },\n ) {\n const { [baseStateKey]: oldState } = this.state;\n\n const addressState = oldState[userAddress];\n const newAddressState = {\n ...addressState,\n ...{ [chainId]: newCollection },\n };\n const newState = {\n ...oldState,\n ...{ [userAddress]: newAddressState },\n };\n\n this.update({\n [baseStateKey]: newState,\n });\n }\n\n /**\n * Request individual NFT information from OpenSea API.\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @returns Promise resolving to the current NFT name and image.\n */\n private async getNftInformationFromApi(\n contractAddress: string,\n tokenId: string,\n ): Promise<NftMetadata> {\n // TODO Parameterize this by chainId for non-mainnet token detection\n // Attempt to fetch the data with the proxy\n const nftInformation: ApiNft | undefined = await fetchWithErrorHandling({\n url: this.getNftApi({\n contractAddress,\n tokenId,\n }),\n });\n\n // if we were still unable to fetch the data we return out the default/null of `NftMetadata`\n if (!nftInformation) {\n return {\n name: null,\n description: null,\n image: null,\n standard: null,\n };\n }\n\n // if we've reached this point, we have successfully fetched some data for nftInformation\n // now we reconfigure the data to conform to the `NftMetadata` type for storage.\n const {\n num_sales,\n background_color,\n image_url,\n image_preview_url,\n image_thumbnail_url,\n image_original_url,\n animation_url,\n animation_original_url,\n name,\n description,\n external_link,\n creator,\n last_sale,\n asset_contract: { schema_name },\n } = nftInformation;\n\n /* istanbul ignore next */\n const nftMetadata: NftMetadata = Object.assign(\n {},\n { name: name || null },\n { description: description || null },\n { image: image_url || null },\n creator && { creator },\n num_sales && { numberOfSales: num_sales },\n background_color && { backgroundColor: background_color },\n image_preview_url && { imagePreview: image_preview_url },\n image_thumbnail_url && { imageThumbnail: image_thumbnail_url },\n image_original_url && { imageOriginal: image_original_url },\n animation_url && { animation: animation_url },\n animation_original_url && {\n animationOriginal: animation_original_url,\n },\n external_link && { externalLink: external_link },\n last_sale && { lastSale: last_sale },\n schema_name && { standard: schema_name },\n );\n\n return nftMetadata;\n }\n\n /**\n * Request individual NFT information from contracts that follows Metadata Interface.\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving to the current NFT name and image.\n */\n private async getNftInformationFromTokenURI(\n contractAddress: string,\n tokenId: string,\n networkClientId?: NetworkClientId,\n ): Promise<NftMetadata> {\n const { ipfsGateway, useIPFSSubdomains, isIpfsGatewayEnabled } =\n this.config;\n const result = await this.getNftURIAndStandard(\n contractAddress,\n tokenId,\n networkClientId,\n );\n let tokenURI = result[0];\n const standard = result[1];\n\n const hasIpfsTokenURI = tokenURI.startsWith('ipfs://');\n\n if (hasIpfsTokenURI && !isIpfsGatewayEnabled) {\n return {\n image: null,\n name: null,\n description: null,\n standard: standard || null,\n favorite: false,\n tokenURI: tokenURI ?? null,\n };\n }\n\n const isDisplayNFTMediaToggleEnabled = this.config.openSeaEnabled;\n if (!hasIpfsTokenURI && !isDisplayNFTMediaToggleEnabled) {\n return {\n image: null,\n name: null,\n description: null,\n standard: standard || null,\n favorite: false,\n tokenURI: tokenURI ?? null,\n };\n }\n\n if (hasIpfsTokenURI) {\n tokenURI = getFormattedIpfsUrl(ipfsGateway, tokenURI, useIPFSSubdomains);\n }\n\n try {\n const object = await handleFetch(tokenURI);\n // TODO: Check image_url existence. This is not part of EIP721 nor EIP1155\n const image = Object.prototype.hasOwnProperty.call(object, 'image')\n ? 'image'\n : /* istanbul ignore next */ 'image_url';\n\n return {\n image: object[image],\n name: object.name,\n description: object.description,\n standard,\n favorite: false,\n tokenURI: tokenURI ?? null,\n };\n } catch {\n return {\n image: null,\n name: null,\n description: null,\n standard: standard || null,\n favorite: false,\n tokenURI: tokenURI ?? null,\n };\n }\n }\n\n /**\n * Retrieve NFT uri with metadata. TODO Update method to use IPFS.\n *\n * @param contractAddress - NFT contract address.\n * @param tokenId - NFT token id.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving NFT uri and token standard.\n */\n private async getNftURIAndStandard(\n contractAddress: string,\n tokenId: string,\n networkClientId?: NetworkClientId,\n ): Promise<[string, string]> {\n // try ERC721 uri\n try {\n const uri = await this.getERC721TokenURI(\n contractAddress,\n tokenId,\n networkClientId,\n );\n return [uri, ERC721];\n } catch {\n // Ignore error\n }\n\n // try ERC1155 uri\n try {\n const tokenURI = await this.getERC1155TokenURI(\n contractAddress,\n tokenId,\n networkClientId,\n );\n\n /**\n * According to EIP1155 the URI value allows for ID substitution\n * in case the string `{id}` exists.\n * https://eips.ethereum.org/EIPS/eip-1155#metadata\n */\n\n if (!tokenURI.includes('{id}')) {\n return [tokenURI, ERC1155];\n }\n\n const hexTokenId = stripHexPrefix(BNToHex(new BN(tokenId)))\n .padStart(64, '0')\n .toLowerCase();\n return [tokenURI.replace('{id}', hexTokenId), ERC1155];\n } catch {\n // Ignore error\n }\n\n return ['', ''];\n }\n\n /**\n * Request individual NFT information (name, image url and description).\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving to the current NFT name and image.\n */\n private async getNftInformation(\n contractAddress: string,\n tokenId: string,\n networkClientId?: NetworkClientId,\n ): Promise<NftMetadata> {\n let { chainId } = this.config;\n if (networkClientId) {\n chainId =\n this.getNetworkClientById(networkClientId).configuration.chainId;\n }\n\n const blockchainMetadata = await safelyExecute(async () => {\n return await this.getNftInformationFromTokenURI(\n contractAddress,\n tokenId,\n networkClientId,\n );\n });\n\n let openSeaMetadata;\n // currently we only need to enter this block if we are on mainnet\n if (this.config.openSeaEnabled && chainId === '0x1') {\n openSeaMetadata = await safelyExecute(async () => {\n return await this.getNftInformationFromApi(contractAddress, tokenId);\n });\n }\n\n return {\n ...openSeaMetadata,\n name: blockchainMetadata.name ?? openSeaMetadata?.name ?? null,\n description:\n blockchainMetadata.description ?? openSeaMetadata?.description ?? null,\n image: blockchainMetadata.image ?? openSeaMetadata?.image ?? null,\n standard:\n blockchainMetadata.standard ?? openSeaMetadata?.standard ?? null,\n tokenURI: blockchainMetadata.tokenURI ?? null,\n };\n }\n\n /**\n * Request NFT contract information from OpenSea API.\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @returns Promise resolving to the current NFT name and image.\n */\n private async getNftContractInformationFromApi(\n contractAddress: string,\n ): Promise<ApiNftContract> {\n /* istanbul ignore if */\n const apiNftContractObject: ApiNftContract | undefined =\n await fetchWithErrorHandling({\n url: this.getNftContractInformationApi({\n contractAddress,\n }),\n });\n\n // if we successfully fetched return the fetched data immediately\n if (apiNftContractObject) {\n return apiNftContractObject;\n }\n\n // If we've reached this point we were unable to fetch data from either the proxy or opensea so we return\n // the default/null of ApiNftContract\n return {\n address: contractAddress,\n asset_contract_type: null,\n created_date: null,\n schema_name: null,\n symbol: null,\n total_supply: null,\n description: null,\n external_link: null,\n collection: {\n name: null,\n image_url: null,\n },\n };\n }\n\n /**\n * Request NFT contract information from the contract itself.\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving to the current NFT name and image.\n */\n private async getNftContractInformationFromContract(\n contractAddress: string,\n networkClientId?: NetworkClientId,\n ): Promise<\n Partial<ApiNftContract> &\n Pick<ApiNftContract, 'address'> &\n Pick<ApiNftContract, 'collection'>\n > {\n const name = await this.getERC721AssetName(\n contractAddress,\n networkClientId,\n );\n const symbol = await this.getERC721AssetSymbol(\n contractAddress,\n networkClientId,\n );\n return {\n collection: { name },\n symbol,\n address: contractAddress,\n };\n }\n\n /**\n * Request NFT contract information from OpenSea API.\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving to the NFT contract name, image and description.\n */\n private async getNftContractInformation(\n contractAddress: string,\n networkClientId?: NetworkClientId,\n ): Promise<\n Partial<ApiNftContract> &\n Pick<ApiNftContract, 'address'> &\n Pick<ApiNftContract, 'collection'>\n > {\n const blockchainContractData: Partial<ApiNftContract> &\n Pick<ApiNftContract, 'address'> &\n Pick<ApiNftContract, 'collection'> = await safelyExecute(async () => {\n return await this.getNftContractInformationFromContract(\n contractAddress,\n networkClientId,\n );\n });\n\n const { chainId } = this.config;\n const getCurrentChainId = this.getCorrectChainId({\n chainId,\n networkClientId,\n });\n\n let openSeaContractData: Partial<ApiNftContract> | undefined;\n if (this.config.openSeaEnabled && getCurrentChainId === '0x1') {\n openSeaContractData = await safelyExecute(async () => {\n return await this.getNftContractInformationFromApi(contractAddress);\n });\n }\n\n if (blockchainContractData || openSeaContractData) {\n return {\n ...openSeaContractData,\n ...blockchainContractData,\n collection: {\n image_url: null,\n ...openSeaContractData?.collection,\n ...blockchainContractData?.collection,\n },\n };\n }\n\n /* istanbul ignore next */\n return {\n address: contractAddress,\n asset_contract_type: null,\n created_date: null,\n schema_name: null,\n symbol: null,\n total_supply: null,\n description: null,\n external_link: null,\n collection: { name: null, image_url: null },\n };\n }\n\n /**\n * Adds an individual NFT to the stored NFT list.\n *\n * @param tokenAddress - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @param nftMetadata - NFT optional information (name, image and description).\n * @param nftContract - An object containing contract data of the NFT being added.\n * @param chainId - The chainId of the network where the NFT is being added.\n * @param userAddress - The address of the account where the NFT is being added.\n * @param source - Whether the NFT was detected, added manually or suggested by a dapp.\n * @returns Promise resolving to the current NFT list.\n */\n private async addIndividualNft(\n tokenAddress: string,\n tokenId: string,\n nftMetadata: NftMetadata,\n nftContract: NftContract,\n chainId: Hex,\n userAddress: string,\n source: Source,\n ): Promise<Nft[]> {\n // TODO: Remove unused return\n const releaseLock = await this.mutex.acquire();\n try {\n tokenAddress = toChecksumHexAddress(tokenAddress);\n const { allNfts } = this.state;\n\n const nfts = allNfts[userAddress]?.[chainId] || [];\n\n const existingEntry: Nft | undefined = nfts.find(\n (nft) =>\n nft.address.toLowerCase() === tokenAddress.toLowerCase() &&\n nft.tokenId === tokenId,\n );\n\n if (existingEntry) {\n const differentMetadata = compareNftMetadata(\n nftMetadata,\n existingEntry,\n );\n if (differentMetadata || !existingEntry.isCurrentlyOwned) {\n // TODO: Switch to indexToUpdate\n const indexToRemove = nfts.findIndex(\n (nft) =>\n nft.address.toLowerCase() === tokenAddress.toLowerCase() &&\n nft.tokenId === tokenId,\n );\n /* istanbul ignore next */\n if (indexToRemove !== -1) {\n nfts.splice(indexToRemove, 1);\n }\n } else {\n return nfts;\n }\n }\n\n const newEntry: Nft = {\n address: tokenAddress,\n tokenId,\n favorite: existingEntry?.favorite || false,\n isCurrentlyOwned: true,\n ...nftMetadata,\n };\n\n const newNfts = [...nfts, newEntry];\n this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY, {\n chainId,\n userAddress,\n });\n\n if (this.onNftAdded) {\n this.onNftAdded({\n address: tokenAddress,\n symbol: nftContract.symbol,\n tokenId: tokenId.toString(),\n standard: nftMetadata.standard,\n source,\n });\n }\n\n return newNfts;\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Adds an NFT contract to the stored NFT contracts list.\n *\n * @param options - options.\n * @param options.tokenAddress - Hex address of the NFT contract.\n * @param options.chainId - The chainId of the network where the NFT is being added.\n * @param options.userAddress - The address of the account where the NFT is being added.\n * @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @param options.source - Whether the NFT was detected, added manually or suggested by a dapp.\n * @returns Promise resolving to the current NFT contracts list.\n */\n private async addNftContract({\n tokenAddress,\n chainId,\n userAddress,\n networkClientId,\n source,\n }: {\n tokenAddress: string;\n chainId?: Hex;\n userAddress?: string;\n networkClientId?: NetworkClientId;\n source?: Source;\n }): Promise<NftContract[]> {\n const releaseLock = await this.mutex.acquire();\n try {\n tokenAddress = toChecksumHexAddress(tokenAddress);\n const { allNftContracts } = this.state;\n const currentChainId = this.getCorrectChainId({\n chainId,\n networkClientId,\n });\n const selectedAddress = userAddress ?? this.config.selectedAddress;\n\n const nftContracts =\n allNftContracts[selectedAddress]?.[currentChainId] || [];\n\n const existingEntry = nftContracts.find(\n (nftContract) =>\n nftContract.address.toLowerCase() === tokenAddress.toLowerCase(),\n );\n if (existingEntry) {\n return nftContracts;\n }\n\n // this doesn't work currently for detection if the user switches networks while the detection is processing\n // will be fixed once detection uses networkClientIds\n const contractInformation = await this.getNftContractInformation(\n tokenAddress,\n networkClientId,\n );\n const {\n asset_contract_type,\n created_date,\n schema_name,\n symbol,\n total_supply,\n description,\n external_link,\n collection: { name, image_url },\n } = contractInformation;\n\n // If the nft is auto-detected we want some valid metadata to be present\n if (\n source === Source.Detected &&\n Object.entries(contractInformation).every(([k, v]: [string, any]) => {\n if (k === 'address') {\n return true; // address will always be present\n }\n // collection will always be an object, we need to check the internal values\n if (k === 'collection') {\n return v?.name === null && v?.image_url === null;\n }\n return !v;\n })\n ) {\n return nftContracts;\n }\n\n /* istanbul ignore next */\n const newEntry: NftContract = Object.assign(\n {},\n { address: tokenAddress },\n description && { description },\n name && { name },\n image_url && { logo: image_url },\n symbol && { symbol },\n total_supply !== null &&\n typeof total_supply !== 'undefined' && { totalSupply: total_supply },\n asset_contract_type && { assetContractType: asset_contract_type },\n created_date && { createdDate: created_date },\n schema_name && { schemaName: schema_name },\n external_link && { externalLink: external_link },\n );\n const newNftContracts = [...nftContracts, newEntry];\n this.updateNestedNftState(newNftContracts, ALL_NFTS_CONTRACTS_STATE_KEY, {\n chainId: currentChainId,\n userAddress: selectedAddress,\n });\n\n return newNftContracts;\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Removes an individual NFT from the stored token list and saves it in ignored NFTs list.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Token identifier of the NFT.\n */\n private removeAndIgnoreIndividualNft(address: string, tokenId: string) {\n address = toChecksumHexAddress(address);\n const { allNfts, ignoredNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const newIgnoredNfts = [...ignoredNfts];\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const newNfts = nfts.filter((nft) => {\n if (\n nft.address.toLowerCase() === address.toLowerCase() &&\n nft.tokenId === tokenId\n ) {\n const alreadyIgnored = newIgnoredNfts.find(\n (c) => c.address === address && c.tokenId === tokenId,\n );\n !alreadyIgnored && newIgnoredNfts.push(nft);\n return false;\n }\n return true;\n });\n\n this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY);\n\n this.update({\n ignoredNfts: newIgnoredNfts,\n });\n }\n\n /**\n * Removes an individual NFT from the stored token list.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Token identifier of the NFT.\n */\n private removeIndividualNft(address: string, tokenId: string) {\n address = toChecksumHexAddress(address);\n const { allNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const newNfts = nfts.filter(\n (nft) =>\n !(\n nft.address.toLowerCase() === address.toLowerCase() &&\n nft.tokenId === tokenId\n ),\n );\n this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY);\n }\n\n /**\n * Removes an NFT contract to the stored NFT contracts list.\n *\n * @param address - Hex address of the NFT contract.\n * @returns Promise resolving to the current NFT contracts list.\n */\n private removeNftContract(address: string): NftContract[] {\n address = toChecksumHexAddress(address);\n const { allNftContracts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nftContracts = allNftContracts[selectedAddress]?.[chainId] || [];\n\n const newNftContracts = nftContracts.filter(\n (nftContract) =>\n !(nftContract.address.toLowerCase() === address.toLowerCase()),\n );\n this.updateNestedNftState(newNftContracts, ALL_NFTS_CONTRACTS_STATE_KEY);\n\n return newNftContracts;\n }\n\n /**\n * EventEmitter instance used to listen to specific EIP747 events\n */\n hub = new EventEmitter();\n\n /**\n * Optional API key to use with opensea\n */\n openSeaApiKey?: string;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'NftController';\n\n private readonly getERC721AssetName: AssetsContractController['getERC721AssetName'];\n\n private readonly getERC721AssetSymbol: AssetsContractController['getERC721AssetSymbol'];\n\n private readonly getERC721TokenURI: AssetsContractController['getERC721TokenURI'];\n\n private readonly getERC721OwnerOf: AssetsContractController['getERC721OwnerOf'];\n\n private readonly getERC1155BalanceOf: AssetsContractController['getERC1155BalanceOf'];\n\n private readonly getERC1155TokenURI: AssetsContractController['getERC1155TokenURI'];\n\n private readonly getNetworkClientById: NetworkController['getNetworkClientById'];\n\n private readonly onNftAdded?: (data: {\n address: string;\n symbol: string | undefined;\n tokenId: string;\n standard: string | null;\n source: Source;\n }) => void;\n\n /**\n * Creates an NftController instance.\n *\n * @param options - The controller options.\n * @param options.chainId - The chain ID of the current network.\n * @param options.onPreferencesStateChange - Allows subscribing to preference controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.\n * @param options.getERC721AssetName - Gets the name of the asset at the given address.\n * @param options.getERC721AssetSymbol - Gets the symbol of the asset at the given address.\n * @param options.getERC721TokenURI - Gets the URI of the ERC721 token at the given address, with the given ID.\n * @param options.getERC721OwnerOf - Get the owner of a ERC-721 NFT.\n * @param options.getERC1155BalanceOf - Gets balance of a ERC-1155 NFT.\n * @param options.getERC1155TokenURI - Gets the URI of the ERC1155 token at the given address, with the given ID.\n * @param options.getNetworkClientById - Gets the network client for the given networkClientId.\n * @param options.onNftAdded - Callback that is called when an NFT is added. Currently used pass data\n * for tracking the NFT added event.\n * @param options.messenger - The controller messenger.\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n {\n chainId: initialChainId,\n onPreferencesStateChange,\n onNetworkStateChange,\n getERC721AssetName,\n getERC721AssetSymbol,\n getERC721TokenURI,\n getERC721OwnerOf,\n getERC1155BalanceOf,\n getERC1155TokenURI,\n getNetworkClientById,\n onNftAdded,\n messenger,\n }: {\n chainId: Hex;\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n getERC721AssetName: AssetsContractController['getERC721AssetName'];\n getERC721AssetSymbol: AssetsContractController['getERC721AssetSymbol'];\n getERC721TokenURI: AssetsContractController['getERC721TokenURI'];\n getERC721OwnerOf: AssetsContractController['getERC721OwnerOf'];\n getERC1155BalanceOf: AssetsContractController['getERC1155BalanceOf'];\n getERC1155TokenURI: AssetsContractController['getERC1155TokenURI'];\n getNetworkClientById: NetworkController['getNetworkClientById'];\n onNftAdded?: (data: {\n address: string;\n symbol: string | undefined;\n tokenId: string;\n standard: string | null;\n source: string;\n }) => void;\n messenger: NftControllerMessenger;\n },\n config?: Partial<BaseConfig>,\n state?: Partial<NftState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n selectedAddress: '',\n chainId: initialChainId,\n ipfsGateway: IPFS_DEFAULT_GATEWAY_URL,\n openSeaEnabled: false,\n useIPFSSubdomains: true,\n isIpfsGatewayEnabled: true,\n };\n\n this.defaultState = {\n allNftContracts: {},\n allNfts: {},\n ignoredNfts: [],\n };\n this.initialize();\n this.getERC721AssetName = getERC721AssetName;\n this.getERC721AssetSymbol = getERC721AssetSymbol;\n this.getERC721TokenURI = getERC721TokenURI;\n this.getERC721OwnerOf = getERC721OwnerOf;\n this.getERC1155BalanceOf = getERC1155BalanceOf;\n this.getERC1155TokenURI = getERC1155TokenURI;\n this.getNetworkClientById = getNetworkClientById;\n this.onNftAdded = onNftAdded;\n this.messagingSystem = messenger;\n\n onPreferencesStateChange(\n ({\n selectedAddress,\n ipfsGateway,\n openSeaEnabled,\n isIpfsGatewayEnabled,\n }) => {\n this.configure({\n selectedAddress,\n ipfsGateway,\n openSeaEnabled,\n isIpfsGatewayEnabled,\n });\n },\n );\n\n onNetworkStateChange(({ providerConfig }) => {\n const { chainId } = providerConfig;\n this.configure({ chainId });\n });\n }\n\n async validateWatchNft(\n asset: NftAsset,\n type: NFTStandardType,\n userAddress: string,\n ) {\n const { address: contractAddress, tokenId } = asset;\n\n // Validate parameters\n if (!type) {\n throw rpcErrors.invalidParams('Asset type is required');\n }\n\n if (type !== ERC721 && type !== ERC1155) {\n throw rpcErrors.invalidParams(\n `Non NFT asset type ${type} not supported by watchNft`,\n );\n }\n\n if (!contractAddress || !tokenId) {\n throw rpcErrors.invalidParams('Both address and tokenId are required');\n }\n\n if (!isAddress(contractAddress)) {\n throw rpcErrors.invalidParams('Invalid address');\n }\n\n if (!/^\\d+$/u.test(tokenId)) {\n throw rpcErrors.invalidParams('Invalid tokenId');\n }\n\n // Check if the user owns the suggested NFT\n try {\n const isOwner = await this.isNftOwner(\n userAddress,\n contractAddress,\n tokenId,\n );\n if (!isOwner) {\n throw rpcErrors.invalidInput(\n 'Suggested NFT is not owned by the selected account',\n );\n }\n } catch (error: any) {\n // error thrown here: \"Unable to verify ownership. Possibly because the standard is not supported or the user's currently selected network does not match the chain of the asset in question.\"\n throw rpcErrors.resourceUnavailable(error.message);\n }\n }\n\n // temporary method to get the correct chainId until we remove chainId from the config & the chainId arg from the detection logic\n // Just a helper method to prefer the networkClient chainId first then the chainId argument and then finally the config chainId\n private getCorrectChainId({\n chainId,\n networkClientId,\n }: {\n chainId?: Hex;\n networkClientId?: NetworkClientId;\n }) {\n if (networkClientId) {\n return this.getNetworkClientById(networkClientId).configuration.chainId;\n } else if (chainId) {\n return chainId;\n }\n return this.config.chainId;\n }\n\n /**\n * Adds a new suggestedAsset to state. Parameters will be validated according to\n * asset type being watched. A `<suggestedNftMeta.id>:pending` hub event will be emitted once added.\n *\n * @param asset - The asset to be watched. For now ERC721 and ERC1155 tokens are accepted.\n * @param asset.address - The address of the asset contract.\n * @param asset.tokenId - The ID of the asset.\n * @param type - The asset type.\n * @param origin - Domain origin to register the asset from.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Object containing a Promise resolving to the suggestedAsset address if accepted.\n */\n async watchNft(\n asset: NftAsset,\n type: NFTStandardType,\n origin: string,\n networkClientId?: NetworkClientId,\n ) {\n const { selectedAddress, chainId } = this.config;\n\n await this.validateWatchNft(asset, type, selectedAddress);\n\n const nftMetadata = await this.getNftInformation(\n asset.address,\n asset.tokenId,\n networkClientId,\n );\n\n if (nftMetadata.standard && nftMetadata.standard !== type) {\n throw rpcErrors.invalidInput(\n `Suggested NFT of type ${nftMetadata.standard} does not match received type ${type}`,\n );\n }\n\n const suggestedNftMeta: SuggestedNftMeta = {\n asset: { ...asset, ...nftMetadata },\n type,\n id: random(),\n time: Date.now(),\n interactingAddress: selectedAddress,\n origin,\n };\n await this._requestApproval(suggestedNftMeta);\n const { address, tokenId } = asset;\n const { name, standard, description, image } = nftMetadata;\n\n await this.addNft(address, tokenId, {\n nftMetadata: {\n name: name ?? null,\n description: description ?? null,\n image: image ?? null,\n standard: standard ?? null,\n },\n chainId,\n userAddress: selectedAddress,\n source: Source.Dapp,\n networkClientId,\n });\n }\n\n /**\n * Sets an OpenSea API key to retrieve NFT information.\n *\n * @param openSeaApiKey - OpenSea API key.\n */\n setApiKey(openSeaApiKey: string) {\n this.openSeaApiKey = openSeaApiKey;\n }\n\n /**\n * Checks the ownership of a ERC-721 or ERC-1155 NFT for a given address.\n *\n * @param ownerAddress - User public address.\n * @param nftAddress - NFT contract address.\n * @param tokenId - NFT token ID.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving the NFT ownership.\n */\n async isNftOwner(\n ownerAddress: string,\n nftAddress: string,\n tokenId: string,\n networkClientId?: NetworkClientId,\n ): Promise<boolean> {\n // Checks the ownership for ERC-721.\n try {\n const owner = await this.getERC721OwnerOf(\n nftAddress,\n tokenId,\n networkClientId,\n );\n return ownerAddress.toLowerCase() === owner.toLowerCase();\n // eslint-disable-next-line no-empty\n } catch {\n // Ignore ERC-721 contract error\n }\n\n // Checks the ownership for ERC-1155.\n try {\n const balance = await this.getERC1155BalanceOf(\n ownerAddress,\n nftAddress,\n tokenId,\n networkClientId,\n );\n return !balance.isZero();\n // eslint-disable-next-line no-empty\n } catch {\n // Ignore ERC-1155 contract error\n }\n\n throw new Error(\n `Unable to verify ownership. Possibly because the standard is not supported or the user's currently selected network does not match the chain of the asset in question.`,\n );\n }\n\n /**\n * Verifies currently selected address owns entered NFT address/tokenId combo and\n * adds the NFT and respective NFT contract to the stored NFT and NFT contracts lists.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @param source - Whether the NFT was detected, added manually or suggested by a dapp.\n */\n async addNftVerifyOwnership(\n address: string,\n tokenId: string,\n networkClientId?: NetworkClientId,\n source?: Source,\n ) {\n const { selectedAddress } = this.config;\n if (\n !(await this.isNftOwner(\n selectedAddress,\n address,\n tokenId,\n networkClientId,\n ))\n ) {\n throw new Error('This NFT is not owned by the user');\n }\n await this.addNft(address, tokenId, { networkClientId, source });\n }\n\n /**\n * Adds an NFT and respective NFT contract to the stored NFT and NFT contracts lists.\n *\n * @param tokenAddress - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @param options - an object of arguments\n * @param options.nftMetadata - NFT optional metadata.\n * @param options.chainId - The chain ID of the current network.\n * @param options.userAddress - The address of the current user.\n * @param options.source - Whether the NFT was detected, added manually or suggested by a dapp.\n * @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving to the current NFT list.\n */\n async addNft(\n tokenAddress: string,\n tokenId: string,\n {\n nftMetadata,\n chainId, // TODO remove and replace chainId arg with fetch chainId using getNetworkClientById(networkClientId).configuration.chainId once polling refactor is complete\n userAddress,\n source = Source.Custom,\n networkClientId,\n }: {\n nftMetadata?: NftMetadata;\n chainId?: Hex;\n userAddress?: string;\n source?: Source;\n networkClientId?: NetworkClientId;\n } = {},\n ) {\n tokenAddress = toChecksumHexAddress(tokenAddress);\n\n const currentChainId = this.getCorrectChainId({ chainId, networkClientId });\n const selectedAddress = userAddress ?? this.config.selectedAddress;\n\n const newNftContracts = await this.addNftContract({\n tokenAddress,\n chainId: currentChainId,\n userAddress: selectedAddress,\n networkClientId,\n source,\n });\n\n nftMetadata =\n nftMetadata ||\n (await this.getNftInformation(tokenAddress, tokenId, networkClientId));\n\n // If NFT contract was not added, do not add individual NFT\n const nftContract = newNftContracts.find(\n (contract) =>\n contract.address.toLowerCase() === tokenAddress.toLowerCase(),\n );\n\n // If NFT contract information, add individual NFT\n if (nftContract) {\n await this.addIndividualNft(\n tokenAddress,\n tokenId,\n nftMetadata,\n nftContract,\n currentChainId,\n selectedAddress,\n source,\n );\n }\n }\n\n /**\n * Removes an NFT from the stored token list.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Token identifier of the NFT.\n */\n removeNft(address: string, tokenId: string) {\n address = toChecksumHexAddress(address);\n this.removeIndividualNft(address, tokenId);\n const { allNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const remainingNft = nfts.find(\n (nft) => nft.address.toLowerCase() === address.toLowerCase(),\n );\n if (!remainingNft) {\n this.removeNftContract(address);\n }\n }\n\n /**\n * Removes an NFT from the stored token list and saves it in ignored NFTs list.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Token identifier of the NFT.\n */\n removeAndIgnoreNft(address: string, tokenId: string) {\n address = toChecksumHexAddress(address);\n this.removeAndIgnoreIndividualNft(address, tokenId);\n const { allNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const remainingNft = nfts.find(\n (nft) => nft.address.toLowerCase() === address.toLowerCase(),\n );\n if (!remainingNft) {\n this.removeNftContract(address);\n }\n }\n\n /**\n * Removes all NFTs from the ignored list.\n */\n clearIgnoredNfts() {\n this.update({ ignoredNfts: [] });\n }\n\n /**\n * Checks whether input NFT is still owned by the user\n * And updates the isCurrentlyOwned value on the NFT object accordingly.\n *\n * @param nft - The NFT object to check and update.\n * @param batch - A boolean indicating whether this method is being called as part of a batch or single update.\n * @param accountParams - The userAddress and chainId to check ownership against\n * @param accountParams.userAddress - the address passed through the confirmed transaction flow to ensure assets are stored to the correct account\n * @param accountParams.chainId - the chainId passed through the confirmed transaction flow to ensure assets are stored to the correct account\n * @returns the NFT with the updated isCurrentlyOwned value\n */\n async checkAndUpdateSingleNftOwnershipStatus(\n nft: Nft,\n batch: boolean,\n { userAddress, chainId } = {\n userAddress: this.config.selectedAddress,\n chainId: this.config.chainId,\n },\n ) {\n const { address, tokenId } = nft;\n let isOwned = nft.isCurrentlyOwned;\n try {\n isOwned = await this.isNftOwner(userAddress, address, tokenId);\n } catch (error) {\n if (\n !(\n error instanceof Error &&\n error.message.includes('Unable to verify ownership')\n )\n ) {\n throw error;\n }\n }\n\n nft.isCurrentlyOwned = isOwned;\n\n if (batch) {\n return nft;\n }\n\n // if this is not part of a batched update we update this one NFT in state\n const { allNfts } = this.state;\n const nfts = allNfts[userAddress]?.[chainId] || [];\n const nftToUpdate = nfts.find(\n (item) =>\n item.tokenId === tokenId &&\n item.address.toLowerCase() === address.toLowerCase(),\n );\n if (nftToUpdate) {\n nftToUpdate.isCurrentlyOwned = isOwned;\n this.updateNestedNftState(nfts, ALL_NFTS_STATE_KEY, {\n userAddress,\n chainId,\n });\n }\n return nft;\n }\n\n /**\n * Checks whether NFTs associated with current selectedAddress/chainId combination are still owned by the user\n * And updates the isCurrentlyOwned value on each accordingly.\n */\n async checkAndUpdateAllNftsOwnershipStatus() {\n const { allNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const updatedNfts = await Promise.all(\n nfts.map(async (nft) => {\n return (\n (await this.checkAndUpdateSingleNftOwnershipStatus(nft, true)) ?? nft\n );\n }),\n );\n\n this.updateNestedNftState(updatedNfts, ALL_NFTS_STATE_KEY);\n }\n\n /**\n * Update NFT favorite status.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Hex address of the NFT contract.\n * @param favorite - NFT new favorite status.\n */\n updateNftFavoriteStatus(address: string, tokenId: string, favorite: boolean) {\n const { allNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const index: number = nfts.findIndex(\n (nft) => nft.address === address && nft.tokenId === tokenId,\n );\n\n if (index === -1) {\n return;\n }\n\n const updatedNft: Nft = {\n ...nfts[index],\n favorite,\n };\n\n // Update Nfts array\n nfts[index] = updatedNft;\n\n this.updateNestedNftState(nfts, ALL_NFTS_STATE_KEY);\n }\n\n /**\n * Returns an NFT by the address and token id.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Number that represents the id of the token.\n * @param selectedAddress - Hex address of the user account.\n * @param chainId - Id of the current network.\n * @returns Object containing the NFT and its position in the array\n */\n findNftByAddressAndTokenId(\n address: string,\n tokenId: string,\n selectedAddress: string,\n chainId: Hex,\n ): { nft: Nft; index: number } | null {\n const { allNfts } = this.state;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const index: number = nfts.findIndex(\n (nft) =>\n nft.address.toLowerCase() === address.toLowerCase() &&\n nft.tokenId === tokenId,\n );\n\n if (index === -1) {\n return null;\n }\n\n return { nft: nfts[index], index };\n }\n\n /**\n * Update NFT data.\n *\n * @param nft - NFT object to find the right NFT to updates.\n * @param updates - NFT partial object to update properties of the NFT.\n * @param selectedAddress - Hex address of the user account.\n * @param chainId - Id of the current network.\n */\n updateNft(\n nft: Nft,\n updates: Partial<Nft>,\n selectedAddress: string,\n chainId: Hex,\n ) {\n const { allNfts } = this.state;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const nftInfo = this.findNftByAddressAndTokenId(\n nft.address,\n nft.tokenId,\n selectedAddress,\n chainId,\n );\n\n if (!nftInfo) {\n return;\n }\n\n const updatedNft: Nft = {\n ...nft,\n ...updates,\n };\n\n const newNfts = [\n ...nfts.slice(0, nftInfo.index),\n updatedNft,\n ...nfts.slice(nftInfo.index + 1),\n ];\n\n this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY);\n }\n\n /**\n * Resets the transaction status of an NFT.\n *\n * @param transactionId - NFT transaction id.\n * @param selectedAddress - Hex address of the user account.\n * @param chainId - Id of the current network.\n * @returns a boolean indicating if the reset was well succeded or not\n */\n resetNftTransactionStatusByTransactionId(\n transactionId: string,\n selectedAddress: string,\n chainId: Hex,\n ): boolean {\n const { allNfts } = this.state;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const index: number = nfts.findIndex(\n (nft) => nft.transactionId === transactionId,\n );\n\n if (index === -1) {\n return false;\n }\n const updatedNft: Nft = {\n ...nfts[index],\n transactionId: undefined,\n };\n\n const newNfts = [\n ...nfts.slice(0, index),\n updatedNft,\n ...nfts.slice(index + 1),\n ];\n\n this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY);\n return true;\n }\n\n async _requestApproval(suggestedNftMeta: SuggestedNftMeta) {\n return this.messagingSystem.call(\n 'ApprovalController:addRequest',\n {\n id: suggestedNftMeta.id,\n origin: suggestedNftMeta.origin,\n type: ApprovalType.WatchAsset,\n requestData: {\n id: suggestedNftMeta.id,\n interactingAddress: suggestedNftMeta.interactingAddress,\n asset: {\n address: suggestedNftMeta.asset.address,\n tokenId: suggestedNftMeta.asset.tokenId,\n name: suggestedNftMeta.asset.name,\n description: suggestedNftMeta.asset.description,\n image: suggestedNftMeta.asset.image,\n standard: suggestedNftMeta.asset.standard,\n },\n },\n },\n true,\n );\n }\n}\n\nexport default NftController;\n"]}
|
|
1
|
+
{"version":3,"file":"NftController.js","sourceRoot":"","sources":["../src/NftController.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAddress } from '@ethersproject/address';\nimport type { AddApprovalRequest } from '@metamask/approval-controller';\nimport type {\n BaseConfig,\n BaseState,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n safelyExecute,\n handleFetch,\n toChecksumHexAddress,\n BNToHex,\n fetchWithErrorHandling,\n IPFS_DEFAULT_GATEWAY_URL,\n ERC721,\n ERC1155,\n OPENSEA_PROXY_URL,\n ApprovalType,\n} from '@metamask/controller-utils';\nimport type {\n NetworkClientId,\n NetworkController,\n NetworkState,\n} from '@metamask/network-controller';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type { Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { BN, stripHexPrefix } from 'ethereumjs-util';\nimport { EventEmitter } from 'events';\nimport { v4 as random } from 'uuid';\n\nimport type { AssetsContractController } from './AssetsContractController';\nimport { compareNftMetadata, getFormattedIpfsUrl } from './assetsUtil';\nimport { Source } from './constants';\nimport type {\n ApiNft,\n ApiNftCreator,\n ApiNftContract,\n ApiNftLastSale,\n} from './NftDetectionController';\n\ntype NFTStandardType = 'ERC721' | 'ERC1155';\n\ntype SuggestedNftMeta = {\n asset: { address: string; tokenId: string } & NftMetadata;\n id: string;\n time: number;\n type: NFTStandardType;\n interactingAddress: string;\n origin: string;\n};\n\n/**\n * @type Nft\n *\n * NFT representation\n * @property address - Hex address of a ERC721 contract\n * @property description - The NFT description\n * @property image - URI of custom NFT image associated with this tokenId\n * @property name - Name associated with this tokenId and contract address\n * @property tokenId - The NFT identifier\n * @property numberOfSales - Number of sales\n * @property backgroundColor - The background color to be displayed with the item\n * @property imagePreview - URI of a smaller image associated with this NFT\n * @property imageThumbnail - URI of a thumbnail image associated with this NFT\n * @property imageOriginal - URI of the original image associated with this NFT\n * @property animation - URI of a animation associated with this NFT\n * @property animationOriginal - URI of the original animation associated with this NFT\n * @property externalLink - External link containing additional information\n * @property creator - The NFT owner information object\n * @property isCurrentlyOwned - Boolean indicating whether the address/chainId combination where it's currently stored currently owns this NFT\n * @property transactionId - Transaction Id associated with the NFT\n */\nexport interface Nft extends NftMetadata {\n tokenId: string;\n address: string;\n isCurrentlyOwned?: boolean;\n}\n\n/**\n * @type NftContract\n *\n * NFT contract information representation\n * @property name - Contract name\n * @property logo - Contract logo\n * @property address - Contract address\n * @property symbol - Contract symbol\n * @property description - Contract description\n * @property totalSupply - Total supply of NFTs\n * @property assetContractType - The NFT type, it could be `semi-fungible` or `non-fungible`\n * @property createdDate - Creation date\n * @property schemaName - The schema followed by the contract, it could be `ERC721` or `ERC1155`\n * @property externalLink - External link containing additional information\n */\nexport interface NftContract {\n name?: string;\n logo?: string;\n address: string;\n symbol?: string;\n description?: string;\n totalSupply?: string;\n assetContractType?: string;\n createdDate?: string;\n schemaName?: string;\n externalLink?: string;\n}\n\n/**\n * @type NftMetadata\n *\n * NFT custom information\n * @property name - NFT custom name\n * @property description - The NFT description\n * @property numberOfSales - Number of sales\n * @property backgroundColor - The background color to be displayed with the item\n * @property image - Image custom image URI\n * @property imagePreview - URI of a smaller image associated with this NFT\n * @property imageThumbnail - URI of a thumbnail image associated with this NFT\n * @property imageOriginal - URI of the original image associated with this NFT\n * @property animation - URI of a animation associated with this NFT\n * @property animationOriginal - URI of the original animation associated with this NFT\n * @property externalLink - External link containing additional information\n * @property creator - The NFT owner information object\n * @property standard - NFT standard name for the NFT, e.g., ERC-721 or ERC-1155\n */\nexport interface NftMetadata {\n name: string | null;\n description: string | null;\n image: string | null;\n standard: string | null;\n favorite?: boolean;\n numberOfSales?: number;\n backgroundColor?: string;\n imagePreview?: string;\n imageThumbnail?: string;\n imageOriginal?: string;\n animation?: string;\n animationOriginal?: string;\n externalLink?: string;\n creator?: ApiNftCreator;\n lastSale?: ApiNftLastSale;\n transactionId?: string;\n tokenURI?: string | null;\n}\n\n/**\n * @type NftConfig\n *\n * NFT controller configuration\n * @property selectedAddress - Vault selected address\n */\nexport interface NftConfig extends BaseConfig {\n selectedAddress: string;\n chainId: Hex;\n ipfsGateway: string;\n openSeaEnabled: boolean;\n useIPFSSubdomains: boolean;\n isIpfsGatewayEnabled: boolean;\n}\n\n/**\n * @type NftState\n *\n * NFT controller state\n * @property allNftContracts - Object containing NFT contract information\n * @property allNfts - Object containing NFTs per account and network\n * @property ignoredNfts - List of NFTs that should be ignored\n */\nexport interface NftState extends BaseState {\n allNftContracts: {\n [key: string]: { [chainId: Hex]: NftContract[] };\n };\n allNfts: { [key: string]: { [chainId: Hex]: Nft[] } };\n ignoredNfts: Nft[];\n}\n\nconst ALL_NFTS_STATE_KEY = 'allNfts';\nconst ALL_NFTS_CONTRACTS_STATE_KEY = 'allNftContracts';\n\ninterface NftAsset {\n address: string;\n tokenId: string;\n}\n\n/**\n * The name of the {@link NftController}.\n */\nconst controllerName = 'NftController';\n\n/**\n * The external actions available to the {@link NftController}.\n */\ntype AllowedActions = AddApprovalRequest;\n\n/**\n * The messenger of the {@link NftController}.\n */\nexport type NftControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n AllowedActions,\n never,\n AllowedActions['type'],\n never\n>;\n\n/**\n * Controller that stores assets and exposes convenience methods\n */\nexport class NftController extends BaseController<NftConfig, NftState> {\n private readonly mutex = new Mutex();\n\n private readonly messagingSystem: NftControllerMessenger;\n\n private getNftApi({\n contractAddress,\n tokenId,\n }: {\n contractAddress: string;\n tokenId: string;\n }) {\n return `${OPENSEA_PROXY_URL}/asset/${contractAddress}/${tokenId}`;\n }\n\n private getNftContractInformationApi({\n contractAddress,\n }: {\n contractAddress: string;\n }) {\n return `${OPENSEA_PROXY_URL}/asset_contract/${contractAddress}`;\n }\n\n /**\n * Helper method to update nested state for allNfts and allNftContracts.\n *\n * @param newCollection - the modified piece of state to update in the controller's store\n * @param baseStateKey - The root key in the store to update.\n * @param passedConfig - An object containing the selectedAddress and chainId that are passed through the auto-detection flow.\n * @param passedConfig.userAddress - the address passed through the NFT detection flow to ensure assets are stored to the correct account\n * @param passedConfig.chainId - the chainId passed through the NFT detection flow to ensure assets are stored to the correct account\n */\n private updateNestedNftState(\n newCollection: Nft[] | NftContract[],\n baseStateKey: 'allNfts' | 'allNftContracts',\n { userAddress, chainId } = {\n userAddress: this.config.selectedAddress,\n chainId: this.config.chainId,\n },\n ) {\n const { [baseStateKey]: oldState } = this.state;\n\n const addressState = oldState[userAddress];\n const newAddressState = {\n ...addressState,\n ...{ [chainId]: newCollection },\n };\n const newState = {\n ...oldState,\n ...{ [userAddress]: newAddressState },\n };\n\n this.update({\n [baseStateKey]: newState,\n });\n }\n\n /**\n * Request individual NFT information from OpenSea API.\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @returns Promise resolving to the current NFT name and image.\n */\n private async getNftInformationFromApi(\n contractAddress: string,\n tokenId: string,\n ): Promise<NftMetadata> {\n // TODO Parameterize this by chainId for non-mainnet token detection\n // Attempt to fetch the data with the proxy\n const nftInformation: ApiNft | undefined = await fetchWithErrorHandling({\n url: this.getNftApi({\n contractAddress,\n tokenId,\n }),\n });\n\n // if we were still unable to fetch the data we return out the default/null of `NftMetadata`\n if (!nftInformation) {\n return {\n name: null,\n description: null,\n image: null,\n standard: null,\n };\n }\n\n // if we've reached this point, we have successfully fetched some data for nftInformation\n // now we reconfigure the data to conform to the `NftMetadata` type for storage.\n const {\n num_sales,\n background_color,\n image_url,\n image_preview_url,\n image_thumbnail_url,\n image_original_url,\n animation_url,\n animation_original_url,\n name,\n description,\n external_link,\n creator,\n last_sale,\n asset_contract: { schema_name },\n } = nftInformation;\n\n /* istanbul ignore next */\n const nftMetadata: NftMetadata = Object.assign(\n {},\n { name: name || null },\n { description: description || null },\n { image: image_url || null },\n creator && { creator },\n num_sales && { numberOfSales: num_sales },\n background_color && { backgroundColor: background_color },\n image_preview_url && { imagePreview: image_preview_url },\n image_thumbnail_url && { imageThumbnail: image_thumbnail_url },\n image_original_url && { imageOriginal: image_original_url },\n animation_url && { animation: animation_url },\n animation_original_url && {\n animationOriginal: animation_original_url,\n },\n external_link && { externalLink: external_link },\n last_sale && { lastSale: last_sale },\n schema_name && { standard: schema_name },\n );\n\n return nftMetadata;\n }\n\n /**\n * Request individual NFT information from contracts that follows Metadata Interface.\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving to the current NFT name and image.\n */\n private async getNftInformationFromTokenURI(\n contractAddress: string,\n tokenId: string,\n networkClientId?: NetworkClientId,\n ): Promise<NftMetadata> {\n const { ipfsGateway, useIPFSSubdomains, isIpfsGatewayEnabled } =\n this.config;\n const result = await this.getNftURIAndStandard(\n contractAddress,\n tokenId,\n networkClientId,\n );\n let tokenURI = result[0];\n const standard = result[1];\n\n const hasIpfsTokenURI = tokenURI.startsWith('ipfs://');\n\n if (hasIpfsTokenURI && !isIpfsGatewayEnabled) {\n return {\n image: null,\n name: null,\n description: null,\n standard: standard || null,\n favorite: false,\n tokenURI: tokenURI ?? null,\n };\n }\n\n const isDisplayNFTMediaToggleEnabled = this.config.openSeaEnabled;\n if (!hasIpfsTokenURI && !isDisplayNFTMediaToggleEnabled) {\n return {\n image: null,\n name: null,\n description: null,\n standard: standard || null,\n favorite: false,\n tokenURI: tokenURI ?? null,\n };\n }\n\n if (hasIpfsTokenURI) {\n tokenURI = getFormattedIpfsUrl(ipfsGateway, tokenURI, useIPFSSubdomains);\n }\n\n try {\n const object = await handleFetch(tokenURI);\n // TODO: Check image_url existence. This is not part of EIP721 nor EIP1155\n const image = Object.prototype.hasOwnProperty.call(object, 'image')\n ? 'image'\n : /* istanbul ignore next */ 'image_url';\n\n return {\n image: object[image],\n name: object.name,\n description: object.description,\n standard,\n favorite: false,\n tokenURI: tokenURI ?? null,\n };\n } catch {\n return {\n image: null,\n name: null,\n description: null,\n standard: standard || null,\n favorite: false,\n tokenURI: tokenURI ?? null,\n };\n }\n }\n\n /**\n * Retrieve NFT uri with metadata. TODO Update method to use IPFS.\n *\n * @param contractAddress - NFT contract address.\n * @param tokenId - NFT token id.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving NFT uri and token standard.\n */\n private async getNftURIAndStandard(\n contractAddress: string,\n tokenId: string,\n networkClientId?: NetworkClientId,\n ): Promise<[string, string]> {\n // try ERC721 uri\n try {\n const uri = await this.getERC721TokenURI(\n contractAddress,\n tokenId,\n networkClientId,\n );\n return [uri, ERC721];\n } catch {\n // Ignore error\n }\n\n // try ERC1155 uri\n try {\n const tokenURI = await this.getERC1155TokenURI(\n contractAddress,\n tokenId,\n networkClientId,\n );\n\n /**\n * According to EIP1155 the URI value allows for ID substitution\n * in case the string `{id}` exists.\n * https://eips.ethereum.org/EIPS/eip-1155#metadata\n */\n\n if (!tokenURI.includes('{id}')) {\n return [tokenURI, ERC1155];\n }\n\n const hexTokenId = stripHexPrefix(BNToHex(new BN(tokenId)))\n .padStart(64, '0')\n .toLowerCase();\n return [tokenURI.replace('{id}', hexTokenId), ERC1155];\n } catch {\n // Ignore error\n }\n\n return ['', ''];\n }\n\n /**\n * Request individual NFT information (name, image url and description).\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving to the current NFT name and image.\n */\n private async getNftInformation(\n contractAddress: string,\n tokenId: string,\n networkClientId?: NetworkClientId,\n ): Promise<NftMetadata> {\n let { chainId } = this.config;\n if (networkClientId) {\n chainId =\n this.getNetworkClientById(networkClientId).configuration.chainId;\n }\n\n const blockchainMetadata = await safelyExecute(async () => {\n return await this.getNftInformationFromTokenURI(\n contractAddress,\n tokenId,\n networkClientId,\n );\n });\n\n let openSeaMetadata;\n // currently we only need to enter this block if we are on mainnet\n if (this.config.openSeaEnabled && chainId === '0x1') {\n openSeaMetadata = await safelyExecute(async () => {\n return await this.getNftInformationFromApi(contractAddress, tokenId);\n });\n }\n\n return {\n ...openSeaMetadata,\n name: blockchainMetadata.name ?? openSeaMetadata?.name ?? null,\n description:\n blockchainMetadata.description ?? openSeaMetadata?.description ?? null,\n image: blockchainMetadata.image ?? openSeaMetadata?.image ?? null,\n standard:\n blockchainMetadata.standard ?? openSeaMetadata?.standard ?? null,\n tokenURI: blockchainMetadata.tokenURI ?? null,\n };\n }\n\n /**\n * Request NFT contract information from OpenSea API.\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @returns Promise resolving to the current NFT name and image.\n */\n private async getNftContractInformationFromApi(\n contractAddress: string,\n ): Promise<ApiNftContract> {\n /* istanbul ignore if */\n const apiNftContractObject: ApiNftContract | undefined =\n await fetchWithErrorHandling({\n url: this.getNftContractInformationApi({\n contractAddress,\n }),\n });\n\n // if we successfully fetched return the fetched data immediately\n if (apiNftContractObject) {\n return apiNftContractObject;\n }\n\n // If we've reached this point we were unable to fetch data from either the proxy or opensea so we return\n // the default/null of ApiNftContract\n return {\n address: contractAddress,\n asset_contract_type: null,\n created_date: null,\n schema_name: null,\n symbol: null,\n total_supply: null,\n description: null,\n external_link: null,\n collection: {\n name: null,\n image_url: null,\n },\n };\n }\n\n /**\n * Request NFT contract information from the contract itself.\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving to the current NFT name and image.\n */\n private async getNftContractInformationFromContract(\n contractAddress: string,\n networkClientId?: NetworkClientId,\n ): Promise<\n Partial<ApiNftContract> &\n Pick<ApiNftContract, 'address'> &\n Pick<ApiNftContract, 'collection'>\n > {\n const name = await this.getERC721AssetName(\n contractAddress,\n networkClientId,\n );\n const symbol = await this.getERC721AssetSymbol(\n contractAddress,\n networkClientId,\n );\n return {\n collection: { name },\n symbol,\n address: contractAddress,\n };\n }\n\n /**\n * Request NFT contract information from OpenSea API.\n *\n * @param contractAddress - Hex address of the NFT contract.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving to the NFT contract name, image and description.\n */\n private async getNftContractInformation(\n contractAddress: string,\n networkClientId?: NetworkClientId,\n ): Promise<\n Partial<ApiNftContract> &\n Pick<ApiNftContract, 'address'> &\n Pick<ApiNftContract, 'collection'>\n > {\n const blockchainContractData: Partial<ApiNftContract> &\n Pick<ApiNftContract, 'address'> &\n Pick<ApiNftContract, 'collection'> = await safelyExecute(async () => {\n return await this.getNftContractInformationFromContract(\n contractAddress,\n networkClientId,\n );\n });\n\n const { chainId } = this.config;\n const getCurrentChainId = this.getCorrectChainId({\n chainId,\n networkClientId,\n });\n\n let openSeaContractData: Partial<ApiNftContract> | undefined;\n if (this.config.openSeaEnabled && getCurrentChainId === '0x1') {\n openSeaContractData = await safelyExecute(async () => {\n return await this.getNftContractInformationFromApi(contractAddress);\n });\n }\n\n if (blockchainContractData || openSeaContractData) {\n return {\n ...openSeaContractData,\n ...blockchainContractData,\n collection: {\n image_url: null,\n ...openSeaContractData?.collection,\n ...blockchainContractData?.collection,\n },\n };\n }\n\n /* istanbul ignore next */\n return {\n address: contractAddress,\n asset_contract_type: null,\n created_date: null,\n schema_name: null,\n symbol: null,\n total_supply: null,\n description: null,\n external_link: null,\n collection: { name: null, image_url: null },\n };\n }\n\n /**\n * Adds an individual NFT to the stored NFT list.\n *\n * @param tokenAddress - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @param nftMetadata - NFT optional information (name, image and description).\n * @param nftContract - An object containing contract data of the NFT being added.\n * @param chainId - The chainId of the network where the NFT is being added.\n * @param userAddress - The address of the account where the NFT is being added.\n * @param source - Whether the NFT was detected, added manually or suggested by a dapp.\n * @returns Promise resolving to the current NFT list.\n */\n private async addIndividualNft(\n tokenAddress: string,\n tokenId: string,\n nftMetadata: NftMetadata,\n nftContract: NftContract,\n chainId: Hex,\n userAddress: string,\n source: Source,\n ): Promise<Nft[]> {\n // TODO: Remove unused return\n const releaseLock = await this.mutex.acquire();\n try {\n tokenAddress = toChecksumHexAddress(tokenAddress);\n const { allNfts } = this.state;\n\n const nfts = allNfts[userAddress]?.[chainId] || [];\n\n const existingEntry: Nft | undefined = nfts.find(\n (nft) =>\n nft.address.toLowerCase() === tokenAddress.toLowerCase() &&\n nft.tokenId === tokenId,\n );\n\n if (existingEntry) {\n const differentMetadata = compareNftMetadata(\n nftMetadata,\n existingEntry,\n );\n if (differentMetadata || !existingEntry.isCurrentlyOwned) {\n // TODO: Switch to indexToUpdate\n const indexToRemove = nfts.findIndex(\n (nft) =>\n nft.address.toLowerCase() === tokenAddress.toLowerCase() &&\n nft.tokenId === tokenId,\n );\n /* istanbul ignore next */\n if (indexToRemove !== -1) {\n nfts.splice(indexToRemove, 1);\n }\n } else {\n return nfts;\n }\n }\n\n const newEntry: Nft = {\n address: tokenAddress,\n tokenId,\n favorite: existingEntry?.favorite || false,\n isCurrentlyOwned: true,\n ...nftMetadata,\n };\n\n const newNfts = [...nfts, newEntry];\n this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY, {\n chainId,\n userAddress,\n });\n\n if (this.onNftAdded) {\n this.onNftAdded({\n address: tokenAddress,\n symbol: nftContract.symbol,\n tokenId: tokenId.toString(),\n standard: nftMetadata.standard,\n source,\n });\n }\n\n return newNfts;\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Adds an NFT contract to the stored NFT contracts list.\n *\n * @param options - options.\n * @param options.tokenAddress - Hex address of the NFT contract.\n * @param options.chainId - The chainId of the network where the NFT is being added.\n * @param options.userAddress - The address of the account where the NFT is being added.\n * @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @param options.source - Whether the NFT was detected, added manually or suggested by a dapp.\n * @returns Promise resolving to the current NFT contracts list.\n */\n private async addNftContract({\n tokenAddress,\n chainId,\n userAddress,\n networkClientId,\n source,\n }: {\n tokenAddress: string;\n chainId?: Hex;\n userAddress?: string;\n networkClientId?: NetworkClientId;\n source?: Source;\n }): Promise<NftContract[]> {\n const releaseLock = await this.mutex.acquire();\n try {\n tokenAddress = toChecksumHexAddress(tokenAddress);\n const { allNftContracts } = this.state;\n const currentChainId = this.getCorrectChainId({\n chainId,\n networkClientId,\n });\n const selectedAddress = userAddress ?? this.config.selectedAddress;\n\n const nftContracts =\n allNftContracts[selectedAddress]?.[currentChainId] || [];\n\n const existingEntry = nftContracts.find(\n (nftContract) =>\n nftContract.address.toLowerCase() === tokenAddress.toLowerCase(),\n );\n if (existingEntry) {\n return nftContracts;\n }\n\n // this doesn't work currently for detection if the user switches networks while the detection is processing\n // will be fixed once detection uses networkClientIds\n const contractInformation = await this.getNftContractInformation(\n tokenAddress,\n networkClientId,\n );\n const {\n asset_contract_type,\n created_date,\n schema_name,\n symbol,\n total_supply,\n description,\n external_link,\n collection: { name, image_url },\n } = contractInformation;\n\n // If the nft is auto-detected we want some valid metadata to be present\n if (\n source === Source.Detected &&\n Object.entries(contractInformation).every(([k, v]: [string, any]) => {\n if (k === 'address') {\n return true; // address will always be present\n }\n // collection will always be an object, we need to check the internal values\n if (k === 'collection') {\n return v?.name === null && v?.image_url === null;\n }\n return !v;\n })\n ) {\n return nftContracts;\n }\n\n /* istanbul ignore next */\n const newEntry: NftContract = Object.assign(\n {},\n { address: tokenAddress },\n description && { description },\n name && { name },\n image_url && { logo: image_url },\n symbol && { symbol },\n total_supply !== null &&\n typeof total_supply !== 'undefined' && { totalSupply: total_supply },\n asset_contract_type && { assetContractType: asset_contract_type },\n created_date && { createdDate: created_date },\n schema_name && { schemaName: schema_name },\n external_link && { externalLink: external_link },\n );\n const newNftContracts = [...nftContracts, newEntry];\n this.updateNestedNftState(newNftContracts, ALL_NFTS_CONTRACTS_STATE_KEY, {\n chainId: currentChainId,\n userAddress: selectedAddress,\n });\n\n return newNftContracts;\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Removes an individual NFT from the stored token list and saves it in ignored NFTs list.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Token identifier of the NFT.\n */\n private removeAndIgnoreIndividualNft(address: string, tokenId: string) {\n address = toChecksumHexAddress(address);\n const { allNfts, ignoredNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const newIgnoredNfts = [...ignoredNfts];\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const newNfts = nfts.filter((nft) => {\n if (\n nft.address.toLowerCase() === address.toLowerCase() &&\n nft.tokenId === tokenId\n ) {\n const alreadyIgnored = newIgnoredNfts.find(\n (c) => c.address === address && c.tokenId === tokenId,\n );\n !alreadyIgnored && newIgnoredNfts.push(nft);\n return false;\n }\n return true;\n });\n\n this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY);\n\n this.update({\n ignoredNfts: newIgnoredNfts,\n });\n }\n\n /**\n * Removes an individual NFT from the stored token list.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Token identifier of the NFT.\n */\n private removeIndividualNft(address: string, tokenId: string) {\n address = toChecksumHexAddress(address);\n const { allNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const newNfts = nfts.filter(\n (nft) =>\n !(\n nft.address.toLowerCase() === address.toLowerCase() &&\n nft.tokenId === tokenId\n ),\n );\n this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY);\n }\n\n /**\n * Removes an NFT contract to the stored NFT contracts list.\n *\n * @param address - Hex address of the NFT contract.\n * @returns Promise resolving to the current NFT contracts list.\n */\n private removeNftContract(address: string): NftContract[] {\n address = toChecksumHexAddress(address);\n const { allNftContracts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nftContracts = allNftContracts[selectedAddress]?.[chainId] || [];\n\n const newNftContracts = nftContracts.filter(\n (nftContract) =>\n !(nftContract.address.toLowerCase() === address.toLowerCase()),\n );\n this.updateNestedNftState(newNftContracts, ALL_NFTS_CONTRACTS_STATE_KEY);\n\n return newNftContracts;\n }\n\n /**\n * EventEmitter instance used to listen to specific EIP747 events\n */\n hub = new EventEmitter();\n\n /**\n * Optional API key to use with opensea\n */\n openSeaApiKey?: string;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'NftController';\n\n private readonly getERC721AssetName: AssetsContractController['getERC721AssetName'];\n\n private readonly getERC721AssetSymbol: AssetsContractController['getERC721AssetSymbol'];\n\n private readonly getERC721TokenURI: AssetsContractController['getERC721TokenURI'];\n\n private readonly getERC721OwnerOf: AssetsContractController['getERC721OwnerOf'];\n\n private readonly getERC1155BalanceOf: AssetsContractController['getERC1155BalanceOf'];\n\n private readonly getERC1155TokenURI: AssetsContractController['getERC1155TokenURI'];\n\n private readonly getNetworkClientById: NetworkController['getNetworkClientById'];\n\n private readonly onNftAdded?: (data: {\n address: string;\n symbol: string | undefined;\n tokenId: string;\n standard: string | null;\n source: Source;\n }) => void;\n\n /**\n * Creates an NftController instance.\n *\n * @param options - The controller options.\n * @param options.chainId - The chain ID of the current network.\n * @param options.onPreferencesStateChange - Allows subscribing to preference controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.\n * @param options.getERC721AssetName - Gets the name of the asset at the given address.\n * @param options.getERC721AssetSymbol - Gets the symbol of the asset at the given address.\n * @param options.getERC721TokenURI - Gets the URI of the ERC721 token at the given address, with the given ID.\n * @param options.getERC721OwnerOf - Get the owner of a ERC-721 NFT.\n * @param options.getERC1155BalanceOf - Gets balance of a ERC-1155 NFT.\n * @param options.getERC1155TokenURI - Gets the URI of the ERC1155 token at the given address, with the given ID.\n * @param options.getNetworkClientById - Gets the network client for the given networkClientId.\n * @param options.onNftAdded - Callback that is called when an NFT is added. Currently used pass data\n * for tracking the NFT added event.\n * @param options.messenger - The controller messenger.\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n {\n chainId: initialChainId,\n onPreferencesStateChange,\n onNetworkStateChange,\n getERC721AssetName,\n getERC721AssetSymbol,\n getERC721TokenURI,\n getERC721OwnerOf,\n getERC1155BalanceOf,\n getERC1155TokenURI,\n getNetworkClientById,\n onNftAdded,\n messenger,\n }: {\n chainId: Hex;\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n getERC721AssetName: AssetsContractController['getERC721AssetName'];\n getERC721AssetSymbol: AssetsContractController['getERC721AssetSymbol'];\n getERC721TokenURI: AssetsContractController['getERC721TokenURI'];\n getERC721OwnerOf: AssetsContractController['getERC721OwnerOf'];\n getERC1155BalanceOf: AssetsContractController['getERC1155BalanceOf'];\n getERC1155TokenURI: AssetsContractController['getERC1155TokenURI'];\n getNetworkClientById: NetworkController['getNetworkClientById'];\n onNftAdded?: (data: {\n address: string;\n symbol: string | undefined;\n tokenId: string;\n standard: string | null;\n source: string;\n }) => void;\n messenger: NftControllerMessenger;\n },\n config?: Partial<BaseConfig>,\n state?: Partial<NftState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n selectedAddress: '',\n chainId: initialChainId,\n ipfsGateway: IPFS_DEFAULT_GATEWAY_URL,\n openSeaEnabled: false,\n useIPFSSubdomains: true,\n isIpfsGatewayEnabled: true,\n };\n\n this.defaultState = {\n allNftContracts: {},\n allNfts: {},\n ignoredNfts: [],\n };\n this.initialize();\n this.getERC721AssetName = getERC721AssetName;\n this.getERC721AssetSymbol = getERC721AssetSymbol;\n this.getERC721TokenURI = getERC721TokenURI;\n this.getERC721OwnerOf = getERC721OwnerOf;\n this.getERC1155BalanceOf = getERC1155BalanceOf;\n this.getERC1155TokenURI = getERC1155TokenURI;\n this.getNetworkClientById = getNetworkClientById;\n this.onNftAdded = onNftAdded;\n this.messagingSystem = messenger;\n\n onPreferencesStateChange(\n ({\n selectedAddress,\n ipfsGateway,\n openSeaEnabled,\n isIpfsGatewayEnabled,\n }) => {\n this.configure({\n selectedAddress,\n ipfsGateway,\n openSeaEnabled,\n isIpfsGatewayEnabled,\n });\n },\n );\n\n onNetworkStateChange(({ providerConfig }) => {\n const { chainId } = providerConfig;\n this.configure({ chainId });\n });\n }\n\n async validateWatchNft(\n asset: NftAsset,\n type: NFTStandardType,\n userAddress: string,\n ) {\n const { address: contractAddress, tokenId } = asset;\n\n // Validate parameters\n if (!type) {\n throw rpcErrors.invalidParams('Asset type is required');\n }\n\n if (type !== ERC721 && type !== ERC1155) {\n throw rpcErrors.invalidParams(\n `Non NFT asset type ${type} not supported by watchNft`,\n );\n }\n\n if (!contractAddress || !tokenId) {\n throw rpcErrors.invalidParams('Both address and tokenId are required');\n }\n\n if (!isAddress(contractAddress)) {\n throw rpcErrors.invalidParams('Invalid address');\n }\n\n if (!/^\\d+$/u.test(tokenId)) {\n throw rpcErrors.invalidParams('Invalid tokenId');\n }\n\n // Check if the user owns the suggested NFT\n try {\n const isOwner = await this.isNftOwner(\n userAddress,\n contractAddress,\n tokenId,\n );\n if (!isOwner) {\n throw rpcErrors.invalidInput(\n 'Suggested NFT is not owned by the selected account',\n );\n }\n } catch (error: any) {\n // error thrown here: \"Unable to verify ownership. Possibly because the standard is not supported or the user's currently selected network does not match the chain of the asset in question.\"\n throw rpcErrors.resourceUnavailable(error.message);\n }\n }\n\n // temporary method to get the correct chainId until we remove chainId from the config & the chainId arg from the detection logic\n // Just a helper method to prefer the networkClient chainId first then the chainId argument and then finally the config chainId\n private getCorrectChainId({\n chainId,\n networkClientId,\n }: {\n chainId?: Hex;\n networkClientId?: NetworkClientId;\n }) {\n if (networkClientId) {\n return this.getNetworkClientById(networkClientId).configuration.chainId;\n } else if (chainId) {\n return chainId;\n }\n return this.config.chainId;\n }\n\n /**\n * Adds a new suggestedAsset to state. Parameters will be validated according to\n * asset type being watched. A `<suggestedNftMeta.id>:pending` hub event will be emitted once added.\n *\n * @param asset - The asset to be watched. For now ERC721 and ERC1155 tokens are accepted.\n * @param asset.address - The address of the asset contract.\n * @param asset.tokenId - The ID of the asset.\n * @param type - The asset type.\n * @param origin - Domain origin to register the asset from.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Object containing a Promise resolving to the suggestedAsset address if accepted.\n */\n async watchNft(\n asset: NftAsset,\n type: NFTStandardType,\n origin: string,\n networkClientId?: NetworkClientId,\n ) {\n const { selectedAddress, chainId } = this.config;\n\n await this.validateWatchNft(asset, type, selectedAddress);\n\n const nftMetadata = await this.getNftInformation(\n asset.address,\n asset.tokenId,\n networkClientId,\n );\n\n if (nftMetadata.standard && nftMetadata.standard !== type) {\n throw rpcErrors.invalidInput(\n `Suggested NFT of type ${nftMetadata.standard} does not match received type ${type}`,\n );\n }\n\n const suggestedNftMeta: SuggestedNftMeta = {\n asset: { ...asset, ...nftMetadata },\n type,\n id: random(),\n time: Date.now(),\n interactingAddress: selectedAddress,\n origin,\n };\n await this._requestApproval(suggestedNftMeta);\n const { address, tokenId } = asset;\n const { name, standard, description, image } = nftMetadata;\n\n await this.addNft(address, tokenId, {\n nftMetadata: {\n name: name ?? null,\n description: description ?? null,\n image: image ?? null,\n standard: standard ?? null,\n },\n chainId,\n userAddress: selectedAddress,\n source: Source.Dapp,\n networkClientId,\n });\n }\n\n /**\n * Sets an OpenSea API key to retrieve NFT information.\n *\n * @param openSeaApiKey - OpenSea API key.\n */\n setApiKey(openSeaApiKey: string) {\n this.openSeaApiKey = openSeaApiKey;\n }\n\n /**\n * Checks the ownership of a ERC-721 or ERC-1155 NFT for a given address.\n *\n * @param ownerAddress - User public address.\n * @param nftAddress - NFT contract address.\n * @param tokenId - NFT token ID.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving the NFT ownership.\n */\n async isNftOwner(\n ownerAddress: string,\n nftAddress: string,\n tokenId: string,\n networkClientId?: NetworkClientId,\n ): Promise<boolean> {\n // Checks the ownership for ERC-721.\n try {\n const owner = await this.getERC721OwnerOf(\n nftAddress,\n tokenId,\n networkClientId,\n );\n return ownerAddress.toLowerCase() === owner.toLowerCase();\n // eslint-disable-next-line no-empty\n } catch {\n // Ignore ERC-721 contract error\n }\n\n // Checks the ownership for ERC-1155.\n try {\n const balance = await this.getERC1155BalanceOf(\n ownerAddress,\n nftAddress,\n tokenId,\n networkClientId,\n );\n return !balance.isZero();\n // eslint-disable-next-line no-empty\n } catch {\n // Ignore ERC-1155 contract error\n }\n\n throw new Error(\n `Unable to verify ownership. Possibly because the standard is not supported or the user's currently selected network does not match the chain of the asset in question.`,\n );\n }\n\n /**\n * Verifies currently selected address owns entered NFT address/tokenId combo and\n * adds the NFT and respective NFT contract to the stored NFT and NFT contracts lists.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @param source - Whether the NFT was detected, added manually or suggested by a dapp.\n */\n async addNftVerifyOwnership(\n address: string,\n tokenId: string,\n networkClientId?: NetworkClientId,\n source?: Source,\n ) {\n const { selectedAddress } = this.config;\n if (\n !(await this.isNftOwner(\n selectedAddress,\n address,\n tokenId,\n networkClientId,\n ))\n ) {\n throw new Error('This NFT is not owned by the user');\n }\n await this.addNft(address, tokenId, { networkClientId, source });\n }\n\n /**\n * Adds an NFT and respective NFT contract to the stored NFT and NFT contracts lists.\n *\n * @param tokenAddress - Hex address of the NFT contract.\n * @param tokenId - The NFT identifier.\n * @param options - an object of arguments\n * @param options.nftMetadata - NFT optional metadata.\n * @param options.chainId - The chain ID of the current network.\n * @param options.userAddress - The address of the current user.\n * @param options.source - Whether the NFT was detected, added manually or suggested by a dapp.\n * @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.\n * @returns Promise resolving to the current NFT list.\n */\n async addNft(\n tokenAddress: string,\n tokenId: string,\n {\n nftMetadata,\n chainId, // TODO remove and replace chainId arg with fetch chainId using getNetworkClientById(networkClientId).configuration.chainId once polling refactor is complete\n userAddress,\n source = Source.Custom,\n networkClientId,\n }: {\n nftMetadata?: NftMetadata;\n chainId?: Hex;\n userAddress?: string;\n source?: Source;\n networkClientId?: NetworkClientId;\n } = {},\n ) {\n tokenAddress = toChecksumHexAddress(tokenAddress);\n\n const currentChainId = this.getCorrectChainId({ chainId, networkClientId });\n const selectedAddress = userAddress ?? this.config.selectedAddress;\n\n const newNftContracts = await this.addNftContract({\n tokenAddress,\n chainId: currentChainId,\n userAddress: selectedAddress,\n networkClientId,\n source,\n });\n\n nftMetadata =\n nftMetadata ||\n (await this.getNftInformation(tokenAddress, tokenId, networkClientId));\n\n // If NFT contract was not added, do not add individual NFT\n const nftContract = newNftContracts.find(\n (contract) =>\n contract.address.toLowerCase() === tokenAddress.toLowerCase(),\n );\n\n // If NFT contract information, add individual NFT\n if (nftContract) {\n await this.addIndividualNft(\n tokenAddress,\n tokenId,\n nftMetadata,\n nftContract,\n currentChainId,\n selectedAddress,\n source,\n );\n }\n }\n\n /**\n * Removes an NFT from the stored token list.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Token identifier of the NFT.\n */\n removeNft(address: string, tokenId: string) {\n address = toChecksumHexAddress(address);\n this.removeIndividualNft(address, tokenId);\n const { allNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const remainingNft = nfts.find(\n (nft) => nft.address.toLowerCase() === address.toLowerCase(),\n );\n if (!remainingNft) {\n this.removeNftContract(address);\n }\n }\n\n /**\n * Removes an NFT from the stored token list and saves it in ignored NFTs list.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Token identifier of the NFT.\n */\n removeAndIgnoreNft(address: string, tokenId: string) {\n address = toChecksumHexAddress(address);\n this.removeAndIgnoreIndividualNft(address, tokenId);\n const { allNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const remainingNft = nfts.find(\n (nft) => nft.address.toLowerCase() === address.toLowerCase(),\n );\n if (!remainingNft) {\n this.removeNftContract(address);\n }\n }\n\n /**\n * Removes all NFTs from the ignored list.\n */\n clearIgnoredNfts() {\n this.update({ ignoredNfts: [] });\n }\n\n /**\n * Checks whether input NFT is still owned by the user\n * And updates the isCurrentlyOwned value on the NFT object accordingly.\n *\n * @param nft - The NFT object to check and update.\n * @param batch - A boolean indicating whether this method is being called as part of a batch or single update.\n * @param accountParams - The userAddress and chainId to check ownership against\n * @param accountParams.userAddress - the address passed through the confirmed transaction flow to ensure assets are stored to the correct account\n * @param accountParams.chainId - the chainId passed through the confirmed transaction flow to ensure assets are stored to the correct account\n * @returns the NFT with the updated isCurrentlyOwned value\n */\n async checkAndUpdateSingleNftOwnershipStatus(\n nft: Nft,\n batch: boolean,\n { userAddress, chainId } = {\n userAddress: this.config.selectedAddress,\n chainId: this.config.chainId,\n },\n ) {\n const { address, tokenId } = nft;\n let isOwned = nft.isCurrentlyOwned;\n try {\n isOwned = await this.isNftOwner(userAddress, address, tokenId);\n } catch (error) {\n if (\n !(\n error instanceof Error &&\n error.message.includes('Unable to verify ownership')\n )\n ) {\n throw error;\n }\n }\n\n nft.isCurrentlyOwned = isOwned;\n\n if (batch) {\n return nft;\n }\n\n // if this is not part of a batched update we update this one NFT in state\n const { allNfts } = this.state;\n const nfts = allNfts[userAddress]?.[chainId] || [];\n const nftToUpdate = nfts.find(\n (item) =>\n item.tokenId === tokenId &&\n item.address.toLowerCase() === address.toLowerCase(),\n );\n if (nftToUpdate) {\n nftToUpdate.isCurrentlyOwned = isOwned;\n this.updateNestedNftState(nfts, ALL_NFTS_STATE_KEY, {\n userAddress,\n chainId,\n });\n }\n return nft;\n }\n\n /**\n * Checks whether NFTs associated with current selectedAddress/chainId combination are still owned by the user\n * And updates the isCurrentlyOwned value on each accordingly.\n */\n async checkAndUpdateAllNftsOwnershipStatus() {\n const { allNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const updatedNfts = await Promise.all(\n nfts.map(async (nft) => {\n return (\n (await this.checkAndUpdateSingleNftOwnershipStatus(nft, true)) ?? nft\n );\n }),\n );\n\n this.updateNestedNftState(updatedNfts, ALL_NFTS_STATE_KEY);\n }\n\n /**\n * Update NFT favorite status.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Hex address of the NFT contract.\n * @param favorite - NFT new favorite status.\n */\n updateNftFavoriteStatus(address: string, tokenId: string, favorite: boolean) {\n const { allNfts } = this.state;\n const { chainId, selectedAddress } = this.config;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const index: number = nfts.findIndex(\n (nft) => nft.address === address && nft.tokenId === tokenId,\n );\n\n if (index === -1) {\n return;\n }\n\n const updatedNft: Nft = {\n ...nfts[index],\n favorite,\n };\n\n // Update Nfts array\n nfts[index] = updatedNft;\n\n this.updateNestedNftState(nfts, ALL_NFTS_STATE_KEY);\n }\n\n /**\n * Returns an NFT by the address and token id.\n *\n * @param address - Hex address of the NFT contract.\n * @param tokenId - Number that represents the id of the token.\n * @param selectedAddress - Hex address of the user account.\n * @param chainId - Id of the current network.\n * @returns Object containing the NFT and its position in the array\n */\n findNftByAddressAndTokenId(\n address: string,\n tokenId: string,\n selectedAddress: string,\n chainId: Hex,\n ): { nft: Nft; index: number } | null {\n const { allNfts } = this.state;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const index: number = nfts.findIndex(\n (nft) =>\n nft.address.toLowerCase() === address.toLowerCase() &&\n nft.tokenId === tokenId,\n );\n\n if (index === -1) {\n return null;\n }\n\n return { nft: nfts[index], index };\n }\n\n /**\n * Update NFT data.\n *\n * @param nft - NFT object to find the right NFT to updates.\n * @param updates - NFT partial object to update properties of the NFT.\n * @param selectedAddress - Hex address of the user account.\n * @param chainId - Id of the current network.\n */\n updateNft(\n nft: Nft,\n updates: Partial<Nft>,\n selectedAddress: string,\n chainId: Hex,\n ) {\n const { allNfts } = this.state;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const nftInfo = this.findNftByAddressAndTokenId(\n nft.address,\n nft.tokenId,\n selectedAddress,\n chainId,\n );\n\n if (!nftInfo) {\n return;\n }\n\n const updatedNft: Nft = {\n ...nft,\n ...updates,\n };\n\n const newNfts = [\n ...nfts.slice(0, nftInfo.index),\n updatedNft,\n ...nfts.slice(nftInfo.index + 1),\n ];\n\n this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY);\n }\n\n /**\n * Resets the transaction status of an NFT.\n *\n * @param transactionId - NFT transaction id.\n * @param selectedAddress - Hex address of the user account.\n * @param chainId - Id of the current network.\n * @returns a boolean indicating if the reset was well succeeded or not\n */\n resetNftTransactionStatusByTransactionId(\n transactionId: string,\n selectedAddress: string,\n chainId: Hex,\n ): boolean {\n const { allNfts } = this.state;\n const nfts = allNfts[selectedAddress]?.[chainId] || [];\n const index: number = nfts.findIndex(\n (nft) => nft.transactionId === transactionId,\n );\n\n if (index === -1) {\n return false;\n }\n const updatedNft: Nft = {\n ...nfts[index],\n transactionId: undefined,\n };\n\n const newNfts = [\n ...nfts.slice(0, index),\n updatedNft,\n ...nfts.slice(index + 1),\n ];\n\n this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY);\n return true;\n }\n\n async _requestApproval(suggestedNftMeta: SuggestedNftMeta) {\n return this.messagingSystem.call(\n 'ApprovalController:addRequest',\n {\n id: suggestedNftMeta.id,\n origin: suggestedNftMeta.origin,\n type: ApprovalType.WatchAsset,\n requestData: {\n id: suggestedNftMeta.id,\n interactingAddress: suggestedNftMeta.interactingAddress,\n asset: {\n address: suggestedNftMeta.asset.address,\n tokenId: suggestedNftMeta.asset.tokenId,\n name: suggestedNftMeta.asset.name,\n description: suggestedNftMeta.asset.description,\n image: suggestedNftMeta.asset.image,\n standard: suggestedNftMeta.asset.standard,\n },\n },\n },\n true,\n );\n }\n}\n\nexport default NftController;\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BaseConfig, BaseState } from '@metamask/base-controller';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import type { NetworkClientId, NetworkController, NetworkState, NetworkClient } from '@metamask/network-controller';
|
|
3
|
+
import { PollingControllerV1 } from '@metamask/polling-controller';
|
|
4
4
|
import type { PreferencesState } from '@metamask/preferences-controller';
|
|
5
5
|
import type { Hex } from '@metamask/utils';
|
|
6
6
|
import type { NftController, NftState } from './NftController';
|
|
@@ -116,7 +116,7 @@ export interface NftDetectionConfig extends BaseConfig {
|
|
|
116
116
|
/**
|
|
117
117
|
* Controller that passively polls on a set interval for NFT auto detection
|
|
118
118
|
*/
|
|
119
|
-
export declare class NftDetectionController extends
|
|
119
|
+
export declare class NftDetectionController extends PollingControllerV1<NftDetectionConfig, BaseState> {
|
|
120
120
|
private intervalId?;
|
|
121
121
|
private getOwnerNftApi;
|
|
122
122
|
private getOwnerNfts;
|
|
@@ -127,6 +127,7 @@ export declare class NftDetectionController extends BaseController<NftDetectionC
|
|
|
127
127
|
private readonly getOpenSeaApiKey;
|
|
128
128
|
private readonly addNft;
|
|
129
129
|
private readonly getNftState;
|
|
130
|
+
private readonly getNetworkClientById;
|
|
130
131
|
/**
|
|
131
132
|
* Creates an NftDetectionController instance.
|
|
132
133
|
*
|
|
@@ -138,11 +139,13 @@ export declare class NftDetectionController extends BaseController<NftDetectionC
|
|
|
138
139
|
* @param options.getOpenSeaApiKey - Gets the OpenSea API key, if one is set.
|
|
139
140
|
* @param options.addNft - Add an NFT.
|
|
140
141
|
* @param options.getNftState - Gets the current state of the Assets controller.
|
|
142
|
+
* @param options.getNetworkClientById - Gets the network client by ID, from the NetworkController.
|
|
141
143
|
* @param config - Initial options used to configure this controller.
|
|
142
144
|
* @param state - Initial state to set on this controller.
|
|
143
145
|
*/
|
|
144
|
-
constructor({ chainId: initialChainId, onPreferencesStateChange, onNetworkStateChange, getOpenSeaApiKey, addNft, getNftState, }: {
|
|
146
|
+
constructor({ chainId: initialChainId, getNetworkClientById, onPreferencesStateChange, onNetworkStateChange, getOpenSeaApiKey, addNft, getNftState, }: {
|
|
145
147
|
chainId: Hex;
|
|
148
|
+
getNetworkClientById: NetworkController['getNetworkClientById'];
|
|
146
149
|
onNftsStateChange: (listener: (nftsState: NftState) => void) => void;
|
|
147
150
|
onPreferencesStateChange: (listener: (preferencesState: PreferencesState) => void) => void;
|
|
148
151
|
onNetworkStateChange: (listener: (networkState: NetworkState) => void) => void;
|
|
@@ -150,6 +153,9 @@ export declare class NftDetectionController extends BaseController<NftDetectionC
|
|
|
150
153
|
addNft: NftController['addNft'];
|
|
151
154
|
getNftState: () => NftState;
|
|
152
155
|
}, config?: Partial<NftDetectionConfig>, state?: Partial<BaseState>);
|
|
156
|
+
_executePoll(networkClientId: string, options: {
|
|
157
|
+
address: string;
|
|
158
|
+
}): Promise<void>;
|
|
153
159
|
/**
|
|
154
160
|
* Start polling for the currency rate.
|
|
155
161
|
*/
|
|
@@ -171,11 +177,16 @@ export declare class NftDetectionController extends BaseController<NftDetectionC
|
|
|
171
177
|
* @returns Whether current network is mainnet.
|
|
172
178
|
*/
|
|
173
179
|
isMainnet: () => boolean;
|
|
180
|
+
isMainnetByNetworkClientId: (networkClient: NetworkClient) => boolean;
|
|
181
|
+
private getCorrectChainId;
|
|
174
182
|
/**
|
|
175
183
|
* Triggers asset ERC721 token auto detection on mainnet. Any newly detected NFTs are
|
|
176
184
|
* added.
|
|
185
|
+
*
|
|
186
|
+
* @param networkClientId - The network client ID to detect NFTs on.
|
|
187
|
+
* @param accountAddress - The address to detect NFTs for.
|
|
177
188
|
*/
|
|
178
|
-
detectNfts(): Promise<void>;
|
|
189
|
+
detectNfts(networkClientId?: NetworkClientId, accountAddress?: string): Promise<void>;
|
|
179
190
|
}
|
|
180
191
|
export default NftDetectionController;
|
|
181
192
|
//# sourceMappingURL=NftDetectionController.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NftDetectionController.d.ts","sourceRoot":"","sources":["../src/NftDetectionController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"NftDetectionController.d.ts","sourceRoot":"","sources":["../src/NftDetectionController.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,14 +10,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.NftDetectionController = void 0;
|
|
13
|
-
const base_controller_1 = require("@metamask/base-controller");
|
|
14
13
|
const controller_utils_1 = require("@metamask/controller-utils");
|
|
14
|
+
const polling_controller_1 = require("@metamask/polling-controller");
|
|
15
15
|
const constants_1 = require("./constants");
|
|
16
16
|
const DEFAULT_INTERVAL = 180000;
|
|
17
17
|
/**
|
|
18
18
|
* Controller that passively polls on a set interval for NFT auto detection
|
|
19
19
|
*/
|
|
20
|
-
class NftDetectionController extends
|
|
20
|
+
class NftDetectionController extends polling_controller_1.PollingControllerV1 {
|
|
21
21
|
/**
|
|
22
22
|
* Creates an NftDetectionController instance.
|
|
23
23
|
*
|
|
@@ -29,10 +29,11 @@ class NftDetectionController extends base_controller_1.BaseController {
|
|
|
29
29
|
* @param options.getOpenSeaApiKey - Gets the OpenSea API key, if one is set.
|
|
30
30
|
* @param options.addNft - Add an NFT.
|
|
31
31
|
* @param options.getNftState - Gets the current state of the Assets controller.
|
|
32
|
+
* @param options.getNetworkClientById - Gets the network client by ID, from the NetworkController.
|
|
32
33
|
* @param config - Initial options used to configure this controller.
|
|
33
34
|
* @param state - Initial state to set on this controller.
|
|
34
35
|
*/
|
|
35
|
-
constructor({ chainId: initialChainId, onPreferencesStateChange, onNetworkStateChange, getOpenSeaApiKey, addNft, getNftState, }, config, state) {
|
|
36
|
+
constructor({ chainId: initialChainId, getNetworkClientById, onPreferencesStateChange, onNetworkStateChange, getOpenSeaApiKey, addNft, getNftState, }, config, state) {
|
|
36
37
|
super(config, state);
|
|
37
38
|
/**
|
|
38
39
|
* Name of this controller used during composition
|
|
@@ -44,6 +45,9 @@ class NftDetectionController extends base_controller_1.BaseController {
|
|
|
44
45
|
* @returns Whether current network is mainnet.
|
|
45
46
|
*/
|
|
46
47
|
this.isMainnet = () => this.config.chainId === controller_utils_1.ChainId.mainnet;
|
|
48
|
+
this.isMainnetByNetworkClientId = (networkClient) => {
|
|
49
|
+
return networkClient.configuration.chainId === controller_utils_1.ChainId.mainnet;
|
|
50
|
+
};
|
|
47
51
|
this.defaultConfig = {
|
|
48
52
|
interval: DEFAULT_INTERVAL,
|
|
49
53
|
chainId: initialChainId,
|
|
@@ -52,6 +56,7 @@ class NftDetectionController extends base_controller_1.BaseController {
|
|
|
52
56
|
};
|
|
53
57
|
this.initialize();
|
|
54
58
|
this.getNftState = getNftState;
|
|
59
|
+
this.getNetworkClientById = getNetworkClientById;
|
|
55
60
|
onPreferencesStateChange(({ selectedAddress, useNftDetection }) => {
|
|
56
61
|
const { selectedAddress: previouslySelectedAddress, disabled } = this.config;
|
|
57
62
|
if (selectedAddress !== previouslySelectedAddress ||
|
|
@@ -74,6 +79,7 @@ class NftDetectionController extends base_controller_1.BaseController {
|
|
|
74
79
|
});
|
|
75
80
|
this.getOpenSeaApiKey = getOpenSeaApiKey;
|
|
76
81
|
this.addNft = addNft;
|
|
82
|
+
this.setIntervalLength(this.config.interval);
|
|
77
83
|
}
|
|
78
84
|
getOwnerNftApi({ address, offset, }) {
|
|
79
85
|
return `${controller_utils_1.OPENSEA_PROXY_URL}/assets?owner=${address}&offset=${offset}&limit=50`;
|
|
@@ -102,6 +108,11 @@ class NftDetectionController extends base_controller_1.BaseController {
|
|
|
102
108
|
return nfts;
|
|
103
109
|
});
|
|
104
110
|
}
|
|
111
|
+
_executePoll(networkClientId, options) {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
yield this.detectNfts(networkClientId, options.address);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
105
116
|
/**
|
|
106
117
|
* Start polling for the currency rate.
|
|
107
118
|
*/
|
|
@@ -139,17 +150,27 @@ class NftDetectionController extends base_controller_1.BaseController {
|
|
|
139
150
|
}), this.config.interval);
|
|
140
151
|
});
|
|
141
152
|
}
|
|
153
|
+
getCorrectChainId(networkClientId) {
|
|
154
|
+
if (networkClientId) {
|
|
155
|
+
return this.getNetworkClientById(networkClientId).configuration.chainId;
|
|
156
|
+
}
|
|
157
|
+
return this.config.chainId;
|
|
158
|
+
}
|
|
142
159
|
/**
|
|
143
160
|
* Triggers asset ERC721 token auto detection on mainnet. Any newly detected NFTs are
|
|
144
161
|
* added.
|
|
162
|
+
*
|
|
163
|
+
* @param networkClientId - The network client ID to detect NFTs on.
|
|
164
|
+
* @param accountAddress - The address to detect NFTs for.
|
|
145
165
|
*/
|
|
146
|
-
detectNfts() {
|
|
166
|
+
detectNfts(networkClientId, accountAddress) {
|
|
147
167
|
return __awaiter(this, void 0, void 0, function* () {
|
|
168
|
+
const chainId = this.getCorrectChainId(networkClientId);
|
|
169
|
+
const selectedAddress = accountAddress || this.config.selectedAddress;
|
|
148
170
|
/* istanbul ignore if */
|
|
149
171
|
if (!this.isMainnet() || this.disabled) {
|
|
150
172
|
return;
|
|
151
173
|
}
|
|
152
|
-
const { selectedAddress, chainId } = this.config;
|
|
153
174
|
/* istanbul ignore else */
|
|
154
175
|
if (!selectedAddress) {
|
|
155
176
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NftDetectionController.js","sourceRoot":"","sources":["../src/NftDetectionController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n OPENSEA_PROXY_URL,\n fetchWithErrorHandling,\n toChecksumHexAddress,\n ChainId,\n} from '@metamask/controller-utils';\nimport type { NetworkState } from '@metamask/network-controller';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport { Source } from './constants';\nimport type { NftController, NftState, NftMetadata } from './NftController';\n\nconst DEFAULT_INTERVAL = 180000;\n\n/**\n * @type ApiNft\n *\n * NFT object coming from OpenSea api\n * @property token_id - The NFT identifier\n * @property num_sales - Number of sales\n * @property background_color - The background color to be displayed with the item\n * @property image_url - URI of an image associated with this NFT\n * @property image_preview_url - URI of a smaller image associated with this NFT\n * @property image_thumbnail_url - URI of a thumbnail image associated with this NFT\n * @property image_original_url - URI of the original image associated with this NFT\n * @property animation_url - URI of a animation associated with this NFT\n * @property animation_original_url - URI of the original animation associated with this NFT\n * @property name - The NFT name\n * @property description - The NFT description\n * @property external_link - External link containing additional information\n * @property assetContract - The NFT contract information object\n * @property creator - The NFT owner information object\n * @property lastSale - When this item was last sold\n */\nexport interface ApiNft {\n token_id: string;\n num_sales: number | null;\n background_color: string | null;\n image_url: string | null;\n image_preview_url: string | null;\n image_thumbnail_url: string | null;\n image_original_url: string | null;\n animation_url: string | null;\n animation_original_url: string | null;\n name: string | null;\n description: string | null;\n external_link: string | null;\n asset_contract: ApiNftContract;\n creator: ApiNftCreator;\n last_sale: ApiNftLastSale | null;\n}\n\n/**\n * @type ApiNftContract\n *\n * NFT contract object coming from OpenSea api\n * @property address - Address of the NFT contract\n * @property asset_contract_type - The NFT type, it could be `semi-fungible` or `non-fungible`\n * @property created_date - Creation date\n * @property collection - Object containing the contract name and URI of an image associated\n * @property schema_name - The schema followed by the contract, it could be `ERC721` or `ERC1155`\n * @property symbol - The NFT contract symbol\n * @property total_supply - Total supply of NFTs\n * @property description - The NFT contract description\n * @property external_link - External link containing additional information\n */\nexport interface ApiNftContract {\n address: string;\n asset_contract_type: string | null;\n created_date: string | null;\n schema_name: string | null;\n symbol: string | null;\n total_supply: string | null;\n description: string | null;\n external_link: string | null;\n collection: {\n name: string | null;\n image_url?: string | null;\n };\n}\n\n/**\n * @type ApiNftLastSale\n *\n * NFT sale object coming from OpenSea api\n * @property event_timestamp - Object containing a `username`\n * @property total_price - URI of NFT image associated with this owner\n * @property transaction - Object containing transaction_hash and block_hash\n */\nexport interface ApiNftLastSale {\n event_timestamp: string;\n total_price: string;\n transaction: { transaction_hash: string; block_hash: string };\n}\n\n/**\n * @type ApiNftCreator\n *\n * NFT creator object coming from OpenSea api\n * @property user - Object containing a `username`\n * @property profile_img_url - URI of NFT image associated with this owner\n * @property address - The owner address\n */\nexport interface ApiNftCreator {\n user: { username: string };\n profile_img_url: string;\n address: string;\n}\n\n/**\n * @type NftDetectionConfig\n *\n * NftDetection configuration\n * @property interval - Polling interval used to fetch new token rates\n * @property chainId - Current chain ID\n * @property selectedAddress - Vault selected address\n */\nexport interface NftDetectionConfig extends BaseConfig {\n interval: number;\n chainId: Hex;\n selectedAddress: string;\n}\n\n/**\n * Controller that passively polls on a set interval for NFT auto detection\n */\nexport class NftDetectionController extends BaseController<\n NftDetectionConfig,\n BaseState\n> {\n private intervalId?: ReturnType<typeof setTimeout>;\n\n private getOwnerNftApi({\n address,\n offset,\n }: {\n address: string;\n offset: number;\n }) {\n return `${OPENSEA_PROXY_URL}/assets?owner=${address}&offset=${offset}&limit=50`;\n }\n\n private async getOwnerNfts(address: string) {\n let nftApiResponse: { assets: ApiNft[] };\n let nfts: ApiNft[] = [];\n let offset = 0;\n let pagingFinish = false;\n /* istanbul ignore if */\n do {\n nftApiResponse = await fetchWithErrorHandling({\n url: this.getOwnerNftApi({ address, offset }),\n timeout: 15000,\n });\n\n if (!nftApiResponse) {\n return nfts;\n }\n\n nftApiResponse?.assets?.length !== 0\n ? (nfts = [...nfts, ...nftApiResponse.assets])\n : (pagingFinish = true);\n offset += 50;\n } while (!pagingFinish);\n\n return nfts;\n }\n\n /**\n * Name of this controller used during composition\n */\n override name = 'NftDetectionController';\n\n private readonly getOpenSeaApiKey: () => string | undefined;\n\n private readonly addNft: NftController['addNft'];\n\n private readonly getNftState: () => NftState;\n\n /**\n * Creates an NftDetectionController instance.\n *\n * @param options - The controller options.\n * @param options.chainId - The chain ID of the current network.\n * @param options.onNftsStateChange - Allows subscribing to assets controller state changes.\n * @param options.onPreferencesStateChange - Allows subscribing to preferences controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.\n * @param options.getOpenSeaApiKey - Gets the OpenSea API key, if one is set.\n * @param options.addNft - Add an NFT.\n * @param options.getNftState - Gets the current state of the Assets controller.\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n {\n chainId: initialChainId,\n onPreferencesStateChange,\n onNetworkStateChange,\n getOpenSeaApiKey,\n addNft,\n getNftState,\n }: {\n chainId: Hex;\n onNftsStateChange: (listener: (nftsState: NftState) => void) => void;\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n getOpenSeaApiKey: () => string | undefined;\n addNft: NftController['addNft'];\n getNftState: () => NftState;\n },\n config?: Partial<NftDetectionConfig>,\n state?: Partial<BaseState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n interval: DEFAULT_INTERVAL,\n chainId: initialChainId,\n selectedAddress: '',\n disabled: true,\n };\n this.initialize();\n this.getNftState = getNftState;\n onPreferencesStateChange(({ selectedAddress, useNftDetection }) => {\n const { selectedAddress: previouslySelectedAddress, disabled } =\n this.config;\n\n if (\n selectedAddress !== previouslySelectedAddress ||\n !useNftDetection !== disabled\n ) {\n this.configure({ selectedAddress, disabled: !useNftDetection });\n }\n\n if (useNftDetection !== undefined) {\n if (useNftDetection) {\n this.start();\n } else {\n this.stop();\n }\n }\n });\n\n onNetworkStateChange(({ providerConfig }) => {\n this.configure({\n chainId: providerConfig.chainId,\n });\n });\n this.getOpenSeaApiKey = getOpenSeaApiKey;\n this.addNft = addNft;\n }\n\n /**\n * Start polling for the currency rate.\n */\n async start() {\n if (!this.isMainnet() || this.disabled) {\n return;\n }\n\n await this.startPolling();\n }\n\n /**\n * Stop polling for the currency rate.\n */\n stop() {\n this.stopPolling();\n }\n\n private stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - An interval on which to poll.\n */\n private async startPolling(interval?: number): Promise<void> {\n interval && this.configure({ interval }, false, false);\n this.stopPolling();\n await this.detectNfts();\n this.intervalId = setInterval(async () => {\n await this.detectNfts();\n }, this.config.interval);\n }\n\n /**\n * Checks whether network is mainnet or not.\n *\n * @returns Whether current network is mainnet.\n */\n isMainnet = (): boolean => this.config.chainId === ChainId.mainnet;\n\n /**\n * Triggers asset ERC721 token auto detection on mainnet. Any newly detected NFTs are\n * added.\n */\n async detectNfts() {\n /* istanbul ignore if */\n if (!this.isMainnet() || this.disabled) {\n return;\n }\n const { selectedAddress, chainId } = this.config;\n\n /* istanbul ignore else */\n if (!selectedAddress) {\n return;\n }\n\n const apiNfts = await this.getOwnerNfts(selectedAddress);\n const addNftPromises = apiNfts.map(async (nft: ApiNft) => {\n const {\n token_id,\n num_sales,\n background_color,\n image_url,\n image_preview_url,\n image_thumbnail_url,\n image_original_url,\n animation_url,\n animation_original_url,\n name,\n description,\n external_link,\n creator,\n asset_contract: { address, schema_name },\n last_sale,\n } = nft;\n\n let ignored;\n /* istanbul ignore else */\n const { ignoredNfts } = this.getNftState();\n if (ignoredNfts.length) {\n ignored = ignoredNfts.find((c) => {\n /* istanbul ignore next */\n return (\n c.address === toChecksumHexAddress(address) &&\n c.tokenId === token_id\n );\n });\n }\n\n /* istanbul ignore else */\n if (!ignored) {\n /* istanbul ignore next */\n const nftMetadata: NftMetadata = Object.assign(\n {},\n { name },\n creator && { creator },\n description && { description },\n image_url && { image: image_url },\n num_sales && { numberOfSales: num_sales },\n background_color && { backgroundColor: background_color },\n image_preview_url && { imagePreview: image_preview_url },\n image_thumbnail_url && { imageThumbnail: image_thumbnail_url },\n image_original_url && { imageOriginal: image_original_url },\n animation_url && { animation: animation_url },\n animation_original_url && {\n animationOriginal: animation_original_url,\n },\n schema_name && { standard: schema_name },\n external_link && { externalLink: external_link },\n last_sale && { lastSale: last_sale },\n );\n\n await this.addNft(address, token_id, {\n nftMetadata,\n userAddress: selectedAddress,\n chainId,\n source: Source.Detected,\n });\n }\n });\n await Promise.all(addNftPromises);\n }\n}\n\nexport default NftDetectionController;\n"]}
|
|
1
|
+
{"version":3,"file":"NftDetectionController.js","sourceRoot":"","sources":["../src/NftDetectionController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport {\n OPENSEA_PROXY_URL,\n fetchWithErrorHandling,\n toChecksumHexAddress,\n ChainId,\n} from '@metamask/controller-utils';\nimport type {\n NetworkClientId,\n NetworkController,\n NetworkState,\n NetworkClient,\n} from '@metamask/network-controller';\nimport { PollingControllerV1 } from '@metamask/polling-controller';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport { Source } from './constants';\nimport type { NftController, NftState, NftMetadata } from './NftController';\n\nconst DEFAULT_INTERVAL = 180000;\n\n/**\n * @type ApiNft\n *\n * NFT object coming from OpenSea api\n * @property token_id - The NFT identifier\n * @property num_sales - Number of sales\n * @property background_color - The background color to be displayed with the item\n * @property image_url - URI of an image associated with this NFT\n * @property image_preview_url - URI of a smaller image associated with this NFT\n * @property image_thumbnail_url - URI of a thumbnail image associated with this NFT\n * @property image_original_url - URI of the original image associated with this NFT\n * @property animation_url - URI of a animation associated with this NFT\n * @property animation_original_url - URI of the original animation associated with this NFT\n * @property name - The NFT name\n * @property description - The NFT description\n * @property external_link - External link containing additional information\n * @property assetContract - The NFT contract information object\n * @property creator - The NFT owner information object\n * @property lastSale - When this item was last sold\n */\nexport interface ApiNft {\n token_id: string;\n num_sales: number | null;\n background_color: string | null;\n image_url: string | null;\n image_preview_url: string | null;\n image_thumbnail_url: string | null;\n image_original_url: string | null;\n animation_url: string | null;\n animation_original_url: string | null;\n name: string | null;\n description: string | null;\n external_link: string | null;\n asset_contract: ApiNftContract;\n creator: ApiNftCreator;\n last_sale: ApiNftLastSale | null;\n}\n\n/**\n * @type ApiNftContract\n *\n * NFT contract object coming from OpenSea api\n * @property address - Address of the NFT contract\n * @property asset_contract_type - The NFT type, it could be `semi-fungible` or `non-fungible`\n * @property created_date - Creation date\n * @property collection - Object containing the contract name and URI of an image associated\n * @property schema_name - The schema followed by the contract, it could be `ERC721` or `ERC1155`\n * @property symbol - The NFT contract symbol\n * @property total_supply - Total supply of NFTs\n * @property description - The NFT contract description\n * @property external_link - External link containing additional information\n */\nexport interface ApiNftContract {\n address: string;\n asset_contract_type: string | null;\n created_date: string | null;\n schema_name: string | null;\n symbol: string | null;\n total_supply: string | null;\n description: string | null;\n external_link: string | null;\n collection: {\n name: string | null;\n image_url?: string | null;\n };\n}\n\n/**\n * @type ApiNftLastSale\n *\n * NFT sale object coming from OpenSea api\n * @property event_timestamp - Object containing a `username`\n * @property total_price - URI of NFT image associated with this owner\n * @property transaction - Object containing transaction_hash and block_hash\n */\nexport interface ApiNftLastSale {\n event_timestamp: string;\n total_price: string;\n transaction: { transaction_hash: string; block_hash: string };\n}\n\n/**\n * @type ApiNftCreator\n *\n * NFT creator object coming from OpenSea api\n * @property user - Object containing a `username`\n * @property profile_img_url - URI of NFT image associated with this owner\n * @property address - The owner address\n */\nexport interface ApiNftCreator {\n user: { username: string };\n profile_img_url: string;\n address: string;\n}\n\n/**\n * @type NftDetectionConfig\n *\n * NftDetection configuration\n * @property interval - Polling interval used to fetch new token rates\n * @property chainId - Current chain ID\n * @property selectedAddress - Vault selected address\n */\nexport interface NftDetectionConfig extends BaseConfig {\n interval: number;\n chainId: Hex;\n selectedAddress: string;\n}\n\n/**\n * Controller that passively polls on a set interval for NFT auto detection\n */\nexport class NftDetectionController extends PollingControllerV1<\n NftDetectionConfig,\n BaseState\n> {\n private intervalId?: ReturnType<typeof setTimeout>;\n\n private getOwnerNftApi({\n address,\n offset,\n }: {\n address: string;\n offset: number;\n }) {\n return `${OPENSEA_PROXY_URL}/assets?owner=${address}&offset=${offset}&limit=50`;\n }\n\n private async getOwnerNfts(address: string) {\n let nftApiResponse: { assets: ApiNft[] };\n let nfts: ApiNft[] = [];\n let offset = 0;\n let pagingFinish = false;\n /* istanbul ignore if */\n do {\n nftApiResponse = await fetchWithErrorHandling({\n url: this.getOwnerNftApi({ address, offset }),\n timeout: 15000,\n });\n\n if (!nftApiResponse) {\n return nfts;\n }\n\n nftApiResponse?.assets?.length !== 0\n ? (nfts = [...nfts, ...nftApiResponse.assets])\n : (pagingFinish = true);\n offset += 50;\n } while (!pagingFinish);\n\n return nfts;\n }\n\n /**\n * Name of this controller used during composition\n */\n override name = 'NftDetectionController';\n\n private readonly getOpenSeaApiKey: () => string | undefined;\n\n private readonly addNft: NftController['addNft'];\n\n private readonly getNftState: () => NftState;\n\n private readonly getNetworkClientById: NetworkController['getNetworkClientById'];\n\n /**\n * Creates an NftDetectionController instance.\n *\n * @param options - The controller options.\n * @param options.chainId - The chain ID of the current network.\n * @param options.onNftsStateChange - Allows subscribing to assets controller state changes.\n * @param options.onPreferencesStateChange - Allows subscribing to preferences controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.\n * @param options.getOpenSeaApiKey - Gets the OpenSea API key, if one is set.\n * @param options.addNft - Add an NFT.\n * @param options.getNftState - Gets the current state of the Assets controller.\n * @param options.getNetworkClientById - Gets the network client by ID, from the NetworkController.\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n {\n chainId: initialChainId,\n getNetworkClientById,\n onPreferencesStateChange,\n onNetworkStateChange,\n getOpenSeaApiKey,\n addNft,\n getNftState,\n }: {\n chainId: Hex;\n getNetworkClientById: NetworkController['getNetworkClientById'];\n onNftsStateChange: (listener: (nftsState: NftState) => void) => void;\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n getOpenSeaApiKey: () => string | undefined;\n addNft: NftController['addNft'];\n getNftState: () => NftState;\n },\n config?: Partial<NftDetectionConfig>,\n state?: Partial<BaseState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n interval: DEFAULT_INTERVAL,\n chainId: initialChainId,\n selectedAddress: '',\n disabled: true,\n };\n this.initialize();\n this.getNftState = getNftState;\n this.getNetworkClientById = getNetworkClientById;\n onPreferencesStateChange(({ selectedAddress, useNftDetection }) => {\n const { selectedAddress: previouslySelectedAddress, disabled } =\n this.config;\n\n if (\n selectedAddress !== previouslySelectedAddress ||\n !useNftDetection !== disabled\n ) {\n this.configure({ selectedAddress, disabled: !useNftDetection });\n }\n\n if (useNftDetection !== undefined) {\n if (useNftDetection) {\n this.start();\n } else {\n this.stop();\n }\n }\n });\n\n onNetworkStateChange(({ providerConfig }) => {\n this.configure({\n chainId: providerConfig.chainId,\n });\n });\n this.getOpenSeaApiKey = getOpenSeaApiKey;\n this.addNft = addNft;\n this.setIntervalLength(this.config.interval);\n }\n\n async _executePoll(\n networkClientId: string,\n options: { address: string },\n ): Promise<void> {\n await this.detectNfts(networkClientId, options.address);\n }\n\n /**\n * Start polling for the currency rate.\n */\n async start() {\n if (!this.isMainnet() || this.disabled) {\n return;\n }\n\n await this.startPolling();\n }\n\n /**\n * Stop polling for the currency rate.\n */\n stop() {\n this.stopPolling();\n }\n\n private stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - An interval on which to poll.\n */\n private async startPolling(interval?: number): Promise<void> {\n interval && this.configure({ interval }, false, false);\n this.stopPolling();\n await this.detectNfts();\n this.intervalId = setInterval(async () => {\n await this.detectNfts();\n }, this.config.interval);\n }\n\n /**\n * Checks whether network is mainnet or not.\n *\n * @returns Whether current network is mainnet.\n */\n isMainnet = (): boolean => this.config.chainId === ChainId.mainnet;\n\n isMainnetByNetworkClientId = (networkClient: NetworkClient): boolean => {\n return networkClient.configuration.chainId === ChainId.mainnet;\n };\n\n private getCorrectChainId(networkClientId?: NetworkClientId) {\n if (networkClientId) {\n return this.getNetworkClientById(networkClientId).configuration.chainId;\n }\n return this.config.chainId;\n }\n\n /**\n * Triggers asset ERC721 token auto detection on mainnet. Any newly detected NFTs are\n * added.\n *\n * @param networkClientId - The network client ID to detect NFTs on.\n * @param accountAddress - The address to detect NFTs for.\n */\n async detectNfts(networkClientId?: NetworkClientId, accountAddress?: string) {\n const chainId = this.getCorrectChainId(networkClientId);\n\n const selectedAddress = accountAddress || this.config.selectedAddress;\n\n /* istanbul ignore if */\n if (!this.isMainnet() || this.disabled) {\n return;\n }\n /* istanbul ignore else */\n if (!selectedAddress) {\n return;\n }\n\n const apiNfts = await this.getOwnerNfts(selectedAddress);\n const addNftPromises = apiNfts.map(async (nft: ApiNft) => {\n const {\n token_id,\n num_sales,\n background_color,\n image_url,\n image_preview_url,\n image_thumbnail_url,\n image_original_url,\n animation_url,\n animation_original_url,\n name,\n description,\n external_link,\n creator,\n asset_contract: { address, schema_name },\n last_sale,\n } = nft;\n\n let ignored;\n /* istanbul ignore else */\n const { ignoredNfts } = this.getNftState();\n if (ignoredNfts.length) {\n ignored = ignoredNfts.find((c) => {\n /* istanbul ignore next */\n return (\n c.address === toChecksumHexAddress(address) &&\n c.tokenId === token_id\n );\n });\n }\n\n /* istanbul ignore else */\n if (!ignored) {\n /* istanbul ignore next */\n const nftMetadata: NftMetadata = Object.assign(\n {},\n { name },\n creator && { creator },\n description && { description },\n image_url && { image: image_url },\n num_sales && { numberOfSales: num_sales },\n background_color && { backgroundColor: background_color },\n image_preview_url && { imagePreview: image_preview_url },\n image_thumbnail_url && { imageThumbnail: image_thumbnail_url },\n image_original_url && { imageOriginal: image_original_url },\n animation_url && { animation: animation_url },\n animation_original_url && {\n animationOriginal: animation_original_url,\n },\n schema_name && { standard: schema_name },\n external_link && { externalLink: external_link },\n last_sale && { lastSale: last_sale },\n );\n\n await this.addNft(address, token_id, {\n nftMetadata,\n userAddress: selectedAddress,\n chainId,\n source: Source.Detected,\n });\n }\n });\n await Promise.all(addNftPromises);\n }\n}\n\nexport default NftDetectionController;\n"]}
|
|
@@ -74,15 +74,15 @@ class TokenBalancesController extends base_controller_1.BaseController {
|
|
|
74
74
|
}
|
|
75
75
|
const { tokens } = this.config;
|
|
76
76
|
const newContractBalances = {};
|
|
77
|
-
for (const
|
|
78
|
-
const { address } =
|
|
77
|
+
for (const token of tokens) {
|
|
78
|
+
const { address } = token;
|
|
79
79
|
try {
|
|
80
80
|
newContractBalances[address] = yield this.getERC20BalanceOf(address, this.getSelectedAddress());
|
|
81
|
-
|
|
81
|
+
token.balanceError = null;
|
|
82
82
|
}
|
|
83
83
|
catch (error) {
|
|
84
84
|
newContractBalances[address] = new ethereumjs_util_1.BN(0);
|
|
85
|
-
|
|
85
|
+
token.balanceError = error;
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
this.update({ contractBalances: newContractBalances });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.js","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,+DAA2D;AAC3D,iEAA2D;AAE3D,qDAAqC;AAO5B,mFAPA,oBAAE,OAOA;AAwBX;;;GAGG;AACH,MAAa,uBAAwB,SAAQ,gCAG5C;IAYC;;;;;;;;;OASG;IACH,YACE,EACE,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GAOlB,EACD,MAAqC,EACrC,KAAmC;QAEnC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAlCvB;;WAEG;QACM,SAAI,GAAG,yBAAyB,CAAC;QAgCxC,IAAI,CAAC,aAAa,GAAG;YACnB,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,EAAE;SACX,CAAC;QACF,IAAI,CAAC,YAAY,GAAG,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE;YACjD,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;YAC3D,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;QAC7C,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED;;;;OAIG;IACG,IAAI,CAAC,QAAiB;;YAC1B,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YACvD,IAAI,CAAC,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACzC,MAAM,IAAA,gCAAa,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;YACjD,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAClC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;KAAA;IAED;;OAEG;IACG,cAAc;;YAClB,IAAI,IAAI,CAAC,QAAQ,EAAE;gBACjB,OAAO;aACR;YACD,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;YAC/B,MAAM,mBAAmB,GAA8B,EAAE,CAAC;YAC1D,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.js","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,+DAA2D;AAC3D,iEAA2D;AAE3D,qDAAqC;AAO5B,mFAPA,oBAAE,OAOA;AAwBX;;;GAGG;AACH,MAAa,uBAAwB,SAAQ,gCAG5C;IAYC;;;;;;;;;OASG;IACH,YACE,EACE,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GAOlB,EACD,MAAqC,EACrC,KAAmC;QAEnC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAlCvB;;WAEG;QACM,SAAI,GAAG,yBAAyB,CAAC;QAgCxC,IAAI,CAAC,aAAa,GAAG;YACnB,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,EAAE;SACX,CAAC;QACF,IAAI,CAAC,YAAY,GAAG,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE;YACjD,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;YAC3D,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;QAC7C,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED;;;;OAIG;IACG,IAAI,CAAC,QAAiB;;YAC1B,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YACvD,IAAI,CAAC,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACzC,MAAM,IAAA,gCAAa,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;YACjD,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAClC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;KAAA;IAED;;OAEG;IACG,cAAc;;YAClB,IAAI,IAAI,CAAC,QAAQ,EAAE;gBACjB,OAAO;aACR;YACD,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;YAC/B,MAAM,mBAAmB,GAA8B,EAAE,CAAC;YAC1D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;gBAC1B,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;gBAC1B,IAAI;oBACF,mBAAmB,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI,CAAC,iBAAiB,CACzD,OAAO,EACP,IAAI,CAAC,kBAAkB,EAAE,CAC1B,CAAC;oBACF,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;iBAC3B;gBAAC,OAAO,KAAK,EAAE;oBACd,mBAAmB,CAAC,OAAO,CAAC,GAAG,IAAI,oBAAE,CAAC,CAAC,CAAC,CAAC;oBACzC,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC;iBAC5B;aACF;YACD,IAAI,CAAC,MAAM,CAAC,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC,CAAC;QACzD,CAAC;KAAA;CACF;AA9FD,0DA8FC;AAED,kBAAe,uBAAuB,CAAC","sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport { BN } from 'ethereumjs-util';\n\nimport type { AssetsContractController } from './AssetsContractController';\nimport type { Token } from './TokenRatesController';\nimport type { TokensState } from './TokensController';\n\n// TODO: Remove this export in the next major release\nexport { BN };\n\n/**\n * @type TokenBalancesConfig\n *\n * Token balances controller configuration\n * @property interval - Polling interval used to fetch new token balances\n * @property tokens - List of tokens to track balances for\n */\nexport interface TokenBalancesConfig extends BaseConfig {\n interval: number;\n tokens: Token[];\n}\n\n/**\n * @type TokenBalancesState\n *\n * Token balances controller state\n * @property contractBalances - Hash of token contract addresses to balances\n */\nexport interface TokenBalancesState extends BaseState {\n contractBalances: { [address: string]: BN };\n}\n\n/**\n * Controller that passively polls on a set interval token balances\n * for tokens stored in the TokensController\n */\nexport class TokenBalancesController extends BaseController<\n TokenBalancesConfig,\n TokenBalancesState\n> {\n private handle?: ReturnType<typeof setTimeout>;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'TokenBalancesController';\n\n private readonly getSelectedAddress: () => PreferencesState['selectedAddress'];\n\n private readonly getERC20BalanceOf: AssetsContractController['getERC20BalanceOf'];\n\n /**\n * Creates a TokenBalancesController instance.\n *\n * @param options - The controller options.\n * @param options.onTokensStateChange - Allows subscribing to assets controller state changes.\n * @param options.getSelectedAddress - Gets the current selected address.\n * @param options.getERC20BalanceOf - Gets the balance of the given account at the given contract address.\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n {\n onTokensStateChange,\n getSelectedAddress,\n getERC20BalanceOf,\n }: {\n onTokensStateChange: (\n listener: (tokenState: TokensState) => void,\n ) => void;\n getSelectedAddress: () => PreferencesState['selectedAddress'];\n getERC20BalanceOf: AssetsContractController['getERC20BalanceOf'];\n },\n config?: Partial<TokenBalancesConfig>,\n state?: Partial<TokenBalancesState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n interval: 180000,\n tokens: [],\n };\n this.defaultState = { contractBalances: {} };\n this.initialize();\n onTokensStateChange(({ tokens, detectedTokens }) => {\n this.configure({ tokens: [...tokens, ...detectedTokens] });\n this.updateBalances();\n });\n this.getSelectedAddress = getSelectedAddress;\n this.getERC20BalanceOf = getERC20BalanceOf;\n this.poll();\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - Polling interval used to fetch new token balances.\n */\n async poll(interval?: number): Promise<void> {\n interval && this.configure({ interval }, false, false);\n this.handle && clearTimeout(this.handle);\n await safelyExecute(() => this.updateBalances());\n this.handle = setTimeout(() => {\n this.poll(this.config.interval);\n }, this.config.interval);\n }\n\n /**\n * Updates balances for all tokens.\n */\n async updateBalances() {\n if (this.disabled) {\n return;\n }\n const { tokens } = this.config;\n const newContractBalances: { [address: string]: BN } = {};\n for (const token of tokens) {\n const { address } = token;\n try {\n newContractBalances[address] = await this.getERC20BalanceOf(\n address,\n this.getSelectedAddress(),\n );\n token.balanceError = null;\n } catch (error) {\n newContractBalances[address] = new BN(0);\n token.balanceError = error;\n }\n }\n this.update({ contractBalances: newContractBalances });\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BaseConfig, BaseState } from '@metamask/base-controller';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import type { NetworkClientId, NetworkController, NetworkState } from '@metamask/network-controller';
|
|
3
|
+
import { PollingControllerV1 } from '@metamask/polling-controller';
|
|
4
4
|
import type { PreferencesState } from '@metamask/preferences-controller';
|
|
5
5
|
import type { Hex } from '@metamask/utils';
|
|
6
6
|
import type { AssetsContractController } from './AssetsContractController';
|
|
@@ -26,7 +26,7 @@ export interface TokenDetectionConfig extends BaseConfig {
|
|
|
26
26
|
/**
|
|
27
27
|
* Controller that passively polls on a set interval for Tokens auto detection
|
|
28
28
|
*/
|
|
29
|
-
export declare class TokenDetectionController extends
|
|
29
|
+
export declare class TokenDetectionController extends PollingControllerV1<TokenDetectionConfig, BaseState> {
|
|
30
30
|
private intervalId?;
|
|
31
31
|
/**
|
|
32
32
|
* Name of this controller used during composition
|
|
@@ -36,6 +36,7 @@ export declare class TokenDetectionController extends BaseController<TokenDetect
|
|
|
36
36
|
private readonly addDetectedTokens;
|
|
37
37
|
private readonly getTokensState;
|
|
38
38
|
private readonly getTokenListState;
|
|
39
|
+
private readonly getNetworkClientById;
|
|
39
40
|
/**
|
|
40
41
|
* Creates a TokenDetectionController instance.
|
|
41
42
|
*
|
|
@@ -49,10 +50,11 @@ export declare class TokenDetectionController extends BaseController<TokenDetect
|
|
|
49
50
|
* @param options.getTokensState - Gets the current state of the Tokens controller.
|
|
50
51
|
* @param options.getNetworkState - Gets the state of the network controller.
|
|
51
52
|
* @param options.getPreferencesState - Gets the state of the preferences controller.
|
|
53
|
+
* @param options.getNetworkClientById - Gets the network client by ID.
|
|
52
54
|
* @param config - Initial options used to configure this controller.
|
|
53
55
|
* @param state - Initial state to set on this controller.
|
|
54
56
|
*/
|
|
55
|
-
constructor({ onPreferencesStateChange, onNetworkStateChange, onTokenListStateChange, getBalancesInSingleCall, addDetectedTokens, getTokenListState, getTokensState, getNetworkState, getPreferencesState, }: {
|
|
57
|
+
constructor({ onPreferencesStateChange, onNetworkStateChange, onTokenListStateChange, getBalancesInSingleCall, addDetectedTokens, getTokenListState, getTokensState, getNetworkState, getPreferencesState, getNetworkClientById, }: {
|
|
56
58
|
onPreferencesStateChange: (listener: (preferencesState: PreferencesState) => void) => void;
|
|
57
59
|
onNetworkStateChange: (listener: (networkState: NetworkState) => void) => void;
|
|
58
60
|
onTokenListStateChange: (listener: (tokenListState: TokenListState) => void) => void;
|
|
@@ -62,6 +64,7 @@ export declare class TokenDetectionController extends BaseController<TokenDetect
|
|
|
62
64
|
getTokensState: () => TokensState;
|
|
63
65
|
getNetworkState: () => NetworkState;
|
|
64
66
|
getPreferencesState: () => PreferencesState;
|
|
67
|
+
getNetworkClientById: NetworkController['getNetworkClientById'];
|
|
65
68
|
}, config?: Partial<TokenDetectionConfig>, state?: Partial<BaseState>);
|
|
66
69
|
/**
|
|
67
70
|
* Start polling for detected tokens.
|
|
@@ -78,10 +81,21 @@ export declare class TokenDetectionController extends BaseController<TokenDetect
|
|
|
78
81
|
* @param interval - An interval on which to poll.
|
|
79
82
|
*/
|
|
80
83
|
private startPolling;
|
|
84
|
+
private getCorrectChainId;
|
|
85
|
+
_executePoll(networkClientId: string, options: {
|
|
86
|
+
address: string;
|
|
87
|
+
}): Promise<void>;
|
|
81
88
|
/**
|
|
82
89
|
* Triggers asset ERC20 token auto detection for each contract address in contract metadata on mainnet.
|
|
90
|
+
*
|
|
91
|
+
* @param options - Options to detect tokens.
|
|
92
|
+
* @param options.networkClientId - The ID of the network client to use.
|
|
93
|
+
* @param options.accountAddress - The account address to use.
|
|
83
94
|
*/
|
|
84
|
-
detectTokens(
|
|
95
|
+
detectTokens(options?: {
|
|
96
|
+
networkClientId?: NetworkClientId;
|
|
97
|
+
accountAddress?: string;
|
|
98
|
+
}): Promise<void>;
|
|
85
99
|
}
|
|
86
100
|
export default TokenDetectionController;
|
|
87
101
|
//# sourceMappingURL=TokenDetectionController.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenDetectionController.d.ts","sourceRoot":"","sources":["../src/TokenDetectionController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"TokenDetectionController.d.ts","sourceRoot":"","sources":["../src/TokenDetectionController.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,14 +10,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.TokenDetectionController = void 0;
|
|
13
|
-
const base_controller_1 = require("@metamask/base-controller");
|
|
14
13
|
const controller_utils_1 = require("@metamask/controller-utils");
|
|
14
|
+
const polling_controller_1 = require("@metamask/polling-controller");
|
|
15
15
|
const assetsUtil_1 = require("./assetsUtil");
|
|
16
16
|
const DEFAULT_INTERVAL = 180000;
|
|
17
17
|
/**
|
|
18
18
|
* Controller that passively polls on a set interval for Tokens auto detection
|
|
19
19
|
*/
|
|
20
|
-
class TokenDetectionController extends
|
|
20
|
+
class TokenDetectionController extends polling_controller_1.PollingControllerV1 {
|
|
21
21
|
/**
|
|
22
22
|
* Creates a TokenDetectionController instance.
|
|
23
23
|
*
|
|
@@ -31,10 +31,11 @@ class TokenDetectionController extends base_controller_1.BaseController {
|
|
|
31
31
|
* @param options.getTokensState - Gets the current state of the Tokens controller.
|
|
32
32
|
* @param options.getNetworkState - Gets the state of the network controller.
|
|
33
33
|
* @param options.getPreferencesState - Gets the state of the preferences controller.
|
|
34
|
+
* @param options.getNetworkClientById - Gets the network client by ID.
|
|
34
35
|
* @param config - Initial options used to configure this controller.
|
|
35
36
|
* @param state - Initial state to set on this controller.
|
|
36
37
|
*/
|
|
37
|
-
constructor({ onPreferencesStateChange, onNetworkStateChange, onTokenListStateChange, getBalancesInSingleCall, addDetectedTokens, getTokenListState, getTokensState, getNetworkState, getPreferencesState, }, config, state) {
|
|
38
|
+
constructor({ onPreferencesStateChange, onNetworkStateChange, onTokenListStateChange, getBalancesInSingleCall, addDetectedTokens, getTokenListState, getTokensState, getNetworkState, getPreferencesState, getNetworkClientById, }, config, state) {
|
|
38
39
|
const { providerConfig: { chainId: defaultChainId }, } = getNetworkState();
|
|
39
40
|
const { useTokenDetection: defaultUseTokenDetection } = getPreferencesState();
|
|
40
41
|
super(config, state);
|
|
@@ -48,6 +49,7 @@ class TokenDetectionController extends base_controller_1.BaseController {
|
|
|
48
49
|
this.getTokenListState = getTokenListState;
|
|
49
50
|
this.addDetectedTokens = addDetectedTokens;
|
|
50
51
|
this.getBalancesInSingleCall = getBalancesInSingleCall;
|
|
52
|
+
this.getNetworkClientById = getNetworkClientById;
|
|
51
53
|
onTokenListStateChange(({ tokenList }) => {
|
|
52
54
|
const hasTokens = Object.keys(tokenList).length;
|
|
53
55
|
if (hasTokens) {
|
|
@@ -116,11 +118,28 @@ class TokenDetectionController extends base_controller_1.BaseController {
|
|
|
116
118
|
}), this.config.interval);
|
|
117
119
|
});
|
|
118
120
|
}
|
|
121
|
+
getCorrectChainId(networkClientId) {
|
|
122
|
+
if (networkClientId) {
|
|
123
|
+
return this.getNetworkClientById(networkClientId).configuration.chainId;
|
|
124
|
+
}
|
|
125
|
+
return this.config.chainId;
|
|
126
|
+
}
|
|
127
|
+
_executePoll(networkClientId, options) {
|
|
128
|
+
return this.detectTokens({
|
|
129
|
+
networkClientId,
|
|
130
|
+
accountAddress: options.address,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
119
133
|
/**
|
|
120
134
|
* Triggers asset ERC20 token auto detection for each contract address in contract metadata on mainnet.
|
|
135
|
+
*
|
|
136
|
+
* @param options - Options to detect tokens.
|
|
137
|
+
* @param options.networkClientId - The ID of the network client to use.
|
|
138
|
+
* @param options.accountAddress - The account address to use.
|
|
121
139
|
*/
|
|
122
|
-
detectTokens() {
|
|
140
|
+
detectTokens(options) {
|
|
123
141
|
return __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
const { networkClientId, accountAddress } = options || {};
|
|
124
143
|
const { disabled, isDetectionEnabledForNetwork, isDetectionEnabledFromPreferences, } = this.config;
|
|
125
144
|
if (disabled ||
|
|
126
145
|
!isDetectionEnabledForNetwork ||
|
|
@@ -128,12 +147,13 @@ class TokenDetectionController extends base_controller_1.BaseController {
|
|
|
128
147
|
return;
|
|
129
148
|
}
|
|
130
149
|
const { tokens } = this.getTokensState();
|
|
131
|
-
const
|
|
150
|
+
const selectedAddress = accountAddress || this.config.selectedAddress;
|
|
151
|
+
const chainId = this.getCorrectChainId(networkClientId);
|
|
132
152
|
const tokensAddresses = tokens.map(
|
|
133
153
|
/* istanbul ignore next*/ (token) => token.address.toLowerCase());
|
|
134
154
|
const { tokenList } = this.getTokenListState();
|
|
135
155
|
const tokensToDetect = [];
|
|
136
|
-
for (const address
|
|
156
|
+
for (const address of Object.keys(tokenList)) {
|
|
137
157
|
if (!tokensAddresses.includes(address)) {
|
|
138
158
|
tokensToDetect.push(address);
|
|
139
159
|
}
|
|
@@ -152,7 +172,7 @@ class TokenDetectionController extends base_controller_1.BaseController {
|
|
|
152
172
|
yield (0, controller_utils_1.safelyExecute)(() => __awaiter(this, void 0, void 0, function* () {
|
|
153
173
|
const balances = yield this.getBalancesInSingleCall(selectedAddress, tokensSlice);
|
|
154
174
|
const tokensToAdd = [];
|
|
155
|
-
for (const tokenAddress
|
|
175
|
+
for (const tokenAddress of Object.keys(balances)) {
|
|
156
176
|
let ignored;
|
|
157
177
|
/* istanbul ignore else */
|
|
158
178
|
const { ignoredTokens } = this.getTokensState();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenDetectionController.js","sourceRoot":"","sources":["../src/TokenDetectionController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n safelyExecute,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type { NetworkState } from '@metamask/network-controller';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport type { AssetsContractController } from './AssetsContractController';\nimport { isTokenDetectionSupportedForNetwork } from './assetsUtil';\nimport type { TokenListState } from './TokenListController';\nimport type { Token } from './TokenRatesController';\nimport type { TokensController, TokensState } from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\n/**\n * @type TokenDetectionConfig\n *\n * TokenDetection configuration\n * @property interval - Polling interval used to fetch new token rates\n * @property selectedAddress - Vault selected address\n * @property chainId - The chain ID of the current network\n * @property isDetectionEnabledFromPreferences - Boolean to track if detection is enabled from PreferencesController\n * @property isDetectionEnabledForNetwork - Boolean to track if detected is enabled for current network\n */\nexport interface TokenDetectionConfig extends BaseConfig {\n interval: number;\n selectedAddress: string;\n chainId: Hex;\n isDetectionEnabledFromPreferences: boolean;\n isDetectionEnabledForNetwork: boolean;\n}\n\n/**\n * Controller that passively polls on a set interval for Tokens auto detection\n */\nexport class TokenDetectionController extends BaseController<\n TokenDetectionConfig,\n BaseState\n> {\n private intervalId?: ReturnType<typeof setTimeout>;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'TokenDetectionController';\n\n private readonly getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n\n private readonly addDetectedTokens: TokensController['addDetectedTokens'];\n\n private readonly getTokensState: () => TokensState;\n\n private readonly getTokenListState: () => TokenListState;\n\n /**\n * Creates a TokenDetectionController instance.\n *\n * @param options - The controller options.\n * @param options.onPreferencesStateChange - Allows subscribing to preferences controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.\n * @param options.onTokenListStateChange - Allows subscribing to token list controller state changes.\n * @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address.\n * @param options.addDetectedTokens - Add a list of detected tokens.\n * @param options.getTokenListState - Gets the current state of the TokenList controller.\n * @param options.getTokensState - Gets the current state of the Tokens controller.\n * @param options.getNetworkState - Gets the state of the network controller.\n * @param options.getPreferencesState - Gets the state of the preferences controller.\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n {\n onPreferencesStateChange,\n onNetworkStateChange,\n onTokenListStateChange,\n getBalancesInSingleCall,\n addDetectedTokens,\n getTokenListState,\n getTokensState,\n getNetworkState,\n getPreferencesState,\n }: {\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n onTokenListStateChange: (\n listener: (tokenListState: TokenListState) => void,\n ) => void;\n getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n addDetectedTokens: TokensController['addDetectedTokens'];\n getTokenListState: () => TokenListState;\n getTokensState: () => TokensState;\n getNetworkState: () => NetworkState;\n getPreferencesState: () => PreferencesState;\n },\n config?: Partial<TokenDetectionConfig>,\n state?: Partial<BaseState>,\n ) {\n const {\n providerConfig: { chainId: defaultChainId },\n } = getNetworkState();\n const { useTokenDetection: defaultUseTokenDetection } =\n getPreferencesState();\n\n super(config, state);\n this.defaultConfig = {\n interval: DEFAULT_INTERVAL,\n selectedAddress: '',\n disabled: true,\n chainId: defaultChainId,\n isDetectionEnabledFromPreferences: defaultUseTokenDetection,\n isDetectionEnabledForNetwork:\n isTokenDetectionSupportedForNetwork(defaultChainId),\n ...config,\n };\n\n this.initialize();\n this.getTokensState = getTokensState;\n this.getTokenListState = getTokenListState;\n this.addDetectedTokens = addDetectedTokens;\n this.getBalancesInSingleCall = getBalancesInSingleCall;\n\n onTokenListStateChange(({ tokenList }) => {\n const hasTokens = Object.keys(tokenList).length;\n\n if (hasTokens) {\n this.detectTokens();\n }\n });\n\n onPreferencesStateChange(({ selectedAddress, useTokenDetection }) => {\n const {\n selectedAddress: currentSelectedAddress,\n isDetectionEnabledFromPreferences,\n } = this.config;\n const isSelectedAddressChanged =\n selectedAddress !== currentSelectedAddress;\n const isDetectionChangedFromPreferences =\n isDetectionEnabledFromPreferences !== useTokenDetection;\n\n this.configure({\n isDetectionEnabledFromPreferences: useTokenDetection,\n selectedAddress,\n });\n\n if (\n useTokenDetection &&\n (isSelectedAddressChanged || isDetectionChangedFromPreferences)\n ) {\n this.detectTokens();\n }\n });\n\n onNetworkStateChange(({ providerConfig: { chainId } }) => {\n const { chainId: currentChainId } = this.config;\n const isDetectionEnabledForNetwork =\n isTokenDetectionSupportedForNetwork(chainId);\n const isChainIdChanged = currentChainId !== chainId;\n\n this.configure({\n chainId,\n isDetectionEnabledForNetwork,\n });\n\n if (isDetectionEnabledForNetwork && isChainIdChanged) {\n this.detectTokens();\n }\n });\n }\n\n /**\n * Start polling for detected tokens.\n */\n async start() {\n this.configure({ disabled: false });\n await this.startPolling();\n }\n\n /**\n * Stop polling for detected tokens.\n */\n stop() {\n this.configure({ disabled: true });\n this.stopPolling();\n }\n\n private stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - An interval on which to poll.\n */\n private async startPolling(interval?: number): Promise<void> {\n interval && this.configure({ interval }, false, false);\n this.stopPolling();\n await this.detectTokens();\n this.intervalId = setInterval(async () => {\n await this.detectTokens();\n }, this.config.interval);\n }\n\n /**\n * Triggers asset ERC20 token auto detection for each contract address in contract metadata on mainnet.\n */\n async detectTokens() {\n const {\n disabled,\n isDetectionEnabledForNetwork,\n isDetectionEnabledFromPreferences,\n } = this.config;\n if (\n disabled ||\n !isDetectionEnabledForNetwork ||\n !isDetectionEnabledFromPreferences\n ) {\n return;\n }\n const { tokens } = this.getTokensState();\n const { selectedAddress, chainId } = this.config;\n\n const tokensAddresses = tokens.map(\n /* istanbul ignore next*/ (token) => token.address.toLowerCase(),\n );\n const { tokenList } = this.getTokenListState();\n const tokensToDetect: string[] = [];\n for (const address in tokenList) {\n if (!tokensAddresses.includes(address)) {\n tokensToDetect.push(address);\n }\n }\n const sliceOfTokensToDetect = [];\n sliceOfTokensToDetect[0] = tokensToDetect.slice(0, 1000);\n sliceOfTokensToDetect[1] = tokensToDetect.slice(\n 1000,\n tokensToDetect.length - 1,\n );\n\n /* istanbul ignore else */\n if (!selectedAddress) {\n return;\n }\n\n for (const tokensSlice of sliceOfTokensToDetect) {\n if (tokensSlice.length === 0) {\n break;\n }\n\n await safelyExecute(async () => {\n const balances = await this.getBalancesInSingleCall(\n selectedAddress,\n tokensSlice,\n );\n const tokensToAdd: Token[] = [];\n for (const tokenAddress in balances) {\n let ignored;\n /* istanbul ignore else */\n const { ignoredTokens } = this.getTokensState();\n if (ignoredTokens.length) {\n ignored = ignoredTokens.find(\n (ignoredTokenAddress) =>\n ignoredTokenAddress === toChecksumHexAddress(tokenAddress),\n );\n }\n const caseInsensitiveTokenKey =\n Object.keys(tokenList).find(\n (i) => i.toLowerCase() === tokenAddress.toLowerCase(),\n ) || '';\n\n if (ignored === undefined) {\n const { decimals, symbol, aggregators, iconUrl, name } =\n tokenList[caseInsensitiveTokenKey];\n tokensToAdd.push({\n address: tokenAddress,\n decimals,\n symbol,\n aggregators,\n image: iconUrl,\n isERC721: false,\n name,\n });\n }\n }\n\n if (tokensToAdd.length) {\n await this.addDetectedTokens(tokensToAdd, {\n selectedAddress,\n chainId,\n });\n }\n });\n }\n }\n}\n\nexport default TokenDetectionController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenDetectionController.js","sourceRoot":"","sources":["../src/TokenDetectionController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport {\n safelyExecute,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type {\n NetworkClientId,\n NetworkController,\n NetworkState,\n} from '@metamask/network-controller';\nimport { PollingControllerV1 } from '@metamask/polling-controller';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport type { AssetsContractController } from './AssetsContractController';\nimport { isTokenDetectionSupportedForNetwork } from './assetsUtil';\nimport type { TokenListState } from './TokenListController';\nimport type { Token } from './TokenRatesController';\nimport type { TokensController, TokensState } from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\n/**\n * @type TokenDetectionConfig\n *\n * TokenDetection configuration\n * @property interval - Polling interval used to fetch new token rates\n * @property selectedAddress - Vault selected address\n * @property chainId - The chain ID of the current network\n * @property isDetectionEnabledFromPreferences - Boolean to track if detection is enabled from PreferencesController\n * @property isDetectionEnabledForNetwork - Boolean to track if detected is enabled for current network\n */\nexport interface TokenDetectionConfig extends BaseConfig {\n interval: number;\n selectedAddress: string;\n chainId: Hex;\n isDetectionEnabledFromPreferences: boolean;\n isDetectionEnabledForNetwork: boolean;\n}\n\n/**\n * Controller that passively polls on a set interval for Tokens auto detection\n */\nexport class TokenDetectionController extends PollingControllerV1<\n TokenDetectionConfig,\n BaseState\n> {\n private intervalId?: ReturnType<typeof setTimeout>;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'TokenDetectionController';\n\n private readonly getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n\n private readonly addDetectedTokens: TokensController['addDetectedTokens'];\n\n private readonly getTokensState: () => TokensState;\n\n private readonly getTokenListState: () => TokenListState;\n\n private readonly getNetworkClientById: NetworkController['getNetworkClientById'];\n\n /**\n * Creates a TokenDetectionController instance.\n *\n * @param options - The controller options.\n * @param options.onPreferencesStateChange - Allows subscribing to preferences controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.\n * @param options.onTokenListStateChange - Allows subscribing to token list controller state changes.\n * @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address.\n * @param options.addDetectedTokens - Add a list of detected tokens.\n * @param options.getTokenListState - Gets the current state of the TokenList controller.\n * @param options.getTokensState - Gets the current state of the Tokens controller.\n * @param options.getNetworkState - Gets the state of the network controller.\n * @param options.getPreferencesState - Gets the state of the preferences controller.\n * @param options.getNetworkClientById - Gets the network client by ID.\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n {\n onPreferencesStateChange,\n onNetworkStateChange,\n onTokenListStateChange,\n getBalancesInSingleCall,\n addDetectedTokens,\n getTokenListState,\n getTokensState,\n getNetworkState,\n getPreferencesState,\n getNetworkClientById,\n }: {\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n onTokenListStateChange: (\n listener: (tokenListState: TokenListState) => void,\n ) => void;\n getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n addDetectedTokens: TokensController['addDetectedTokens'];\n getTokenListState: () => TokenListState;\n getTokensState: () => TokensState;\n getNetworkState: () => NetworkState;\n getPreferencesState: () => PreferencesState;\n getNetworkClientById: NetworkController['getNetworkClientById'];\n },\n config?: Partial<TokenDetectionConfig>,\n state?: Partial<BaseState>,\n ) {\n const {\n providerConfig: { chainId: defaultChainId },\n } = getNetworkState();\n const { useTokenDetection: defaultUseTokenDetection } =\n getPreferencesState();\n\n super(config, state);\n this.defaultConfig = {\n interval: DEFAULT_INTERVAL,\n selectedAddress: '',\n disabled: true,\n chainId: defaultChainId,\n isDetectionEnabledFromPreferences: defaultUseTokenDetection,\n isDetectionEnabledForNetwork:\n isTokenDetectionSupportedForNetwork(defaultChainId),\n ...config,\n };\n\n this.initialize();\n this.getTokensState = getTokensState;\n this.getTokenListState = getTokenListState;\n this.addDetectedTokens = addDetectedTokens;\n this.getBalancesInSingleCall = getBalancesInSingleCall;\n this.getNetworkClientById = getNetworkClientById;\n\n onTokenListStateChange(({ tokenList }) => {\n const hasTokens = Object.keys(tokenList).length;\n\n if (hasTokens) {\n this.detectTokens();\n }\n });\n\n onPreferencesStateChange(({ selectedAddress, useTokenDetection }) => {\n const {\n selectedAddress: currentSelectedAddress,\n isDetectionEnabledFromPreferences,\n } = this.config;\n const isSelectedAddressChanged =\n selectedAddress !== currentSelectedAddress;\n const isDetectionChangedFromPreferences =\n isDetectionEnabledFromPreferences !== useTokenDetection;\n\n this.configure({\n isDetectionEnabledFromPreferences: useTokenDetection,\n selectedAddress,\n });\n\n if (\n useTokenDetection &&\n (isSelectedAddressChanged || isDetectionChangedFromPreferences)\n ) {\n this.detectTokens();\n }\n });\n\n onNetworkStateChange(({ providerConfig: { chainId } }) => {\n const { chainId: currentChainId } = this.config;\n const isDetectionEnabledForNetwork =\n isTokenDetectionSupportedForNetwork(chainId);\n const isChainIdChanged = currentChainId !== chainId;\n\n this.configure({\n chainId,\n isDetectionEnabledForNetwork,\n });\n\n if (isDetectionEnabledForNetwork && isChainIdChanged) {\n this.detectTokens();\n }\n });\n }\n\n /**\n * Start polling for detected tokens.\n */\n async start() {\n this.configure({ disabled: false });\n await this.startPolling();\n }\n\n /**\n * Stop polling for detected tokens.\n */\n stop() {\n this.configure({ disabled: true });\n this.stopPolling();\n }\n\n private stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - An interval on which to poll.\n */\n private async startPolling(interval?: number): Promise<void> {\n interval && this.configure({ interval }, false, false);\n this.stopPolling();\n await this.detectTokens();\n this.intervalId = setInterval(async () => {\n await this.detectTokens();\n }, this.config.interval);\n }\n\n private getCorrectChainId(networkClientId?: NetworkClientId) {\n if (networkClientId) {\n return this.getNetworkClientById(networkClientId).configuration.chainId;\n }\n return this.config.chainId;\n }\n\n _executePoll(\n networkClientId: string,\n options: { address: string },\n ): Promise<void> {\n return this.detectTokens({\n networkClientId,\n accountAddress: options.address,\n });\n }\n\n /**\n * Triggers asset ERC20 token auto detection for each contract address in contract metadata on mainnet.\n *\n * @param options - Options to detect tokens.\n * @param options.networkClientId - The ID of the network client to use.\n * @param options.accountAddress - The account address to use.\n */\n async detectTokens(options?: {\n networkClientId?: NetworkClientId;\n accountAddress?: string;\n }) {\n const { networkClientId, accountAddress } = options || {};\n const {\n disabled,\n isDetectionEnabledForNetwork,\n isDetectionEnabledFromPreferences,\n } = this.config;\n if (\n disabled ||\n !isDetectionEnabledForNetwork ||\n !isDetectionEnabledFromPreferences\n ) {\n return;\n }\n const { tokens } = this.getTokensState();\n const selectedAddress = accountAddress || this.config.selectedAddress;\n const chainId = this.getCorrectChainId(networkClientId);\n\n const tokensAddresses = tokens.map(\n /* istanbul ignore next*/ (token) => token.address.toLowerCase(),\n );\n const { tokenList } = this.getTokenListState();\n const tokensToDetect: string[] = [];\n for (const address of Object.keys(tokenList)) {\n if (!tokensAddresses.includes(address)) {\n tokensToDetect.push(address);\n }\n }\n const sliceOfTokensToDetect = [];\n sliceOfTokensToDetect[0] = tokensToDetect.slice(0, 1000);\n sliceOfTokensToDetect[1] = tokensToDetect.slice(\n 1000,\n tokensToDetect.length - 1,\n );\n\n /* istanbul ignore else */\n if (!selectedAddress) {\n return;\n }\n\n for (const tokensSlice of sliceOfTokensToDetect) {\n if (tokensSlice.length === 0) {\n break;\n }\n\n await safelyExecute(async () => {\n const balances = await this.getBalancesInSingleCall(\n selectedAddress,\n tokensSlice,\n );\n const tokensToAdd: Token[] = [];\n for (const tokenAddress of Object.keys(balances)) {\n let ignored;\n /* istanbul ignore else */\n const { ignoredTokens } = this.getTokensState();\n if (ignoredTokens.length) {\n ignored = ignoredTokens.find(\n (ignoredTokenAddress) =>\n ignoredTokenAddress === toChecksumHexAddress(tokenAddress),\n );\n }\n const caseInsensitiveTokenKey =\n Object.keys(tokenList).find(\n (i) => i.toLowerCase() === tokenAddress.toLowerCase(),\n ) || '';\n\n if (ignored === undefined) {\n const { decimals, symbol, aggregators, iconUrl, name } =\n tokenList[caseInsensitiveTokenKey];\n tokensToAdd.push({\n address: tokenAddress,\n decimals,\n symbol,\n aggregators,\n image: iconUrl,\n isERC721: false,\n name,\n });\n }\n }\n\n if (tokensToAdd.length) {\n await this.addDetectedTokens(tokensToAdd, {\n selectedAddress,\n chainId,\n });\n }\n });\n }\n }\n}\n\nexport default TokenDetectionController;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask/assets-controllers",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.0",
|
|
4
4
|
"description": "Controllers which manage interactions involving ERC-20, ERC-721, and ERC-1155 tokens (including NFTs)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"@ethersproject/contracts": "^5.7.0",
|
|
35
35
|
"@ethersproject/providers": "^5.7.0",
|
|
36
36
|
"@metamask/abi-utils": "^2.0.2",
|
|
37
|
-
"@metamask/approval-controller": "^4.0
|
|
37
|
+
"@metamask/approval-controller": "^4.1.0",
|
|
38
38
|
"@metamask/base-controller": "^3.2.3",
|
|
39
39
|
"@metamask/contract-metadata": "^2.3.1",
|
|
40
40
|
"@metamask/controller-utils": "^5.0.2",
|
|
41
41
|
"@metamask/eth-query": "^3.0.1",
|
|
42
42
|
"@metamask/metamask-eth-abis": "3.0.0",
|
|
43
|
-
"@metamask/network-controller": "^15.
|
|
44
|
-
"@metamask/polling-controller": "^0.
|
|
43
|
+
"@metamask/network-controller": "^15.1.0",
|
|
44
|
+
"@metamask/polling-controller": "^1.0.0",
|
|
45
45
|
"@metamask/preferences-controller": "^4.4.3",
|
|
46
46
|
"@metamask/rpc-errors": "^6.1.0",
|
|
47
47
|
"@metamask/utils": "^8.1.0",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"uuid": "^8.3.2"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@metamask/auto-changelog": "^3.
|
|
57
|
+
"@metamask/auto-changelog": "^3.4.2",
|
|
58
58
|
"@types/jest": "^27.4.1",
|
|
59
59
|
"@types/node": "^16.18.54",
|
|
60
60
|
"deepmerge": "^4.2.2",
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
"typescript": "~4.8.4"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
|
-
"@metamask/approval-controller": "^4.0
|
|
73
|
-
"@metamask/network-controller": "^15.
|
|
72
|
+
"@metamask/approval-controller": "^4.1.0",
|
|
73
|
+
"@metamask/network-controller": "^15.1.0",
|
|
74
74
|
"@metamask/preferences-controller": "^4.4.3"
|
|
75
75
|
},
|
|
76
76
|
"engines": {
|