foundry-contract-dev

Comprehensive Foundry smart contract development skill with code style, naming conventions, contract structure, security guidelines, testing standards, and gas optimization. Use when working with .sol files, Foundry projects (foundry.toml), test files (.t.sol), or deployment scripts. Triggers on "smart contract", "Solidity", "Foundry", "forge", "deploy contract", "write test", "security audit".

Foundry Smart Contract Development

Skill for professional Solidity smart contract development using Foundry framework. Provides code standards, security-first practices, and comprehensive testing patterns.

When to Use

Activate when:

  • Working with .sol (Solidity) files
  • Foundry project detected (foundry.toml present)
  • User mentions smart contracts, Solidity, Foundry, Web3
  • Contract development, testing, deployment, or auditing

Quick Reference

Key Standards Summary

CategoryKey Rules
Versionpragma solidity ^0.8.23; with MIT license
ImportsNamed imports {Contract}, grouped by source
Private varsUnderscore prefix: _variableName
ConstantsSCREAMING_SNAKE_CASE: MAX_SUPPLY
InterfacesDefine ALL errors, events, structs in interface
FunctionsInternal/private use _functionName prefix
NatSpec@notice + @param + @return on all external functions
CoverageMinimum 95% line coverage required

Section Headers

Use decorative ASCII headers for contract organization:

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/*                      SECTION NAME                         */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

Section order: LIBRARIES → CUSTOM ERRORS → EVENTS → STORAGE → CONSTANTS → MODIFIERS → CONSTRUCTOR → PUBLIC UPDATE FUNCTIONS → ADMIN FUNCTIONS → PUBLIC READ FUNCTIONS → INTERNAL FUNCTIONS → PRIVATE FUNCTIONS

Project Initialization

Create New Project

# Initialize new Foundry project
forge init my-project
cd my-project

# Initialize in existing directory
forge init --force

# Initialize with specific template
forge init --template transmissions11/solmate my-project

Install Dependencies

# Install OpenZeppelin contracts
forge install OpenZeppelin/openzeppelin-contracts

# Install Solady (gas-optimized utilities)
forge install Vectorized/solady

# Install specific version
forge install OpenZeppelin/[email protected]

# Update dependencies
forge update

Recommended Contract Libraries

LibraryInstall CommandUse For
OpenZeppelinforge install OpenZeppelin/openzeppelin-contractsERC20/721/1155, Access Control, Proxies, Security
Soladyforge install Vectorized/soladyGas-optimized ERC20/721, Utilities, Math
Solmateforge install transmissions11/solmateMinimal, gas-efficient implementations
PRBMathforge install PaulRBerg/prb-mathFixed-point math (UD60x18, SD59x18)
forge-std(included by default)Testing utilities, console.log, vm cheats

OpenZeppelin

import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";

Solady

import {ERC721} from "solady/tokens/ERC721.sol";
import {ERC20} from "solady/tokens/ERC20.sol";
import {Ownable} from "solady/auth/Ownable.sol";
import {LibString} from "solady/utils/LibString.sol";
import {LibPRNG} from "solady/utils/LibPRNG.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";

Configure foundry.toml

[profile.default]
src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.23"
optimizer = true
optimizer_runs = 200

# Remappings
remappings = [
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "solady/=lib/solady/src/",
]

# Formatter
[fmt]
line_length = 120
tab_width = 4
bracket_spacing = true

# Testing
[fuzz]
runs = 256

[invariant]
runs = 256
depth = 15

Configure remappings.txt

Foundry supports two ways to configure import remappings:

Option 1: In foundry.toml (shown above)

  • Remappings are part of project config
  • Single source of truth

Option 2: remappings.txt file (recommended for IDE compatibility)

  • Better compatibility with VS Code Solidity extension, Hardhat, etc.
  • Can be auto-generated from dependencies
# Auto-generate remappings.txt from installed dependencies
forge remappings > remappings.txt

remappings.txt format:

@openzeppelin/=lib/openzeppelin-contracts/
solady/=lib/solady/src/
forge-std/=lib/forge-std/src/

Best practice:

  • Use remappings.txt for IDE compatibility
  • Run forge remappings > remappings.txt after installing new dependencies
  • Add remappings.txt to version control

Project Structure

my-project/
├── foundry.toml          # Configuration
├── remappings.txt        # Import remappings (for IDE)
├── src/                  # Contract source files
│   ├── interfaces/       # Interface definitions
│   └── libraries/        # Shared libraries
├── test/                 # Test files (.t.sol)
│   ├── unit/
│   ├── integration/
│   └── security/
├── script/               # Deployment scripts (.s.sol)
└── lib/                  # Dependencies (git submodules)

Development Workflow

1. Write Contract

Follow security-first patterns:

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

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";

import {IMyContract} from "./interfaces/IMyContract.sol";

/// @title MyContract
/// @notice Brief description
/// @author Development Team
contract MyContract is IMyContract, Ownable, ReentrancyGuard {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         STORAGE                            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
    mapping(address => uint256) private _balances;
    uint256 private _nextTokenId;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CONSTRUCTOR                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    constructor() Ownable(msg.sender) {
        _nextTokenId = 1;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function deposit() external payable nonReentrant {
        // CEI Pattern
        if (msg.value == 0) revert InvalidAmount();
        
        _balances[msg.sender] += msg.value;
        
        emit Deposited(msg.sender, msg.value);
    }
}

2. Write Tests

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

import {Test} from "forge-std/Test.sol";
import {MyContract} from "../src/MyContract.sol";

contract MyContractTest is Test {
    MyContract public myContract;
    address constant ALICE = address(0x1);

    function setUp() public {
        myContract = new MyContract();
    }

    function test_Deposit_Success() public {
        vm.deal(ALICE, 1 ether);
        vm.prank(ALICE);
        myContract.deposit{value: 0.1 ether}();
        // assertions
    }

    function test_Deposit_RevertWhen_ZeroAmount() public {
        vm.prank(ALICE);
        vm.expectRevert(IMyContract.InvalidAmount.selector);
        myContract.deposit{value: 0}();
    }

    function testFuzz_Deposit(uint256 amount) public {
        amount = bound(amount, 1, 100 ether);
        vm.deal(ALICE, amount);
        vm.prank(ALICE);
        myContract.deposit{value: amount}();
    }
}

3. Run Tests & Coverage

forge test -vvv              # Run all tests verbose
forge test --gas-report      # Include gas costs
forge coverage               # Coverage report (target ≥95%)
forge snapshot               # Gas snapshot

4. Security Analysis

# Static analysis
slither . --exclude-optimization --exclude-informational

# Linting
solhint 'src/**/*.sol' 'test/**/*.sol'

5. Build & Deploy

# Build with optimization
forge build --optimize --optimizer-runs 200

# Simulate deployment
forge script script/Deploy.s.sol --rpc-url $RPC_URL

# Deploy to testnet
forge script script/Deploy.s.sol --rpc-url $RPC_URL --broadcast --verify

# Verify contract
forge verify-contract $ADDRESS src/MyContract.sol:MyContract --chain-id 1

Reference Files

Consult these for detailed standards:

FileUse When
code-style.mdSPDX, pragma, imports, formatting
naming-conventions.mdNaming contracts, variables, functions, events
contract-structure.mdOrganizing sections, interface architecture
documentation-standards.mdNatSpec comments for contracts, functions, events, errors
security-guidelines.mdAccess control, CEI, reentrancy, hooks
testing-standards.mdUnit, integration, fuzz, gas tests
gas-optimization.mdStorage packing, loops, unchecked

Interface Architecture (Critical)

MUST define errors, events, and structs in interface:

// Interface - defines everything
interface IMyContract {
    error InvalidAmount();
    error Unauthorized();
    
    event Deposited(address indexed user, uint256 amount);
    
    struct UserInfo { uint256 balance; uint64 lastUpdate; }
    
    function deposit() external payable;
}

// Contract - inherits and uses
contract MyContract is IMyContract {
    // DO NOT re-define errors, events, or structs here
    // Use inherited ones directly
    
    function deposit() external payable {
        if (msg.value == 0) revert InvalidAmount(); // ✓ Uses interface error
        emit Deposited(msg.sender, msg.value);      // ✓ Uses interface event
    }
}

Common Commands

# Project Setup
forge init my-project          # New project
forge init --force             # Init in existing dir
forge install org/repo         # Install dependency
forge update                   # Update dependencies

# Development
forge build                    # Compile
forge test                     # Test
forge fmt                      # Format
forge clean                    # Clean

# Testing
forge test -vvv                # Verbose
forge test --match-test NAME   # Specific test
forge test --fork-url $RPC     # Fork testing
forge coverage                 # Coverage

# Deployment
forge script script/Deploy.s.sol --broadcast
forge verify-contract $ADDR src/Contract.sol:Contract

# Local node
anvil                          # Start local node
anvil --fork-url $RPC          # Fork mainnet

Pre-deployment Checklist

  • All errors, events, structs defined in interfaces
  • Reentrancy protection on external call functions
  • Access controls on privileged functions
  • Input validation on all external functions
  • CEI pattern followed for state changes
  • Events emitted for all state changes
  • Test coverage ≥95%
  • Slither analysis passed
  • solhint checks passed
  • Gas usage within acceptable limits
  • Professional audit for mainnet