
Introduction
When I began developing with Solidity, I soon left online IDEs and adopted Hardhat as my new main framework. Months into the construction of projects and the creation of a workflow surrounding it I felt comfortable with its network layout, testing strategy and project organization.
Yet, when someone said that Foundry performance benefits were impressive, I became interested enough to make a proper comparison between both popular frameworks of smart contracts development.
To come up with a just assessment, I created the same projects with the help of both of them and applied the same MiniBank contract and further test settings. This practical experience demonstrated that performance, experience of developers, and workflow differ drastically between these two tools and that is worth examining by anybody who decides between the two.
Project Architecture and Setups
The initial experience in setting them up is quite different in the two frameworks. Foundry creates a foundry.toml configuration file based on key-value pairs, which is analogous to Hardhat hardhat.config.js file. Both also support customizing source folders, compilation output directories, etc.
The default folder structure is quite different in Foundry, though, multiple networks cannot be directly configured in the configuration file, as Hardhat does a fantastic job in maintaining multiple networks. The rest of the parameters in foundry.toml are connected to testing options including verbosity or account settings and gas price options.
The remappings feature of Foundry is particularly interesting because it offers a strong approach to the importation of dependencies. You can make imports of contracts much easier by making shortcuts in the configuration.
As an example, as an alternative to writing long import paths, you can write short hand-mappings that help your code be more readable and maintainable.
Dependency Management Approaches
The frameworks differ radically in the way they manage dependencies:
- Hardhat uses the established npm ecosystem, which is instantly open to JavaScript developers
- Installation of OpenZeppelin contracts is just an npm install command
- Foundry relies on dependency management by Git submodule via the forge CLI tool
- Dependencies are stored in a lib/ folder and trailed in a .gitmodules file as opposed to package.json
This method will take any GitHub repository that includes smart contracts as a dependency, and lets you be more flexible in the choice of libraries.
You can specify the GitHub organization and repository name, and optionally specify the branch or tag to install. Once the forge is installed, the forge remapping command can be used to view the default import paths that Foundry will use which can then be further customized with configuration files.
Development and Debugging Tools
One of the areas where Hardhat excels is in its debugging experience. The framework also offers the use of console.log by default, which resembles the expected debugging style in JavaScript development. This functionality is heavily marketed as a selling point and can be easily tracked by developers to understand the way the execution has been conducted and what is wrong.
Foundry needs an alternative method of logging in contracts. Although it is possible to install and import Hardhat manually and install a console contract, the best way to do this would be to copy a specialized console contract into your project.
In the case of test files particularly, the DSTest contract that is included includes logging events such as logstring, logint, and logaddress which can be emitted without any extra dependencies.
Testing Methodologies
The test experience is perhaps the most notable point of difference between the two frameworks.
Hardhat Testing Approach
Hardhat is based on standard JavaScript testing patterns:
- Uses describe and it blocks alongside Mocha as a default assertion library
- Test files may resemble this more natural style of developers who are familiar with web development
- Offers descriptive test names that make a clear statement of purpose
- Familiar asynchronous patterns
- Learning curve is minimal to those who are already familiar with modern web development
Foundry Testing Approach
Foundry functions in an entirely different manner:
- Tests are developed as Solidity smart contracts, which inherit DSTest
- All tests are function prefixed with the word test or testFail
- All assertions are made via the inherited DSTest contract
- Test files are real smart contracts that are used in testing
Master Smart Contract Testing
Compare frameworks and choose the best testing approach for your project.
The Foundry method has a number of implications:
- Test contracts have to be instantiated in a setUp() procedure analogous to beforeEach in JavaScript customers
- Contract method dispatching of ETH involves certain syntax with value parameters enclosed by curly braces
- The initial steep learning curve may be offset by the fact that it does not require handling asynchronous operations
Learning Foundry Testing Features
Foundry introduces the concept known as the so-called cheat codes which offer powerful testing utilities. These are functions that are special at a designated contract address that enables you to access state in the blockchain during testing.
You may:
- Alter the current block number
- Impersonate other accounts
- Claim certain contract behavior
To use cheat codes you must define an interface and instantiate it as a state variable to the special address of the cheat. After setup, it is possible to cheat with functions such as:
- vm.prank() to set the next contract call to a different receiver
- vm.roll() to advance the blockchain to a certain point
One of the most interesting cheat codes is vm.expectRevert(), which has to be called prior to the transaction that you want to fail. This inversion of standard patterns of assertion can be counter-intuitive but it gives clear control of the likely failure conditions.
Results of Performance Comparison
The performance variation between the frameworks is significant and is instantly evident. On the same contracts and test scenarios, I obtained revealing results on compilation and test execution time:
Performance Comparison Results
| Scenario | Foundry | Hardhat |
|---|---|---|
| Clean projects (no cache) | 1.44 seconds | 5.17 seconds |
| With caching enabled | 0.45 seconds | 3.98 seconds |
| 26 smart contracts project | 8.53 seconds | 14.56 seconds |
These performance differences are more pronounced when the project complexity increases.
Foundry Testing: Benefits and Disadvantages
Foundry Testing Pros and Cons
| Pros | Cons |
|---|---|
| No async/await complexity | Tests names are not as descriptively named as in JavaScript tests |
| Tests run very fast | expectRevert assertion is counterintuitive |
| Auto-generated gas report | Learning curve for cheat codes |
| Everything written in Solidity | Deployment tooling still developing |
The benefits of performance can scarcely be disputed, and the Solidity test names cannot be as descriptive as JavaScript ones, which may cause obscured test intent. The cheat code system, though powerful, will need a startup cost of learning.
Contract Deployment Strategies
Deployment is one place where Hardhat has a better developer experience at the time. The framework accepts:
- JavaScript-based deployment scripts
- Connection with a variety of networks
- Load configuration via environment variables
- Deal with complicated deployment situations gracefully
JavaScript-based deployment scripts are presently required in Foundry, but can be cumbersome when constructors have to have their arguments provided. The suggested solution is to come up with deployment bash scripts to handle the complexity of deployments, but the development team is in the process of developing more advanced deployment solutions.
This is one of the most serious contemporary weak points of Foundry in relation to Hardhat having a developed deployment ecosystem.
Command-Line Interface Tools
Foundry has the cast CLI tool of interacting with the blockchain and querying smart contracts. This is a strong utility that enables you to:
- Call contract functions
- Query blockchain state
- Perform a range of blockchain actions at the command line
Though cast provides full functionality in interacting with the blockchain, it necessitates complex command-line construction to perform complex operations. Similar to deployment, this may require bash scripting to avoid typing long commands repeatedly.
Framework Comparison Summary
Framework Feature Comparison
| Feature | Foundry | Hardhat |
|---|---|---|
| Installation | Via CLI curl command | Not required with npx, or via npm |
| CLI tools | forge to manage project, cast to interact with contracts | hardhat manage project (build/compile/run scripts) |
| Build and test performance | Exceptional speed | Moderate performance |
| Dependencies | Git submodules | npm packages |
| Configuration | foundry.toml | hardhat.config.js |
| Test isolation | Yes via -match-test -match-contract | Yes via only or skip in test files |
| Contract deployments | Via Cast CLI tool | JavaScript-based scripts |
Making the Right Choice
Foundry demonstrates massive potential with its outstanding performance, engaged community, and creative method of testing smart contracts. The structure is excellent in fast development cycle and has great testing utilities that could accelerate development greatly after understanding how to use them.
Nonetheless, Foundry is still developing especially in the deployment tooling and developer experience enhancements. Foundry has some interesting strengths to those developers who prefer a hybrid model with:
- Foundry for contract development and testing
- Hardhat for deployment and scripts
Users who are more accustomed to standard JavaScript tooling and established deployment practices might be more prolific with Hardhat now, although the decision is again dependent on your background and the ability to take on newer tooling.
Both architectures are still changing rapidly, and the ecosystem of smart contract development has a variety of viable alternatives that address the needs of various developers and application cases.


