1.Truffle的使用
1.1 Truffle介绍
truffle是专门为合约开发设计的框架,它可以很方便的编译,部署,测试合约。
你不必再去remix平台,编译,然后复制ABI,手动来做这些繁琐的事情,基本上一个命令就解决。
1.2 安装Truffle
我们使用npm命令就能安装,如下:
npm install -g truffle
然后输入
truffle -v
出现版本号则表示安装成功:
1.3 创建truffle项目
然后我们新建一个truffle1文件夹,命令行进入该文件夹,输入
truffle init
创建一个空的truffle项目。
然后就会在该目录下生成三个文件夹,contracts,migrations,test。
还有一个truffle-config.js文件,如下:
2.Truffle部署调用合约
2.1 编译部署合约
接着我们用vscode打开truffle1文件,(vscode是我的代码编辑器,推荐使用,当然你也可以用别的)
我们的合约代码要写在contracts里,就是sol结尾的文件(记住这个合约代码是存储在contracts里)
在contracts文件下新建Storage.sol 代码如下:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.2 <0.9.0;
/**
* @title Storage
* @dev Store & retrieve value in a variable
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}
基本上就是把remix在线平台的代码复制过来。
然后在migration文件下1_initial_migration.js 代码如下:
const Storage = artifacts.require("./Storage.sol");
module.exports = function(deployer) {
deployer.deploy(Storage);
};
在migration文件夹里的代码是跟合约部署有关,告诉truffle部署什么合约。
这个代码是加载我的storage.sol,然后deploy storage给外部调用。
这样我们输入truffle命令时,就会正常的部署我的合约了。
PS:注意文件名的前缀数字编号1_initial_migration.js,它告诉truffle按顺序部署(存在多个的情况下)。并且部署过后。再输入部署命令不会再次部署,你需要再创建一个2_开头的,然后后面字符可以随便自己设定。或者命令带上参数加上编号强制重新部署,如truffle migrate -f 1。
好,最后一步,是告诉truffle 部署到哪,你是要部署到ganache还是其它链。
我们这里部署到本地ganache链,打开truffle-config.js
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 7545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
},
将上面这段代码注释取消,并更改端口为7545,对应我们的ganache端口。
一切准备就绪后。
我们输入truffle compile 命令编译合约:
OK,编译成功,然后会生成一个bulid文件夹,然后生成一个Storage.json文件里面有我们合约的ABI等信息:
编译成功后,我们来进行部署,输入 truffle migrate 部署合约
但是报如下错误:
这个问题跟我们第一章碰到的是一样的,solc编译的版本和ganache不兼容。
我们得改成london版的。
在truffle-config.js里添加settings: { evmVersion: 'london' } 如下:
输入truffle migrate部署OK:
上面会显示我们合约的一些信息,合约地址,部署地址,费用等。
2.2 ganache导入truffle项目
我们创建了truffle项目部署好后,可以将这个项目导入到ganache里面,这样我们就能看到这个项目合约的一些信息,我们点击齿轮图标:
然后选择add project
选择我们项目的truffle-config.js 添加。
接着选择save and restart
然后我们点contracts就可以看到合约的一些信息了
但是导入之后,如果我们一关闭ganache,所有的一切都消失了,我们需要保存这个区块链。
点save可以保存这个区块链(在齿轮按钮旁边)。这样我们创建的账号和合约,不会因为ganache的关闭而丢失。需要时在界面加载就行,方便我们下次使用。
点了保存之后,得到项目名:
然后再次打开ganache,选择项目名打就行:
2.3 truffle调用合约
安装truffle后,我们在项目里输入truffle console进入控制台模式,然后可以输入代码调用测试合约。代码如下:
Storage.deployed().then(function(con) {con.store(66);})
获得合约对象con,然后调用store方法存入数字66,注意合约名是Storage。
打开ganache查看结果:
执行OK
如果要获取,调用retrieve()方法如下代码:
Storage.deployed().then(function(con) {con.retrieve().then(res=>{console.log(res.words[0])})})
结果:
就是给retrieve加个回调函数,res就是获得执行结果,然后res.words[0]提取出数字。
3.Truffle前端
前面我们介绍了在控制台里调用合约,现在我们要写一个前端调用,其实原理跟我们之前的前端写法是一样的,甚至你可以把之前的前端代码放到这里来依然可以。
不过为了配合Truffle使用,这里我们就用新的一种方法来实现:
3.1 webpack-dev-server安装使用
webpack之前我们已经使用过了,它可以帮我们打包静态js,那么webpack-dev-server是什么呢?
它在webpack的基础上还多了一个功能,一个小型的网页服务器,是基于nodejs(express)的。
我们在项目里新建一个app文件夹,这个目录就放我们前端相关的代码文件。
然后命令行进入这个目录输入:
npm install webpack-dev-server
然后目录是这样:
然后我们通过添加npm命令方式调用,打开package.json添加如下代码:
"scripts": {
"dev": "webpack-dev-server"
},
然后我们命令行输入npm run dev 就会识别到scripts里的dev,然后再执行webpack-dev-server命令。(ps:如果显示webpack-dev-server不是命令,可以全局安装试试npm install webpack-dev-server -g)
此时还会安装一些依赖条件后才会启动,结果启动失败:
那是因为我们的配置文件没弄好,将之前的webpack用的webpack.config.js配置文件复制过来,代码如下:
const path=require('path');
module.exports={
//JavaScript执行入口文件,
entry:'./src/main.js',
//需要指定一下输出的路径path和输出的文件名filename
output:{
filename:'bundle.js', //自定义输出文件名
path:path.resolve(__dirname,'./dist') //自定义输出文件所在目录
} ,
mode: 'development' // 设置mode
}
然后新建一个src文件夹,里面创建main.js,随便写点js代码:
document.write('hello')
接着再运行npm run dev成功,或者你直接运行webpack-dev-server也可:
然后浏览器输入:http://localhost:8080/bundle.js
可以访问我们打包后的文件:
怎么样,运行了webpack-dev-server 后,不仅帮我们打包了js文件,还直接提供了网页服务,我们不必再像之前那样去自己写代码创建node网页服务了。
但是这里需要注意的是,webpack-dev-server跟webpack的区别,这里我们生成的目标js文件是在内存中的,并不是磁盘中真有dist这样一个目录,所以你在磁盘目录中是找不到这个bundle.js。
崦浏览器通过webpack-dev-server访问获取bundle.js,直接是从内存中获取数据。
3.2 webpack-dev-server添加index.html
但是如果我要新建一个index.html怎么访问呢?直接在app下新建一个index.html?是访问不了的。
webpack-dev-server的根目录是在public文件里,所以我们得在app下新建一个public,然后添加
index.html代码如下:
<script src="bundle.js"></script>
重启webpack-dev-server,浏览器输入localhost:8080
当然,你也可以在webpack-dev-server指定一个输出目录,用如下语句:
devServer: {
static: {
directory: path.join(__dirname, "./src"),
},
},
指定为src目录。(PS:__dirname是当前目录名。可用console.log(__dirname),node test.js执行查看)
好了,到了这一步,你知道了怎么打包,又知道了怎么输出index.html,剩下的事就简单了,基本上是把之前的前端代码复制过来就行了。
但是在这里我们还是不用老方法,我们用一些不同的方法,ABI在truffle里我们是不需要手动指定的。
还记得前面我们输入truffle compile 生成的Storage.json文件吗,这里有我们的合约信息,需要时我们直接加载就行了。
3.3 前端js代码
这里我们将第三章的main.js代码拿来进行一些修改如下:
import conInfo from "../../build/contracts/Storage.json"; //导入Storage.json合约信息
//合约abi
var abi=conInfo.abi; //获得合约ABI
//获取合约地址
var conadr=conInfo.networks['5777'].address; //这里的5777 id具体看你们的storage.json 你也可以直接填,或者通过 web3.eth.net.getId();之类的获取匹配
我这里只是演示一下方法
var Web3=require("web3");
var web3;
var accountFrom;
var myContract;
if (window.ethereum)
{
alert('请使用未安装metamask的浏览器!');
}
else
{
web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:7545"));
myContract=new web3.eth.Contract( //根据abi和合约地址创建
abi,
conadr,
);
//获取第一个账号地址
web3.eth.getAccounts().then(e => { //异步then中获取
accountFrom = e[0];
}) ;
}
//更新数字函数
window.updateNum =function() //注意这里使用window 否则会报错
{
//获取网页input的数字
const number = parseInt(document.getElementById("number").value);
myContract.methods.store(number).send({from:accountFrom}, function(error, transactionHash){ //执行合约的store方法存储一个数字
const status = document.getElementById("status");
status.innerHTML = '更新成功:'+transactionHash;
});
}
//获取数字函数
window.getNum =function ()
{
myContract.methods.retrieve().call({from: accountFrom}, function(error, result){//调用retrieve方法获得你存在区链中的那个数字
const status = document.getElementById("status");
status.innerHTML='数字:'+result;
});
}
然后记得安装一下web3,进入app目录,输入npm install web3@1.2.4
3.4 前端index.html
跟第三章index代码差不多,只是少了自建nodejs网页服务,public下index.html代码如下:
<!doctype html>
<html>
<head>
<meta charset=UTF-8>
<script src="bundle.js"></script>
</head>
<body>
<input type="text" id="number">
<button onclick="updateNum()">更新</button>
<br>
<button onclick="getNum()">获取</button>
<br>
<h4 id="status">请操作...</h4>
</body>
</html>
3.5 项目启动测试
然后我们进入app 输入npm run dev 运行webpack-dev-server服务
结果报错:
报了很多个 Can't resolve 'url' 之类的错误。
我们可以依次安装,提示缺少什么就npm install url这样进行安装。
也可以这样,输入npm audit fix 修复。
接着再输入npm run dev 成功运行。
然后浏览器访问localhost:8080测试(注意打开ganache)
ganache链上有对应的记录:
测试都OK。
另附项目文件结构,以便大家对应创建: