如何设置 Solidity 的本地开发环境

  • QuickNode
  • 发布于 2024-07-22 20:36
  • 阅读 97

本文介绍了如何使用Truffle和Hardhat进行智能合约的开发、测试和部署。文章详细讲解了Solidity语言的基础知识、开发环境的搭建、Truffle和Hardhat的使用方法,并提供了从本地开发到测试网络部署的完整流程。

重要通知

本指南中提到的 Truffle 或 Ganache 已不再积极维护。我们建议探索 Hardhat 框架作为替代方案,因为 Consensys 在 停止 Truffle 和 Ganache 后,已与 Hardhat 建立了新的合作伙伴关系。你可以在 这里找到我们关于 Hardhat 的指南。如果你希望看到本指南的更新版本,请 告诉我们

概述

区块链的成功故事始于比特币,并由以太坊赋予了翅膀。以太坊是第一个在不可变账本中引入可编程软件的区块链;这些运行在区块链上的程序被称为智能合约。Solidity 是用于编写智能合约的编程语言。

为了构建智能合约,我们需要一个开发环境。在这里,你有两个选择。你可以在浏览器中设置开发环境,也可以在本地机器上设置。在前者的情况下,我们有 Remix IDE 和 EthFiddle。而在后者的情况下,我们有 Hardhat 和 Truffle。本文将重点介绍如何在本地环境中开发、测试和部署我们的智能合约。

前提条件

  • NodeJS
  • VS Code 编辑器
  • 熟悉终端
  • 好奇的心态

什么是 Solidity?

根据 官方文档

Solidity 是一种面向对象的高级语言,用于实现智能合约。智能合约是管理以太坊状态中账户行为的程序。

Solidity 的灵感来自一些流行的编程语言,如 C++、JavaScript 和 Python。它是开发智能合约的理想选择。一些著名的 DeFi 项目利用 Solidity 设计智能合约,如 UniSwap、SushiSwap 和 Aave。现在你知道了一些使用 Solidity 的地方,让我们来分解它的一些特性。

  • 它是一种静态类型语言。

  • 它支持面向对象的核心范式,如继承、用户定义的数据类型、库和封装。

  • Solidity 的编译器将智能合约编译为 BytecodeABI。Bytecode 是由以太坊虚拟机 (EVM) 解释的低级语言,用于执行函数。另一方面,ABI 或应用二进制接口是一个 JSON 文件,供 Web 应用程序调用智能合约函数。

编写 Solidity 智能合约的环境

现在你已经对 Solidity 有了基本的了解,让我们谈谈可以编写和执行智能合约的地方。让我们从在线编辑器开始。

1. Remix

Remix 是一个流行的基于浏览器的 IDE(集成开发环境),用于编写智能合约。它甚至允许你编译和部署合约。默认情况下,它提供了模拟以太坊虚拟机的 Javascript 虚拟机。随着最近的 London 升级,你可以看到两个版本的 Javascript 虚拟机:Berlin 和 London。它们各自提供 15 个以太坊地址和 100 个虚拟 Ether。这些非常适合测试、部署以及与你的合约函数进行交互。下一步是使用 Injected Web3 将你的合约部署到任何测试网络。这使你可以连接到你的 MetaMask 钱包,并让你感受到这些函数在主网上的工作方式。最后一个选项是 Web3 Provider,你可以使用 Geth 提供自定义的 provider。它用于在私有以太坊区块链中测试合约函数。

2. EthFiddle

EthFiddle 是 Looms Network 提供的另一个知名的在线编辑器。与 Remix 相比,它的功能有限,但开发者广泛使用它来测试他们的合约函数。你可以将你的合约(称为 fiddles)与其他开发者分享。

现在,让我们谈谈本地开发。当你学习 Solidity 或将它们用于博客时,在线编辑器已经足够好了。但在生产级别的工作中,建议设置一个本地开发环境。

Truffle Suite

Truffle Suite 是一个著名的开发套件,它使智能合约的开发周期变得甜美,就像任何用松露烹饪的菜肴一样。在套件中,我们有:

  • Truffle:一个用于开发和测试智能合约的框架。它为你管理复杂的事务,因此开发可以专注于智能合约的逻辑。它使你能够编写自动化测试用例,以保护你的合约免受外部攻击。最后,它为你提供了将合约部署到任何网络的必要支持。
  • Ganache:只需点击几下/命令即可立即生成一个私有以太坊网络。它带有 CLI 和 GUI。无论你选择哪一个,它们默认提供 10 个以太坊账户和 100 个虚拟 Ether。通过几次点击,你可以设置你的区块时间。它还带有一个区块链浏览器,以深入了解发生的任何交易。Ganache 与 Truffle 兼容,反之亦然。这创造了一个强大的工具组合,使智能合约开发变得无缝。

Truffle 总体上是一个可靠的开发选择。它不会让你陷入困境,许多开发者在日常工作中依赖它。接下来,我们将讨论 Truffle Suite 的另一个替代选择。

Hardhat

Hardhat 是一个全方位的开发环境,你可以在其中编译、部署、测试和基准测试你的智能合约。Hardhat 实现了各种工具包来帮助你进行智能合约开发。第一个要介绍的是 task。Tasks 是从终端运行的命令;一个任务的例子是 hardhat deploy,它将把你的合约部署到区块链网络。Hardhat 工具带的第二个补充是插件生态系统。你可以使用插件来扩展和定制 Hardhat 以满足你的特定用例。Hardhat 的另一个独特功能是 console.log,它使你能够使用 JavaScript 控制台。Hardhat 甚至提供了覆盖内置功能并按需使用它们的灵活性。

考虑到这一点,让我们开始使用 Truffle Suite 和 Hardhat 测试一个示例合约。

Truffle

安装

1. 创建一个名为 Hero 的文件夹。

2. 在文件夹内,打开你的终端。

3. 在终端中,输入:

npm install -g truffle
truffle init
## 如果你在 MacOS 或 Linux 上,可能需要运行这个命令
sudo npm install -g truffle
truffle init

4. 你可以在你喜欢的编辑器中打开项目文件夹。初始文件夹结构将如下所示:

项目结构

5. 在 contracts 文件夹内,创建一个名为 Hero.sol 的文件。

6. 如果你是第一次看到 Solidity 代码,你可以查看我们关于 如何使用 Solidity 编写以太坊智能合约 的全面指南。否则,继续并复制粘贴以下代码。

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.7.0 <0.9.0;

contract Hero{
    address owner;
    string hero;
    constructor(string memory _hero)
    {
        owner=msg.sender;
        hero=_hero;
    }

    function setHero(string memory _hero)public
    {
        require(msg.sender==owner,"Not the owner");
        hero =_hero;
    }
    function getHero() public view returns(string memory)
    {
        return hero;
    }

}

7. 在 truffle-config.json 中,使用以下代码片段定义你的编译器版本。我们将使用 0.8.1。你可以删除文件中的所有起始代码。

module.exports = {
    compilers: {
        solc: {
            version: '0.8.1',
        },
    },
}

8. 现在,回到你的终端并输入:

truffle compile

它应该编译你的代码而没有任何错误。

9. 现在,你应该看到一个 build 文件夹,里面有两个 json 文件。每个文件都包含智能合约的 ABI 代码和 Bytecode。如果你已经做到了这一步,就给自己一个鼓励吧。

编译后的项目目录

运行 Truffle compile 后的项目

部署到 Ganache

现在我们的合约已经准备好并配置了 truffle,让我们将合约部署到 Ganache 并测试其功能。

1. 现在,让我们部署到我们的本地以太坊网络,Ganache 将提供这个网络。

2. 为此,你可以从 官方网站 下载 Ganache 并安装它。之后,你可以启动它。它应该显示如下屏幕:

Ganache

3. 点击 QUICKSTART,它应该会打开一个包含 10 个钱包的屏幕。

Ganache QuickStart

4. 记下 RPC Server 下可见的端口号。在本例中,它是 7545。

Ganache 端口号

5. 现在,我们将不得不更新 truffle-config.js 以启用部署到本地网络。

module.exports = {
    networks: {
        development: {
            host: '127.0.0.1',
            port: 7545,
            network_id: '*',
        },
    },
    compilers: {
        solc: {
            version: '0.8.1',
        },
    },
}

6. 现在,我们必须开发一个迁移脚本来部署我们的合约。以下代码片段将使你能够做到这一点。在 migrations 文件夹中删除起始文件并创建一个新文件:2_deploy_contract.js

var Hero = artifacts.require('Hero')
module.exports = function (deployer) {
    deployer.deploy(Hero, 'Hulk')
}
  • 第 1 行:它从 build 文件夹中导入 artifacts
  • 第 2 行:它导出了以 deployer 作为函数参数的模块。
  • 第 3 行:deployer 部署合约。它接受我们之前定义的 Hero artifact,并且我们传递构造函数参数。在本例中,我们传递 Hulk

7. 在终端中运行以下命令以将你的合约部署到本地网络:

truffle migrate

完成后,你应该会看到如下日志。记下你在那里看到的合约地址。在我们的例子中,它是 0x571dF6CbCAF5A64DDe1862D90689f2D503565Bf0。注意合约是从你在 Ganache 中看到的第一个地址部署的。

Truffle migrate

8. 我们可以轻松地在终端中与合约进行交互。在终端中运行此命令:

truffle console

此命令将你的 shell 包装在 truffle 控制台中。你应该看到你的 shell 变为:truffle(development)>

9. 让我们通过在 Truffle 控制台中输入此命令来存储合约的实例:

let instance = await Hero.deployed()

10. 让我们也将地址存储在一个变量中,以便我们可以轻松地使用它们:

let accounts = await web3.eth.getAccounts()

11. 一切就绪后,让我们调用 getHero 函数来查看我们的英雄。我们将在控制台窗口中使用 instance.getHero() 来实现这一点。

## 确保你仍在 truffle 控制台中
instance.getHero()

你应该会看到一个输出,其中包含你在部署期间传递的英雄名称。

Truffle 控制台

合约的测试用例

现在,让我们编写一些测试用例!!

1. 在 test 文件夹下创建一个名为 Hero.test.js 的文件。它应该如下所示:

2. 现在,将以下代码复制并粘贴到文件中。

const Hero = artifacts.require('Hero')
contract('Hero', accounts => {
    let hero
    before(async () => {
        hero = await Hero.deployed()
    })

    it('It should return Hulk', async () => {
        const receivedHero = await hero.getHero()
        assert.equal(receivedHero, 'Hulk')
    })

    it('It should set hero to Iron Man', async () => {
        await hero.setHero('Iron Man', { from: accounts[0] })
        const receivedHero = await hero.getHero()
        assert.equal(receivedHero, 'Iron Man')
    })
})

第 1 行:我们使用 artifacts.require 导入了我们想要测试的合约,即 Hero 合约。

第 2 行:接下来,我们定义了要测试的合约,即 Hero,然后我们将包含所有地址的 accounts 作为参数传递。

第 3 行:定义了 hero 变量,它存储了已部署合约的实例。

第 4-7 行:before 是一个命令,它将确保在测试继续之前调用其中的内容。在本例中,我们需要确保在测试之前部署了合约 Hero

第 9 行:it 包含了一个简短的描述,说明我们想要运行的测试,它是一个包含所有测试相关脚本的异步函数。

第 10 行:我们正在调用 getHero() 并将其存储在 recivedHero 中。

第 11 行:我们正在断言之前的输出是否符合我们的预期,即 Hulk。

第 14-18 行:用于测试 setHero() 函数。请注意,在函数参数中,我们传递了 from:accounts[0],因为我们使用第一个地址部署了合约,并且根据合约,只有所有者可以更改值。如果你使用其他值,如 accounts[1]、accounts[2] 等,它会抛出错误。

3. 现在我们的测试用例已经完成,让我们测试一下。在终端中运行以下命令:

你应该会看到如下输出:

这意味着,我们得到的输出符合我们预期的行为。现在我们知道我们的合约按照我们的意愿工作,我们可以将它们部署到网络!

部署到 Goerli 测试网

最后,我们需要做的是将其部署到公共测试网络。在此之前,我们需要为 Goerli 网络获取一些测试 Ether。我们需要这些来支付将智能合约部署到网络的 gas 费用。前往 QuickNode Faucet 并粘贴你的钱包地址以发送给自己一些测试 Ether。现在你已经准备好开始部署了!

1. 从你的 QuickNode 仪表板中获取 HTTP Provider URL。

Quicknode goerli 端点的截图

2. 现在,你将从 MetaMask 中获取助记词。转到设置→安全与隐私→显示秘密恢复短语。输入你的密码,它应该会显示种子短语。

3. 更新 truffle-config.js 以包含将帮助我们部署到 Goerli 的网络详细信息。

const HDWalletProvider = require('@truffle/hdwallet-provider')
const mnemonic = '<你的钱包助记词>'
const QuickNodeURL = '<你的节点 URL>'
module.exports = {
    networks: {
        development: {
            host: '127.0.0.1',
            port: 7545,
            network_id: '*',
        },
        goerli: {
            provider: () => new HDWalletProvider(mnemonic, QuickNodeURL),
            network_id: 5, // goerli 的链 id
            gas: 550000, // goerli 的区块限制比主网低
            confirmations: 0, // 部署之间的确认次数。(默认:0)
            timeoutBlocks: 200000, // 部署超时前的区块数 (最小/默认:50)
            skipDryRun: false, // 在迁移之前跳过试运行?(对于公共网络,默认为 false)
        },
    },
    compilers: {
        solc: {
            version: '0.8.1',
        },
    },
}

4. 更新配置后,你需要安装一个额外的包。运行以下命令来安装它:

npm install @truffle/hdwallet-provider

5. 在终端中,运行以下命令以部署到测试网络:

truffle migrate --network goerli

这可能需要一些时间,所以请耐心等待!!最后,你将得到如下日志。记下合约地址。在我们的例子中,它是 0x675aC09AC86dfc3CE15C93Ab307c0Da17eBE84F3

6. 前往 Goerli 区块浏览器 并将合约地址粘贴到搜索栏中。你应该会看到如下内容:

恭喜你完成了智能合约开发的生命周期!!!你可以继续用一些黑巧克力松露来犒劳自己! :)

Hardhat

要使用 Hardhat 进行部署,你可以按照我们的教程 如何使用 Hardhat 创建和部署智能合约

结论

在这里,我们看到了如何使用 Truffle 和 Hardhat。使用它们,你可以为你的合约编写测试并有效地调试它们。你也可以参考它们的官方

  • 原文链接: quicknode.com/guides/eth...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。