Hardhat's plugin system provides a modular architecture for extending functionality through plugins with dependency management, lifecycle hooks, and type-safe extensions.
Core interface for defining Hardhat plugins with metadata and functionality.
interface HardhatPlugin {
/** Unique plugin identifier */
id: string;
/** NPM package name containing the plugin */
npmPackage: string;
/** Hook handlers provided by the plugin */
hookHandlers?: Partial<HardhatPluginHookHandlers>;
/** Global CLI options added by the plugin */
globalOptions?: GlobalOptionDefinition[];
/** Plugin dependencies (lazy-loaded) */
dependencies?: () => Promise<HardhatPlugin>[];
/** Plugin initialization function */
init?: (hre: HardhatRuntimeEnvironment) => Promise<void>;
}Usage Examples:
import type { HardhatPlugin } from "hardhat/types";
const myPlugin: HardhatPlugin = {
id: "my-custom-plugin",
npmPackage: "hardhat-my-plugin",
hookHandlers: {
hre: () => import("./hre-hooks.js"),
config: () => import("./config-hooks.js")
},
globalOptions: [
{
name: "my-option",
description: "Custom plugin option",
type: ArgumentType.STRING,
defaultValue: "default"
}
],
dependencies: () => [
import("hardhat-dependency-plugin")
],
init: async (hre) => {
// Plugin initialization logic
console.log(`Initializing plugin on network: ${hre.network.name}`);
}
};
export default myPlugin;Hook handler definitions that plugins can implement.
interface HardhatPluginHookHandlers {
/** Configuration-related hook handlers */
config?: () => Promise<ConfigHookHandlers>;
/** HRE lifecycle hook handlers */
hre?: () => Promise<HreHookHandlers>;
/** Configuration variable hook handlers */
configurationVariables?: () => Promise<ConfigVarHookHandlers>;
/** User interruption hook handlers */
userInterruptions?: () => Promise<UserInterruptionHookHandlers>;
}
interface ConfigHookHandlers {
resolveUserConfig?: (
userConfig: HardhatUserConfig,
context: ConfigHookContext
) => Promise<HardhatUserConfig>;
configResolved?: (
config: HardhatConfig,
context: ConfigHookContext
) => Promise<void>;
validateConfig?: (
config: HardhatConfig,
context: ConfigHookContext
) => Promise<void>;
}
interface HreHookHandlers {
initialized?: (
hre: HardhatRuntimeEnvironment,
context: HookContext
) => Promise<void>;
beforeTaskRun?: (
taskName: string,
taskArguments: TaskArguments,
context: HookContext
) => Promise<void>;
afterTaskRun?: (
taskName: string,
taskArguments: TaskArguments,
result: any,
context: HookContext
) => Promise<void>;
taskFailed?: (
taskName: string,
taskArguments: TaskArguments,
error: Error,
context: HookContext
) => Promise<void>;
}Plugin-defined global CLI options that extend Hardhat's command-line interface.
interface GlobalOptionDefinition {
/** Option name (without dashes) */
name: string;
/** Option description for help text */
description: string;
/** Argument type */
type: ArgumentType;
/** Default value */
defaultValue?: ArgumentTypeToValueType[ArgumentType];
/** Short flag name */
shortName?: string;
/** Whether the option is a flag (boolean) */
isFlag?: boolean;
/** Whether the option is required */
isRequired?: boolean;
}
/**
* Creates a global option definition
* @param definition - The option definition
*/
function globalOption(definition: GlobalOptionDefinition): void;Usage Examples:
import { globalOption, ArgumentType } from "hardhat/config";
// Add a string option
globalOption({
name: "api-key",
description: "API key for external service",
type: ArgumentType.STRING,
defaultValue: undefined
});
// Add a boolean flag
globalOption({
name: "verbose-logging",
description: "Enable verbose logging",
type: ArgumentType.BOOLEAN,
defaultValue: false,
isFlag: true
});
// Add a number option with short name
globalOption({
name: "max-retries",
shortName: "r",
description: "Maximum number of retries",
type: ArgumentType.INT,
defaultValue: 3
});System for managing plugin dependencies and load order.
type PluginDependency =
| string
| { name: string; optional?: boolean }
| (() => Promise<HardhatPlugin>);
interface PluginDependencyInfo {
/** Plugin name or identifier */
name: string;
/** Whether the dependency is optional */
optional: boolean;
/** Resolved plugin instance */
plugin?: HardhatPlugin;
/** Load status */
status: "pending" | "loading" | "loaded" | "failed";
/** Load error if failed */
error?: Error;
}System for registering and managing plugins.
interface PluginRegistry {
/** Register a plugin */
register(plugin: HardhatPlugin): void;
/** Get a registered plugin by ID */
get(pluginId: string): HardhatPlugin | undefined;
/** Check if a plugin is registered */
has(pluginId: string): boolean;
/** Get all registered plugin IDs */
getPluginIds(): string[];
/** Load and initialize all plugins */
loadAll(hre: HardhatRuntimeEnvironment): Promise<void>;
/** Get plugin dependency graph */
getDependencyGraph(): Map<string, string[]>;
}Specialized error handling for plugin-related issues.
class HardhatPluginError extends Error {
/** Plugin name that caused the error */
readonly pluginName: string;
/** Original error if this wraps another error */
readonly parent?: Error;
constructor(
pluginName: string,
message: string,
parent?: Error
);
}
interface PluginLoadError extends Error {
/** Plugin ID that failed to load */
pluginId: string;
/** Type of load error */
errorType: "missing" | "invalid" | "dependency" | "initialization";
/** Additional error context */
context?: any;
}Usage Examples:
import { HardhatPluginError } from "hardhat/plugins";
// In plugin code, throw plugin-specific errors
throw new HardhatPluginError(
"my-plugin",
"Failed to connect to external service",
originalError
);
// Handle plugin errors
try {
await hre.run("my-plugin-task");
} catch (error) {
if (error instanceof HardhatPluginError) {
console.log(`Plugin error in ${error.pluginName}: ${error.message}`);
}
}Plugins can extend Hardhat's type system through module augmentation.
Usage Examples:
// In plugin's type extension file
declare module "hardhat/types/hre" {
interface HardhatRuntimeEnvironment {
myPlugin: {
doSomething(): Promise<void>;
getSomething(): string;
};
}
}
declare module "hardhat/types/config" {
interface HardhatUserConfig {
myPlugin?: {
enabled?: boolean;
apiKey?: string;
};
}
interface HardhatConfig {
myPlugin: {
enabled: boolean;
apiKey?: string;
};
}
}
// In plugin implementation
export async function initialized(hre, context) {
hre.myPlugin = {
doSomething: async () => {
console.log("Doing something...");
},
getSomething: () => "something"
};
}Plugins follow a specific lifecycle during loading and execution.
interface PluginLifecycle {
/** Phase 1: Plugin registration */
register: () => void;
/** Phase 2: Dependency resolution */
resolveDependencies: () => Promise<void>;
/** Phase 3: Plugin initialization */
initialize: (hre: HardhatRuntimeEnvironment) => Promise<void>;
/** Phase 4: Hook handler registration */
registerHooks: (hre: HardhatRuntimeEnvironment) => Promise<void>;
/** Phase 5: Plugin ready for use */
ready: () => void;
}