接 上一节,继续学习solidity高级语法。
继续前面上一节 NumberInterface 的例子,咱们既然将接口定义为:java
contract NumberInterface { function getNum(address _myAddress) public view returns (uint); }
咱们能够在合约中这样使用:web
contract MyContract { address NumberInterfaceAddress = 0xab38...; // ^ 这是FavoriteNumber合约在以太坊上的地址 NumberInterface numberContract = NumberInterface(NumberInterfaceAddress); // 如今变量 `numberContract` 指向另外一个合约对象 function someFunction() public { // 如今咱们能够调用在那个合约中声明的 `getNum`函数: uint num = numberContract.getNum(msg.sender); // ...在这儿使用 `num`变量作些什么 } }
经过这种方式,只要将您合约的可见性设置为public
(公共)或external
(外部),它们就能够与以太坊区块链上的任何其余合约进行交互。segmentfault
咱们来建个本身的合约去读取另外一个智能合约-- CryptoKitties 的内容吧!api
ckAddress
的变量中。在下一行中,请建立一个名为 kittyContract
的 KittyInterface,并用 ckAddress
为它初始化 —— 就像咱们为 numberContract
所作的同样。zombiefeeding.sol
服务器
pragma solidity ^0.4.19; import "./zombiefactory.sol"; 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 ); } contract ZombieFeeding is ZombieFactory { address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d; // Initialize kittyContract here using `ckAddress` from above KittyInterface kittyContract = KittyInterface(ckAddress); function feedAndMultiply(uint _zombieId, uint _targetDna) public { require(msg.sender == zombieToOwner[_zombieId]); Zombie storage myZombie = zombies[_zombieId]; _targetDna = _targetDna % dnaModulus; uint newDna = (myZombie.dna + _targetDna) / 2; _createZombie("NoName", newDna); } }
getKitty
是咱们所看到的第一个返回多个值的函数。咱们来看看是如何处理的:函数
unction multipleReturns() internal returns(uint a, uint b, uint c) { return (1, 2, 3); } function processMultipleReturns() external { uint a; uint b; uint c; // 这样来作批量赋值: (a, b, c) = multipleReturns(); } // 或者若是咱们只想返回其中一个变量: function getLastReturnValue() external { uint c; // 能够对其余字段留空: (,,c) = multipleReturns(); }
是时候与 CryptoKitties
合约交互起来了!学习
咱们来定义一个函数,从 kitty
合约中获取它的基因:区块链
feedOnKitty
的函数。它须要2个 uint
类型的参数,_zombieId
和_kittyId
,这是一个 public
类型的函数。kittyDna
的 uint
。注意:在咱们的 KittyInterface 中,genes 是一个 uint256 类型的变量,可是若是你记得,咱们在第一课中提到过,uint 是 uint256 的别名,也就是说它们是一回事。
feedAndMultiply
,并传入了 _zombieId
和 kittyDna
两个参数。zombiefeeding.sol
ui
pragma solidity ^0.4.19; import "./zombiefactory.sol"; 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 ); } contract ZombieFeeding is ZombieFactory { address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d; KittyInterface kittyContract = KittyInterface(ckAddress); function feedAndMultiply(uint _zombieId, uint _targetDna) public { require(msg.sender == zombieToOwner[_zombieId]); Zombie storage myZombie = zombies[_zombieId]; _targetDna = _targetDna % dnaModulus; uint newDna = (myZombie.dna + _targetDna) / 2; _createZombie("NoName", newDna); } // define function here function feedOnKitty(uint _zombieId, uint _kittyId) public { uint kittyDna; // 声明一个参数 // 多参数返回,前边不须要的能够用空格,只获取须要的返回参数 (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId); feedAndMultiply(_zombieId, kittyDna); } }
咱们的功能逻辑主体已经完成了...如今让咱们来添一个奖励功能吧。url
这样吧,给从小猫制造出的僵尸添加些特征,以显示他们是猫僵尸。
要作到这一点,我们在新僵尸的DNA中添加一些特殊的小猫代码。
还记得吗,第一课中咱们提到,咱们目前只使用16位DNA的前12位数来指定僵尸的外观。因此如今咱们可使用最后2个数字来处理“特殊”的特征。
这样吧,把猫僵尸DNA的最后两个数字设定为99
(由于猫有9条命)。因此在咱们这么来写代码:若是
这个僵尸是一只猫变来的,就将它DNA的最后两位数字设置为99
。
if语句的语法在 Solidity 中,与在 JavaScript 中差很少:
function eatBLT(string sandwich) public { // 看清楚了,当咱们比较字符串的时候,须要比较他们的 keccak256 哈希码 if (keccak256(sandwich) == keccak256("BLT")) { eat(); } }
让咱们在咱们的僵尸代码中实现小猫的基因。
解释:假设 newDna 是 334455。那么 newDna%100 是 55,因此 newDna - newDna%100 获得 334400。最后加上 99 可获得 334499。
zombiefeeding.sol
pragma solidity ^0.4.19; import "./zombiefactory.sol"; 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 ); } contract ZombieFeeding is ZombieFactory { address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d; KittyInterface kittyContract = KittyInterface(ckAddress); // Modify function definition here: function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) public { require(msg.sender == zombieToOwner[_zombieId]); Zombie storage myZombie = zombies[_zombieId]; _targetDna = _targetDna % dnaModulus; uint newDna = (myZombie.dna + _targetDna) / 2; // Add an if statement here,添加if语句,而且将后两位数替换为99 if (keccak256(_species) == keccak256("kitty")) { newDna = newDna - newDna % 100 + 99; } _createZombie("NoName", newDna); } function feedOnKitty(uint _zombieId, uint _kittyId) public { uint kittyDna; (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId); // And modify function call here:添加参数 feedAndMultiply(_zombieId, kittyDna, "kitty"); } }
咱们只用编译和部署 ZombieFeeding
,就能够将这个合约部署到以太坊了。咱们最终完成的这个合约继承自 ZombieFactory
,所以它能够访问本身和父辈合约中的全部 public 方法。
咱们来看一个与咱们的刚部署的合约进行交互的例子, 这个例子使用了 JavaScript 和 web3.js:
var abi = /* abi generated by the compiler */ var ZombieFeedingContract = web3.eth.contract(abi) var contractAddress = /* our contract address on Ethereum after deploying */ var ZombieFeeding = ZombieFeedingContract.at(contractAddress) // 假设咱们有咱们的僵尸ID和要攻击的猫咪ID let zombieId = 1; let kittyId = 1; // 要拿到猫咪的DNA,咱们须要调用它的API。这些数据保存在它们的服务器上而不是区块链上。 // 若是一切都在区块链上,咱们就不用担忧它们的服务器挂了,或者它们修改了API, // 或者由于不喜欢咱们的僵尸游戏而封杀了咱们 let apiUrl = "https://api.cryptokitties.co/kitties/" + kittyId $.get(apiUrl, function(data) { let imgUrl = data.image_url // 一些显示图片的代码 }) // 当用户点击一只猫咪的时候: $(".kittyImage").click(function(e) { // 调用咱们合约的 `feedOnKitty` 函数 ZombieFeeding.feedOnKitty(zombieId, kittyId) }) // 侦听来自咱们合约的新僵尸事件好来处理 ZombieFactory.NewZombie(function(error, result) { if (error) return // 这个函数用来显示僵尸: generateZombie(result.zombieId, result.name, result.dna) })