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

贝拉儿NG在Unsplash上的“飞鸟在半建筑和工地上飞行”

设计选择,可扩展性和安全性

正如本系列第1部分所讨论的, ERC721标准适用于管理以太坊区块链上不可替代资产的合同。 ERC721代币所代表的资产将影响您的合同工作方式的一些设计选择,最显着的是如何创建新代币。

例如,在Cryptokitties游戏中,玩家可以“培育”他们的Kitty,从而创造出新的Kitty(代币)。 然而,如果您的ERC721令牌代表更有形的东西,比如演唱会门票,您可能不希望令牌持有者能够创建更多的令牌。 在某些情况下,你甚至可能希望代币持有者能够“烧”他们的代币,有效地摧毁他们。

ERC721标准对于如何创建或刻录令牌或谁可以创建它们没有限制,因此您可以根据自己的需要自由做出这些决定。

设计选择

在我的合同中,为了可扩展性和简单性,令牌将以两种方式创建:

  1. 在令牌创建期间将定义初始的令牌供应,所有这些供应最初都属于合同创建者。
  2. 一个只能由合约创建者调用的函数,在调用时会发出更多的令牌。 这些新的令牌最初也属于合同创建者。

你会看到它如何适应不久的可扩展性。 至于刻录,将会有一个函数烧毁一个只能由令牌所有者(或授权的操作员)调用的给定令牌。

可扩展性

由于每个令牌都有一个所有者,因此我将有一个跟踪令牌所有权的映射,定义为:

  映射 ( uint256 => 地址 ) 内部所有者; 

该标准说令牌不能属于零地址( 0x0 ),所以在所有者映射中 ,我们可以让0x0回退到合同创建者。 当我们编写ownerOf函数时,我会详细ownerOf它,但这意味着如果我们发行10个标记, ownerOf每个标记的所有权明确地设置为合同创建者。

为了简单起见,因为属于0x0令牌会回落到合同创建者身上,我不会让令牌合同被转移给其他所有者。

安全

在我们开始之前,我们需要覆盖的最后一件事非常重要 - 防止溢出。 如果你不知道溢出,整数类型有它可以容纳的最大值和最小值。 在uint256的情况下,它可以容纳的最小值是0,它可容纳的最大值是115792089237316195423570985008687907853269984665640564039457584007913129639935。

如果你有一个uint256的值为0,你从它减去1,它的价值将是那个大疯狂的数字。 如果你加1,它将再次为0。 基本上uint不能算更高的数字,所以它只是从底部开始。 将两个数字相乘的结果大于变量可容纳的最大值时,您会得到相似的结果。

如果你不小心,恶意(或愚蠢)的人可以利用这种行为来对你的代码做各种令人讨厌的事情。 就在上周,由于与溢出相关的令牌合约存在漏洞,一些ERC20令牌在许多交易所中被从交易中移除。 有人聪明地找到了一种方法来发送他自己实际上没有的疯狂数量的令牌,然后迅速将它们出售。 对于受影响的人来说,这是绝对的混乱。

但是不要绝望, OpenZeppelin的那些人已经写了一个库来帮助防止这种攻击。 SafeMath.sol用于仔细检查在重要计算过程中没有发生溢出。

通过声明using SafeMath for uint256; 在合同的顶部,这意味着以下计算:

  c = a + b 

存在溢出风险,变成:

  c = a.add(b); 

如果它溢出会抛出。 这比一般的增加成本要高一些,所以我们应该在SafeMath操作存在溢出的可能性(来自不可预知的用户输入 - 包括合同所有者/创建者!)时保存操作。

注意:我在本合同中使用SafeMath的唯一时间是发布额外令牌。 所以如果你的合同设计不同,你可能不需要使用这个库; 然而,这是一个非常有用的项目,在你的工具带上。

合同

我们终于准备好开始编写我们的合同,所以让我们直接去做。 我们将使用Solidity 0.4.22,并列出依赖关系。

 杂注扎实度0.4.22; 
  导入 “./CheckERC165.sol”;  导入 “./standard/ERC721.sol”;  导入 “./standard/ERC721TokenReceiver.sol”;  导入 “./libraries/SafeMath.sol”; 

关于ERC721.sol(ERC721标准接口)的快速注释,我对某些函数的可变性和可见性做了一些细微的修改。 但正如前一篇文章中提到的那样,这是标准所允许的。 我将在他们出现时讨论这些更改,并且所有文件都可以在我的GitHub上找到 。

接下来我们宣布我们的合同 ,记住要扩展ERC721 接口和我们的ERC165实现,并且包括适用于uint256的SafeMath 。

  合同 TokenERC721 是 ERC721,CheckERC165 {  使用 SafeMath for uint256 ; 

变量

在契约顶部声明合同变量总是一个很好的做法,因为它使得阅读起来更容易。 现在我会对他们全部进行闪光,并对每种情况做一个简短的解释,但是当我们真正使用它们时,我会进一步细化。 注意:我使用内部可见性而不是私有的 ,以便扩展我们的令牌合约(如“元数据”和“可枚举”变体)的合约可以访问它们。

  //合同创建者的地址
  解决内部创建者; 
  //最高有效的tokenId,用于检查tokenId是否有效
  uint256内部 maxId; 
  //存储每个地址的余额的映射
  映射 ( 地址 => uint256 ) 内部余额; 
  //   刻录令牌的映射,用于检查tokenId是否有效
  //如果您的令牌无法被烧毁,则不需要
  映射 ( uint256 => bool ) 内部烧毁; 
  //令牌所有者的映射
  映射 ( uint256 => 地址 ) 内部所有者; 
  //每个令牌的“批准”地址映射
  映射 ( uint256 => 地址 ) 内部补贴; 
  //用于管理“运营商”的嵌套映射
  映射 ( 地址 => 映射 ( 地址 => 布尔 )) 内部授权; 

构造函数

接下来我们将定义构造函数。 根据你的令牌需要做什么,你的构造函数很可能与我的不同。 但是,无论如何你仍然应该包括ERC165部分。 我会在之后讨论一些怪癖。

 构造函数( uint _initialSupply) public CheckERC165(){  //存储创建者的地址  creator = msg.sender;  //所有初始令牌都属于创作者,因此请设置天平 余额[msg.sender] = _initialSupply; 
  //将maxId设置为令牌的数量  maxId = _initialSupply; 
      //我们必须为我们创建的每个标记发出一个事件
  for ( uint i = 1; i <= maxId; i ++){ 发送Transfer(0x0,创建者,i);  } 

你们中的一些人可能已经看到了maxId = _initialSupply并立即想到“这是一个错字吗? 或者tokenIds从1开始而不是0? 什么样的反社会人士从1开始索引?!?“

不,这不是一个错字。 我选择在1开始tokenIds的原因是,当您开始向合约添加额外层(超出ERC721标准的范围)时,0非常方便。 由于uint的默认值为0,并且对变量使用删除操作会给出气体退款,因此它可以是对令牌的引用无效的简写方式。 实际上,tokenId 不是索引。 标准只要求每个令牌都有自己独特的uint ,永远不会改变,它并不太在乎你如何确定它们。 引用标准:

“虽然一些ERC-721智能合约可能会发现从ID 0开始并为每个新的NFT简单地增加一个便利,但呼叫者不应假定ID号码对他们有任何特定模式,并且必须将该ID作为”黑色框'。”

您可能还注意到,对于interfaceId ,我使用了模式bytes4 (keccak256(“...用于两个函数签名),这是因为ERC721 Standard 接口重载safeTransferFrom ,所以只调用this.safeTransfeFrom.selector将导致TypeError 。

有效令牌

所有_tokenId作为参数的函数都要求我们检查这个tokenId是否对应于一个有效的标记。 所以让我们编写一个可重用的内部函数来检查。

  函数 isValidToken( uint256 _tokenId) 内部视图返回 ( bool ){  return _tokenId!= 0 && _tokenId <= maxId &&!burned [_tokenId];  } 

如果给定的_tokenId为0,大于maxId或对应于烧毁的标记,这将返回false 。 如果您的令牌不允许刻录,则可以删除&& !burned[_tokenId]部分。

平衡和所有者

接下来介绍两个基本的getter函数balanceOfownerOf 。 他们很简单, 一个返回给定地址的余额,另一个返回给定令牌的所有者。

balanceOf很简单,它只是从我们的余额映射中读取一个值:

  函数 balanceOf( 地址 _owner) 外部视图返回 ( uint256 ){  回报余额[_owner];  } 

ownerOf有点复杂:

  函数 ownerOf( uint256 _tokenId) 公共视图返回 ( 地址 ){ 要求(isValidToken(_tokenId));  如果 (所有者[_tokenId]!= 0x0){  返回所有者[_tokenId];  } else {  回报创造者;  }  } 

首先我们使用我们之前的isValidToken 函数来检查它是否是有效的标记。因为我使用0x0的所有者将令牌返回给合同创建者,所以我还必须为此添加支票。 但取决于您的合同设计,您可能可以省略此检查并仅返回owners[_tokenId] 。

还要注意,我在这里和界面中都改变了可见性,从外部到公共 。 这是因为我们的ownerOf函数最终会在我们的合同中被相当频繁地调用,而且如果它不是外部函数,则会更便宜。

问题和烧伤

我今天要添加的最后一件事就是我的实现的问题和刻录功能。 除非您正在复制我的实现(欢迎您这样做),否则这些内容不太可能有用。 然而,为了让我可以把下一篇文章的全部内容用于审批,运营商和转账,我现在将介绍他们:

  函数 issueTokens(uint256_extraTokens) public {  //确保只有合约创建者可以调用它  require(msg.sender == creator); 余额[msg.sender] =余额[msg.sender]。  添加 (_extraTokens);  maxId = maxId。  添加 (_extraTokens);  //我们必须为每个创建的标记发出一个事件  for ( uint i = maxId  -  _extraTokens + 1; i <= maxId; i ++){ 发送Transfer(0x0,创建者,i);  }  } 

请注意,在这两种情况下,我都使用了SafeMath的add函数。 否则,一个足够大的_extraTokens值可能会导致各种破坏。

  函数 burnToken( uint256 _tokenId) external {  地址所有者= ownerOf(_tokenId); 要求(所有者== msg.sender  || 津贴[_tokenId] == msg.sender  || 授权[所有者] [msg.sender]  ); 烧毁[_tokenId] = true ; 结余[所有者]  - ; 
 发射传输(所有者,0x0,_tokenId);  } 

不要过于担心allowanceauthorised ,我会在下一篇文章中详细解释它们。 我们也会在那里看到这个确切的模式,所以我可以用更多的上下文来解释它。

包起来

今天我们介绍了一些设计选择,以及了解溢出的危险以及如何防止溢出。 如果你一直在和我一起写自己的合同,现在应该开始形成。 我们已经基本建立了框架,接下来要做的就是添加移动部分。

在下一篇文章中,我们将通过添加批准,操作员和传输函数的不同变体来完成我们的合同。

https://medium.com/coinmonks/jumping-into-solidity-the-erc721-standard-part-3-5f38e012248b

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

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

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

  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. [转]Iphone NSString字符串常用方法
  2. Codeforces614D Skills
  3. 深入理解分布式技术 - 先更新数据库,还是先更新缓存
  4. git错误fatal: remote origin already exists.
  5. Linux字符设备驱动框架
  6. CodeForces - 817F Graph and String(dfs判二分图)
  7. Apache-Jmeter监控服务资源
  8. mysql替换sql中rank函数_MySQL sql Rank()函数实现
  9. centos5.5 挂载NTFS优盘
  10. html常用的符号实体
  11. IDEA springboot maven 项目部署
  12. 如何测试硬盘软件,win10系统怎么用硬盘检测工具HDTune【图文】
  13. h5 android上传本地视频文件,关于webview适配H5上传照片或者视频文件的方法
  14. 哈希表算法通俗理解和实现
  15. 陈艾盐:《春燕》百集访谈节目第三十七集
  16. java货郎担问题求解_货郎担问题(TSP)
  17. 长沙“一江两岸”新网红,看铜官古镇的“文和游”
  18. clamp.js 使用
  19. html中url表示方法,HTML网页中的URL表示方式介绍
  20. Mac下修复Node版本不正确的问题

热门文章

  1. BZOJ3626 LNOI2014 LCA 树链剖分
  2. C# App.config全攻略
  3. Android自动化测试 - 自动化测试工具比较
  4. QUrl不同版本之间的坑
  5. matlab中svd函数用法总结
  6. 吴恩达 coursera ML 第九课总结+作业答案
  7. 基于VTK User Guide和VTK Textbook学习
  8. [Python语音识别项目笔记] 2矩阵标准化和去标准化
  9. [云炬python3玩转机器学习笔记] 3-12 数据加载和简单的数据探索
  10. [C++调试笔记]推动粒子move.cpp