• 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
    
    • 1

    查看版本:

    $ truffle version
    
    • 1

    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

    打开文件,如下所示:

    在这里插入图片描述

    4. 编译合约

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

    truffle compile
    
    • 1

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

    4.1 编译构件

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

    4.2 依赖项

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

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

    import "./AnotherContract.sol";
    
    • 1

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

    2、从外部包导入合约

    import "somepackage/SomeContract.sol";
    
    • 1

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

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

    5. 部署 (迁移) 合约

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

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

    若运行迁移,执行以下命令:

    $ truffle migrate
    
    • 1

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

    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 == 'live') {
        console.log("--------------------我是live-------------------")
        console.log("当前网络账户:" + account);
      }
      
      deployer.deploy(ConvertLib,{from: "0x8038F0BF1CE32A31325BC4e166fcaCCFB171d1d6"}); // 使用部署器deployer来部署合约
      deployer.link(ConvertLib, MetaCoin);
      deployer.deploy(MetaCoin);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    直接通过命令运行即可部署两个合约,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
    
    • 1

    当不指定网络直接 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...."});
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可以设置 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]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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];
    	}
    }
    
    • 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

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

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

    6.4 执行交易

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

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

    在这里插入图片描述

    在这里插入图片描述

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

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

    6.5 执行调用

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

    instance.getBalance(accounts[0])
    
    • 1

    7. Ganache连接MetaMask

    MetaMask上有一个本地网络:localhost:8545
    因此 我们将ganache上的网络也设置为localhost:8545

    然后卸载MetaMask重新下载,导入ganache的助记词
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

  • 相关阅读:
    初始Linux(2):Shell、文件权限
    C++之STL容器(2/3)
    自制快速冒烟测试小工具--基于python多线程
    边缘计算、云计算、雾计算在物联网中的作用
    windows10 sockect tcp
    macbook电脑磁盘满了怎么删东西?
    进程间通讯(匿名管道,命名管道,共享空间)
    Operator3-设计一个operator二-owns的使用
    Day34-Linux网络管理4
    RAG 高级应用:基于 Nougat、HTML 转换与 GPT-4o 解析复杂 PDF 内嵌表格
  • 原文地址:https://blog.csdn.net/henulmh/article/details/126137076