分享一個(gè)很有趣的以太坊開(kāi)發(fā)教程亭敢。從零開(kāi)始手把手教你以太坊開(kāi)發(fā)。
第1章: 課程概述
第一課你將創(chuàng)造一個(gè)"僵尸工廠", 用它建立一支僵尸部隊(duì)。
- 我們的工廠會(huì)把我們部隊(duì)中所有的僵尸保存到數(shù)據(jù)庫(kù)中
- 工廠會(huì)有一個(gè)函數(shù)能產(chǎn)生新的僵尸
- 每個(gè)僵尸會(huì)有一個(gè)隨機(jī)的獨(dú)一無(wú)二的面孔
在后面的課程里害碾,我們會(huì)增加功能。比如赦拘,讓僵尸能攻擊人類或其它僵尸! 但是在實(shí)現(xiàn)這些好玩的功能之前慌随,我們先要實(shí)現(xiàn)創(chuàng)建僵尸這樣的基本功能。
僵尸DNA如何運(yùn)作
僵尸的面孔取決于它的DNA。它的DNA很簡(jiǎn)單阁猜,由一個(gè)16位的整數(shù)組成:
8356281049284737
如同真正的DNA, 這個(gè)數(shù)字的不同部分會(huì)對(duì)應(yīng)不同的特點(diǎn)丸逸。 前2位代表頭型,緊接著的2位代表眼睛蹦漠,等等椭员。
注: 本教程我們盡量簡(jiǎn)化车海。我們的僵尸只有7種頭型(雖然2位數(shù)字允許100種可能性)笛园。以后我們會(huì)加入更多的頭型, 如果我們想讓僵尸有更多造型。
例如侍芝,前兩位數(shù)字是 83
研铆, 計(jì)算僵尸的頭型,我們做83 % 7 + 1
= 7 運(yùn)算州叠, 此僵尸將被賦予第七類頭型棵红。
在右邊頁(yè)面,移動(dòng)頭基因head gene
滑塊到第七位置(圣誕帽)可見(jiàn)83
所對(duì)應(yīng)的特點(diǎn)咧栗。
實(shí)戰(zhàn)演習(xí)
- 玩一下頁(yè)面右側(cè)的滑塊逆甜。檢驗(yàn)一下不同的數(shù)字對(duì)應(yīng)不同的僵尸的長(zhǎng)相。
第2章: 合約
從最基本的開(kāi)始入手:
Solidity 的代碼都包裹在合約里面. 一份合約
就是以太應(yīng)幣應(yīng)用的基本模塊致板, 所有的變量和函數(shù)都屬于一份合約, 它是你所有應(yīng)用的起點(diǎn).
一份名為 HelloWorld
的空合約如下:
contract HelloWorld {
}
版本指令
所有的 Solidity 源碼都必須冠以 "version pragma" — 標(biāo)明 Solidity 編譯器的版本. 以避免將來(lái)新的編譯器可能破壞你的代碼交煞。
例如: pragma solidity ^0.4.19
; (當(dāng)前 Solidity 的最新版本是 0.4.19).
綜上所述, 下面就是一個(gè)最基本的合約 — 每次建立一個(gè)新的項(xiàng)目時(shí)的第一段代碼:
pragma solidity ^0.4.19;
contract HelloWorld {
}
實(shí)戰(zhàn)演習(xí)
為了建立我們的僵尸部隊(duì)斟或, 讓我們先建立一個(gè)基礎(chǔ)合約素征,稱為
ZombieFactory
。在右邊的輸入框里輸入
0.4.19
萝挤,我們的合約基于這個(gè)版本的編譯器御毅。建立一個(gè)空合約
ZombieFactory
。
pragma solidity ^0.4.19;
contract ZombieFactory {
}
第3章: 狀態(tài)變量和整數(shù)
真棒怜珍!我們已經(jīng)為我們的合約做了一個(gè)外殼端蛆, 下面學(xué)習(xí) Solidity 中如何使用變量。
狀態(tài)變量是被永久地保存在合約中酥泛。也就是說(shuō)它們被寫入以太幣區(qū)塊鏈中. 想象成寫入一個(gè)數(shù)據(jù)庫(kù)今豆。
例子:
contract Example {
// 這個(gè)無(wú)符號(hào)整數(shù)將會(huì)永久的被保存在區(qū)塊鏈中
uint myUnsignedInteger = 100;
}
在上面的例子中,定義 myUnsignedInteger
為 uint
類型揭璃,并賦值100晚凿。
無(wú)符號(hào)整數(shù): uint
uint
無(wú)符號(hào)數(shù)據(jù)類型, 指其值不能是負(fù)數(shù)瘦馍,對(duì)于有符號(hào)的整數(shù)存在名為 int
的數(shù)據(jù)類型歼秽。
注: Solidity中,
uint
實(shí)際上是uint256
代名詞情组, 一個(gè)256位的無(wú)符號(hào)整數(shù)燥筷。你也可以定義位數(shù)少的uints —uint8
箩祥,uint16
,uint32
肆氓, 等…… 但一般來(lái)講你愿意使用簡(jiǎn)單的uint
袍祖, 除非在某些特殊情況下,這我們后面會(huì)講谢揪。
實(shí)戰(zhàn)演習(xí)
我們的僵尸DNA將由一個(gè)十六位數(shù)字組成蕉陋。
定義
dnaDigits
為uint
數(shù)據(jù)類型, 并賦值16
。
pragma solidity ^0.4.19;
contract ZombieFactory {
uint dnaDigits = 16;
}
第4章: 數(shù)學(xué)運(yùn)算
在 Solidity 中拨扶,數(shù)學(xué)運(yùn)算很直觀明了凳鬓,與其它程序設(shè)計(jì)語(yǔ)言相同:
- 加法:
x + y
- 減法:
x - y
, - 乘法:
x * y
- 除法:
x / y
- 取模 / 求余:
x % y
(例如,13 % 5
余3
, 因?yàn)?3除以5,余3)
Solidity 還支持 乘方操作 (如:x 的 y次方) // 例如: 5 ** 2 = 25
uint x = 5 ** 2; // equal to 5^2 = 25
實(shí)戰(zhàn)演習(xí)
為了保證我們的僵尸的DNA只含有16個(gè)字符患民,我們先造一個(gè)uint
數(shù)據(jù)缩举,讓它等于10^16。這樣一來(lái)以后我們可以用模運(yùn)算符 %
把一個(gè)整數(shù)變成16位匹颤。
- 建立一個(gè)
uin
t類型的變量仅孩,名字叫dnaModulus
, 令其等于 10 的dnaDigits
次方.
pragma solidity ^0.4.19;
contract ZombieFactory {
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
}
第5章: 結(jié)構(gòu)體
有時(shí)你需要更復(fù)雜的數(shù)據(jù)類型,Solidity 提供了 結(jié)構(gòu)體:
struct Person {
uint age;
string name;
}
結(jié)構(gòu)體允許你生成一個(gè)更復(fù)雜的數(shù)據(jù)類型印蓖,它有多個(gè)屬性辽慕。
注:我們剛剛引進(jìn)了一個(gè)新類型,
string
。 字符串用于保存任意長(zhǎng)度的 UTF-8 編碼數(shù)據(jù)另伍。 如:string greeting = "Hello world!"
鼻百。
實(shí)戰(zhàn)演習(xí)
在我們的程序中,我們將創(chuàng)建一些僵尸摆尝!每個(gè)僵尸將擁有多個(gè)屬性温艇,所以這是一個(gè)展示結(jié)構(gòu)體的完美例子。
建立一個(gè)
struct
命名為Zombie
.我們的
Zombie
結(jié)構(gòu)體有兩個(gè)屬性:name
(類型為string
), 和dna
(類型為uint
)堕汞。
pragma solidity ^0.4.19;
contract ZombieFactory {
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
}
第6章: 數(shù)組
如果你想建立一個(gè)集合勺爱,可以用 數(shù)組這樣的數(shù)據(jù)類型. Solidity 支持兩種數(shù)組: 靜態(tài) 數(shù)組和動(dòng)態(tài) 數(shù)組:
// 固定長(zhǎng)度為2的靜態(tài)數(shù)組:
uint[2] fixedArray;
// 固定長(zhǎng)度為5的string類型的靜態(tài)數(shù)組:
string[5] stringArray;
// 動(dòng)態(tài)數(shù)組,長(zhǎng)度不固定讯检,可以動(dòng)態(tài)添加元素:
uint[] dynamicArray;
你也可以建立一個(gè) 結(jié)構(gòu)體類型的數(shù)組 例如琐鲁,上一章提到的 Person
:
Person[] people; // dynamic Array, we can keep adding to it
記住:狀態(tài)變量被永久保存在區(qū)塊鏈中人灼。所以在你的合約中創(chuàng)建動(dòng)態(tài)數(shù)組來(lái)保存成結(jié)構(gòu)的數(shù)據(jù)是非常有意義的围段。
公共數(shù)組
你可以定義 public 數(shù)組, Solidity 會(huì)自動(dòng)創(chuàng)建 getter 方法. 語(yǔ)法如下:
Person[] public people;
其它的合約可以從這個(gè)數(shù)組讀取數(shù)據(jù)(但不能寫入數(shù)據(jù)),所以這在合約中是一個(gè)有用的保存公共數(shù)據(jù)的模式投放。
實(shí)戰(zhàn)演習(xí)
為了把一個(gè)僵尸部隊(duì)保存在我們的APP里奈泪,并且能夠讓其它APP看到這些僵尸,我們需要一個(gè)公共數(shù)組。
- 創(chuàng)建一個(gè)數(shù)據(jù)類型為
Zombie
的結(jié)構(gòu)體數(shù)組涝桅,用public
修飾拜姿,命名為:zombies
.
pragma solidity ^0.4.19;
contract ZombieFactory {
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
uint dna;
string name;
}
Zombie[] public zombies;
}
第7章: 定義函數(shù)
在 Solidity 中函數(shù)定義的句法如下:
function eatHamburgers(string _name, uint _amount) {
}
這是一個(gè)名為 eatHamburgers
的函數(shù),它接受兩個(gè)參數(shù):一個(gè) string
類型的 和 一個(gè) uint
類型的》胨欤現(xiàn)在函數(shù)內(nèi)部還是空的蕊肥。
注:習(xí)慣上函數(shù)里的變量都是以(_)開(kāi)頭 (但不是硬性規(guī)定) 以區(qū)別全局變量。我們整個(gè)教程都會(huì)沿用這個(gè)習(xí)慣蛤肌。
我們的函數(shù)定義如下:
eatHamburgers("vitalik", 100);
實(shí)戰(zhàn)演習(xí)
在我們的應(yīng)用里壁却,我們要能創(chuàng)建一些僵尸,讓我們寫一個(gè)函數(shù)做這件事吧寻定!
- 建立一個(gè)函數(shù)
createZombie
儒洛。 它有兩個(gè)參數(shù): _name (類型為string
), 和 _dna (類型為uint
)。
暫時(shí)讓函數(shù)空著——我們?cè)诤竺鏁?huì)增加內(nèi)容狼速。
pragma solidity ^0.4.19;
contract ZombieFactory {
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
function createZombie(string _name, uint _dna) {
}
}
第8章: 使用結(jié)構(gòu)體和數(shù)組
創(chuàng)建新的結(jié)構(gòu)體
還記得上個(gè)例子中的 Person 結(jié)構(gòu)嗎?
struct Person {
uint age;
string name;
}
Person[] public people;
現(xiàn)在我們學(xué)習(xí)創(chuàng)建新的 Person
結(jié)構(gòu)卦停,然后把它加入到名為 people
的數(shù)組中.
// 創(chuàng)建一個(gè)新的Person:
Person satoshi = Person(172, "Satoshi");
// 將新創(chuàng)建的satoshi添加進(jìn)people數(shù)組:
people.push(satoshi);
你也可以兩步并一步向胡,用一行代碼更簡(jiǎn)潔:
people.push(Person(16, "Vitalik"));
注:array.push() 在數(shù)組的 尾部 加入新元素 ,所以元素在數(shù)組中的順序就是我們添加的順序惊完, 如:
uint[] numbers;
numbers.push(5);
numbers.push(10);
numbers.push(15);
// numbers is now equal to [5, 10, 15]
實(shí)戰(zhàn)演習(xí)
讓我們創(chuàng)建名為createZombie的函數(shù)來(lái)做點(diǎn)兒什么吧僵芹。
- 在函數(shù)體里新創(chuàng)建一個(gè)
Zombie
, 然后把它加入zombies
數(shù)組中小槐。 新創(chuàng)建的僵尸的name
和dna
拇派,來(lái)自于函數(shù)的參數(shù)。 - 讓我們用一行代碼簡(jiǎn)潔地完成它凿跳。
pragma solidity ^0.4.19;
contract ZombieFactory {
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
function createZombie(string _name, uint _dna) {
zombies.push(Zombie(_name, _dna));
}
}
第9章: 私有 / 公共函數(shù)
Solidity 定義的函數(shù)的屬性默認(rèn)為公共
件豌。 這就意味著任何一方 (或其它合約) 都可以調(diào)用你合約里的函數(shù)。
顯然控嗜,不是什么時(shí)候都需要這樣茧彤,而且這樣的合約易于受到攻擊。 所以將自己的函數(shù)定義為私有
是一個(gè)好的編程習(xí)慣疆栏,只有當(dāng)你需要外部世界調(diào)用它時(shí)才將它設(shè)置為公共
曾掂。
如何定義一個(gè)私有的函數(shù)呢?
uint[] numbers;
function _addToArray(uint _number) private {
numbers.push(_number);
}
這意味著只有我們合約中的其它函數(shù)才能夠調(diào)用這個(gè)函數(shù)壁顶,給 numbers
數(shù)組添加新成員珠洗。
可以看到,在函數(shù)名字后面使用關(guān)鍵字 private
即可若专。和函數(shù)的參數(shù)類似许蓖,私有函數(shù)的名字用(_
)起始。
實(shí)戰(zhàn)演習(xí)
我們合約的函數(shù) createZombie
的默認(rèn)屬性是公共的,這意味著任何一方都可以調(diào)用它去創(chuàng)建一個(gè)僵尸蛔糯。 咱們來(lái)把它變成私有吧拯腮!
- 變
createZombie
為私有函數(shù),不要忘記遵守命名的規(guī)矩哦蚁飒!
pragma solidity ^0.4.19;
contract ZombieFactory {
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
function _createZombie(string _name, uint _dna) private {
zombies.push(Zombie(_name, _dna));
}
}
第10章: 函數(shù)的更多屬性
返回值
要想函數(shù)返回一個(gè)數(shù)值动壤,按如下定義:
string greeting = "What's up dog";
function sayHello() public returns (string) {
return greeting;
}
Solidity 里,函數(shù)的定義里可包含返回值的數(shù)據(jù)類型(如本例中 string
)淮逻。
函數(shù)的修飾符
上面的函數(shù)實(shí)際上沒(méi)有改變 Solidity 里的狀態(tài)琼懊,即,它沒(méi)有改變?nèi)魏沃祷蛘邔懭魏螙|西爬早。
這種情況下我們可以把函數(shù)定義為 view
, 意味著它只能讀取數(shù)據(jù)不能更改數(shù)據(jù):
function _generateRandomDna(string _str) private view returns(uint){
}
Solidity 還支持 pure
函數(shù), 表明這個(gè)函數(shù)甚至都不訪問(wèn)應(yīng)用里的數(shù)據(jù)哼丈,例如:
function _multiply(uint a, uint b) private pure returns (uint) {
return a * b;
}
這個(gè)函數(shù)甚至都不讀取應(yīng)用里的狀態(tài) — 它的返回值完全取決于它的輸入?yún)?shù),在這種情況下我們把函數(shù)定義為 pure
.
注:可能很難記住何時(shí)把函數(shù)標(biāo)記為 pure/view筛严。 幸運(yùn)的是醉旦, Solidity 編輯器會(huì)給出提示,提醒你使用這些修飾符桨啃。
實(shí)戰(zhàn)演習(xí)
我們想建立一個(gè)幫助函數(shù)车胡,它根據(jù)一個(gè)字符串隨機(jī)生成一個(gè)DNA數(shù)據(jù)。
創(chuàng)建一個(gè)
private
函數(shù)照瘾,命名為_generateRandomDna
匈棘。它只接收一個(gè)輸入變量_str
(類型string
), 返回一個(gè)uint
類型的數(shù)值。此函數(shù)只讀取我們合約中的一些變量析命,所以標(biāo)記為
view
主卫。函數(shù)內(nèi)部暫時(shí)留空,以后我們?cè)偬砑哟a鹃愤。
pragma solidity ^0.4.19;
contract ZombieFactory {
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
function _createZombie(string _name, uint _dna) private {
zombies.push(Zombie(_name, _dna));
}
function _generateRandomDna(string _str) private view returns (uint) {
}
}
第11章: Keccak256 和 類型轉(zhuǎn)換
Keccak256
如何讓 _generateRandomDna
函數(shù)返回一個(gè)全(半) 隨機(jī)的 uint
?
Ethereum 內(nèi)部有一個(gè)散列函數(shù)keccak256
簇搅,它用了SHA3版本。一個(gè)散列函數(shù)基本上就是把一個(gè)字符串轉(zhuǎn)換為一個(gè)256位的16進(jìn)制數(shù)字昼浦。字符串的一個(gè)微小變化會(huì)引起散列數(shù)據(jù)極大變化馍资。
這在 Ethereum 中有很多應(yīng)用,但是現(xiàn)在我們只是用它造一個(gè)偽隨機(jī)數(shù)关噪。
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256("aaaab");
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256("aaaac");
顯而易見(jiàn)鸟蟹,輸入字符串只改變了一個(gè)字母,輸出就已經(jīng)天壤之別了使兔。
注: 在區(qū)塊鏈中安全地產(chǎn)生一個(gè)隨機(jī)數(shù)是一個(gè)很難的問(wèn)題建钥, 本例的方法不安全,但是在我們的Zombie DNA算法里不是那么重要虐沥,已經(jīng)很好地滿足我們的需要了熊经。
類型轉(zhuǎn)換
有時(shí)你需要變換數(shù)據(jù)類型泽艘。例如:
uint8 a = 5;
uint b = 6;
// 將會(huì)拋出錯(cuò)誤,因?yàn)?a * b 返回 uint, 而不是 uint8:
uint8 c = a * b;
// 我們需要將 b 轉(zhuǎn)換為 uint8:
uint8 c = a * uint8(b);
上面, a * b
返回類型是 uint
, 但是當(dāng)我們嘗試用 uint8
類型接收時(shí), 就會(huì)造成潛在的錯(cuò)誤镐依。如果把它的數(shù)據(jù)類型轉(zhuǎn)換為 uint8
, 就可以了匹涮,編譯器也不會(huì)出錯(cuò)。
實(shí)戰(zhàn)演習(xí)
-
給
_generateRandomDna
函數(shù)添加代碼! 它應(yīng)該完成如下功能:第一行代碼取
_str
的keccak256
散列值生成一個(gè)偽隨機(jī)十六進(jìn)制數(shù)槐壳,類型轉(zhuǎn)換為uint
, 最后保存在類型為uint
名為rand
的變量中然低。第二行代碼應(yīng)該
return
上面計(jì)算的數(shù)值對(duì)dnaModulus
求余數(shù)(%
)。
pragma solidity ^0.4.19;
contract ZombieFactory {
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
function _createZombie(string _name, uint _dna) private {
zombies.push(Zombie(_name, _dna));
}
function _generateRandomDna(string _str) private view returns (uint) {
uint rand = uint(keccak256(_str));
return rand % dnaModulus;
}
}
第12章: 放在一起
我們就快完成我們的隨機(jī)僵尸制造器了务唐,來(lái)寫一個(gè)公共的函數(shù)把所有的部件連接起來(lái)雳攘。
寫一個(gè)公共函數(shù),它有一個(gè)參數(shù)枫笛,用來(lái)接收僵尸的名字吨灭,之后用它生成僵尸的DNA。
實(shí)戰(zhàn)演習(xí)
創(chuàng)建一個(gè)
public
函數(shù)刑巧,命名為createRandomZombie
. 它將被傳入一個(gè)變量_name
(數(shù)據(jù)類型是string
)喧兄。 (注: 定義公共函數(shù) public 和定義一個(gè)私有 private 函數(shù)的做法一樣)。函數(shù)的第一行應(yīng)該調(diào)用
_generateRandomDna
函數(shù)海诲,傳入_name
參數(shù), 結(jié)果保存在一個(gè)類型為uint
的變量里繁莹,命名為randDna
。第二行調(diào)用
_createZombie
函數(shù)特幔, 傳入?yún)?shù):_name
和randDna
。整個(gè)函數(shù)應(yīng)該是4行代碼 (包括函數(shù)的結(jié)束符號(hào)
}
)闸昨。
pragma solidity ^0.4.19;
contract ZombieFactory {
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
function _createZombie(string _name, uint _dna) private {
zombies.push(Zombie(_name, _dna));
}
function _generateRandomDna(string _str) private view returns (uint) {
uint rand = uint(keccak256(_str));
return rand % dnaModulus;
}
function createRandomZombie(string _name) public {
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
}
第13章: 事件
我們的合約幾乎就要完成了蚯斯!讓我們加上一個(gè)事件.
事件 是合約和區(qū)塊鏈通訊的一種機(jī)制。你的前端應(yīng)用“監(jiān)聽(tīng)”某些事件饵较,并做出反應(yīng)拍嵌。
例子:
// 這里建立事件
event IntegersAdded(uint x, uint y, uint result);
function add(uint _x, uint _y) public {
uint result = _x + _y;
//觸發(fā)事件,通知app
IntegersAdded(_x, _y, result);
return result;
}
你的 app 前端可以監(jiān)聽(tīng)這個(gè)事件循诉。JavaScript 實(shí)現(xiàn)如下:
YourContract.IntegersAdded(function(error, result) {
// 干些事
}
實(shí)戰(zhàn)演習(xí)
我們想每當(dāng)一個(gè)僵尸創(chuàng)造出來(lái)時(shí)横辆,我們的前端都能監(jiān)聽(tīng)到這個(gè)事件,并將它顯示出來(lái)茄猫。
1狈蚤。 定義一個(gè) 事件
叫做 NewZombie
。 它有3個(gè)參數(shù): zombieId (uint)
划纽, name (string)
脆侮, 和 dna (uint)
。
2勇劣。 修改 _createZombie
函數(shù)使得當(dāng)新僵尸造出來(lái)并加入zombies
數(shù)組后靖避,生成事件NewZombie
潭枣。
3。 需要定義僵尸id
幻捏。 array.push()
返回?cái)?shù)組的長(zhǎng)度類型是uint
- 因?yàn)閿?shù)組的第一個(gè)元素的索引是 0盆犁, array.push() - 1
將是我們加入的僵尸的索引。 zombies.push() - 1
就是 id
篡九,數(shù)據(jù)類型是 uint
谐岁。在下一行中你可以把它用到 NewZombie
事件中。
pragma solidity ^0.4.19;
contract ZombieFactory {
event NewZombie (uint zombieId, string name, uint dna);
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie{
string name;
uint dna;
}
Zombie[] public zombies;
function _createZombie(string _name, uint _dna) private {
uint id = zombies.push(Zombie(_name, _dna)) - 1;
NewZombie(id, _name, _dna);
}
function _generateRandomDna(string _str) private view returns(uint){
uint rand = uint(keccak256(_str));
return rand % dnaModulus;
}
function createRandomZombie (string _name) public {
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
}
第14章: Web3.js
我們的 Solidity 合約完工了瓮下! 現(xiàn)在我們要寫一段 JavaScript 前端代碼來(lái)調(diào)用這個(gè)合約翰铡。
以太坊有一個(gè) JavaScript 庫(kù),名為Web3.js讽坏。
在后面的課程里锭魔,我們會(huì)進(jìn)一步地教你如何安裝一個(gè)合約,如何設(shè)置Web3.js路呜。 但是現(xiàn)在我們通過(guò)一段代碼來(lái)了解 Web3.js 是如何和我們發(fā)布的合約交互的吧迷捧。
如果下面的代碼你不能全都理解,不用擔(dān)心胀葱。
// 下面是調(diào)用合約的方式:
var abi = /* abi是由編譯器生成的 */
var ZombieFactoryContract = web3.eth.contract(abi)
var contractAddress = /* 發(fā)布之后在以太坊上生成的合約地址 */
var ZombieFactory = ZombieFactoryContract.at(contractAddress)
// `ZombieFactory` 能訪問(wèn)公共的函數(shù)以及事件
// 某個(gè)監(jiān)聽(tīng)文本輸入的監(jiān)聽(tīng)器:
$("#ourButton").click(function(e) {
var name = $("#nameInput").val()
//調(diào)用合約的 `createRandomZombie` 函數(shù):
ZombieFactory.createRandomZombie(name)
})
// 監(jiān)聽(tīng) `NewZombie` 事件, 并且更新UI
var event = ZombieFactory.NewZombie(function(error, result) {
if (error) return
generateZombie(result.zombieId, result.name, result.dna)
})
// 獲取 Zombie 的 dna, 更新圖像
function generateZombie(id, name, dna) {
let dnaStr = String(dna)
// 如果dna少于16位,在它前面用0補(bǔ)上
while (dnaStr.length < 16)
dnaStr = "0" + dnaStr
let zombieDetails = {
// 前兩位數(shù)構(gòu)成頭部.我們可能有7種頭部, 所以 % 7
// 得到的數(shù)在0-6,再加上1,數(shù)的范圍變成1-7
// 通過(guò)這樣計(jì)算:
headChoice: dnaStr.substring(0, 2) % 7 + 1漠秋,
// 我們得到的圖片名稱從head1.png 到 head7.png
// 接下來(lái)的兩位數(shù)構(gòu)成眼睛, 眼睛變化就對(duì)11取模:
eyeChoice: dnaStr.substring(2, 4) % 11 + 1,
// 再接下來(lái)的兩位數(shù)構(gòu)成衣服,衣服變化就對(duì)6取模:
shirtChoice: dnaStr.substring(4, 6) % 6 + 1,
//最后6位控制顏色. 用css選擇器: hue-rotate來(lái)更新
// 360度:
skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360),
eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360),
clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360),
zombieName: name,
zombieDescription: "A Level 1 CryptoZombie",
}
return zombieDetails
}
我們的 JavaScript 所做的就是獲取由zombieDetails 產(chǎn)生的數(shù)據(jù), 并且利用瀏覽器里的 JavaScript 神奇功能 (我們用 Vue.js)抵屿,置換出圖像以及使用CSS過(guò)濾器庆锦。在后面的課程中,你可以看到全部的代碼轧葛。
演示