孤荷凌寒自学第0202天
区块链学习第116天
NFT013继续erc721接口标准
(文末有具体的学习过程录屏视频地址等)
笔记合集在github上:
https://github.com/lhghroom/Self-learning-blockchain-from-scratch
【主要内容】
今天继续学习了解ntf的相关知识,今天继续批注分析从github上获取的erc721合约的代码,添加自己的理解并批注,共耗时32分钟。
(此外整理作笔记花费了约12分钟)
详细学习过程见文末学习过程屏幕录像。
【今天批注的ERC721的智能合约】
文件:
ERC721Base.sol
已批注的部分内容如下:
```
pragma solidity ^0.4.18;
import './SafeMath.sol'; //这是一个library
import './AssetRegistryStorage.sol'; //资产登记所需要的基类全约,其中声明了各种需要登记到区块中的(变量类型为storage)mapping表变量
import './IERC721Base.sol'; //这儿声明了erc721的interface
import './IERC721Receiver.sol'; //在这个文件中声明了一个Interface,声明了让合约可以接收nft的函数onERC721Received
import './ERC165.sol'; //erc165的interface
contract ERC721Base is AssetRegistryStorage, IERC721Base, ERC165 {
//作为基类合约的IERC721Base和ERC165中声明的只是一个interface,但仍然可以被当作基类合约使用。
using SafeMath for uint256; //这儿使用了库 SafeMath.sol中的内容
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
bytes4 private constant ERC721_RECEIVED = 0x150b7a02; //ERC165接口需要的常量,表示 合约是否可以接收 NFT资产的标识
bytes4 private constant InterfaceId_ERC165 = 0x01ffc9a7; //ERC165需要的常量,表示合约是否支持erc165接口
/*
* 0x01ffc9a7 ===
* bytes4(keccak256('supportsInterface(bytes4)'))
*/
bytes4 private constant Old_InterfaceId_ERC721 = 0x7c0633c6;
bytes4 private constant InterfaceId_ERC721 = 0x80ac58cd; //ERC721需要的常量,表示合约是否支持erc721接口
/*
* 0x80ac58cd ===
* bytes4(keccak256('balanceOf(address)')) ^
* bytes4(keccak256('ownerOf(uint256)')) ^
* bytes4(keccak256('approve(address,uint256)')) ^
* bytes4(keccak256('getApproved(uint256)')) ^
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
*/
//
// Global Getters
//
/**
* @dev Gets the total amount of assets stored by the contract 获取当前合约管理着多少个NFT的总数量
* @return uint256 representing the total amount of assets
*/
function totalSupply() external view returns (uint256) {
return _totalSupply(); //通过调用下一个函数到获取
}
function _totalSupply() internal view returns (uint256) {
return _count; //状态变量(全局变量)_count是在sol文件AssetRegistryStorage.sol中声明的
}
//注意到,这儿的每一个方法,都进行了安全隔离,对外,采用external关键字修饰,对内(包括对下级合约——也就是把当前合约当作父级合约的子合约)使用internal关键字。
/*
public与private
对于public和private,相信学过其他主流语言的人都能明白:
public修饰的变量和函数,任何用户或者合约都能调用和访问。
private修饰的变量和函数,只能在其所在的合约中调用和访问,即使是其子合约也没有权限访问。
external和internal
除 public 和 private 属性之外,Solidity 还使用了另外两个描述函数可见性的修饰词:internal(内部) 和 external(外部)。
internal 和 private 类似,不过, 如果某个合约继承自其父合约,这个合约即可以访问父合约中定义的“内部”函数。
external 与public 类似,只不过这些函数只能在合约之外调用 - 它们不能被合约内的其他函数调用。
internal、private、external、public这4种关键字都是可见性修饰符,互不共存(也就是说,同一时间只能使用这四个修饰字中的一个)
————————————————
版权声明:本文为CSDN博主「黄嘉成」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_33829547/java/article/details/80460013
*/
//
// Asset-centric getter functions 查询有关NFT的所有者等相关信息的操作
//
/**
* @dev Queries what address owns an asset. This method does not throw. 传入NFT的ID值:assetId,以查询得到此NFT资产的拥有人是谁。
* In order to check if the asset exists, use the `exists` function or check if the
* return value of this call is `0`.
* @return uint256 the assetId
*/
function ownerOf(uint256 assetId) external view returns (address) {
return _ownerOf(assetId);
}
function _ownerOf(uint256 assetId) internal view returns (address) {
return _holderOf[assetId]; //状态变量(全局变量)_holderOf是在sol文件AssetRegistryStorage.sol中声明的
}
//
// Holder-centric getter functions
//
/**
* @dev Gets the balance of the specified address 查看一个节点(形参 owner指定)拥有总共多少个NFT
* @param owner address to query the balance of
* @return uint256 representing the amount owned by the passed address
*/
function balanceOf(address owner) external view returns (uint256) {
return _balanceOf(owner);
}
function _balanceOf(address owner) internal view returns (uint256) {
return _assetsOf[owner].length; //状态变量(全局变量)_assetsOf是在sol文件AssetRegistryStorage.sol中声明的
}
//
// Authorization getters 获取当前资产授权状态的函数方法
//
/**
* @dev Query whether an address has been authorized to move any assets on behalf of someone else 查询 形参 assetHolder 节点 是否已经把 它的所有NFT资产都授权给了 形参 operator 节点,允许 形参 operator 节点 全权处理(包括移动)这些NFT资产。
* @param operator the address that might be authorized
* @param assetHolder the address that provided the authorization
* @return bool true if the operator has been authorized to move any assets
*/
function isApprovedForAll(address assetHolder, address operator)
external view returns (bool)
{
return _isApprovedForAll(assetHolder, operator);
}
function _isApprovedForAll(address assetHolder, address operator)
internal view returns (bool)
{
return _operators[assetHolder][operator]; //状态变量(全局变量)_operators是在sol文件AssetRegistryStorage.sol中声明的
}
/**
* @dev Query what address has been particularly authorized to move an asset 查询指定ID的NFT资产(形参 assetId 指定) 当前 已经被 授权给哪个 节点 处理。
* @param assetId the asset to be queried for
* @return bool true if the asset has been approved by the holder
*/
function getApproved(uint256 assetId) external view returns (address) {
return _getApprovedAddress(assetId);
}
function getApprovedAddress(uint256 assetId) external view returns (address) {
return _getApprovedAddress(assetId);
}
function _getApprovedAddress(uint256 assetId) internal view returns (address) {
return _approval[assetId];
}
/**
* @dev Query if an operator can move an asset. 查询 一个 节点(形参 operator 指定)是不是已经 被 授权处理 指定ID的NFT(形参 assetId)
* @param operator the address that might be authorized
* @param assetId the asset that has been `approved` for transfer
* @return bool true if the asset has been approved by the holder
*/
function isAuthorized(address operator, uint256 assetId) external view returns (bool) {
return _isAuthorized(operator, assetId);
}
function _isAuthorized(address operator, uint256 assetId) internal view returns (bool)
{
require(operator != 0); //检查 节点地址是否为空
address owner = _ownerOf(assetId); //得到指定ID的NFT本身是属于哪个节点地址的
if (operator == owner) { //如果指定ID的NFT的归属节点就是当前 要查询 的operator 那么,就直接返回TRUE
return true;
}
return _isApprovedForAll(owner, operator) || _getApprovedAddress(assetId) == operator;
//双竖线左边是检查节点owner是否已经授权节点operator处理它的全部NFT资产,双竖线右边是获取指定id的assetId现在的授权处理节点地址是否等于Operator
//如果以上两个条件二者之一符合,则会返回TRUE,否则 返回FALSE
}
//
// Authorization 执行授权等操作的函数方法区域
//
/**
* @dev Authorize a third party operator to manage (send) msg.sender's asset 当前调用合约的节点(msg.sender)调用此方法来向指定的节点(形参 operator 指定)授权允许其 操作自己的所有NFT资产,或撤消这个授权——是发起授权还是撤消授权由形参 authorized 的BOOL值决定
* @param operator address to be approved
* @param authorized bool set to true to authorize, false to withdraw authorization
*/
function setApprovalForAll(address operator, bool authorized) external {
return _setApprovalForAll(operator, authorized);
}
function _setApprovalForAll(address operator, bool authorized) internal {
if (authorized) { //如果是发起授权
require(!_isApprovedForAll(msg.sender, operator));
_addAuthorization(operator, msg.sender); //这个方法在本合约稍后定义的,直接操纵 映射表 _operators 来完成
} else { //如果是撤消授权
require(_isApprovedForAll(msg.sender, operator));
_clearAuthorization(operator, msg.sender); //这个方法在本合约稍后定义的,直接操纵 映射表 _operators 来完成
}
emit ApprovalForAll(msg.sender, operator, authorized); //广播这个事件,事件的定义在sol文件IERC721Base.sol中声明的
}
/**
* @dev Authorize a third party operator to manage one particular asset //这个函数是授权节点Operator可以操作调用合约节点(msg.sender)的指定ID的一个nft资产。
* @param operator address to be approved
* @param assetId asset to approve
*/
function approve(address operator, uint256 assetId) external {
address holder = _ownerOf(assetId); //获取要操作的指定ID的NFT(这儿就是形参assetId)当前的所有者节点的地址
require(msg.sender == holder || _isApprovedForAll(msg.sender, holder));
//上一个语句,左边是检查当前合约调用节点是不是就是此ID的NFT资产的所有人,右边检查,当前调用合约的节点是不是已经把自己的所有NFT资产的操作权授权给了当前操作的指定ID的NFT资产的所有人(现在是变量holder表示)。
//只要两者有其一是true ,语句将会继续 执行,如果两者都是false,则语句就不再继续往下执行。
require(operator != holder); //如果要被授权的节点就是holder节点,那么就没有意义了,所以这儿要检测一下。
//于是下一句话进行了进一步的检查,如果要被授权的节点operator已经是指定id的Nft资产可操作者,那就不必要进行重复授权操作了。
if (_getApprovedAddress(assetId) != operator) {
_approval[assetId] = operator; //直接将映射表_approval中指定ID的NFT资产的被授权操作节点修改为operator就可以了。
emit Approval(holder, operator, assetId); //广播这个更改映射表的事件。
}
}
//--下面这个函数 是为函数 _setApprovalForAll 准备的子函数
function _addAuthorization(address operator, address holder) private {
_operators[holder][operator] = true;
}
//--下面这个函数 是为函数 _setApprovalForAll 准备的子函数
function _clearAuthorization(address operator, address holder) private {
_operators[holder][operator] = false;
}
//
// Internal Operations
//
//下面这个函数把指定ID的NFT资产(形参assetId指定)的 所有人 指定为 to这个节点 (变更资产所有人)
function _addAssetTo(address to, uint256 assetId) internal {
_holderOf[assetId] = to; //在资产所有人的映射表中,变更 此资产的所有人为to节点
uint256 length = _balanceOf(to); //记录下to这个节点现在有多少个NFT资产
_assetsOf[to].push(assetId); //向映射表_assetsOf中的to节点下添加新的NFT资产的ID
_indexOfAsset[assetId] = length; //更改映射表 _indexOfAsset中指定的当前 ID的 NFT资产在to节点自己的资产登记表_assetOf中的Index值,现在发现,这个Index值 是从1开始的,而不是从零开始的。
_count = _count.add(1); //这儿没有理解为什么_count需要增加1!!!!
}
```
github: https://github.com/lhghroom/Self-learning-blockchain-from-scratch
原文地址:
【欢迎大家加入[就是要学]社群】
如今,这个世界的变化与科技的发展就像一个机器猛兽,它跑得越来越快,跑得越来越快,在我们身后追赶着我们。
很多人很早就放弃了成长,也就放弃了继续奔跑,多数人保持终身不变的样子,原地不动,成为那猛兽的肚中餐——当然那也不错,在猛兽的逼迫下,机械的重复着自我感觉还良好地稳定工作与生活——而且多半感觉不到这有什么不正常的地方,因为在猛兽肚子里的是大多数人,就好像大多数人都在一个大坑里,也就感觉不出来这是一个大坑了,反而坑外的世界显得有些不大正常。
为什么我们不要做坑里的大多数人?
因为真正的人生,应当有百万种可能 ;因为真正的一生可以有好多辈子组成,每一辈子都可以做自己喜欢的事情;因为真正的人生,应当有无数种可以选择的权利,而不是总觉得自己别无选择。因为我们要成为一九法则中为数不多的那个一;因为我们要成为自己人生的导演而不是*成为别人安排的戏目中的演员。
【请注意】
就是要学社群并不会告诉你怎样一夜暴富!也不会告诉你怎样不经努力就实现梦想!
【请注意】
就是要学社群并没有任何可以应付未来一切变化的独门绝技,也没有值得吹嘘的所谓价值连城的成功学方法论!
【请注意】
社群只会互相帮助,让每个人都看清自己在哪儿,自己是怎样的,重新看见心中的梦想,唤醒各自内心中的那个英雄,然后勇往直前,成为自己想要成为的样子!
期待与你并肩奔赴未来!
QQ群:646854445 (【就是要学】终身成长)
【原文地址】
https://www.941xue.com/content.aspx?id=1727
【同步语音笔记】
https://www.ximalaya.com/keji/19103006/357046001
【学习过程屏幕录屏】
https://www.bilibili.com/video/BV1NZ4y1N7aG/