Truffle 框架开发 Dapp 中的注意事项
介绍
Truffle 是开发 Ethereum 的一个框架。
编写合约
概述
智能合约使用的是 solidity 语言,这个语言在一些地方与平常使用的高级语言不同,因而可能会遇到一些坑。
注意事项
变量
- solidity 中变量分为两类:memory 和 storage,前者就类似于我们平常语言中的变量,存放在内存中,在调用结束后就会释放掉。
- 而 storage 类型的变量则会存放在区块链中,因而它实际表示的是指针,也正是因为这个原因,这两种变量不能混合使用。
- 另外,定义在 Contract 函数之外的变量默认是 storage 类型的。
事件
- solidity 中 event 的作用就是把发生的事件记录在区块链上成为日志。事件可以通过 web3 来监听。
// 定义事件
event transfer(address indexed _from, address indexed _to, uint indexed value);
// 事件发生,新版本需要加入 emit 关键词
emit transfer(msg.sender, current, value);
映射
mapping(address => User) userPool;
- solidty 中的映射不需要初始化,而且对于不在映射中的内容,通过 m[key] 来调用并不会返回类似于 null 的内容,而是会返回这个类型的默认值,比如 uint 返回的就是 0,因而要检查是否在映射中的时候可能需要辅助的属性:
struct Num {
uint count,
bool inMap
}
mapping(uint => Num) numMap;
if (numMap[1].inMap) { 在映射中 }
else { 不在映射中 }
字符串比较
- solidity 中不能直接使用
==
来比较字符串,需要一个一个字符来比较或者来比较二者的 hash 值:
keccak256(str1) == keccak256(str2)
msg.sender && tx.origin
- 在用户调用合约的时候,msg.sender 和 tx.origin 都是用户的地址
- 在合约调用合约的时候,msg.sender 是 合约的地址,而 tx.origin 是用户
这个一定要注意,我就在这里卡了很久。
三个常用方法
require()
里面是一个 bool 表达式,为真的时候才会执行下面的内容。用来检查不严重的错误,可以退回 gas。
assert()
这个方法和 require 类似,但是一般是用来检查严重的错误,会拿走 gas。
revert()
这个方法里面没有参数,会终止并且恢复之前的状态。
数组
- solidity 数组可以定义不定长的数组,但是要初始化空的不定长数组则要使用 new
// 这里默认是长度为一的数组
// 但是可以通过两种方法来改变:
// 1. 直接改变长度:list.length++
// 2. 通过 push 来增加元素 list.push(123)
uint[] list = new int[](1);
truffle 常用命令
compile
truffle compile
这个命令用来把合约编译成 EVM 可以读懂的语言
migrate
truffle migrate
这个命令用来把合约部署到区块链上,在修改合约之后就不用再次编译,直接使用这个命令部署即可
如果想清除掉之前的数据而不像删除掉编译已生成的 build 则可以加一个 --reset
参数
test
truffle test
这个命令会运行 test 文件夹中的单元测试,其中单元测试文件要以 Test 作为开头,单元测试方法需要以 test 开头
在 js 中调用合约
创建 web3 对象
if (typeof web3 !== 'undefined') {
App.web3Provider = web3.currentProvider;
web3 = new Web3(web3.currentProvider);
} else {
App.web3Provider = new Web3(new Web3.providers.HttpProvider('http://localhost:7545'));
web3 = new Web3(web3.web3Provider);
}
如果已经存在就直接绑定,不存在的话就根据本地的内容来创建再绑定。
读取合约
$.getJSON('TeamMarket.json', function(data) {
var TMArtifact = data;
App.contracts.TeamMarket = TruffleContract(TMArtifact);
App.contracts.TeamMarket.setProvider(App.web3Provider);
App.initCompetitions();
return App.markJoined();
});
这里的 json 文件是编译所产生的,其中要注意 getJSON 中的 JSON 是大写的,不然就会出现傻逼的错误(说的就是我……)。
另外在 HTML 部分记得要引入 truffle 相应的 js 文件。
和合约交互
web3.eth.getAccounts(function(error, accounts) {
if (error) {
console.log(error);
}
var account = accounts[0];
App.contracts.TeamMarket.deployed().then(function(instance) {
TMInstance = instance;
return TMInstance.createUser(name, email, major);
}).then(function () {
alert("创建账号成功,等待写入区块链");
}).catch(function(err) {
alert("有错误发生,创建账号失败");
console.log(err.message);
});
});
这里是通过 web3 接口来读取用户信息,返回的是一个 list,一般第一个就是当前用户
然后获取合约的实例App.contracts.TeamMarket.deployed()
,这里是一个 Promise,在 then 里面就已经获取到了相应的实例,然后再通过这个实例来调用合约里面的方法就可以了。
不过在调用合约之后的 then 中不知道为什么有时候不会执行里面的语句……还得研究研究……
配置文件
truffle.js
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*",
}
}
};
这里是配置区块链网络
package.json
{
"name": "teamseeker",
"version": "1.0.0",
"description": "",
"main": "truffle.js",
"directories": {
"test": "test"
},
"scripts": {
"dev": "lite-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"lite-server": "^2.3.0"
},
"dependencies": {
"web3": "^1.0.0-beta.34"
}
}
这里更多是运行的 js 的一系列配置了,比如在这里配置了成序运行所使用的服务器