Chainlink预言机

Posted on Jun 07, 2022Read on Mirror.xyz

学习创建NFT

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

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

我们开始吧。

复制代码仓库

第一步是复制Chainlink智能合约示例存储库。完成此操作后,前往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=<YOUR ETHERSCAN API>

RINKEBY_URL=https://eth-rinkeby.alchemyapi.io/v2/<YOUR ALCHEMY KEY>

PRIVATE_KEY=<YOUR 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 订阅页面,选择 Rinkeby 网络,连接你的钱包,然后单击“创建订阅”。 然后,将你的 subscriptionId保存为 SUBSCRIPTION_ID 环境变量。 命令行中输入下面的内部署EmojiNFT 智能合约:

yarn deploy

或者

SUBSCRIPTION_ID=<your_subscription_id> yarn deploy

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

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

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

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

总结

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