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
Fast test execution using EVM snapshots with automatic deployment state management and custom fixture creation.
Execute deployments as test fixtures with automatic EVM snapshot management.
/**
* Execute deployments as test fixture with EVM snapshots
* @param tags - Deployment tags to execute (optional)
* @param options - Fixture configuration options
* @returns Promise resolving to deployment results
*/
fixture(
tags?: string | string[],
options?: {
fallbackToGlobal?: boolean;
keepExistingDeployments?: boolean;
}
): Promise<{ [name: string]: Deployment }>;Usage Examples:
import { deployments, getNamedAccounts } from "hardhat";
describe("Contract Tests", function () {
// Run all deployments before each test
beforeEach(async function () {
await deployments.fixture();
});
it("should have deployed contracts", async function () {
const contract = await deployments.get("MyContract");
expect(contract.address).to.not.be.undefined;
});
});
// Use specific deployment tags
describe("Token Tests", function () {
beforeEach(async function () {
await deployments.fixture(["Token", "TokenSale"]);
});
it("should work with tokens", async function () {
const token = await deployments.get("Token");
const sale = await deployments.get("TokenSale");
// Test logic here
});
});
// Multiple tags
describe("Full System Tests", function () {
beforeEach(async function () {
await deployments.fixture(["Infrastructure", "Business", "Frontend"]);
});
});
// Fixture with options
describe("Persistent Tests", function () {
beforeEach(async function () {
await deployments.fixture(["Core"], {
keepExistingDeployments: true, // Don't reset existing deployments
fallbackToGlobal: true, // Use global deployments if tag not found
});
});
});Create custom test fixtures with automatic snapshot optimization.
/**
* Create a custom fixture function with EVM snapshot optimization
* @param func - Fixture function to execute
* @param id - Optional unique identifier for the fixture
* @returns Function that executes fixture with snapshot caching
*/
createFixture<T, O>(
func: FixtureFunc<T, O>,
id?: string
): (options?: O) => Promise<T>;
type FixtureFunc<T, O> = (
env: HardhatRuntimeEnvironment,
options?: O
) => Promise<T>;Usage Examples:
import { deployments } from "hardhat";
import { ethers } from "hardhat";
// Custom fixture for complex test setup
const setupTokenEcosystem = deployments.createFixture(
async ({ deployments, getNamedAccounts }, options) => {
await deployments.fixture(["Token", "DEX", "Governance"]);
const { deployer, user1, user2 } = await getNamedAccounts();
const token = await ethers.getContract("Token", deployer);
const dex = await ethers.getContract("DEX", deployer);
// Setup initial state
await token.mint(user1, ethers.utils.parseEther("1000"));
await token.mint(user2, ethers.utils.parseEther("1000"));
return {
token,
dex,
deployer,
user1,
user2,
initialBalance: ethers.utils.parseEther("1000"),
};
},
"tokenEcosystem" // Optional ID for caching
);
describe("DEX Tests", function () {
it("should handle token swaps", async function () {
const { token, dex, user1, user2 } = await setupTokenEcosystem();
// Test logic with pre-configured state
await token.connect(user1).approve(dex.address, ethers.utils.parseEther("100"));
await dex.connect(user1).swap(ethers.utils.parseEther("100"));
});
it("should handle liquidity provision", async function () {
const { token, dex, user2, initialBalance } = await setupTokenEcosystem();
// Each test gets the same pre-configured state via snapshots
await token.connect(user2).approve(dex.address, initialBalance);
await dex.connect(user2).addLiquidity(initialBalance);
});
});
// Fixture with options
const setupWithCustomAmounts = deployments.createFixture(
async ({ deployments, getNamedAccounts }, options) => {
await deployments.fixture(["Token"]);
const { deployer, user1 } = await getNamedAccounts();
const token = await ethers.getContract("Token", deployer);
const amount = options?.amount || ethers.utils.parseEther("100");
await token.mint(user1, amount);
return { token, user1, amount };
}
);
describe("Custom Amount Tests", function () {
it("should work with default amount", async function () {
const { token, user1, amount } = await setupWithCustomAmounts();
expect(amount).to.equal(ethers.utils.parseEther("100"));
});
it("should work with custom amount", async function () {
const { token, user1, amount } = await setupWithCustomAmounts({
amount: ethers.utils.parseEther("500"),
});
expect(amount).to.equal(ethers.utils.parseEther("500"));
});
});Use fixtures that automatically execute tagged deploy scripts.
Usage Examples:
// Deploy script with tags (deploy/001_token.ts)
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre;
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();
await deploy("Token", {
from: deployer,
args: ["MyToken", "MTK", 18],
log: true,
});
};
func.tags = ["Token", "ERC20"];
func.dependencies = ["Infrastructure"];
export default func;
// Test using deploy script tags
describe("Tagged Deployment Tests", function () {
beforeEach(async function () {
// This will execute deploy scripts with "Token" tag
await deployments.fixture(["Token"]);
});
it("should have token deployed", async function () {
const token = await deployments.get("Token");
expect(token.address).to.not.be.undefined;
});
});
// Test with dependency resolution
describe("Dependency Tests", function () {
beforeEach(async function () {
// This will execute "Infrastructure" dependencies first, then "Token"
await deployments.fixture(["Token"]);
});
});
// Test subset of deployments
describe("ERC20 Tests", function () {
beforeEach(async function () {
// Execute all scripts tagged with "ERC20"
await deployments.fixture(["ERC20"]);
});
});Fixtures automatically use EVM snapshots for performance optimization.
Usage Examples:
// Expensive setup fixture
const expensiveSetup = deployments.createFixture(
async ({ deployments, getNamedAccounts }) => {
// This expensive setup only runs once, then snapshots are used
await deployments.fixture(["ComplexSystem"]);
const { deployer } = await getNamedAccounts();
// Expensive initialization
const contracts = await Promise.all([
ethers.getContract("ContractA", deployer),
ethers.getContract("ContractB", deployer),
ethers.getContract("ContractC", deployer),
]);
// Complex state setup
for (let i = 0; i < 100; i++) {
await contracts[0].createItem(i);
}
return { contracts, deployer };
},
"expensiveSetup"
);
describe("Performance Tests", function () {
// Each test gets the expensive setup instantly via snapshots
it("test 1", async function () {
const { contracts } = await expensiveSetup();
// Test logic
});
it("test 2", async function () {
const { contracts } = await expensiveSetup();
// Test logic - same setup, no re-execution
});
it("test 3", async function () {
const { contracts } = await expensiveSetup();
// Test logic - still using cached snapshot
});
});
// Nested describe blocks with different fixtures
describe("System Tests", function () {
describe("Basic functionality", function () {
beforeEach(async function () {
await deployments.fixture(["Basic"]);
});
it("basic test", async function () {
// Test logic
});
});
describe("Advanced functionality", function () {
beforeEach(async function () {
await deployments.fixture(["Basic", "Advanced"]);
});
it("advanced test", async function () {
// Test logic
});
});
});interface FixtureOptions {
/** Use global deployments if tagged deployments not found */
fallbackToGlobal?: boolean;
/** Keep existing deployments instead of resetting */
keepExistingDeployments?: boolean;
}
type FixtureFunc<T, O> = (
env: HardhatRuntimeEnvironment,
options?: O
) => Promise<T>;
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;
}
type Address = string;
type ABI = any[];