// 使 uint256 具有 toHexString 功能
using Strings for uint256;
// 使 uint160 具有自定义 toHexString 功能
using HexStrings for uint160;
// 使 bytes3 可以方便生成前端颜色表示
using ToColor for bytes3;
// 计数功能
using Counters for Counters.Counter;
Mint 期限
以下代码是 Mint 时间限制:
uint256 mintDeadline = block.timestamp + 24 hours;
function mintItem()
public
returns (uint256)
{
require( block.timestamp < mintDeadline, "DONE MINTING");
...
首先我们找到 App Home 对应使用的组件,从下面的代码中可以看到,对应使用 Home 组件,所在位置为 src/views/Home.jsx 。
...
<Switch>
<Route exact path="/">
{/* pass in any web3 props to this Home component. For example, yourLocalBalance */}
<Home yourLocalBalance={yourLocalBalance} readContracts={readContracts} />
</Route>
....
...
<Switch>
<Route exact path="/">
{/* pass in any web3 props to this Home component. For example, yourLocalBalance */}
<Home
isSigner={userSigner}
loadWeb3Modal={loadWeb3Modal}
tx={tx}
writeContracts={writeContracts}
/>
...
效果图为:
点击 Mint 之后,我们可以看到交易成功发出,这时,虽然我们成功 mint 了 NFT,但是我们还需要添加列表来展示我们的 NFT。
...
<Switch>
<Route exact path="/">
{/* pass in any web3 props to this Home component. For example, yourLocalBalance */}
<Home
isSigner={userSigner}
loadWeb3Modal={loadWeb3Modal}
yourCollectibles={yourCollectibles}
address={address}
blockExplorer={blockExplorer}
mainnetProvider={mainnetProvider}
tx={tx}
writeContracts={writeContracts}
readContracts={readContracts}
/>
...
效果图为:
但是我们发现,当我们再次 mint 时,列表并不会更新,还是原来的样子,因此我们需要在 App.jsx 中添加事件监听,一旦我们铸造 NFT 之后,列表将刷新:
<Route exact path="/debug">
{/*
🎛 this scaffolding is full of commonly used components
this <Contract/> component will automatically parse your ABI
and give you a form to interact with it locally
*/}
<Contract
name="YourCollectible"
price={price}
signer={userSigner}
provider={localProvider}
address={address}
blockExplorer={blockExplorer}
contractConfig={contractConfig}
/>
注:这篇文章是我投稿于“李大狗Leeduckgo”公众号的文章,原文地址:SVG NFT 全面实践 | Web3.0 dApp 开发(六)。
loogies-svg-nft 是 scaffold-eth 提供的一个简单的 NFT 铸造和展示的项目,在本教程中,我们将带领大家一步步分析和实现这个项目。
由于项目的
loogies-svg-nft
分支与master
分支在组件库和主页上有一些变化,故先将master
分支代码与loogies-svg-nft
分支进行了合并,解决冲突,得到一份基新组件库的全新的代码。可以参考项目地址: https://github.com/qiwihui/scaffold-eth.git 的loogies-svg-nft
分支。本文以下内容将基于这些代码进行部署和分析。本地运行和测试
首先我们先运行项目查看我们将要分析实现的功能。
本地运行
首先我们在本地运行项目:
clone 项目并切换到
loogies-svg-nft
分支:安装依赖包
运行前端
在第二个终端窗口中,运行本地测试链
在第三个终端窗口中,运行部署合约
此时在浏览器中访问
http://localhost:3000
,就可以看到程序了。本地测试
Localhost 8545
http://localhost:8545
31337
ETH
connect
连接钱包;当交易成功后,可以看到新铸造的 NFT;
下面,我们开始对项目合约进行分析。
Loogies 合约分析
NFT 与 ERC721
NFT,全称为Non-Fungible Token,指非同质化代币,对应于以太坊上 ERC-721 标准。 一般在智能合约中,NFT 的定义包含
tokenId
和tokenURI
,每一个 NFT 的tokenId
是唯一的,tokenURI
对于保存了NFT的元数据,可以是图像URL、描述、属性等。如果一个 NFT 想在 NFT 市场上进行展示和销售,则tokenURI
内容需要对应符合 NFT 市场的标准,比如,在 NFT 市场 OpenSea 元数据标准中,就指出了 NFT 展示需要设置的属性。OpenSea 中 NFT 元数据与展示对应关系
合约概览
loogies-svg-nft 项目的合约文件在
packages/hardhat/contracts/
路径下,包含以下三个文件:HexString.sol
:生成地址字符串;ToColor.sol
:生成颜色编码字符串;YourCollectible.sol
:Loogies
NFT的合约文件,主要功能涉及合约铸造和元数据生成。合约的主要结构和方法为:
构造函数
代币符号:
Loogies
代币名称:
LOOG
合约继承自 OpenZeppelin 的
ERC721.sol
,这是 OpenZeppelin 提供的基本合约代码,可以方便开发者使用。应用库函数
合约中分别对
uint256
,uint160
和bytes3
等应用了不同库函数,扩展对应功能:Mint 期限
以下代码是 Mint 时间限制:
合约在部署之后的24小时内可以铸造,超过24小时则会引发异常。这个机制类似于预售,由于这个合约比较简单,所以没有使用白名单机制,一般在实际情况,会使用预售和白名单的方式来控制 NFT 的发行。
Mint 铸造
铸造 NFT 其实就是在合约中设置两个信息:
tokenId
及其owner
tokenId
及其tokenURI
我们首先看铸造函数
mintItem
:其中:
tokenId
在每次铸造时会自增,确保tokenId
唯一;_mint
函数绑定tokenId
及其owner
;tokenId
对应的属性通过随机方式生成,具体为:blockhash(block.number-1)
),当前铸造账户(msg.sender
),合约地址(address(this)
)和tokenId
生成哈希predictableRandom
;predictableRandom
前三位得到颜色,颜色表示用 bytes3 表示,其中bytes2(predictableRandom[0])
对应最低位蓝色数值,( bytes2(predictableRandom[1]) >> 8 )
对应中间位绿色数值,( bytes3(predictableRandom[2]) >> 16 )
对应最高位红色数值;35+((55*uint256(uint8(predictableRandom[3])))/255);
,uint8(predictableRandom[3])
介于0~255,故最小值为35,最大值为 35+55 = 90;例如:
color
为0x4cc4c1
,chubbiness
为 88 时对应的 NFT 图片为:tokenURI 函数
函数
tokenURI
接受tokenId
参数,返回编码之后的元数据字符串:其中,
generateSVGofTokenById
函数返回tokenId
对应的颜色和胖瘦属性生成的 SVG 字符串,renderTokenById
用户绘制图像。我们可以看到,NFT 元数据中包含的属性有:
这里,我们通过实际数据了解一下什么是 SVG。
tokenId
为 1 时对应的tokenURI
结果为:通过 base64 解码
data:application/json;base64,
之后的字符串可以得到如下 json(以下 json 经过了格式化,方便阅读):我们对
image
字段进行解码并格式化就得到图片的 SVG:SVG是一种用 XML 定义的语言,用来描述二维矢量及矢量/栅格图形。它可以任意放大图形显示,也不会牺牲图像质量,它可以使用代码进行描述,方便编辑,因此被广泛使用。
从上面的代码结合以下的图像可以看出,这个 SVG 包含如下内容:
eye1
:由两个椭圆(ellipse)绘制的眼圈和黑色眼珠;head
:填充#4cc4c1
颜色的椭圆作为身体;eye2
:与eye1
一致,位置不同;eye1
,head
和eye2
依次叠加得到最终的图形:辅助函数解析
uint2str
将uint
转变为字符串,例如123
变为'123'
ToColor.sol
库:将byte3
类型转换为前端颜色字符串,例如:输入0x4cc4c1
输出'4cc4c1'
HexStrings.sol
库:主要作用是将uint
按length
位提取,对应于生成公钥时截取前20位的功能:(*uint160*(ownerOf(id))).toHexString(20)
,此表达式生成对应tokenId
所有者的地址。至此,合约源码分析完成。
下面我们将对前端的逻辑进行简要分析,然后我们将一步步实现 NFT 铸造和展示的功能。将代码切换到前端代码提交之前,按照以下的步骤一步步添加功能。
前端逻辑分析
项目前端文件在
packages/react-app
内,以下文章中涉及文件的位置都将在这个文件中寻找。我们首先来看一下
src/App.jsx
,这是项目的主要页面,我们可以利用代码编辑器查看这个文件的主要部分:其中包含的功能和组件包括:
Header
:标题栏,显示标题NetworkDisplay
:所处网络状态Menu
,Switch
:菜单切换ThemeSwitch
:右下角明暗主题切换Account
:右上角账户信息组件Row
对应左下角的 Gas 显示、支持和本地的水龙头下面我们主要看一下
NetworkDisplay
和Account
的逻辑实现,以及Menu
,Switch
中的功能。NetworkDisplay
组件位置:
src/components/NetworkDisplay.jsx
主要包含两个功能:
31337
。Account
组件位置:
src/components/Account.jsx
主要包含两个功能:
Connect
或者Logout
其中,当用户点击
Connect
时,前端调用loadWeb3Modal
,代码如下,这个函数的需要功能是与MetaMask等钱包进行连接,并监听钱包的chainChanged
,accountsChanged
和disconnect
事件,即当我们在钱包中切换网络,选择连接账户以及取消连接时对应修改显示状态。同理,在连接钱包情况下,用户点击
Logout
会调用logoutOfWeb3Modal
功能,Menu
,Switch
这两个分别对应显示菜单和对应切换菜单功能,这些菜单包括:
App Home
:项目希望我们将需要实现的功能放在这个菜单中,比如我们将要实现的 NFT 的铸造和展示功能;Debug Contracts
:调试自己编写的合约功能,将会根据合约的 ABI 文件 展示可以合约的状态变量和可以调用的函数;Hints
:编程提示ExampleUI
:示例UI,可以做为编程使用Mainnet DAI
:以太坊主网DAI
的合约状态和可用函数,与Debug Contracts
功能一直Subgraph
:使用 The Graph 协议对合约中的事件进行监听和查询。调试信息
App.jsx
中还包含了打印当前页面状态的调试信息,可以在开发的过程中实时查看当前状态变量。查看完主页的基本功能,下面我们开始实现 NFT 铸造和展示 NFT 列表这两个功能。
NFT 功能实现
我们将主要实现以下三个部分功能:
铸造 NFT
首先我们找到
App Home
对应使用的组件,从下面的代码中可以看到,对应使用Home
组件,所在位置为src/views/Home.jsx
。删除
Home.jsx
中内容,添加以下 Mint 按钮:同时将
Switch
中对应组件使用修改为:效果图为:
点击 Mint 之后,我们可以看到交易成功发出,这时,虽然我们成功 mint 了 NFT,但是我们还需要添加列表来展示我们的 NFT。
展示 NFT 列表
添加列表展示,其中包含 NFT 的转移功能可以将对应的 NFT 发送给其他地址。
对应组件使用修改为:
效果图为:
但是我们发现,当我们再次 mint 时,列表并不会更新,还是原来的样子,因此我们需要在
App.jsx
中添加事件监听,一旦我们铸造 NFT 之后,列表将刷新:此时,当我们再次 Mint 时,就是自动更新列表,显示最新铸造的 NFT 了。
展示 NFT 合约接口列表
这个功能比较简单,只需要修改对应 debug 部分即可:
更新之后,可以在
Debug Contracts
菜单下看到合约的可以调用的函数。至此,我们就完成了一个简单 NFT 铸造和展示的 DApp 了。
总结
通过这个项目,我们可以学习并了解以下知识: