美文网首页
通过一个案例精通以太坊智能合约和Solidity

通过一个案例精通以太坊智能合约和Solidity

作者: 建怀 | 来源:发表于2018-09-17 17:11 被阅读0次

    通过一个案例精通以太坊智能合约和Solidity

    项目组件

      这个项目是一个构建在以太坊上的游戏,感谢这个团队给我们提供的案例:https://cryptozombies.io

      从功能的角度看,有如下脚本:

    • zombiefactory.sol:定义zombie和生成zombie。
    • zombiefeeding.sol:定义小猫接口,给zombie吃小猫。
    • zombieattack.sol:zombie打架的功能。
    • erc721.sol:ERC721代币的接口。
    • ownable.sol:用户认证的接口。
    • safemath.sol:运算时安全检查。
    • zombieownership.sol:zombie的所属功能,transfer,approve,takeOwnership等功能。
    • zombiehelper.sol:zombie的辅助功能。该名字,改DNA,提升等级,设置levelUpFee,提现,查看僵尸军团等功能。
    • index.html:前端交互调用。
    • cryptozombies_abi.js:ABI文档。

    zombiefactory.sol

    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);
      }
    
    }
    

    zombiefeeding.sol

    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");
      }
    }
    

    zombieattack.sol

    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);
        }
      }
    }
    

    erc721.sol

    /**
      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;
    }
    

    ownable.sol

    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;
      }
    
    }
    

    safemath.sol

    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;
      }
    }
    

    zombieownership.sol

    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);
      }
    }
    

    zombiehelper.sol

    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;
      }
    
    }
    

    index.html

    <!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>
    

    cryptozombies_abi.js

    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"
      }
    ]      
    

    相关文章

      网友评论

          本文标题:通过一个案例精通以太坊智能合约和Solidity

          本文链接:https://www.haomeiwen.com/subject/mpyrnftx.html