# 用 Python 制作 NFT 区块链作品（上）

By [quantbang](https://paragraph.com/@quantbang) · 2022-03-24

---

![](https://storage.googleapis.com/papyrus_images/e02407e3b9994e8bb31a8bcd6fc6ea05988c389692f44d9c53b9536909fffbfe.jpg)

在本文中，我们将学习如何使用 Brownie、Python 和 Chainlink 来制作非同质化的 NFT 作品，并在 OpenSea NFT 市场上展示和销售我们的成果。

**什么是 NFT？**

NFT英文全称为Non-Fungible Token，翻译成中文就是：非同质化代币，具有不可分割、不可替代、独一无二等特点。NFT由于其非同质化、不可拆分的特性，使得它可以和现实世界中的一些商品绑定。换言之，其实就是发行在区块链上的数字资产，这个资产可以是游戏道具、数字艺术品、门票等，并且具有唯一性和不可复制性。由于NFT具备天然的收藏属性和便于交易，加密艺术家们可以利用NFT创造出独一无二的数字艺术品。

**ERC20 与 ERC721**

NFT 是类似于 ERC20 的区块链token标准，如 AAVE、SNX 和 LINK（技术上为 ERC677）。ERC20 是“可替代”的代币，意思是“可替换”或“可互换”。

例如，无论您使用什么美元纸币，您的美元纸币都将值 1 美元。美元钞票上的序列号可能不同，但钞票是可以互换的，无论如何它们都值 1 美元。

另一方面，NFT 是“不可替代的”，它们遵循自己的代币标准 ERC721。例如，蒙娜丽莎是“不可替代的”。即使有人可以复制它，但永远只有一个蒙娜丽莎。如果蒙娜丽莎是在区块链上创建的，它将是 NFT。

![](https://storage.googleapis.com/papyrus_images/8bf01d758a5844aa7f39d681fa5fffa886d4bb32aa548791f655bce3d8981f34.png)

**NFT 有什么用？**

NFT 通过将合约永久地部署在链上，让创作者、艺术家、游戏设计师等创造价值。

你将永远知道谁创造了 NFT，谁拥有 NFT，它来自哪里等等，这种独特性让他们比传统艺术更有价值。在传统艺术中，理解什么是“假货”可能很棘手，而链上的历史很容易追溯。

由于智能合约和 NFT 是 100% 可编程的，NFT 还可以添加内置的版税和任何其他功能。补偿艺术家一直是一个问题，因为通常艺术家的作品在没有任何归属的情况下四处传播。

越来越多的艺术家和工程师开始利用这一巨大的附加值，因为这最终成为艺术家获得工作报酬的好方法。不仅如此，NFT 还是一种展示您的创造力并成为数字世界收藏家的有趣方式。

**NFT 的价值**

NFT 已经走过了漫长的道路，我们不断看到 NFT 的销售额创下纪录，例如下面这幅名为“每天：前 5,000 天”的画作售价为 6930 万美元。

![](https://storage.googleapis.com/papyrus_images/4aa8417505dce1f8a388079f9ff4e79f87dc395db3f59c0a540503ca8b946134.png)

NFT也是在数字世界中创造艺术和了解智能合约创建的一种有趣、动态和引人入胜的方式。所以现在将教你制作 NFT 所需的一切知识。

**如何制作 NFT**

现在，制作 NFT 的最简单方法就是前往 Opensea、Rarible 或 Mintible 等平台，并按照他们的分步指南在他们的平台上进行部署即可。

你可以 100% 走这条路，但是你可能会被平台绑定。你无法实现无限的定制，或者真正利用 NFT 的任何优势。

如果你想用代码来实现，学习一些扎实的知识，并有能力以无限的创造力创造出一些东西，那么请继续阅读！

**如何进行无限定制的 NFT**

首先介绍NFT Brownie Mix。这是一个包含大量样板代码的工作仓库。

[https://github.com/PatrickAlphaC/nft-mix](https://github.com/PatrickAlphaC/nft-mix)

**先决条件**

我们需要安装一些东西才能开始：

*   Python
    
*   Nodejs 和 npm
    
*   Metamask
    

如果您不熟悉 Metamask，您可以按照下面教程进行设置。

[https://docs.chain.link/docs/install-metamask/](https://docs.chain.link/docs/install-metamask/)

**Rinkeby Testnet ETH 和 LINK**

我们还将部署在 Rinkeby 以太坊测试链上！

测试链是测试我们的智能合约在现实世界中表现的好方法。我们需要 Rinkeby ETH 和 Rinkeby LINK，我们可以从 Chainlink 文档中最新水龙头的链接中免费获得它们。

我们还需要将 rinkeby LINK 的token添加到metamask中，我们可以按照 LINK 文档进行操作。

[https://docs.chain.link/docs/acquire-link/](https://docs.chain.link/docs/acquire-link/)

注意一定要使用 Rinkeby 而不是 Ropsten。在使用像以太坊这样的智能合约平台时，我们需要支付一点 ETH，而从链下获取数据时，我们需要支付一点 LINK。这就是我们需要获取测试链上的LINK 和 ETH 的原因。

下面这件作品就是我们要部署到 OpenSea 的 NFT。

[https://testnets.opensea.io/assets/0x8acb7ca932892eb83e4411b59309d44dddbc4cdf/0](https://testnets.opensea.io/assets/0x8acb7ca932892eb83e4411b59309d44dddbc4cdf/0)

![](https://storage.googleapis.com/papyrus_images/0c3ee5e50d76da19a2aa47e1dde1c664d6da20b23a8dcddebaba663d142cb1c7.png)

**快速上手**

    git clone https://github.com/PatrickAlphaC/nft-mix
    cd nft-mix
    

现在我们需要安装 `ganache-cli` 和 `eth-brownie`。

    pip install eth-brownie
    npm install -g ganache-cli
    

现在我们可以设置我们的环境变量。如果您不熟悉环境变量，只需将它们添加到 `.env` 文件中，然后运行：

    source .env
    

`.env`的示例应该在你刚刚克隆的 repo 中，并注释掉了环境变量。请确保取消注释以使用它们！

您需要一个 `WEB3_INFURA_PROJECT_ID`和一个 `PRIVATE_KEY` 。`WEB3_INFURA_PROJECT_ID` 可以在注册免费 Infura 帐户时找到。这将为我们提供一种将交易发送到区块链的方法。

我们还需要一个私钥，您可以从您的 Metamask 中获取。点击 3 个小点，然后单击帐户详细信息和导出私钥。如果您投入现金进去，请不要与任何人分享此密钥！

    export PRIVATE_KEY=YOUR_KEY_HERE
    export WEB3_INFURA_PROJECT_ID=YOUR_PROJECT_ID_HERE
    

现在我们可以部署我们的 NFT 合约并使用以下两个命令创建我们的第一个收藏品。

    brownie run scripts/simple_collectible/deploy_simple.py --network rinkeby
    brownie run scripts/simple_collectible/create_collectible.py --network rinkeby
    

第一个脚本将我们的 NFT 合约部署到 Rinkeby 区块链，第二个脚本创建了我们的第一个收藏品。

您刚刚部署了第一个智能合约！

它根本没有什么作用，但别担心——我将在本教程的高级部分向您展示如何在 OpenSea 上渲染它。首先，让我们看看 ERC721 代币标准。

**ERC721 代币标准**

我们来看看我们刚刚部署的合约，在 SimpleCollectible.sol 文件中。

    // SPDX-License-Identifier: MIT
    pragma solidity 0.6.6;
    import"@openzeppelin/contracts/token/ERC721/ERC721.sol";
    contract SimpleCollectibleis ERC721 {
        uint256 public tokenCounter;
        constructor () public ERC721 ("Dogie", "DOG"){
            tokenCounter = 0;
    }
    function createCollectible(string memory tokenURI) public returns (uint256) {
            uint256 newItemId = tokenCounter;
            _safeMint(msg.sender, newItemId);
            _setTokenURI(newItemId, tokenURI);
            tokenCounter = tokenCounter + 1;
    return newItemId;
    }
    }
    

我们将 OpenZeplin 包用于 ERC721 token。导入的这个包允许我们使用典型 ERC721 token的所有功能。这定义了我们的代币将具有的所有功能，例如 `transfer`——将代币转移给新用户， `safeMint`——创建新代币，等等。

您可以通过查看 OpenZepplin ERC721 代币合约找到赋予我们合约的所有功能。我们的合约在下面一行继承了这些功能：

[https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol)

    contract SimpleCollectibleis ERC721 {
    

这就是 Solidity 继承的方式。当我们部署一个合约时，构造函数会被自动调用，它需要一些参数。

    constructor () public ERC721 ("Dogie", "DOG"){
            tokenCounter = 0;
    }
    

我们还使用了 ERC721 的构造函数，在我们的构造函数中，我们只需要给它一个名称和一个符号。在我们的例子中，它是“Dogie”和“DOG”。这意味着我们创建的每个 NFT 都将是 Dogie/DOG 类型。

这就像每张口袋妖怪卡仍然是口袋妖怪，或者交易卡上的每个棒球运动员仍然是棒球运动员。每个棒球运动员都是独一无二的，但他们仍然都是棒球运动员。我们只是使用 DOG 类型。

我们在顶部有 `tokenCounter` 来计算我们创建了多少这种类型的 NFT。每个新token都会根据当前的 `tokenCounter` 获得一个 `tokenId`。

实际上可以使用 `createCollectible` 函数创建 NFT。这就是我们在 `create_collectible.py` 脚本中所写的。

    function createCollectible(string memory tokenURI) public returns (uint256) {
            uint256 newItemId = tokenCounter;
            _safeMint(msg.sender, newItemId);
            _setTokenURI(newItemId, tokenURI);
            tokenCounter = tokenCounter + 1;
    return newItemId;
    }
    

`_safeMint`函数创建新的 NFT，并将其分配给调用 `createdCollectible`的人，也就是 `msg.sender`，并使用从 `tokenCounter` 派生的 `newItemId`。这就是我们如何通过检查 `tokenId`的所有者来跟踪谁拥有什么。

您会注意到我们还调用了 `_setTokenURI`。让我们来看一下。

**什么是 NFT 元数据和 TokenURI？**

当创建智能合约和创建 NFT 时，人们很快意识到将大量数据部署到区块链是非常昂贵的。小至 1 KB 的图像的存储成本很容易超过 100 万美元。

这显然是 NFT 的一个问题，因为拥有创意艺术意味着您必须将这些信息存储在某个地方。他们还想要一种轻量级的方式来存储有关 NFT 的属性——这就是 `tokenURI` 和元数据发挥作用的地方。

**TokenURI**

NFT 上的 `tokenURI` 是token“外观”的唯一标识符。URI 可以是通过 HTTPS 的 API 调用、IPFS 哈希值或任何其他独特的东西。

它们遵循显示元数据的标准，如下所示：

    {
    "name": "name",
    "description": "description",
    "image": "https://ipfs.io/ipfs/QmTgqnhFBMkfT9s8PHKcdXBn1f5bG3Q5hmBaR4U6hoTvb1?filename=Chainlink_Elf.png",
    "attributes": [
    {
    "trait_type": "trait",
    "value": 100
    }
    ]
    }
    

这些显示了 NFT 的外观及其属性。图像部分指向 NFT 外观的另一个 URI。这使得 Opensea、Rarible 和 Mintable 等 NFT 平台可以轻松地在其平台上呈现 NFT，因为它们都在寻找这种元数据。

**链下元数据与链上元数据**

现在你可能会想“等等......如果元数据不在链上，这是否意味着我的 NFT 可能会在某个时候消失”？你是对的。

您认为链下元数据意味着您不能使用该元数据让您的智能合约相互交互也是正确的。

这就是为什么我们要专注于链上元数据，以便我们可以对 NFT 进行编程以相互交互。

但是，我们仍然需要链下元数据的图像部分，因为我们没有一种很好的方法来在链上存储大图像。但是别担心，我们仍然可以通过使用 IPFS 在去中心化网络上免费做到这一点。

这是来自 IPFS 的 `imageURI`示例，它显示了在龙与地下城教程中创建的 `ChainlinkElf`。

[https://blog.chain.link/build-deploy-and-sell-your-own-dynamic-nft/](https://blog.chain.link/build-deploy-and-sell-your-own-dynamic-nft/)

![](https://storage.googleapis.com/papyrus_images/e3493123b2b22e8fe7ccb6ef652c0ab9372efd60b50d8eb02189c7c069ff7928.png)

我们没有为简单的 NFT 设置 `tokenURI`，因为我们只想展示一个基本示例。

下一篇将讲解 NFT 进阶知识，这样就可以看到我们用链上元数据实现的一些惊人功能，在 opeansea 上渲染 NFT，并让我们的狗狗振作起来！

---

*Originally published on [quantbang](https://paragraph.com/@quantbang/python-nft)*
