如何创建NFT

  • Chainlink
  • 更新于 2022-06-08 08:56
  • 阅读 1837

在本技术教程中,你将学习如何开发 NFT作品并将其部署到 OpenSea市场。你的NFT将是具有不同背景颜色的单个表情符号。我们将使用Chainlink VRF的可验证随机数生成每个 NFT 的表情符号和背景颜色的组合。

1.png

非同质化通证(NFT)是区块链上的数字通证,每一个都代表着独特的东西,例如数字艺术品、特殊的游戏物品、稀有的交易卡收藏品或任何其他独特的数字/物理资产等。NFT与同质化通证完全不同:每一个都是独一无二的。NFT持有者们关心的他们持有的是哪一个,而不是数量。

在本技术教程中,你将学习如何开发 NFT作品并将其部署到 OpenSea市场。你的NFT将是具有不同背景颜色的单个表情符号。我们将使用Chainlink VRF的可验证随机数生成每个 NFT 的表情符号和背景颜色的组合。

我们开始吧。

复制代码仓库

第一步是复制 Chainlink智能合约示例存储库https://github.com/smartcontractkit/smart-contract-examples)。完成此操作后,前往Random SVG NFT目录并安装必要的依赖项。

git clone https://github.com/smartcontractkit/smart-contract-examples.git
cd smart-contract-examples/random-svg-nft

yarn

然后,使用代码编辑器中打开项目。按照项目“Readme”文件中的说明设置所需的环境变量(需要注册一个免费的Alchemy帐户和一个免费的Etherscan API密钥)。在本教程中,我们将在以太坊上使用Rinkeby测试网。

ETHERSCAN_API_KEY=

RINKEBY_URL=https://eth-rinkeby.alchemyapi.io/v2/

PRIVATE_KEY=

NFT 元数据

连接到NFT的元数据提供描述性信息,这样交易市场和dApp能够显示该NFT的可视化表示。开发者要做的第一个决定是如何以及在哪里存储这些数据:它可以完全写入智能合约本身(链上)或托管在IPFS或者说Filecoin等去中心化存储解决方案上(链下)。在本教程中,我们会将元数据存储在链上,即将通过基于随机数生成NFT 艺术作品,并将这些值的SVG 表示存储在智能合约中。

开发 NFT 智能合约

创建一个名为EmojiNFT.sol的新Solidity文件。我们将从OpenZeppelin库中继承几个智能合约,并使用 Chainlink VRF。

初始化存储变量并用你最喜欢的表情符号填充表情符号数组,比如使用你在手机上最近使用的十个表情符号。

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Base64.sol";

contract EmojiNFT is ERC721URIStorage, VRFConsumerBaseV2 {
  using Counters for Counters.Counter;
  Counters.Counter private tokenIds;

  string[] private emojis = [
    unicode"😁",
    unicode"😂",
    unicode"😍",
    unicode"😭",
    unicode"😴",
    unicode"😎",
    unicode"🤑",
    unicode"🥳",
    unicode"😱",
    unicode"🙄"
];

VRFCoordinatorV2Interface internal immutable vrfCoordinator;
bytes32 internal immutable keyHash;
uint64 internal immutable subscriptionId;
uint32 internal immutable callbackGasLimit;
uint32 internal immutable numWords;
uint16 internal immutable requestConfirmations;

mapping(uint256 => address) requestToSender;

event RandomnessRequested(uint256 indexed requestId);

为了确保智能合约能够正确部署,我们添加一个构造函数,并使用“EmojiNFT”作为该NFT系列名称,使用“EMOJI”作为符号。可自由更改这些值并用你任何你喜欢的名字来命名NFT系列。

constructor(
  address _vrfCoordinator,
  bytes32 _keyHash,
  uint64 _subscriptionId,
  uint32 _callbackGasLimit,
  uint16 _requestConfirmations
) VRFConsumerBaseV2(_vrfCoordinator) ERC721("EmojiNFT", "EMOJI") {
  vrfCoordinator = VRFCoordinatorV2Interface(_vrfCoordinator);
  keyHash = _keyHash;
  subscriptionId = _subscriptionId;
  callbackGasLimit = _callbackGasLimit;
  numWords = 4;
  requestConfirmations = _requestConfirmations;
}

现在我们添加一个铸造新NFT的方法。我们的方法将从Chainlink VRF请求四个随机值,然后在fulfillRandomWords函数中,根据第一个随机值从数组中选取表情符号,根据其余三个随机值生成随机颜色,生成链上SVG文件,创建一个OpenSea兼容的通证URL,然后铸造一个新的 NFT。由于Chainlink VRF是异步的,我们将使用 requestToSender将所有 Chainlink VRF 请求映射到通证的铸造者。

function mint() public returns (uint256 requestId) {
  requestId = vrfCoordinator.requestRandomWords(
    keyHash,
    subscriptionId,
    requestConfirmations,
    callbackGasLimit,
    numWords
);

  requestToSender[requestId] = msg.sender;

  emit RandomnessRequested(requestId);
}

function fulfillRandomWords(uint256 requestId, uint256[] memory randomNumbers)
  internal
  override
{
  uint256 tokenId = tokenIds.current();

  uint256 emojiIndex = (randomNumbers[0] % emojis.length) + 1;
  string memory emoji = emojis[emojiIndex];
  string memory color = pickRandomColor(randomNumbers[1], randomNumbers[2], randomNumbers[3]);
  string memory svg = createOnChainSvg(emoji, color);
  string memory tokenUri = createTokenUri(emoji, svg);

  _safeMint(requestToSender[requestId], tokenId);
  _setTokenURI(tokenId, tokenUri);

  tokenIds.increment();
 }
}

最后一步是为缺少的pickRandomColor、createOnChainSvg 和 createTokenUri函数添加代码。

我们将使用Chainlink VRF通过请求三个不同的随机值来为我们的 NFT 艺术生成随机背景颜色,每个值代表 RGB 格式的颜色。RGB 是一种颜色格式,其中将三种原色组合以产生另一种颜色。RGB 常用于计算机科学以及电视、摄像机和显示器中。

每个参数(红色、绿色和蓝色)将颜色的强度定义为介于 0 和 255 之间的整数。例如,rgb(0, 0, 255) 被渲染为蓝色,因为蓝色参数设置为最高值 (255) 和其他设置为 0。类似地,rgb(255, 0, 0) 呈现为红色。

由于 VRF 提供的值可能远大于 255,因此我们需要取模来计算 r、g 和 b 参数。

function pickRandomColor(uint256 firstRandomNumber, uint256 secondRandomNumber, uint256 thirdRandomNumber)
internal
pure
returns (string memory)
{
  uint256 r = firstRandomNumber % 256;
  uint256 g = secondRandomNumber % 256;
  uint256 b = thirdRandomNumber % 256;

  return
  string(
    abi.encodePacked(
      "rgb(",
      Strings.toString(r),
      ", ",
      Strings.toString(g),
      ", ",
      Strings.toString(b),
      ");"
    )
  );
 }

SVG (Scalable Vector Graphics) 是一种基于XML的标记语言,用于描述基于二维的矢量图形。简单来说,SVG 是一种图像,但它是一个用代码构建的图像。这些图像可以在任何分辨率下以高质量打印,并且在调整大小时不会损失任何质量。这就是为什么SVG是适合我们这里用例的完美格式,我们将NFT元数据完全存储在链上并创建真正永久的通证——我们的SVG可以从通证的元数据中生成,不依赖于外部托管。

function createOnChainSvg(string memory emoji, string memory color) internal pure returns(string memory svg) {

    string memory baseSvg = "<svg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='xMinYMin meet' viewBox='0 0 350 350'><style>.base { font-size: 100px; }</style><rect width='100%' height='100%' style='fill:";

    string memory afterColorSvg = "' /><text x='50%' y='50%' class='base' dominant-baseline='middle' text-anchor='middle'>";

    svg = string(abi.encodePacked(baseSvg, color, afterColorSvg, emoji, "</text></svg>"));
}

通证URL是通证元数据的链接。在我们的例子中,它将包含带有“name”、“description”和“image”属性的 JSON,看起来像下面这样:

{"name": "😍", "description": "Random Emoji NFT Collection Powered by Chainlink VRF", "image": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHByZXNlcnZlQXNwZWN0UmF0aW89J3hNaW5ZTWluIG1lZXQnIHZpZXdCb3g9JzAgMCAzNTAgMzUwJz48c3R5bGU+LmJhc2UgeyBmb250LXNpemU6IDEwMHB4OyB9PC9zdHlsZT48cmVjdCB3aWR0aD0nMTAwJScgaGVpZ2h0PScxMDAlJyBzdHlsZT0nZmlsbDpyZ2IoNDcsIDIyNCwgNzYpOycgLz48dGV4dCB4PSc1MCUnIHk9JzUwJScgY2xhc3M9J2Jhc2UnIGRvbWluYW50LWJhc2VsaW5lPSdtaWRkbGUnIHRleHQtYW5jaG9yPSdtaWRkbGUnPvCfmI08L3RleHQ+PC9zdmc+"}
Notice that the SVG image representation is Base64 encoded to match OpenSea’s requirements.
function createTokenUri(string memory emoji, string memory svg) internal pure returns(string memory tokenUri) {
    string memory json = Base64.encode(
      bytes(
        string(
          abi.encodePacked(
            '{"name": "',
            emoji,
            '", "description": "Random Emoji NFT Collection Powered by Chainlink VRF", "image": "data:image/svg+xml;base64,',
            Base64.encode(bytes(svg)),
            '"}'
          )
        )
      )
    );

      tokenUri = string(
      abi.encodePacked("data:application/json;base64,", json)
    );
  }

VRF v2

为了向区块链获取随机值,我们将使用最新发布的 Chainlink VRF v2。新版本的 VRF 对为智能合约提供资金和请求随机性的方式进行了多项改进。

首先,导航到VRF订阅页面(https://vrf.chain.link/),选择 Rinkeby 网络,连接你的钱包,然后单击“创建订阅”。然后,将你的subscriptionId保存为 SUBSCRIPTION_ID 环境变量。命令行中输入下面的内部署EmojiNFT 智能合约:

yarn deploy

或者:

SUBSCRIPTION_ID=yarn deploy

部署后,返回 VRF 订阅页面,导航到你的订阅,单击“添加消费者”按钮,并粘贴最近部署的合约的地址。

最后,向你的订阅充值一些Rinkeby测试网LINK。可以在faucets.chain.link领取。

铸造你的通证并在 OpenSea上进行交易

现在,你可以通过将你的钱包连接到Etherscan 并单击“mint”函数,或者通过创建一个 dApp UI 与你的智能合约进行交互来轻松地铸币。完成铸造后,可前往 Rinkeby 上的OpenSeahttps://testnets.opensea.io/)并搜索你的NFT收藏或钱包地址

总结

在本文中,我们学习了如何编写 NFT 智能合约、链上和链下 NFT 元数据之间的区别、如何使用 Chainlink VRF,以及如何在 Solidity 中生成 SVG 图像并在像 OpenSeaNFT等市场上正确显示它们。要了解更多信息,可前往 Chainlink 智能合约示例存储库并开始试验这个和其他示例项目。

另外可访问chain.link 或阅读 docs.chain.link 上的文档来了解有关 Chainlink 的更多信息。要讨论集成,可联系专家。

原文链接:https://blog.chain.link/how-to-create-an-nft/

本文首发于:https://mp.weixin.qq.com/s/OaFQ0c0qYAa8wZGRzbBBfg

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

0 条评论

请先 登录 后评论
Chainlink
Chainlink
顶尖的智能合约去中心化预言机网络解决方案 https://chain.link/