{"version":3,"sources":["jsdelivr-header.js","/npm/@peopledata/minty@1.1.0/src/minty.js"],"names":[],"mappings":"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","file":"/npm/@peopledata/minty@1.1.0/src/minty.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/@peopledata/minty@1.1.0/src/minty.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","const fs = require('fs/promises')\nconst path = require('path')\n\nconst CID = require('cids')\nconst ipfsClient = require('ipfs-http-client')\nconst all = require('it-all')\nconst uint8ArrayConcat = require('uint8arrays/concat')\nconst uint8ArrayToString = require('uint8arrays/to-string')\nconst {BigNumber} = require('ethers')\n\n\nconst { loadDeploymentInfo } = require('./deploy')\n\n// The getconfig package loads configuration from files located in the the `config` directory.\n// See https://www.npmjs.com/package/getconfig for info on how to override the default config for\n// different environments (e.g. testnet, mainnet, staging, production, etc).\nconst config = require('getconfig')\n\n// ipfs.add parameters for more deterministic CIDs\nconst ipfsAddOptions = {\n  cidVersion: 1,\n  hashAlg: 'sha2-256'\n}\n\n/**\n * Construct and asynchronously initialize a new Minty instance.\n * @returns {Promise<Minty>} a new instance of Minty, ready to mint NFTs.\n */\n async function MakeMinty() {\n    const m = new Minty()\n    await m.init()\n    return m\n}\n\n/**\n * Minty is the main object responsible for storing NFT data and interacting with the smart contract.\n * Before constructing, make sure that the contract has been deployed and a deployment\n * info file exists (the default location is `minty-deployment.json`)\n * \n * Minty requires async initialization, so the Minty class (and its constructor) are not exported. \n * To make one, use the async {@link MakeMinty} function.\n */\nclass Minty {\n    constructor() {\n        this.ipfs = null\n        this.contract = null\n        this.deployInfo = null\n        this._initialized = false\n    }\n\n    async init() {\n        if (this._initialized) {\n            return\n        }\n        this.hardhat = require('hardhat')\n\n        // The Minty object expects that the contract has already been deployed, with\n        // details written to a deployment info file. The default location is `./minty-deployment.json`,\n        // in the config.\n        this.deployInfo = await loadDeploymentInfo()\n\n        // connect to the smart contract using the address and ABI from the deploy info\n        const {abi, address} = this.deployInfo.contract\n        this.contract = await this.hardhat.ethers.getContractAt(abi, address)\n\n        // create a local IPFS node\n        this.ipfs = ipfsClient(config.ipfsApiUrl)\n\n        this._initialized = true\n    }\n\n    //////////////////////////////////////////////\n    // ------ NFT Creation\n    //////////////////////////////////////////////\n\n    /**\n     * Create a new NFT from the given asset data.\n     * \n     * @param {Buffer|Uint8Array} content - a Buffer or UInt8Array of data (e.g. for an image)\n     * @param {object} options\n     * @param {?string} path - optional file path to set when storing the data on IPFS\n     * @param {?string} name - optional name to set in NFT metadata\n     * @param {?string} description - optional description to store in NFT metadata\n     * @param {?string} owner - optional ethereum address that should own the new NFT. \n     * If missing, the default signing address will be used.\n     * \n     * @typedef {object} CreateNFTResult\n     * @property {string} tokenId - the unique ID of the new token\n     * @property {string} ownerAddress - the ethereum address of the new token's owner\n     * @property {object} metadata - the JSON metadata stored in IPFS and referenced by the token's metadata URI\n     * @property {string} metadataURI - an ipfs:// URI for the NFT metadata\n     * @property {string} metadataGatewayURL - an HTTP gateway URL for the NFT metadata\n     * @property {string} assetURI - an ipfs:// URI for the NFT asset\n     * @property {string} assetGatewayURL - an HTTP gateway URL for the NFT asset\n     * \n     * @returns {Promise<CreateNFTResult>}\n     */\n    async createNFTFromAssetData(content, options) {\n        // add the asset to IPFS\n        const filePath = options.path || 'asset.bin'\n        const basename =  path.basename(filePath)\n\n        // When you add an object to IPFS with a directory prefix in its path,\n        // IPFS will create a directory structure for you. This is nice, because\n        // it gives us URIs with descriptive filenames in them e.g.\n        // 'ipfs://QmaNZ2FCgvBPqnxtkbToVVbK2Nes6xk5K4Ns6BsmkPucAM/cat-pic.png' instead of\n        // 'ipfs://QmaNZ2FCgvBPqnxtkbToVVbK2Nes6xk5K4Ns6BsmkPucAM'\n        const ipfsPath = '/nft/' + basename\n        const { cid: assetCid } = await this.ipfs.add({ path: ipfsPath, content }, ipfsAddOptions)\n\n        // make the NFT metadata JSON\n        const assetURI = ensureIpfsUriPrefix(assetCid) + '/' + basename\n        const metadata = await this.makeNFTMetadata(assetURI, options)\n\n        // add the metadata to IPFS\n        const { cid: metadataCid } = await this.ipfs.add({ path: '/nft/metadata.json', content: JSON.stringify(metadata)}, ipfsAddOptions)\n        const metadataURI = ensureIpfsUriPrefix(metadataCid) + '/metadata.json'\n\n        // get the address of the token owner from options, or use the default signing address if no owner is given\n        let ownerAddress = options.owner\n        if (!ownerAddress) {\n            ownerAddress = await this.defaultOwnerAddress()\n        }\n\n        // mint a new token referencing the metadata URI\n        const tokenId = await this.mintToken(ownerAddress, metadataURI)\n\n        // format and return the results\n        return {\n            tokenId,\n            ownerAddress,\n            metadata,\n            assetURI,\n            metadataURI,\n            assetGatewayURL: makeGatewayURL(assetURI),\n            metadataGatewayURL: makeGatewayURL(metadataURI),\n        }\n    }\n\n    /**\n     * Create a new NFT from an asset file at the given path.\n     * \n     * @param {string} filename - the path to an image file or other asset to use\n     * @param {object} options\n     * @param {?string} name - optional name to set in NFT metadata\n     * @param {?string} description - optional description to store in NFT metadata\n     * @param {?string} owner - optional ethereum address that should own the new NFT. \n     * If missing, the default signing address will be used.\n     * \n     * @returns {Promise<CreateNFTResult>}\n     */\n    async createNFTFromAssetFile(filename, options) {\n        const content = await fs.readFile(filename)\n        return this.createNFTFromAssetData(content, {...options, path: filename})\n    }\n\n    /**\n     * Helper to construct metadata JSON for \n     * @param {string} assetCid - IPFS URI for the NFT asset\n     * @param {object} options\n     * @param {?string} name - optional name to set in NFT metadata\n     * @param {?string} description - optional description to store in NFT metadata\n     * @returns {object} - NFT metadata object\n     */\n    async makeNFTMetadata(assetURI, options) {\n        const {name, description} = options;\n        assetURI = ensureIpfsUriPrefix(assetURI)\n        return {\n            name,\n            description,\n            image: assetURI\n        }\n    }\n\n    //////////////////////////////////////////////\n    // -------- NFT Retreival\n    //////////////////////////////////////////////\n\n    /**\n     * Get information about an existing token. \n     * By default, this includes the token id, owner address, metadata, and metadata URI.\n     * To include info about when the token was created and by whom, set `opts.fetchCreationInfo` to true.\n     * To include the full asset data (base64 encoded), set `opts.fetchAsset` to true.\n     *\n     * @param {string} tokenId\n     * @param {object} opts\n     * @param {?boolean} opts.fetchAsset - if true, asset data will be fetched from IPFS and returned in assetData (base64 encoded)\n     * @param {?boolean} opts.fetchCreationInfo - if true, fetch historical info (creator address and block number)\n     * \n     * \n     * @typedef {object} NFTInfo\n     * @property {string} tokenId\n     * @property {string} ownerAddress\n     * @property {object} metadata\n     * @property {string} metadataURI\n     * @property {string} metadataGatewayURI\n     * @property {string} assetURI\n     * @property {string} assetGatewayURL\n     * @property {?string} assetDataBase64\n     * @property {?object} creationInfo\n     * @property {string} creationInfo.creatorAddress\n     * @property {number} creationInfo.blockNumber\n     * @returns {Promise<NFTInfo>}\n     */\n    async getNFT(tokenId, opts) {\n        const {metadata, metadataURI} = await this.getNFTMetadata(tokenId)\n        const ownerAddress = await this.getTokenOwner(tokenId)\n        const metadataGatewayURL = makeGatewayURL(metadataURI)\n        const nft = {tokenId, metadata, metadataURI, metadataGatewayURL, ownerAddress}\n\n        const {fetchAsset, fetchCreationInfo} = (opts || {})\n        if (metadata.image) {\n            nft.assetURI = metadata.image\n            nft.assetGatewayURL = makeGatewayURL(metadata.image)\n            if (fetchAsset) {\n                nft.assetDataBase64 = await this.getIPFSBase64(metadata.image)\n            }\n        }\n\n        if (fetchCreationInfo) {\n            nft.creationInfo = await this.getCreationInfo(tokenId)\n        }\n        return nft\n    }\n\n    /**\n     * Fetch the NFT metadata for a given token id.\n     * \n     * @param tokenId - the id of an existing token\n     * @returns {Promise<{metadata: object, metadataURI: string}>} - resolves to an object containing the metadata and\n     * metadata URI. Fails if the token does not exist, or if fetching the data fails.\n     */\n    async getNFTMetadata(tokenId) {\n        const metadataURI = await this.contract.tokenURI(tokenId)\n        const metadata = await this.getIPFSJSON(metadataURI)\n\n        return {metadata, metadataURI}\n    }\n\n    //////////////////////////////////////////////\n    // --------- Smart contract interactions\n    //////////////////////////////////////////////\n\n    /**\n     * Create a new NFT token that references the given metadata CID, owned by the given address.\n     * \n     * @param {string} ownerAddress - the ethereum address that should own the new token\n     * @param {string} metadataURI - IPFS URI for the NFT metadata that should be associated with this token\n     * @returns {Promise<string>} - the ID of the new token\n     */\n    async mintToken(ownerAddress, metadataURI) {\n        // the smart contract adds an ipfs:// prefix to all URIs, so make sure it doesn't get added twice\n        metadataURI = stripIpfsUriPrefix(metadataURI)\n\n        // Call the mintToken method to issue a new token to the given address\n        // This returns a transaction object, but the transaction hasn't been confirmed\n        // yet, so it doesn't have our token id.\n        const tx = await this.contract.mintToken(ownerAddress, metadataURI)\n\n        // The OpenZeppelin base ERC721 contract emits a Transfer event when a token is issued.\n        // tx.wait() will wait until a block containing our transaction has been mined and confirmed.\n        // The transaction receipt contains events emitted while processing the transaction.\n        const receipt = await tx.wait()\n        for (const event of receipt.events) {\n            if (event.event !== 'Transfer') {\n                console.log('ignoring unknown event type ', event.event)\n                continue\n            }\n            return event.args.tokenId.toString()\n        }\n\n        throw new Error('unable to get token id')\n    }\n\n    async transferToken(tokenId, toAddress) {\n        const fromAddress = await this.getTokenOwner(tokenId)\n\n        // because the base ERC721 contract has two overloaded versions of the safeTranferFrom function,\n        // we need to refer to it by its fully qualified name.\n        const tranferFn = this.contract['safeTransferFrom(address,address,uint256)']\n        const tx = await tranferFn(fromAddress, toAddress, tokenId)\n\n        // wait for the transaction to be finalized\n        await tx.wait()\n    }\n\n    /**\n     * @returns {Promise<string>} - the default signing address that should own new tokens, if no owner was specified.\n     */\n    async defaultOwnerAddress() {\n        const signers = await this.hardhat.ethers.getSigners()\n        return signers[0].address\n    }\n\n    /**\n     * Get the address that owns the given token id.\n     * \n     * @param {string} tokenId - the id of an existing token\n     * @returns {Promise<string>} - the ethereum address of the token owner. Fails if no token with the given id exists.\n     */\n    async getTokenOwner(tokenId) {\n        return this.contract.ownerOf(tokenId)\n    }\n\n    /**\n     * Get historical information about the token.\n     * \n     * @param {string} tokenId - the id of an existing token\n     * \n     * @typedef {object} NFTCreationInfo\n     * @property {number} blockNumber - the block height at which the token was minted\n     * @property {string} creatorAddress - the ethereum address of the token's initial owner\n     * \n     * @returns {Promise<NFTCreationInfo>}\n     */\n    async getCreationInfo(tokenId) {\n        const filter = await this.contract.filters.Transfer(\n            null,\n            null,\n            BigNumber.from(tokenId)\n        )\n\n        const logs = await this.contract.queryFilter(filter)\n        const blockNumber = logs[0].blockNumber\n        const creatorAddress = logs[0].args.to\n        return {\n            blockNumber,\n            creatorAddress,\n        }\n    }\n\n    //////////////////////////////////////////////\n    // --------- IPFS helpers\n    //////////////////////////////////////////////\n\n    /**\n     * Get the full contents of the IPFS object identified by the given CID or URI.\n     * \n     * @param {string} cidOrURI - IPFS CID string or `ipfs://<cid>` style URI\n     * @returns {Promise<Uint8Array>} - contents of the IPFS object\n     */\n    async getIPFS(cidOrURI) {\n        const cid = stripIpfsUriPrefix(cidOrURI)\n        return uint8ArrayConcat(await all(this.ipfs.cat(cid)))\n    }\n\n    /**\n     * Get the contents of the IPFS object identified by the given CID or URI, and return it as a string.\n     * \n     * @param {string} cidOrURI - IPFS CID string or `ipfs://<cid>` style URI\n     * @returns {Promise<string>} - the contents of the IPFS object as a string\n     */\n    async getIPFSString(cidOrURI) {\n        const bytes = await this.getIPFS(cidOrURI)\n        return uint8ArrayToString(bytes)\n    }\n\n    /**\n     * Get the full contents of the IPFS object identified by the given CID or URI, and return it as a base64 encoded string.\n     * \n     * @param {string} cidOrURI - IPFS CID string or `ipfs://<cid>` style URI\n     * @returns {Promise<string>} - contents of the IPFS object, encoded to base64\n     */\n    async getIPFSBase64(cidOrURI) {\n        const bytes = await this.getIPFS(cidOrURI)\n        return uint8ArrayToString(bytes, 'base64')\n    }\n\n    /**\n     * Get the contents of the IPFS object identified by the given CID or URI, and parse it as JSON, returning the parsed object.\n     *  \n     * @param {string} cidOrURI - IPFS CID string or `ipfs://<cid>` style URI\n     * @returns {Promise<string>} - contents of the IPFS object, as a javascript object (or array, etc depending on what was stored). Fails if the content isn't valid JSON.\n     */\n    async getIPFSJSON(cidOrURI) {\n        const str = await this.getIPFSString(cidOrURI)\n        return JSON.parse(str)\n    }\n\n    //////////////////////////////////////////////\n    // -------- Pinning to remote services\n    //////////////////////////////////////////////\n\n    /**\n     * Pins all IPFS data associated with the given tokend id to the remote pinning service.\n     * \n     * @param {string} tokenId - the ID of an NFT that was previously minted.\n     * @returns {Promise<{assetURI: string, metadataURI: string}>} - the IPFS asset and metadata uris that were pinned.\n     * Fails if no token with the given id exists, or if pinning fails.\n     */\n    async pinTokenData(tokenId) {\n        const {metadata, metadataURI} = await this.getNFTMetadata(tokenId)\n        const {image: assetURI} = metadata\n        \n        console.log(`Pinning asset data (${assetURI}) for token id ${tokenId}....`)\n        await this.pin(assetURI)\n\n        console.log(`Pinning metadata (${metadataURI}) for token id ${tokenId}...`)\n        await this.pin(metadataURI)\n\n        return {assetURI, metadataURI}\n    }\n\n    /**\n     * Request that the remote pinning service pin the given CID or ipfs URI.\n     * \n     * @param {string} cidOrURI - a CID or ipfs:// URI\n     * @returns {Promise<void>}\n     */\n    async pin(cidOrURI) {\n        const cid = extractCID(cidOrURI)\n\n        // Make sure IPFS is set up to use our preferred pinning service.\n        await this._configurePinningService()\n\n        // Check if we've already pinned this CID to avoid a \"duplicate pin\" error.\n        const pinned = await this.isPinned(cid)\n        if (pinned) {\n            return\n        }\n\n        // Ask the remote service to pin the content.\n        // Behind the scenes, this will cause the pinning service to connect to our local IPFS node\n        // and fetch the data using Bitswap, IPFS's transfer protocol.\n        await this.ipfs.pin.remote.add(cid, { service: config.pinningService.name })\n    }\n\n\n    /**\n     * Check if a cid is already pinned.\n     * \n     * @param {string|CID} cid \n     * @returns {Promise<boolean>} - true if the pinning service has already pinned the given cid\n     */\n    async isPinned(cid) {\n        if (typeof cid === 'string') {\n            cid = new CID(cid)\n        }\n\n        const opts = {\n            service: config.pinningService.name,\n            cid: [cid], // ls expects an array of cids\n        }\n        for await (const result of this.ipfs.pin.remote.ls(opts)) {\n            return true\n        }\n        return false\n    }\n\n    /**\n     * Configure IPFS to use the remote pinning service from our config.\n     * \n     * @private\n     */\n    async _configurePinningService() {\n        if (!config.pinningService) {\n            throw new Error(`No pinningService set up in minty config. Unable to pin.`)\n        }\n\n        // check if the service has already been added to js-ipfs\n        for (const svc of await this.ipfs.pin.remote.service.ls()) {\n            if (svc.service === config.pinningService.name) {\n                // service is already configured, no need to do anything\n                return\n            }\n        }\n\n        // add the service to IPFS\n        const { name, endpoint, key } = config.pinningService\n        if (!name) {\n            throw new Error('No name configured for pinning service')\n        }\n        if (!endpoint) {\n            throw new Error(`No endpoint configured for pinning service ${name}`)\n        }\n        if (!key) {\n            throw new Error(`No key configured for pinning service ${name}.` +\n              `If the config references an environment variable, e.g. '$$PINATA_API_TOKEN', ` + \n              `make sure that the variable is defined.`)\n        }\n        await this.ipfs.pin.remote.service.add(name, { endpoint, key })\n\n    }\n}\n\n//////////////////////////////////////////////\n// -------- URI helpers\n//////////////////////////////////////////////\n\n/**\n * @param {string} cidOrURI either a CID string, or a URI string of the form `ipfs://${cid}`\n * @returns the input string with the `ipfs://` prefix stripped off\n */\n function stripIpfsUriPrefix(cidOrURI) {\n    if (cidOrURI.startsWith('ipfs://')) {\n        return cidOrURI.slice('ipfs://'.length)\n    }\n    return cidOrURI\n}\n\nfunction ensureIpfsUriPrefix(cidOrURI) {\n    let uri = cidOrURI.toString()\n    if (!uri.startsWith('ipfs://')) {\n        uri = 'ipfs://' + cidOrURI\n    }\n    // Avoid the Nyan Cat bug (https://github.com/ipfs/go-ipfs/pull/7930)\n    if (uri.startsWith('ipfs://ipfs/')) {\n      uri = uri.replace('ipfs://ipfs/', 'ipfs://')\n    }\n    return uri\n}\n\n/**\n * Return an HTTP gateway URL for the given IPFS object.\n * @param {string} ipfsURI - an ipfs:// uri or CID string\n * @returns - an HTTP url to view the IPFS object on the configured gateway.\n */\nfunction makeGatewayURL(ipfsURI) {\n    return config.ipfsGatewayUrl + '/' + stripIpfsUriPrefix(ipfsURI)\n}\n\n/**\n * \n * @param {string} cidOrURI - an ipfs:// URI or CID string\n * @returns {CID} a CID for the root of the IPFS path\n */\nfunction extractCID(cidOrURI) {\n    // remove the ipfs:// prefix, split on '/' and return first path component (root CID)\n    const cidString = stripIpfsUriPrefix(cidOrURI).split('/')[0]\n    return new CID(cidString)\n}\n\n\n//////////////////////////////////////////////\n// -------- Exports\n//////////////////////////////////////////////\n\nmodule.exports = {\n    MakeMinty,\n}\n"]}