本文介绍了如何使用 Hardhat 设置 Solidity 项目,编译 Solidity 源代码,添加更多合约,以及如何使用 OpenZeppelin Contracts。内容涵盖了Solidity 语言的基础知识,并提供了以太坊和智能合约的工作原理,包括使用Hardhat进行智能合约的开发、测试和部署。
欢迎来到激动人心的的智能合约开发世界!本指南将通过以下内容让你开始编写Solidity合约:
本指南不会涵盖诸如语法或关键字之类的语言概念。为此,你需要查看以下精选内容,其中包含适用于新手和经验丰富的开发人员的优秀学习资源:
为了全面了解以太坊和智能合约的工作方式,官方网站有一个了解以太坊部分,其中包含大量适合初学者的内容。
如果你是这门语言的新手,官方Solidity文档是一个很好的参考资源。查看他们的安全建议,其中很好地介绍了区块链和传统软件平台之间的区别。
Ethernaut基于Web的游戏将让你在智能合约中寻找细微的漏洞,随着你通过难度越来越大的关卡。
说完这些,让我们开始吧!
创建一个项目后的第一步是安装开发工具。
以太坊最流行的开发框架是Hardhat和Foundry。 每个都有自己的优势,并且熟练使用所有这些工具都很有用。
在这些指南中,我们将展示如何使用Hardhat开发、测试和部署智能合约,并且我们将介绍它与ethers.js最常见的用法。
要开始使用Hardhat,我们将其安装在我们的项目目录中。
$ npm install --save-dev hardhat
安装完成后,我们可以运行 npx hardhat
。 这将在我们的项目目录中创建一个 Hardhat 配置文件(hardhat.config.js
)。
$ npx hardhat
888 888 888 888 888
888 888 888 888 888
888 888 888 888 888
8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
888 888 "88b 888P" d88" 888 888 "88b "88b 888
888 888 .d888888 888 888 888 888 888 .d888888 888
888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
👷 Welcome to Hardhat v2.22.12 👷
✔ What do you want to do? · Create an empty hardhat.config.js
配置文件已创建
我们将Solidity源文件(.sol
)存储在contracts
目录中。 这相当于你可能从其他语言中熟悉的src
目录。
我们现在可以编写我们的第一个简单的智能合约,称为Box
:它将允许人们存储一个可以稍后检索的值。
我们将此文件另存为contracts/Box.sol
。 每个.sol
文件都应包含单个合约的代码,并以其命名。
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Box {
uint256 private _value;
// Emitted when the stored value changes
// 当存储的值更改时发出
event ValueChanged(uint256 value);
// Stores a new value in the contract
// 在合约中存储一个新值
function store(uint256 value) public {
_value = value;
emit ValueChanged(value);
}
// Reads the last stored value
// 读取最后存储的值
function retrieve() public view returns (uint256) {
return _value;
}
}
以太坊虚拟机(EVM)无法直接执行Solidity代码:我们首先需要将其编译为EVM字节码。
我们的 Box.sol
合约使用 Solidity 0.8,因此我们需要首先配置 Hardhat 以使用适当的 solc 版本。
我们在 hardhat.config.js
中指定一个 Solidity 0.8 solc 版本。
// hardhat.config.js
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.24",
};
然后可以通过运行单个编译命令来实现编译:
如果你不熟悉 npx 命令,请查看我们的Node 项目设置指南。 |
$ npx hardhat compile
Compiled 1 Solidity file successfully (evm target: paris).
成功编译 1 个 Solidity 文件(EVM 目标:巴黎)。
compile
内置任务将自动查找contracts
目录中的所有合约,并使用Solidity编译器使用hardhat.config.js
中的配置来编译它们。
你会注意到创建了一个artifacts
目录:它保存了已编译的artifacts(字节码和元数据),这些是.json文件。 最好将此目录添加到你的.gitignore
中。
随着项目的增长,你将开始创建更多相互交互的合约:每个合约都应存储在其自己的 .sol
文件中。
要了解它的外观,让我们向我们的 Box
合约添加一个简单的访问控制系统:我们将在一个名为 Auth
的合约中存储一个管理员地址,并且只允许 Box
被 Auth
允许的那些帐户使用。
因为编译器会拾取contracts
目录和子目录中的所有文件,所以你可以随意组织你的代码。 在这里,我们将 Auth
合约存储在 access-control
子目录中:
// contracts/access-control/Auth.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Auth {
address private _administrator;
constructor(address deployer) {
// Make the deployer of the contract the administrator
// 使合约的部署者成为管理员
_administrator = deployer;
}
function isAdministrator(address user) public view returns (bool) {
return user == _administrator;
}
}
要从Box
中使用此合约,我们使用import
语句,通过其相对路径引用Auth
:
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Import Auth from the access-control subdirectory
// 从access-control子目录导入Auth
import "./access-control/Auth.sol";
contract Box {
uint256 private _value;
Auth private _auth;
event ValueChanged(uint256 value);
constructor() {
_auth = new Auth(msg.sender);
}
function store(uint256 value) public {
// Require that the caller is registered as an administrator in Auth
// 要求调用者在Auth中注册为管理员
require(_auth.isAdministrator(msg.sender), "Unauthorized");
_value = value;
emit ValueChanged(value);
}
function retrieve() public view returns (uint256) {
return _value;
}
}
跨多个合约分离关注点是保持每个合约简单的好方法,并且通常是一个好的做法。
但是,这并不是将代码拆分为模块的唯一方法。 你还可以在Solidity中使用继承来进行封装和代码重用,正如我们接下来将看到的。
可重用的模块和库是优秀软件的基石。OpenZeppelin Contracts包含许多有用的构建块,用于构建智能合约。 你可以放心地构建它们:它们已经过多次审计,其安全性和正确性经过了实战测试。
库中的许多合约不是独立的,也就是说,你不应该按原样部署它们。 相反,你将使用它们作为起点,通过向其中添加功能来构建你自己的合约。 Solidity提供多重继承作为实现此目的的一种机制:有关更多详细信息,请查看Solidity文档。
例如,Ownable
合约将部署者帐户标记为合约的所有者,并提供一个名为onlyOwner
的修饰符。 当应用于函数时,onlyOwner
将导致所有不是来自所有者帐户的函数调用都恢复。 用于转移和放弃所有权的函数也可用。
以这种方式使用时,继承成为一种强大的机制,允许模块化,而无需你部署和管理多个合约。
可以通过运行以下命令下载OpenZeppelin Contracts库的最新发布版本:
$ npm install @openzeppelin/contracts
你应该始终使用这些已发布版本中的库:将库源代码复制粘贴到你的项目中是一种危险的做法,很容易在你的合约中引入安全漏洞。 |
要使用其中一个OpenZeppelin合约,请使用@openzeppelin/contracts
作为其路径的前缀来import
它。 例如,为了替换我们自己的Auth
合约,我们将导入@openzeppelin/contracts/access/Ownable.sol
以将访问控制添加到Box
:
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Import Ownable from the OpenZeppelin Contracts library
// 从OpenZeppelin Contracts库导入Ownable
import "@openzeppelin/contracts/access/Ownable.sol";
// Make Box inherit from the Ownable contract
// 使Box继承自Ownable合约
contract Box is Ownable {
uint256 private _value;
event ValueChanged(uint256 value);
constructor() Ownable(msg.sender) {}
// The onlyOwner modifier restricts who can call the store function
// onlyOwner修饰符限制了谁可以调用store函数
function store(uint256 value) public onlyOwner {
_value = value;
emit ValueChanged(value);
}
function retrieve() public view returns (uint256) {
return _value;
}
}
OpenZeppelin Contracts文档是学习开发安全智能合约系统的好地方。 它包含指南和详细的API参考:例如,请参阅访问控制指南,以了解有关上述代码示例中使用的Ownable
合约的更多信息。
编写和编译Solidity合约只是在以太坊网络上运行去中心化应用程序的第一步。 一旦你对此设置感到满意,你将需要继续执行更高级的任务:
- 原文链接: docs.openzeppelin.com/le...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!