NFT制作与OpenSea部署教程

在这篇文章里,笔者将展示如何在没有软件公共技术背景下制作 NFT。然后,我们将学习如何用 Brownie、 Python、和 Chainlink 制作无限制可自定义的 NFT。以及我们也会看到如何在 OpenSea 的 NFT 市场上部署和销售我们的创作。

来源 | freecodecamp.org

在这篇文章里,笔者将展示如何在没有软件公共技术背景下制作 NFT。然后,我们将学习如何用 BrowniePython、和 Chainlink 制作无限制可自定义的 NFT。以及我们也会看到如何在 OpenSea 的 NFT 市场上部署和销售我们的创作。

如果你在找使用 Truffle、JavaScript 和有趣的中世纪人物的教程,可以看看如何《构造、部署、和出售你的NFT》 。

什么是 NFT?

NFTs (Non-Fungible Tokens,不可置换代币) 可以用一个词来总结:唯一性。这些是部署在区块链上的智能合约,代表着某种东西的唯一性。

ERC20 vs ERC721

NFT 是一种区块链代币标准,与 ERC20 相似,像 AAVE、SNX、和LINK (准确来说是 ERC677)。ERC20 是“可置换”代币,这意味着那些代币是可被替换的 (replaceable) 和可互相兑换 (interchangeable)。

例如,无论你使用的是哪种美元钞票,你的美元钞票都将价值 1 美元。美元钞票上的序列号可能不一样,但钞票间是可以互相兑换的,而且它们仍然价值1美元。

另一方面,NFT 是“不可置换的”,它们遵守的是它们自己的代币标准 ERC721。例如,蒙罗丽莎是“不可置换的”。即使有人可以制作一个副本,永远也只会有一幅蒙罗丽莎。如果蒙罗丽莎是在区块链上创造的,那么它就是个 NFT。

原始图像来自 Wikipedia

NFT 是做什么的?

NFT 给创作者、艺术家、游戏设计师和其他职业者提供价值,保存在链上的部署还能永久保存。

你总能知道是谁创作了 NFT、谁拥有了 NFT、NFT 的来源,等等,它们给这些创作者带来的价值比传统艺术更大。在传统艺术里,分辨赝品会有点难,但链上历史是很容追踪到的。

由于智能合约和 NFT 是100%可编程的,NFT 还可以添加内置的版税和其他功能。给艺术家补助一直是一个问题,因为情况经常是艺术家的作品广泛传播而人们不知道作品是谁的。

越来越多的艺术家和工程师正在跃跃欲试,参与到这个有巨大价值的领域,因为这最终是让艺术家获得作品报酬的好途径。不止于此,NFT 是展示你的创造力以及在数字世界成为收藏者的有趣方式。

NFT 的价值

NFT 已经走了很长一段路了,并且我们不断看到有拍卖价格破纪录的 NFT,比如“Everyday:The First 5,000 Days” 以 6930 万美元售出。

图像来自 Twitter

因此,这里蕴含着很多价值,它也是在数字世界创作艺术和学习创建智能合约的有趣、动态、和很有魅力的方式。那么,现在我将教你如何制作 NFT 。

如何制作 NFT ?

文章不会涉及的内容

现在,制作 NFT 最简单的方法是到像 OpenseaRaribleMintible 这些平台,跟着它们的逐步指引在它们平台部署。

你可以完全采用这个方法,但你可能会被绑定在平台上,受限于平台提供的功能。你不能实现无限制的自定义制作,或真正使用 NFT 的优势。但如果你是一个初级软件工程师,或没有非常丰富的技术知识,这是适合你的方法。

如果你想成为一名很强的软件工程师,学习一些 solidity 的知识,且有能力可以用无限的创造力创造一些东西,请继续往下看吧!

如果你刚接触 solidity,不用担心,我们会从基础讲起。

如何用无限制的定制制作一个 NFT

我将用这个 NFT Brownie Mix 让你快速入门。这是一个工作代码库,有非常多样板代码。

准备要求

我们需要安装下列内容才能开始:

如果你不熟悉 Metamask,你可以按着这个教程设置。

Rinkeby测试网上的ETH和LINK

我们还需要在 Rinkeby 以太坊测试网上工作,因此我们能免费把我们的合约部署到一个真正的区块链网络上。

测试网是用来测试我们的智能合约在现实世界运行得如何的好方法。我们需要有 Rinkeby 网络的 ETH和 LINK 测试币,我们可以在 Chainlink 文档中最新的水龙头链接里免费获得。

我们还需要添加 Rinkeby 的LINK测试币到我们的 metamask 里,我们可以跟着这个获取 LINK 文档的步骤。

如果你还是很疑惑,你可以跟着这个视频去做,只是要注意是用的 Rinkeby 而不是 Ropsten。

当在像 Ethereum 这样的智能合约平台上工作时,我们需要支付少量的 ETH,在需要从链下获取数据时,我们需要支付少量的 LINK。这就是为什么我们需要测试网的 LINK 和 ETH 测试币。

正式开始吧!这是我们准备部署到 OpenSea 的 NFT

快速开始

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

现在我们需要安装 ganache-clieth-brownie

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

现在我们可以设置环境变量。如果你对环境变量不熟悉,你可以只把它们添加到你的 .env文档,然后运行:

source .env

你刚刚克隆的代码库里应该有一个 .env (环境)样本,它是加了环境变量注释的。取消注释就可以使用它们了!

你将需要一个 WEB3_INFURA_PROJECT_ID (Web3 Infura 项目 ID) 和一个 PRIVATE_KEY (私钥)。注册一个免费 Infura 账号就会有这个 WEB3_INFURA_PROJECT_ID。它会使我们可以发送交易到区块链上。

你还需要一个密钥,你可以在你的 Metamask 里获得。点一下那三个小点,然后点击Account Details (账户详情)和 Export Private Key (导出私钥)。如果你准备把真钱放进去的话,不要把私钥告诉别人。

export PRIVATE_KEY=YOUR_KEY_HERE
export WEB3_INFURA_PROJECT_ID=YOUR_PROJECT_ID_HERE

.env

现在我们可以用以下两个命令部署我们的 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 SimpleCollectible is 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;
    }

}

我们采用的是 OpenZepplin 的 ERC721 代币标准。我们导入的这个包 (package) 允许我们使用一个典型 ERC721 代币的全部功能。这定义了我们的代币将具有的所有功能,例如 transfer 意味着把我们的代币转移给新用户,safeMint表示创建新的代币,等等。

在 OpenZepplin 的ERC721 代币合约里,你可以找到赋予我们合约的所有功能。我们的合约是通过这个命令行承继这些功能的:

contract SimpleCollectible is ERC721 {

solidity 的承继就是这样工作的。当我们部署合约时,constructor会被自动调用,它还需要一些参数。

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

我们的构造函数也使用了 ERC721 的构造函数,我们只需要给它取一个名字和一个符号。在我们的例子中,是"Dogie"和"DOG"。这意味着每个我们创造的 NFT 都是 Dogie/DOG 类。

这就像每张 Pokemon 卡片都是一个 pokermon,或每个在集换式卡片上的棒球手都是一名棒球手。每个棒球手是独一无二的,但他们全部是棒球手。我们只是在用 DOG类。

在顶部我们有 tokenCounter,用来计数我们创建了多少这个类型的 NFT。每个新代币都会获得一个基于当前 tokenCountertokenId

实际上,我们可以用 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 是能展示这个代币的唯一标识符。一个 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 是什么样的以及它的属性。 image部分指向另一个展示该 NFT 的 URI。这使得 NFT 可以更容易在像 Opensea、Rarible和 Mintable 这些 NFT 平台上部署,因为它们都需要这种元数据。

链下元数据 vs 链上元数据

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

如果你认为链下元数据意味着你无法用那元数据让你的智能合约互相交互,你的想法也是对的。

这也是为什么我们专注在链上元数据上,这样我们才可以对我们的 NFT 进行编程,使它们互相交互。

尽管如此,我们仍然需要 image部分的链下元数据,因为我们还没有在链上储存大图像的好方法。但不要担心,通过使用 IPFS,我们还是可以免费在一个去中心化网络实现这一点。

这里是 IPFS 的一个 imageURI 示例,这个图像是按照《龙与地下城》教程创建的 Chainlink Elf

The Chainlink Elf

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

现在进入高级 NFT 部分,我们可以看到用链上元数据实现的一些很棒的功能,把 NFT 部署到 Opensea 上,把我们的 Dogie 放上去!

如果你想看我们刚讲过部分的重温视频,可以点击这里

动态和高级的 NFT

动态 NFT 是那种随时间变化的,或具有我们可以用来互相交互的链上功能的 NFT。这些 NFT 给了我们无限制的自定义空间去制作整个游戏、构建世界、以及某种交互艺术。现在让我们进入高级部分吧。

高级部分快速开始

确认你的 metamask 里有足够的 ETH 和 LINK 测试币,然后运行下面的命令行:

brownie run scripts/advanced_collectible/deploy_advanced.py --network rinkeby
brownie run scripts/advanced_collectible/create_collectible.py --network rinkeby

我们这里的收藏品是从 Chainlink VRF (Virtual Routing and Forwarding,虚拟路由和转发) 返回的一个随机犬种。Chainlink VRF 能提供可验证随机数,也就我们 NFT 真正稀缺性来源。然后,我们就要创建它的元数据了。

brownie run scripts/advanced_collectible/create_metadata.py --network rinkeby

然后,我们可以选择性地上载这个数据到 IPFS,这样我们就会获得一个 tokenURI。我在后面会展示给你看要怎么做。现在,我只需要用下面这个样本 tokenURI。

https://ipfs.io/ipfs/Qmd9MCGtdVz2miNumBHDbvj8bigSgTwnr4SbyH6DNnpWdt?filename=1-PUG.json

如果你下载 IPFS Companion 扩展程序到你的浏览器,你可以使用该 URL 来查看这个 URI 返回的是什么。它应该是下面展示的这样:

{
    "name": "PUG",
    "description": "An adorable PUG pup!",
    "image": "https://ipfs.io/ipfs/QmSsYRx3LpDAb1GZQm7zZ1AuHZjfbPkD6J7s9r41xu1mf8?filename=pug.png",
    "attributes": [
        {
            "trait_type": "cuteness",
            "value": 100
        }
    ]
}

然后运行我们的 set_tokenuri.py 脚本:

brownie run scripts/advanced_collectible/set_tokenuri.py --network rinkeby

我们得到的输出如下:

Running 'scripts/advanced_collectible/set_tokenuri.py::main'...
Working on rinkeby
Transaction sent: 0x8a83a446c306d6255952880c0ca35fa420248a84ba7484c3798d8bbad421f88e
  Gas price: 1.0 gwei   Gas limit: 44601   Nonce: 354
  AdvancedCollectible.setTokenURI confirmed - Block: 8331653   Gas used: 40547 (90.91%)

Awesome! You can view your NFT at https://testnets.opensea.io/assets/0x679c5f9adC630663a6e63Fa27153B215fe021b34/0
Please give up to 20 minutes, and hit the "refresh metadata" button

我们可以点击里面的链接看看它在 Opensea 上怎么样的。你可能需要点 refresh metadata ,等上几分钟。

Refresh Metadata

随机犬种

现在说一下我们刚做了什么。这是我们的 AdvancedCollectible.sol

pragma solidity 0.6.6;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol";

contract AdvancedCollectible is ERC721, VRFConsumerBase {
    uint256 public tokenCounter;
    enum Breed{PUG, SHIBA_INU, BRENARD}
    // add other things
    mapping(bytes32 => address) public requestIdToSender;
    mapping(bytes32 => string) public requestIdToTokenURI;
    mapping(uint256 => Breed) public tokenIdToBreed;
    mapping(bytes32 => uint256) public requestIdToTokenId;
    event requestedCollectible(bytes32 indexed requestId); 

    bytes32 internal keyHash;
    uint256 internal fee;
    uint256 public randomResult;
    constructor(address _VRFCoordinator, address _LinkToken, bytes32 _keyhash)
    public 
    VRFConsumerBase(_VRFCoordinator, _LinkToken)
    ERC721("Dogie", "DOG")
    {
        tokenCounter = 0;
        keyHash = _keyhash;
        fee = 0.1 * 10 ** 18;
    }

    function createCollectible(string memory tokenURI, uint256 userProvidedSeed) 
        public returns (bytes32){
            bytes32 requestId = requestRandomness(keyHash, fee, userProvidedSeed);
            requestIdToSender[requestId] = msg.sender;
            requestIdToTokenURI[requestId] = tokenURI;
            emit requestedCollectible(requestId);
    }

    function fulfillRandomness(bytes32 requestId, uint256 randomNumber) internal override {
        address dogOwner = requestIdToSender[requestId];
        string memory tokenURI = requestIdToTokenURI[requestId];
        uint256 newItemId = tokenCounter;
        _safeMint(dogOwner, newItemId);
        _setTokenURI(newItemId, tokenURI);
        Breed breed = Breed(randomNumber % 3); 
        tokenIdToBreed[newItemId] = breed;
        requestIdToTokenId[requestId] = newItemId;
        tokenCounter = tokenCounter + 1;
    }

    function setTokenURI(uint256 tokenId, string memory _tokenURI) public {
        require(
            _isApprovedOrOwner(_msgSender(), tokenId),
            "ERC721: transfer caller is not owner nor approved"
        );
        _setTokenURI(tokenId, _tokenURI);
    }
}

我们使用 Chainlink VRF 从 PUG, SHIBA_INU, BRENARD列表里创建一个随机犬种。此时当我们调用 createCollectible 时,我们实际上在链下向 Chainlink VRF 节点发出了一个请求,然后返回一个随机数,以三个犬种里的一种来创建这个 NFT。

在你的 NFT 里使用一个真正的随机数是创造真正稀缺性的好方法,使用 Chainlink 预言机的随机数意味着你的数字具有可验证的随机性,无法被矿工修改。

你可以在 Chainlink VRF 文档里了解更多。

Chainlink 节点通过调用 fulfillRandomness函数来响应,并基于随机数创建收藏品。然后,我们还是必须调用 _setTokenURI 来给 NFT 提供所需的外观。

我们此时不设置我们 NFT 的属性,但属性对提升我们 NFT 的竞争力和交互性非常有用。你可以在龙与地下城的例子中看到设置了属性的 NFT 是怎样的。

来自 IPFS 的元数据

我们正在使用 IPFS 来存储两个文档:

  1. NFT 的图像 (八哥的形象)
  2. tokenURI文档 (还包含了图像链接的 JSON 文档)

我们使用 IPFS 因为它是免费的去中心化平台。通过下载 IPFS 桌面版,点击import 按钮,我们可以把 tokenURI 和图像添加到 IPFS。

添加文档到 IPFS

然后,点击文档傍边的三个点,点 share link,然后复制提供的链接, 我们就可以分享 URI 了。然后,我们可以把这个链接添加到我们的 set_tokenuri.py 文档,把代 tokenURI 改为我们想要使用的。

坚持去中心化

但是,如果 tokenURI 只在我们自己的节点上,这意味着如果我们的节点离线了,其他人就看不到它了。因此,我们希望别人可以 pin (挂)我们的 NFT。我们可以使用像 Pinata 这样的服务商,即使我们的 IPFS 节点处于离线状态,他们也可以帮助我们保持数据在线上。

我想,以后会有越来越多的元数据储存在 IPFS 和去中心化储存平台。中心化服务器会式微,也就是说在上面的艺术品会永远消失。因此,请务必检查你的NFT使用的 tokenURI 的位置!

我预计以后会有越来越多人使用像 Filecoin 这样的去中心化储存平台,因为使用 pinning 服务并不非常去中心化。

继续前进

如果你想看一个高级 NFT 讲解视频,你可以看这个

现在你已经有了制作好看、有趣、可自定义、具有交互性的 NFT,并把它部署到交易平台的技能了。

NFT 是能给艺术家的辛勤创作提供精确回报的一种有趣、强大的方式。祝你好运,享受这个过程吧!


ECN的翻译工作旨在为中国以太坊社区传递优质资讯和学习资源,文章版权归原作者所有,转载须注明原文出处以及ETH中文站。若需长期转载,请联系eth@ecn.co进行授权。

本文首发于:https://news.ethereum.cn/Ecosystem/how-to-make-an-nft-and-render-on-opensea-marketplace

点赞 6
收藏 10
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
ETH中文网
ETH中文网
https://ethereum.cn ECN 以太坊中国