truffle

1. 介绍

Truffle套件是一个开发以太坊 (Ethereum) 区块链DApp (分布式应用程序) 的开发环境,是开发DApp的一站式解决方案,功能包括: 编译合约部署合约开发DApp前端测试DApp等。

1.1 三个模块

Truffle套件主要有3个模块:

  • Truffle:以太坊 (Ethereum) 区块链DApp开发环境。
  • Ganache:Ganache可以创建本地区块链网络,用于测试智合约,你可以在本地区块链网络上部署合约、开发应用程序、运行测试和执行其他任务,不需要付任何费用。
  • Drizzle:前端库的集合,使编写Dapp用户界面更容易。

1.2 Truffle Ethereum 特性

  • 内置智能合约编译、链接、部署和二进制管理功能。
  • 用于快速开发的自动化合约测试功能。
  • 脚本化、可扩展的合约部署和迁移框架。
  • 强大的网络管理功能,可以部署到任意数量的公共和私有网络。
  • 使用EthPM和NPM进行包管理,使用ERC190标准。
  • 交互式控制台工具可以直接与合约通信。
  • 可配置的构建管道,支持紧密集成。
  • 强大的外部脚本运行器功能,支持在Truffle环境中执行外部脚本。

2. 安装

2.1 安装Node 和 NPM

2.2 安装Truffle

安装:

$ npm install -g truffle

查看版本:

$ truffle version

3. 创建项目

可以使用 truffle init 命令可以创建原始的项目模板,但是对于初学者,使用 Truffle Box 更简单,Truffle Box中包含了很多示例应用程序和项目模板。我们将使用 MetaCoin box ,它将创建一个通证,可以在账户之间传输。

github上下载truffle各模板地址:https://github.com/truffle-box

我们下载的是matacoin-box:https://github.com/truffle-box/metacoin-box

打开文件,如下所示:

在这里插入图片描述
合约编译后生成的构建默认地址是 build/contracts
但可以改。在 truffle-config.js 中 通过 contracts_build_directory: ‘’ 修改位置。

创建项目可以直接用指令:truffle unbox

4. 编译合约

要编译Truffle项目,切换到项目根目录,然后在终端中执行以下命令:

truffle compile

在第一次执行时,将编译contracts文件夹下的所有合约。在随后的执行中,Truffle将只编译更改过的合约。如果你想全部编译,可以使用 --all 选项执行上面的命令。

4.1 编译构件

编译的构件将放在项目根目录下的build/contract/目录中(默认,可更换), 如果此目录不存在,则创建该目录。这些构件是Truffle内部工作不可或缺的,它们在应用程序的成功部署中扮演着重要的角色。你不应该编辑这些文件,在编译和部署合约时,它们将被覆盖。

4.2 依赖项

使用Solidity语言的 import 命令声明合约依赖项

1、通过文件名导入依赖项

import "./AnotherContract.sol";

这将导入另一个源文件 AnotherContract.sol 中的所有合约,导入文件的路径是基于当前源文件的相对路径。

2、从外部包导入合约

import "somepackage/SomeContract.sol";

这里,somepackage表示通过 EthPMNPM 安装的包。SomeContract.sol表示该包提供的Solidity源文件。

注意,在搜索NPM安装的包之前,Truffle将首先搜索EthPM安装的包,因此在命名冲突的罕见情况下,将使用EthPM安装的包。

5. 部署 (迁移) 合约

迁移脚本是JavaScript文件,用于将合约部署到Ethereum网络。

migrations文件夹下存放所有的迁移脚本。

若运行迁移,执行以下命令(默认是development,也就是本地ganache网络):

$ truffle migrate

若要指定特定网络:

truffle migrate --network goerli

简单地说,迁移脚本就是一组托管的部署脚本。

在 Truffle 中,migrate 命令用于部署 Solidity 智能合约到区块链网络中。当你多次运行 migrate 命令时,Truffle 会跟踪每个智能合约的部署,并尝试在之前的部署基础上进行更新。如果你的合约代码已经改变了,这可能会导致一些问题。

使用 --reset 标志可以解决这个问题。这个标志告诉 Truffle 忽略之前的部署记录,并重新部署所有合约。这将确保你的合约代码和部署状态是最新的,并可以避免由于部署记录问题而导致的错误。

truffle migrate --reset --network goerli

5.1 迁移脚本文件

const ConvertLib = artifacts.require("ConvertLib");  // 通过artifacts.require方法告诉truffle,希望与哪些合约进行交互
const MetaCoin = artifacts.require("MetaCoin");  // 此方法与Node的require类似,它返回一个合约抽象,后续代码可以使用该抽象。方法参数是合约名称

module.exports = function (deployer,network,account) { // 所有迁移脚本都必须通过module.exports导出一个函数,
                                        // 该函数接受deployer对象作为其第一个参数。deployer对象是执行部署任务的主接口
  if (network == 'goerli') {
    console.log("--------------------我是goerli-------------------")
    console.log("当前网络账户:" + account);
  }
  
  deployer.deploy(ConvertLib,{from: "0x8038F0BF1CE32A31325BC4e166fcaCCFB171d1d6"}); // 使用部署器deployer来部署合约
  deployer.link(ConvertLib, MetaCoin);
  deployer.deploy(MetaCoin);
};

直接通过命令运行即可部署两个合约,ConvertLib 和 MetaCoin。

对于上述代码:

5.2 artifacts.require()

告诉Truffle,希望与哪些合约进行交互。此方法与Node的require类似,它返回一个合约抽象,后续代码可以使用该抽象。 方法参数是合约名称,不要传递源文件的名称,因为一个源文件可以包含多个合约。

5.3 module.exports

所有迁移脚本都必须通过 module.exports 导出一个函数

第一个参数 deployer ,deployer对象是执行部署任务的主接口。

第二个参数 network,用于获取当前的网络。可以在配置文件truffle-config中配置网络节点:比如我配置了私有链网络节点 (Ganache)。

在这里插入图片描述
在部署合约时,可以指定到该网络:

truffle migrate --network live

当不指定网络直接 truffle migrate 迁移时,会自动选取本地的网络,这里我在未指定时自动选取的就是我的 Ganache 私有链网络。

第三个参数 accounts,可获取到当前网络的所有账户

5.4 Deployer API

5.4.1 deployer.deploy(contract, args…, options)

迁移脚本中,使用部署器 (Deployer) 来部署合约,部署器(Deployer)将按正确的代码顺序执行

这个函数部署指定合约,可以传入合约构造函数的参数。

  • contract 要部署的合约
  • args… 合约构造函数参数
  • options 部署选项

部署后将覆盖以前的合约地址(即 Contract.address 将等于新部署的地址)。

// 部署单个合约,不传入构造函数参数
deployer.deploy(A);

// 使用构造函数参数部署单个合约
deployer.deploy(A, arg1, arg2, ...);

// 如果已经部署了此合约,设置不要部署它
deployer.deploy(A, {overwrite: false});

// 设置部署的gas量上限,及“from”地址
deployer.deploy(A, {gas: 4612388, from: "0x...."});

可以设置 from 地址,当不设置 from 地址时,默认的是 网络中的第一个账户

5.4.2 deployer.link(library, destinations)

这个函数将已部署的库链接到一个或多个合约。目标可以是单个合约,也可以是合约数组。如果目标中有合约不依赖于被链接的库,则该合约将被忽略。

// 部署合约A,然后将contract A链接到contract B,然后部署B。
deployer.deploy(A);
deployer.link(A, B);
deployer.deploy(B);

// 将A链接到多个合约
deployer.link(A, [B, C, D]);

5.4.3 deployer.then(function() {…})

deployer的同步机制。

var a, b;
deployer.then(function() {
  // 创建a的新版本
  return A.new();
}).then(function(instance) {
  a = instance;
  // 获取B的已部署实例
  return B.deployed();
}).then(function(instance) {
  b = instance;
  // 通过B的setA()函数在B上设置A的address新实例。
  return b.setA(a.address);
});

6. 与合约交互

自己编写原始请求,与以太坊网络中的智能合约进行交互,是一件相当繁琐的工作。

Truffle简化了这个工作,利用Truffle提供的功能,我们可以方便地与合约进行交互

以太坊网络中,对于向网络写入数据,以及从网络读取数据的2种行为作了区分通常,写入数据称为交易,而读取数据称为调用。交易和调用的处理方式非常不同。

6.1 交易

交易会向网络写入数据,改变网络状态。

交易可以是简单地向一个账户发送以太币,也可以复杂到执行合约函数或向网络添加新合约。

交易有运行成本,需要消耗Gas,Gas需要用以太币支付。

交易处理需要消耗时间,不会马上返回。如果你通过交易执行合约的函数,该函数不会马上返回,因为交易不是立即处理的。通常,通过交易执行的函数不会有返回值,而是返回一个交易哈希

总结交易的特点:

  • 有执行成本,消耗Gas
  • 会更改网络的状态
  • 不立即处理
  • 没有返回值 (返回交易哈希)

6.2 调用

与交易相比,调用是非常不同的,调用是只读的。

调用可用于在以太坊网络上执行代码,但不会更改网络中的数据。

调用可以免费执行。

当通过调用执行合约函数时,将立即收到返回值。

总结调用的特点:

  • 免费,不消耗Gas
  • 不改变网络的状态
  • 立即处理
  • 有返回值

是交易还是调用,只需看是否向网络写入数据。

6.3 合约抽象对象

合约抽象对象在Javascript中表示一个合约,可以使用合约抽象对象与以太坊网络中的合约进行交互

truff-contract 包提供对合约抽象的支持。

前面提到过,获取合约抽象对象的方法之一是,使用 **artifacts.require()**函数获取。

接下来看看truffle提供的js对象 MetaCoin.sol

// SPDX-License-Identifier: MIT
// Tells the Solidity compiler to compile only from v0.8.13 to v0.9.0
pragma solidity ^0.8.13;

import "./ConvertLib.sol";

// This is just a simple example of a coin-like contract.
// It is not ERC20 compatible and cannot be expected to talk to other
// coin/token contracts.

contract MetaCoin {
	mapping (address => uint) balances;

	event Transfer(address indexed _from, address indexed _to, uint256 _value);

	constructor() {
		balances[tx.origin] = 10000;
	}

	function sendCoin(address receiver, uint amount) public returns(bool sufficient) {
		if (balances[msg.sender] < amount) return false;
		balances[msg.sender] -= amount;
		balances[receiver] += amount;
		emit Transfer(msg.sender, receiver, amount);
		return true;
	}

	function getBalanceInEth(address addr) public view returns(uint){
		return ConvertLib.convert(getBalance(addr),2);
	}

	function getBalance(address addr) public view returns(uint) {
		return balances[addr];
	}
}

通过 truffle console 指令打开 truffle 控制台。

可以指定网络 truffle console --network goerli ,不指定的话默认情况下是本地

在truffle控制台中,调用 deployed() 返回合约抽象对象并查看:
在这里插入图片描述

6.4 执行交易

使用合约抽象,可以方便地在以太坊网络上执行合约函数。

let accounts = await web3.eth.getAccounts() // 获取所有账户
instance.sendCoin(accounts[1], 10, {from: accounts[0]}) // 调用合约的sendCoin写函数,会在ganache中产生一个调用合约的交易

在这里插入图片描述

在这里插入图片描述

交易参数 – sendCoin函数没有第3个参数,我们传入了第三个参数 {from: accounts[0]},这个参数是一个特殊参数,称为交易参数,它总是可以作为最后一个参数传递给一个函数,用于设置交易的相关细节。
这里我们设置了交易来源地址,确保该交易来自accounts[0](其实不设置的话默认也是account[0] ),交易参数中,可以设置以下选项:

  • from
  • to
  • gas
  • gasPrice
  • value
  • data
  • nonce

6.5 执行调用

执行 getBalance 函数,该函数从网络中读取数据,不做任何更改,它只返回指定地址的MetaCoin余额。

instance.getBalance(accounts[0])

7. Ganache连接MetaMask

MetaMask上添加网络

在这里插入图片描述
这样就行了。但默认不会展示 ganache 本身钱包的地址。需要手动导入。
还有一种方法是卸载 metamask 重新下。导入助记词时导入ganache 自身钱包的助记词,这样就会直接把 ganache 中的多个账户展示在 metamask。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

henulmh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值