接 上一節,繼續學習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) })