您好,登錄后才能下訂單哦!
Silver CEO 星際區塊鏈信息發展有限公司
??這個項目是一個構建在以太坊上的游戲,感謝這個團隊給我們提供的案例:https://cryptozombies.io
??從功能的角度看,有如下腳本:
pragma solidity ^0.4.19;
import "./ownable.sol";
import "./safemath.sol";
contract ZombieFactory is Ownable {
using SafeMath for uint256;
// 事件是合約和區塊鏈通訊的一種機制。
// 當僵尸創造出來時,前端能監聽到這個事件,并將它顯示出來。
event NewZombie(uint zombieId, string name, uint dna);
/**
僵尸的NDA只有16個字符
dnaModulus等于10^16,DNA可以用模運算符 % 把一個整數變成16位
冷卻時間長達1天
**/
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
uint cooldownTime = 1 days;
// 使用struct節省存儲空間,節約gas。readyTime實現“冷卻定時器”
struct Zombie {
string name;
uint dna;
uint32 level;
uint32 readyTime;
uint16 winCount;
uint16 lossCount;
}
// 公共可變長數組
Zombie[] public zombies;
// 給數據庫中的僵尸指定主人,支持多玩家模式
// mapping和address,通過僵尸id查到擁有者的address
mapping (uint => address) public zombieToOwner;
// 通過address查詢到有多少只僵尸
mapping (address => uint) ownerZombieCount;
// 內部方法以 _ 開頭,函數里面的變量以 _ 開頭,區別全局變量。
function _createZombie(string _name, uint _dna) internal {
uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime), 0, 0)) - 1;
// msg.sender 是合約當前調用者的address或者智能合約的address
zombieToOwner[id] = msg.sender;
ownerZombieCount[msg.sender]++;
NewZombie(id, _name, _dna);
}
// 函數沒有改變Solidity里的狀態,沒有改變任何值或者寫任何東西,把函數定義為view,
// 意味著只能讀取數據不能更改數據
// keccak256是以太坊提供的SHA3散列函數,把一個字符串轉換成一個256位的16進制數字。
// 只能用它造一個偽隨機數。
function _generateRandomDna(string _str) private view returns (uint) {
uint rand = uint(keccak256(_str));
return rand % dnaModulus;
}
function createRandomZombie(string _name) public {
// require使函數在執行過程中,當不滿足某些條件時拋出錯誤,并停止執行
require(ownerZombieCount[msg.sender] == 0);
uint randDna = _generateRandomDna(_name);
randDna = randDna - randDna % 100;
_createZombie(_name, randDna);
}
}
pragma solidity ^0.4.19;
import "./zombiefactory.sol";
// 定義一個借口,使用contract關鍵字,在接口里定義getKitty函數
contract KittyInterface {
function getKitty(uint256 _id) external view returns (
bool isGestating,
bool isReady,
uint256 cooldownIndex,
uint256 nextActionAt,
uint256 siringWithId,
uint256 birthTime,
uint256 matronId,
uint256 sireId,
uint256 generation,
uint256 genes
);
}
// 繼承ZombieFactory合約
contract ZombieFeeding is ZombieFactory {
KittyInterface kittyContract;
// modifier關鍵字告訴編譯器,這是個modifier修飾符,而不是function,不能像函數直接調用,只能添加到函數定義的末尾,用以改變函數的行為
modifier onlyOwnerOf(uint _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
_;
}
// external 函數只能在合約之外調用,不能被合約內的其他函數調用
// 通過程序更改CryptoKities合約地址
// onlyOwner是指定合約的所有權,指定一個主人,只有主人(合約部署者)對它享有特權
function setKittyContractAddress(address _address) external onlyOwner {
kittyContract = KittyInterface(_address);
}
// internal 合約可訪問父合約中定義的內部函數
function _triggerCooldown(Zombie storage _zombie) internal {
_zombie.readyTime = uint32(now + cooldownTime);
}
function _isReady(Zombie storage _zombie) internal view returns (bool) {
return (_zombie.readyTime <= now);
}
function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal onlyOwnerOf(_zombieId) {
// storage變量永久存儲在區塊鏈中
Zombie storage myZombie = zombies[_zombieId];
require(_isReady(myZombie));
_targetDna = _targetDna % dnaModulus;
uint newDna = (myZombie.dna + _targetDna) / 2;
// 如果是kitty變過來的,用99替換新僵尸DNA的最后兩位數字
if (keccak256(_species) == keccak256("kitty")) {
newDna = newDna - newDna % 100 + 99;
}
_createZombie("NoName", newDna);
_triggerCooldown(myZombie);
}
function feedOnKitty(uint _zombieId, uint _kittyId) public {
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
feedAndMultiply(_zombieId, kittyDna, "kitty");
}
}
pragma solidity ^0.4.19;
import "./zombiehelper.sol";
/**
僵尸戰斗功能,繼承自ZombieHelper
**/
contract ZombieAttack is ZombieHelper {
uint randNonce = 0;
uint attackVictoryProbability = 70;
// 拿now,msg.sender,自增的nonce,轉成一個哈希值,再轉uint,% 100 取最后兩位,生成一個0到100的隨機數
// 當然最后拿到的是一個偽隨機數,但在具體的項目中,不會吸引×××者來×××就是現實安全的。
function randMod(uint _modulus) internal returns(uint) {
randNonce++;
return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
}
// 選擇自己的僵尸,選擇對手的一個僵尸去×××
// ×××方有70%的獲勝概率
// 所有的僵尸都有一個winCount和lossCount,記錄輸贏
// ×××方獲勝,僵尸升級并產生一個新僵尸,成功次數累加1
// ×××方失敗,失敗次數累加1
// 無論輸贏,當前僵尸的冷卻時間將被激活
function attack(uint _zombieId, uint _targetId) external onlyOwnerOf(_zombieId) {
Zombie storage myZombie = zombies[_zombieId];
Zombie storage enemyZombie = zombies[_targetId];
uint rand = randMod(100);
if (rand <= attackVictoryProbability) {
myZombie.winCount++;
myZombie.level++;
enemyZombie.lossCount++;
feedAndMultiply(_zombieId, enemyZombie.dna, "zombie");
} else {
myZombie.lossCount++;
enemyZombie.winCount++;
_triggerCooldown(myZombie);
}
}
}
/**
ERC20代幣,一個代幣只是一個追蹤誰擁有多少該代幣的合約,和一些可以讓那些用戶將它們的代幣轉移到其他地址的函數
ERC721 適合CryptpZombies這樣的加密收藏品,是ERC721代幣
ERC721代幣是不能互換的,因為每個代幣都被認為是唯一且不可分割的。只能以整個單位交易它們。
搞出一個ERC721的標準,好處顯而易見:我們不必在合約中實現拍賣和托管邏輯,符合其規范,其他人可以為
加密可交易的ERC721資產搭建交易平臺,ERC721僵尸也可在上面使用和交易。
**/
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint256 _tokenId) public view returns (address _owner);
function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
}
pragma solidity ^0.4.19;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
pragma solidity ^0.4.18;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
pragma solidity ^0.4.19;
import "./zombieattack.sol";
import "./erc721.sol";
import "./safemath.sol";
contract ZombieOwnership is ZombieAttack, ERC721 {
using SafeMath for uint256;
mapping (uint => address) zombieApprovals;
// 傳入address函數,返回address擁有多少ERC721代幣,整數不可分割
function balanceOf(address _owner) public view returns (uint256 _balance) {
return ownerZombieCount[_owner];
}
// 傳入一個代幣ID,也是僵尸ID,返回該代幣擁有者的address
function ownerOf(uint256 _tokenId) public view returns (address _owner) {
return zombieToOwner[_tokenId];
}
// 代幣的擁有者調用transfer方法,傳入需要轉移到的address和他想轉移的代幣的_tokenId。
// 合約安全增強:溢出和下溢
// 溢出(overflow),uint8的變量,存儲的最大數就是二進制11111111,也就是十進制的255,加1就出現溢出。
// 下溢(underflow),一個等于0的uint8,減去1就出現下溢,變成255,uint是無符號的,不能等于復數。
function _transfer(address _from, address _to, uint256 _tokenId) private {
ownerZombieCount[_to] = ownerZombieCount[_to].add(1);
ownerZombieCount[msg.sender] = ownerZombieCount[msg.sender].sub(1);
zombieToOwner[_tokenId] = _to;
Transfer(_from, _to, _tokenId);
}
// 都有一個修飾符onlyOwnerOf
function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
_transfer(msg.sender, _to, _tokenId);
}
// 代幣的擁有者調用approve,傳入允許提取代幣的address和允許提取的代幣_tokenId。
function approve(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
zombieApprovals[_tokenId] = _to;
Approval(msg.sender, _to, _tokenId);
}
// require檢查確保msg.sender已經被批準來提取這個代幣,也就是僵尸。
function takeOwnership(uint256 _tokenId) public {
require(zombieApprovals[_tokenId] == msg.sender);
address owner = ownerOf(_tokenId);
_transfer(owner, msg.sender, _tokenId);
}
}
pragma solidity ^0.4.19;
import "./zombiefeeding.sol";
/**
輔助方法
**/
contract ZombieHelper is ZombieFeeding {
uint levelUpFee = 0.001 ether;
// 修飾符modifier的最后一行為 _ ,表示修飾符調用結束后返回,并執行調用函數余下的部分。
modifier aboveLevel(uint _level, uint _zombieId) {
require(zombies[_zombieId].level >= _level);
_;
}
// 提現功能,只能是合約主人享有
// 通過transfer函數向一個地址發送以太
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
// 合約主人設置levelUpFee
function setLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}
// payable修飾符,是一種可以接收以太的特殊函數
// 通過支付ETH來升級僵尸,ETH將存儲在你擁有的合約中
function levelUp(uint _zombieId) external payable {
require(msg.value == levelUpFee);
zombies[_zombieId].level++;
}
// 2級以上的僵尸可以改名
function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) onlyOwnerOf(_zombieId) {
zombies[_zombieId].name = _newName;
}
// 20級以上的僵尸可以定制DNA
function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) onlyOwnerOf(_zombieId) {
zombies[_zombieId].dna = _newDna;
}
// 查看某個某家的整個僵尸軍團,只需要從區塊鏈中讀取數據,view函數。
function getZombiesByOwner(address _owner) external view returns(uint[]) {
// memory 關鍵字是使用臨時存儲,調用完就釋放
uint[] memory result = new uint[](ownerZombieCount[_owner]);
uint counter = 0;
for (uint i = 0; i < zombies.length; i++) {
if (zombieToOwner[i] == _owner) {
result[counter] = i;
counter++;
}
}
return result;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CryptoZombies front-end</title>
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script language="javascript" type="text/javascript" src="web3.min.js"></script>
<script language="javascript" type="text/javascript" src="cryptozombies_abi.js"></script>
</head>
<body>
<div id="txStatus"></div>
<div id="zombies"></div>
<script>
var cryptoZombies;
var userAccount;
// Web3.js需要合約地址和ABI來和合約進行對話
// 編譯了ABI放在cryptozobimes_abi.js文件中,保存為一個名為cryptoZombiesABI的變量中
// Web3.js有兩個方法來調用合約的函數:call和send
// call用來調用view和pure函數,只運行在本地節點,不會在區塊鏈上創建事務
// send將創建一個事務并改變區塊鏈上的數據,用send調用非view和pure的函數
function startApp() {
var cryptoZombiesAddress = "YOUR_CONTRACT_ADDRESS";
cryptoZombies = new web3js.eth.Contract(cryptoZombiesABI, cryptoZombiesAddress);
// 在MetaMask中切換賬戶,應用需要監控這個變量,通過setInterval方法來實現
var accountInterval = setInterval(function() {
// Check if account has changed
if (web3.eth.accounts[0] !== userAccount) {
userAccount = web3.eth.accounts[0];
// Call a function to update the UI with the new account
getZombiesByOwner(userAccount)
.then(displayZombies);
}
}, 100);
// Web3 provider說明跟哪個節點進行交互處理我們的讀寫。
// Infura是一個服務,維護了大量以太坊節點并提供一個緩存層來實現高速讀取。
var web3Infura = new Web3(new Web3.providers.WebsocketProvider("wss://mainnet.infura.io/ws"));
var czEvents = new web3Infura.eth.Contract(cryptoZombiesABI, cryptoZombiesAddress);
// 任何僵尸生成的時候激發一個警告信息,
// 如果只想監聽對當前用戶的提醒呢,使用indexed關鍵字
// 例如 event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
// _from 和 _to 都是indexed,可在前端事件監聽中過濾事件
czEvents.events.Transfer({ filter: { _to: userAccount } })
.on("data", function(event) {
let data = event.returnValues;
getZombiesByOwner(userAccount).then(displayZombies);
}).on('error', console.error);
}
function displayZombies(ids) {
$("#zombies").empty();
for (id of ids) {
// Look up zombie details from our contract. Returns a `zombie` object
getZombieDetails(id)
.then(function(zombie) {
// Using ES6's "template literals" to inject variables into the HTML.
// Append each one to our #zombies div
$("#zombies").append(`<div class="zombie">
<ul>
<li>Name: ${zombie.name}</li>
<li>DNA: ${zombie.dna}</li>
<li>Level: ${zombie.level}</li>
<li>Wins: ${zombie.winCount}</li>
<li>Losses: ${zombie.lossCount}</li>
<li>Ready Time: ${zombie.readyTime}</li>
</ul>
</div>`);
});
}
}
function createRandomZombie(name) {
// This is going to take a while, so update the UI to let the user know
// the transaction has been sent
$("#txStatus").text("Creating new zombie on the blockchain. This may take a while...");
// Send the tx to our contract:
return cryptoZombies.methods.createRandomZombie(name)
.send({ from: userAccount })
.on("receipt", function(receipt) {
$("#txStatus").text("Successfully created " + name + "!");
// Transaction was accepted into the blockchain, let's redraw the UI
getZombiesByOwner(userAccount).then(displayZombies);
})
.on("error", function(error) {
// Do something to alert the user their transaction has failed
$("#txStatus").text(error);
});
}
function feedOnKitty(zombieId, kittyId) {
$("#txStatus").text("Eating a kitty. This may take a while...");
return cryptoZombies.methods.feedOnKitty(zombieId, kittyId)
.send({ from: userAccount })
.on("receipt", function(receipt) {
$("#txStatus").text("Ate a kitty and spawned a new Zombie!");
getZombiesByOwner(userAccount).then(displayZombies);
})
.on("error", function(error) {
$("#txStatus").text(error);
});
}
function levelUp(zombieId) {
$("#txStatus").text("Leveling up your zombie...");
return cryptoZombies.methods.levelUp(zombieId)
.send({ from: userAccount, value: web3.utils.toWei("0.001", "ether") })
.on("receipt", function(receipt) {
$("#txStatus").text("Power overwhelming! Zombie successfully leveled up");
})
.on("error", function(error) {
$("#txStatus").text(error);
});
}
function getZombieDetails(id) {
return cryptoZombies.methods.zombies(id).call()
}
function zombieToOwner(id) {
return cryptoZombies.methods.zombieToOwner(id).call()
}
function getZombiesByOwner(owner) {
return cryptoZombies.methods.getZombiesByOwner(owner).call()
}
window.addEventListener('load', function() {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// Use Mist/MetaMask's provider
web3js = new Web3(web3.currentProvider);
} else {
// Handle the case where the user doesn't have Metamask installed
// Probably show them a message prompting them to install Metamask
}
// Now you can start your app & access web3 freely:
startApp()
})
</script>
</body>
</html>
var cryptozombiesABI = [
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_tokenId",
"type": "uint256"
}
],
"name": "approve",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_zombieId",
"type": "uint256"
}
],
"name": "levelUp",
"outputs": [],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_zombieId",
"type": "uint256"
},
{
"name": "_kittyId",
"type": "uint256"
}
],
"name": "feedOnKitty",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint256"
}
],
"name": "zombies",
"outputs": [
{
"name": "name",
"type": "string"
},
{
"name": "dna",
"type": "uint256"
},
{
"name": "level",
"type": "uint32"
},
{
"name": "readyTime",
"type": "uint32"
},
{
"name": "winCount",
"type": "uint16"
},
{
"name": "lossCount",
"type": "uint16"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "withdraw",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
}
],
"name": "getZombiesByOwner",
"outputs": [
{
"name": "",
"type": "uint256[]"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint256"
}
],
"name": "zombieToOwner",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_address",
"type": "address"
}
],
"name": "setKittyContractAddress",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_zombieId",
"type": "uint256"
},
{
"name": "_newDna",
"type": "uint256"
}
],
"name": "changeDna",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_tokenId",
"type": "uint256"
}
],
"name": "ownerOf",
"outputs": [
{
"name": "_owner",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"name": "_balance",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_name",
"type": "string"
}
],
"name": "createRandomZombie",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "owner",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_tokenId",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getAllZombies",
"outputs": [
{
"name": "",
"type": "uint256[]"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_tokenId",
"type": "uint256"
}
],
"name": "takeOwnership",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_zombieId",
"type": "uint256"
},
{
"name": "_newName",
"type": "string"
}
],
"name": "changeName",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_fee",
"type": "uint256"
}
],
"name": "setLevelUpFee",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_zombieId",
"type": "uint256"
},
{
"name": "_targetId",
"type": "uint256"
}
],
"name": "attack",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_from",
"type": "address"
},
{
"indexed": true,
"name": "_to",
"type": "address"
},
{
"indexed": false,
"name": "_tokenId",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_owner",
"type": "address"
},
{
"indexed": true,
"name": "_approved",
"type": "address"
},
{
"indexed": false,
"name": "_tokenId",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "attackResult",
"type": "bool"
},
{
"indexed": false,
"name": "winCount",
"type": "uint16"
},
{
"indexed": false,
"name": "lossCount",
"type": "uint16"
}
],
"name": "AttackResult",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "zombieId",
"type": "uint256"
},
{
"indexed": false,
"name": "name",
"type": "string"
},
{
"indexed": false,
"name": "dna",
"type": "uint256"
}
],
"name": "NewZombie",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
}
]
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。