NFT简介

数字加密货币大致可以分为原生币(coin)和代币(token)两大类。前者如BTC、ETH等,拥有自己的区块链。后者如Tether、TRON、ONT等,依附于现有的区块链。市场上流通的基于以太坊的代币大都遵从ERC20协议。

NFT的全称是Non-Fungible Tokens,中文常翻译为“非同质化代币”,具有不可分割、不可替代、独一无二等特点。非同质化是一个经济术语,比如博物馆里的蒙娜丽莎原画,或者一块土地的所有权。而同质化物品则可以相互替换, 例如,ETH或美元具有同质化属性,你手里的1 ETH/1 USD与另一个人所拥有的1 ETH/1 USD,没有任何不同。

NFT目前在数字艺术品和收藏品世界中掀起风潮。CryptoPunks算是最早的NFT项目。这是一整套像素风格的图标,一共有1万个。最终生成的朋克有5种类型:外星人、僵尸、猿猴、男性、女性,有87种不同的属性:发型、发色、眼镜、五官、肤色等。
file
2021年5月,其中9个作品首次在线下拍卖行拍卖,最终以超过1600万美元的总价售出,引起轰动。到了9月,下图的头像又以3385万港元(约合人民币2876万)成交,远超预估价。
file
NFT通过智能合约铸造,智能合约分配 NFT 的所有权并管理它们的可转让性。 有人创建或铸造 NFT 时,他们会执行存储在符合不同标准的智能合约中的代码,如 ERC-721。ERC-721 是由Dieter Shirley 在2017年9月提出。Dieter Shirley正是CryptoKitties背后的公司Axiom Zen的技术总监。因此CryptoKitties也是第一个实现了ERC-721 标准的去中心化应用。ERC-721号提议已经被以太坊作为标准接受,但该标准仍处于草稿阶段。
file
(Cryptokitties基于以太坊平台运行,用户在游戏中可以养大、买卖并繁育“电子宠物”小猫,每只小猫和繁衍的后代都是独一无二的。这款游戏的交易平台至少完成了2万只电子猫的买卖,交易额约330万美元。)

大多数 NFT 使用同一套标准建立,那就是 ERC-721。 然而,还有其他的标准。 ERC-1155 标准允许半同质化代币,这在游戏领域特别有用。 最近还提出了 EIP-2309,这使得制作 NFT 更高效。 这个标准让您在一次交易中尽情铸币!

可以看到,当我们谈及NFT时,会涉及到以太坊、智能合约、ERC-721协议等,接下来我们将对这些概念逐一加以解读。
file

以太坊

以太坊(英文Ethereum)是一个开源的有智能合约功能的公共区块链平台,通过其专用加密货币以太币(Ether,简称“ETH”)提供去中心化的以太虚拟机(Ethereum Virtual Machine)来处理点对点合约。以太坊的概念首次在2013至2014年间由程序员Vitalik Buterin受比特币启发后提出,大意为“下一代加密货币与去中心化应用平台”,在2014年通过ICO众筹开始得以发展。截至2018年2月,以太币是市值第二高的加密货币,仅次于比特币。

以太坊最重要的创新就是以太坊虚拟机,它使得任何人都可以运行一段虚拟机程序,并通过网络的共识机制被所有人所接受。以太坊虚拟机赋予了区块链技术极大的灵活性,在此之上,可以非常容易地构建各种去中心化应用(Dapp),像上述提到的智能合约,以及去中心化金融(Defi)等都是基于以太坊虚拟机构建的应用。

太坊虚拟机(EVM)可以看做是以太坊网络中的一台规范化计算机,其状态得到以太坊网络中所有人的一致同意。 每个参与以太坊网络的人(每个以太坊节点)都会保存一份这台计算机的状态。 此外,任何参与者都可以广播请求这台计算机进行任意计算。 每当广播这样的请求网络时,网络上的其他参与者就会验证、确认并进行(“执行”)计算。 这个命令会导致 EVM 的状态变化,并且在整个网络中传播。
file

计算请求被称为交易请求,多条交易的记录以及 EVM 的当前状态被打包成区块,存储在区块链中。区块链通过共识机制,各个节点完成校验达成一致后进行存储,确保了数据的一致性,并防止被恶意篡改。
file

以太坊网络

由于以太坊是一种协议,因此这意味着可以有多个独立的“网络”与该协议兼容,并且彼此之间不会相互作用。

公共网络:每个人都能通过互联网连接到公共网络。 任何人都可以在公共区块链上读取或创造交易,并且可以验证已经执行的交易。 交易协议以及网络状态由网络上的同行共同决定。

  1. 主网:主网是指主要的以太坊生态区块链,所有具有实际价值的交易都发生在该链的分散账本中。大众和交易所涉及的 ETH 价格是主网的 ETH。
  2. 测试网:除了主网外,还有公开的测试网。 这是一种模拟生态环境的网络,协议开发者或智能合约开发者可以使用它们测试尚未部署在主网上的协议升级和智能合约。 测试网上的 ETH 没有实际价值,可以从测试网水龙头免费获取测试用的ETH。

私有网络:如果以太坊网络的节点未连接到公共网络(即 主网或测试网),则以太坊网络是专用网络。

  1. 联盟网络:共识过程由一组预定义的受信任节点控制。 例如,一个由已知学术机构组成的私有网络,每个学术机构管理一个节点。
  2. 开发网络:与在计算机上创建用于 web 开发的本地服务器类似,你可以创建本地区块链实例来测试你的 dapp。 这允许比公共测试网更快的迭代。

接下来我们介绍以太坊中各个重要的组件。

区块链

区块的概念:区块是指一批交易的组合,并且包含链中上一个区块的哈希。 这将区块连接在一起(成为一个链),因为哈希是从区块数据中加密得出的。 这可以防止欺诈,因为以前的任何区块中的任何改变都会使后续所有区块无效,而且所有哈希都会改变,所有运行区块链的人都会注意到。这样就防止了有人对历史数据进行篡改。

所有已在网络历史上提交给以太坊网络的区块的序列,称为区块链。 每个区块都包含对前一个区块的引用,这有助于我们在所有区块间(同时在精确的历史记录)上保持顺序。在以太网中,我们可以将区块链视为一个可靠的分布式数据库,用于存储整个以太网的历史、以及当前状态。
file
为什么需要区块?

在以太坊网络上,每秒可能就会有成百数千的交易请求,如果对每个请求都要经过共识机制达成一致后进行存储,速度将非常缓慢,会导致大量交易请求积压。通过对交易请求,进行分批打包,存储到区块中,我们可以给所有网络参与者足够的时间达成共识,这样就大大提升了网络处理交易的效率。以太坊上的区块大约每十五秒提交一次,这也意味着为了确认你的一次交易是否成功,大约也要等上十几秒的时间。这里我们可以思考下,如果将网络设计成集中式的,只由少量信任节点来验证交易,是不是可以大幅提升效率?(我的支付宝免密支付,好像唰的一下就完成了。所以信任就是效率,信任就是金钱)。

以太坊网络中,一个区块中包含如下信息:

字段 说明
timestamp 开采区块的时间
blockNumber 区块链中区块的长度
baseFeePerGas 要将交易纳入区块,每个 gas 所需的最低费用。
difficulty 开采所需的努力
mixHash 该区块的唯一标识符
parentHash 前一区块的唯一标识符,相当于指向前一区块的指针
nonce 哈希,当与 mixHash 结合使用时,可以证明该块已经通过了工作量证明
transactions 包含在区块中的交易
stateRoot 系统的整个状态:帐户余额、合约存储、合约代码和帐户随机数。

区块链如何确保存储数据的正确性与一致性?

为了让一个状态转换成下一个状态,交易必须是有效的。为了让一个交易被认为是有效的,它必须要经过一个验证过程,此过程也就是挖矿。挖矿就是一组节点(即电脑)用它们的计算资源来创建一个包含有效交易的区块出来。

任何在网络上宣称自己是矿工的节点都可以尝试创建和验证区块。世界各地的很多矿工都在同一时间创建和验证区块。每个矿工在提交一个区块到区块链上的时候都会提供一个数学机制的“证明”,这个证明就像一个保证:如果这个证明存在,那么这个区块一定是有效的。

PoW(proof of work):为了让一个区块添加到主链上,一个矿工必须要比其他矿工更快的提供出这个“证明”。通过矿工提供的一个数学机制的“证明”来证实每个区块的过程称之为工作量证明(proof of work)。其简单的原理可以理解为,通过寻找一个随机数nonce,使得Hash(mixHash, nonce)的前n位为0。这个方法很容易验证一个nonce是否合法,但是求解这个nonce的过程则非常耗时,几乎只能通过暴力搜索的方式得到,需要消耗大量的计算资源。最先求解到nonce的矿工获得大家的认可,他打包出的区块被所有节点添加到区块链的末尾,在完成区块的创建后,这个矿工会获得2 个新铸造的 ETH 和这个区块内所有的交易费用,作为奖励 。

账户

一个以太坊帐户是一个具有以太币 (ETH) 余额的实体,可以在以太坊上发送交易。 帐户可以由用户控制,也可以作为智能合约部署。每个账户都有一个与之关联的状态(state)和一个20字节的地址(address)。在以太坊中一个地址是160位的标识符,用来识别账户的。

以太坊的账户有2种类型

  • 外部拥有的账户:被私钥的所有者控制且没有任何代码与之关联。
  • 合约账户:被合约代码控制且有代码与之关联。
    file

外部拥有账户与合约账户的区别

外部拥有的账户

  • 创建帐户是免费的

  • 可以发起交易

  • 外部所有的帐户可以通过创建和用自己的私钥来对交易进行签名,来发送消息给另一个外部拥有账户或合约账户。在两个外部拥有账户之间传送的消息只是一个简单的价值转移(ETH 和代币交易)。

    合约账户

  • 创建合约存在成本,因为需要使用网络存储空间

  • 只能在收到交易时发送交易

  • 从外部帐户向合约帐户发起的交易能触发可执行多种操作的代码,例如转移代币甚至创建新合约。
    file

在以太坊上任何的动作,总是被外部控制账户触发的交易所发动的。
file
以太坊帐户有四个字段:

字段 说明
nonce 显示从帐户发送的交易数量的计数器。 这将确保交易只处理一次。 在合约帐户中,这个数字代表该帐户创建的合约数量
balance 这个地址拥有的 Wei 数量。 Wei 是以太币的最小计数单位,1 Wei = 1e-18 ETH
codeHash 该哈希表示以太坊虚拟机 (EVM) 上的帐户代码。 合约帐户具有编程的代码片段,可以执行不同的操作。 如果帐户收到消息调用,则执行此 EVM 代码。 与其他帐户字段不同,不能更改。 所有代码片段都被保存在状态数据库的相应哈希下,供后续检索。 此哈希值称为 codeHash。 对于外部所有的帐户,codeHash 字段是空字符串的哈希。
storageRoot 存储哈希。 记录Merkle Patricia树的根节点Hash值。Merkle树会将此账户存储内容的Hash值进行编码,默认是空值。

file

以太坊的账户作为系统状态的一部分,存储于区块的stateRoot字段下。
file

创建账户的过程

  1. 随机生成256bit(32字节)的整数作为私钥。通常使用密码加密保存。
  2. 使用secp256k1椭圆曲线数字签名算法从私钥生成公钥(‘04’ + 64字节)
  3. 对公钥使用Keccak-256(SHA3的候选算法)算法计算哈希,取最后 20 个字节(160bit)作为地址

    合约账户是在部署合约的时候创建的,方法与上面的步骤3类似,但使用的信息为用户的地址+用户nonce信息,取低位的20字节。

    从上述步骤中可以看到,可以通过私钥获取公钥,进而得到地址,但不能反向推导,通过公钥获取私钥,或通过地址获取公钥。

file

示例

第一步:私钥 (private key)

伪随机数产生的256bit私钥示例(256bit 16进制32字节)

18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725

第二步:公钥 (public key)

  1. 采用椭圆曲线数字签名算法ECDSA-secp256k1将私钥(32字节)映射成公钥(65字节)(前缀04+X公钥+Y公钥):

    04
    50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352
    2cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
  2. 拿公钥(非压缩公钥)来hash,计算公钥的 Keccak-256 哈希值(32bytes):
    fc12ad814631ba689f7abe671016f75c54c607f082ae6b0881fac0abeda21781

  3. 取上一步结果取后20bytes即以太坊地址:
    1016f75c54c607f082ae6b0881fac0abeda21781

第三步:地址 (address)
0x1016f75c54c607f082ae6b0881fac0abeda21781

钱包的概念

帐户和钱包不同。 账户是用户拥有的以太坊账户的密钥和地址对。 钱包是一个界面或者说应用程序,可以让您与以太坊账户交互。

交易

以太坊交易是指由外部持有账户发起的行动,是指由人管理而不是智能合约管理的账户。 例如,如果 Bob 发送 Alice 1 ETH,则 Bob 的帐户必须减少 1 ETH,而 Alice 的账户必须增加 1 ETH。 此项操作发生在交易中,会变更状态。改变 EVM 状态的交易需要广播到整个网络。 任何节点都可以在 EVM 上广播交易请求;此后,矿工将执行交易并将由此产生的状态变化传播到网络的其他部分。

费用:交易需要收费并且必须开采(记录到区块链)才能有效。 为了使这种概述更加简单,我们将其称为 Gas 费,Gas的英文意义是汽油,顾名思义,就像开车需要烧汽油一样,在以太坊网络中交易也需要燃烧掉一定的Gas作为成本,Gas 代表了矿工处理交易所需的算力,而Gas的价格也随着当前网络算力供给情况与交易需求的变化而发生变化,就像当前石油紧缺时,汽油价格大涨一样。如果当前的交易需求量巨大,导致矿工算力不足时,Gas的价格也会随之提高。 gasLimit 和 gasPrice 决定了支付给矿工的最高交易费用。

例如,假设发送者设置gas limit为50,000,gas price为20gwei(gwei=1e-9ETH)。这就表示发送者愿意最多支付50,000 * 20gwei = 1,000,000,000,000,000 Wei = 0.001 Ether来执行此交易。
file

一条交易消息包括下列信息:

字段 说明
recipient(to) 接收地址(如果为一个外部持有的帐户,交易将传输值。 如果为合约帐户,交易将执行合约代码)。在合约创建交易中,合约账户的地址还没有存在,所以值先空着
signature(v, s, r) 签名。 当通过发送者的私钥签名交易来确保发送者已授权此交易时,生成此签名。
value 从发件人向收件人转移 ETH 的金额 (以 WEI 为单位)
data 可包括任意数据的可选字段
init 只有在合约创建交易中存在,用来初始化新合约账户的EVM代码片段。init值会执行一次,然后就会被丢弃。当init第一次执行的时候,它返回一个账户代码体,也就是永久与合约账户关联的一段代码
gasLimit 交易可以消耗的 Gas 的最大数量。 Gas 单位代表了消耗的算力
maxPriorityFeePerGas 矿工小费。小费越高,越有可能被矿工优先处理。
maxFeePerGas 愿意为交易支付的最大 gas 数量(包括 baseFeePerGas 和 maxPriorityFeePerGas)

下面是发送一条交易消息数据的示例

 {
 result: {

 // RLP编码数据
 raw: 0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663,

 tx: {
 nonce: 0x0,
 maxFeePerGas: 0x1234,
 maxPriorityFeePerGas: 0x1234,
 gas: 0x55555,     to:0x07a565b7ed7d7a678680a4c162885bedbb695fe0,
 value: 0x1234,
 input: 0xabcd,
 v: 0x26,
 r: 0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e,
 s: 0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663,
 hash: 0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e
 }
 }
 }

交易类型:

  • 常规交易:从一个钱包到另一个钱包的交易。
  • 合约部署交易:没有“to”地址的交易,数据字段用于合约代码。

交易流程:

  1. 一旦您发送交易,加密法生成交易哈希: 如0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017
  2. 然后将该交易转播到网络,并且与大量其他交易一起包含在一个集合中。
  3. 矿工必须选择您的交易并将它包含在一个区块中,以便验证交易并认为它“成功”。
    • 如果网络繁忙,矿工无法跟上,您可能会在这个阶段等候。
  4. 您的交易将收到确认。 确认的数量是自包含您交易的区块以来创建的区块数。 这个数字越大,交易被网络处理和承认的确定性就越强。
    • 最近的区块可能会被重组,给人留下交易失败的印象;但交易可能仍然有效,但包含在另一个区块中。
    • 重组的概率随着其后每一次挖掘的区块而降低,即确认次数越多,交易就越不可改变。

计费流程:

(1)如果发送者账户余额中有足够的Ether来支付交易产生的费用,则交易成功,在交易结束时任何未使用的gas都会被返回给发送者
file
(2)如果发送者没有提供足够的gas来执行交易,那么交易执行就会出现“gas不足”然后被认为是无效的。在这种情况下,交易处理就会被终止,所有已改变的状态将会被恢复。因为机器在耗尽gas之前还是为计算做出了努力,所以不会有任何的gas被返回给发送者(汽油烧掉了就没有了)。
file

为什么会有交易费(Gas)?

前面我们已经介绍过,以太坊可以看做是一个大型的虚拟机(EVM)。每个网络上执行的操作都会同时影响所有节点。由于计算操作在以太坊虚拟机上是非常昂贵的。因此,以太坊智能合约最好是用来执行最简单的任务,比如运行一个简单的业务逻辑或者验证签名和其他密码对象,而不是用于复杂的操作,比如文件存储,电子邮件,或机器学习,这些会给网络造成压力。甚至如果有人想利用恶意程序对网络进行攻击,去执行一个死循环程序,有可能会造成整个网络的瘫痪。而交易费用这个机制,则从经济的角度上,让这种攻击几乎不可能发生。可以看到,以太坊将很多技术上难以解决的问题,通过经济学的手段,完美地解决了。

智能合约

在生活中,如果你想从一个陌生人那里购买商品,你是希望先交钱还是先交货呢?(卖家肯定是希望先收钱的)如果交易双方互不认识,你恐怕是不太敢直接把钱交给对方的,那么如何解决互联网上,互不认识的买家和买家之间的交易呢?支付宝在这个问题上,给出了一个解答,那就是引入信任的第三方,先把前交给信任的第三方,待收到货之后,由第三方再进行货款的结算。
file

但是,如果支付宝这个公司作弊呢?这个公司在尚未完成交易时,倒闭了呢?被政府审查,停止营业了呢?所谓的具有权威的第三方,看上去也不那么可靠,怎么办?这就引入了智能合约的概念,由计算机程序来替代第三方完成一份合约或交易,这段程序运行在区块链上,公开可见,被交易双方认可,不可篡改,无法作弊,利用区块链的共识机制解决了信任问题。
file

智能合约并不是一个新的概念,早在1995年就由跨领域法律学者尼克萨博提出,是对现实中的合约条款执行电子化的量化交易协议。智能合约设计的总体目标是最大限度地减少对可信中介的依赖。

智能合约有以下特点:

  1. 规则公开透明,合约内的规则以及数据对外部可见;
  2. 所有交易公开可见,不会存在任何虚假或者隐藏的交易。

以太坊上的智能合约是一个运行在以太坊链上的程序。 它由位于以太坊区块链上一个特定地址的一系列代码(函数)和数据(状态)组成。智能合约使用了特定的编程语言(如 Solidity,一种长相酷似Javascript,但采用静态类型编译的语言)来编译到 EVM 字节码(调用 opcodes 的低级机器指令)。

智能合约也是一个以太坊帐户,我们称之为合约帐户。 这意味着他们有余额,他们可以通过网络进行交易。 但是,他们无法被人操控,他们是被部署在网络上作为程序运行着。 个人用户可以通过提交交易执行智能合约的某一个函数来与智能合约进行交互。 智能合约能像常规合约一样定义规则,并通过代码自动强制执行。 默认情况下,您无法删除智能合约,与它们的交互是不可逆的。(没有7天无理由退货)

合约账户的创建流程:

我们介绍过,以太坊中有两种账户类型:合约账户和外部拥有账户。创建一个新的合约账户,也是通过“交易”来完成。

为了创建一个新的合约账户,我们使用一个特殊的公式来声明新账户的地址。然后我们使用下面的方法来初始化一个账户:

  1. 设置nonce为0
  2. 如果发送者通过交易发送了一定量的ETH作为value,那么设置账户的余额为value
  3. 将存储设置为0
  4. 设置合约的codeHash为一个空字符串的Hash值

一旦我们完成了账户的初始化,使用交易发送过来的init code,实际上就创造了一个账户。init code的执行过程是各种各样的。取决于合约的构造器,可能是更新账户的存储,也可能是创建另一个合约账户,或者发起另一个消息通信等等。

当初始化合约的代码被执行之后,会使用gas。交易不允许使用的gas超过剩余gas。如果它使用的gas超过剩余gas,那么就会发生gas不足异(OOG)常并退出。

如果初始化代码成功的执行完成,最后的合约创建的花费会被支付。这些是存储成本,与创建的合约代码大小成正比(存储也需要Gas)。如果没有足够的剩余gas来支付最后的花费,那么交易就会再次宣布gas不足异常并中断退出。
如果所有的都正常进行没有任何异常出现,那么任何剩余的未使用gas都会被退回给原始的交易发送者,现在改变的状态才被允许永久保存。

ERC20

NFT通常采用ERC-721协议的智能合约来实现,在介绍ERC-721之前,我们先来了解下与之相似的同质化代币协议ERC-20。

ERC20协议诞生于2015年,到2017年9月被正式标准化。协议规定了具有可互换性(fungible)代币的一组基本接口,包括代币符号、发行量、转账、授权等。

ERC20定义了三个可选函数:

// 代币名称。比如Sleepism Token。
function name() view returns (string name);

// 代币符号。按惯例一般用全大写字母,比如“SLPT”。
function symbol() view returns (string symbol);

// 小数位数。不少代币采用与ETH一样的设定,也就是18位。
// 这只影响用户界面中货币量的显示方式。代币本身都统一用uint256表示。
function decimal() view returns (uint8 decimals);

ERC20定义了六个必须声明的函数:

// 代币总量。
function totalSupply() view returns (uint256 totalSupply);

// 查询指定地址下的代币余额。
function balanceOf(address _owner) view returns (uint256 balance);

// 给指定地址转入指定量的代币,转账成功则返回true。
// 如果源地址没有足够量的代币,函数应该抛出异常。
// 即使转零个代币,也应该触发Transfer事件(下文中有解释)。
function transfer(address _to, uint256 _value) returns (bool success);

// 与transfer类似的转账函数。区别在于可以指定一个转出地址。
// 如果当前地址得到了转出地址的授权,则可以代理转账操作。
function transferFrom(address _from, address _to, uint256 _value)
  returns (bool success);

// 授权指定地址特定的转账额度。
// 被授权的地址可以多次调用transferFrom函数代替源地址转账,总值不超过_value。
// 实际使用时,每次重设额度前应该先调用approve(_spender, 0),
// 等交易被确认后再调用approve(_spender, newAllowance)。
// 如果直接调用一次approve,被授权地址有机会转出高出指定额度的代币。
function approve(address _spender, uint256 _value) returns (bool success);

// 查询指定授权地址剩余的转账额度。
function allowance(address _owner, address _spender) view returns (uint256 remaining);

ERC20还定义了两个事件:

// 转账事件。必须在成功转账(哪怕是零个代币)时触发。
event Transfer(address indexed _from, address indexed _to, uint256 _value);

// 授权事件。必须在成功授权时触发。
event Approval(address indexed _owner, address indexed _spender, uint256 _value);

ERC721

ERC721最早是应用于区块链游戏CryptoKitties中,每个token代表一个与众不同的数字宠物猫,可以在网上进行交易。这种非标准化资产很长时间内都没有标准协议,直到2017年9月才出现ERC721提案,定义了一组常用的接口。ERC721至今仍旧处于草案阶段,但已经被不少dApp采用。

ERC721与ERC20有些相似,但由于它管理的是非互换性资产(non-fungible token,简称NFT),所以函数语义并不一样。合约下每份ERC721资产都拥有一个uint256类型的独立编号(以下代码中的_tokenId)。

ERC721函数:

// 查询_owner地址拥有的NFT总个数。
function balanceOf(address _owner) external view returns (uint256);

// 查询_tokenId资产的所属地址。
function ownerOf(uint256 _tokenId) external view returns (address);

// 将_from地址所拥有的_tokenId资产转移给_to地址。
// 调用方必须是资产主人或是已被授权的地址,否则会抛出异常。
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

// 与transferFrom类似的资产转移函数。
// 它会额外检查_to地址和_tokenId的有效性,另外如果_to是合约地址,
// 还会触发它的onERC721Received回调函数。
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

// 与上述接口类似的资产转移函数。
// 唯一不同点是可以传入额外的自定义参数。
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;

// 把_tokenId资产授权给_approved地址。
function approve(address _approved, uint256 _tokenId) external payable;

// 查询_tokenId资产对应的授权地址。
function getApproved(uint256 _tokenId) external view returns (address);

// 指定或撤销_operator地址的管理权限。
function setApprovalForAll(address _operator, bool _approved) external;

// 查询_operator地址是否已经获得_owner地址的管理权。
function isApprovedForAll(address _owner, address _operator) external view returns (bool);

ERC721元数据接口(可选项):

// 资产名称。比如Sleepism Collectible。
function name() external pure returns (string _name);

// 资产符号。比如SLPC。
function symbol() external pure returns (string _symbol);

// 描述_tokenId资产的URI。指向一个符合ERC721元数据描述结构的JSON文件。
function tokenURI(uint256 _tokenId) external view returns (string);

元数据描述结构如下所示:

 {
 title: Sleepism Collectible Metadata,
 type: object,
 properties: {
 name: {
 type: string,
 description: Sleep and his Half-brother Death,
 },
 description: {
 type: string,
 description: A painting by John William Waterhouse,
 },
 image: {
 type: string,
 description: https://i.imgur.com/sahhbd.png,
 }
 }
 }

ERC721事件:

// 转账事件。从_from地址转移_tokenId对应资产的所有权到_to地址时触发。
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);

// 授权转账事件。把_owner地址控制的_tokenId资产授权给_approved地址时触发。
// 发生转账事件时,对应资产的授权地址应被清空。
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);

// 授权管理事件。_owner地址授权或取消授权_operator地址的管理权时触发。
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

ERC721枚举扩宽接口(可选项):

// 合约管理的总NFT数量。
function totalSupply() external view returns (uint256);

// 查询第_index个资产的编码。
function tokenByIndex(uint256 _index) external view returns (uint256);

// 查询_owner地址拥有的第_index个资产的编码。
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);

符合ERC721协议的合约还需要符合ERC165规范,实现以下函数:

// 查询合约是否实现了interfaceID对应的接口。
function supportsInterface(bytes4 interfaceID) external view returns (bool);

interfaceID由bytes4(keccak256(函数签名))计算得到。有多个函数时,将全部byte4异或(xor)得到最终结果。详见ERC165标准文档(https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md)。

NFT的铸造发行及交易流程

使用ERC721协议铸造(生成)NTF的示例代码

// contracts/GameItem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import @openzeppelin/contracts/token/ERC721/ERC721.sol;
import @openzeppelin/contracts/utils/Counters.sol;

contract GameItem is ERC721 {
using Counters for Counters.Counter;

 Counters.Counter private _tokenIds;

 constructor() public ERC721(GameItem, ITM) {}

// 铸造NFT的方法,只需要传入铸造给谁,再加上这个NFT的tokenURI即可
function awardItem(address player, string memory tokenURI) public returns (uint256)
{

 // 一次只生成一个NFT token
 _tokenIds.increment();

 uint256 newItemId = _tokenIds.current();
 // 将这个新的tokenId与owner绑定

 _mint(player, newItemId);
 // 将tokenURI与这个newTokenId绑定, uri中包含NFT的描述信息

 _setTokenURI(newItemId, tokenURI);
 return newItemId;
 }
 }

内部方法mint方法的实现

function _safeMint(address _to, uint256 _tokenId, bytes memory _data) internal {

//要求tokenId必须不能是一个已经存在tokenId
//要求地址to如果是合约地址,则需要实现onERC721Received方法

require(!_exists[_tokenId],ERC721/_safeMint tokenId already exists);

_mint(_to,_tokenId);

require(_checkOnERC721Received(address(0),_to,_tokenId,_data),ERC721/_safeMint not a valid receiver);
}

function _safeMint(address _to, uint256 _tokenId) internal {
_safeMint(_to,_tokenId,\);
}

function _mint(address _to, uint256 _tokenId) internal {

 //要求_tokenId必须不能是一个已经存在的tokenId
 //要求地址_to必须不能是address(0)
 require(!_exists(_tokenId), ERC721/_mint tokenId already exists);
 require(_to != address(0),ERC721/_mint _to can not be address(0));

 _owners[_tokenId] = _to;

 _balances[_to] += 1;

 emit Transfer(address(0), _to, _tokenId);
}

NFT的销毁:将token转移到0地址address(0)

function _burn(uint256 _tokenId) internal {

 //要求tokenId必须存在,但是不能真的把tokenId转给地址0,只是删除owners中对应的tokenId
 require(_exists(_tokenId),\);

 //要求清除该tokenId对应的授权地址,但不能清除经销商的授权
 _tokenApproves[_tokenId] = address(0);

 _balances[msg.sender] -= 1;

 delete _owners[_tokenId];

 emit Transfer(msg.sender, address(0), _tokenId);
}

协议概览图
file

ERC721A

批量铸造NFT:ERC721适合铸造单个NFT,比如用来代表一个头像。但如果一个数字藏品的价格比较高,比如一幅名人名画,一个音乐家的专辑,我们希望能用多个NFT来代表这个作品(就像股票一样),这样每个NFT的价格就比较低,可以被普通大众购买。

从 Openzeppelin 对ERC721协议的实现来看,由于没有提供批量 Mint 的 API,使得用户批量 Mint 时,其算法复杂度达到 O(N),如果要一次铸造成千上万个NFT,其消耗的GAS费将是惊人的。故 ERC721A 提出了一种批量 Mint 的 API,使得其算法复杂度降为 O(1)。

function _mint(address to, uint256 quantity) internal virtual {

 ...
 //checks

 uint256 tokenId = _currIndex;
 _balances[to] += quantity;
 _owners[tokenId] = to;

 ···
 //emit Event
 for (uint256 i = 0; i < quantity; i++) {
 emit Transfer(address(0),to,tokenId);
 tokenId++;
 }

 //update index
 _currIndex = tokenId;
 }

相比于ERC721,ERC721A有如下几个特点:

  1. 存储成本降低:在 NFT 批量铸造过程,tokenId 从 0 开始连续单调递增,去除了大量额外的存储开销(不必每个token存储一个对应的owner),GAS费用大幅降低。
  2. 更新计算减少:因为更新NFT余额的操作涉及到账户状态的更新,也消耗GAS,ERC721中,每次铸造一个新的NFT,就需要更新一次账户余额。ERC721批量铸造的复杂度为O(1),只需要更新一次账户余额,GAS开销也大幅下降。
  3. 批量转移高效:可以实现比较简单的批量转移,操作开销小。

国内外市场的发展现状

OpenSea

OpenSea自称是最大的NFT市场。它提供广泛的涵盖艺术、防审查域名、虚拟世界、 交易卡、体育、收藏品等领域的NFT通证。

OpenSea支持ERC721和ERC1155资产(半同质化资产,支持批量铸造转移)。你可以在OpenSea 上购买、出售很多知名资产,如 Axies、ENS名、加密猫、DecentralLand等。OpenSea提供 超过 700 个不同的项目,包括交易纸牌游戏、数字艺术项目的收藏游戏以及 ENS(以太坊名称服务)等。

OpenSea是建立在公共网络上的,并且现在提供跨区块链支持,支持跨越以太坊、Polygon和Klatyn。其中Polygon(以前称为Matic网络)是一个独立的区块链,提供可扩展、安全和即时的以太坊货币交易,如ETH、USDC和DAI。最主要的特征是,使用Polygon创建、购买和出售NFT,而无需支付交易费用,本质上是一个无GAS的市场。

创作者可以使用 OpenSea 的NFT铸造工具在区块链上创建自己的项目。使用这个工具无需代码就可以免费制作 NFT和藏品集。OpenSea也支持你自己开发的NFT合约。OpenSea还提供延迟铸币的功能,只有当有人购买NFT时,才执行铸币操作,并且由买家支付GAS费用,这样可以大大降低数字创作者的负担,让创作者可以免费发布自己的作品。

支付宝的鲸探

支付宝参与打造的蚂蚁链的数字藏品发布平台。“蚂蚁开放联盟链是面向企业和开发者提供的“无需搭链、快速上链、接近公链”的区块链服务网络。开放联盟链以类公链的燃料计价方式,通过速搭平台、多个合约开发模板、按需计价等实现用户”低成本低门槛上链”。属于联盟网络。在流通交易方面,支付宝NFT小程序在条款注明,用户至少持有180天后才可转赠给好友。据媒体报道,6月23日,在二手交易平台闲鱼上,出现了挂单价从几百元到上百万元的支付宝NFT,“敦煌飞天”NFT皮肤最高被炒到了 150 万元。第二天,闲鱼官方紧急下架 NFT 相关商品。

腾讯幻核

一款NFT藏品资源交易服务软件。用户在幻核app就能了解到商品的所有信息实时查看各类珍品,线上收藏方便快捷。未来,幻核将NFT发售功能应用于数字艺术与收藏、IP限量周边开发等业务中。腾讯幻核基于至信链构建,也属于联盟网络。幻核目前所售NFT均不可二手交易,不可转让赠送。在幻核平台购买NFT的买家,更多地还是寄望于未来腾讯可以开放流通交易。

至信链基于国产开源自主可控的“长安链”(ChainMaker)构建。“长安链ChainMaker”是在科技部、工信部、国资委等国家部委及北京市政府的指导下发布的国内首个自主可控区块链软硬件技术体系,由微芯研究院联合头部企业和高校共同研发,具有全自主、高性能、强隐私、广协作的突出特点。长安链支持五种共识算法:Solo,Raft,TBFT,Maxbft,DPoS。

长安链在技术架构上,非常类似于以太坊,支持最底层的区块链和智能合约。腾讯幻核的技术平台至信链,可能是在长安链上,实现了ERC721或改进版本的智能合约,来支持NFT和其它应用。

在国内联盟链上发行一般性的NFT产品,用于产品营销,吸粉圈流量,还是可以的。如果是有价值的艺术品、收藏品,还是要尽量考虑在公链上发行NFT,这样其价值才可能显现出来。

参考资料

5 1 投票
文章评分
订阅评论
提醒
guest

0 评论
最旧
最新 最多投票
内联反馈
查看所有评论