{"version":3,"file":"ERC721Standard.cjs","sourceRoot":"","sources":["../../../../src/Standards/NftStandards/ERC721/ERC721Standard.ts"],"names":[],"mappings":"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","sourcesContent":["import { Contract } from '@ethersproject/contracts';\nimport type { Web3Provider } from '@ethersproject/providers';\nimport {\n  timeoutFetch,\n  ERC721_INTERFACE_ID,\n  ERC721_METADATA_INTERFACE_ID,\n  ERC721_ENUMERABLE_INTERFACE_ID,\n  ERC721,\n  safelyExecute,\n} from '@metamask/controller-utils';\nimport { abiERC721 } from '@metamask/metamask-eth-abis';\n\nimport { getFormattedIpfsUrl } from '../../../assetsUtil';\n\nexport class ERC721Standard {\n  private readonly provider: Web3Provider;\n\n  constructor(provider: Web3Provider) {\n    this.provider = provider;\n  }\n\n  /**\n   * Query if contract implements ERC721Metadata interface.\n   *\n   * @param address - ERC721 asset contract address.\n   * @returns Promise resolving to whether the contract implements ERC721Metadata interface.\n   */\n  contractSupportsMetadataInterface = async (\n    address: string,\n  ): Promise<boolean> => {\n    return this.contractSupportsInterface(\n      address,\n      ERC721_METADATA_INTERFACE_ID,\n    );\n  };\n\n  /**\n   * Query if contract implements ERC721Enumerable interface.\n   *\n   * @param address - ERC721 asset contract address.\n   * @returns Promise resolving to whether the contract implements ERC721Enumerable interface.\n   */\n  contractSupportsEnumerableInterface = async (\n    address: string,\n  ): Promise<boolean> => {\n    return this.contractSupportsInterface(\n      address,\n      ERC721_ENUMERABLE_INTERFACE_ID,\n    );\n  };\n\n  /**\n   * Query if contract implements ERC721 interface.\n   *\n   * @param address - ERC721 asset contract address.\n   * @returns Promise resolving to whether the contract implements ERC721 interface.\n   */\n  contractSupportsBase721Interface = async (\n    address: string,\n  ): Promise<boolean> => {\n    return this.contractSupportsInterface(address, ERC721_INTERFACE_ID);\n  };\n\n  /**\n   * Enumerate assets assigned to an owner.\n   *\n   * @param address - ERC721 asset contract address.\n   * @param selectedAddress - Current account public address.\n   * @param index - An NFT counter less than `balanceOf(selectedAddress)`.\n   * @returns Promise resolving to token identifier for the 'index'th asset assigned to 'selectedAddress'.\n   */\n  getNftTokenId = async (\n    address: string,\n    selectedAddress: string,\n    index: number,\n  ): Promise<string> => {\n    const contract = new Contract(address, abiERC721, this.provider);\n    return contract.tokenOfOwnerByIndex(selectedAddress, index);\n  };\n\n  /**\n   * Query for tokenURI for a given asset.\n   *\n   * @param address - ERC721 asset contract address.\n   * @param tokenId - ERC721 asset identifier.\n   * @returns Promise resolving to the 'tokenURI'.\n   */\n  getTokenURI = async (address: string, tokenId: string): Promise<string> => {\n    const contract = new Contract(address, abiERC721, this.provider);\n    const supportsMetadata =\n      await this.contractSupportsMetadataInterface(address);\n    if (!supportsMetadata) {\n      // Do not throw error here, supporting Metadata interface is optional even though majority of ERC721 nfts do support it.\n      // This change is made because of instances of NFTs that are ERC404( mixed ERC20 / ERC721 implementation).\n      // As of today, ERC404 is unofficial but some people use it, the contract does not support Metadata interface, but it has the tokenURI() fct.\n      console.warn('Contract does not support ERC721 metadata interface.');\n    }\n    return contract.tokenURI(tokenId);\n  };\n\n  /**\n   * Query for name for a given asset.\n   *\n   * @param address - ERC721 asset contract address.\n   * @returns Promise resolving to the 'name'.\n   */\n  getAssetName = async (address: string): Promise<string> => {\n    const contract = new Contract(address, abiERC721, this.provider);\n    return contract.name();\n  };\n\n  /**\n   * Query for symbol for a given asset.\n   *\n   * @param address - ERC721 asset contract address.\n   * @returns Promise resolving to the 'symbol'.\n   */\n  getAssetSymbol = async (address: string): Promise<string> => {\n    const contract = new Contract(address, abiERC721, this.provider);\n    return contract.symbol();\n  };\n\n  /**\n   * Query for owner for a given ERC721 asset.\n   *\n   * @param address - ERC721 asset contract address.\n   * @param tokenId - ERC721 asset identifier.\n   * @returns Promise resolving to the owner address.\n   */\n  async getOwnerOf(address: string, tokenId: string): Promise<string> {\n    const contract = new Contract(address, abiERC721, this.provider);\n    return contract.ownerOf(tokenId);\n  }\n\n  /**\n   * Query if a contract implements an interface.\n   *\n   * @param address - Asset contract address.\n   * @param interfaceId - Interface identifier.\n   * @returns Promise resolving to whether the contract implements `interfaceID`.\n   */\n  private readonly contractSupportsInterface = async (\n    address: string,\n    interfaceId: string,\n  ): Promise<boolean> => {\n    const contract = new Contract(address, abiERC721, this.provider);\n    try {\n      return await contract.supportsInterface(interfaceId);\n    } catch (err) {\n      // Mirror previous implementation\n      if (\n        err instanceof Error &&\n        err.message.includes('call revert exception')\n      ) {\n        return false;\n      }\n      throw err;\n    }\n  };\n\n  /**\n   * Query if a contract implements an interface.\n   *\n   * @param address - Asset contract address.\n   * @param ipfsGateway - The user's preferred IPFS gateway.\n   * @param tokenId - tokenId of a given token in the contract.\n   * @returns Promise resolving an object containing the standard, tokenURI, symbol and name of the given contract/tokenId pair.\n   */\n  getDetails = async (\n    address: string,\n    ipfsGateway: string,\n    tokenId?: string,\n  ): Promise<{\n    standard: string;\n    tokenURI: string | undefined;\n    symbol: string | undefined;\n    name: string | undefined;\n    image: string | undefined;\n  }> => {\n    const isERC721 = await this.contractSupportsBase721Interface(address);\n    if (!isERC721) {\n      throw new Error(\"This isn't a valid ERC721 contract\");\n    }\n\n    const [symbol, name, tokenURI] = await Promise.all([\n      safelyExecute(() => this.getAssetSymbol(address)),\n      safelyExecute(() => this.getAssetName(address)),\n      tokenId\n        ? safelyExecute(() =>\n            this.getTokenURI(address, tokenId).then((uri) =>\n              uri.startsWith('ipfs://')\n                ? getFormattedIpfsUrl(ipfsGateway, uri, true)\n                : uri,\n            ),\n          )\n        : undefined,\n    ]);\n\n    let image;\n    if (tokenURI) {\n      try {\n        const response = await timeoutFetch(tokenURI);\n        const object = await response.json();\n        image = object?.image;\n        if (image?.startsWith('ipfs://')) {\n          image = await getFormattedIpfsUrl(ipfsGateway, image, true);\n        }\n      } catch {\n        // ignore\n      }\n    }\n\n    return {\n      standard: ERC721,\n      tokenURI,\n      symbol,\n      name,\n      image,\n    };\n  };\n}\n"]}