引言
CryptoKitties是第一个基于Etherum的游戏Dapp,2017年下半年风靡一时,一度造成了Etherum网络的堵塞。虽然现在这款游戏的热度已经开始消退,但是作为技术学习而言,CryptoKitties确实是一个很好的案例参考教程。本节内容作为专栏的开篇,先以最基本的几个合约开始入手讲解。
目录
Ownable合约
这是来自OpenZeppelin Solidity 库的 Ownable 合约,提供了基本的权限控制功能。大多数人开发自己的 DApp,都是从复制/粘贴 Ownable 开始的,从它再继承出的子合约,并在之上进行功能开发。源码先贴上:
contract Ownable {
address public owner;
function Ownable() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
成员变量:
owner:address类型,指向合约拥有者的账户地址
构造函数:
function Ownable():仅在合约最初被部署时执行一次,并将部署者的账户地址赋值给owner变量
函数修饰符:
modifier onlyOwner():修饰符跟函数很类似,不过是用来修饰其他已有函数用的,在其他语句执行前,为它检查下先验条件。
一般函数:
function transferOwnership(address newOwner) onlyOwner:该函数的作用是将合约的拥有权转交给另外一个账户地址,由于加上了上述的修饰符onlyOwner,意味着只有合约当前的拥有者才有权限调用此函数。
ERC721合约
这个合约涉及到ERC标准的内容,以及ERC20同质化货币和ERC721非同质化货币的区别。首先同质化货币比较好理解,我们日常生活用的货币就是同质化货币,我手里的一块钱和你手里的一块钱没有任何区别,我钱包账户里的1 ether和任何人钱包里的1 ether 也是完全等同的。而非同质化货币就类似于收藏品了,你的Kitty猫的基因性格外观和我的Kitty猫绝对不会一模一样。另外同质化货币是可分割的,一块钱可以拆成两枚五毛,但是一只kitty就是一只,不可能送给你半只。。。
游戏里的猫其实就是基于ERC721的加密货币,而ERC721作为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721和ECR165接口,接口定义如下:
contract ERC721 {
function totalSupply() public view returns (uint256 total);
function balanceOf(address _owner) public view returns (uint256 balance);
function ownerOf(uint256 _tokenId) external view returns (address owner);
function approve(address _to, uint256 _tokenId) external;
function transfer(address _to, uint256 _tokenId) external;
function transferFrom(address _from, address _to, uint256 _tokenId) external;
event Transfer(address from, address to, uint256 tokenId);
event Approval(address owner, address approved, uint256 tokenId);
function supportsInterface(bytes4 _interfaceID) external view returns (bool);
}
totalSupply() returns (uint256 total):返回代币总供应量balanceOf(address _owner) returns (uint256 balance):传入address参数,然后返回这个address拥有多少代币ownerOf(uint256 _tokenId) returns (address owner):传入一个代币ID作为参数 ,然后返回该代币拥有者的address。approve(address _to, uint256 _tokenId):授予地址_to具有_tokenId的控制权,方法成功后需触发Approval事件。transfer(address _to, uint256 _tokenId):代币的拥有者调用transfer方法,传入他想转移到的address和他想转移的代币的_tokenId,代币直接传入对方地址。transferFrom(address _from, address _to, uint256 _tokenId):转移代币所有权,一次成功的转移操作必须发起Transer事件。函数的实现需要做一下几种检查:调用者msg.sender应该是当前tokenId的所有者或被授权的地址;_from必须是_tokenId的所有者;_tokenId应该是当前合约正在监测的NFTs中的任何一个;_to地址不应该为 0。supportsInterface(bytes4 _interfaceID) returns (bool):基于ECR165的自检接口。合约实现了任何标准化接口则返回true。
GeneScienceInterface合约
这个合约很简单,直接贴代码:
contract GeneScienceInterface {
function isGeneScience() public pure returns (bool);
function mixGenes(uint256 genes1, uint256 genes2, uint256 targetBlock) public returns (uint256);
}
isGeneScience() returns (bool):单纯返回一个bool值,用来判断这个合约不是期望的那个。注:该合约即基因算法的实现部署在另一个地址,这样实现了业务逻辑的解耦,方便更新业务逻辑。mixGenes(uint256 genes1, uint256 genes2, uint256 targetBlock) returns (uint256):传入妈妈的基因genes1、爸爸的基因genes2以及目标区块的targetBlock来计算生成后代的基因组。
KittyAccessControl合约
KittyAccessControl合约定义了访问控制权限,共有CEO、CFO、COO三种角色权限,代码如下:
contract KittyAccessControl {
event ContractUpgrade(address newContract);
address public ceoAddress;
address public cfoAddress;
address public cooAddress;
bool public paused = false;
modifier onlyCEO() {
require(msg.sender == ceoAddress);
_;
}
modifier onlyCFO() {
require(msg.sender == cfoAddress);
_;
}
modifier onlyCOO() {
require(msg.sender == cooAddress);
_;
}
modifier onlyCLevel() {
require(
msg.sender == cooAddress ||
msg.sender == ceoAddress ||
msg.sender == cfoAddress
);
_;
}
function setCEO(address _newCEO) external onlyCEO {
require(_newCEO != address(0));
ceoAddress = _newCEO;
}
function setCFO(address _newCFO) external onlyCEO {
require(_newCFO != address(0));
cfoAddress = _newCFO;
}
function setCOO(address _newCOO) external onlyCEO {
require(_newCOO != address(0));
cooAddress = _newCOO;
}
modifier whenNotPaused() {
require(!paused);
_;
}
modifier whenPaused {
require(paused);
_;
}
function pause() external onlyCLevel whenNotPaused {
paused = true;
}
/// @dev Unpauses the smart contract. Can only be called by the CEO, since
/// one reason we may pause the contract is when CFO or COO accounts are
/// compromised.
/// @notice This is public rather than external so it can be called by
/// derived contracts.
function unpause() public onlyCEO whenPaused {
// can't unpause if contract was upgraded
paused = false;
}
}
成员变量:
address ceoAddress:CEO账户的addressaddress cfoAddress:CFO账户的addressaddress cooAddress:COO账户的addressbool paused:合约是否被暂停,如果为true,大部分的功能将不可用
函数修饰符:
modifier onlyCEO():CEO账户专属的修饰符,被该修饰符修饰的函数只有CEO才能访问modifier onlyCFO():CFO账户专属的修饰符,被该修饰符修饰的函数只有CFO才能访问modifier onlyCOO():COO账户专属的修饰符,被该修饰符修饰的函数只有COO才能访问modifier onlyCLevel():CEO、CFO、COO的修饰符,被该修饰符修饰的函数只有是CXO们才可以访问modifier whenNotPaused():修饰符,被修饰的函数只有成员变量pause为false才能调用modifier whenPaused():修饰符,被修饰的函数只有成员变量pause为true才能调用
一般函数:
setCEO(address _newCEO) external onlyCEO:设置新的CEO,只有当前CEO才能调用setCFO(address _newCFO) external onlyCEO:设置新的CFO,只有当前CEO才能调用setCOO(address _newCOO) external onlyCEO:设置新的COO,只有当前CEO才能调用pause() external onlyCLevel whenNotPaused:合约状态不是暂停时,将合约状态设为暂停,即成员变量pause变为true,只有CXO们可以调用。一般只在系统出现bug时使用来减少损失unpause() public onlyCEO whenPaused:解除合约的暂停状态,只有CEO才能调用,因为暂停合约的一个可能的原因就是CFO或COO账户被泄露
1万+

被折叠的 条评论
为什么被折叠?



