通过本文了解: 什么是ERC-721? ERC-721如何实现? 如何使用?
在2017年ICO泡沫最严重的时候,ERC-20代币无处不在。 科技公司将其用作众筹的一种形式,其中一些公司声称将来会在其平台上使用这些代币。
ERC-20代币就像货币。 每美元的价值都与其他美元相同。 将一美元的钞票换成另一美元的钞票,本质上是一样的。 这就是所谓的“可替代Token”。
ERC-721Token就像收藏品。 每张口袋妖怪卡与其他口袋妖怪卡都不相同。 甚至代表同一个Pokemon的卡的价值也有所不同。 有些卡的状态比其他卡好,有些是限量版或特别版等等。一张皮卡丘卡并不总是等于另一张皮卡丘卡。 这就是所谓的“不可替代Token”。
ERC-721标准描述了任何不可替代令牌都必须遵守的接口才能被视为ERC-721。
幸运的是,我们每次创建ERC-721时都不需要创建新代码来满足该标准。 使用社区维护如[OpenZeppelin对我们来说是一个捷径。
让我们看一下如何使用OpenZeppelin创建简单数字化模仿Pocket Monsters的游戏。 我们将其称为“Ethermon”游戏。
首先,让我们对每个Ethermon做一些假设(游戏逻辑描述):
每个Ethermon都归某人所有。
他们从第一级开始。
你和其他口袋妖怪战斗。
通过战斗获得升级。
我们还需要围绕战斗的一些逻辑。 为简单起见,假设如果一个Ethermon攻击另一个,则级别更高Ethermon的将获胜 。 如果级别相同,则攻击者获胜。 战斗的胜利者上升两级,失败者升一级。
使用Truffle开发框架创建这个基于ERC721的Pokemon游戏项目
首先创建一个新的文件夹,然后初始化Truffle项目:
mkdir ethermon
cd ethermon/
truffle init
为了使用OpenZepplin,我们需要利用npm导入这个库。让我们先初始化npm, 然后获取正确版本的OpenZeppelin。我们使用最新的稳定版,2.5.0
版本的OpenZeppelin, 确保你需要使用的是 0.5.5
版本的Solidity编译器:
npm init
npm install @openzeppelin/contracts@2.5.0 --save
在我们的contracts/
文件夹,先创建一个新的名为Ethermon.sol
文件。要使用 OpenZeppelin 代码,我们需要引入并扩展 ERC721.sol。
当前 Ethermon.sol 代码如下:
pragma solidity ^0.5.5;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract Ethermon is ERC721 {
}
首先使用truffle compile
检查确保我们的合约可以正确编译。 接下来,我们编写迁移脚本以便将合约部署到本地区块链。在migrations/
目录下创建一个新的迁移文件2_deploy_contracts.js
,代码如下:
const Ethermon = artifacts.require("Ethermon");
module.exports = function(deployer) {
deployer.deploy(Ethermon);
};
确保 truffle-config.js
配置可以正确连接本地区块链,你可以使用truffle test
先测试一下。
Ethermon合约需要实现如下功能:
让我们先实现第一步。我们需要在Ethermon
合约中用一个数组保存所有的妖怪。需要保存的妖怪相关的数据包括名字、级别等。因此我们使用一个结构体。
现在 Ethermon 合约的代码如下:
pragma solidity ^0.5.5;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract Ethermon is ERC721 {
struct Monster {
string name;
uint level;
}
Monster[] public monsters;
address public gameOwner;
constructor() public {
gameOwner = msg.sender;
}
function createNewMonster(string memory _name, address _to) public {
require(msg.sender == gameOwner, "Only game owner can create new monsters");
uint id = monsters.length;
monsters.push(Monster(_name, 1));
_safeMint(_to, id);
}
}
Monster
结构体在第7行定义,数组在第12行定义。我们也添加了一个gameOwner
变量来保存Ethermon
合约的部署账户。第19行开始是createNewMonster()
函数的实现, 该函数负责创建新的妖怪。
首先,它会检查这个函数是否是由合约的部署账号调用的。然后为新妖怪生成一个ID,并将新妖怪存入数组,最后使用_safeMint()
函数将这个新创建的妖怪分配给其主人,这就完成了第一二步。
_safeMint()
是我们继承的ERC721合约中实现的函数。它可以安全地将一个 ID 分配给指定的账号,在分配之前会检查ID是否已经存在。
好了,现在我们已经可以创建新的妖怪并将其分配给指定的账号。该进行 第三步了:战斗逻辑。
正如之前所述,我们的战斗逻辑决定了一个妖怪可以升多少等级。较高等级的妖怪可以获胜并升两级,失败的妖怪升一级。如果两个妖怪处于同一等级,那么进攻者获胜。下面的代码展示了合约中战斗逻辑的实现:
pragma solidity ^0.5.5;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract Ethermon is ERC721 {
struct Monster {
string name;
uint level;
}
Monster[] public monsters;
address public gameOwner;
constructor() public {
gameOwner = msg.sender;
}
function battle(uint _attackingMonster, uint _defendingMonster) public {
Monster storage attacker = monsters[_attackingMonster];
Monster storage defender = monsters[_defendingMonster];
if (attacker.level >= defender.level) {
attacker.level += 2;
defender.level += 1;
}
else{
attacker.level += 1;
attacker.level += 2;
}
}
function createNewMonster(string memory _name, address _to) public {
require(msg.sender == gameOwner, "Only game owner can create new monsters");
uint id = monsters.length;
monsters.push(Monster(_name, 1));
_safeMint(_to, id);
}
}
第19行开始展示了妖怪的战斗逻辑。目前任何账号都可以调用battle()方法。我们需要对此加以限制,只允许发起进攻的妖怪的主人调用该方法。为此,我们可以添加一个修饰器(参考Solidity 文档 - 函数修饰器),该修饰器onlyOwnerOf
利用ERC721.sol
合约中的ownerOf()
函数来检查调用者账号。
修改后的代码如下:
pragma solidity ^0.5.5;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract Ethermon is ERC721 {
struct Monster {
string name;
uint level;
}
Monster[] public monsters;
address public gameOwner;
constructor() public {
gameOwner = msg.sender;
}
modifier onlyOwnerOf(uint _monsterId) {
require(ownerOf(_monsterId) == msg.sender, "Must be owner of monster to battle");
_;
}
function battle(uint _attackingMonster, uint _defendingMonster) public onlyOwnerOf(_attackingMonster) {
Monster storage attacker = monsters[_attackingMonster];
Monster storage defender = monsters[_defendingMonster];
if (attacker.level >= defender.level) {
attacker.level += 2;
defender.level += 1;
}
else{
attacker.level += 1;
attacker.level += 2;
}
}
function createNewMonster(string memory _name, address _to) public {
require(msg.sender == gameOwner, "Only game owner can create new monsters");
uint id = monsters.length;
monsters.push(Monster(_name, 1));
_safeMint(_to, id);
}
}
这样就完成了一个使用ERC721的妖怪战斗游戏:我们可以创建新的怪物并分配给某主人。 怪物的主人可以与其他人战斗以升级他们的怪物。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!