引言
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賬戶的address
address cfoAddress
:CFO賬戶的address
address cooAddress
:COO賬戶的address
bool 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賬戶被泄露