在我上一篇文章中,我们开始编写ERC721合同。 在前三篇文章( 第1部分, 第2部分 , 第3部分 )中有很多序言和解释,所以我会假设你在家中玩耍并直接跳回到代码中。

照片由RémiJacquaint在Unsplash上拍摄

代码!

在上一篇文章的最后,在我的burnToken函数中,您可能注意到我引用了我们在合同开始时声明的两个映射, allowanceauthorised 。 这两个都用于管理批准,这基本上是一个系统,用于控制您的令牌给其他人,同时保留它的所有权。

有两种形式的批准 ,一种授予某人权限来控制一个特定的令牌 ( allowance) ,另一种授予他们控制所有令牌( authorised )的authorised 。 这两种批准都有自己的功能,并且它们的工作方式彼此不同,因此我们将分别进行介绍。

合法

授权在ERC721标准中由两个功能描述,

  函数 setApprovalForAll( 地址 _operator, 布尔 _批准) 外部 ; 

它设置谁有权控制​​所有令牌(让这些人员操作员 ),以及

  函数 isApprovedForAll( 地址 _owner, 地址 _operator) 外部视图返回 ( bool ){  返回授权[_owner] [_运营商];  } 

它读取数据。 这是我们在合同开始时宣布的authorised 映射派上用场的地方。 如果你忘记了,那个映射被声明为:

  映射 ( 地址 => 映射 ( 地址 => 布尔 )) 内部授权; 

如果你以前从未见过嵌套映射,那实际上很简单。 在这种情况下,每个拥有者地址映射到它自己的映射,映射将操作员地址 es映射到bool,说明它们是否实际上是操作员。

所以从这个嵌套映射读取只需要以下结构,

 授权[所有者] [操作] 

如果operatorowner的操作员,则评估为真 ,否则为false 。

事实上,这正是我们的isApprovedForAll函数的工作原理,它只是从这个映射中返回一个值:

  函数 isApprovedForAll( 地址 _owner, 地址 _operator) 外部视图返回 ( bool ){  返回授权[_owner] [_运营商];  } 

同样,我们的setApprovalForAll函数只是在这个映射中设置一个值。 唯一的另一个要求是我们的函数“发出ApprovalForAll事件” ,它在标准接口中定义为

  事件 ApprovalForAll( 地址索引_owner, 地址索引_operator, 布尔 _批准); 

所以我们的setApprovalForAll函数很简单,

  函数 setApprovalForAll( 地址 _operator, 布尔 _批准) 外部 {  发出 ApprovalForAll(msg.sender,_operator,_approved); 授权[msg.sender] [_运营商] = _批准;  } 

津贴

津贴也由两个功能来描述,

  功能批准( 地址 _approved, uint256 _tokenId) 外部应付款 ; 

其中设置了谁被批准用于给定的令牌,并且

  函数 getApproved( uint256 _tokenId) 外部视图返回 ( 地址 ); 

读取这些数据。 最重要的是要注意的是,对于任何给定的令牌,在任何给定时间只能有一个授权地址。 如果您为某个令牌批准某人,然后批准其他人,则第一个人将不再被授权使用该令牌。

与我们处理运营商的authorised 映射类似,我们authorised处理单令牌批准。 刷新你的记忆:

  映射 ( uint256 => 地址 ) 内部补贴; 

这里没有花哨的技巧,它只是每个tokenId到其认可地址的映射。 如果它没有一个,它的计算结果为0x0 。

我们的getApproved函数的两个要求是, 如果 _tokenId 不是有效的NFT ,它会抛出 ,并返回“此NFT的批准地址或无地址的零地址” ,这给了我们:

  函数 getApproved( uint256 _tokenId) 外部视图返回 ( 地址 ){ 要求(isValidToken(_tokenId));  返回津贴[_tokenId];  } 

为了取得批准,我们有我们的approve功能。 请注意,我已将可变性从应付款更改为隐式不付款 - 我也在界面副本中进行了相同的更改。 我们不需要从这个功能付款。

  函数批准( 地址 _approved, uint256 _tokenId) external { 

该标准对于我们的approve功能具有以下条件,

“除非`msg.sender`是当前的NFT所有者或当前所有者的授权运营商,否则会抛出。”

我们首先获取ownerOf的结果并将其存储在一个临时变量中。 如果你从前面记得, ownerOf比读取变量稍微复杂一点,所以这比每次我们需要所有者地址时重新调用ownerOf便宜。

  地址所有者= ownerOf(_tokenId); 

那么我们根据需求检查msg.sender是否是所有者或操作员。 请注意,我直接读取authorised数组而不是调用isApprovedForAll ,这只是另一种节省燃料的措施。

  要求 (所有者== msg.sender  || 授权[所有者] [msg.sender]  ); 

最后我们要发布一个事件 ,我们必须实际更新allowance 映射 。

  发出批准(所有者,_批准,_tokenId); 津贴[_tokenId] = _批准; 

传递函数

我们合同中最后也是最重要的部分是转让职能。 技术上有三种,但他们基本上都是做同样的事情,除了其中两个有一些额外的功能。 让我们从最简单的开始,

从转移

顾名思义, transferFrom函数用于将令牌从一个地址传送到另一个地址。在我们进一步讨论之前,请注意我已将可变性从支付变为隐性不支付(因为我们不需要用这个支付),并且我还将外部公开性的可见性更改为(因为transferFrom将被我们的其他传输功能重新使用,因此公开将节省燃气)。

  函数 transferFrom( 地址 _from, 地址 _to, uint256 _tokenId) public { 

接下来是这个函数的一些要求,如果它们没有被满足,会导致它抛出。 让我们开始获取令牌的owner因为我们将在检查中使用它,而且我们的ownerOf函数实际上包含检查tokenId是否有效的检查,这是transferFrom的要求之一:

  地址所有者= ownerOf(_tokenId); 

接下来,我们有“ 抛出,除非`msg.sender`是当前所有者,授权运营商或此NFT的批准地址”,即:

  要求 (所有者== msg.sender  || 津贴[_tokenId] == msg.sender
      || 授权[所有者] [msg.sender]
  ); 

我再次直接访问映射,而不是使用我们之前编写的批准函数,因为它更便宜。

接下来, “如果`_from`不是当前所有者 ,则抛出。”只是:

  需要 (所有者== _from); 

而我们的最后一个检查,“ 如果_to`抛出是零地址。” :

  require (_to!= 0x0); 

就我个人而言,我喜欢在做检查后立即发布任何事件 ,但是现在或最后做这些事情并不重要。

  发出转移(_from,_to,_tokenId); 

接下来我们更新我们的owners 映射以反映新的所有者,

 所有者[_tokenId] = _to; 

并调整_to_from 地址的余额,

 结余[_from]  - ; 结余[_to] ++; 

请注意,在这种情况下不需要SafeMath,我们的合约逻辑可确保没有任何余额为0的人可以达到此点,这意味着他们无法溢出余额。

剩下的唯一要做的就是重置这个令牌的allowance 。 现在令牌已经转移,新的所有者需要决定谁能控制它。

  if (allowance [_tokenId]!= 0x0){  删除津贴[_tokenId];  } 

Transfer 事件意味着我们重置了配额,所以不需要发出Approval 事件 。

safeTransferFrom

safeTransferFrom函数与transferFrom几乎相同,只是它们检查收件人是否是有效的ERC721接收方合同,如果是,则让您将某些数据传递给该合同。这个函数有3个参数的版本和4个参数的版本,但是3个参数只是调用4个参数,最后一个参数为空,所以我们将从4个参数开始。

再次注意,我已经将外部应付款的可变性和可见性修改为公开的和隐含的不付款,这是因为气体原因,并且我们不需要付款。

  函数 safeTransferFrom( 地址 _from, 地址 _to, uint256 _tokenId, 字节数据) public { 

我们要做的第一件事就是调用我们的transferFrom函数,这将对我们执行大部分检查并转移令牌。

  transferFrom(_from,_to,_tokenId); 

接下来我们必须满足safeTransferFrom的附加条件,

传输完成后,此函数检查`_to`是否是智能合约(代码大小> 0)。 如果是这样,它调用`_to`上的`onERC721Received`,并在返回值不是`bytes4(keccak256(“onERC721Received(address,uint256,bytes)”))`时抛出。

我们将不得不在这里使用一些程序集 ,幸运的是有一个操作码可以返回给定地址的代码大小。 我从来没有使用过汇编,因为所有这一切都是在检查_to的代码大小并将其存储在size 。 然后我们安全地回到Solidity的界限。

  uint32大小;  组件 {  size:= extcodesize(_to)  } 

如果大小为0,则该地址不属于合同。 但是如果确实如此,我们需要在本合同上致电onERC721Received ,并要求它给我们正确的回应:

  if (size> 0){  ERC721TokenReceiver接收器= ERC721TokenReceiver(_to);  require(receiver.onERC721Received(_from,_tokenId,data)== bytes4 (keccak256(“onERC721Received(address,uint256,bytes)”)));  } 

这是transferFromsafeTransferFrom之间的唯一区别。 唯一剩下的就是3参数版本,它只是将最终参数的4参数版本调用为空字符串。 再次注意到我已将可变性从支付变更为隐式不支付:

  函数 safeTransferFrom( 地址 _from, 地址 _to, uint256 _tokenId) 外部 {  safeTransferFrom(_from,_to,_tokenId, “”);  } 

包起来

瞧! 您刚完成编写您自己的ERC721实施合同,很不错! 如果您没有在家玩过, 可以在我的GitHub上找到这份合同的副本供您使用。 我希望你到目前为止已经发现了这个系列的信息,并且不要失望,因为还有更多!

下一次我们将测试我们的合同,因为如果代码没有经过测试,代码就不好,我也将在以后的文章中运行元数据和枚举扩展。

https://medium.com/coinmonks/jumping-into-solidity-the-erc721-standard-part-4-ad21e3a5d9c

【译】Jumping into Solidity — The ERC721 Standard (Part 4)相关推荐

  1. 【译】Jumping into Solidity — The ERC721 Standard (Part 3)

    到目前为止,在本系列中,我介绍了非易失性和ERC721的基础知识 ,然后介绍了标准接口及其中的一些要求 . 在本文中,我们将对我们的ERC721合同做出一些设计决定,并开始编写它. 贝拉儿NG在Uns ...

  2. 【译】Jumping into Solidity — The ERC721 Standard (Part 2)

    在本系列的第一篇文章中 ,我介绍了非真伪令牌(NFT)的概念以及对ERC721(草案)标准的需求 . 在本文中,我们将首先介绍ERC721标准接口,并分解一些要求. 关于ERC165标准还将有一个简短 ...

  3. 【译】Jumping into Solidity —The ERC721 Standard (Part 1)

    本文不是Solidity的介绍,所以我会假设你知道什么是区块链,以太坊是什么,以及Solidity的基础知识. *(如果你是初学者,请参阅脚注). 在接下来的几篇文章中,我将探索ERC721标准,它是 ...

  4. [译] 标准化的包布局(Standard Package Layout)

    原文地址:Standard Package Layout 原文作者:Ben Johnson 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m- 译者:steinlib ...

  5. 十一课堂|通过小游戏学习Ethereum DApps编程(4)

    2019独角兽企业重金招聘Python工程师标准>>> 在上篇完结的时候,我们制造出了这个独一无二可爱至极的角色: 这里我们继续总结一些关于solidity语言的知识点.并且开始了解 ...

  6. 非你莫属 java全球排名_TIOBE:2018年5月全球编程语言排行榜

    原标题:TIOBE:2018年5月全球编程语言排行榜 TIOBE 刚刚发布了 5 月编程语言排行榜.Scala 经过长时间的沉淀,本月终于进入了 TIOBE 榜单的前 20 名.Scala 是一种 J ...

  7. TIOBE 6 月编程语言排行榜:Java 放缓,C 复兴了!

    一月一更的 TIOBE 编程语言社区最新发布了 2018 年 6 月排行榜. 和 5 月榜单相比,前十名没有任何变化,依然是 Java.C.C++.Python.C#.Visual Basic .NE ...

  8. TIOBE 5 月编程语言排行榜:暴涨的 C,逆袭的 Scala

    点击上方"CSDN",选择"置顶公众号" 关键时刻,第一时间送达! TIOBE 刚刚发布了 5 月编程语言排行榜. 对比 4 月榜单,这个月的排名并没有太大变化 ...

  9. 计算机考研四大名著,2018考研英语翻译每日一句:四大名著

    考研英语翻译部分在试题中分值占10%,建议同学们还是要重视起来,为自己争取更多的分数.新东方在线考研为大家搜集整理有关社会生活.科普知识.文化教育等方面的相关句子,以此拓展大家的视野.一起来学习吧! ...

最新文章

  1. 商丘学院计算机专业分数线,商丘学院录取分数线2021是多少分(附历年录取分数线)...
  2. ndroid网络(4):HttpClient必经之路----使用线程安全的单例模式HttpClient,及HttpClient和Application的融合...
  3. 【Linux】一步一步学Linux——watch命令(135)
  4. 解决卸载时残留目标文件夹的问题
  5. 大数据从哪里来?| 技术头条
  6. Java集合:什么是Java集合?
  7. python中tolist_python 列表,数组,矩阵两两转换tolist()的实例
  8. python_vlc 播放http流
  9. 英语计算机试卷二,计算机专业英语模拟试题2参考答案.doc
  10. jupyter 代码到 pycharm 的迁移
  11. modbus地址扫描_西门子PLC通信编程MODBUS通信举例
  12. Sort Integers by the Number of 1 Bits
  13. stata面板数据:xtserial下载问题解决
  14. okhttp返回内容乱码_tomcat服务器接口返回的数据乱码
  15. ClientKey实现登录QQ空间,并设置背景音乐
  16. 海外苹果ID游戏下载教程
  17. 计算机软考中级网络工程师考点总结——待续
  18. H77、Z75、Z77主板有什么区别?
  19. 实验六、contentprovider实验+SQLite数据库的实现
  20. 2021-05-05 数组、 元组、字典、字符串常见操作

热门文章

  1. linux上安装samba
  2. linux中使用ssh或scp时如何跳过RSA key fingerprint输入yes/no
  3. CUDA占用率计算方法
  4. 纯Python包发布setup脚本编写示例
  5. tensorflow object detection API训练错误解决
  6. Java基础----对象与类
  7. 论文阅读:Learnable pooling with Context Gating for video classification
  8. [云炬创业基础笔记]第二章创业者测试2
  9. 是什么在吞食我们的科研时间2019-11-24
  10. 吴恩达《Machine Learning》精炼笔记 11:推荐系统