• 介绍
• 异步函数
• 任务的动作要求
• 定义参数
• 位置参数限制
• 类型验证
• 覆盖任务
• 子任务
• hardhat Tutorials , hardhat 教程
• Contact 联系方式
• 介绍
本指南将探讨在 Hardhat 中创建任务,这是用于自动化的核心组件。
任务是带有一些关联元数据的 JavaScript 异步函数。 Hardhat 使用此元数据为您自动执行某些操作。处理参数解析、验证和帮助消息。
您在 Hardhat 中可以做的所有事情都被定义为一项任务。开箱即用的默认操作是内置任务,它们使用与您作为用户可用的相同 API 来实现。
要查看项目中当前可用的任务,请运行 npx hardhat
:
$ npx hardhat
Hardhat version 2.9.10
Usage: hardhat [GLOBAL OPTIONS] <TASK> [TASK OPTIONS]
GLOBAL OPTIONS:
--config A Hardhat config file.
--emoji Use emoji in messages.
--help Shows this message, or a task's help if its name is provided
--max-memory The maximum amount of memory that Hardhat can use.
--network The network to connect to.
--show-stack-traces Show stack traces.
--tsconfig A TypeScript config file.
--verbose Enables Hardhat verbose logging
--version Shows hardhat's version.
AVAILABLE TASKS:
check Check whatever you need
clean Clears the cache and deletes all artifacts
compile Compiles the entire project, building all artifacts
console Opens a hardhat console
flatten Flattens and prints contracts and their dependencies
help Prints this message
node Starts a JSON-RPC server on top of Hardhat Network
run Runs a user-defined script after compiling the project
test Runs mocha tests
To get help for a specific task run: npx hardhat help [task]
您可以创建其他任务,这些任务将显示在此列表中。例如,您可以创建一个任务来重置开发环境的状态,或与您的合约交互,或打包您的项目。
让我们来看看创建一个与智能合约交互的过程。
Hardhat 中的任务是异步 JavaScript 函数,可以访问 Hardhat 运行时环境,它公开其配置和参数,以及对其他任务和任何插件的编程访问可能已注入的对象。
对于我们的示例,我们将使用 @nomicfoundation/hardhat-toolbox
,其中包括 [ethers.js](https://docs.ethers. io/v5/) 库来与我们的合约进行交互。
npm install --save-dev @nomicfoundation/hardhat-toolbox
任务创建代码可以放在 hardhat.config.js
中,或者任何你调用的配置文件中。这是创建简单任务的好地方。如果您的任务更复杂,将代码拆分为多个文件并从配置文件中“要求”它们也是完全有效的。
(如果您正在编写添加任务的 Hardhat 插件,它们也可以从单独的 npm 包中创建。在我们的 [构建插件部分](…/advanced/building-plugins.md)中了解有关通过插件创建任务的更多信息).)
配置文件总是在启动时执行,然后才会发生其他任何事情。 记住这一点是很好的。我们将加载 Hardhat 工具箱并将我们的任务创建代码添加到其中。
对于本教程,我们将创建一个任务以从终端获取帐户余额。您可以使用 Hardhat 的配置 API 来执行此操作,该 API 在 hardhat.config.js
的全局范围内可用:
require("@nomicfoundation/hardhat-toolbox");
task("balance", "Prints an account's balance").setAction(async () => {});
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.9",
};
保存文件后,您应该可以在 Hardhat 中看到任务:
$ npx hardhat
Hardhat version 2.9.10
Usage: hardhat [GLOBAL OPTIONS] <TASK> [TASK OPTIONS]
GLOBAL OPTIONS:
--config A Hardhat config file.
...
AVAILABLE TASKS:
balance Prints an account's balance
check Check whatever you need
clean Clears the cache and deletes all artifacts
...
To get help for a specific task run: npx hardhat help [task]
现在让我们实现我们想要的功能。我们需要从用户那里获取账户地址。我们可以通过向我们的任务添加一个参数来做到这一点:
task("balance", "Prints an account's balance")
.addParam("account", "The account's address")
.setAction(async () => {});
当您向任务添加参数时,Hardhat 将为您处理其帮助消息:
$ npx hardhat help balance
Hardhat version 2.9.10
Usage: hardhat [GLOBAL OPTIONS] balance --account <STRING>
OPTIONS:
--account The account's address
balance: Prints an account's balance
For global options help run: hardhat help
现在让我们获取帐户的余额。 Hardhat 运行时环境 将在全局范围内可用。通过使用 Hardhat 工具箱中包含的 Hardhat 的 ether.js 插件,我们可以访问 ethers.js 实例:
task("balance", "Prints an account's balance")
.addParam("account", "The account's address")
.setAction(async (taskArgs) => {
const balance = await ethers.provider.getBalance(taskArgs.account);
console.log(ethers.utils.formatEther(balance), "ETH");
});
最后,我们可以运行它:
$ npx hardhat balance --account 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
10000.0 ETH
这就是你的第一个功能齐全的 Hardhat 任务,它允许你以一种简单的方式与以太坊区块链进行交互。
• 异步函数
您可以在 hardhat.config.js
文件中创建自己的任务。 Config API 将在全局环境中可用,具有用于定义任务的功能。如果您希望保持明确,也可以使用 require("hardhat/config")
导入 API,并利用编辑器的自动完成功能。
通过调用 task
函数来创建任务。它将返回一个 TaskDefinition
对象,该对象可用于定义任务的参数。
您可以定义的最简单的任务是
task(
"hello",
"Prints 'Hello, World!'",
async function (taskArguments, hre, runSuper) {
console.log("Hello, World!");
}
);
task
的第一个参数是任务名称。第二个是它的描述,用于在 CLI 中打印帮助消息。第三个是接收以下参数的异步函数:
-
taskArguments
是一个带有已解析的任务 CLI 参数的对象。在这种情况下,它是一个空对象。 -
hre
是 Hardhat 运行时环境。 -
runSuper
仅在您覆盖现有任务时才相关,我们将在接下来了解。它的目的是让你运行原始任务的动作。
定义动作的参数是可选的。 Hardhat Runtime Environment 和 runSuper
也将在全球范围内可用。我们可以这样重写我们的“hello”任务:
task("hello", "Prints 'Hello, World!'", async () => {
console.log("Hello, World!");
});
• 任务的动作要求
编写任务的唯一要求是其操作返回的“承诺”必须在它启动的每个异步进程完成之前解决。
这是其操作不满足此要求的任务示例:
task("BAD", "This task is broken", async () => {
setTimeout(() => {
throw new Error(
"This task's action returned a promise that resolved before I was thrown"
);
}, 1000);
});
这个其他任务使用“Promise”来等待超时触发:
task("delayed-hello", "Prints 'Hello, World!' after a second", async () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("Hello, World!");
resolve();
}, 1000);
});
});
手动创建一个 Promise
看起来很有挑战性,但如果你坚持使用 async
/await
和基于 Promise
的 API,则不必这样做。例如,您可以将 npm 包 delay
用于 setTimeout
的承诺版本。
• 定义参数
安全帽任务可以接收带值的命名参数(例如 --parameter-name parameterValue
)、没有值的标志(例如 --flag-name
)、位置参数或可变参数。可变参数的作用类似于 JavaScript 的其余参数。 Config API task
函数返回一个对象,其中包含定义所有方法的方法。一旦定义,Hardhat 就可以控制解析参数、验证它们并打印帮助消息。
向 hello
任务添加可选参数如下所示:
task("hello", "Prints a greeting'")
.addOptionalParam("greeting", "The greeting to print", "Hello, World!")
.setAction(async ({ greeting }) => console.log(greeting));
并且将使用 npx hardhat hello --greeting Hola
运行。
• 位置参数限制
位置和可变参数不必命名,并且具有编程语言的通常限制:
- 没有位置参数可以跟随可变参数
- 必需/强制参数不能跟随可选参数。
不遵守这些限制将导致加载安全帽时引发异常。
• 类型验证
Hardhat 负责验证和解析为每个参数提供的值。您可以声明参数的类型,Hardhat 将获取 CLI 字符串并将它们转换为您想要的类型。如果此转换失败,它将打印一条错误消息,解释原因。
Config API 中可以通过 types
对象使用多种类型。这个对象在处理你的 hardhat.config.js
之前被注入到全局范围中,但你也可以使用 const { types } = require("hardhat/config")
显式地导入它,并利用编辑器的自动完成功能。
为其参数之一定义类型的任务的示例是
task("hello", "Prints 'Hello' multiple times")
.addOptionalParam(
"times",
"The number of times to print 'Hello'",
1,
types.int
)
.setAction(async ({ times }) => {
for (let i = 0; i < times; i++) {
console.log("Hello");
}
});
使用 npx hardhat hello --times notanumber
调用它会导致错误。
• 覆盖任务
定义与现有任务同名的任务将覆盖现有任务。这对于更改或扩展内置和插件提供的任务的行为很有用。
任务覆盖的工作方式与扩展类时的覆盖方法非常相似。您可以设置自己的操作,该操作可以调用被覆盖的操作。覆盖任务时的唯一限制是您不能添加或删除参数。
任务覆盖顺序很重要,因为操作只能使用 runSuper
函数调用立即覆盖的定义。
覆盖内置任务是自定义和扩展安全帽的好方法。要知道要覆盖哪些任务,请查看 src/builtin-tasks。
• runSuper
函数
runSuper
是一个可用于覆盖任务操作的函数。它可以作为任务的第三个参数接收,也可以直接从全局对象中使用。
这个函数的工作方式类似于 JavaScript 的 super
关键字:它调用任务之前定义的动作。
如果任务没有覆盖之前的任务定义,那么调用 runSuper
将导致错误。要检查调用它是否会失败,您可以使用 boolean
字段 runSuper.isDefined
。
runSuper
函数接收一个可选参数:一个带有任务参数的对象。如果未提供此参数,则将使用调用它的操作接收到的相同任务参数。
• 子任务
创建具有大量逻辑的任务使得扩展或自定义它们变得困难。制作多个相互调用的小而专注的任务是允许扩展的更好方法。如果您以这种方式设计任务,则只想更改其中一小部分的用户可以覆盖您的子任务之一。
例如,compile
任务被实现为多个任务的管道。它只调用像compile:get-source-paths
、compile:get-dependency-graph
和compile:build-artifacts
这样的子任务。我们建议在中间任务前加上主要任务和冒号。
为避免帮助消息因大量中间任务而变得混乱,您可以使用 subtask
配置 API 函数定义这些任务。 subtask
函数的工作方式几乎与 task
完全一样。唯一的区别是用它定义的任务不会包含在帮助消息中。
要运行子任务或任何任务,您可以使用“运行”功能。它有两个参数:要运行的任务的名称,以及一个带有参数的对象。
这是运行子任务的任务示例:
task("hello-world", "Prints a hello world message").setAction(
async (taskArgs, hre) => {
await hre.run("print", { message: "Hello, World!" });
}
);
subtask("print", "Prints a message")
.addParam("message", "The message to print")
.setAction(async (taskArgs) => {
console.log(taskArgs.message);
});
• hardhat Tutorials , hardhat 教程
CN 中文 Github hardhat 教程 : github.com/565ee/hardhat_CN
CN 中文 CSDN hardhat 教程 : blog.csdn.net/wx468116118
EN 英文 Github hardhat Tutorials : github.com/565ee/hardhat_EN
• Contact 联系方式
Homepage : 565.ee
微信公众号 : wx468116118
微信 QQ : 468116118
GitHub : github.com/565ee
CSDN : blog.csdn.net/wx468116118
Email : [email protected]