Truffle tutorial

米霖

2024-10-20

什么是Truffle

Truffle 是一个面向以太坊虚拟机(EVM)的顶级开发环境、测试框架和资产管理管道,旨在让开发人员的工作变得更加轻松。使用 Truffle,你可以获得以下功能:

通过 Truffle,开发者可以轻松管理和自动化智能合约与 dApp 的开发流程,从而提升工作效率。

快速入门

什么是 Truffle 快速入门?

本页将带你了解如何创建 Truffle 项目并将智能合约部署到区块链。

创建项目

大多数 Truffle 命令需要在现有的 Truffle 项目中运行。因此,第一步是创建一个 Truffle 项目。

你可以通过 truffle init 创建一个没有智能合约的空项目,但对于初学者来说,可以使用 Truffle Boxes。这些是示例应用程序和项目模板。我们将使用 MetaCoin Box,它会创建一个可以在账户之间转移的代币(注意:该代币并不兼容 ERC-20 标准)。

使用以下命令下载(“unbox”)MetaCoin Box:

truffle unbox metacoin [PATH/TO/DIRECTORY]

完成后,你将获得以下项目结构:

探索项目

注意:本页仅是快速入门,因此不会深入讨论细节。我们将从命令行构建一个 Truffle 项目,所有命令也可以通过 VS Code 扩展执行。

打开 contracts/MetaCoin.sol 文件。这是一个使用 Solidity 编写的智能合约,用于创建 MetaCoin 代币。它还引用了同目录下的 contracts/ConvertLib.sol 文件。

接下来,查看 migrations/1_deploy_contracts.js 文件,这是迁移(部署)脚本。

然后打开 test/TestMetaCoin.sol 文件,这是一个用 Solidity 编写的测试文件,确保你的合约正常工作。

此外,还可以查看 test/metacoin.js 文件,这是一个用 JavaScript 编写的测试文件,功能与 Solidity 测试文件类似。虽然这个 Box 中没有包含,但 Truffle 也支持使用 TypeScript 编写测试。

最后,查看 truffle-config.js 文件,这是 Truffle 的配置文件,用于设置网络信息和其他项目相关的设置。文件目前为空,不过没关系,我们将在后续的 Truffle 命令中使用一些默认设置。

测试

要运行所有测试,你可以直接运行 truffle test。由于 truffle-config.js 文件中注释掉了开发配置,truffle test 会自动启动并关闭一个本地测试实例(Ganache)。如果你想使用 Ganache 的更多功能,可以单独启动实例并指定端口号。

运行以下命令,测试 MetaCoin:

truffle test

你将看到类似以下的输出:

TestMetaCoin
  ✔ testInitialBalanceUsingDeployedContract
  ✔ testInitialBalanceWithNewMetaCoin

Contract: MetaCoin
  ✔ should put 10000 MetaCoin in the first account
  ✔ should call a function that depends on a linked library
  ✔ should send coin correctly (52ms)

你也可以单独运行测试,使用以下命令:

truffle test ./test/TestMetaCoin.sol
truffle test ./test/metacoin.js

注意:如果你在 Windows 上遇到问题,请参考文档中的解决方案。

编译

只想编译合约?你可以运行 truffle compile

truffle compile

你将看到以下输出:

Compiling your contracts...
===========================
> Compiling ./contracts/ConvertLib.sol
> Compiling ./contracts/MetaCoin.sol
> Artifacts written to /路径/to/build/contracts
> Compiled successfully using:
- solc: 0.8.13+commit.abaa5c0e.Emscripten.clang

使用 Truffle Develop 进行迁移

要部署智能合约,我们需要连接到区块链。Truffle 内置了一个个人区块链用于测试。它是本地的,不会与主以太坊网络交互。

运行以下命令启动 Truffle Develop:

truffle develop

你将看到以下信息:

Truffle Develop started at http://127.0.0.1:9545/
Accounts:
(0) 0x627306090abab3a6e1400e9345bc60c78a8bef57
...
Mnemonic: candy maple cake sugar pudding cream honey rich smooth crumble sweet treat

这些是可以与区块链交互的十个账户及其私钥。

在 Truffle Develop 提示符下运行 migrate 命令,将编译好的合约部署到区块链:

migrate

你将看到类似以下的输出:

Starting migrations...
======================
> Network name:    'develop'
> Network id:      4447
> Block gas limit: 6721975

1_initial_migration.js
======================
...
> Total cost:          0.00554924 ETH

通过 Truffle Console 迁移

除了 Truffle Develop,你还可以使用 Ganache 启动你的个人区块链。需要修改 Truffle 配置文件以指向 Ganache 实例:

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 7545,
      network_id: "*"
    }
  }
};

运行 truffle migrate 部署合约。

交互

使用 Truffle Console 交互智能合约:

truffle console

执行以下命令与合约交互:

let instance = await MetaCoin.deployed()
let accounts = await web3.eth.getAccounts()

要转移代币,执行以下命令:

instance.sendCoin(accounts[1], 500)

部署到主网和测试网

要部署到其他网络,可以使用 Truffle Dashboard,只需运行 truffle dashboard 并使用 --network dashboard 进行部署、测试和运行控制台。

Truffle 使用方式

迁移到hardhat

像 Truffle 一样,Hardhat 也是一个以太坊软件的开发环境。它包括编辑、编译、调试和部署智能合约和去中心化应用程序的各种组件。

我们提供两种迁移到 Hardhat 的方法:

  1. 使用 Hardhat 插件:通过 Hardhat 插件与 Truffle 文件集成。这是一种部分迁移方法,允许您在 Hardhat 中运行为 Truffle 编写的测试和脚本。
  2. 完全迁移:配置 Hardhat,并将您的脚本和测试迁移到 Hardhat Runner 中原生运行。

使用 Hardhat 插件

通过 hardhat-truffle 插件,可以在 Hardhat 环境中使用 Truffle 合约。该插件提供了 Truffle 与 Hardhat 环境之间的桥梁,使开发人员能够在 Hardhat 项目中使用 Truffle 的合约和库。

以下步骤展示了如何使用 Hardhat 插件。有关插件的更多信息,请参阅 Hardhat 文档。

安装 Hardhat 和所需插件

使用以下命令安装 Hardhat:

npm install --save-dev hardhat

安装所需插件:

npm install --save-dev @nomiclabs/hardhat-truffle5 @nomiclabs/hardhat-web3 'web3@^1.0.0-beta.36'

创建 Hardhat 配置文件

创建 hardhat.config.js(或使用 TypeScript 时为 hardhat.config.ts),并确保其配置与 Truffle 的配置一致。请参考 Hardhat 文档,了解高级参数设置。

在配置文件中添加以下内容:

require("@nomiclabs/hardhat-truffle5");

创建 hardhat-truffle fixture

如果您的项目使用 Truffle 迁移来初始化测试环境(即测试中调用 Contract.deployed()),那么您需要将迁移调整为 hardhat-truffle fixture。

创建一个 test/truffle-fixture.js 文件,部署合约并调用 setAsDeployed() 方法以便测试。

例如,Truffle 中的迁移文件:

const Greeter = artifacts.require("Greeter");

module.exports = function (deployer) {
  deployer.deploy(Greeter);
};

需要转换为以下 hardhat-truffle fixture 文件:

const Greeter = artifacts.require("Greeter");

module.exports = async () => {
  const greeter = await Greeter.new();
  Greeter.setAsDeployed(greeter);
};

更多信息请参考 Hardhat fixtures 文档。

现在,您可以在 Hardhat 中编译、测试和部署合约。

完全迁移

您可以将 Truffle 项目完全迁移到 Hardhat。该过程包括安装和配置 Hardhat,然后更新测试和脚本。此外,您也可以使用 Hardhat 插件在 Hardhat 环境中使用 Truffle 合约。

安装 Hardhat

使用以下命令安装 Hardhat:

npm install --save-dev hardhat

更新文件夹结构

将 Truffle 文件夹结构更新为以下形式:

  • migrations 目录重命名为 scripts
  • 创建 hardhat.config.js 文件,添加网络和 Solidity 配置。更多高级参数设置请参阅 Hardhat 文档。

Truffle 和 Hardhat 的标准文件夹结构如下:

Truffle 文件夹结构 - truffle-config.js
- contracts/:用于存放合约的源文件 - migrations/:用于存放脚本文件,例如部署到链上的脚本 - test/:用于存放合约测试

Hardhat 文件夹结构 - hardhat.config.js - contracts/ - scripts/ - test/

编译、测试和部署合约

您可能需要将 Truffle 的 Web3.js 替换为 Hardhat 的 Web3.js 或 ethers.js 插件来部署合约。有关更多信息,请参考 Hardhat 文档。

使用以下步骤在 Hardhat 中编译、测试和部署合约:

  • 安装项目中的依赖:
npm i
  • 编译合约:
npx hardhat compile
  • 测试合约:
npx hardhat test
  • 测试合约的部署:
npx hardhat run scripts/deploy.js

Hardhat 在执行测试、脚本或任务时会自动启动一个内置的 Hardhat Network 节点。您也可以使用 --network 选项指定配置在 hardhat.config.js 文件中的网络,例如:

npx hardhat run --network <your-network> scripts/deploy.js

运行本地以太坊网络节点

由于 Ganache 不再可用作为本地以太坊网络节点,Hardhat 使用 Hardhat Network 作为其本地以太坊网络节点。

运行以下命令启动一个本地以太坊测试网络,向 Hardhat Network 暴露一个 JSON-RPC 接口:

npx hardhat node

您可以在 hardhat.config.js 文件中配置 Hardhat Network。例如,以下配置定义了一个名为 quickstart 的网络:

npx hardhat run --network quickstart scripts/deploy.js

配置钱包

要在 Hardhat 中使用 HD(分层确定性)钱包,您需要在 hardhat.config.js 文件中设置 accounts 字段,例如:

module.exports = {
  networks: {
    sepolia: {
      url: "...",
      accounts: {
        mnemonic: "test test test test test test test test test test test junk",
        path: "m/44'/60'/0'/0",
        initialIndex: 0,
        count: 20,
        passphrase: "",
      },
    },
  },
};

您也可以通过 Ethers Signer 接口加载可访问的账户:

module.exports = {
  networks: {
    // 内置测试网络用于合约开发
    hardhat: {
      chainId: 1337
    },
    quickstart: {
      url: "http://127.0.0.1:8545",
      chainId: 1337,
      // 仅测试账户
      accounts: [
        "0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
        "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
        "0xae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f"
      ]
    },
  }
}

使用以下代码访问前两个账户:

const [owner, otherAccount] = await ethers.getSigners();

使用助记词生成钱包

您可以在代码中通过定义在 hardhat.config.js 文件中的助记词生成钱包:

import {ethers} = require("ethers");
import { HDNodeWallet } from 'ethers';
let node = ethers.HDNodeWallet.fromMnemonic(words);

连接 MetaMask

使用以下代码将 Hardhat 网络连接到您的 MetaMask 钱包:

const provider = new ethers.BrowserProvider(window.ethereum);

从现有私钥加载钱包

您可以通过指定的网络从现有私钥加载钱包,代码如下:

import { ethers } from "hardhat";
const provider = new ethers.JsonRpcApiProvider("JSON-RPC-http-endpoint");
const wallet = new ethers.Wallet("0xMY_PRIVATE_KEY");
const signer = wallet.connect(provider);

// 也可以直接使用 provider
const wallet = new ethers.Wallet("0xMY_PRIVATE_KEY", provider);

这样,您就可以轻松将您的 Truffle 项目迁移到 Hardhat。

安装truffle

安装 Node.js

注意: 如果想安装 npm 的最新版本,可以运行以下命令:

npm i -g npm

Node Package Manager (NPM) 建议使用 Node 版本管理器(如 nvm)来安装 Node.js 和 npm,以避免安装全局模块时出现权限错误。您可以根据自己的操作系统,按照 这个链接 提供的说明进行安装。

Truffle 需要 node-gyp 来编译 Node.js 的原生附加模块。为了避免在安装 Truffle 时出现错误,建议您根据 安装说明 安装 node-gyp

使用 nvm 来安装与 Truffle 兼容的 Node.js 版本。例如,在 OSX 或 Linux 上安装 Node.js v18,可以运行以下命令:

nvm install 18

通过运行以下命令确认 Node.js 是否正确安装:

node --version

安装 Truffle

警告: 安装 Truffle 时,避免使用 sudo 命令,因为这可能会导致权限错误。

在终端中,使用 npm 安装 Truffle:

npm install -g truffle

在安装过程中,您可能会收到一些警告。为了确认 Truffle 是否安装正确,可以运行以下命令:

truffle version

如果命令成功执行并显示版本信息,说明 Truffle 已正确安装。

创建项目

大多数 Truffle 命令需要在已存在的 Truffle 项目中运行。因此,第一步是创建一个 Truffle 项目。

你可以创建一个空项目,但对于刚开始学习的人来说,建议使用 Truffle Boxes,这是一些示例应用程序和项目模板。我们将使用 MetaCoin box,它会创建一个可以在账户之间转移的代币。

创建 Truffle 项目的新目录:

mkdir MetaCoin
cd MetaCoin

下载 MetaCoin box:

truffle unbox metacoin

注意: 你可以使用 truffle unbox <box-name> 命令下载任何其他 Truffle Boxes。

注意: 如果你想创建一个不包含智能合约的空 Truffle 项目,可以使用以下命令:

truffle init

注意: 你可以使用 --force 选项来在当前目录初始化项目,不论其状态如何(例如,即使它包含其他文件或目录)。此选项适用于 initunbox 命令。请谨慎使用,因为这可能会覆盖目录中现有的文件。

完成该操作后,你将拥有如下项目结构:

  • contracts/: 存放 Solidity 智能合约的目录
  • migrations/: 存放脚本化部署文件的目录
  • test/: 存放测试应用程序和智能合约的测试文件的目录
  • truffle-config.js: Truffle 配置文件

编译项目

位置

所有的智能合约文件位于项目的 contracts/ 目录中。由于合约是用 Solidity 编写的,所有包含智能合约的文件都使用 .sol 扩展名。相关的 Solidity 库也会使用 .sol 扩展名。

命令

要编译一个 Truffle 项目,请切换到项目所在目录的根目录,并在终端中输入以下命令:

truffle compile

首次运行时,所有合约都会被编译。随后的运行中,Truffle 只会编译自上次编译后发生更改的合约。如果你想覆盖此行为,可以在命令后加上 --all 选项,强制编译所有合约。

编译产物

编译后的产物会被放置在项目根目录下的 build/contracts/ 目录中(如果该目录不存在,Truffle 会自动创建)。生成的 .json 文件的名称并不反映源文件名,而是对应智能合约的名称。这意味着如果你更改了 artifacts.require 方法中的合约名称,而该名称与源文件中的合约定义名称不匹配,可能会导致错误提示:“Error: Could not find artifacts for {yourContract} from any sources”。

这些编译产物是 Truffle 内部工作机制的重要部分,影响着应用的成功部署。你不应手动编辑这些文件,因为它们会在合约编译和部署时被覆盖。

依赖

你可以使用 Solidity 的 import 命令声明合约依赖。Truffle 会按照正确的顺序编译合约,并确保所有依赖项都被发送到编译器。依赖可以通过两种方式指定:

通过文件名导入依赖¶

要从另一个文件导入合约,在你的 Solidity 源文件中添加以下代码:

import "./AnotherContract.sol";

这将使 AnotherContract.sol 文件中的所有合约可用。在此示例中,AnotherContract.sol 是相对于当前编写的合约路径的文件。

请注意,Solidity 允许其他导入语法。更多信息请参考 Solidity import 文档

从外部包导入合约¶

Truffle 支持通过 NPM 安装的依赖项。要从依赖项中导入合约,使用以下语法:

import "somepackage/SomeContract.sol";

在这里,somepackage 代表通过 NPM 安装的包,而 SomeContract.sol 是该包提供的 Solidity 源文件。

更多关于如何使用 Truffle 的包管理功能的信息,请参考 Truffle NPM 文档

与Metamask 交互

Truffle 与 MetaMask¶

在浏览器中与智能合约交互之前,确保它们已经被编译和部署,并通过客户端 JavaScript 使用 web3 进行交互。我们推荐使用 @truffle/contract 库,因为它使得与合约的交互更加简单和稳健。

注意: 有关更多信息,包括如何使用 @truffle/contract,请参考我们的 Pet Shop 教程。

完成上述步骤后,你就可以开始使用 MetaMask 了。

什么是 MetaMask?¶

MetaMask 是最简单的方式来在浏览器中与去中心化应用(dApps)交互的工具。它是 Chrome 或 Firefox 的一个扩展,能够连接到以太坊网络而无需在浏览器的机器上运行完整节点。它可以连接到主以太坊网络、任何测试网络(如 Ropsten、Kovan 和 Rinkeby),或者本地区块链,例如由 Ganache 或 Truffle Develop 创建的本地区块链。

MetaMask

对于 Truffle 开发来说,这意味着我们可以以与用户在实际网络上交互 dApp 的相同方式使用 dApp。

安装 MetaMask¶

要在 Chrome 上安装 MetaMask,请访问 Chrome 网上应用店,然后点击 “Add to Chrome” 按钮。

要在 FireFox 上安装 MetaMask,请访问 Firefox 附加组件页面,然后点击 “Add to Firefox” 按钮。

当前端准备就绪且 MetaMask 安装完成后,你就可以看到你的 dApp 完整运行了。

使用 MetaMask 与 Ganache 交互¶

Ganache 是一个用于测试目的的区块链图形应用程序。它运行在 127.0.0.1:7545

注意: 我们建议使用 127.0.0.1 而不是 localhost,因为该地址不需要网络连接,开发时更为合适。

检测 MetaMask 的 web3 注入¶

在开始之前,我们需要确保 dApp 正在检测 MetaMask 的 web3 实例,并确保扩展程序正确配置为使用 Ganache。

MetaMask 会注入它自己的 web3 实例,因此我们要确保检查这一点。页面加载完成后,执行以下检查:

// 是否存在注入的 web3 实例?
if (typeof web3 !== 'undefined') {
  App.web3Provider = web3.currentProvider;
  web3 = new Web3(web3.currentProvider);
} else {
  // 如果没有检测到注入的 web3 实例,回退到 Ganache。
  App.web3Provider = new web3.providers.HttpProvider('http://127.0.0.1:7545');
  web3 = new Web3(App.web3Provider);
}

设置 MetaMask¶

要使用 Ganache 与 MetaMask 交互,请点击浏览器中的 MetaMask 图标,出现以下界面:

  1. 点击 “Import with seed phrase”,并在 “Wallet Seed” 输入框中输入启动 Ganache 时显示的助记词。

警告: 请不要在主以太坊网络上使用此助记词。确保将网络设置为 “Private Network”(使用 “Custom RPC” 设置)。

  1. 输入密码并点击 OK。

  2. 在 MetaMask 菜单中选择 “Custom RPC”,在 “New RPC URL” 中输入 http://127.0.0.1:7545,然后点击保存。

现在,MetaMask 已连接到由 Ganache 创建的区块链。每个由 Ganache 创建的账户都被分配了 100 个以太币。第一个账户的以太币会比其他账户少,因为它为智能合约部署提供了 gas 费用。

使用 MetaMask 与 Truffle Develop 交互¶

Truffle Develop 是一个命令行应用程序,用于运行测试区块链。它默认运行在 127.0.0.1:9545

要使用 MetaMask 与 Truffle Develop 交互,需将上面的 web3 代码中的端口号更改为 9545

// 是否存在注入的 web3 实例?
if (typeof web3 !== 'undefined') {
  App.web3Provider = web3.currentProvider;
  web3 = new Web3(web3.currentProvider);
} else {
  // 如果没有检测到注入的 web3 实例,回退到 Truffle Develop。
  App.web3Provider = new web3.providers.HttpProvider('http://127.0.0.1:9545');
  web3 = new Web3(App.web3Provider);
}

使用 MetaMask 与 Ganache CLI 交互¶

Ganache 类似,Ganache CLI 也可以与 MetaMask 一起使用,但默认运行在 127.0.0.1:8545。因此,你需要将 web3 代码中的端口号更改为 8545

// 是否存在注入的 web3 实例?
if (typeof web3 !== 'undefined') {
  App.web3Provider = web3.currentProvider;
  web3 = new Web3(web3.currentProvider);
} else {
  // 如果没有检测到注入的 web3 实例,回退到 Ganache CLI。
  App.web3Provider = new web3.providers.HttpProvider('http://127.0.0.1:8545');
  web3 = new Web3(App.web3Provider);
}

在 MetaMask 中,将 “New RPC URL” 设置为 http://127.0.0.1:8545

部署合约 migratons

运行迁移¶

迁移(Migrations)是帮助你将智能合约部署到以太坊网络的 JavaScript 文件。它们负责分阶段执行你的部署任务,假设随着项目的发展,你的部署需求会不断变化。随着项目的进化,你将创建新的迁移脚本,以将这些变化进一步应用到区块链上。之前运行的迁移历史会通过一个特殊的 Migrations 合约记录在链上,下面会详细介绍。

命令¶

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

$ truffle migrate

该命令将运行项目 migrations 目录中的所有迁移文件。最简单的迁移文件就是一组托管的部署脚本。如果你的迁移文件已经成功运行过,truffle migrate 将从上一次运行的迁移文件开始,仅运行新创建的迁移文件。如果没有新的迁移文件,truffle migrate 不会执行任何操作。你可以使用 --reset 选项从头开始运行所有迁移。有关其他命令选项的文档请参考 这里。本地测试时,请确保配置并运行了像 Ganache 这样的测试区块链,然后再执行 truffle migrate。你也可以使用 truffle develop 并运行迁移。

迁移文件¶

一个简单的迁移文件示例如下:

文件名:4_example_migration.js

var MyContract = artifacts.require("MyContract");

module.exports = function(deployer) {
  // 部署步骤
  deployer.deploy(MyContract);
};

注意,文件名以数字作为前缀,并以描述为后缀。数字前缀是为了记录迁移是否成功运行,而后缀纯粹是为了方便阅读和理解。

artifacts.require()

在迁移文件的开头,我们使用 artifacts.require() 方法告诉 Truffle 我们想要与哪些合约进行交互。此方法类似于 Node.js 中的 require,但在这里,它专门返回一个可以在部署脚本中使用的合约抽象。指定的名称应与合约定义中的名称一致,而不是源文件的名称,因为一个文件可能包含多个合约。

例如,假设有两个合约在同一个源文件中:

文件名:./contracts/Contracts.sol

contract ContractOne {
  // ...
}

contract ContractTwo {
  // ...
}

如果你只想使用 ContractTwo,你的 artifacts.require() 语句应该是:

var ContractTwo = artifacts.require("ContractTwo");

如果你想使用两个合约,则需要两个 artifacts.require() 语句:

var ContractOne = artifacts.require("ContractOne");
var ContractTwo = artifacts.require("ContractTwo");

module.exports

所有迁移文件必须通过 module.exports 语法导出一个函数。每个迁移文件导出的函数应该接受一个 deployer 对象作为第一个参数。这个对象为部署提供了一个清晰的语法,既能部署智能合约,也能处理部署中的一些重复性任务,比如保存已部署的合约。deployer 对象是你安排部署任务的主要接口,其 API 在本文后面描述。

你的迁移函数还可以接受其他参数,具体示例如下:

文件名:migrations/1_deploy_contracts.js

var SolidityContract = artifacts.require("SolidityContract");

module.exports = function(deployer) {
  // 部署 SolidityContract 合约,作为唯一的任务
  deployer.deploy(SolidityContract);
};

Deployer¶

在迁移文件中,你将使用 deployer 对象来安排部署任务。你可以像这样同步写出部署任务,并保证它们按正确的顺序执行:

// 先部署 A,再部署 B
deployer.deploy(A);
deployer.deploy(B);

或者,deployer 的每个函数都可以作为一个 Promise 来使用,以便在前一个任务执行后再执行后续任务:

// 先部署 A,然后部署 B,并传入 A 的新部署地址
deployer.deploy(A).then(function() {
  return deployer.deploy(B, A.address);
});

你可以使用 promise 链来编写整个部署流程,如果你认为这种语法更清晰。deployer API 在后文中讨论。

网络环境注意事项¶

可以根据部署的网络环境有条件地运行部署步骤。要了解详情,请参考 Networks 部分

module.exports = function(deployer, network) {
  if (network == "live") {
    // 针对名为 "live" 的网络执行特定操作
  } else {
    // 否则执行其他步骤
  }
}

可用账户¶

迁移文件还会接收由以太坊客户端和 web3 提供的账户列表,供你在部署过程中使用。这与 web3.eth.getAccounts() 返回的账户列表相同。

module.exports = function(deployer, network, accounts) {
  // 在迁移中使用账户信息
}

Deployer API¶

deployer 对象包含许多简化迁移过程的函数。它的 API 详细描述了如何处理合约部署、链上操作及条件执行的各种任务。

合约交互

与合约交互¶

简介¶

如果你自己手动编写与以太坊网络交互的请求,你很快就会发现编写这些请求非常麻烦,并且管理每个请求的状态也很复杂。幸运的是,Truffle 简化了这一过程,使与合约的交互变得轻松简单。

读取和写入数据¶

以太坊网络将读取数据和写入数据区分开来,这一区别在编写应用程序时起着重要作用。通常情况下,写入数据称为交易(Transaction),而读取数据称为调用(Call)。交易和调用的特性如下:

交易¶

交易会从根本上改变网络的状态。交易可以像发送 Ether 到另一个账户一样简单,也可以像执行合约函数或将新合约添加到网络那样复杂。交易的关键特性是它会写入或更改数据。执行交易会花费 Ether,称为gas,而且交易需要时间来处理。通过交易执行合约函数时,无法立即获得该函数的返回值,因为交易不会立即处理。通常情况下,通过交易执行的函数不会返回值,而是返回交易 ID。

总结一下,交易: - 需要消耗 gas(即 Ether) - 更改网络的状态 - 不会立即处理 - 不会返回函数值(只返回交易 ID)

调用¶

调用与交易不同。调用可以在网络上执行代码,但不会永久更改数据。调用是免费的,其关键特性是它只读取数据。通过调用执行合约函数时,你会立即获得返回值。

总结一下,调用: - 免费(不消耗 gas) - 不会更改网络的状态 - 会立即处理 - 能返回函数值

选择使用交易还是调用,取决于你是否想写入数据或读取数据。

合约抽象¶

合约抽象是通过 Javascript 与以太坊合约交互的核心。合约抽象是将合约的交互封装起来的代码,使得你可以更轻松地使用它,而不必担心底层复杂的引擎。Truffle 使用其专属的合约抽象模块 @truffle/contract,该模块提供了一种更为便捷的合约交互方式。

我们将使用 Truffle Box 中的 MetaCoin 合约作为例子。MetaCoin 合约包含以下三种方法:sendCoingetBalanceInEthgetBalance,这三种方法都可以通过交易或调用来执行。

pragma solidity >=0.4.25 <0.6.0;

import "./ConvertLib.sol";

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

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

    constructor() public {
        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 提供的 MetaCoin 抽象,你可以轻松地在以太坊网络上执行合约方法:

truffle(develop)> let instance = await MetaCoin.deployed()
truffle(develop)> instance

执行合约方法¶

执行交易¶

你可以使用合约抽象来执行合约方法,sendCoin 是一个典型的需要通过交易执行的方法。它的目的是将 MetaCoin 从一个账户发送到另一个账户,这会改变网络状态,因此应通过交易执行。

truffle(develop)> let accounts = await web3.eth.getAccounts()
truffle(develop)> instance.sendCoin(accounts[1], 10, {from: accounts[0]})

在此代码中: - sendCoin 方法会触发一笔交易,第三个参数是交易的参数对象,用来指定交易的 from 地址。

执行调用¶

getBalance 方法是一个读取网络数据的典型例子,因此通过调用来执行:

truffle(develop)> let balance = await instance.getBalance(accounts[0])
truffle(develop)> balance.toNumber()

处理交易结果¶

当你执行交易时,返回的是一个包含交易详细信息的结果对象。

truffle(develop)> let result = await instance.sendCoin(accounts[1], 10, {from: accounts[0]})
truffle(develop)> result

该结果对象包含以下信息: - result.tx:交易哈希 - result.logs:已解码的事件日志 - result.receipt:交易收据,包括消耗的 gas 数量

捕获事件¶

合约可以触发事件,你可以通过交易结果的 logs 数组来捕获这些事件。

truffle(develop)> result.logs[0]

输出会显示事件的详细信息,如事件名和参数。

部署新合约到网络¶

你可以通过 .new() 方法将自己的合约部署到网络上:

truffle(develop)> let newInstance = await MetaCoin.new()

使用指定地址的合约¶

如果你已经有了一个合约地址,可以通过 at() 方法创建该合约的抽象:

let specificInstance = await MetaCoin.at("0x1234...");

发送 Ether 到合约¶

你可以直接发送 Ether 到合约,或触发合约的回退函数,方式如下:

instance.sendTransaction({...}).then(function(result) {
  // 交易结果对象
});

或者,简化版的方式:

instance.send(web3.utils.toWei("1", "ether")).then(function(result) {
  // 交易结果对象
});

特殊方法¶

  • estimateGas:估算一笔交易需要消耗的 gas。

  • sendTransaction:强制执行交易,即使该方法可以通过调用执行。

  • call:强制执行调用。

  • request:返回一个对象,可以手动传递给 web3.eth.sendTransactionweb3.eth.call 来执行交易或调用。

调用重载方法¶

如果遇到重载方法,可能需要通过 methods 属性显式调用:

instance.methods ;
instance.methods['setValue(uint256,uint256)'](11, 55);

使用枚举¶

合约中的枚举可以通过合约抽象轻松访问。例如:

contract ExampleContract {
  enum ExampleEnum {
    Option0,
    Option1,
    Option2
  }
}

可以这样访问枚举:

ExampleContract.ExampleEnum.Option0

更多阅读¶

Truffle 提供的合约抽象包含丰富的工具,便于与合约交互。建议查阅 @truffle/contract 文档 了解更多技巧和深入见解。

truffle dashboard

使用 Truffle Dashboard¶

在部署智能合约时,你需要指定一个拥有足够资金的以太坊账户,以支付部署时的交易费用。一个常见的方法是将助记词复制粘贴到 .env 文件中,这样可以与例如 @truffle/hdwallet-provider 等工具配合使用。然而,直接复制粘贴私钥并不是最佳实践,特别是当我们有像 MetaMask 这样的钱包可以直接发送交易时。

为了解决这个问题,Truffle 开发了 Truffle Dashboard,让你可以轻松使用现有的 MetaMask 钱包来进行部署和发送其他交易。由于 Truffle Dashboard 直接连接 MetaMask,因此你还可以结合硬件钱包(如 Ledger 或 Trezor)使用它。

启动 Dashboard¶

要启动 Truffle Dashboard,你需要在一个单独的终端窗口中运行以下命令:

truffle dashboard [--port <number>] [--host <string>] [--verbose]

默认情况下,上述命令会在 http://localhost:24012 启动一个 Dashboard,并在你的默认浏览器中打开该页面。Dashboard 会提示你连接钱包并确认你连接到正确的网络。在此时,应仔细检查连接的网络,因为在部署过程中切换网络可能会导致意外情况。

通过 Truffle Dashboard 连接你的钱包后,你可以使用 MetaMask 来处理交易。

配置 Dashboard¶

Truffle 配置文件 truffle-config.js 中可以配置 Dashboard 的端口和主机。例如:

module.exports = {
  // ... 其他 Truffle 配置

  dashboard: {
    port: 24012,
  },

  networks: {
    // ... 网络配置
  }
}

连接到 Dashboard¶

Truffle 中内置了一个名为 dashboard 的网络,该网络会自动使用 Dashboard 的端口和主机地址。你可以在部署合约或运行控制台时指定使用此网络:

truffle migrate --network dashboard
truffle console --network dashboard

这样,所有的以太坊 RPC 请求都会通过 Truffle 转发到 Truffle Dashboard,在 Dashboard 上用户可以检查请求并使用 MetaMask 处理它们。

如果你需要自定义网络选项,你可以在 truffle-config.js 中添加 dashboard 网络,并提供必要的网络参数。例如:

module.exports = {
  // ... 其他 Truffle 配置

  networks: {
    dashboard: {
      networkCheckTimeout: 120000,
    }
  },

  dashboard: {
    port: 24012,
    host: "localhost",
  }
}

解码请求¶

Truffle Dashboard 支持解码某些 RPC 调用,能够更直观地显示你和以太坊网络之间的通信。当前支持的方法包括 eth_sendTransactionpersonal_signeth_signTypedData_v3eth_signTypedData_v4

每次运行 truffle compile 时,Truffle 都会将合约信息共享给 Truffle Dashboard,这样在调用方法时,Truffle Dashboard 会通过解码器将请求翻译成人类可读的格式。

例如,低级别请求看起来可能是这样的:

0xa0e9439c000000000000000000000000627306090abab3a6e1400e9345bc60c78a8bef570000000000000000000000000000000000000000000000000000000000000005

而在 Truffle Dashboard 上,它会显示为:

mint(myaccount.eth, 5)

与非 Truffle 工具一起使用¶

为了让 Truffle Dashboard 对所有开发者都可用,我们设计它时不依赖于特定工具。你可以将 Truffle Dashboard 与非 Truffle 工具(如 Hardhat)结合使用。

使用 Truffle Dashboard 和 Hardhat 插件¶

我们为 Hardhat 开发了插件,使 Truffle Dashboard 的体验无缝集成到 Hardhat 项目中。在 hardhat.config.js 文件中配置 Truffle Dashboard 的 RPC URL:

module.exports = {
  networks: {
    'truffle-dashboard': {
      url: "http://localhost:24012/rpc"
    }
  },
};

然后,你可以像平时一样使用 Hardhat 命令:

hardhat deploy --network truffle-dashboard