truffle基础

Last updated on a day ago

truffle 是什么

Truffle 是一个在以太坊进行 DApp 开发的世界级开发环境、测试框架。可以合约的编译、部署、管理,通过命令控制台直接与智能合约进行交互等功能。

truffle 安装

首先需要安装 NodeJS v8.9.4 或 之后的版本,然后通过 npm 安装 truffle

1
npm install -g truffle

创建项目

  1. 创建项目文件夹

    1
    mkdir project
  2. 创建空项目

    1
    2
    cd project
    truffle init
  3. 创建实例代码和项目模板,init 创建的是一个空项目,如果需要创建模板,可以 Truffle Box 进行下载

    1
    truffle unbox <box-name>
  4. 项目结构

    • contracts/:存放合约
    • migrations/:部署脚本文件目录
    • test/:测试脚本目录
    • truffle-config.js:配置文件

修改配置文件

配置文件自带注释,这里只讲简单的修改

  • networks-development:用于设置连接的网络,以 ganache 为例子,在 ganache 顶部有相关参数

    1
    2
    3
    4
    5
    development: {
    host: "127.0.0.1",
    port: 7545,
    network_id: 5777,
    }
  • compilers-solc-version:编译器版本

    1
    2
    3
    4
    5
    compilers: {
    sole: {
    versioin: "0.8.21",
    }
    }

项目全过程

连接到 Ganache

编写合约

contracts/文件夹中编写合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8;
contract MyContract {
struct Flag {
string title;
string content;
address owner;
uint ts;
}
Flag[] public flags;

// 添加评论
function set(string memory title , string memory content) public {
flags.push(Flag(title, content, msg.sender, block.timestamp));
}

获取评论数量
function getLength() public view returns(uint) {
return flags.length;
}
}

编写迁移脚本

迁移脚本文件,也可以叫迁移文件,是放在migrations/文件夹中的 js 文件,以数字开头,用于部署合约

1_deploy_MyContract.js

1
2
3
4
5
6
const MyContract = artifacts.require("MyContract");

module.exports = function (deployer) {
// 部署迁移合约
deployer.deploy(MyContract);
};

MyContract是合约名,而不是文件名,一个文件可以有多个合约,当有多个合约需要依次部署时,可以使用异步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const MyContract = artifacts.require("MyContract1");
const MyContract = artifacts.require("MyContract2");

module.exports = async function (deployer, network, accounts) {
// 部署合约
await deployer.deploy(MyContract1);
// 获取部署后的合约实例
const myContract1Instance = await MyContract1.deployed();
// 获取合约地址
const contract1Address = myContract1Instance.address;
// 利用合约地址部署合约(需要的话)
await deployer.deploy(MyContract2, contract1Address);
// 获取部署后的合约实例
const myContract2Instance = await MyContract2.deployed();
// 获取合约地址
const contract2Address = myContract2Instance.address;
};

部署合约

终端输入 (truffle complietruffle migrate) 或 truffle migrate --reset 进行合约的编译部署或重部署

如果出现了hit an invalid opcode while deploying这样的报错,需要修改truffle-config.js配置文件中版本 solc 版本,和合约对应。(这里是 0.8.0)

部署成功后出现下面类似信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1_deploy_MyContract.js
======================

Deploying 'MyContract'
----------------------
> transaction hash: 0xa0516507693bd555e0b6bf49ee2fc0f3ea41041f1db897d0ea7481beb5978b09
> Blocks: 0 Seconds: 0
> contract address: 0x1E5e59B3FeD2030F8D6b7B2ec1D0D53E441f23a9
> block number: 1
> block timestamp: 1713065479
> account: 0x9d3f59003Da03D71e647eE10294395Aba69C87Ae
> balance: 99.998420554
> gas used: 467984 (0x72410)
> gas price: 3.375 gwei
> value sent: 0 ETH
> total cost: 0.001579446 ETH

> Saving artifacts
-------------------------------------
> Total cost: 0.001579446 ETH

调用合约

调用合约的方法有三种:通过 test 脚本调用,通过网页 js 调用,使用 truffle 控制台

  • 使用 truffle 控制台truffle console

    在终端中输入truffle console可以启动控制台,在控制台中,可以输入 js 语句对合约进行调用,且注意,一定要用异步方法

    首先要实例化合约

    1
    2
    // 获取合约实例
    let MyContract = await MyContract.deployed()

    接着就可以通过实例调用合约了

    1
    2
    // 调用合约的set方法
    await MyContract.set("title","content")

    此时在 ganache 的transaction中会出现CONTRACT CALL的交易,在终端中也会出现对应的信息

    而如果调用的函数没有对链上数据进行修改,则不会出现CONTRACT CALL交易

    1
    2
    3
    4
    // 调用合约的get方法
    let flagLength = await MyContract.getLength()
    // 查看数据
    flagLength.toNumber()

    而如果我们想要查看公开的状态变量,也可以通过实例进行访问,如果状态变量是个数组则需要输入一个 uint 作为参数

    1
    2
    // 查看公开的状态变量
    MyContract.flags(0)
  • 使用网页 js

编写测试脚本

测试脚本可以部署并测试合约,也可以测试以部署的合约,测试使用mocha语法,支持一样的钩子函数before()after()beforeEach()afterEach()
测试脚本一般与对应的合约同名,并加上.test.js后缀
大体格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 导入合约
const Contract1 = artifacts.require("Contract1");

// 定义测试块,truffle使用了 contract 替换了 describe,函数参数为账户列表
contract("Contract1的测试", async function(accounts) => {

let contract1;

before(async ()=>{
// 所有测试用例之前执行

// 部署合约并获取合约实例
// 部署合约的三种方式
// 1. 使用nwe创建新的合约实例,构造参数可自己写
contract1 = await Contract1.new(params,{value: web3.utils.toWei("1","Ether"), gas:..., from: accounts[1]});
// 2. 使用deployed 通过迁移脚本创建合约实例,构造参数为迁移脚本中指定的参数
contract1 = await Contract1.deployed();
// 3. 使用at连接到已部署的合约
contract1 = awati Contrac1.at(address);
})
beforeEach( async function () {
// 在本区块的每个测试用例之前执行
// 如果需要用例之间互相独立,则可以使用beforeEach进行初始化
})
// 测试用例
it("测试内容:应该初始化合约", async ()=>{
// 执行的操作
// 获取合约的状态或调用方法等


// 断言
// 断言相等,不等则抛出错误,错误信息为message
assert.equal(actual, expected, message);
// 断言不等,相等抛出错误,错误信息为message
assert.notEqual(actual, expected, message);
// 断言为真,不为真则抛出错误,错误信息为message
assert.isTrue(value, message);
// 断言为假,不为假则抛出错误,错误信息为message
assert.isFalse(value, message)
})
// 更多测试用例
it(){}
afterEach( async function (){
// 在本区块的每个测试用例之后执行
// 可以对所有用例进行统一断言或清理操作
})
after( async function (){
// 在所有测试用例执行完后执行
// 可以进行清理操作,比如撤销合约或释放资源
})

})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 引用合约
const Greeter = artifacts.require("Greeter");

//执行定义合约中的其他操作
contract("Greeter", (accounts) => {

// before 测试前的准备工作
before(async () => {
// 部署合约的两种方式
//01 通过迁移脚本部署,不能传递构造参数
let a = await Greeter.deployed();
//02 直接创建新的合约实例,可以传递构造参数
//let Contract_Ches = await Ches.new(var_a);
let c = await a.getGreeting();
let d = await a.setGreeting(var_b);
let f = await a.getGreeting();

//在原有的合约上进行获取
let g = await Greeter.at("0x592C0067a4B3Fc66D7f26430b3EBeF9ac7796F85");
let h = await g.getGreeting();
let i = await g.setGreeting("您好,世界!");
let k = await g.getGreeting();
console.log("合约地址:", a.address);
console.log("获取原数据:", c);
console.log("获取新数据:", f);
console.log("获取原来的合约地址:", g.address);

console.log("获取原来的合约值:", h);
console.log("更改原来合约的值,并且显示出来:", k);
// console.log("02合约地址:",Contract_Ches.address);
});

// it一个测试用例
it("实例描述,比如‘应该初始化合约状态’", async () => {
console.log("测试合约的操作:");
});
// 可以有多个测试用例
it("应该调用xx函数"async ()=>{
// 测试
asset.equal(i, "你好世界", "i的值不是你好世界,执行结果不对");
})
});

常用 truffle 命令

truffle complie - 合约编译

默认情况下,truffle complie只会编译更新的合约。可以用 --all强制编译所有合约,--network指定使用的网络

truffle development - 开发网络

会启动 truffle 内置的测试网络,在该网络中部署调用等操作不会影响真实网络

truffle console - 控制台

可以使用--network指定要使用的网络

truffle migrate - 部署合约

truffle migrate.命令将从最后完成的迁移脚本开始运行。
--reset:从头运行所有迁移脚本

truffle networks - 检查网络构件

显示每个网络上的部署合约的地址

truffle test - 运行测试脚本

执行test/文件夹中的测试脚本,可以加上文件名运行特定测试。

truffle unbox - 下载模板

下载指定模板,后接模板名