在本技术教程中,你将学习如何开发 NFT作品并将其部署到 OpenSea市场。你的NFT将是具有不同背景颜色的单个表情符号。我们将使用Chainlink VRF的可验证随机数生成每个 NFT 的表情符号和背景颜色的组合。
非同质化通证(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的元数据提供描述性信息,这样交易市场和dApp能够显示该NFT的可视化表示。开发者要做的第一个决定是如何以及在哪里存储这些数据:它可以完全写入智能合约本身(链上)或托管在IPFS或者说Filecoin等去中心化存储解决方案上(链下)。在本教程中,我们会将元数据存储在链上,即将通过基于随机数生成NFT 艺术作品,并将这些值的SVG 表示存储在智能合约中。
创建一个名为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)
);
}
为了向区块链获取随机值,我们将使用最新发布的 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领取。
现在,你可以通过将你的钱包连接到Etherscan 并单击“mint”函数,或者通过创建一个 dApp UI 与你的智能合约进行交互来轻松地铸币。完成铸造后,可前往 Rinkeby 上的OpenSea (https://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/
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!