区块链火热,做为程序猿的我,固然也不能袖手旁观,一位资深技术开发朋友曾笑说:这是屌丝程序猿改变命运为数很少的机会之一。因此,从今天开始,就要步入区块链的开发大潮中。
语言:使用 node.js
开发该项目
大概流程:css
合约代码编写(Solidity)-> 合约编译(solc)-> 合约部署(web3)html
sudo npm install -g ganache-cli
运行:前端
ganache-cli
输出:node
➜ ~ ganache-cli Ganache CLI v6.1.0 (ganache-core: 2.1.0) Available Accounts ================== (0) 0x2c0e940b0732a3b49cb7dccc26e14cb4801dd1c3 (1) 0x65afabcf1fdb19ef88f8433af90136de56e7e412 (2) 0x65111c1fa94e15e8e3bdedb466004f67d6b46bab (3) 0xfa44030a4216193d19a811267528e86cf1851e48 (4) 0xc29473dca76a2ebbb8b1badf6a8093c11b56ea84 (5) 0x06e55addeef67a46015e2790be1ada1deb3c9c70 (6) 0xc1ec7f3d08692d0bdd70d6ab3d5701f22f53a521 (7) 0x42e52cbb5e226ef8c2c9bf54737b87ccf94ebb08 (8) 0x8cebfdb948266022d2499118b0989b290d146d4c (9) 0x17b791127c57dff3eb31cc203e404536ef7e0ba7 Private Keys ================== (0) 3bb508f1c2c35083f7d69466830067c6582e4464ba61daffc947bb1aa98618e9 (1) fc06e722c10cd80b1b5b43355f81363dcbe6dcc8d3c59387f69c68ce99f36c53 (2) 07f37ed746ba88da289eaa780d6155d9fee456106d85169ad92a526c22192695 (3) 2619b581c083d20ff84db2688f4a9d836206ee37e993bc8cb1e089ad68c8673f (4) c3f61de226b5d5c06cb941f93a2a3ec321dabc53a8fb68bee64d3aed5bc130e6 (5) f86e7b7e7a9cf7532004694cb22997ac521567b7c8e480dbee23e426ed787234 (6) 2035f13d8d64109f21e4eb32970e5934cddcd27bc55439634f49d4479c7abe77 (7) 3395049c4f8749b17e154c47199fa42ce538ed051b6240afc55f49d30406a4f0 (8) 976f56be1b1cd9f5c420a3fdb71eb3a8c3875a7bd3fba20c342389ba97b0a165 (9) a2a7a190ee76cdb0675b8af773fba55187ff4a0fc6c1e1021e717d19e0d591ee HD Wallet ================== Mnemonic: result casino this poverty sleep joy toy sort onion spider bind evolve Base HD Path: m/44'/60'/0'/0/{account_index} Listening on localhost:8545
ganache
默认会自动建立 10 个帐户,每一个帐户有 100 个以太币(ETH:Ether)。 能够把帐户视为银行帐户,以太币就是以太坊生态系统中的货币。git
面输出的最后一句话,描述了节点仿真器的监听地址和端口为localhost:8545
,在使用 web3.js
时,须要传入这个地址来告诉web3js库应当链接到哪个节点。web
咱们使用 Solidity 语言来编写合约。若是你熟悉面向对象的开发和JavaScript,那么学习Solidity 应该很是简单。能够将合约类比于OOP的类:合约中的属性
用来声明合约的状态
,而合约中的方法
则提供修改状态的访问接口。算法
重点:数据库
从最基本的开始入手:npm
Solidity
的代码都包裹在合约里面. 一份合约就是以太应币应用的基本模块, 全部的变量和函数都属于一份合约, 它是你全部应用的起点.编程
一份名为 HelloWorld
的空合约以下:
contract HelloWorld { }
全部的 Solidity 源码都必须冠以 "version pragma" — 标明 Solidity 编译器的版本. 以免未来新的编译器可能破坏你的代码。
例如: pragma solidity ^0.4.19;
(当前 Solidity 的最新版本是 0.4.19).
综上所述, 下面就是一个最基本的合约 — 每次创建一个新的项目时的第一段代码:
contract.sol
合约文件
// 1. 这里写版本指令 pragma solidity ^0.4.19; // 2. 这里创建智能合约 contract HelloWorld { }
为了创建咱们的僵尸部队, 让咱们先创建一个基础合约,称为 ZombieFactory
。
0.4.19
,咱们的合约基于这个版本的编译器。ZombieFactory
。Contract.sol
pragma solidity ^0.4.19; // 1. 这里写版本指令 // 2. 这里创建智能合约 contract ZombieFactory { }
状态变量
是被永久地保存在合约中。也就是说它们被写入以太币区块链中. 想象成写入一个数据库。
示例:
contract Example { // 这个无符号整数将会永久的被保存在区块链中 uint myUnsignedInteger = 100; }
在上面的例子中,定义 myUnsignedInteger 为 uint 类型,并赋值100。
uint
无符号数据类型, 指其值不能是负数,对于有符号的整数存在名为 int
的数据类型。
注: Solidity中, uint
其实是 uint256
代名词, 一个256位的无符号整数。你也能够定义位数少的uints — uint8
, uint16
, uint32
, 等…… 但通常来说你愿意使用简单的 uint
, 除非在某些特殊状况下,这咱们后面会讲。
咱们的僵尸DNA将由一个十六位数字组成。
dnaDigits
为 uint
数据类型, 并赋值 16
。Contract.sol
pragma solidity ^0.4.19; // 1. 这里写版本指令 // 2. 这里创建智能合约 contract ZombieFactory { // 3.定义 dnaDigits 为 uint 数据类型, 并赋值 16 uint dnaDigits = 16; }
在 Solidity 中,数学运算很直观明了,与其它程序设计语言相同:
Solidity 还支持 乘方操做
(如:x 的 y次方) // 例如: 5 ** 2 = 25
uint x = 5 ** 2; // equal to 5^2 = 25
为了保证咱们的僵尸的DNA只含有16个字符,咱们先造一个uint数据,让它等于10^16。这样一来之后咱们能够用模运算符 % 把一个整数变成16位。
uint
类型的变量,名字叫dnaModulus
, 令其等于 10
的 dnaDigits
次方。Contract.sol
pragma solidity ^0.4.19; // 1. 这里写版本指令 // 2. 这里创建智能合约 contract ZombieFactory { // 3. 定义 dnaDigits 为 uint 数据类型, 并赋值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; }
有时你须要更复杂的数据类型,Solidity 提供了 结构体:
struct Person { uint age; string name; }
结构体容许你生成一个更复杂的数据类型,它有多个属性。
注:咱们刚刚引进了一个新类型,string
。 字符串用于保存任意长度的UTF-8
编码数据。 如:string greeting = "Hello world!"
。
在咱们的程序中,咱们将建立一些僵尸!每一个僵尸将拥有多个属性,因此这是一个展现结构体的完美例子。
struct
命名为 Zombie
。Zombie
结构体有两个属性: name
(类型为 string
), 和 dna
(类型为 uint
)。Contract.sol
// 1. 这里写版本指令 pragma solidity ^0.4.19; // 2. 这里创建智能合约 contract ZombieFactory { // 3. 定义 dnaDigits 为 uint 数据类型, 并赋值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.结构体定义 struct Zombie { string name; uint dna; } }
若是你想创建一个集合,能够用 数组
这样的数据类型. Solidity 支持两种数组: 静态
数组和动态
数组:
// 固定长度为2的静态数组: uint[2] fixedArray; // 固定长度为5的string类型的静态数组: string[5] stringArray; // 动态数组,长度不固定,能够动态添加元素: uint[] dynamicArray;
你也能够创建一个 结构体类型的数组 例如,上一节提到的 Person:
Person[] people; // dynamic Array, we can keep adding to it
记住:状态变量被永久保存在区块链中。因此在你的合约中建立动态数组来保存成结构的数据是很是有意义的。
你能够定义 public
数组, Solidity 会自动建立 getter
方法. 语法以下:
Person[] public people;
其它的合约能够从这个数组读取数据(但不能写入数据),因此这在合约中是一个有用的保存公共数据的模式。
为了把一个僵尸部队保存在咱们的APP里,而且可以让其它APP看到这些僵尸,咱们须要一个公共数组。
Zombie
的结构体数组,用 public
修饰,命名为:zombies
.Contract.sol
// 1. 这里写版本指令 pragma solidity ^0.4.19; // 2. 这里创建智能合约 contract ZombieFactory { // 3. 定义 dnaDigits 为 uint 数据类型, 并赋值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.结构体定义 struct Zombie { string name; uint dna; } // 6.数组类型为结构体的公共数组 Zombie[] public zombies; }
在 Solidity 中函数定义的句法以下:
function eatHamburgers(string _name, uint _amount) { }
这是一个名为 eatHamburgers
的函数,它接受两个参数:一个 string
类型的 和 一个 uint
类型的。如今函数内部仍是空的。
注:: 习惯上函数里的变量都是以(
_
)开头 (但不是硬性规定) 以区别全局变量。咱们整个教程都会沿用这个习惯。
咱们的函数定义以下:
eatHamburgers("vitalik", 100);
在咱们的应用里,咱们要能建立一些僵尸,让咱们写一个函数作这件事吧!
createZombie
。 它有两个参数: _name
(类型为string
), 和 _dna
(类型为uint
)。暂时让函数空着——咱们在后面会增长内容。Contract.sol
// 1. 这里写版本指令 pragma solidity ^0.4.19; // 2. 这里创建智能合约 contract ZombieFactory { // 3. 定义 dnaDigits 为 uint 数据类型, 并赋值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.结构体定义 struct Zombie { string name; uint dna; } // 6.数组类型为结构体的公共数组 Zombie[] public zombies; // 7.建立函数 function createZombie(string _name, uint _dna){ } }
还记得上个例子中的 Person 结构吗?
struct Person { uint age; string name; } Person[] public people;
如今咱们学习建立新的 Person
结构,而后把它加入到名为 people
的数组中.
// 建立一个新的Person: Person satoshi = Person(172, "Satoshi"); // 将新建立的satoshi添加进people数组: people.push(satoshi);
你也能够两步并一步,用一行代码更简洁:
people.push(Person(16, "Vitalik"));
注:array.push()
在数组的尾部
加入新元素 ,因此元素在数组中的顺序就是咱们添加的顺序, 如:
uint[] numbers; numbers.push(5); numbers.push(10); numbers.push(15); // numbers is now equal to [5, 10, 15]
让咱们建立名为createZombie的函数来作点儿什么吧。
Zombie
, 而后把它加入 zombies
数组中。 新建立的僵尸的 name
和 dna
,来自于函数的参数。Contract.sol
// 1. 这里写版本指令 pragma solidity ^0.4.19; // 2. 这里创建智能合约 contract ZombieFactory { // 3. 定义 dnaDigits 为 uint 数据类型, 并赋值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.结构体定义 struct Zombie { string name; uint dna; } // 6.数组类型为结构体的公共数组 Zombie[] public zombies; // 7.建立函数 function createZombie(string _name, uint _dna){ // 8.使用结构体和数组(初始化全局数组) zombies.push(Zombie(_name, _dna)); } }
Solidity 定义的函数的属性默认
为公共
。 这就意味着任何一方 (或其它合约) 均可以调用你合约里的函数。
显然,不是何时都须要这样,并且这样的合约易于受到攻击。 因此将本身的函数定义为私有
是一个好的编程习惯,只有当你须要外部世界调用它时才将它设置为公共
。
如何定义一个私有的函数呢?
uint[] numbers; function _addToArray(uint _number) private { numbers.push(_number); }
这意味着只有咱们合约中的其它函数才可以调用这个函数,给 numbers
数组添加新成员。
能够看到,在函数名字后面使用关键字 private
便可。和函数的参数相似,私有函数的名字用(_
)起始。
咱们合约的函数 createZombie
的默认属性是公共的,这意味着任何一方均可以调用它去建立一个僵尸。 我们来把它变成私有吧!
Contract.sol
// 1. 这里写版本指令 pragma solidity ^0.4.19; // 2. 这里创建智能合约 contract ZombieFactory { // 3. 定义 dnaDigits 为 uint 数据类型, 并赋值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.结构体定义 struct Zombie { string name; uint dna; } // 6.数组类型为结构体的公共数组 Zombie[] public zombies; /* // 7.建立函数 function createZombie(string _name, uint _dna){ // 8.使用结构体和数组(初始化全局数组) zombies.push(Zombie(_name, _dna)); } */ // 7.建立函数(改成私有方法) function _createZombie(string _name, uint _dna) private { // 8.使用结构体和数组(初始化全局数组) zombies.push(Zombie(_name, _dna)); } }
本节中咱们将学习函数的返回值和修饰符。
要想函数返回一个数值,按以下定义:
string greeting = "What's up dog"; function sayHello() public returns (string) { return greeting; }
Solidity 里,函数的定义里可包含返回值的数据类型(如本例中 string
)。
上面的函数实际上没有改变 Solidity 里的状态,即,它没有改变任何值或者写任何东西。
这种状况下咱们能够把函数定义为 view
, 意味着它只能读取数据不能更改数据:
function sayHello() public view returns (string) {}
Solidity 还支持 pure
函数, 代表这个函数甚至都不访问应用里的数据,例如:
function _multiply(uint a, uint b) private pure returns (uint) { return a * b; }
这个函数甚至都不读取应用里的状态 — 它的返回值彻底取决于它的输入参数,在这种状况下咱们把函数定义为 pure
.
注:可能很难记住什么时候把函数标记为
pure/view
。 幸运的是, Solidity 编辑器会给出提示,提醒你使用这些修饰符。
咱们想创建一个帮助函数,它根据一个字符串随机生成一个DNA数据。
private
函数,命名为 _generateRandomDna
。它只接收一个输入变量 _str
(类型 string
), 返回一个 uint
类型的数值。view
。Contract.sol
// 1. 这里写版本指令 pragma solidity ^0.4.19; // 2. 这里创建智能合约 contract ZombieFactory { // 3. 定义 dnaDigits 为 uint 数据类型, 并赋值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.结构体定义 struct Zombie { string name; uint dna; } // 6.数组类型为结构体的公共数组 Zombie[] public zombies; /* // 7.建立函数 function createZombie(string _name, uint _dna){ // 8.使用结构体和数组(初始化全局数组) zombies.push(Zombie(_name, _dna)); } */ // 7.建立函数(改成私有方法) function _createZombie(string _name, uint _dna) private { // 8.使用结构体和数组(初始化全局数组) zombies.push(Zombie(_name, _dna)); } // 9.函数修饰符 private, view, returns 返回值 function _generateRandomDna(string _str) private view returns (uint){ } }
如何让 _generateRandomDna
函数返回一个全(半) 随机的 uint
?
Ethereum 内部有一个散列函数keccak256
,它用了SHA3版本。一个散列函数基本上就是把一个字符串转换为一个256位的16进制数字。字符串的一个微小变化会引发散列数据极大变化。
这在 Ethereum 中有不少应用,可是如今咱们只是用它造一个伪随机数。
示例:
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5 keccak256("aaaab"); //b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9 keccak256("aaaac");
显而易见,输入字符串只改变了一个字母,输出就已经天壤之别了。
注: 在区块链中安全地产生一个随机数是一个很难的问题, 本例的方法不安全,可是在咱们的Zombie DNA算法里不是那么重要,已经很好地知足咱们的须要了。
有时你须要变换数据类型。例如:
uint8 a = 5; uint b = 6; // 将会抛出错误,由于 a * b 返回 uint, 而不是 uint8: uint8 c = a * b; // 咱们须要将 b 转换为 uint8: uint8 c = a * uint8(b);
上面,a * b
返回类型是uint
, 可是当咱们尝试用uint8
类型接收时, 就会形成潜在的错误。若是把它的数据类型转换为uint8
, 就能够了,编译器也不会出错。
给 _generateRandomDna
函数添加代码! 它应该完成以下功能:
_str
的 keccak256
散列值生成一个伪随机十六进制数,类型转换为 uint
, 最后保存在类型为 uint
名为 rand
的变量中。return
上面计算的数值对 dnaModulus
求余数(%
)。Contract.sol
// 1. 这里写版本指令 pragma solidity ^0.4.19; // 2. 这里创建智能合约 contract ZombieFactory { // 3. 定义 dnaDigits 为 uint 数据类型, 并赋值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.结构体定义 struct Zombie { string name; uint dna; } // 6.数组类型为结构体的公共数组 Zombie[] public zombies; /* // 7.建立函数 function createZombie(string _name, uint _dna){ // 8.使用结构体和数组(初始化全局数组) zombies.push(Zombie(_name, _dna)); } */ // 7.建立函数(改成私有方法) function _createZombie(string _name, uint _dna) private { // 8.使用结构体和数组(初始化全局数组) zombies.push(Zombie(_name, _dna)); } // 9.函数修饰符 private, view, returns 返回值 function _generateRandomDna(string _str) private view returns (uint){ // 10.散列并取模 uint rand = uint(keccak256(_str)); // 注意这里须要将string类型转为uint类型 return rand % dnaModulus; } } }
咱们就快完成咱们的随机僵尸制造器了,来写一个公共的函数把全部的部件链接起来。
写一个公共函数,它有一个参数,用来接收僵尸的名字,以后用它生成僵尸的DNA。
public
函数,命名为createRandomZombie
. 它将被传入一个变量 _name
(数据类型是 string
)。 (注: 定义公共函数 public
和定义一个私有 private
函数的作法同样)。_generateRandomDna
函数,传入 _name
参数, 结果保存在一个类型为 uint
的变量里,命名为 randDna
。_createZombie
函数, 传入参数: _name
和 randDna
。}
)。Contract.sol
// 1. 这里写版本指令 pragma solidity ^0.4.19; // 2. 这里创建智能合约 contract ZombieFactory { // 3. 定义 dnaDigits 为 uint 数据类型, 并赋值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.结构体定义 struct Zombie { string name; uint dna; } // 6.数组类型为结构体的公共数组 Zombie[] public zombies; /* // 7.建立函数 function createZombie(string _name, uint _dna){ // 8.使用结构体和数组(初始化全局数组) zombies.push(Zombie(_name, _dna)); } */ // 7.建立函数(改成私有方法) function _createZombie(string _name, uint _dna) private { // 8.使用结构体和数组(初始化全局数组) zombies.push(Zombie(_name, _dna)); } // 9.函数修饰符 private, view, returns 返回值 function _generateRandomDna(string _str) private view returns (uint){ // 10.散列并取模 uint rand = uint(keccak256(_str)); // 注意这里须要将string类型转为uint类型 return rand % dnaModulus; } // 十一、事件 function createRandomZombie(string _name) public { uint randDna = _generateRandomDna(_name); _createZombie(_name, randDna); } }
咱们的合约几乎就要完成了!让咱们加上一个事件.
事件
是合约和区块链通信的一种机制。你的前端应用“监听”某些事件,并作出反应。
示例:
// 这里创建事件 event IntegersAdded(uint x, uint y, uint result); function add(uint _x, uint _y) public { uint result = _x + _y; //触发事件,通知app IntegersAdded(_x, _y, result); return result; }
你的 app 前端能够监听这个事件。JavaScript 实现以下:
YourContract.IntegersAdded(function(error, result) { // 干些事 }
咱们想每当一个僵尸创造出来时,咱们的前端都能监听到这个事件,并将它显示出来。
事件
叫作 NewZombie
。 它有3个参数: zombieId (uint)
, name (string)
, 和 dna (uint)
。_createZombie
函数使得当新僵尸造出来并加入zombies
数组后,生成事件NewZombie
。id
。 array.push()
返回数组的长度
类型是uint
- 由于数组的第一个元素的索引是 0
, array.push() - 1
将是咱们加入的僵尸的索引。 zombies.push() - 1
就是 id
,数据类型是 uint
。在下一行中你能够把它用到 NewZombie
事件中。Contract.sol
// 1. 这里写版本指令 pragma solidity ^0.4.19; // 2. 这里创建智能合约 contract ZombieFactory { // 12.这里创建事件 event NewZombie(uint zombieId, string name, uint dna); // 3. 定义 dnaDigits 为 uint 数据类型, 并赋值 16 uint dnaDigits = 16; // 4. 10 的 dnaDigits 次方 uint dnaModulus = 10 ** dnaDigits; // 5.结构体定义 struct Zombie { string name; uint dna; } // 6.数组类型为结构体的公共数组 Zombie[] public zombies; /* // 7.建立函数 function createZombie(string _name, uint _dna){ // 8.使用结构体和数组(初始化全局数组) zombies.push(Zombie(_name, _dna)); } */ // 7.建立函数(改成私有方法) function _createZombie(string _name, uint _dna) private { // 8.使用结构体和数组(初始化全局数组) // zombies.push(Zombie(_name, _dna)); // 十二、数组长度减一就是当前的数组ID uint id = zombies.push(Zombie(_name, _dna)) - 1; // 十二、这里触发事件 NewZombie(id, _name, _dna); } // 9.函数修饰符 private, view, returns 返回值 function _generateRandomDna(string _str) private view returns (uint){ // 10.散列并取模 uint rand = uint(keccak256(_str)); // 注意这里须要将string类型转为uint类型 return rand % dnaModulus; } // 十一、综合函数 function createRandomZombie(string _name) public { uint randDna = _generateRandomDna(_name); _createZombie(_name, randDna); } }
咱们的 Solidity
合约完工了! 如今咱们要写一段 JavaScript
前端代码来调用这个合约。
以太坊有一个 JavaScript 库,名为Web3.js
。
在后面的课程里,咱们会进一步地教你如何安装一个合约,如何设置Web3.js。 可是如今咱们经过一段代码来了解 Web3.js 是如何和咱们发布的合约交互的吧。
若是下面的代码你不能全都理解,不用担忧。
// 下面是调用合约的方式: var abi = /* abi是由编译器生成的 */ var ZombieFactoryContract = web3.eth.contract(abi) var contractAddress = /* 发布以后在以太坊上生成的合约地址 */ var ZombieFactory = ZombieFactoryContract.at(contractAddress) // `ZombieFactory` 能访问公共的函数以及事件 // 某个监听文本输入的监听器: $("#ourButton").click(function(e) { var name = $("#nameInput").val() //调用合约的 `createRandomZombie` 函数: ZombieFactory.createRandomZombie(name) }) // 监听 `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补上 while (dnaStr.length < 16) dnaStr = "0" + dnaStr let zombieDetails = { // 前两位数构成头部.咱们可能有7种头部, 因此 % 7 // 获得的数在0-6,再加上1,数的范围变成1-7 // 经过这样计算: headChoice: dnaStr.substring(0, 2) % 7 + 1, // 咱们获得的图片名称从head1.png 到 head7.png // 接下来的两位数构成眼睛, 眼睛变化就对11取模: eyeChoice: dnaStr.substring(2, 4) % 11 + 1, // 再接下来的两位数构成衣服,衣服变化就对6取模: shirtChoice: dnaStr.substring(4, 6) % 6 + 1, //最后6位控制颜色. 用css选择器: hue-rotate来更新 // 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
产生的数据, 而且利用浏览器里的 JavaScript 神奇功能 (咱们用 Vue.js
),置换出图像以及使用CSS过滤器。在后面的课程中,你能够看到所有的代码。
Truffle
是一个DApp开发框架,它简化了去中心化应用的构建和管理。
注:本教程是汇智网-以太坊DApp开发入门学习笔记,感兴趣的同窗能够去学习。