Hardhat plugin providing comprehensive deployment system for Ethereum smart contracts with replicable deployments and enhanced testing capabilities.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Store, retrieve, and manage deployment artifacts with comprehensive metadata tracking, deployment execution control, and artifact access.
Save and manage deployment artifacts with comprehensive metadata.
/**
* Save a deployment with metadata
* @param name - Deployment name
* @param deployment - Deployment data to save
* @returns Promise that resolves when saved
*/
save(name: string, deployment: DeploymentSubmission): Promise<void>;
/**
* Delete a saved deployment
* @param name - Deployment name to delete
* @returns Promise that resolves when deleted
*/
delete(name: string): Promise<void>;
interface DeploymentSubmission {
/** Contract ABI */
abi: ABI;
/** Deployed contract address */
address: Address;
/** Deployment transaction receipt */
receipt?: Receipt;
/** Deployment transaction hash */
transactionHash?: string;
/** Constructor arguments */
args?: any[];
/** Additional deployment metadata */
linkedData?: any;
/** Implementation address for proxies */
implementation?: string;
/** Diamond facets for diamond contracts */
facets?: Facet[];
/** Execution details for initialization */
execute?: {
methodName: string;
args: any[];
};
/** Linked library addresses */
libraries?: Libraries;
/** Contract metadata from compilation */
metadata?: string;
/** Contract creation bytecode */
bytecode?: string;
/** Contract runtime bytecode */
deployedBytecode?: string;
/** Deployment history for upgrades */
history?: Deployment[];
/** Solidity compiler input */
solcInput?: string;
/** Hash of solidity compiler input */
solcInputHash?: string;
/** Contract documentation */
userdoc?: any;
devdoc?: any;
/** Method identifiers mapping */
methodIdentifiers?: any;
/** Contract storage layout */
storageLayout?: any;
/** Gas usage estimates */
gasEstimates?: any;
/** Factory dependencies (zksync) */
factoryDeps?: string[];
}Usage Examples:
import { deployments } from "hardhat";
// Save custom deployment
await deployments.save("CustomContract", {
abi: contractAbi,
address: "0x1234567890123456789012345678901234567890",
args: ["constructor", "arguments"],
linkedData: { version: "1.0.0", deployer: "Alice" },
metadata: "contract metadata string",
libraries: {
MathLib: "0x9876543210987654321098765432109876543210",
},
});
// Save deployment with receipt
await deployments.save("TrackedContract", {
abi: abi,
address: contractAddress,
receipt: transactionReceipt,
transactionHash: txHash,
args: constructorArgs,
bytecode: compiledBytecode,
deployedBytecode: runtimeBytecode,
});
// Save proxy deployment
await deployments.save("ProxyContract", {
abi: mergedAbi,
address: proxyAddress,
implementation: implementationAddress,
execute: {
methodName: "initialize",
args: ["init", "params"],
},
history: [previousDeployment], // For upgrade tracking
});
// Delete deployment
await deployments.delete("OldContract");Access stored deployment information with error handling.
/**
* Get a deployment by name (throws if not found)
* @param name - Deployment name to retrieve
* @returns Promise resolving to deployment data
*/
get(name: string): Promise<Deployment>;
/**
* Get a deployment by name (returns null if not found)
* @param name - Deployment name to retrieve
* @returns Promise resolving to deployment data or null
*/
getOrNull(name: string): Promise<Deployment | null>;
/**
* Get all deployments for a specific address
* @param address - Contract address to find deployments for
* @returns Promise resolving to array of deployments
*/
getDeploymentsFromAddress(address: string): Promise<Deployment[]>;
/**
* Get all saved deployments
* @returns Promise resolving to mapping of deployment names to data
*/
all(): Promise<{ [name: string]: Deployment }>;
interface Deployment {
address: Address;
abi: ABI;
receipt?: Receipt;
transactionHash?: string;
args?: any[];
linkedData?: any;
implementation?: string;
facets?: Facet[];
libraries?: Libraries;
metadata?: string;
bytecode?: string;
deployedBytecode?: string;
history?: Deployment[];
numDeployments?: number;
solcInputHash?: string;
userdoc?: any;
devdoc?: any;
methodIdentifiers?: any;
storageLayout?: any;
gasEstimates?: any;
factoryDeps?: string[];
}Usage Examples:
// Get deployment (throws if not exists)
try {
const deployment = await deployments.get("MyContract");
console.log(`Contract at: ${deployment.address}`);
console.log(`ABI functions: ${deployment.abi.length}`);
} catch (error) {
console.log("Contract not deployed");
}
// Get deployment safely
const deployment = await deployments.getOrNull("OptionalContract");
if (deployment) {
console.log(`Found contract at: ${deployment.address}`);
} else {
console.log("Contract not found");
}
// Get all deployments
const allDeployments = await deployments.all();
for (const [name, deployment] of Object.entries(allDeployments)) {
console.log(`${name}: ${deployment.address}`);
}
// Find deployments by address
const deployments_at_address = await deployments.getDeploymentsFromAddress(
"0x1234567890123456789012345678901234567890"
);
console.log(`Found ${deployments_at_address.length} deployments at address`);
// Access deployment metadata
const contract = await deployments.get("DetailedContract");
if (contract.implementation) {
console.log(`Proxy implementation: ${contract.implementation}`);
}
if (contract.libraries) {
console.log(`Linked libraries:`, contract.libraries);
}
if (contract.history) {
console.log(`Upgrade history: ${contract.history.length} versions`);
}Run deployment scripts with tagging and dependency management.
/**
* Execute deployment scripts
* @param tags - Optional tags to filter deployment scripts
* @param options - Execution configuration options
* @returns Promise resolving to deployments that were executed
*/
run(
tags?: string | string[],
options?: {
/** Reset deployment memory before execution */
resetMemory?: boolean;
/** Delete existing deployment files */
deletePreviousDeployments?: boolean;
/** Write deployments to disk */
writeDeploymentsToFiles?: boolean;
/** Export deployments to file */
export?: string;
/** Export all network deployments to file */
exportAll?: string;
}
): Promise<{ [name: string]: Deployment }>;Usage Examples:
// Run all deployment scripts
const deployments_result = await deployments.run();
console.log(`Deployed ${Object.keys(deployments_result).length} contracts`);
// Run specific tagged deployments
await deployments.run(["Token", "DEX"]);
// Run with reset
await deployments.run(["Core"], {
resetMemory: true,
deletePreviousDeployments: true,
});
// Run with export
await deployments.run(["Production"], {
writeDeploymentsToFiles: true,
export: "./exports/mainnet-deployments.json",
});
// Run multiple tags
await deployments.run(["Infrastructure", "Business", "Frontend"]);
// Run with comprehensive options
const result = await deployments.run(["System"], {
resetMemory: false,
deletePreviousDeployments: false,
writeDeploymentsToFiles: true,
export: "./deployments-export.json",
exportAll: "./all-networks-export.json",
});
// Check what was deployed
for (const [name, deployment] of Object.entries(result)) {
console.log(`Deployed ${name} at ${deployment.address}`);
}Access contract compilation artifacts without deployment information.
/**
* Get hardhat compilation artifact
* @param name - Contract name to get artifact for
* @returns Promise resolving to compilation artifact
*/
getArtifact(name: string): Promise<Artifact>;
/**
* Get extended artifact with additional compilation information
* @param name - Contract name to get extended artifact for
* @returns Promise resolving to extended artifact
*/
getExtendedArtifact(name: string): Promise<ExtendedArtifact>;
interface Artifact {
contractName: string;
sourceName: string;
abi: ABI;
bytecode: string;
deployedBytecode: string;
linkReferences: LinkReferences;
deployedLinkReferences: LinkReferences;
}
interface ExtendedArtifact {
abi: ABI;
bytecode: string;
deployedBytecode?: string;
metadata?: string;
linkReferences?: LinkReferences;
deployedLinkReferences?: LinkReferences;
solcInput?: string;
solcInputHash?: string;
userdoc?: any;
devdoc?: any;
methodIdentifiers?: any;
storageLayout?: any;
evm?: any;
}Usage Examples:
// Get basic artifact
const artifact = await deployments.getArtifact("MyContract");
console.log(`Contract name: ${artifact.contractName}`);
console.log(`Source file: ${artifact.sourceName}`);
console.log(`ABI entries: ${artifact.abi.length}`);
// Get extended artifact with metadata
const extendedArtifact = await deployments.getExtendedArtifact("DetailedContract");
if (extendedArtifact.storageLayout) {
console.log("Storage layout available");
}
if (extendedArtifact.userdoc) {
console.log("User documentation available");
}
if (extendedArtifact.methodIdentifiers) {
console.log("Method identifiers:", extendedArtifact.methodIdentifiers);
}
// Use artifact for deployment
const contractArtifact = await deployments.getArtifact("CustomContract");
await deployments.deploy("CustomDeployment", {
from: deployer,
contract: contractArtifact,
args: ["arg1", "arg2"],
});
// Access compilation metadata
const artifact = await deployments.getExtendedArtifact("VerifiableContract");
if (artifact.solcInput && artifact.solcInputHash) {
console.log("Contract can be verified with solc input");
// Use for contract verification
}Task identifiers used by hardhat-deploy for defining and referencing deployment tasks.
/** Main deploy task identifier */
const TASK_DEPLOY = 'deploy';
/** Internal main deployment task */
const TASK_DEPLOY_MAIN = 'deploy:main';
/** Internal run deployment subtask */
const TASK_DEPLOY_RUN_DEPLOY = 'deploy:runDeploy';
/** Export deployment data task */
const TASK_EXPORT = 'export';
/** Etherscan verification task */
const TASK_ETHERSCAN_VERIFY = 'etherscan-verify';
/** Sourcify verification task */
const TASK_SOURCIFY = 'sourcify';Usage Examples:
import { TASK_DEPLOY, TASK_EXPORT } from "hardhat-deploy";
// Use in custom tasks
task("custom-deploy", "Custom deployment task")
.addFlag("verify", "Verify contracts after deployment")
.setAction(async (taskArgs, hre) => {
// Run main deployment task
await hre.run(TASK_DEPLOY, {
tags: "core",
export: "./deployments.json",
});
if (taskArgs.verify) {
await hre.run(TASK_ETHERSCAN_VERIFY);
}
});
// Reference in scripts
if (hre.hardhatArguments.task === TASK_DEPLOY) {
console.log("Running deployment task");
}Access deployment state information, logging, and helper utilities.
/**
* Log messages (only shown when logging is enabled)
* @param args - Arguments to log
*/
log(...args: any[]): void;
/**
* Get the current network name
* @returns Network name string
*/
getNetworkName(): string;
/**
* Get total gas used in current deployment session
* @returns Total gas used as number
*/
getGasUsed(): number;
/**
* Get network name from network object
* @param network - Hardhat network object
* @returns Network name string
*/
function getNetworkName(network: Network): string;
/**
* Traverse multiple directories and return all subdirectories
* @param dirs - Array of directory paths to traverse
* @returns Array of all subdirectory paths found
*/
function traverseMultipleDirectory(dirs: string[]): string[];
/**
* Filter ABI entries based on filter criteria
* @param abi - Contract ABI to filter
* @param filter - Filter criteria object
* @returns Filtered ABI array
*/
function filterABI(abi: ABI[], filter: any): ABI[];
/**
* Merge multiple ABIs into a single ABI
* @param abis - Array of ABIs to merge
* @param options - Merge options
* @returns Merged ABI array
*/
function mergeABIs(abis: ABI[][], options?: any): ABI[];Usage Examples:
// Network utilities
import { getNetworkName } from "hardhat-deploy";
const network = hre.network;
const name = getNetworkName(network);
console.log(`Current network: ${name}`);
// Directory traversal for deployment scripts
import { traverseMultipleDirectory } from "hardhat-deploy";
const deployPaths = ["./deploy", "./deploy-scripts", "./migrations"];
const allDirs = traverseMultipleDirectory(deployPaths);
console.log(`Found deployment directories: ${allDirs.length}`);
// ABI manipulation
import { filterABI, mergeABIs } from "hardhat-deploy";
// Filter ABI to only include specific function types
const fullABI = contractArtifact.abi;
const viewFunctions = filterABI(fullABI, { type: "function", stateMutability: "view" });
console.log(`Contract has ${viewFunctions.length} view functions`);
// Merge multiple contract ABIs
const tokenABI = await deployments.getArtifact("Token").then(a => a.abi);
const saleABI = await deployments.getArtifact("Sale").then(a => a.abi);
const mergedABI = mergeABIs([tokenABI, saleABI], {
deduplicate: true,
filter: { type: "function" }
});
console.log(`Merged ABI has ${mergedABI.length} entries`);Handle deployment errors and unknown signer scenarios.
/**
* Error thrown when a required signer is not available
*/
class UnknownSignerError extends Error {
/** Transaction that failed due to unknown signer */
txInfo: {
from: string;
to?: string;
value?: string;
data?: string;
};
constructor(message: string, txInfo: any);
}
/**
* Catch and handle unknown signer errors gracefully
* @param action - Promise or function that might throw UnknownSignerError
* @param options - Error handling options
* @returns Transaction info if error caught, null if successful
*/
catchUnknownSigner(
action: Promise<any> | (() => Promise<any>),
options?: { log?: boolean }
): Promise<null | {
from: string;
to?: string;
value?: string;
data?: string;
}>;Usage Examples:
// Handle unknown signer scenarios
try {
await deployments.execute("MyContract",
{ from: multisigAddress },
"updateSettings",
newValue
);
} catch (error) {
if (error instanceof UnknownSignerError) {
console.log("Multisig signer not available locally");
console.log("Transaction details:", error.txInfo);
}
}
// Use catchUnknownSigner wrapper
const result = await deployments.catchUnknownSigner(
deployments.execute("ProxyContract",
{ from: proxyOwner },
"upgrade",
newImplementation
),
{ log: true }
);
if (result) {
// Transaction details for manual execution
console.log("Manual transaction required:");
console.log(`From: ${result.from}`);
console.log(`To: ${result.to}`);
console.log(`Data: ${result.data}`);
} else {
console.log("Transaction executed successfully");
}
// Wrap deployment for multisig scenarios
const txInfo = await deployments.catchUnknownSigner(async () => {
return await deployments.deploy("NewContract", {
from: deployerMultisig,
args: ["constructor", "args"],
proxy: {
owner: upgradeMultisig,
proxyContract: "OpenZeppelinTransparentProxy",
},
});
});
if (txInfo) {
console.log("Deployment requires multisig execution");
// Save transaction details for later execution
}
// Logging during deployment
await deployments.log("Starting deployment phase 1");
await deployments.deploy("Contract1", { from: deployer });
await deployments.log("Contract1 deployed successfully");
// Get network information
const networkName = deployments.getNetworkName();
console.log(`Deploying on network: ${networkName}`);
// Track gas usage
const initialGas = deployments.getGasUsed();
await deployments.deploy("GasHeavyContract", { from: deployer });
const finalGas = deployments.getGasUsed();
console.log(`Deployment used ${finalGas - initialGas} gas`);
// Conditional behavior based on network
const network = deployments.getNetworkName();
if (network === "mainnet") {
deployments.log("Deploying to mainnet - using conservative gas settings");
} else {
deployments.log("Deploying to testnet - using aggressive gas settings");
}Manage deployment-related files and metadata.
/**
* Read a dot file from deployments directory
* @param name - File name to read (without .dot prefix)
* @returns Promise resolving to file contents
*/
readDotFile(name: string): Promise<string>;
/**
* Save content to a dot file in deployments directory
* @param name - File name to save (without .dot prefix)
* @param content - Content to save
* @returns Promise that resolves when saved
*/
saveDotFile(name: string, content: string): Promise<void>;
/**
* Delete a dot file from deployments directory
* @param name - File name to delete (without .dot prefix)
* @returns Promise that resolves when deleted
*/
deleteDotFile(name: string): Promise<void>;Usage Examples:
// Save deployment metadata
await deployments.saveDotFile("deployment-info", JSON.stringify({
timestamp: Date.now(),
deployer: "0x...",
network: "mainnet",
version: "1.0.0",
}));
// Read deployment metadata
try {
const info = await deployments.readDotFile("deployment-info");
const metadata = JSON.parse(info);
console.log(`Deployment from: ${new Date(metadata.timestamp)}`);
} catch (error) {
console.log("No deployment info found");
}
// Save configuration
await deployments.saveDotFile("config", JSON.stringify({
tokenName: "MyToken",
tokenSymbol: "MT",
initialSupply: "1000000",
}));
// Delete temporary files
await deployments.deleteDotFile("temp-data");
// Use for deployment state management
const deploymentState = {
phase: "initialization",
contracts: ["Token", "Sale", "Governance"],
completed: ["Token"],
pending: ["Sale", "Governance"],
};
await deployments.saveDotFile("state", JSON.stringify(deploymentState));
// Later, resume deployment
const stateContent = await deployments.readDotFile("state");
const state = JSON.parse(stateContent);
console.log(`Resuming deployment at phase: ${state.phase}`);interface DeploymentSubmission {
abi: ABI;
address: Address;
receipt?: Receipt;
transactionHash?: string;
args?: any[];
linkedData?: any;
implementation?: string;
facets?: Facet[];
execute?: {
methodName: string;
args: any[];
};
libraries?: Libraries;
metadata?: string;
bytecode?: string;
deployedBytecode?: string;
history?: Deployment[];
solcInput?: string;
solcInputHash?: string;
userdoc?: any;
devdoc?: any;
methodIdentifiers?: any;
storageLayout?: any;
gasEstimates?: any;
factoryDeps?: string[];
}
interface Deployment {
address: Address;
abi: ABI;
receipt?: Receipt;
transactionHash?: string;
args?: any[];
linkedData?: any;
implementation?: string;
facets?: Facet[];
libraries?: Libraries;
metadata?: string;
bytecode?: string;
deployedBytecode?: string;
history?: Deployment[];
numDeployments?: number;
solcInputHash?: string;
userdoc?: any;
devdoc?: any;
methodIdentifiers?: any;
storageLayout?: any;
gasEstimates?: any;
factoryDeps?: string[];
}
interface Facet {
facetAddress: string;
functionSelectors: string[];
}
type Libraries = { [libraryName: string]: Address };
type Address = string;
type ABI = any[];