Foundry Setup Skill
This skill provides templates, scripts, and best practices for setting up Foundry-based Solidity projects.
When to Use
Use this skill when:
- •Initializing a new Foundry project
- •Adding Foundry to an existing Solidity codebase
- •Configuring Foundry settings (optimization, tests, etc.)
- •Setting up Foundry in a hybrid Hardhat/Foundry project
- •Updating Foundry configuration
Prerequisites: Foundry must be installed (foundryup)
Integration with Framework Detection
Before using this skill, reference the framework-detection skill to:
- •Check if Foundry is already configured
- •Determine if this is a hybrid setup
- •Avoid overwriting existing configuration
Quick Setup
Basic Initialization
# Initialize new Foundry project forge init my-project cd my-project # Or initialize in existing directory forge init --force
Project Structure
Foundry creates this structure:
project/
├── foundry.toml # Configuration
├── .env.example # Environment variables template
├── lib/ # Dependencies (git submodules)
├── src/ # Contract source files
│ ├── interfaces # Interfaces
│ │ └──ICounter # Example interface
│ └── Counter.sol # Example contract
├── test/ # Test files
│ └── Counter.t.sol # Example test
└── script/ # Deployment scripts
└── Counter.s.sol # Example script
Configuration Templates
foundry.toml
See ./templates/foundry.toml for the complete configuration template.
Key Configuration Sections:
[profile.default] src = "src" out = "out" libs = ["lib"] solc_version = "0.8.30" optimizer = true optimizer_runs = 200 via_ir = false # Testing verbosity = 2 fuzz_runs = 256 # Gas reporting gas_reports = ["*"] # Formatting line_length = 120 tab_width = 4 bracket_spacing = false
Environment Variables
See ./templates/.env.example for complete environment variable template.
Essential Variables:
# RPC URLs MAINNET_RPC_URL= SEPOLIA_RPC_URL= ARBITRUM_RPC_URL= # Private Keys (NEVER commit actual keys) PRIVATE_KEY= # Etherscan API Keys ETHERSCAN_API_KEY= ARBISCAN_API_KEY= # Gas Price Settings GAS_PRICE=
Common Configurations
1. High Optimization for Production
[profile.production] optimizer = true optimizer_runs = 10000 via_ir = true
2. Detailed Testing
[profile.test] verbosity = 3 fuzz_runs = 1000 invariant_runs = 256
3. Gas Optimization Focus
[profile.gas-optimized] optimizer = true optimizer_runs = 1000000 via_ir = true gas_reports = ["*"]
4. Mainnet Forking for Tests
[profile.default]
fork_url = "${MAINNET_RPC_URL}"
fork_block_number = 18000000
Dependencies Management
Adding Libraries
# Add OpenZeppelin contracts forge install OpenZeppelin/openzeppelin-contracts # Add Solmate forge install transmissions11/solmate # Add Forge Standard Library (included by default) forge install foundry-rs/forge-std
Remappings
Foundry auto-generates remappings.txt, but you can customize:
@openzeppelin/=lib/openzeppelin-contracts/ @solmate/=lib/solmate/src/ forge-std/=lib/forge-std/src/
Or configure in foundry.toml:
remappings = [
"@openzeppelin/=lib/openzeppelin-contracts/",
"@solmate/=lib/solmate/src/"
]
Initialization Script
See ./scripts/init-foundry.sh for automated setup.
Usage:
# Basic initialization ./scripts/init-foundry.sh # With project name ./scripts/init-foundry.sh my-project # In existing directory ./scripts/init-foundry.sh --force
What the script does:
- •Checks if Foundry is installed
- •Initializes Foundry project
- •Copies configuration templates
- •Sets up .gitignore
- •Installs essential dependencies
- •Creates initial directory structure
Hybrid Setup (Foundry + Hardhat)
When adding Foundry to an existing Hardhat project:
1. Initialize Foundry Without Overwriting
# Initialize but don't overwrite existing files forge init --no-commit
2. Configure Separate Directories
# foundry.toml [profile.default] src = "contracts" # Use Hardhat's contracts dir test = "test/foundry" # Separate Foundry tests out = "out" libs = ["node_modules", "lib"] # Include both package managers
3. Update .gitignore
# Foundry out/ cache/ lib/ # Hardhat artifacts/ cache/ node_modules/
4. Install Shared Dependencies
# Install via Foundry
forge install OpenZeppelin/openzeppelin-contracts
# Reference in Hardhat
# Add to hardhat.config.js:
# paths: { sources: "./contracts" }
Testing Setup
Basic Test Structure
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import "forge-std/Test.sol";
import "../src/MyContract.sol";
contract MyContractTest is Test {
MyContract public myContract;
function setUp() public {
myContract = new MyContract();
}
function testBasic() public {
// Test implementation
}
function testFuzz_Amount(uint256 amount) public {
// Fuzz test
}
}
Running Tests
# Run all tests forge test # Run specific test forge test --match-test testBasic # Run with verbosity forge test -vvvv # Run with gas reporting forge test --gas-report # Run with coverage forge coverage
Security Best Practices for Private Keys
⚠️ CRITICAL: Never store production private keys in .env files!
Recommended Approaches (in order of preference)
1. Hardware Wallets (Most Secure - Production)
# Deploy using Ledger forge script script/Deploy.s.sol:DeployScript \ --rpc-url $RPC_URL \ --ledger \ --broadcast # Deploy using Trezor forge script script/Deploy.s.sol:DeployScript \ --rpc-url $RPC_URL \ --trezor \ --broadcast
2. Cast Wallet (Recommended - Development & Production)
Create a named keystore:
# Create a new wallet (prompts for password) cast wallet new ~/.foundry/keystores/deployer # Import existing private key into keystore cast wallet import deployer --interactive
Use in deployment:
# Deploy using named account forge script script/Deploy.s.sol:DeployScript \ --rpc-url $RPC_URL \ --account deployer \ --sender 0xYourAddress \ --broadcast
Update your script to use the account:
contract DeployScript is Script {
function run() external {
// No private key needed - uses --account flag
vm.startBroadcast();
MyContract myContract = new MyContract();
vm.stopBroadcast();
console.log("MyContract deployed to:", address(myContract));
}
}
3. Interactive Private Key (Development Only)
# Prompts for private key (not stored anywhere) forge script script/Deploy.s.sol:DeployScript \ --rpc-url $RPC_URL \ --private-key-interactive \ --broadcast
4. .env Variables (Development/Testing ONLY)
⚠️ Use ONLY for local development or testnet testing with non-production keys!
contract DeployScript is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
MyContract myContract = new MyContract();
vm.stopBroadcast();
console.log("MyContract deployed to:", address(myContract));
}
}
If using .env:
- •✅ Only use accounts created specifically for development/testing
- •✅ Never reuse production private keys
- •✅ Keep test funds minimal
- •✅ Add .env to .gitignore
- •❌ Never commit .env to version control
- •❌ Never use for mainnet deployments
Deployment Setup
Deploy Commands
# Dry run (simulation) forge script script/Deploy.s.sol:DeployScript --rpc-url $RPC_URL # Actual deployment with hardware wallet (RECOMMENDED for production) forge script script/Deploy.s.sol:DeployScript \ --rpc-url $RPC_URL \ --ledger \ --broadcast \ --verify # Actual deployment with cast wallet (RECOMMENDED for all deployments) forge script script/Deploy.s.sol:DeployScript \ --rpc-url $RPC_URL \ --account deployer \ --sender 0xYourAddress \ --broadcast \ --verify # Development only: with .env private key forge script script/Deploy.s.sol:DeployScript \ --rpc-url $RPC_URL \ --broadcast \ --verify
Best Practices
- •Secure private key management - Use hardware wallets or
cast walletfor all deployments; never store production keys in .env - •Use profiles - Create different profiles for dev, test, production
- •High optimizer runs for production - Use 10,000+ optimizer runs for deployed contracts
- •Comprehensive .env.example - Document all required environment variables (but discourage private keys)
- •Git submodules for deps - Let Foundry manage dependencies via git
- •Separate test directories - Use
test/foundry/for Foundry tests in hybrid setups - •Enable via-ir for optimization - Use
via_ir = truefor complex contracts - •Version pin Solidity - Specify exact
solc_versionin foundry.toml
Troubleshooting
Issue: "forge: command not found"
# Install/update Foundry curl -L https://foundry.paradigm.xyz | bash foundryup
Issue: Dependency conflicts in hybrid setup
# Prioritize Foundry libs over node_modules libs = ["lib", "node_modules"]
Issue: Compilation errors with remappings
# Regenerate remappings forge remappings > remappings.txt
Issue: Tests not found
# Check test file naming (must end in .t.sol) mv test/MyTest.sol test/MyTest.t.sol
Quick Reference
| Task | Command | Notes |
|---|---|---|
| Init project | forge init | Creates new project |
| Add dependency | forge install <repo> | Uses git submodules |
| Build | forge build | Compiles contracts |
| Test | forge test | Runs tests |
| Coverage | forge coverage | Test coverage |
| Gas report | forge test --gas-report | Gas usage |
| Format | forge fmt | Code formatting |
| Deploy | forge script | Run deployment |
| Verify | forge verify-contract | Verify on Etherscan |
Template Files
This skill provides the following templates:
- •
./templates/foundry.toml- Complete Foundry configuration - •
./templates/.env.example- Environment variables template
Scripts
This skill provides the following scripts:
- •
./scripts/init-foundry.sh- Automated project initialization
Next Steps After Setup:
- •Configure
foundry.tomlfor your specific needs - •Copy
.env.exampleto.envand fill in values - •Install required dependencies with
forge install - •Write contracts in
src/ - •Write tests in
test/ - •Run
forge testto verify setup