Skip to content

Commit f3c049c

Browse files
authored
feat(erc721-01): erc721-01 (#852)
* feat(erc721-01): erc721-01
1 parent 162c1c1 commit f3c049c

File tree

2 files changed

+142
-2
lines changed

2 files changed

+142
-2
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
# WTF Solidity
66

7-
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。
7+
我最近在重新学Solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。
88

99
路线图根据本仓库star数量来定:
10+
1011
- [x] 64 :star: 建立社群:[discord](https://discord.gg/5akcruXrsk) | [微信群](https://docs.google.com/forms/d/e/1FAIpQLSe4KGT8Sh6sJ7hedQRuIYirOoZK_85miz3dw7vA1-YjodgJ-A/viewform)
1112

1213
- [ ] 128 :star: 录教学视频
@@ -289,7 +290,7 @@
289290

290291
### NFT
291292

292-
**第1讲:ERC721库:Address, Strings, Context** [代码](https://github.com/AmazingAng/WTF-Solidity/blob/main/Topics/ERC721) | [文章](https://mirror.xyz/wtfacademy.eth/PAsIFLAmEoMufZsXlX0NWsVF8DHpHz3OrYlooosy9Ho)
293+
**第1讲:ERC721库:Address, Strings, Context** [代码](https://github.com/AmazingAng/WTF-Solidity/blob/main/Topics/ERC721) | [文章](./Topics/ERC721/1_related_libraries/readme.md)
293294

294295
**第2讲:ERC721相关接口** [代码](https://github.com/AmazingAng/WTF-Solidity/blob/main/Topics/ERC721) | [文章](https://mirror.xyz/wtfacademy.eth/4mPkMgHViRjx8OM7TAI-M-2oMfRle36ULzqlpC6S7IQ)
295296

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# WTF Solidity极简入门: ERC721专题:1. ERC721相关库
2+
3+
我最近在重新学 Solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新 1-3 讲。
4+
5+
推特:[@0xAA_Science](https://twitter.com/0xAA_Science)[@WTFAcademy_](https://twitter.com/WTFAcademy_)
6+
7+
社区:[Discord](https://discord.gg/5akcruXrsk)[微信群](https://docs.google.com/forms/d/e/1FAIpQLSe4KGT8Sh6sJ7hedQRuIYirOoZK_85miz3dw7vA1-YjodgJ-A/viewform?usp=sf_link)[官网 wtf.academy](https://wtf.academy)
8+
9+
所有代码和教程开源在 github: [github.com/AmazingAng/WTF-Solidity](https://github.com/AmazingAng/WTF-Solidity)
10+
11+
在进阶内容之前,我决定做一个`ERC721`的专题,把之前的内容综合运用,帮助大家更好的复习基础知识,并且更深刻的理解`ERC721`合约。希望在学习完这个专题之后,每个人都能发行自己的`NFT`
12+
13+
---
14+
15+
## ERC721合约概览
16+
17+
`ERC721`主合约一共引用了7个合约:
18+
19+
```Solidity
20+
import "./Address.sol";
21+
import "./Context.sol";
22+
import "./Strings.sol";
23+
import "./IERC721.sol";
24+
import "./IERC721Receiver.sol";
25+
import "./IERC721Metadata.sol";
26+
import "./ERC165.sol";
27+
```
28+
29+
他们分别是:
30+
31+
* 3个库合约:`Address.sol`, `Context.sol``Strings.sol`
32+
* 3个接口合约:`IERC721.sol`, `IERC721Receiver.sol`, `IERC721Metadata.sol`
33+
* 1个`EIP165`合约:`ERC165.sol`
34+
所以在讲`ERC721`的主合约之前,我们会花两讲在引用的库合约和接口合约上。
35+
36+
## ERC721相关库
37+
38+
### Address库
39+
40+
`Address`库是`Address`变量相关函数的合集,包括判断某地址是否为合约,更安全的function call。`ERC721`用到其中的`isContract()`
41+
42+
```Solidity
43+
function isContract(address account) internal view returns (bool) {
44+
return account.code.length > 0;
45+
}
46+
```
47+
48+
这个函数利用了非合约地址`account.code`的长度为0的特性,从而区分某个地址是否为合约地址。
49+
50+
ERC721主合约在`_checkOnERC721Received()`函数中调用了`isContract()`
51+
52+
```Solidity
53+
function _checkOnERC721Received(
54+
address from,
55+
address to,
56+
uint256 tokenId,
57+
bytes memory _data
58+
) private returns (bool) {
59+
if (to.isContract()) {
60+
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
61+
return retval == IERC721Receiver.onERC721Received.selector;
62+
} catch (bytes memory reason) {
63+
if (reason.length == 0) {
64+
revert("ERC721: transfer to non ERC721Receiver implementer");
65+
} else {
66+
assembly {
67+
revert(add(32, reason), mload(reason))
68+
}
69+
}
70+
}
71+
} else {
72+
return true;
73+
}
74+
}
75+
```
76+
77+
该函数的目的是在接收`ERC721`代币的时候判断该地址是否是合约地址;如果是合约地址,则继续检查是否实现了`IERC721Receiver`接口(`ERC721`的接收接口),防止有人误把代币转到了黑洞。
78+
79+
### Context库
80+
81+
`Context`库非常简单,封装了两个Solidity的`global`变量:`msg.sender``msg.data`
82+
83+
```Solidity
84+
abstract contract Context {
85+
function _msgSender() internal view virtual returns (address) {
86+
return msg.sender;
87+
}
88+
89+
function _msgData() internal view virtual returns (bytes calldata) {
90+
return msg.data;
91+
}
92+
}
93+
```
94+
95+
这两个函数只是单纯的返回`msg.sender``msg.data`。所以`Context`库就是为了用函数把`msg.sender``msg.data`关键词包装起来,应对Solidity未来某次升级换掉关键字的情况,没其他作用。
96+
97+
### Strings库
98+
99+
`Strings`库包含两个库函数:`toString()``toHexString()``toString()``uint256`直接转换成`string`,比如777变为”777”;而`toHexString()``uint256`先转换为`16进制`,再转换为`string`,比如170变为”0xaa”。`ERC721`调用了`toString()`函数:
100+
101+
```Solidity
102+
function toString(uint256 value) internal pure returns (string memory) {
103+
if (value == 0) {
104+
return "0";
105+
}
106+
uint256 temp = value;
107+
uint256 digits;
108+
while (temp != 0) {
109+
digits++;
110+
temp /= 10;
111+
}
112+
bytes memory buffer = new bytes(digits);
113+
while (value != 0) {
114+
digits -= 1;
115+
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
116+
value /= 10;
117+
}
118+
return string(buffer);
119+
}
120+
```
121+
122+
这个函数先确定了传入的`uint256`参数是几位数,并存在digits变量中。然后用循环把每一位数字的`ASCII码`转换成`bytes1`,存在`buffer`中,最后把`buffer`转换成`string`返回。
123+
124+
`ERC721`主合约在`tokenURI()`函数中调用了`toString()`
125+
126+
```Solidity
127+
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
128+
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
129+
130+
string memory baseURI = _baseURI();
131+
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
132+
}
133+
```
134+
135+
这个函数把`baseURI`和指定的`tokenId`拼接到一起,返回`ERC721 metadata`的网址,你花几十个ETH买的的jpeg就是存在这个网址上的。
136+
137+
## 总结
138+
139+
这一讲是`ERC721`专题的第一讲,我们概览了`ERC721`的合约,并介绍了`ERC721`主合约调用的3个库合约`Address``Context``String`

0 commit comments

Comments
 (0)