Hardhat plugin that integrates Foundry testing with Diamond proxy contracts, providing deployment helpers and fuzzing support
npm install @diamondslab/diamonds-hardhat-foundry



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.
- ๐ 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
``bash`
npm install --save-dev @diamondslab/diamonds-hardhat-foundryor
yarn add -D @diamondslab/diamonds-hardhat-foundryor
pnpm add -D @diamondslab/diamonds-hardhat-foundry
- Foundry: Install from getfoundry.sh
- Hardhat: ^2.26.0 or later@diamondslab/diamonds
- Required Peer Dependencies:
- - Core Diamond deployment library@diamondslab/hardhat-diamonds
- - Hardhat Diamond configuration and LocalDiamondDeployer@nomicfoundation/hardhat-ethers
- - Ethers.js integrationethers
- - Ethereum library
`bash`
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.
Import the plugin in your hardhat.config.ts:
`typescript
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;
`
`bash`
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
`bash`
npx hardhat diamonds-forge:deploy --diamond-name YourDiamond --network hardhat
`bash`
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
Version 2.0.0+ provides importable Solidity helper contracts for your tests:
Extend DiamondFuzzBase to create Diamond fuzz tests with built-in helpers:
`solidity
// 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
Use DiamondForgeHelpers for validation, assertions, and DiamondLoupe queries:
`solidity
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 deploymentassertValidFacet(address, name)
- - Validate facet deploymentisValidTestAddress(address)
- - Filter fuzz addressesisValidTestAmount(uint256)
- - Filter fuzz amountsassertSelectorExists(diamond, selector)
- - Verify selector registeredassertSelectorRouting(diamond, selector, facet)
- - Verify routinggetFacetAddress(diamond, selector)
- - Get facet for selectorgetFacetAddresses(diamond)
- - Get all facet addressesgetFacetSelectors(diamond, facet)
- - Get selectors for facetgetDiamondOwner(diamond)
- - Get owner addressboundAddress(seed)
- - Generate valid fuzz addressboundAmount(seed, min, max)
- - Generate valid fuzz amountselectorsEqual(a, b)
- - Compare selector arrays
Load and parse Diamond ABI files in your tests:
`solidity
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 fileextractSelectors(abiJson)
- - Extract all function selectorsextractSignatures(abiJson)
- - Extract all function signaturesgetFunctionInfo(abiJson, index)
- - Get selector and signatureverifySelectorsMatch(abiJson, expectedSelectors)
- - Validate selectors
`bash`
npx hardhat diamonds-forge:test
Initialize the Foundry test directory structure.
Options:
- --helpers-dir - Custom directory for helpers (default: test/foundry/helpers)--examples
- - Generate example tests (default: from config)--force
- - Overwrite existing files
Example:
`bash`
npx hardhat diamonds-forge:init --helpers-dir test/forge/helpers --examples
Deploy a Diamond contract to the specified network.
Options:
- --diamond-name - Name of the Diamond to deploy (default: from config)--network
- - Network to deploy to (default: hardhat)--reuse
- - Reuse existing deployment if found (default: from config)--force
- - Force new deployment even if one exists
Example:
`bash`
npx hardhat diamonds-forge:deploy --diamond-name MyDiamond --network sepolia
Generate Solidity helper contract with deployment data.
Options:
- --diamond-name - Name of the Diamond (default: from config)--output-dir
- - Output directory for helpers (default: from config)--network
- - Network to use for deployment data (default: hardhat)
Example:
`bash`
npx hardhat diamonds-forge:generate-helpers --diamond-name MyDiamond --output-dir test/foundry/helpers
Run Foundry tests with optional deployment and helper generation.
Options:
- --diamond-name - Diamond to deploy/test (default: from config)--network
- - Network for deployment (default: from config)--skip-deployment
- - Skip deployment step--skip-helpers
- - Skip helper generation step--match-test
- - Run tests matching pattern--match-contract
- - Run tests in matching contracts--verbosity
- - Forge verbosity level (1-5)--gas-report
- - Show gas usage report
Example:
`bashRun all tests with gas reporting
npx hardhat diamonds-forge:test --gas-report
$3
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 - Diamond to analyze (default: from config)
- --network - Network for deployment (default: from config)
- --report - Report format: summary, lcov, debug, bytecode (default: summary)
- --report-file - Output file for coverage report
- --lcov-version - LCOV version for LCOV reports (default: v1)
- --match-test - Run tests matching pattern
- --match-contract - Run tests in matching contracts
- --match-path - Run tests in files matching glob
- --no-match-test - Skip tests matching pattern
- --no-match-contract - Skip tests in matching contracts
- --no-match-path - Skip tests in files matching glob
- --no-match-coverage - Exclude contracts from coverage
- --verbosity - Verbosity level: 0-5 (default: 0)
- --color - Color output: auto, always, never (default: auto)
- And many more options for filtering, optimization, and EVM configurationWorkflow (Required):
`bash
Step 1: Start Hardhat node (persistent network)
npx hardhat nodeStep 2: Deploy Diamond to localhost network (in another terminal)
npx hardhat diamonds-forge:deploy --diamond-name MyDiamond --network localhostStep 3: Run coverage against deployed Diamond
npx hardhat diamonds-forge:coverage --diamond-name MyDiamond --network localhost
`Examples:
`bash
Basic coverage with default summary
npx hardhat diamonds-forge:coverage --diamond-name MyDiamond --network localhostGenerate LCOV report for CI/CD
npx hardhat diamonds-forge:coverage \
--diamond-name MyDiamond \
--network localhost \
--report lcov \
--report-file coverage/lcov.infoCoverage for specific test patterns
npx hardhat diamonds-forge:coverage \
--diamond-name MyDiamond \
--network localhost \
--match-contract "Unit" \
--verbosity 2Multiple 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 networkSee 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.
$3
When you deploy a Diamond and generate helpers, the plugin creates
test/foundry/helpers/DiamondDeployment.sol with:`solidity
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
$3
`solidity
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:
$3
For quick testing without persisting deployment records:
`bash
Deploy Diamond for this test run only
npx hardhat diamonds-forge:test --diamond-name MyDiamondDeployment 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
$3
Save deployment records for reuse across test runs:
`bash
Deploy and save deployment record
npx hardhat diamonds-forge:deploy --diamond-name MyDiamond --network localhostRun tests using saved deployment
npx hardhat diamonds-forge:test --network localhost --use-deploymentDeployment 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
$3
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 existsHelper Control:
-
--skip-helpers - Don't generate DiamondDeployment.sol
- --helpers-dir - Custom output directory for helpersTest Filtering:
-
--match-test - Run only tests matching name pattern
- --match-contract - Run only tests in specified contract
- --match-path - Run only tests in files matching pathOutput Control:
-
--verbosity <1-5> - Set Forge output verbosity (default: 2)
- --gas-report - Display detailed gas usage report
- --coverage - Generate test coverage reportNetwork Control:
-
--network - Network to use (hardhat, localhost, sepolia, etc.)
- --fork-url - Custom RPC URL for forking$3
`bash
Quick ephemeral test (default)
npx hardhat diamonds-forge:testSave deployment for reuse
npx hardhat diamonds-forge:test --save-deployment --network localhostReuse saved deployment
npx hardhat diamonds-forge:test --use-deployment --network localhostTest specific functionality with gas report
npx hardhat diamonds-forge:test --match-test "testOwnership" --gas-reportRun only fuzz tests with high verbosity
npx hardhat diamonds-forge:test --match-contract "Fuzz" --verbosity 4Force 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.$3
`solidity
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();
}
}
`$3
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)
$3
See comprehensive examples in
test/foundry/integration/SnapshotExample.t.sol:`bash
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:
`typescript
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,
},
});
`$3
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$3
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$3
Orchestrates the complete test workflow.
Methods:
-
runTests(options) - Run complete test workflow
- deployOnly(diamondName?, networkName?, force?) - Deploy only
- generateHelpersOnly(diamondName?, networkName?, outputDir?) - Generate helpers onlyConfiguration
Configure the plugin in
hardhat.config.ts:`typescript
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:
$3
Utility functions for Diamond testing:
`solidity
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);
}
}
`$3
Base contract for fuzz tests:
`solidity
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
$3
`bash
Initialize project
npx hardhat diamonds-forge:initDeploy Diamond
npx hardhat diamonds-forge:deploy --diamond-name MyDiamondRun tests
npx hardhat diamonds-forge:test
`$3
`typescript
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);
`$3
`yaml
.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 settingsExample with existing Diamond:
`typescript
// 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
$3
#### Foundry Not Found
Error:
Foundry not installed or forge: command not foundSolution: Install Foundry from getfoundry.sh:
`bash
curl -L https://foundry.paradigm.xyz | bash
foundryup
`Verify installation:
`bash
forge --version
`#### LocalDiamondDeployer Not Found
Error:
LocalDiamondDeployer not found or Cannot find moduleSolution: Ensure you have the
@diamondslab/hardhat-diamonds package installed:`bash
npm install --save-dev @diamondslab/hardhat-diamonds
`Import it in your
hardhat.config.ts:`typescript
import "@diamondslab/hardhat-diamonds";
import "@diamondslab/diamonds-hardhat-foundry";
`#### Deployment Record Not Found
Error:
No deployment record found for MyDiamond-hardhat-31337.jsonSolution: Deploy your Diamond first:
`bash
npx hardhat diamonds-forge:deploy --diamond-name MyDiamond --network localhost
`Or use ephemeral deployment (default) which doesn't require saved records:
`bash
npx hardhat diamonds-forge:test
`#### Diamond Has No Code
Error:
DiamondFuzzBase: Diamond has no code when running testsCause: 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 pattern2. 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 localhost3. Make tests fork-aware:
`solidity
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.solSolutions:
1. Ensure Foundry remappings are correct in
foundry.toml:
`toml
[profile.default]
src = "contracts"
test = "test/foundry"
remappings = [
"@diamondslab/diamonds-hardhat-foundry/=node_modules/@diamondslab/diamonds-hardhat-foundry/",
]
`2. Verify Diamond deployed successfully before generating helpers:
`bash
npx hardhat diamonds-forge:deploy --diamond-name MyDiamond
npx hardhat diamonds-forge:generate-helpers --diamond-name MyDiamond
`3. 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 conflictsSolution: Install all required peer dependencies:
`bash
npm install --save-dev \
@diamondslab/diamonds \
@diamondslab/hardhat-diamonds \
@nomicfoundation/hardhat-ethers \
ethers \
hardhat
`For package.json, specify compatible versions:
`json
{
"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:
`yaml
GitHub Actions example
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1- name: Run tests
run: npx hardhat diamonds-forge:test
`2. Use ephemeral deployments for CI (default):
`bash
This works in CI without persistent network
npx hardhat diamonds-forge:test
`3. For persistent network testing in CI:
`yaml
- 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 failuresSolution: Check your
hardhat.config.ts has correct imports:`typescript
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:
`json
// tsconfig.json
{
"compilerOptions": {
"module": "CommonJS",
"moduleResolution": "node"
}
}
`$3
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 installedSolution: Install Foundry from getfoundry.sh:
`bash
curl -L https://foundry.paradigm.xyz | bash
foundryup
`$3
Error:
LocalDiamondDeployer not foundSolution: Ensure you have a
LocalDiamondDeployer class in your project at scripts/setup/LocalDiamondDeployer.ts. This is required for Diamond deployments.$3
Error:
No deployment record foundSolution: Deploy your Diamond first:
`bash
npx hardhat diamonds-forge:deploy --diamond-name YourDiamond
`$3
Error: Compilation errors in
DiamondDeployment.solSolution: Ensure:
1. Foundry is properly installed
2. Your
foundry.toml includes the correct remappings
3. The Diamond was successfully deployed before generating helpers$3
Warning:
@diamondslab/diamonds not foundSolution: Install required peer dependencies:
`bash
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:
$3
- Total Tests: 144
- Passing: 141 (98%)
- Skipped: 3 (intentional - deployment-dependent)
- Failed: 0
- Execution Time: ~8-9 seconds
$3
#### 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
$3
Run the complete test suite:
`bash
npx hardhat diamonds-forge:test --network localhost
`Run specific test categories:
`bash
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"
`$3
All tests follow best practices:
- Role Setup: Access control tests grant necessary roles in
setUp()
- Invariant Targeting: Invariant tests use targetContract()` for fuzzingSee TESTING.md for detailed testing guide and patterns.
Contributions are welcome! Please feel free to submit a Pull Request.
MIT ยฉ DiamondsLab
- GitHub Repository
- npm Package
- @diamondslab/diamonds
- @diamondslab/hardhat-diamonds
- ERC-2535 Diamond Standard
- Foundry Book