一个NFT可以在存在owner授权的operator的情况下,由owner转账给其他账户地址吗?

ERC-1155是ERC-20和ERC-721的升级规范,它允许在一个交易中发送多种不同的代币,就像同时转账人民币和美元。ERC-1155以在区块链游戏中的广泛使用而闻名,但它其实也适合有很多其他的应用场景。在这个教程中我们将学习ERC-1155规范约定的主要接口,并利用openzepplin实现一个用于航空业的ERC-1155多重代币。

ERC-1155引入的关键功能是多代币支持。让我们通过航空代币来理解这一点的重要性。

假设我们处在一个世界上,每个航空公司都有自己的代币。现在,我想预订一架环球航班,其中有来自三家不同航空公司的三架转机航班。要支付机票,我需要进行三笔单独的交易,并分别为每笔交易使用不同的代币付款。

这个过程中许多事情可能会出错。如果我为航班1付费,然后已经预订了航班2怎么办?现在,我必须找到一个新的连接。在具有集中货币的现实世界中,这项工作是由在线旅行社(OTA)或更传统的传统旅行社完成的。但是,在我们的示例中,必须创建一个新的智能合约,该合约可以连续执行所有三笔交易,将其部署,然后调用交易功能。只为一次飞行就这么付费实在是有点复杂,不是吗?

好在我们可以创建了一个包含多个航空公司的单独代币的单一多代币合约,这正是ERC-1155允许我们执行的操作。我们可以添加任意种类的代币,而不仅仅是一个航空公司的代币。下面让我们更深入地了解ERC-1155的功能。

  • 批量转移:一次调用即可转移多个资产。
  • 批次余额:一次调用即可获取多个资产的余额。
  • 批量批准:批准所有令牌到一个地址。
  • EIP-165支持:声明支持的接口。
  • 钩子接口:提供代币接受钩子接口。
  • NFT支持:如果供应量仅为1,则将其视为NFT。
  • 安全转移规则:安全转移的规则集

批量转账与常规ERC-20转账非常相似。让我们看一下常规的ERC-20 transferFrom函数:

ERC-1155的唯一区别是,我们将值作为数组传递,并且还传递了id数组。例如,给定_ids= [3,6,13]和_values= [100,200,5],则转账结果为:

在ERC-1155中只有transferFrom,没有transfer。要将其像ERC20转账一样使用,只需将from地址设置为调用该函数的地址即可。

  • 为什么将其称为“安全” -BatchTransferFrom? -有关安全转移规则,请参见下面的第7节。
  • 为什么不返回布尔值?-交易失败时将回滚,与我们之前讨论的SafeERC-20实现相同。
  • 什么是字节数据字段?-就像ERC-777一样,你可以传入任意数据,这些数据也将传递给接收钩子。

对于余额查询来说,甚至更简单,我们可以在一个调用中检索多个余额。传入所有者数组,然后传入代币ID数组。

批准授权与ERC-20略有不同。在ERC1155中无需设置授权金额,只需调用setApprovalForAll将操作员设置为批准或未批准即可。

可以调用isApprovedForAll方法读取当前的授权状态。如你所见,它是全部或全部。你无法定义要批准的代币数量,也不能指定授权哪种代币。

这是有意的设计,目的是保持使用的简单性。你只能批准一个地址的所有内容。如果需要对特定批准进行更精细的控制,请查看EIP-1761。

如果还不了解EIP-165是什么,请查看。简而言之,
EIP165规范了智能合约如何声明其支持的接口。例如,智能合约是否支持接收ERC-1155代币?如果是这样,则相应的supportsInterface功能必须存在,并且对于该合约返回true。

这直接将我们带到了下一个概念:钩子。

有了EIP-165支持,ERC-1155仅支持智能合约的接收钩子。钩子函数必须返回一个预定义的4字节magic值,该值指定为:

当接收合约返回此值时,假定合约接受转账并知道如何处理ERC-1155代币,那么合约就不再会出现卡死的代币了。

如果代币的发行量为1,那么这种代币本质上是非同质化代币(NFT)。按照ERC-721的标准,你可以定义元数据URL。客户端可以读取和修改该URL,详细内容请参见。

在前面的说明中,我们已经谈到了一些安全的转账规则。但是,让我们看一下最重要的规则:

  • 必须批准调用方可以消费该_from地址持有的代币,否则调用方必须为_from。
  • 在出现如下状况时,转账交易必须回滚:
  • 代币持有人在_ids中的任何余额都低于_values发送给接收者的相应金额。

注意:包括钩子在内的所有批处理功能也作为不带批处理的版本存在。这样做是出于提高gas效率的考虑,考虑仅转移一种资产仍可能是最常用的方式。为了简单起见,我们省略了它们。名称相同,只需删除Batch前缀即可。

我们之前的航空公司示例可以这样实现:

这就是我们所需要的。现在可以调用:

一次交易就可以支付各航空公司的代币。棒极了!


ERC721标准包括四个接口:

ERC721必须满足的条件

  • 如何处理Token的所有权

  • Token如何转账以及如何允许其他地址具有转账的能力

从所有权来看,通过一个映射,来实现token余额和用户地址的对应

如果用户购买了ERC20 token,用户最终对token的所有权可以通过合约来验证,因为在用户购买token时,合约里保有一条记录表明每个地址拥有多少tokens。

由于ERC721是不可分的,所以ERC721不能像ERC20一样映射到一个地址,而必须知道拥有的每一个唯一的token。所以在ERC721中,所有权是由映射到一个地址的一个token的索引/ID的数组决定的。因为每一个token的值唯一,而不能只看token的余额,必须仔细检查合约创建的每一个token。主合约必须保有所有合约创建的token的列表。每个token都有各自的索引号,定义在合约的allTokens数组。

同时为了知道一个地址拥有哪些token,需要把token的索引和数量映射到一个地址上。

同时为了了解token属于哪个地址,需要将tokenId和地址映射起来

为了针对要把一个token从一个地址拥有列表里删除的需求,必须追踪一下信息。ownedTokensIndex映射把每个token ID映射到它们所有者数组相应的位置/索引上。同时,把token ID映射到全局的allTokens数组上。

同时引入一个变量来追踪一个地址拥有多少ERC721 token

在ERC20标准里,有一个变量**totalSupply_来记录所有可用token的供应量。构造函数用来设置变量的初始值,所有权等。同时引入mint()**函数用来处理增加token发行的需求。(在mint函数中需要更新totalSupply_)

对于ERC721来说,因为每个token都是唯一的,所以必须手工创建每一个token。在ERC721合约中有关于总供应量的两个函数addTokenTo()和_mint()。

  • 更新所有全局的所有权变量

函数有两个参数:to或者拥有token的账户地址和tokenId或者token的唯一ID。

2.设置所请求的token ID的所有者,并且更新那个账户拥有token的数量

  1. mint()函数首先跳到基类的合约实现里,保证铸币地址不是0

ERC721中的元数据的作用是什么呢?已经创建了token和token ID,但是还没任何数据。OpenZeppelin提供了一个例子,说明如何将一个token ID映射到URL字符串。

1.首先通过mint()函数得到tokenID和URL信息,便可以设置数据

注意:在设置数据前,必须要确认一个token ID存在

在ERC20标准中,有一个全局变量allowed,表示一个所有者的地址被映射到一个已授权的地址并且同时被映射到token的数量。为了设置这个变量,在approve()函数里,可以映射授权到期望的spender和value。

一旦允许另外一个地址来传输token,具体运输过程如下

1.已授权的spender使用transferFrom()函数,其中,函数的参数from代表原始的所有者地址;to代表接受者地址;value代表token数量

2.要检查最初的所有者确实拥有请求数量的token

对于ERC721标准,通过token ID来授权使用approve()函数。tokenApprovals是一个全局变量,把一个token索引或者ID映射到一个地址上,而这个地址是获得授权可以传输token的。

  • Token必须被一个特定地址所拥有
  • 在全局变量operatorApprovals中,所有者的地址被映射到一个被授权的spender的地址,然后再被映射一个bool变量,这个变量默认为false,但使用setApprovalForAll()函数可以设置这个映射为true,并且允许该地址来处理所有拥有的ERC721

在完整的实现中,有两种方法

  • 1.当确认发送者和接受者的地址都是合法的后,clearApproval()函数被用来删除token原来的拥有者授权,也就是原来的拥有者不再拥有授权的权限,从而使以前获得授权的spender不再能够传输token。

    3.另外,需要把所有者的ownedToken数组里的最后一个token移到被传输的token的索引位置,同时将数组长度减一

外部拥有的账户可以使用ERC721的完整合约来交易token;但是如果发送token到一个合约而这个合约没有函数通过原有的ERC721合约来交易和传输token的化,token就会丢失并且没有办法找回。针对这个问题,提出了ERC223,ERC223实在ERC20的基础上进行了改进来防止这种错误的传输。

ERC721Holder.sol是钱包的一部分,也是一个拍卖或者经纪人合约。

EIP165的目标是创建一个标准用来发布和发现一个智能合约实现了哪些接口。那么如何来发现一个接口?

实用函数签名来验证在合约的字节码中是否被使用,从而判断函数是否被调用。

合约里每一个函数都有独有的签名,并且当调用合约时,EVM使用一系列的switch/case语句来找到函数签名,通过函数签名来找到所匹配的函数,然后执行相应的函数。

*@dev 如果接收到NFT则返回魔术值,魔术值等于 *@notice处理函数当收到一个 *这个函数可能抛出异常,导致回退以及拒绝Transfer *这个函数可能使用20000GAS。如果返回的不是魔术值,则必须回退 *@para_data 额外数据,没有指定的数据格式

现在ERC721Holder合约还不是一个处理ERC721 token的完整的合约。这个模块用来提供一种标准的方法来验证ERC721Receive标准接口是否被使用。需要继承或者派生ERC721Holder合约来调用钱包或者拍卖合约里的代码来处理ERC721 token。甚至对于代管的token,也需要这样的功能来调用合约函数来在需要的时候从合约里转出token。

下面讨论safeTransferFrom()函数的工作原理。可以选择选项1来传输token,这种情况下调用safeTransferFrom()函数不需要任何参数;也可以选择选项2,用参数bytes_data。与此类似,transferFrom()函数被用来把token的所有权从from地址转到to地址。同时还调用了checkAndCallSafeTransfer()函数,首先通过AddressUtils.sol库包来检查to地址是否是一个合约地址。可以通过isContract()函数来理解实现过程。在确认to是否是一个合约地址后,检查onERC721Received()的函数签名是否符合期望的接口的标准接口。如果不匹配的话,则transferFrom()函数就会吊销,因为判定to地址上的合约没有实现所期望的接口。

对于ERC20标准,因为只是操作一个映射的余额,因此只需要销毁一个特定地址的token。地址可以是一个用户或者合约地址。在下面的burn()函数中,通过value变量来指定住呢比销毁的token的数量。要销毁的token的拥有者由msg.sender指定,所以必须要更新他们的地址余额,然后减少token的总供应量totalSupply。这里Burn和Transfer是events

3.删除和token相关联的元数据

4.最后,就像删除token的所有权,重排allTokens数组,用数组里的最后一个token代替tokenId索引位置

钱包应用必须要实现钱包接口。一个合法的ERC721TokenReceiver需要实现函数:

一个非法的Receiver要么不是先那个函数,要么返回其他任何内容,下面是一个合法的返回:

下面的示例是一个非法的返回

元数据扩展给代币合约一个名字和代码(如ERC20 token),并且给每个代币一些额外的数据使代币独一无二。可枚举的拓展使对代币的排序更容易,而不仅仅通过tokenID来排序。元数据拓展是可选的,元数据接口允许智能合约获得不可分通证的元数据,如名字以及其他详细信息。

Metadata扩展由以下三个函数组成:

//循环并且将字节加入数组

ERC165只有一个函数,用来检查合约的指纹是否和指定的接口的指纹相符

///@notice 查询一个合约是否实现了某个接口 ///@return 如果合约实现了指定的接口,则返回‘true’

本篇文章我们简单介绍了的一些基础概念,并针对没有编程基础的同学介绍了编程中常见的概念。和大家一起分析了个简易版的智能合约帮助大家了解智能合约的整体结构。最后结合智能合约比较热门的应用NFT了解了接口的概念,再次讨论了什么是上链。

从抽象概念上讲,智能合约是一种可以自动化执行的规则,现实生活中的合约,合约制定完成后需要有专门的执行角色,而智能合约将这个步骤自动化,只有满足智能合约中制定的条件就会被执行。

从具体概念上讲,我们之前有一篇文章讲中提到每个账户都有状态树,而智能合约的目的就是通过一系列逻辑执行触发合约账户的状态变化以及通过交易改变外部账户的状态,关于合约账号和外部账户可见""一文。

编程语言的一些基础概念

编程语言中,变量起着重要作用,用来存储内存数据。变量内存储的数据有各种各样的类型,有的是简单的整型,字符串,有的是较为复杂的集合类型,比如列表,映射表或上自定义的类型。变量还有1个很重要的概念就是作用域。

作用域代表变量的生命周期,是函数级别-函数执行完就销毁,还是对象级别,依附对象销毁就会销毁,或又是全局级别,只有程序在运行就一直都在。在智能合约中合约中声明的变量都是存储在状态树中,所以只要合约账户存在,这个变量就存在。这一点认识非常重要,后面在讲NFT的时候会讲1个误区。

函数代表着逻辑的执行,函数的输入有几种 1。可以访问的内存 磁盘 网络中的输入 2.调用函数者传入的参数,函数的输出也是有2种 1.可以访问的内存 磁盘 网络的输出 2.函数返回值 。

有一种纯函数的概念 即 输入只包含了调用函数者传入的参数,输出只包含了返回值。

在智能合约中,非依赖库所有能够让外部账户或者合约账户调用的函数都不应该是纯函数,否则就是对gas的浪费。

如上图,智能合约由外部账户发起调用,合约直接也可以相互调用,每个合约内部执行函数转移状态。

1.合约变量,即状态每个状态代表了什么含义。

2.函数 即合约中的函数是如何运转的,又是如何影响状态变化。

// 这声明了一个状态变量,为每个可能的地址存储一个 `Voter`。

这3个变量就是这个智能合约的状态

function开头的代表是一系列函数就是和前面说到的3个状态交互

如上图所示,智能合约中对外可调用的函数围绕着如何修改 或 读取状态而来,如同文章开始所讲到的,智能合约执行的过程就是以太坊中状态树变化的过程。

智能合约相比于其他编程非常要注意的1点就是运行逻辑需要收取gas费用,这使得合约编写过程要额外注重代码的高效性,尽可能地减少冗余代码。

gas费用存在的原因以及gas费用为什么高昂,之前中有讲,简单来说gas费用存在是因为计算需要耗费资源,存在gas费用可以避免恶意消耗资源的情况发生。gas费用高昂主要是因为竞争激烈。

gas费用计算可以看这里

这是一个函数的修饰符,只有被payable修饰过的函数才可以在调用时附带ETH。

调用到智能合约没有的函数时会走到fallback函数, 如果没有写fallback会抛出异常,如果是涉及到附加ETH,fallback必须被payable修饰,否则也会抛出异常

对合约单纯转账会进入这个函数,前提是有这个函数,如果没有会进入被payable修饰的fallback函数,否则会抛出异常。

可以看到合约语言无不透露着对交易属性,这在一般的编程语言中是不会看到的。

合约一旦发布,即使存在漏洞就不可更改,所以对于逻辑要格外严谨。我们来看看1个常见漏洞

上面的代码中A给B转账会走到B的fallback函数,而B的fallback函数又调用A的转账函数,这样会导致A向B不停转账。

改进办法就是先修改状态再转账

NFT是最近比较热门的话题,很多人认为NFT代表了数字化的确权,也有人认为目前就是一张jpg图片,我们这里不讨论他的经济价值,讨论NFT的技术实现。在每一组NFT背后都是一份智能合约。这些智能合约都遵守着相同的规范。

上面的函数就是NFT智能合约需要遵守的规范- ERC721。

在编程语言中有个概念-接口。之前讲过函数的概念,而接口就是一组函数声明,要实现类似的东西大家都要有个规范,于是乎就有了接口。接口不负责具体实现,只是告诉你要实现什么能力,才能够满足规范。

我们从接口中挑1个大家有过讨论的函数看看。

这个函数目的是给这组NFT某个编号 设置一个url,这个url我们可以简单地理解为是一张图片(先简单理解)。这个接口的出现导致了,如果可以成功调用该接口,那么你高价购买到的NFT将有可能变成另外1张没有任何艺术价值的图片。

有的说法是这个url没有上链,如果上链了就不会有这个问题。所以我们接下来讨论下什么是上链。

当我们讨论上链时,什么是上链

很多人讨论链上的数据不可被更改,那到底什么是上链呢?在之前文章中曾经提到以太坊当中的状态树,只要存在这棵状态树里面的数据都是上链了的。而智能合约中每个合约状态都是存在状态树中的,所以只要是合约中声明的变量都可以称为上链 。另外1个问题,链上数据是否不可更改呢?准确回答说按照规则就可以更改,不按照规则就不可以更改。什么是按照规则,按照规则就是如果智能合约中有对应修改这个状态的函数,满足调用函数的条件,就可以改掉它!

回到NFT url的问题,这个url肯定是存到状态树上了,毋容置疑也是上链了的,但就因为暴露了这个接口所以才导致可以被更改。

这里另外提1个NFT url篡改的风险在于同1个url内映射的文件也可能被更改,这里就需要用到不可变更的存储,如ipfs等,后面我们专门有一篇内容来分析分布式存储。

本篇文章我们简单介绍了智能合约的一些基础概念,并针对没有编程基础的同学介绍了编程中常见的概念。说到学习代码唯有2招最为有效,1.多读代码 2.多写代码 所以我们和大家一起分析了个简易版的智能合约帮助大家了解智能合约的整体结构,并分析了智能合约一些针对交易设计的特点。最后结合智能合约比较热门的应用NFT了解了接口的概念,再次讨论了什么是上链。文中我们多次涉及到之前写的一文,可见底层知识的重要性。

更多web3相关文章 可关注公众号 "web3探索者"

我要回帖

更多关于 owners是什么意思 的文章

 

随机推荐