npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@diamondslab/diamonds-hardhat-foundry

v2.4.0

Published

Hardhat plugin that integrates Foundry testing with Diamond proxy contracts, providing deployment helpers and fuzzing support

Readme

@diamondslab/diamonds-hardhat-foundry

npm version License: MIT Tests Test Coverage

Hardhat plugin that seamlessly integrates Foundry testing with ERC-2535 Diamond proxy contracts. This plugin provides deployment helpers, test scaffolding, and automated test generation for Diamond-based smart contracts using Foundry's powerful testing framework.

Production Ready: 141/141 tests passing (100% success rate) with comprehensive coverage across unit, integration, fuzz, and invariant testing.

Features

  • 🚀 Automated Diamond Deployment - Deploy Diamond contracts with a single command
  • 📝 Helper Generation - Automatically generate Solidity helpers with deployment data
  • 🧪 Test Scaffolding - Create unit, integration, and fuzz test templates
  • Coverage Testing - Run forge coverage with full Diamond integration and multiple report formats
  • 🔧 Hardhat Tasks - CLI tasks for init, deploy, generate, test, and coverage workflows
  • 🎯 Programmatic API - Use framework classes directly in scripts
  • 📚 Base Contracts - Reusable Solidity utilities and test base classes
  • Foundry Integration - Leverage Forge's speed and fuzzing capabilities
  • 🔗 Diamonds Ecosystem - Works seamlessly with @diamondslab/diamonds and @diamondslab/hardhat-diamonds

Installation

npm install --save-dev @diamondslab/diamonds-hardhat-foundry
# or
yarn add -D @diamondslab/diamonds-hardhat-foundry
# or
pnpm add -D @diamondslab/diamonds-hardhat-foundry

Prerequisites

  • Foundry: Install from getfoundry.sh
  • Hardhat: ^2.26.0 or later
  • Required Peer Dependencies:
    • @diamondslab/diamonds - Core Diamond deployment library
    • @diamondslab/hardhat-diamonds - Hardhat Diamond configuration and LocalDiamondDeployer
    • @nomicfoundation/hardhat-ethers - Ethers.js integration
    • ethers - Ethereum library
npm install --save-dev @diamondslab/diamonds @diamondslab/hardhat-diamonds @nomicfoundation/hardhat-ethers ethers hardhat

Note: Version 2.0.0+ requires @diamondslab/hardhat-diamonds as a peer dependency for LocalDiamondDeployer. See MIGRATION.md for upgrade instructions from v1.x.

Quick Start

1. Configure Hardhat

Import the plugin in your hardhat.config.ts:

import "@diamondslab/diamonds-hardhat-foundry";
import type { HardhatUserConfig } from "hardhat/config";

const config: HardhatUserConfig = {
  solidity: "0.8.28",

  // Optional: Configure diamonds-foundry settings
  diamondsFoundry: {
    helpersDir: "test/foundry/helpers",
    generateExamples: true,
    exampleTests: ["unit", "integration", "fuzz"],
    defaultNetwork: "hardhat",
    reuseDeployment: false,
    forgeTestArgs: ["-vvv"],
  },
};

export default config;

2. Initialize Test Structure

npx hardhat diamonds-forge:init

This creates:

test/foundry/
├── helpers/           # Generated Diamond deployment helpers
├── unit/              # Unit test examples
├── integration/       # Integration test examples
└── fuzz/              # Fuzz test examples

3. Deploy Your Diamond

npx hardhat diamonds-forge:deploy --diamond-name YourDiamond --network hardhat

4. Generate Helpers

npx hardhat diamonds-forge:generate-helpers --diamond-name YourDiamond

This generates test/foundry/helpers/DiamondDeployment.sol with:

  • Diamond contract address
  • All facet addresses
  • Helper functions for test setup

Importing Helper Contracts

Version 2.0.0+ provides importable Solidity helper contracts for your tests:

DiamondFuzzBase - Base Contract for Fuzz Tests

Extend DiamondFuzzBase to create Diamond fuzz tests with built-in helpers:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "@diamondslab/diamonds-hardhat-foundry/contracts/DiamondFuzzBase.sol";
import "../helpers/DiamondDeployment.sol";

contract MyDiamondFuzzTest is DiamondFuzzBase {
    /// Override to load your deployed Diamond
    function _loadDiamondAddress() internal view override returns (address) {
        return DiamondDeployment.diamond();
    }

    function setUp() public override {
        super.setUp(); // Loads Diamond and ABI
        // Your additional setup
    }

    function testFuzz_SomeFunction(address user, uint256 amount) public {
        // Use built-in helpers
        vm.assume(DiamondForgeHelpers.isValidTestAddress(user));
        vm.assume(DiamondForgeHelpers.isValidTestAmount(amount));
        
        bytes4 selector = bytes4(keccak256("transfer(address,uint256)"));
        bytes memory data = abi.encode(user, amount);
        (bool success, ) = _callDiamond(selector, data);
        assertTrue(success);
    }
}

Built-in DiamondFuzzBase Methods:

  • _loadDiamondAddress() - Override to provide Diamond address
  • _getDiamondABIPath() - Override to customize ABI file path
  • _callDiamond(selector, data) - Call Diamond function
  • _callDiamondWithValue(selector, data, value) - Call payable function
  • _expectDiamondRevert(selector, data, expectedError) - Test reverts
  • _verifyFacetRouting(selector, expectedFacet) - Check selector routing
  • _measureDiamondGas(selector, data) - Measure gas consumption
  • _getDiamondOwner() - Get Diamond owner address
  • _hasRole(role, account) - Check role-based access control
  • _grantRole(role, account) - Grant role (requires permissions)
  • _revokeRole(role, account) - Revoke role

DiamondForgeHelpers - Utility Library

Use DiamondForgeHelpers for validation, assertions, and DiamondLoupe queries:

import "@diamondslab/diamonds-hardhat-foundry/contracts/DiamondForgeHelpers.sol";

contract MyTest is Test {
    using DiamondForgeHelpers for address;

    function setUp() public {
        address diamond = deployDiamond();
        
        // Validate Diamond deployment
        DiamondForgeHelpers.assertValidDiamond(diamond);
        
        // Validate facet
        address facet = getFacet();
        DiamondForgeHelpers.assertValidFacet(facet, "MyFacet");
    }

    function testFuzz_ValidInputs(address addr, uint256 amount) public {
        // Filter fuzz inputs
        vm.assume(DiamondForgeHelpers.isValidTestAddress(addr));
        vm.assume(DiamondForgeHelpers.isValidTestAmount(amount));
        // Your test logic
    }

    function testSelectorRouting() public {
        bytes4 selector = bytes4(keccak256("someFunction()"));
        
        // Assert selector exists
        DiamondForgeHelpers.assertSelectorExists(diamond, selector);
        
        // Assert routing to expected facet
        DiamondForgeHelpers.assertSelectorRouting(diamond, selector, expectedFacet);
        
        // Get facet address
        address facet = DiamondForgeHelpers.getFacetAddress(diamond, selector);
    }
}

DiamondForgeHelpers Functions:

  • assertValidDiamond(address) - Validate Diamond deployment
  • assertValidFacet(address, name) - Validate facet deployment
  • isValidTestAddress(address) - Filter fuzz addresses
  • isValidTestAmount(uint256) - Filter fuzz amounts
  • assertSelectorExists(diamond, selector) - Verify selector registered
  • assertSelectorRouting(diamond, selector, facet) - Verify routing
  • getFacetAddress(diamond, selector) - Get facet for selector
  • getFacetAddresses(diamond) - Get all facet addresses
  • getFacetSelectors(diamond, facet) - Get selectors for facet
  • getDiamondOwner(diamond) - Get owner address
  • boundAddress(seed) - Generate valid fuzz address
  • boundAmount(seed, min, max) - Generate valid fuzz amount
  • selectorsEqual(a, b) - Compare selector arrays

DiamondABILoader - ABI File Parser

Load and parse Diamond ABI files in your tests:

import "@diamondslab/diamonds-hardhat-foundry/contracts/DiamondABILoader.sol";

contract MyIntegrationTest is Test {
    using DiamondABILoader for string;

    function testABILoading() public {
        // Load Diamond ABI
        string memory abiJson = DiamondABILoader.loadDiamondABI("./diamond-abi/MyDiamond.json");
        
        // Extract selectors and signatures
        bytes4[] memory selectors = abiJson.extractSelectors();
        string[] memory signatures = abiJson.extractSignatures();
        
        console.log("Functions found:", selectors.length);
        
        // Get function info
        for (uint i = 0; i < selectors.length; i++) {
            (bytes4 sel, string memory sig) = abiJson.getFunctionInfo(i);
            console.log("Function:", sig);
        }
    }
}

DiamondABILoader Functions:

  • loadDiamondABI(path) - Load ABI JSON from file
  • extractSelectors(abiJson) - Extract all function selectors
  • extractSignatures(abiJson) - Extract all function signatures
  • getFunctionInfo(abiJson, index) - Get selector and signature
  • verifySelectorsMatch(abiJson, expectedSelectors) - Validate selectors

5. Run Foundry Tests

npx hardhat diamonds-forge:test

Hardhat Tasks

diamonds-forge:init

Initialize the Foundry test directory structure.

Options:

  • --helpers-dir <path> - Custom directory for helpers (default: test/foundry/helpers)
  • --examples - Generate example tests (default: from config)
  • --force - Overwrite existing files

Example:

npx hardhat diamonds-forge:init --helpers-dir test/forge/helpers --examples

diamonds-forge:deploy

Deploy a Diamond contract to the specified network.

Options:

  • --diamond-name <name> - Name of the Diamond to deploy (default: from config)
  • --network <name> - Network to deploy to (default: hardhat)
  • --reuse - Reuse existing deployment if found (default: from config)
  • --force - Force new deployment even if one exists

Example:

npx hardhat diamonds-forge:deploy --diamond-name MyDiamond --network sepolia

diamonds-forge:generate-helpers

Generate Solidity helper contract with deployment data.

Options:

  • --diamond-name <name> - Name of the Diamond (default: from config)
  • --output-dir <path> - Output directory for helpers (default: from config)
  • --network <name> - Network to use for deployment data (default: hardhat)

Example:

npx hardhat diamonds-forge:generate-helpers --diamond-name MyDiamond --output-dir test/foundry/helpers

diamonds-forge:test

Run Foundry tests with optional deployment and helper generation.

Options:

  • --diamond-name <name> - Diamond to deploy/test (default: from config)
  • --network <name> - Network for deployment (default: from config)
  • --skip-deployment - Skip deployment step
  • --skip-helpers - Skip helper generation step
  • --match-test <pattern> - Run tests matching pattern
  • --match-contract <pattern> - Run tests in matching contracts
  • --verbosity <level> - Forge verbosity level (1-5)
  • --gas-report - Show gas usage report

Example:

# Run all tests with gas reporting
npx hardhat diamonds-forge:test --gas-report

# Run specific tests with high verbosity
npx hardhat diamonds-forge:test --match-test "testOwnership" --verbosity 4

# Skip deployment (use existing)
npx hardhat diamonds-forge:test --skip-deployment --match-contract "MyTest"

diamonds-forge:coverage

Run forge coverage for your Diamond contracts with full integration support.

Important: Coverage requires a deployed Diamond on a persistent network (like localhost). See workflow below.

Options:

  • --diamond-name <name> - Diamond to analyze (default: from config)
  • --network <name> - Network for deployment (default: from config)
  • --report <format> - Report format: summary, lcov, debug, bytecode (default: summary)
  • --report-file <path> - Output file for coverage report
  • --lcov-version <version> - LCOV version for LCOV reports (default: v1)
  • --match-test <pattern> - Run tests matching pattern
  • --match-contract <pattern> - Run tests in matching contracts
  • --match-path <glob> - Run tests in files matching glob
  • --no-match-test <pattern> - Skip tests matching pattern
  • --no-match-contract <pattern> - Skip tests in matching contracts
  • --no-match-path <glob> - Skip tests in files matching glob
  • --no-match-coverage <pattern> - Exclude contracts from coverage
  • --verbosity <level> - Verbosity level: 0-5 (default: 0)
  • --color <mode> - Color output: auto, always, never (default: auto)
  • And many more options for filtering, optimization, and EVM configuration

Workflow (Required):

# Step 1: Start Hardhat node (persistent network)
npx hardhat node

# Step 2: Deploy Diamond to localhost network (in another terminal)
npx hardhat diamonds-forge:deploy --diamond-name MyDiamond --network localhost

# Step 3: Run coverage against deployed Diamond
npx hardhat diamonds-forge:coverage --diamond-name MyDiamond --network localhost

Examples:

# Basic coverage with default summary
npx hardhat diamonds-forge:coverage --diamond-name MyDiamond --network localhost

# Generate LCOV report for CI/CD
npx hardhat diamonds-forge:coverage \
  --diamond-name MyDiamond \
  --network localhost \
  --report lcov \
  --report-file coverage/lcov.info

# Coverage for specific test patterns
npx hardhat diamonds-forge:coverage \
  --diamond-name MyDiamond \
  --network localhost \
  --match-contract "Unit" \
  --verbosity 2

# Multiple report formats
npx hardhat diamonds-forge:coverage \
  --diamond-name MyDiamond \
  --network localhost \
  --report summary \
  --report lcov \
  --report debug

Important Notes:

  • Always specify --network localhost (coverage needs deployed contracts)
  • Cannot use --network hardhat (ephemeral in-memory network)
  • Diamond must be deployed before running coverage
  • Same workflow as diamonds-forge:test - both require persistent network

See the Coverage Guide for complete documentation, CI/CD integration examples, and best practices.

Dynamic Helper Generation

Version 2.0.0+ introduces dynamic helper generation that creates deployment-specific Solidity helpers without hardcoded addresses.

How It Works

When you deploy a Diamond and generate helpers, the plugin creates test/foundry/helpers/DiamondDeployment.sol with:

library DiamondDeployment {
    /// @notice Get the deployed Diamond address
    function getDiamondAddress() internal pure returns (address) {
        return 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512;
    }

    /// @notice Get the deployer address
    function getDeployerAddress() internal pure returns (address) {
        return 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
    }
    
    // Additional deployment-specific data...
}

Key Benefits:

  • No hardcoded addresses in test files - All addresses come from deployment records
  • Network-specific helpers - Different helpers for different networks
  • Automatic regeneration - Helpers update when you redeploy
  • Type-safe - Solidity library ensures compile-time validation

Using Generated Helpers

import "../helpers/DiamondDeployment.sol";

contract MyTest is Test {
    address diamond;
    address deployer;

    function setUp() public {
        // Load from generated helpers
        diamond = DiamondDeployment.getDiamondAddress();
        deployer = DiamondDeployment.getDeployerAddress();
        
        // Use in tests
        vm.prank(deployer);
        IDiamond(diamond).someFunction();
    }
}

Deployment Management

The plugin provides flexible deployment management with two modes:

Ephemeral Deployments (Default)

For quick testing without persisting deployment records:

# Deploy Diamond for this test run only
npx hardhat diamonds-forge:test --diamond-name MyDiamond

# Deployment is cached in memory but not saved to file
# Helpers are generated from cached data
# Next run will deploy fresh Diamond

Use ephemeral mode when:

  • Running tests in CI/CD
  • Testing with default Hardhat network
  • You don't need to reuse deployments
  • Each test run should start clean

Persistent Deployments

Save deployment records for reuse across test runs:

# Deploy and save deployment record
npx hardhat diamonds-forge:deploy --diamond-name MyDiamond --network localhost

# Run tests using saved deployment
npx hardhat diamonds-forge:test --network localhost --use-deployment

# Deployment is loaded from file
# Subsequent runs reuse the same Diamond

Use persistent mode when:

  • Testing on persistent networks (localhost, testnets)
  • Developing against specific Diamond deployment
  • Testing upgrade scenarios
  • Running integration tests

Task Flags Reference

Deployment Control:

  • --save-deployment - Save deployment record to file (persistent mode)
  • --use-deployment - Load existing deployment instead of deploying new one
  • --force-deploy - Force new deployment even if one exists

Helper Control:

  • --skip-helpers - Don't generate DiamondDeployment.sol
  • --helpers-dir <path> - Custom output directory for helpers

Test Filtering:

  • --match-test <pattern> - Run only tests matching name pattern
  • --match-contract <contract> - Run only tests in specified contract
  • --match-path <path> - Run only tests in files matching path

Output Control:

  • --verbosity <1-5> - Set Forge output verbosity (default: 2)
  • --gas-report - Display detailed gas usage report
  • --coverage - Generate test coverage report

Network Control:

  • --network <name> - Network to use (hardhat, localhost, sepolia, etc.)
  • --fork-url <url> - Custom RPC URL for forking

Examples

# Quick ephemeral test (default)
npx hardhat diamonds-forge:test

# Save deployment for reuse
npx hardhat diamonds-forge:test --save-deployment --network localhost

# Reuse saved deployment
npx hardhat diamonds-forge:test --use-deployment --network localhost

# Test specific functionality with gas report
npx hardhat diamonds-forge:test --match-test "testOwnership" --gas-report

# Run only fuzz tests with high verbosity
npx hardhat diamonds-forge:test --match-contract "Fuzz" --verbosity 4

# Force redeploy even if deployment exists
npx hardhat diamonds-forge:test --force-deploy --network localhost

Snapshot and Restore

The DiamondForgeHelpers library provides snapshot/restore functionality for advanced testing scenarios.

Basic Usage

import "@diamondslab/diamonds-hardhat-foundry/contracts/DiamondForgeHelpers.sol";

contract MyTest is Test {
    using DiamondForgeHelpers for *;

    function test_MultipleScenarios() public {
        // Save initial state
        uint256 snapshot = DiamondForgeHelpers.snapshotState();
        
        // Test scenario A
        runScenarioA();
        
        // Restore to initial state
        DiamondForgeHelpers.revertToSnapshot(snapshot);
        
        // Test scenario B from same starting point
        runScenarioB();
    }
}

When to Use Snapshots

Good Use Cases:

  • Testing multiple outcomes from same initial state
  • Expensive setup that you want to reuse
  • Testing state transitions and rollbacks
  • Benchmarking gas costs across scenarios

Don't Use For:

  • Normal test isolation (Forge does this automatically)
  • Production/testnet testing (snapshots only work locally)

Snapshot Examples

See comprehensive examples in test/foundry/integration/SnapshotExample.t.sol:

forge test --match-path "**/SnapshotExample.t.sol" -vv

Available in examples:

  • Basic snapshot/restore workflow
  • Multiple snapshots with different states
  • Snapshot with contract state changes
  • Test isolation patterns

For detailed snapshot documentation, see TESTING.md.

Programmatic API

Use the framework classes directly in your scripts:

import hre from "hardhat";
import {
  ForgeFuzzingFramework,
  DeploymentManager,
  HelperGenerator,
} from "@diamondslab/diamonds-hardhat-foundry";

// Deploy a Diamond
const deployer = new DeploymentManager(hre);
await deployer.deploy("MyDiamond", "hardhat");

// Generate helpers
const generator = new HelperGenerator(hre);
await generator.scaffoldProject();
await generator.generateDeploymentHelpers("MyDiamond", "hardhat");

// Run tests
const framework = new ForgeFuzzingFramework(hre);
await framework.runTests({
  diamondName: "MyDiamond",
  networkName: "hardhat",
  skipDeployment: false,
  skipHelpers: false,
  forgeTestOptions: {
    matchTest: "testFuzz",
    verbosity: 3,
  },
});

DeploymentManager

Manages Diamond contract deployments.

Methods:

  • deploy(diamondName, networkName, force?) - Deploy a Diamond
  • getDeployment(diamondName, networkName, chainId?) - Get deployment data
  • ensureDeployment(diamondName, networkName, force?) - Deploy if needed
  • hasDeploymentRecord(diamondName, networkName) - Check if deployment exists

HelperGenerator

Generates Solidity test helpers and examples.

Methods:

  • scaffoldProject(outputDir?, generateExamples?) - Create test directory structure
  • generateDeploymentHelpers(diamondName, networkName, outputDir?) - Generate DiamondDeployment.sol
  • generateExampleTests(testTypes?, outputDir?) - Generate example test files

ForgeFuzzingFramework

Orchestrates the complete test workflow.

Methods:

  • runTests(options) - Run complete test workflow
  • deployOnly(diamondName?, networkName?, force?) - Deploy only
  • generateHelpersOnly(diamondName?, networkName?, outputDir?) - Generate helpers only

Configuration

Configure the plugin in hardhat.config.ts:

export default {
  diamondsFoundry: {
    // Directory for generated helper contracts
    helpersDir: "test/foundry/helpers",

    // Whether to generate example tests during init
    generateExamples: true,

    // Types of example tests to generate
    exampleTests: ["unit", "integration", "fuzz"],

    // Default network for deployments
    defaultNetwork: "hardhat",

    // Reuse existing deployments instead of redeploying
    reuseDeployment: false,

    // Default arguments to pass to forge test
    forgeTestArgs: ["-vv", "--gas-report"],
  },
};

Configuration Options:

| Option | Type | Default | Description | | ------------------ | ---------- | --------------------------------- | ------------------------------- | | helpersDir | string | "test/foundry/helpers" | Directory for generated helpers | | generateExamples | boolean | true | Generate example tests on init | | exampleTests | array | ["unit", "integration", "fuzz"] | Types of examples to generate | | defaultNetwork | string | "hardhat" | Default deployment network | | reuseDeployment | boolean | false | Reuse existing deployments | | forgeTestArgs | string[] | [] | Default forge test arguments |

Base Contracts

The plugin provides base Solidity contracts for your tests:

DiamondForgeHelpers

Utility functions for Diamond testing:

import "@diamondslab/diamonds-hardhat-foundry/contracts/DiamondForgeHelpers.sol";

contract MyTest is Test {
    using DiamondForgeHelpers for *;

    function testDiamond() public {
        address diamond = DiamondForgeHelpers.getDiamondAddress();
        DiamondForgeHelpers.logDiamondInfo(diamond);
        DiamondForgeHelpers.assertValidDiamond(diamond);
    }
}

DiamondFuzzBase

Base contract for fuzz tests:

import "@diamondslab/diamonds-hardhat-foundry/contracts/DiamondFuzzBase.sol";

contract MyFuzzTest is DiamondFuzzBase {
    function setUp() public override {
        super.setUp();
        // Your setup
    }

    function testFuzz_Transfer(address to, uint256 amount) public {
        assumeValidAddress(to);
        amount = boundValue(amount, 1, 1000 ether);
        // Test logic
    }
}

Usage Examples

Example 1: Deploy and Test

# Initialize project
npx hardhat diamonds-forge:init

# Deploy Diamond
npx hardhat diamonds-forge:deploy --diamond-name MyDiamond

# Run tests
npx hardhat diamonds-forge:test

Example 2: Custom Workflow

import hre from "hardhat";
import { ForgeFuzzingFramework } from "@diamondslab/diamonds-hardhat-foundry";

async function main() {
  const framework = new ForgeFuzzingFramework(hre);

  // Deploy only
  await framework.deployOnly("MyDiamond", "sepolia");

  // Generate helpers
  await framework.generateHelpersOnly("MyDiamond", "sepolia");

  // Run specific tests
  await framework.runTests({
    skipDeployment: true,
    skipHelpers: true,
    forgeTestOptions: {
      matchContract: "MyIntegrationTest",
      verbosity: 4,
    },
  });
}

main().catch(console.error);

Example 3: CI/CD Integration

# .github/workflows/test.yml
name: Tests
on: [push, pull_request]

jobs:
  foundry-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: foundry-rs/foundry-toolchain@v1
      - uses: actions/setup-node@v3
        with:
          node-version: "18"

      - run: npm ci
      - run: npx hardhat diamonds-forge:deploy
      - run: npx hardhat diamonds-forge:test --gas-report

Integration with Existing Diamonds Projects

This plugin is designed to work seamlessly with existing @diamondslab/diamonds projects:

  1. Use your existing Diamond configuration - The plugin reads from your project's Diamond deployment configuration
  2. LocalDiamondDeployer integration - Works with your project's LocalDiamondDeployer class
  3. Deployment records - Uses the same deployment record format as @diamondslab/diamonds
  4. Hardhat configuration - Integrates with @diamondslab/hardhat-diamonds settings

Example with existing Diamond:

// Your existing hardhat.config.ts
import "@diamondslab/hardhat-diamonds";
import "@diamondslab/diamonds-hardhat-foundry";

export default {
  diamonds: {
    diamondsPath: "./diamonds",
    deploymentsPath: "./diamonds/ExampleDiamond/deployments",
  },
  diamondsFoundry: {
    helpersDir: "test/foundry/helpers",
    defaultNetwork: "hardhat",
  },
};

Troubleshooting

Common Issues

Foundry Not Found

Error: Foundry not installed or forge: command not found

Solution: Install Foundry from getfoundry.sh:

curl -L https://foundry.paradigm.xyz | bash
foundryup

Verify installation:

forge --version

LocalDiamondDeployer Not Found

Error: LocalDiamondDeployer not found or Cannot find module

Solution: Ensure you have the @diamondslab/hardhat-diamonds package installed:

npm install --save-dev @diamondslab/hardhat-diamonds

Import it in your hardhat.config.ts:

import "@diamondslab/hardhat-diamonds";
import "@diamondslab/diamonds-hardhat-foundry";

Deployment Record Not Found

Error: No deployment record found for MyDiamond-hardhat-31337.json

Solution: Deploy your Diamond first:

npx hardhat diamonds-forge:deploy --diamond-name MyDiamond --network localhost

Or use ephemeral deployment (default) which doesn't require saved records:

npx hardhat diamonds-forge:test

Diamond Has No Code

Error: DiamondFuzzBase: Diamond has no code when running tests

Cause: Test is trying to use a Diamond address that doesn't have deployed code.

Solutions:

  1. For tests using hardhat network (ephemeral):

    • Tests should deploy their own Diamond in setUp()
    • See BasicDiamondIntegration.t.sol for self-deploying pattern
  2. For tests using deployed Diamond:

    • Start Hardhat node: npx hardhat node
    • Deploy Diamond: npx hardhat diamonds-forge:deploy --network localhost
    • Run tests: npx hardhat diamonds-forge:test --network localhost
  3. Make tests fork-aware:

function setUp() public {
    diamond = DiamondDeployment.getDiamondAddress();
    
    // Check if Diamond is deployed (forking)
    if (diamond.code.length == 0) {
        // Skip test or deploy in test
        return;
    }
    
    // Proceed with test setup
}

Generated Helpers Not Compiling

Error: Compilation errors in test/foundry/helpers/DiamondDeployment.sol

Solutions:

  1. Ensure Foundry remappings are correct in foundry.toml:
[profile.default]
src = "contracts"
test = "test/foundry"
remappings = [
    "@diamondslab/diamonds-hardhat-foundry/=node_modules/@diamondslab/diamonds-hardhat-foundry/",
]
  1. Verify Diamond deployed successfully before generating helpers:
npx hardhat diamonds-forge:deploy --diamond-name MyDiamond
npx hardhat diamonds-forge:generate-helpers --diamond-name MyDiamond
  1. Check that deployment data is valid:
    • Look in diamonds/MyDiamond/deployments/
    • Verify JSON file contains DiamondAddress field

Peer Dependency Warnings

Warning: ERESOLVE unable to resolve dependency tree or peer dependency conflicts

Solution: Install all required peer dependencies:

npm install --save-dev \
  @diamondslab/diamonds \
  @diamondslab/hardhat-diamonds \
  @nomicfoundation/hardhat-ethers \
  ethers \
  hardhat

For package.json, specify compatible versions:

{
  "devDependencies": {
    "@diamondslab/diamonds": "^3.0.0",
    "@diamondslab/hardhat-diamonds": "^2.0.0",
    "@diamondslab/diamonds-hardhat-foundry": "^2.0.0",
    "@nomicfoundation/hardhat-ethers": "^3.0.0",
    "ethers": "^6.0.0",
    "hardhat": "^2.26.0"
  }
}

Tests Pass Locally But Fail in CI

Cause: CI environment differences (network, timing, dependencies)

Solutions:

  1. Ensure Foundry is installed in CI:
# GitHub Actions example
- name: Install Foundry
  uses: foundry-rs/foundry-toolchain@v1

- name: Run tests
  run: npx hardhat diamonds-forge:test
  1. Use ephemeral deployments for CI (default):
# This works in CI without persistent network
npx hardhat diamonds-forge:test
  1. For persistent network testing in CI:
- name: Start Hardhat node
  run: npx hardhat node &
  
- name: Wait for node
  run: sleep 5
  
- name: Run tests
  run: npx hardhat diamonds-forge:test --network localhost

Import Resolution Errors

Error: File import callback not supported or module resolution failures

Solution: Check your hardhat.config.ts has correct imports:

import "@nomicfoundation/hardhat-ethers";
import "@diamondslab/hardhat-diamonds";
import "@diamondslab/diamonds-hardhat-foundry";

export default {
  solidity: "0.8.28",
  // ... config
};

Ensure TypeScript compilation targets CommonJS:

// tsconfig.json
{
  "compilerOptions": {
    "module": "CommonJS",
    "moduleResolution": "node"
  }
}

Getting Help

If you encounter issues not covered here:

  1. Check the TESTING.md guide for detailed testing workflows
  2. Review MIGRATION.md if upgrading from v1.x
  3. See DESIGN.md for architecture details
  4. Open an issue on GitHub

Foundry Not Found

Error: Foundry not installed

Solution: Install Foundry from getfoundry.sh:

curl -L https://foundry.paradigm.xyz | bash
foundryup

LocalDiamondDeployer Not Found

Error: LocalDiamondDeployer not found

Solution: Ensure you have a LocalDiamondDeployer class in your project at scripts/setup/LocalDiamondDeployer.ts. This is required for Diamond deployments.

Deployment Record Not Found

Error: No deployment record found

Solution: Deploy your Diamond first:

npx hardhat diamonds-forge:deploy --diamond-name YourDiamond

Generated Helpers Not Compiling

Error: Compilation errors in DiamondDeployment.sol

Solution: Ensure:

  1. Foundry is properly installed
  2. Your foundry.toml includes the correct remappings
  3. The Diamond was successfully deployed before generating helpers

Peer Dependency Warnings

Warning: @diamondslab/diamonds not found

Solution: Install required peer dependencies:

npm install --save-dev @diamondslab/diamonds @diamondslab/hardhat-diamonds

Test Suite

This module maintains a comprehensive test suite with 100% pass rate across multiple testing categories:

Test Statistics

  • Total Tests: 144
  • Passing: 141 (98%)
  • Skipped: 3 (intentional - deployment-dependent)
  • Failed: 0
  • Execution Time: ~8-9 seconds

Test Categories

Unit Tests (3 tests)

Basic functionality validation:

  • Diamond deployment verification
  • Deployer address validation
  • Example functionality tests

Integration Tests (14 tests)

Real-world workflow validation:

  • Multi-facet interaction workflows
  • Cross-facet state management
  • Diamond deployment validation
  • Facet introspection and enumeration
  • On-chain selector verification
  • Gas measurement and profiling

Fuzz Tests (93 tests)

Property-based testing with randomized inputs:

  • Access Control (19 tests): Role granting, revocation, enumeration
  • Ownership (7 tests): Transfer, renounce, unauthorized access
  • Routing (11 tests): Selector routing, facet lookup, consistency
  • ABI Loader (11 tests): ABI parsing, selector extraction, signature verification
  • Example Fuzz (5 tests): Address/amount validation, bounded values

Invariant Tests (24 tests)

State invariants and Diamond integrity:

  • Diamond Invariants (13 tests): Facet validity, selector consistency, role hierarchy
  • Proxy Invariants (11 tests): ABI matching, facet existence, storage consistency

Running Tests

Run the complete test suite:

npx hardhat diamonds-forge:test --network localhost

Run specific test categories:

# Unit tests only
forge test --match-path "test/foundry/unit/**/*.t.sol"

# Fuzz tests only
forge test --match-path "test/foundry/fuzz/**/*.t.sol"

# Invariant tests only
forge test --match-path "test/foundry/invariant/**/*.t.sol"

# Integration tests only
forge test --match-path "test/foundry/integration/**/*.t.sol"

Test Patterns

All tests follow best practices:

  • Role Setup: Access control tests grant necessary roles in setUp()
  • Invariant Targeting: Invariant tests use targetContract() for fuzzing
  • Selector Filtering: Tests skip undeployed selectors (facetAddress returns address(0))
  • Gas Profiling: Gas measurements included in relevant tests
  • Comprehensive Coverage: Edge cases, error conditions, and happy paths

See TESTING.md for detailed testing guide and patterns.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT © DiamondsLab

Links