Truffle是一个功能强大的Dapp开发框架。使用Truffle在以太坊上开发应用程序变得更加简单。
Truffle官网:www.truffleframework.com
(1)安装Truffle
>> npm install -g truffle
安装结束后,可以执行如下命令测试是否安装成功。
>> truffle version
这时候如果可以看到以下输出,就代表安装成功。
(2)创建Truffle工程
第一步:在磁盘上新建一个空目录。
第二步:进入该目录后,执行下面命令。
>> truffle init
执行完成后,系统会自动生成以下目录结构:
contracts:存放合约文件
migrations:存放部署文件
test:存放测试文件
truffle-config.js:配置文件,描述不同网络
(3)编写合约
首先,在contracts目录下新建一个合约文件,代码如下所示:
pragma solidity ^0.4.24;
contract SimpleStorage {
uint storedData;
function set(uint x) public {
storedData = x;
}
function get() public view returns(uint) {
return storedData;
}
}
然后,进入命令窗口,在项目根路径下执行以下命令编译合约:
>> truffle compile
执行该命令的时候,truffle框架会把contracts目录下的每一个合约都进行编译,并且在项目根路径下生成build文件夹。
build文件夹下有一些json文件,这些json文件记录了合约的详细信息(比如abi、bytecode、network等等)。
(4)部署合约
第一步:在migrations目录下创建部署脚本,脚本文件的名称为“2_simplestorage_migration.js”。
const SimpleStorage = artifacts.require("./SimpleStorage.sol");
module.exports = function(deployer) {
deployer.deploy(SimpleStorage);
};
第二步:打开truffle-config.js文件,在networks节点下添加以下配置网络。
ganacheNet: {
host: "127.0.0.1",
port: 7545,
network_id: "*"
}
其中ganacheNet代表要部署合约的网络名,可以自己定义。
第三步:启动ganache客户端,并且在命令窗口中执行以下命令部署合约。
>> truffle migrate --network ganacheNet
执行完该命令后,networks会填充对应的网络和部署合约地址。
(5)测试合约
第一步:打开remix,然后在browse菜单下新建一个合约,然后把上面的合约代码复制过来。从truffle-config.js文件中复制合约地址到remix上。
第二步:连接ganche本地网络,然后选择SimpleStorage合约后点击Deploy按钮部署合约。
第三步:测试网络。
至此,合约的编译、部署、测试工作已经完成。接下来我们会更进一步地去看一下truffle框架的高级用法。
truffle本身也内置了一个测试环境。
(1)启动truffle测试环境
>> truffle develop
启动后的控制台如下所示:
truffle内置环境的网络地址为:http://127.0.0.1:9545,打开metamask,拷贝助记词后切换到该网络即可。
(2)truffle交互命令:
命令 | 说明 |
compile | 编译合约 |
migrate | 部署合约 |
使用truffle-contract库调用合约方法的步骤与使用web3调用合约方法有点类似,下面对比两种方法的区别:
对比项 | web3 | truffle-contract |
创建合约实例 | var instance = new web3.eth.Contract(abi, address) | var contract = require("truffle-contract"); var myContract = contract(SimpleStorage.json) |
设置web3 | instance.setProvider(provider) | myContract .setProvider(provider) |
获取合约实例 | var deployedInstance= await myContract.deployed() | |
调用send方法 | instance.methods.setValue(10).send({from : xxx}).then(res) | let res = await deployedInstance.setValue(5, {from:xxxx}) |
调用call方法 | instance.methods.getValue().call({from : xxx}).then(res) | deployedInstance.getValue.call(参数填这里, {from :xxx}).then(res) |
上面第一个对比项的myContract 变量是一个使用truffle-contract包装的web3实例。它提供了一些调用合约的方法。
truffle控制台已经自带了web3的实例,但是该实例是使用truffle-contract库进行了包装。
关于truffle-contract的用法,可以参考:https://github.com/trufflesuite/truffle-contract
(3)truffle演示
>> compile
>> migrate
>> var deployedInstance;
>> SimpleStorage.deployed().then(instance => {deployedInstance = instance})
>> deployedInstance.get.call()
第1行:编译合约
第2行:部署合约
第3行:定义合约实例
第4行:初始化合约实例
第5行:调用合约的get方法
(4)测试合约
truffle提供了两种测试方式:JavaScript和Solidity。
官方文档:https://truffleframework.com/docs/truffle/testing/testing-your-contracts
使用solidity测试合约的具体步骤:
1)第一步:在test文件夹下创建测试合约。
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/SimpleStorage.sol";
contract TestSimpleStorage {
function testSet() public {
// 获取合约实例
SimpleStorage ss = SimpleStorage(DeployedAddresses.SimpleStorage());
// 调用set方法
ss.set(1000);
// 调用get方法
uint res = ss.get();
// 断言
Assert.equal(res, 1000, "res should be 1000.");
}
}
第1、2行的import命令是固定的,第3行import导入要测试的合约。
2)第二步:执行test命令。
>> truffle test
如果断言正确,控制台会输出:
如果断言失败,控制台输出:
truffle内置了一些项目(比如react),能够方便我们进行二次开发。如果要使用这些项目,就要先下载到本地。
(1)下载内置的react项目
第一步:在本地磁盘上创建一个空目录;
第二步:在命令行进入该目录,然后以下执行命令;
>> npm unbox react
由于下载时间很长,所以笔者直接到官网上下载项目。
下载地址:https://truffleframework.com/boxes/react
下载完成后,把压缩文件家压缩到新建目录中。目录结构如下图所示:
从图上可以看出,truffle-react项目的目录比之前truffle项目多了一个client目录。
在控制台进入client目录所在路径,然后执行命令下载依赖包。
>> npm install
(2)运行内置的react项目
第一步:打开控制台,启动develop环境,然后复制助记词;
>> truffle develop
2)在develop环境下编译和部署合约;
>> compile
>> migrate
3)在浏览器打开metamask插件,输入助记词,然后切换到本地网络(http://127.0.0.1:9545);
4)打开另外一个控制台,进入client目录所在路径,然后启动react项目:
>> npm start
如果启动成功,在浏览器上输入localhost:3000,界面效果如下所示:
(3)App.js源码分析
import React, { Component } from "react";
import SimpleStorageContract from "./contracts/SimpleStorage.json";
import getWeb3 from "./utils/getWeb3";
import "./App.css";
class App extends Component {
// 初始化状态变量
state = {
storageValue: 0, // 调用合约实例get方法返回的结果
web3: null, // web3实例
accounts: null, // 合约帐号
contract: null // 合约实例
};
// 定义构造函数
componentDidMount = async () => {
try {
// 获取web3实例
const web3 = await getWeb3();
// 获取所有用户
const accounts = await web3.eth.getAccounts();
// 获取合约实例
const networkId = await web3.eth.net.getId();
const deployedNetwork = SimpleStorageContract.networks[networkId];
const instance = new web3.eth.Contract(
SimpleStorageContract.abi,
deployedNetwork && deployedNetwork.address,
);
// 把web3、accounts和contract设置到状态变量中
// 然后运行runExample方法与合约进行交互
this.setState({ web3, accounts, contract: instance }, this.runExample);
} catch (error) {
...
}
};
runExample = async () => {
// 从state中把accounts和contract解构出来
const { accounts, contract } = this.state;
// 调用合约set方法
await contract.methods.set(5).send({ from: accounts[0] });
// 调用合约get方法
const response = await contract.methods.get().call();
// 把结果设置到状态变量storageValue中
this.setState({ storageValue: response });
};
// 渲染页面
render() {
if (!this.state.web3) {
return Loading Web3, accounts, and contract...;
}
return (
Good to Go!
Your Truffle Box is installed and ready.
Smart Contract Example
If your contracts compiled and migrated successfully, below will show
a stored value of 5 (by default).
Try changing the value stored on line 40 of App.js.
The stored value is: {this.state.storageValue}
);
}
}
export default App;