一个简单的以太坊ERC721通证例子

ERC-721 token(通证)是当今的一个热门话题,随着crypto kitties的出现以及其成功的产生许多其他数字收藏品。ERC-721标准已经经历了几次迭代,现在或多或少已经到位,所以期待越来越多的玩家进入这个领域。这些不可替代的token(通证)的基本前提是每个token(通证)都是唯一的,因此不能像ERC20通证那样以1:1的方式进行交换。有许多用例,这些ERC-721代币可以代表独特的有形或数字资产,例如房地产,艺术品,宝石等。实际上,数字可收集用例可能是它们的所有最低市场价值用例。

本文的目的

本文将尝试使用ERC-721标准的OpenZeppelin实现以最简单有用的形式创建ERC-721。我建议查看上面链接的标准,以便熟悉这些要求,因为它们有时可能隐藏在优秀的OpenZeppelin实现中。这里还有一篇精彩的文章,深入介绍了ERC-721规范。

项目设置

确保安装了node,npm和truffle

1
mkdir erc721 && cd erc721 && truffle init

用以下内容替换truffle.js内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Allows us to use ES6 in our migrations and tests.
require('babel-register')
require('babel-polyfill')

module.exports = {
networks: {
development: {
host: '127.0.0.1',
port: 8545,
network_id: '*', // Match any network id
}
},
};

安装Ganache并确保它在8545端口上运行。

然后编译和迁移:

1
2
truffle compile
truffle migrate

将文件夹初始化为npm项目:

1
npm init

安装zeppelin依赖项:

1
npm install zeppelin-solidity

token 通证

将以下内容添加到/contract作为MyERC721.sol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pragma solidity ^0.4.23;

import "../node_modules/zeppelin-solidity/contracts/token/ERC721/ERC721Token.sol";

contract MyERC721 is ERC721Token {
constructor (string _name, string _symbol) public
ERC721Token(_name, _symbol)
{
}

/**
* Custom accessor to create a unique token
*/
function mintUniqueTokenTo(
address _to,
uint256 _tokenId,
string _tokenURI
) public
{
super._mint(_to, _tokenId);
super._setTokenURI(_tokenId, _tokenURI);
}
}

将以下内容添加到/migration作为2_erc721_migration.js

1
2
3
4
5
6
const MyERC721 = artifacts.require("./MyERC721.sol");

module.exports = async function(deployer) {
await deployer.deploy(MyERC721, "MyERC721", "MyERC721")
const erc721 = await MyERC721.deployed()
};

运行迁移脚本并验证它是否正确部署:

1
2
3
4
5
6
7
8
9
10
11
Marks-MacBook-Pro:erc721 markmathis$ truffle migrate
Using network 'development'.
Running migration: 2_erc721_migration.js
Deploying MyERC721...
...
0x25c26fd5b79b6328bee75bd34d78b37ff9389aad2d487600d46595adf0ee398d
MyERC721: 0x6d9b92dfaf3cc3ae2e45b37b584f52f23bc03085
Saving successful migration to network...
...
0x5c147afb3f4c2d225ef3b4cabcd7b9d3b5e4cbab88617fa150b061b85eb4cc0a
Saving artifacts...

测试一下

安装测试库:

1
2
3
4
5
npm install chai --save-dev
npm install chai-as-promised --save-dev
npm install babel-preset-es2015 --save-dev
npm install babel-register --save-dev
npm install babel-polyfill --save-dev

.babelrc添加到项目根目录:

1
2
3
{
"presets": ["babel-preset-es2015"]
}

erc721.spec.js添加到/test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
chai.use(chaiAsPromised)
const { expect, assert } = chai

var MyERC721 = artifacts.require("MyERC721");

contract('Testing ERC721 contract', function(accounts) {

let token;
const name = "BlueCat";
const symbol = "BCat"

const account1 = accounts[1]
const tokenId1 = 1111;
const tokenUri1 = "This is data for the token 1"; // Does not have to be unique

const account2 = accounts[2]
const tokenId2 = 2222;
const tokenUri2 = "This is data for the token 2"; // Does not have to be unique

const account3 = accounts[3]

it(' should be able to deploy and mint ERC721 token', async () => {
token = await MyERC721.new(name, symbol)
await token.mintUniqueTokenTo(account1, tokenId1, tokenUri1, {from: accounts[0]})

expect(await token.symbol()).to.equal(symbol)
expect(await token.name()).to.equal(name)
})

it(' should be unique', async () => {
const duplicateTokenID = token.mintUniqueTokenTo(account2, tokenId1, tokenUri2, {from: accounts[0]}) //tokenId
expect(duplicateTokenID).to.be.rejectedWith(/VM Exception while processing transaction: revert/)
})

it(' should allow creation of multiple unique tokens and manage ownership', async () => {
const additionalToken = await token.mintUniqueTokenTo(account2, tokenId2, tokenUri2, {from: accounts[0]})
expect(Number(await token.totalSupply())).to.equal(2)

expect(await token.exists(tokenId1)).to.be.true
expect(await token.exists(tokenId2)).to.be.true
expect(await token.exists(9999)).to.be.false // Dummy tokenId

expect(await token.ownerOf(tokenId1)).to.equal(account1)
expect(await token.ownerOf(tokenId2)).to.equal(account2)
})

it(' should allow safe transfers', async () => {
const unownedTokenId = token.safeTransferFrom(account2, account3, tokenId1, {from: accounts[2]}) // tokenId
expect(unownedTokenId).to.be.rejectedWith(/VM Exception while processing transaction: revert/)

const wrongOwner = token.safeTransferFrom(account1, account3, tokenId2, {from: accounts[1]}) // wrong owner
expect(wrongOwner).to.be.rejectedWith(/VM Exception while processing transaction: revert/)

// Noticed that the from gas param needs to be the token owners or it fails
const wrongFromGas = token.safeTransferFrom(account2, account3, tokenId2, {from: accounts[1]}) // wrong owner
expect(wrongFromGas).to.be.rejectedWith(/VM Exception while processing transaction: revert/)

await token.safeTransferFrom(account2, account3, tokenId2, {from: accounts[2]})
expect(await token.ownerOf(tokenId2)).to.equal(account3)
})
})

从项目根目录运行测试:

1
truffle test

附录

几乎所有常规的ERC20功能都可以在ERC721合约中找到。你可以批准第三方使用你的token(通证),刻录token(通证)等。功能是相同的,但输入和幕后发生的事情有点复杂。本文未讨论的一些ERC721方法也值得独立研究:

  • safeTransferFrom
  • isApprovedForAll
  • setApprovedForAll

总结

让我们回顾一下我们在本文中学到的内容。首先,我们使用truffle设置项目并导入我们的依赖项,包括世界级智能合约库OpenZeppelin。接下来,我们使用OpenZeppelin——ERC721Token模板编写了ERC721 token(通证)。我们进行了一些检查以确保我们的合约有效,然后我们设置了一个mocha测试来测试我们的断言。我们的solidity代码看似简单,我建议更深入地了解ERC721标准和OpenZeppelin实现。我们通过创建一个简单的ERC721 token(通证)来完成我们在本文中的目的,你应该在继续学习这种新token类型的过程中保持良好的感觉。即使这个token不是很有趣的——我希望你还会继续玩下去:)

完整的可用项目源码在这里

如果想更深入的了解和学习也可以试试我们的ERC721以太坊通证实战,课程以一个数字艺术品创作与分享DApp的实战开发为主线,深入讲解以太坊非同质化通证的概念、标准与开发方案。内容包含ERC-721标准的自主实现,讲解OpenZeppelin合约代码库二次开发,实战项目采用Truffle,IPFS,实现了通证以及去中心化的通证交易所。

======================================================================

分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:

  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

汇智网原创翻译,转载请标明出处。这里是原文一个简单的以太坊ERC721通证例子