Hardhat's hook system provides an event-driven architecture for plugin lifecycle management and extensibility, allowing plugins to register handlers for various system events with type safety and proper context management.
Central interface for hook registration and execution.
interface HookManager {
/** Add a hook handler for a specific hook */
addHookHandler<T extends keyof HardhatHooks>(
hookName: T,
handler: HookHandler<T>
): void;
/** Run a hook with arguments */
runHook<T extends keyof HardhatHooks>(
hookName: T,
...args: Parameters<HardhatHooks[T]>
): Promise<any>;
/** Check if a hook has handlers */
hasHandlers<T extends keyof HardhatHooks>(hookName: T): boolean;
/** Get all registered hook names */
getHookNames(): string[];
}
type HookHandler<T extends keyof HardhatHooks> = HardhatHooks[T];Usage Examples:
import { hooks } from "hardhat";
// Add a configuration hook handler
hooks.addHookHandler("config.resolveUserConfig", async (userConfig, context) => {
// Modify user configuration before it's processed
return {
...userConfig,
paths: {
...userConfig.paths,
sources: "./custom-contracts"
}
};
});
// Add HRE initialization hook
hooks.addHookHandler("hre.initialized", async (hre, context) => {
console.log(`HRE initialized for network: ${hre.network.name}`);
});
// Run a hook manually
await hooks.runHook("hre.beforeTaskRun", taskName, taskArgs);Complete hook system with all available hook categories and their specific hooks.
interface HardhatHooks {
/** Configuration-related hooks */
config: ConfigHooks;
/** User interruption handling hooks */
userInterruptions: UserInterruptionHooks;
/** Configuration variable resolution hooks */
configurationVariables: ConfigurationVariableHooks;
/** Hardhat Runtime Environment lifecycle hooks */
hre: HardhatRuntimeEnvironmentHooks;
}Hooks for customizing configuration resolution and processing.
interface ConfigHooks {
/** Called when resolving user configuration */
resolveUserConfig: (
userConfig: HardhatUserConfig,
context: ConfigHookContext
) => Promise<HardhatUserConfig>;
/** Called after configuration is fully resolved */
configResolved: (
config: HardhatConfig,
context: ConfigHookContext
) => Promise<void>;
/** Called when configuration validation occurs */
validateConfig: (
config: HardhatConfig,
context: ConfigHookContext
) => Promise<void>;
}
interface ConfigHookContext {
/** Path to the configuration file */
configPath?: string;
/** Global options passed to Hardhat */
globalOptions: GlobalOptions;
}Hooks for Hardhat Runtime Environment lifecycle events.
interface HardhatRuntimeEnvironmentHooks {
/** Called when HRE is initialized */
initialized: (
hre: HardhatRuntimeEnvironment,
context: HookContext
) => Promise<void>;
/** Called before a task is run */
beforeTaskRun: (
taskName: string,
taskArguments: TaskArguments,
context: HookContext
) => Promise<void>;
/** Called after a task completes successfully */
afterTaskRun: (
taskName: string,
taskArguments: TaskArguments,
result: any,
context: HookContext
) => Promise<void>;
/** Called when a task fails */
taskFailed: (
taskName: string,
taskArguments: TaskArguments,
error: Error,
context: HookContext
) => Promise<void>;
/** Called before HRE is destroyed */
beforeDestroy: (
hre: HardhatRuntimeEnvironment,
context: HookContext
) => Promise<void>;
}Hooks for customizing configuration variable resolution.
interface ConfigurationVariableHooks {
/** Called when resolving a configuration variable */
resolve: (
variable: ConfigurationVariable,
context: ConfigVarHookContext
) => Promise<string | undefined>;
/** Called to validate a resolved configuration variable */
validate: (
variable: ConfigurationVariable,
value: string,
context: ConfigVarHookContext
) => Promise<boolean>;
/** Called when configuration variable resolution fails */
resolutionFailed: (
variable: ConfigurationVariable,
error: Error,
context: ConfigVarHookContext
) => Promise<void>;
}
interface ConfigVarHookContext {
/** The HRE instance */
hre: HardhatRuntimeEnvironment;
/** Additional context data */
data?: any;
}Hooks for handling user interruptions and graceful shutdown.
interface UserInterruptionHooks {
/** Called when user interruption is detected */
interrupted: (
interruption: UserInterruption,
context: HookContext
) => Promise<void>;
/** Called before cleanup begins */
beforeCleanup: (
interruption: UserInterruption,
context: HookContext
) => Promise<void>;
/** Called after cleanup completes */
afterCleanup: (
interruption: UserInterruption,
context: HookContext
) => Promise<void>;
}
interface UserInterruption {
/** Type of interruption */
type: "SIGINT" | "SIGTERM" | "uncaughtException" | "unhandledRejection";
/** Interruption signal or error */
signal?: NodeJS.Signals;
error?: Error;
/** Timestamp when interruption occurred */
timestamp: Date;
}Context object passed to most hook handlers providing access to HRE functionality.
type HookContext = Omit<HardhatRuntimeEnvironment, "tasks">;
interface HookContextWithTasks extends HookContext {
tasks: TaskManager;
}Common patterns for registering hooks in plugins.
Usage Examples:
// In a plugin file
import type { HardhatPlugin } from "hardhat/types";
const plugin: HardhatPlugin = {
id: "my-plugin",
hookHandlers: {
config: () => import("./hook-handlers/config.js"),
hre: () => import("./hook-handlers/hre.js"),
configurationVariables: () => import("./hook-handlers/config-vars.js")
},
npmPackage: "my-hardhat-plugin"
};
// In hook-handlers/config.js
export async function resolveUserConfig(userConfig, context) {
return {
...userConfig,
// Add plugin-specific configuration
myPlugin: {
enabled: true,
customPath: "./my-plugin"
}
};
}
export async function configResolved(config, context) {
console.log(`Configuration resolved for ${context.configPath}`);
}
// In hook-handlers/hre.js
export async function initialized(hre, context) {
// Add custom properties to HRE
hre.myPluginApi = {
doSomething: () => console.log("Plugin functionality")
};
}
export async function beforeTaskRun(taskName, taskArgs, context) {
if (taskName === "compile") {
console.log("About to compile contracts");
}
}Hook handlers are executed in the order they were registered, allowing plugins to depend on each other's modifications.
interface HookExecutionInfo {
/** Order in which the hook was registered */
registrationOrder: number;
/** Plugin that registered the hook */
pluginId: string;
/** Whether the hook is async */
isAsync: boolean;
}