- Foundry is a complete toolkit that simplifies Ethereum smart contract development with modular components like Forge (build/test), Anvil (local node), Cast (CLI interactions), and Chisel (Solidity REPL).
- You can compile and deploy contracts using Solidity-based scripts, specify private keys via CLI flags, and switch between local or custom RPC endpoints.
- Cast offers flexible commands for sending transactions, reading contract data, or converting values, making it a powerful tool for rapid iteration and debugging.
- Built-in testing (Forge) supports local, forked, and coverage tests, making it straightforward to manage dependencies, run end-to-end scenarios, and ensure code reliability.
Main article
If you’re diving into smart contract development, you’ll find Foundry to be an incredibly handy companion. It’s more than just a tool; it’s a full-fledged framework that makes smart contracts’ development, testing, and deployment smoother and more efficient. Whether you’re new to blockchain or an experienced developer, Foundry is designed to be a reliable and comprehensive toolkit that fits right into your workflow. Foundry is comprised of several key components, each serving a distinct purpose in the smart contract development lifecycle:- Forge: Forge is the heart of the Foundry framework, acting as a powerful compilation and testing tool. It’s instrumental in compiling smart contracts, running a suite of tests (including fuzz and property-based tests), and ensuring the contracts are robust and secure before deployment.
- Anvil: Anvil is a local node tailored for development. This component is invaluable for developers who need a quick and easy way to test their contracts in a local blockchain environment. Anvil enables rapid iteration and debugging without connecting to the main networks or testnets.
- Cast: Cast is a versatile tool within Foundry designed to interact with Ethereum. It facilitates a range of actions, from sending transactions and querying blockchain data to manipulating local Ethereum states. This utility makes interacting with deployed contracts easier and performs various blockchain-related tasks.
- Chisel: Chisel enriches the Foundry suite as a Solidity REPL (Read-Eval-Print Loop), enabling developers to interactively test and experiment with Solidity code snippets. It’s ideal for immediate feedback and debugging in real-time, and it’s compatible both within and outside Foundry projects.
Initializing a Project
Creating a New Project
- In an empty directory, initialize a new Foundry project:
- To create a new directory with the project:
- Note: The
srcdirectory is where the smart contracts are placed.
Compiling Contracts
-
To compile the contracts, run either:
or
-
The
outdirectory will generate a JSON file containing compilation data, such as ABI.
Setting Up a Local Blockchain
- Use Anvil to start a local blockchain for testing:
- The local blockchain will run at
127.0.0.1:8545, and you can add it to MetaMask for ease of testing.
Deploying Contracts
Deploying Locally or to a Custom RPC
- To deploy smart contracts, use
forge create. Forge defaults to the Anvil local blockchain, but other RPCs can be specified using the--rpc-urlflag.
- Deploying locally with Anvil running:
- Deploying to a custom endpoint:
- This will likely not work because it needs a private key to deploy.
Options for Specifying Private Key
- Run the create command with the
--interactiveflag for a prompt to add a private key: - Or, directly include the private key in the command:
Writing Deploy Scripts
- Scripts in Foundry are written in Solidity. We’ll use a Solidity script to deploy a contract. By convention, script files end with
.s.sol. - Example: Deploying
SimpleStorage.sol.
Creating the Deploy Script
-
Create a file named
deploySimpleStorage.s.solwith the following content: -
Deploy the contract using the script:
-
Pragma Directive:
pragma solidity ^0.8.19;: Specifies that the script is compatible with Solidity version 0.8.19 or any newer version of the 0.8 series but not version 0.9 or above.
-
Imports:
import {Script} from "forge-std/Script.sol";: Imports theScriptclass from theforge-stdlibrary, which is a part of Foundry, a development environment for Ethereum smart contracts.import {SimpleStorage} from "../src/SimpleStorage.sol";: Imports theSimpleStoragecontract, presumably a custom contract located in thesrcdirectory.
-
Contract Declaration:
contract DeploySimpleStorage is Script: Defines a new contract namedDeploySimpleStoragethat inherits from theScriptclass. This setup is typical for deployment scripts in Foundry.
-
Function Definition:
function run() external returns (SimpleStorage): Therunfunction is the main entry point for the deployment script. It’s markedexternalas it’s intended to be called externally, and it returns an instance ofSimpleStorage.
-
Deployment Process:
vm.startBroadcast();: Initiates a transaction broadcast. Thevmobject is a special component in Foundry, providing various functionalities related to the Ethereum Virtual Machine (EVM).SimpleStorage simpleStorage = new SimpleStorage();: Instantiates theSimpleStoragecontract.vm.stopBroadcast();: Ends the transaction broadcast.
-
Return Statement:
return simpleStorage;: Returns the deployed instance ofSimpleStorage.
SimpleStorage contract, which is not detailed here, would contain the actual business logic or data storage mechanisms.
Interacting with Contracts Using Cast
Sending Transactions
- To send transactions, use
cast send: - Example:
Reading from Contracts
- Use
cast callfor reading view functions: - Example:
Managing Dependencies
Installing Smart Contract Dependencies
- Use the following command to install dependencies from a repository:
- Dependencies are added to the
libdirectory.
Remapping Dependencies
- Add remappings in the
foundry.tomlfile for syntax convenience:
Writing and Running Tests
Example Test Contract
- Tests in Foundry are also written in Solidity. Here’s an example:
- Run tests with:
- The
-vvflag outputs detailed logs for better insight.
-
License and Solidity Version Declaration:
// SPDX-License-Identifier: MIT: This is a comment specifying the license under which this file is released, in this case, the MIT License.pragma solidity ^0.8.18;: This line specifies the compiler version. The file is compatible with Solidity version 0.8.18 and above within the 0.8.x range.
-
Imports:
-
import {Test, console} from "forge-std/Test.sol";: This line imports two elements from the Forge standard library (forge-std):Test: A base contract that provides testing functionalities.console: A utility to log output to the console. This is particularly useful for debugging and tracking variable values during test execution.
-
-
Test Contract Declaration:
contract FundMeTest is Test {: This line declares a new contractFundMeTestwhich inherits from theTestcontract. In the context of Forge, this meansFundMeTestis a test suite.
-
State Variable:
uint256 number = 33;: A state variablenumberof typeuint256(unsigned integer of 256 bits) is declared and initialized to 33. This variable is used to demonstrate state manipulation and assertion in the test.
-
Setup Function:
function setUp() external { number = 3333; }: ThesetUp()function is a special function in the Forge framework that runs before each test function. It’s used for initializing or resetting the state. Here, it sets thenumbervariable to 3333.
-
Test Function:
-
function testDemo() public { ... }: This is the actual test function. In Forge, any function with a name starting withtestis considered a test case.console.log("The saved number is", number);: This line logs the value ofnumberto the console, which is useful for debugging or verifying the test state.assertEq(number, 3333);: This is an assertion statement provided by theTestcontract. It checks whether the value ofnumberis equal to 3333. If the assertion fails (i.e., ifnumberis not 3333), the test will fail.
-
Testing on a Fork
- Run tests on a forked network by adding an RPC URL:
Coverage Analysis
- Use
forge coverageto analyze how much of your contracts are tested:It will display a nice table: