cryptozombies7-Truffle部署智能合约
第1章:简介
如果你是 CryptoZombies 的新手,强烈建议在开始本章之前先完成前六节课。请花时间熟悉编写智能合约。否则,你可能会很难跟上本节课的内容。
缺失的一块
如果你已经完成了之前的课程,太棒了!这意味着你几乎掌握了构建 DApp 的艺术。
但是,还有一个重要的部分尚未完成。
没错……你还需要学习如何部署智能合约。
如果你有前端开发的背景,可能已经非常熟悉像 Webpack、Gulp 或 Browserify 这样的工具,它们让开发者的生活变得更轻松。
但是,Solidity 开发者使用什么工具呢?
Truffle
Truffle 是最受欢迎的区块链开发框架,原因很简单——它具有许多有用的功能:
- 简单的智能合约编译
- 自动生成 ABI
- 集成的智能合约测试——甚至支持 Mocha 和 Chai!
- 支持多个网络——代码可以部署到 Rinkeby、Ethereum,甚至 Loom。我们稍后会演示这个😉
只要你的计算机上已经安装了 npm 和 node,我们将教你如何安装 Truffle,并将其设置为全局可用。
实践测试
我们将从基础开始。打开一个新的终端窗口,创建一个名为 CryptoZombies 的目录,并进入该目录。
现在,让我们安装 Truffle 并使其全局可用。
注意:这是如何使用 npm 安装一个包并使其全局可用的命令:
1 | |
truffle fountry hardhat有什么区别?
Truffle、Foundry 和 Hardhat 都是用于开发和测试 Ethereum 智能合约的开发框架,但它们在设计理念、功能和使用场景上有所不同。下面是它们的主要区别:
1. Truffle
Truffle 是一个非常成熟和广泛使用的 Ethereum 开发框架,提供了完整的开发工具链。
主要特点:
- 合约编译和部署:Truffle 提供了一整套从编译到部署的工具链。你可以方便地编写、测试和部署智能合约。
- 测试框架:集成了 Mocha 和 Chai,支持自动化单元测试,使用 JavaScript 编写测试。
- 网络管理:内置了与 Ethereum 网络交互的功能,支持本地和远程的区块链网络。
- 脚本化迁移:通过迁移文件来管理合约的部署,支持版本控制。
- Truffle Console:一个交互式的 JavaScript 控制台,可以直接与部署在区块链上的合约进行交互。
- 集成 GANACHE:集成了 GANACHE(一个本地 Ethereum 仿真网络),允许开发人员快速在本地进行开发和测试。
优势:
- 生态系统成熟,文档丰富。
- 强大的集成测试支持。
- 适合大规模的项目,尤其是那些涉及多个合约的复杂项目。
缺点:
- 启动速度较慢,尤其是在大规模合约部署时。
- 对比其它工具,构建过程可能稍显笨重。
2. Foundry
Foundry 是一个新兴的智能合约开发框架,设计为轻量级和快速的工具。它主要侧重于高性能的合约开发和测试。
主要特点:
- Rust 编写:Foundry 使用 Rust 编写,具有比 Truffle 和 Hardhat 更高的性能,尤其是在测试和合约编译方面。
- 测试:支持用 Solidity 编写的测试,提供了高效的运行时和异步执行环境。可以通过
forge test来运行测试。 - 编译:Foundry 使用
forge工具进行高效的智能合约编译。它的编译速度非常快。 - 链上工具:Foundry 提供了一些高级功能,如链上调试和与链交互的能力。
- 无依赖:Foundry 依赖极少,旨在提供轻量的开发体验。
优势:
- 速度:相比 Truffle 和 Hardhat,Foundry 提供了显著更高的速度,尤其是在合约编译和测试方面。
- 轻量级:工具和框架更简洁,且没有太多依赖。
- 性能优化:由于使用 Rust 编写,Foundry 具有更高的性能,适合需要快速反馈的开发流程。
缺点:
- 较新的工具:相比 Truffle 和 Hardhat,Foundry 较新,社区和文档可能不如前者成熟。
- 较少的功能集成:虽然 Foundry 很强大,但它不如 Truffle 和 Hardhat 在多功能集成方面完备。
3. Hardhat
Hardhat 是一个快速的开发框架,专为以太坊智能合约开发设计,注重开发者体验和性能。
主要特点:
- 本地节点支持:Hardhat 提供一个本地 Ethereum 节点,可以在本地进行开发和测试。
- 强大的调试功能:Hardhat 提供了强大的调试功能,可以在发生合约错误时提供详细的堆栈跟踪信息。
- 易于集成:Hardhat 可以与其它工具如 Truffle 和 Web3.js 集成,支持使用 Solidity 编写的合约和测试。
- 插件支持:Hardhat 提供了丰富的插件支持,允许开发者通过插件扩展功能(例如,部署插件、Ethers.js 插件等)。
- 集成测试框架:支持 Mocha 和 Chai 的集成测试框架,允许用 JavaScript 编写测试。
- Solidity 版本管理:Hardhat 可以轻松地切换和管理不同版本的 Solidity 编译器。
优势:
- 调试工具:Hardhat 的调试工具非常强大,能够详细显示错误和调试信息。
- 插件系统:支持丰富的插件扩展,可以集成许多额外功能,如 Ethers.js、部署、测试等。
- 社区活跃:作为广泛使用的工具,Hardhat 有着非常活跃的社区和丰富的文档支持。
缺点:
- 性能:虽然 Hardhat 性能较好,但与 Foundry 相比,它的编译速度可能较慢。
- 相对较新的工具:虽然 Hardhat 在业内已被广泛使用,但它相对较新,某些高级功能的稳定性和成熟度可能还在发展中。
总结:
- Truffle 适合需要一个完整工具链的开发者,特别是那些处理多个合约和需要强大集成测试功能的项目。
- Foundry 提供了更高的性能,适合快速开发和测试的项目,特别是当速度和轻量级是首要需求时。
- Hardhat 提供了强大的调试工具和插件支持,适合希望灵活自定义开发环境并快速调试的开发者。
如果你希望高效的开发并且注重性能,Foundry 可能是最佳选择。如果你希望更成熟的工具和广泛的社区支持,Truffle 或 Hardhat 可能是更合适的选择。
第二章:开始使用 Truffle
现在我们已经安装了 Truffle,是时候通过运行 truffle init 来初始化我们的新项目了。这个命令的作用是创建一组文件夹和配置文件,目录结构如下:
1 | |
合约、迁移、测试… 看起来有点复杂 😟
别担心,学习使用 Truffle 不会让你头疼。本章将带你了解 Truffle 的默认项目结构,一旦你熟悉了 Truffle,部署智能合约将变得非常简单。
Truffle 的默认目录结构
在 CryptoZombies 目录中运行 truffle init 命令后,会创建几个目录和一些 JavaScript 和 Solidity 文件。让我们详细看看:
contracts:这是 Truffle 默认会查找所有智能合约的地方。为了让代码更加有序,我们甚至可以创建嵌套文件夹,例如
contracts/tokens。是不是很方便?😉注意:
truffle init命令会自动创建一个名为Migrations.sol的合约文件和相应的迁移文件,稍后我们会讲解它们。migrations:迁移是一个 JavaScript 文件,告诉 Truffle 如何部署智能合约。
test:这里是放置单元测试的地方,测试可以是 JavaScript 或 Solidity 文件。记住,一旦合约部署到区块链上,它就不能再修改了,因此在部署之前,测试智能合约是至关重要的。
truffle.js 和 truffle-config.js:这两个配置文件用于存储部署时的网络设置。Truffle 需要两个配置文件,因为在 Windows 系统上,
truffle.js和truffle.exe在同一个文件夹中可能会产生冲突。简单来说,如果你使用 Windows 系统,建议删除truffle.js并使用truffle-config.js作为默认配置文件。你可以查看 Truffle 的官方文档,进一步了解这部分内容。
为什么要使用这个目录结构?我不习惯,它看起来很复杂…
好吧,这里有一些理由。首先,如果你更改这些文件夹的名称,Truffle 可能不会按预期工作。
其次,遵循这种约定可以使其他开发者轻松理解你的项目结构。简而言之,使用标准的文件夹结构和代码约定,未来如果你扩展或更换团队,将会更加容易。
truffle-hdwallet-provider
在本课中,我们将使用 Infura 将我们的代码部署到 Ethereum。通过这种方式,我们可以运行应用程序,而无需自己设置 Ethereum 节点或钱包。然而,为了保证安全,Infura 不会管理私钥,这意味着它不能代表我们签署交易。由于部署智能合约需要 Truffle 来签署交易,我们需要一个名为 truffle-hdwallet-provider 的工具,它的唯一作用是处理交易签名。
注意:也许你会问,为什么我们没有在前一章就使用以下命令安装 truffle-hdwallet-provider:
1 | |
原因是,truffle init 命令期望找到一个空目录。如果目录中已经有文件,它会报错。因此,我们需要按照正确的顺序操作,先运行 truffle init,然后再安装 truffle-hdwallet-provider。
测试
运行
truffle init。该命令将生成我们讨论过的目录结构。运行
npm install truffle-hdwallet-provider。
Infura是什么
Infura 是一个基于云的服务平台,提供了通过 API 访问以太坊(Ethereum)和其他区块链网络的基础设施。它为开发者提供了一个高可用性、可扩展的接口,帮助他们与区块链进行交互,而无需自己部署和维护节点。
主要功能
以太坊节点访问:Infura 提供了一个公共的以太坊节点,开发者无需自己搭建和维护节点,就可以通过 Infura 的 API 访问以太坊网络。这使得开发者可以轻松地查询区块链状态、发送交易、调用智能合约等。
高可用性和可扩展性:由于 Infura 运行在云平台上,它能够处理大量的并发请求,并提供高可用的服务,这对于需要高频率与区块链交互的 DApp(去中心化应用)非常重要。
支持多个区块链:除了以太坊,Infura 还支持其他区块链网络,如 IPFS(InterPlanetary File System),以及多种 Layer 2 解决方案,如 Polygon(前身为 Matic)和 Arbitrum 等。
为什么使用 Infura
- 简化开发:Infura 为开发者提供了与区块链网络交互的便利,而不需要担心节点的维护、同步和管理。
- 节省资源:搭建和维护一个全节点需要大量的计算资源和存储空间,而通过 Infura,开发者可以节省这些成本。
- 可靠性和扩展性:Infura 提供了一个稳定的服务,能够高效处理大量请求,适合需要高并发的应用。
如何使用 Infura
注册 Infura 账户:你需要在 Infura 官网注册一个账户。
获取 API 密钥:注册后,你可以创建一个新的项目,并获得一个 API 密钥。这个密钥用于在应用中通过 Infura 访问以太坊网络。
配置 Web3.js:在你的 JavaScript 代码中,你可以使用 Web3.js 库将 Infura 作为你的 Web3 提供者来连接以太坊网络。例如:
1
2const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider('https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY'));进行区块链交互:一旦设置好 Infura,你就可以使用 Web3.js 或其他库来与以太坊进行交互,如查询区块链状态、发送交易、调用智能合约等。
总结
Infura 使得与区块链的交互变得更加容易、快速和高效,尤其是对于开发去中心化应用(DApp)的人来说,它提供了一个可靠的解决方案,无需自行管理区块链节点。
第3章:编译源代码
恭喜你!现在我们已经设置了项目结构并配置了 truffle-hdwallet-provider,接下来让我们编译智能合约。
为什么需要编译?
以太坊虚拟机(EVM)不能直接理解我们写的 Solidity 源代码。因此,我们需要运行一个编译器,将我们的智能合约“翻译”成机器可读的字节码。虚拟机接着执行这些字节码,并完成智能合约所要求的操作。
想知道字节码长什么样吗?让我们看一下:
1 | |
正如你所看到的,字节码对人类来说几乎就像是对真实僵尸一样难以理解!
使用 Solidity 编译器
既然我们已经提到 Solidity 编译器,我们应该说明编译器有一些非常有用的功能。
假设我们想修改 SafeMath 中的 add 函数定义:
1 | |
如果我们编译这个函数,Solidity 编译器会抛出一个警告:
1 | |
编译器试图告诉我们,这个函数不会从区块链读取或写入数据,我们应该使用 pure 修饰符。
为什么这很重要?
将函数标记为 pure 或 view 可以节省 gas。由于这些函数不会修改区块链的状态,矿工不需要执行它们。简而言之,pure 和 view 函数可以免费调用。
CryptoZombies - 游戏
记住,我们已经将逻辑嵌入到一个名为 ZombieOwnership.sol 的智能合约中。
嗯… 对于一个游戏来说,这个名字不太合适。
幸运的是,这并不是问题。我们可以使用继承创建一个具有相同操作和功能的智能合约,并可以随意命名。
让我们创建一个名为 CryptoZombies 的新智能合约,继承自 ZombieOwnership.sol:
1 | |
接下来,我们将所有智能合约复制到 ./contracts 文件夹中。现在,项目结构应该是这样的:
1 | |
一切都已正确设置。接下来,我们来编译我们的项目。
动手实践:
执行 truffle compile 命令。此命令将生成构建产物并将其放置在 ./build/contracts 目录下。
注意: 构建产物包括智能合约的“字节码”版本、ABI 以及一些 Truffle 用于正确部署代码的内部数据。避免编辑这些文件,否则 Truffle 可能会停止正常工作。
第4章:迁移
通常在这一步,部署到以太坊主网之前,你需要先在本地测试智能合约。可以使用一款名为 Ganache 的工具来实现,它会搭建一个本地以太坊测试网络。
不过,测试虽然至关重要,但完整讲解需要一整节课的篇幅 —— 因此本课程我们将专注于部署流程。如果你有兴趣深入学习测试相关内容,推荐参考我们的专属课程《使用 Truffle 测试智能合约》(Testing Smart Contracts with Truffle)。
要向以太坊网络部署合约,我们需要创建一种名为 “迁移脚本”(migration)的文件。
迁移脚本是一类 JavaScript 文件,其作用是协助 Truffle 将合约代码部署到以太坊网络。需要注意的是,执行truffle init命令时会自动生成一个特殊合约Migrations.sol,该合约用于跟踪你对代码所做的所有变更。其工作原理是将变更历史记录存储在链上,从而确保同一版本的代码不会被重复部署。
创建新的迁移
我们将从 Truffle 已经为我们创建的文件 ./contracts/1_initial_migration.js 开始。让我们看看里面的内容:
1 | |
非常简单,对吧?
首先,脚本告诉 Truffle,我们希望与 Migrations 合约进行交互。
接下来,它导出一个函数,该函数接受一个名为 deployer 的对象作为参数。这个对象充当了你(开发者)和 Truffle 部署引擎之间的接口。尽管 deployer 提供了许多有用的功能,但在本节课的范围内我们不会使用它们。如果你完成本节课后有兴趣了解更多,可以查看 Truffle 的文档,深入了解 Truffle 的其他功能。
为了准备部署,我们已经创建了一个新的文件 ./contracts/2_crypto_zombies.js,并将 ./contracts/1_initial_migration.js 中的内容复制粘贴到了其中。
实践测试
修改 ./contracts/2_crypto_zombies.js 文件,使其内容如下:
1 | |
第5章:配置文件
太棒了!你已经成功编译了源代码并创建了迁移文件。
在我们部署之前,还有一件事需要做。我们需要编辑配置文件,告诉 Truffle 我们希望部署到哪些网络。
等一下,我以为只有一个以太坊网络。这里我错过了什么吗?
以太坊测试网络
多个公共的以太坊测试网络允许你在将合约部署到主网之前免费测试它们(记住,一旦你将合约部署到主网,就无法再修改它)。这些测试网络使用与主网不同的共识算法(通常是 PoA),而且以太币是免费的,目的是鼓励进行彻底的测试。
在本节课中,我们将使用由以太坊基金会创建的公共测试网络 Rinkeby。
第5章:配置文件
太棒了!你已经成功编译了源代码并创建了迁移文件。
在我们部署之前,还有一件事需要做。我们需要编辑配置文件,告诉 Truffle 我们希望部署到哪些网络。
等一下,我以为只有一个以太坊网络。这里我错过了什么吗?
以太坊测试网络
多个公共的以太坊测试网络允许你在将合约部署到主网之前免费测试它们(记住,一旦你将合约部署到主网,就无法再修改它)。这些测试网络使用与主网不同的共识算法(通常是 PoA),而且以太币是免费的,目的是鼓励进行彻底的测试。
在本节课中,我们将使用由以太坊基金会创建的公共测试网络 Rinkeby。
truffle.js 配置文件
现在,让我们来看一下默认的 Truffle 配置文件:
1 | |
它只是一个空壳。因此,我们需要更新这个文件,以便能够将合约部署到 Rinkeby 和以太坊主网。
Truffle 的 HD 钱包提供者
还记得第二章吗?
我们要求你安装一个额外的包,叫做 truffle-hdwallet-provider,它帮助 Truffle 签署交易。
现在,我们想编辑配置文件以使用 HDWalletProvider。为了使它工作,我们将在文件的顶部添加一行代码:
1 | |
接下来,我们将创建一个新变量来存储我们的助记词(mnemonic):
1 | |
请注意,我们不建议将像助记词或私钥这样的秘密信息存储在配置文件中。
…为什么呢?
配置文件通常会被推送到 GitHub,任何人都可以看到它们,这样就容易遭受攻击 😱!为了避免泄露你的助记词(或私钥!),你应该从文件中读取它,并将该文件添加到 .gitignore 中。稍后我们会向你展示如何做到这一点。
为了简化起见,在这种情况下,我们将助记词复制并存储在一个变量中。
为 Rinkeby 和以太坊主网设置 Truffle
接下来,为了确保 Truffle “知道” 我们希望部署的网络,我们需要创建两个独立的对象——一个用于 Rinkeby,另一个用于以太坊主网:
1 | |
注意:provider 的值被包装在一个函数中,这确保了它不会在不需要时初始化。
总结
现在,让我们将这些部分组合起来,看看我们的配置文件应该是什么样子的:
1 | |
实践测试
我们已经为你更新了大部分配置文件。现在,让我们填写缺失的部分:
- 在文件的顶部,添加初始化
truffle-hdwallet-provider的代码行。 - 填写 Rinkeby 网络的
network_id。如果你不记得这个参数的值,可以查看上面的代码片段。