Claude Skill

foundry-solidity

Build and test Solidity smart contracts with Foundry toolkit. Use when developing Ethereum contracts, writing Forge tests, deploying with scripts, or debugging with Cast/Anvil. Triggers on Foundry commands (forge, cast, anvil), Solidity testing, smart contract development, or fil

LLM Mart · 0 points · 18 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download tenequm-skills-skills_foundry-solidity-1ff2284.zip · 50 KB
Part of tenequm/skills — 25 skills

Install

skills CLI npx skills add https://github.com/tenequm/skills/tree/main/skills/foundry-solidity
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install tenequm-skills@llmmart
Git git clone https://github.com/tenequm/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole tenequm/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Foundry Solidity Development

Complete guide for building secure, efficient smart contracts with Foundry 1.5.0 and Solidity 0.8.30.

When to Use This Skill

  • Developing Ethereum/EVM smart contracts
  • Writing Forge tests (unit, fuzz, invariant, fork)
  • Deploying contracts with scripts
  • Using Foundry tools (forge, cast, anvil, chisel)
  • Working with foundry.toml, *.t.sol, *.s.sol files
  • Debugging transactions and contract interactions

Quick Start

# Create new project
forge init my-project && cd my-project

# Build contracts
forge build

# Run tests
forge test

# Deploy (dry-run)
forge script script/Deploy.s.sol --rpc-url sepolia

# Deploy (broadcast)
forge script script/Deploy.s.sol --rpc-url sepolia --broadcast --verify

Project Structure

my-project/
├── foundry.toml          # Configuration
├── src/                  # Contracts
│   └── Counter.sol
├── test/                 # Tests (*.t.sol)
│   └── Counter.t.sol
├── script/               # Deploy scripts (*.s.sol)
│   └── Deploy.s.sol
└── lib/                  # Dependencies
    └── forge-std/

Core Commands

Build & Test

forge build                          # Compile
forge test                           # Run all tests
forge test -vvvv                     # With traces
forge test --match-test testDeposit  # Filter by test name
forge test --match-contract Vault    # Filter by contract
forge test --fork-url $RPC_URL       # Fork testing
forge test --gas-report              # Gas usage report

Deployment

# Single contract
forge create src/Token.sol:Token --rpc-url sepolia --private-key $KEY --broadcast

# Script deployment (recommended)
forge script script/Deploy.s.sol:Deploy --rpc-url sepolia --broadcast --verify

# Verify existing contract
forge verify-contract $ADDRESS src/Token.sol:Token --chain sepolia

Cast - Blockchain Interactions

cast call $CONTRACT "balanceOf(address)" $USER --rpc-url mainnet
cast send $CONTRACT "transfer(address,uint256)" $TO $AMOUNT --private-key $KEY
cast decode-tx $TX_HASH
cast storage $CONTRACT 0 --rpc-url mainnet

Anvil - Local Node

anvil                           # Start local node
anvil --fork-url $RPC_URL       # Fork mainnet
anvil --fork-block-number 18000000

Basic Test Contract

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

import {Test, console} from "forge-std/Test.sol";
import {Counter} from "../src/Counter.sol";

contract CounterTest is Test {
    Counter public counter;
    address public user;

    function setUp() public {
        counter = new Counter();
        user = makeAddr("user");
        deal(user, 10 ether);
    }

    function test_Increment() public {
        counter.increment();
        assertEq(counter.number(), 1);
    }

    function test_RevertWhen_Unauthorized() public {
        vm.expectRevert("Unauthorized");
        vm.prank(user);
        counter.adminFunction();
    }

    function testFuzz_SetNumber(uint256 x) public {
        x = bound(x, 0, 1000);
        counter.setNumber(x);
        assertEq(counter.number(), x);
    }
}

Essential Cheatcodes

// Identity & ETH
address alice = makeAddr("alice");          // Create labeled address
deal(alice, 10 ether);                      // Give ETH
deal(address(token), alice, 1000e18);       // Give ERC20

// Impersonation
vm.prank(alice);                            // Next call as alice
vm.startPrank(alice);                       // All calls as alice
vm.stopPrank();

// Time & Block
vm.warp(block.timestamp + 1 days);          // Set timestamp
vm.roll(block.number + 100);                // Set block number

// Assertions
vm.expectRevert("Error message");           // Expect revert
vm.expectRevert(CustomError.selector);      // Custom error
vm.expectEmit(true, true, false, true);     // Expect event
emit Transfer(from, to, amount);            // Must match next emit

// Storage
vm.store(addr, slot, value);                // Write storage
vm.load(addr, slot);                        // Read storage

Deploy Script

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

import {Script, console} from "forge-std/Script.sol";
import {Counter} from "../src/Counter.sol";

contract Deploy is Script {
    function run() external {
        uint256 deployerKey = vm.envUint("PRIVATE_KEY");

        vm.startBroadcast(deployerKey);
        Counter counter = new Counter();
        counter.setNumber(42);
        vm.stopBroadcast();

        console.log("Deployed to:", address(counter));
    }
}

Modern Solidity Patterns (0.8.30)

// Custom errors (gas efficient)
error InsufficientBalance(uint256 available, uint256 required);

// Transient storage (0.8.28+) - cheap reentrancy guard
bool transient locked;
modifier nonReentrant() {
    require(!locked, "Reentrancy");
    locked = true;
    _;
    locked = false;
}

// Immutable variables (cheap reads)
address public immutable owner;

// Named mapping parameters
mapping(address user => uint256 balance) public balances;

// require with custom error (0.8.26+)
require(amount <= balance, InsufficientBalance(balance, amount));

Configuration (foundry.toml)

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

fuzz.runs = 256
invariant.runs = 256
invariant.depth = 50

[rpc_endpoints]
mainnet = "${MAINNET_RPC_URL}"
sepolia = "${SEPOLIA_RPC_URL}"

[etherscan]
mainnet = { key = "${ETHERSCAN_API_KEY}" }
sepolia = { key = "${ETHERSCAN_API_KEY}" }

[profile.ci]
fuzz.runs = 10000
invariant.runs = 1000

References

For detailed guides, see:

  • Testing: See references/testing.md for complete testing patterns (unit, fuzz, invariant, fork), all cheatcodes, and best practices
  • forge-std API: See references/forge-std-api.md for complete library reference (150+ functions)
  • Solidity 0.8.30: See references/solidity-modern.md for new features and modern syntax
  • Deployment: See references/deployment.md for scripting, verification, and multi-chain deployment
  • Configuration: See references/configuration.md for all foundry.toml options
  • Gas Optimization: See references/gas-optimization.md for storage packing, compiler settings, and profiling
  • Patterns: See references/patterns.md for access control, reentrancy guards, factories, and common idioms
  • Security: See references/security.md for vulnerabilities, defensive patterns, and audit preparation
  • Resources: See references/resources.md for official docs, libraries, security tools, and learning paths
  • Debugging: See references/debugging.md for traces, breakpoints, console.log, and the interactive debugger
  • Dependencies: See references/dependencies.md for forge install, remappings, and Soldeer package manager
  • CI/CD: See references/cicd.md for GitHub Actions workflows, caching, and gas tracking
  • Chisel: See references/chisel.md for the interactive Solidity REPL
  • Cast Advanced: See references/cast-advanced.md for decoding, encoding, wallet management, and batch operations
  • Anvil Advanced: See references/anvil-advanced.md for impersonation, state manipulation, and mining modes
Files (skills)
  • references
    • anvil-advanced.md 5.2 KB
      # Anvil Advanced Usage
      
      Advanced Anvil features for local development and testing.
      
      ## Account Impersonation
      
      ### Auto-Impersonate All Accounts
      
      ```bash
      # Start with auto-impersonation
      anvil --auto-impersonate
      ```
      
      Now any account can send transactions without private key:
      
      ```bash
      cast send $CONTRACT "transfer(address,uint256)" $TO 1000 \
        --from 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 \
        --unlocked
      ```
      
      ### Impersonate Specific Account
      
      ```bash
      # Via RPC
      cast rpc anvil_impersonateAccount 0x1234...
      
      # Stop impersonating
      cast rpc anvil_stopImpersonatingAccount 0x1234...
      ```
      
      ### In Foundry Tests
      
      ```solidity
      address whale = 0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503;
      
      vm.startPrank(whale);
      usdc.transfer(address(this), 1_000_000e6);
      vm.stopPrank();
      ```
      
      ## State Manipulation
      
      ### Set Balance
      
      ```bash
      # Set ETH balance
      cast rpc anvil_setBalance 0x1234... 0xDE0B6B3A7640000  # 1 ETH in hex
      
      # In tests
      vm.deal(address, 100 ether);
      ```
      
      ### Set Code
      
      ```bash
      # Deploy code at address
      cast rpc anvil_setCode 0x1234... 0x608060405234801...
      
      # In tests
      vm.etch(address, code);
      ```
      
      ### Set Storage
      
      ```bash
      # Set storage slot
      cast rpc anvil_setStorageAt 0x1234... 0x0 0x...
      
      # In tests
      vm.store(address, slot, value);
      ```
      
      ### Set Nonce
      
      ```bash
      cast rpc anvil_setNonce 0x1234... 0x10  # 16 in hex
      ```
      
      ## Mining Modes
      
      ### Auto-Mining (Default)
      
      ```bash
      anvil  # Mines block on each transaction
      ```
      
      ### Interval Mining
      
      ```bash
      # Mine every 12 seconds
      anvil --block-time 12
      ```
      
      ### Manual Mining
      
      ```bash
      # Disable auto-mining
      anvil --no-mining
      
      # Mine manually
      cast rpc evm_mine
      
      # Mine multiple blocks
      cast rpc anvil_mine 10  # Mine 10 blocks
      ```
      
      ### Mining Control
      
      ```bash
      # Enable auto-mine
      cast rpc evm_setAutomine true
      
      # Set interval
      cast rpc evm_setIntervalMining 5000  # 5 seconds in ms
      ```
      
      ## State Snapshots
      
      ### Dump State
      
      ```bash
      # Start anvil, make changes, then dump
      anvil --dump-state state.json
      
      # Load from previous state
      anvil --load-state state.json
      ```
      
      ### Runtime Snapshots
      
      ```bash
      # Create snapshot
      SNAPSHOT_ID=$(cast rpc evm_snapshot)
      
      # Make changes...
      
      # Revert to snapshot
      cast rpc evm_revert $SNAPSHOT_ID
      ```
      
      ### In Tests
      
      ```solidity
      uint256 snapshot = vm.snapshot();
      
      // Make changes...
      
      vm.revertTo(snapshot);
      ```
      
      ## Fork Configuration
      
      ### Basic Fork
      
      ```bash
      anvil --fork-url https://eth-mainnet.g.alchemy.com/v2/KEY
      ```
      
      ### Pin Block
      
      ```bash
      anvil --fork-url $RPC_URL --fork-block-number 18000000
      ```
      
      ### Fork with Caching
      
      ```bash
      # Cache fork data locally
      anvil --fork-url $RPC_URL --fork-retry-backoff 1000
      
      # Disable caching
      anvil --fork-url $RPC_URL --no-storage-caching
      ```
      
      ### Multiple Forks
      
      ```solidity
      // In tests
      uint256 mainnetFork = vm.createFork("mainnet");
      uint256 arbitrumFork = vm.createFork("arbitrum");
      
      vm.selectFork(mainnetFork);
      // Test on mainnet...
      
      vm.selectFork(arbitrumFork);
      // Test on arbitrum...
      ```
      
      ## Time Manipulation
      
      ```bash
      # Set timestamp
      cast rpc evm_setNextBlockTimestamp 1700000000
      
      # Increase time
      cast rpc evm_increaseTime 86400  # 1 day
      
      # In tests
      vm.warp(block.timestamp + 1 days);
      vm.roll(block.number + 100);
      ```
      
      ## RPC Methods
      
      ### Common Anvil RPC
      
      | Method | Description |
      |--------|-------------|
      | `anvil_setBalance` | Set ETH balance |
      | `anvil_setCode` | Set contract code |
      | `anvil_setStorageAt` | Set storage slot |
      | `anvil_setNonce` | Set account nonce |
      | `anvil_impersonateAccount` | Impersonate address |
      | `anvil_mine` | Mine blocks |
      | `anvil_reset` | Reset fork |
      | `anvil_dumpState` | Export state |
      | `anvil_loadState` | Import state |
      
      ### EVM Methods
      
      | Method | Description |
      |--------|-------------|
      | `evm_snapshot` | Create snapshot |
      | `evm_revert` | Revert to snapshot |
      | `evm_mine` | Mine single block |
      | `evm_setAutomine` | Toggle auto-mining |
      | `evm_increaseTime` | Advance time |
      | `evm_setNextBlockTimestamp` | Set next timestamp |
      
      ## Configuration
      
      ### Startup Options
      
      ```bash
      anvil \
        --port 8545 \
        --accounts 10 \
        --balance 10000 \
        --mnemonic "test test test..." \
        --derivation-path "m/44'/60'/0'/0/" \
        --block-time 12 \
        --gas-limit 30000000 \
        --gas-price 0 \
        --chain-id 31337 \
        --hardfork prague
      ```
      
      ### Hardfork Selection
      
      ```bash
      anvil --hardfork shanghai
      anvil --hardfork cancun
      anvil --hardfork prague  # Latest
      ```
      
      ## Testing Patterns
      
      ### Fork Test Setup
      
      ```solidity
      function setUp() public {
          vm.createSelectFork(vm.envString("MAINNET_RPC_URL"), 18000000);
      
          // Impersonate whale
          address whale = 0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503;
          vm.startPrank(whale);
          usdc.transfer(address(this), 1_000_000e6);
          vm.stopPrank();
      }
      ```
      
      ### State Reset Between Tests
      
      ```solidity
      uint256 snapshot;
      
      function setUp() public {
          if (snapshot == 0) {
              // First run: setup and snapshot
              _deployContracts();
              snapshot = vm.snapshot();
          } else {
              // Subsequent runs: revert to clean state
              vm.revertTo(snapshot);
          }
      }
      ```
      
      ### Testing Mainnet Interactions
      
      ```solidity
      function test_SwapOnUniswap() public {
          vm.createSelectFork("mainnet");
      
          address router = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
          address weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
      
          deal(address(this), 10 ether);
      
          IRouter(router).swapExactETHForTokens{value: 1 ether}(
              0, path, address(this), block.timestamp
          );
      }
      ```
      
    • cast-advanced.md 4.6 KB
      # Cast Advanced Usage
      
      Advanced cast commands for blockchain interaction, decoding, and analysis.
      
      ## Transaction Decoding
      
      ### Decode Transaction
      
      ```bash
      # Decode transaction by hash
      cast decode-tx 0x1234... --rpc-url mainnet
      
      # Output includes:
      # - from, to, value
      # - function selector
      # - decoded calldata
      # - gas used
      ```
      
      ### 4byte Signature Lookup
      
      ```bash
      # Get function name from selector
      cast 4byte 0xa9059cbb
      # transfer(address,uint256)
      
      # Get selector from signature
      cast sig "transfer(address,uint256)"
      # 0xa9059cbb
      
      # Decode calldata with known selector
      cast 4byte-decode 0xa9059cbb000000000000000000000000...
      ```
      
      ### Decode Calldata
      
      ```bash
      # Decode with ABI
      cast calldata-decode "transfer(address,uint256)" 0xa9059cbb...
      
      # Output:
      # 0x1234...  [address]
      # 1000000    [uint256]
      ```
      
      ## ABI Encoding/Decoding
      
      ### Encode Function Call
      
      ```bash
      # Encode calldata
      cast calldata "transfer(address,uint256)" 0x1234... 1000000
      # 0xa9059cbb000000000000000000000000...
      
      # Encode with complex types
      cast calldata "swap((address,uint256,bytes))" "(0x1234...,100,0x)"
      ```
      
      ### Encode Arguments
      
      ```bash
      # ABI encode
      cast abi-encode "constructor(string,uint256)" "Token" 1000000
      
      # ABI encode packed
      cast abi-encode --packed "test(string)" "hello"
      ```
      
      ### Decode ABI Data
      
      ```bash
      # Decode return data
      cast abi-decode "balanceOf(address)(uint256)" 0x00000000...
      # 1000000
      
      # Decode with multiple returns
      cast abi-decode "getReserves()(uint112,uint112,uint32)" 0x...
      ```
      
      ## Wallet Management
      
      ### Create Wallet
      
      ```bash
      # Generate new wallet
      cast wallet new
      
      # Generate with mnemonic
      cast wallet new-mnemonic
      
      # Derive from mnemonic
      cast wallet derive-private-key "word1 word2 ... word12"
      ```
      
      ### Wallet Info
      
      ```bash
      # Get address from private key
      cast wallet address --private-key 0x...
      
      # Get address from mnemonic
      cast wallet address --mnemonic "word1 word2..."
      
      # Sign message
      cast wallet sign "message" --private-key 0x...
      ```
      
      ### Keystore
      
      ```bash
      # Import to keystore
      cast wallet import my-wallet --private-key 0x...
      
      # List keystores
      cast wallet list
      
      # Use keystore
      cast send ... --account my-wallet
      ```
      
      ## Contract Interaction
      
      ### Read Functions
      
      ```bash
      # Call view function
      cast call $CONTRACT "balanceOf(address)" $USER --rpc-url mainnet
      
      # With block number
      cast call $CONTRACT "balanceOf(address)" $USER --block 18000000
      
      # Decode result
      cast call $CONTRACT "decimals()" | cast to-dec
      ```
      
      ### Write Functions
      
      ```bash
      # Send transaction
      cast send $CONTRACT "transfer(address,uint256)" $TO $AMOUNT \
        --private-key $KEY \
        --rpc-url mainnet
      
      # With value
      cast send $CONTRACT "deposit()" --value 1ether --private-key $KEY
      
      # Estimate gas
      cast estimate $CONTRACT "transfer(address,uint256)" $TO $AMOUNT
      ```
      
      ## Storage Inspection
      
      ```bash
      # Read storage slot
      cast storage $CONTRACT 0 --rpc-url mainnet
      
      # Read multiple slots
      for i in {0..10}; do
        echo "Slot $i: $(cast storage $CONTRACT $i)"
      done
      
      # Find storage slot for mapping
      cast index address $KEY 0  # mapping at slot 0
      ```
      
      ## Block & Transaction Info
      
      ```bash
      # Get block
      cast block latest --rpc-url mainnet
      cast block 18000000 --field timestamp
      
      # Get transaction
      cast tx 0x1234... --rpc-url mainnet
      
      # Get receipt
      cast receipt 0x1234... --rpc-url mainnet
      
      # Get logs
      cast logs --from-block 18000000 --to-block 18000100 \
        --address $CONTRACT \
        --topic0 0xddf252ad...  # Transfer event
      ```
      
      ## Type Conversions
      
      ```bash
      # Hex to decimal
      cast to-dec 0x64
      # 100
      
      # Decimal to hex
      cast to-hex 100
      # 0x64
      
      # Wei conversions
      cast to-wei 1 ether
      # 1000000000000000000
      
      cast from-wei 1000000000000000000
      # 1.000000000000000000
      
      # ASCII/bytes conversions
      cast to-ascii 0x68656c6c6f
      # hello
      
      cast from-utf8 "hello"
      # 0x68656c6c6f
      ```
      
      ## Address Utilities
      
      ```bash
      # Checksum address
      cast to-checksum-address 0x1234...
      
      # Compute CREATE address
      cast compute-address $DEPLOYER --nonce 5
      
      # Compute CREATE2 address
      cast create2 --starts-with 0x1234 --init-code 0x...
      ```
      
      ## ENS
      
      ```bash
      # Resolve ENS name
      cast resolve-name vitalik.eth --rpc-url mainnet
      
      # Lookup address
      cast lookup-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
      ```
      
      ## Gas
      
      ```bash
      # Get gas price
      cast gas-price --rpc-url mainnet
      
      # Get base fee
      cast base-fee --rpc-url mainnet
      
      # Estimate gas
      cast estimate $CONTRACT "transfer(address,uint256)" $TO $AMOUNT
      ```
      
      ## Batch Operations
      
      ```bash
      # Multiple calls with bash
      for addr in $ADDR1 $ADDR2 $ADDR3; do
        echo "$addr: $(cast call $TOKEN 'balanceOf(address)' $addr | cast to-dec)"
      done
      
      # Using multicall3
      cast call 0xcA11bde05977b3631167028862bE2a173976CA11 \
        "aggregate((address,bytes)[])" \
        "[($TOKEN,$(cast calldata 'balanceOf(address)' $ADDR1)),...]"
      ```
      
    • chisel.md 2.3 KB
      # Chisel REPL
      
      Interactive Solidity REPL for quick experimentation.
      
      ## Starting Chisel
      
      ```bash
      # Basic REPL
      chisel
      
      # With fork
      chisel --fork-url https://eth-mainnet.g.alchemy.com/v2/KEY
      
      # Specific block
      chisel --fork-url $RPC_URL --fork-block-number 18000000
      ```
      
      ## Basic Usage
      
      ```
      ➜ uint256 x = 42
      ➜ x * 2
      Type: uint256
      ├ Hex: 0x54
      ├ Hex (full word): 0x0000000000000000000000000000000000000000000000000000000000000054
      └ Decimal: 84
      
      ➜ address alice = address(0x1234)
      ➜ alice.balance
      Type: uint256
      └ Decimal: 0
      ```
      
      ## Commands
      
      | Command | Description |
      |---------|-------------|
      | `!help` | Show all commands |
      | `!clear` | Clear session state |
      | `!source` | Show generated source |
      | `!rawstack` | Show raw stack output |
      | `!edit` | Open in editor |
      | `!export` | Export session to script |
      
      ## Session Management
      
      ```bash
      # List saved sessions
      chisel list
      
      # Load session
      chisel load my-session
      
      # Save current session (in REPL)
      !save my-session
      
      # Clear cache
      chisel clear-cache
      ```
      
      ## Working with Contracts
      
      ```
      ➜ interface IERC20 {
          function balanceOf(address) external view returns (uint256);
      }
      
      ➜ IERC20 usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)
      
      ➜ usdc.balanceOf(0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503)
      Type: uint256
      └ Decimal: 1234567890
      ```
      
      ## Math Testing
      
      Quick calculations without deploying:
      
      ```
      ➜ uint256 a = 1000000
      ➜ uint256 b = 3
      ➜ a / b
      Type: uint256
      └ Decimal: 333333
      
      ➜ (a * 1e18) / b
      Type: uint256
      └ Decimal: 333333333333333333333333
      ```
      
      ## Hash Functions
      
      ```
      ➜ keccak256("hello")
      Type: bytes32
      └ 0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
      
      ➜ keccak256(abi.encode(uint256(1), address(0x1234)))
      ```
      
      ## ABI Encoding
      
      ```
      ➜ abi.encode(uint256(42), address(0x1234))
      ➜ abi.encodePacked("hello", "world")
      ➜ abi.encodeWithSelector(bytes4(0x12345678), 100)
      ```
      
      ## Use Cases
      
      1. **Quick math**: Test calculations before implementing
      2. **ABI encoding**: Debug encoding issues
      3. **Hash verification**: Check keccak256 outputs
      4. **Contract interaction**: Test calls on fork
      5. **Solidity syntax**: Experiment with new features
      
      ## Configuration
      
      Chisel inherits project settings from `foundry.toml`:
      
      ```toml
      [profile.default]
      solc = "0.8.30"
      evm_version = "prague"
      ```
      
      Run chisel from project root to use these settings.
      
    • cicd.md 5.6 KB
      # CI/CD Integration
      
      GitHub Actions workflows for Foundry projects.
      
      ## Basic Workflow
      
      ```yaml
      # .github/workflows/test.yml
      name: Test
      
      on:
        push:
          branches: [main]
        pull_request:
      
      env:
        FOUNDRY_PROFILE: ci
      
      jobs:
        test:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v4
              with:
                submodules: recursive
      
            - name: Install Foundry
              uses: foundry-rs/foundry-toolchain@v1
      
            - name: Run tests
              run: forge test -vvv
      ```
      
      ## With Caching
      
      ```yaml
      name: Test
      
      on: [push, pull_request]
      
      env:
        FOUNDRY_PROFILE: ci
      
      jobs:
        test:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v4
              with:
                submodules: recursive
      
            - name: Install Foundry
              uses: foundry-rs/foundry-toolchain@v1
              with:
                cache: true
      
            - name: Cache dependencies
              uses: actions/cache@v4
              with:
                path: |
                  lib
                  cache
                  out
                key: <runner.os>-foundry-<hashFiles('**/foundry.toml')>
                restore-keys: |
                  <runner.os>-foundry-
      
            - name: Build
              run: forge build
      
            - name: Run tests
              run: forge test -vvv
      ```
      
      ## Full Pipeline
      
      ```yaml
      name: CI
      
      on:
        push:
          branches: [main]
        pull_request:
      
      env:
        FOUNDRY_PROFILE: ci
      
      jobs:
        build:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v4
              with:
                submodules: recursive
      
            - name: Install Foundry
              uses: foundry-rs/foundry-toolchain@v1
              with:
                cache: true
      
            - name: Build
              run: forge build --sizes
      
        test:
          runs-on: ubuntu-latest
          needs: build
          steps:
            - uses: actions/checkout@v4
              with:
                submodules: recursive
      
            - name: Install Foundry
              uses: foundry-rs/foundry-toolchain@v1
              with:
                cache: true
      
            - name: Run tests
              run: forge test -vvv
      
        coverage:
          runs-on: ubuntu-latest
          needs: build
          steps:
            - uses: actions/checkout@v4
              with:
                submodules: recursive
      
            - name: Install Foundry
              uses: foundry-rs/foundry-toolchain@v1
              with:
                cache: true
      
            - name: Generate coverage
              run: forge coverage --report lcov
      
            - name: Upload coverage
              uses: codecov/codecov-action@v4
              with:
                files: lcov.info
      
        gas-report:
          runs-on: ubuntu-latest
          needs: build
          steps:
            - uses: actions/checkout@v4
              with:
                submodules: recursive
      
            - name: Install Foundry
              uses: foundry-rs/foundry-toolchain@v1
              with:
                cache: true
      
            - name: Gas snapshot
              run: forge snapshot --check --tolerance 5
      ```
      
      ## Fork Testing in CI
      
      ```yaml
      jobs:
        fork-test:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v4
              with:
                submodules: recursive
      
            - name: Install Foundry
              uses: foundry-rs/foundry-toolchain@v1
              with:
                cache: true
      
            - name: Run fork tests
              run: forge test --match-test "testFork" -vvv
              env:
                MAINNET_RPC_URL: <secret-MAINNET_RPC_URL>
      ```
      
      In your real workflow, replace `<secret-MAINNET_RPC_URL>` with the GitHub Actions secrets expression for the same name (see [Using encrypted secrets in a workflow](https://docs.github.com/en/actions/security-guides/encrypted-secrets#using-encrypted-secrets-in-a-workflow)). The same convention applies to the deploy block below.
      
      ## Gas Snapshot Tracking
      
      ### Check for regressions
      
      ```yaml
      - name: Gas snapshot check
        run: |
          forge snapshot
          forge snapshot --diff .gas-snapshot
      ```
      
      ### Comment on PR
      
      ```yaml
      - name: Compare gas
        run: forge snapshot --diff .gas-snapshot > gas-diff.txt
      
      - name: Post gas diff
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const diff = fs.readFileSync('gas-diff.txt', 'utf8');
            if (diff.trim()) {
              github.rest.issues.createComment({
                issue_number: context.issue.number,
                owner: context.repo.owner,
                repo: context.repo.repo,
                body: '## Gas Changes\n```\n' + diff + '\n```'
              });
            }
      ```
      
      ## Deployment
      
      ```yaml
      jobs:
        deploy:
          runs-on: ubuntu-latest
          if: github.ref == 'refs/heads/main'
          environment: production
          steps:
            - uses: actions/checkout@v4
              with:
                submodules: recursive
      
            - name: Install Foundry
              uses: foundry-rs/foundry-toolchain@v1
      
            - name: Deploy
              run: |
                forge script script/Deploy.s.sol \
                  --rpc-url <secret-RPC_URL> \
                  --broadcast \
                  --verify
              env:
                PRIVATE_KEY: <secret-DEPLOYER_PRIVATE_KEY>
                ETHERSCAN_API_KEY: <secret-ETHERSCAN_API_KEY>
      ```
      
      ## CI Profile
      
      Configure higher fuzz runs for CI in `foundry.toml`:
      
      ```toml
      [profile.default]
      fuzz.runs = 256
      invariant.runs = 256
      
      [profile.ci]
      fuzz.runs = 10000
      invariant.runs = 1000
      verbosity = 3
      ```
      
      Use with `FOUNDRY_PROFILE=ci forge test`.
      
      ## Secrets Management
      
      Required secrets for CI:
      - `MAINNET_RPC_URL`: For fork testing
      - `DEPLOYER_PRIVATE_KEY`: For deployment
      - `ETHERSCAN_API_KEY`: For verification
      
      **Never commit secrets to the repository.**
      
      ## Matrix Testing
      
      Test across Solidity versions:
      
      ```yaml
      jobs:
        test:
          runs-on: ubuntu-latest
          strategy:
            matrix:
              solc: ["0.8.20", "0.8.25", "0.8.30"]
          steps:
            - uses: actions/checkout@v4
              with:
                submodules: recursive
      
            - name: Install Foundry
              uses: foundry-rs/foundry-toolchain@v1
      
            - name: Test with Solc <matrix.solc>
              run: forge test
              env:
                FOUNDRY_SOLC_VERSION: <matrix.solc>
      ```
      
    • configuration.md 8.4 KB
      # Foundry Configuration Reference
      
      Complete reference for `foundry.toml` configuration options.
      
      ## Basic Structure
      
      ```toml
      # Default profile
      [profile.default]
      src = "src"
      out = "out"
      libs = ["lib"]
      
      # Additional profiles
      [profile.ci]
      # CI-specific overrides
      
      [profile.production]
      # Production build settings
      ```
      
      ## Project Structure
      
      ```toml
      [profile.default]
      # Source directories
      src = "src"                    # Contract sources
      test = "test"                  # Test files
      script = "script"              # Deployment scripts
      out = "out"                    # Compiled output
      libs = ["lib"]                 # Dependency directories
      cache_path = "cache"           # Compilation cache
      
      # Remappings (alternative to remappings.txt)
      remappings = [
          "@openzeppelin/=lib/openzeppelin-contracts/",
          "@solmate/=lib/solmate/src/",
          "forge-std/=lib/forge-std/src/"
      ]
      ```
      
      ## Compiler Settings
      
      ```toml
      [profile.default]
      # Solidity version
      solc = "0.8.30"                # Exact version
      # solc = "^0.8.0"              # Version range
      # auto_detect_solc = true      # Auto-detect from pragmas
      
      # EVM version
      evm_version = "prague"         # Target EVM version
      # Options: homestead, tangerineWhistle, spuriousDragon, byzantium,
      #          constantinople, petersburg, istanbul, berlin, london,
      #          paris, shanghai, cancun, prague
      
      # Optimizer
      optimizer = true
      optimizer_runs = 200           # Optimize for ~200 runs
      via_ir = false                 # Use IR-based compilation
      
      # Output
      extra_output = ["abi", "evm.bytecode", "storageLayout"]
      extra_output_files = ["abi", "storageLayout"]
      
      # Bytecode hash
      bytecode_hash = "ipfs"         # ipfs, bzzr1, or none
      cbor_metadata = true           # Include CBOR metadata
      ```
      
      ## Testing Configuration
      
      ```toml
      [profile.default]
      # Verbosity (0-5)
      verbosity = 2
      
      # Gas settings
      gas_limit = 9223372036854775807
      gas_price = 0
      block_base_fee_per_gas = 0
      tx_origin = "0x0000000000000000000000000000000000000001"
      
      # Block settings
      block_coinbase = "0x0000000000000000000000000000000000000000"
      block_timestamp = 1
      block_number = 1
      block_difficulty = 0
      block_gas_limit = 30000000
      chain_id = 31337
      
      # Sender
      sender = "0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38"
      
      # Memory limit (bytes)
      memory_limit = 33554432        # 32 MB
      
      # Show gas reports
      gas_reports = ["*"]            # All contracts
      # gas_reports = ["MyContract", "OtherContract"]
      gas_reports_ignore = []
      
      # Fail test if gas exceeds this limit
      # gas_report_fail_on_increase = true
      ```
      
      ## Fuzz Testing
      
      ```toml
      [profile.default]
      # Number of fuzz runs
      fuzz.runs = 256
      
      # Seed for deterministic fuzzing
      fuzz.seed = "0x1234"
      
      # Maximum test rejects before failing
      fuzz.max_test_rejects = 65536
      
      # Dictionary weight (how much to use discovered values)
      fuzz.dictionary_weight = 40
      
      # Include push bytes
      fuzz.include_push_bytes = true
      
      # Include storage
      fuzz.include_storage = true
      
      # Show logs
      fuzz.show_logs = false
      ```
      
      ## Invariant Testing
      
      ```toml
      [profile.default]
      # Number of runs (sequences)
      invariant.runs = 256
      
      # Depth (calls per run)
      invariant.depth = 15
      
      # Fail on revert
      invariant.fail_on_revert = false
      
      # Call override
      invariant.call_override = false
      
      # Dictionary weight
      invariant.dictionary_weight = 80
      
      # Include storage
      invariant.include_storage = true
      
      # Include push bytes
      invariant.include_push_bytes = true
      
      # Shrink run limit
      invariant.shrink_run_limit = 5000
      
      # Max fuzz dictionary addresses
      invariant.max_fuzz_dictionary_addresses = 15
      
      # Max fuzz dictionary values
      invariant.max_fuzz_dictionary_values = 10
      
      # Gas limit
      invariant.gas_limit = 9223372036854775807
      ```
      
      ## Fork Testing
      
      ```toml
      [profile.default]
      # Default fork URL
      # eth_rpc_url = "https://eth-mainnet.alchemyapi.io/v2/..."
      
      # Fork block number
      # fork_block_number = 18000000
      
      # Fork retry backoff
      fork_retry_backoff = "0"
      
      # RPC storage caching
      rpc_storage_caching = {
          chains = "all",
          endpoints = "all"
      }
      
      # No storage caching
      no_storage_caching = false
      ```
      
      ## RPC Endpoints
      
      ```toml
      [rpc_endpoints]
      mainnet = "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}"
      sepolia = "https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_KEY}"
      arbitrum = "https://arb-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}"
      optimism = "https://opt-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}"
      polygon = "https://polygon-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}"
      base = "https://base-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}"
      
      # Local
      localhost = "http://localhost:8545"
      anvil = "http://127.0.0.1:8545"
      
      # Environment variable interpolation
      custom = "${CUSTOM_RPC_URL}"
      ```
      
      ## Etherscan Configuration
      
      ```toml
      [etherscan]
      mainnet = { key = "${ETHERSCAN_API_KEY}" }
      sepolia = { key = "${ETHERSCAN_API_KEY}" }
      arbitrum = { key = "${ARBISCAN_API_KEY}" }
      optimism = { key = "${OPTIMISTIC_ETHERSCAN_API_KEY}" }
      polygon = { key = "${POLYGONSCAN_API_KEY}" }
      base = { key = "${BASESCAN_API_KEY}" }
      
      # Custom chain
      custom = { key = "${CUSTOM_API_KEY}", url = "https://api.custom-explorer.com/api" }
      ```
      
      ## Formatting
      
      ```toml
      [fmt]
      # Line length
      line_length = 120
      
      # Tab width
      tab_width = 4
      
      # Bracket spacing
      bracket_spacing = false
      
      # Int types (preserve, short, long)
      int_types = "long"
      
      # Multiline function header
      multiline_func_header = "attributes_first"
      
      # Quote style
      quote_style = "double"
      
      # Number underscore (preserve, thousands, none)
      number_underscore = "preserve"
      
      # Hex underscore
      hex_underscore = "remove"
      
      # Single line statement blocks
      single_line_statement_blocks = "preserve"
      
      # Override spacing
      override_spacing = false
      
      # Wrap comments
      wrap_comments = false
      
      # Ignore files
      ignore = ["src/external/**"]
      
      # Contract new lines
      contract_new_lines = false
      
      # Sort imports
      sort_imports = false
      ```
      
      ## Documentation
      
      ```toml
      [doc]
      # Output directory
      out = "docs"
      
      # Repository link
      repository = "https://github.com/user/repo"
      
      # Ignore patterns
      ignore = ["src/test/**"]
      ```
      
      ## Profiles
      
      ### Default Profile
      
      ```toml
      [profile.default]
      src = "src"
      out = "out"
      libs = ["lib"]
      optimizer = true
      optimizer_runs = 200
      ```
      
      ### CI Profile
      
      ```toml
      [profile.ci]
      fuzz.runs = 10000
      invariant.runs = 1000
      invariant.depth = 100
      verbosity = 3
      ```
      
      ### Production Profile
      
      ```toml
      [profile.production]
      optimizer = true
      optimizer_runs = 1000000
      via_ir = true
      bytecode_hash = "none"
      cbor_metadata = false
      ```
      
      ### Gas Optimization Profile
      
      ```toml
      [profile.gas]
      optimizer = true
      optimizer_runs = 1000000
      gas_reports = ["*"]
      ```
      
      ### Fast Development Profile
      
      ```toml
      [profile.fast]
      optimizer = false
      fuzz.runs = 100
      invariant.runs = 50
      no_match_test = "testFork_"
      ```
      
      ## Using Profiles
      
      ```bash
      # Use default profile
      forge build
      forge test
      
      # Use CI profile
      FOUNDRY_PROFILE=ci forge test
      
      # Use production profile
      FOUNDRY_PROFILE=production forge build
      ```
      
      ## Environment Variables
      
      ```bash
      # Override any config option
      FOUNDRY_SRC=contracts forge build
      FOUNDRY_OPTIMIZER=false forge build
      FOUNDRY_OPTIMIZER_RUNS=1000000 forge build
      FOUNDRY_EVM_VERSION=shanghai forge build
      
      # Common overrides
      FOUNDRY_PROFILE=ci              # Select profile
      FOUNDRY_FUZZ_RUNS=10000         # Fuzz runs
      FOUNDRY_INVARIANT_RUNS=1000     # Invariant runs
      FOUNDRY_VERBOSITY=3             # Test verbosity
      ```
      
      ## Complete Example
      
      ```toml
      # foundry.toml
      
      [profile.default]
      # Project
      src = "src"
      out = "out"
      libs = ["lib"]
      test = "test"
      script = "script"
      
      # Compiler
      solc = "0.8.30"
      evm_version = "prague"
      optimizer = true
      optimizer_runs = 200
      
      # Testing
      verbosity = 2
      fuzz.runs = 256
      fuzz.seed = "0x1234"
      invariant.runs = 256
      invariant.depth = 50
      
      # Gas
      gas_reports = ["*"]
      
      # Output
      extra_output = ["storageLayout"]
      
      # Remappings
      remappings = [
          "@openzeppelin/=lib/openzeppelin-contracts/contracts/",
          "forge-std/=lib/forge-std/src/"
      ]
      
      [profile.ci]
      fuzz.runs = 10000
      fuzz.seed = "0xdeadbeef"
      invariant.runs = 1000
      invariant.depth = 100
      verbosity = 3
      
      [profile.production]
      optimizer = true
      optimizer_runs = 1000000
      via_ir = true
      
      [profile.local]
      optimizer = false
      fuzz.runs = 100
      
      [rpc_endpoints]
      mainnet = "${MAINNET_RPC_URL}"
      sepolia = "${SEPOLIA_RPC_URL}"
      arbitrum = "${ARBITRUM_RPC_URL}"
      optimism = "${OPTIMISM_RPC_URL}"
      base = "${BASE_RPC_URL}"
      localhost = "http://127.0.0.1:8545"
      
      [etherscan]
      mainnet = { key = "${ETHERSCAN_API_KEY}" }
      sepolia = { key = "${ETHERSCAN_API_KEY}" }
      arbitrum = { key = "${ARBISCAN_API_KEY}" }
      optimism = { key = "${OPTIMISTIC_ETHERSCAN_API_KEY}" }
      base = { key = "${BASESCAN_API_KEY}" }
      
      [fmt]
      line_length = 120
      tab_width = 4
      bracket_spacing = false
      int_types = "long"
      multiline_func_header = "attributes_first"
      quote_style = "double"
      number_underscore = "thousands"
      sort_imports = true
      ```
      
    • debugging.md 5.1 KB
      # Debugging Workflows
      
      Foundry debugging tools for finding and fixing smart contract issues.
      
      ## Verbosity Levels
      
      ```bash
      forge test                 # Summary only
      forge test -v              # Show passing test names
      forge test -vv             # Print logs for all tests
      forge test -vvv            # Traces for failing tests
      forge test -vvvv           # Traces for all tests + setup
      forge test -vvvvv          # All traces + storage changes
      ```
      
      **Use:**
      - `-vv`: Quick check of console.log output
      - `-vvv`: First step when test fails
      - `-vvvv`: Full debugging with all traces
      
      ## Understanding Traces
      
      ### Trace Format
      
      ```
      [24661] TestContract::testFunction()
      ├─ [2262] Target::read()
      │   └─ ← 0
      ├─ [20398] Target::write(42)
      │   └─ ← ()
      └─ ← ()
      ```
      
      - `[gas]`: Gas consumed by call and nested calls
      - **Green**: Successful calls
      - **Red**: Reverting calls
      - **Blue**: Cheatcode calls
      - **Cyan**: Emitted logs
      - **Yellow**: Contract deployments
      
      ### Common Trace Errors
      
      | Error | Meaning |
      |-------|---------|
      | `OOG` | Out of gas |
      | `Revert` | Transaction reverted |
      | `InvalidFEOpcode` | Unknown opcode (0xFE) |
      | `NotActivated` | EVM feature not available |
      
      ## Console Logging
      
      ### Basic Usage
      
      ```solidity
      import {console} from "forge-std/console.sol";
      
      function test_Debug() public {
          console.log("Value:", value);
          console.log("Address:", msg.sender);
          console.log("Balance:", address(this).balance);
      }
      ```
      
      ### Format Specifiers
      
      ```solidity
      console.log("String: %s", "hello");
      console.log("Decimal: %d", 123);
      console.log("Hex: %x", 255);
      
      // Multiple arguments (up to 4)
      console.log("From %s to %s: %d", from, to, amount);
      
      // Type-specific
      console.logBytes32(hash);
      console.logAddress(token);
      console.logBool(success);
      ```
      
      ### Debugging Pattern
      
      ```solidity
      function test_Transfer() public {
          console.log("=== Transfer ===");
          console.log("From:", from);
          console.log("To:", to);
          console.log("Amount:", amount);
      
          token.transfer(to, amount);
      
          console.log("Balance after:", token.balanceOf(to));
      }
      ```
      
      ## Breakpoints
      
      Set breakpoints in code, jump to them in debugger:
      
      ```solidity
      function test_Complex() public {
          vm.breakpoint("start");
          uint256 x = calculate();
      
          vm.breakpoint("middle");
          process(x);
      
          vm.breakpoint("end");
      }
      ```
      
      In debugger, press `'` + letter to jump (e.g., `'a` for first breakpoint).
      
      ## Interactive Debugger
      
      ### Starting
      
      ```bash
      # Debug specific test
      forge test --debug --match-test "test_Function"
      
      # Debug script
      forge script script/Deploy.s.sol --debug
      
      # Debug transaction from chain
      cast run --debug 0x123...
      ```
      
      ### Debugger Layout
      
      Four quadrants:
      1. **Top-left**: EVM opcodes (current instruction highlighted)
      2. **Top-right**: Stack state
      3. **Bottom-left**: Solidity source code
      4. **Bottom-right**: Memory contents
      
      ### Navigation Keys
      
      **Movement:**
      - `j/k`: Step forward/backward
      - `g/G`: Go to beginning/end
      - `c/C`: Next/previous CALL instruction
      - `'a-z`: Jump to breakpoint
      
      **Display:**
      - `t`: Toggle stack labels
      - `m`: Toggle memory as UTF8
      - `h`: Help
      - `q`: Quit
      
      ### Debugging Workflow
      
      ```bash
      # 1. Test fails
      forge test --match-test "test_Deposit"
      
      # 2. See what failed
      forge test -vvv --match-test "test_Deposit"
      
      # 3. Add console.log for quick debugging
      # OR use interactive debugger
      forge test --debug --match-test "test_Deposit"
      
      # 4. Step through with j/k, watch stack with t
      ```
      
      ## Gas Profiling
      
      ### Gas Reports
      
      ```bash
      forge test --gas-report
      ```
      
      Output:
      ```
      │ Function    │ min   │ avg   │ median │ max   │ calls │
      ├─────────────┼───────┼───────┼────────┼───────┼───────┤
      │ transfer    │ 2900  │ 5234  │ 5200   │ 8901  │ 145   │
      │ balanceOf   │ 596   │ 596   │ 596    │ 596   │ 234   │
      ```
      
      ### Gas Snapshots
      
      ```bash
      forge snapshot              # Create snapshot
      forge snapshot --diff       # Compare to previous
      forge snapshot --check      # Fail if changed
      ```
      
      ### Inline Measurement
      
      ```solidity
      function test_GasUsage() public {
          uint256 gasBefore = gasleft();
      
          contract.operation();
      
          uint256 gasUsed = gasBefore - gasleft();
          console.log("Gas used:", gasUsed);
      }
      ```
      
      ## Common Error Patterns
      
      ### Assertion Failure
      
      ```
      AssertionError: a == b
        Expected: 1000000
        Actual: 500000
      ```
      
      **Debug:** Run with `-vvv` to trace calculation.
      
      ### Revert Without Message
      
      ```
      Error: reverted
      ```
      
      **Debug:**
      1. Run with `-vvvv` for full trace
      2. Find red (reverting) call in trace
      3. Add console.log before suspect operations
      
      ### Out of Gas
      
      ```
      Error: OutOfGas
      ```
      
      **Fix:** Reduce loop iterations or split into batches.
      
      ### Custom Error
      
      ```solidity
      vm.expectRevert(abi.encodeWithSelector(
          InsufficientBalance.selector,
          1000,  // have
          2000   // need
      ));
      token.transfer(recipient, 2000);
      ```
      
      ## Best Practices
      
      1. **Start simple**: Use console.log before --debug
      2. **Isolate tests**: Test one thing per test function
      3. **Use descriptive logs**: Log state at each step
      4. **Check assumptions**: Verify preconditions
      5. **Save traces**: `forge test -vvvv > trace.txt`
      
    • dependencies.md 3.8 KB
      # Dependency Management
      
      Managing dependencies in Foundry using git submodules and Soldeer.
      
      ## Git Submodules (Default)
      
      ### Installing Dependencies
      
      ```bash
      # Install latest master
      forge install vectorized/solady
      
      # Install specific tag
      forge install vectorized/solady@v0.0.265
      
      # Install specific commit
      forge install vectorized/solady@a5bb996e91aae5b0c068087af7594d92068b12f1
      
      # No automatic commit (for CI)
      forge install OpenZeppelin/openzeppelin-contracts --no-commit
      ```
      
      Dependencies are cloned to `lib/[name]`.
      
      ### Remappings
      
      Forge auto-generates remappings:
      
      ```bash
      $ forge remappings
      forge-std/=lib/forge-std/src/
      solady/=lib/solady/src/
      ```
      
      Use in imports:
      
      ```solidity
      import {Test} from "forge-std/Test.sol";
      import {ERC20} from "solady/tokens/ERC20.sol";
      ```
      
      ### Custom Remappings
      
      Create `remappings.txt`:
      
      ```
      @openzeppelin/=lib/openzeppelin-contracts/contracts/
      @solmate/=lib/solmate/src/
      forge-std/=lib/forge-std/src/
      ```
      
      Or in `foundry.toml`:
      
      ```toml
      [profile.default]
      remappings = [
          "@openzeppelin/=lib/openzeppelin-contracts/contracts/",
          "@solmate/=lib/solmate/src/"
      ]
      ```
      
      ### Updating Dependencies
      
      ```bash
      # Update specific dependency
      forge update lib/solady
      
      # Update all
      forge update
      ```
      
      ### Removing Dependencies
      
      ```bash
      forge remove solady
      # or
      forge remove lib/solady
      ```
      
      ## Soldeer (Modern Package Manager)
      
      Soldeer provides npm-style dependency management with a central registry.
      
      ### Initialize
      
      ```bash
      forge soldeer init
      ```
      
      Creates `dependencies/` folder and configures `foundry.toml`.
      
      ### Installing Packages
      
      ```bash
      # From registry (soldeer.xyz)
      forge soldeer install @openzeppelin-contracts~5.0.2
      forge soldeer install forge-std~1.8.1
      
      # From URL
      forge soldeer install @custom~1.0.0 --url https://example.com/lib.zip
      
      # From git
      forge soldeer install lib~1.0 --git https://github.com/org/lib.git --tag v1.0
      ```
      
      Configuration in `foundry.toml`:
      
      ```toml
      [profile.default]
      libs = ["dependencies"]
      
      [dependencies]
      "@openzeppelin-contracts" = { version = "5.0.2" }
      forge-std = { version = "1.8.1" }
      ```
      
      ### Updating
      
      ```bash
      forge soldeer update
      forge soldeer update --regenerate-remappings
      ```
      
      ### Removing
      
      ```bash
      forge soldeer uninstall @openzeppelin-contracts
      ```
      
      ### Soldeer Config
      
      ```toml
      [soldeer]
      remappings_generate = true
      remappings_version = true      # @lib-5.0.2 suffix
      remappings_prefix = "@"        # @lib instead of lib
      remappings_location = "txt"    # or "config"
      recursive_deps = true          # Install sub-dependencies
      ```
      
      ## Comparison
      
      | Feature | Git Submodules | Soldeer |
      |---------|----------------|---------|
      | Setup | Simple | Requires config |
      | Version pinning | Commit hash | Semantic version |
      | Central registry | No | Yes (soldeer.xyz) |
      | Team sharing | .gitmodules | foundry.toml |
      | Private repos | Full support | URL only |
      
      ## Best Practices
      
      ### Version Pinning
      
      ```bash
      # Production: Use specific tag
      forge install openzeppelin/openzeppelin-contracts@v5.0.0
      
      # Development: Can use master
      forge install vectorized/solady
      ```
      
      ### Handling Conflicts
      
      When dependencies have conflicting versions, use remapping contexts:
      
      ```
      # remappings.txt
      lib/lib_1/:@openzeppelin/=lib/lib_1/node_modules/@openzeppelin/
      lib/lib_2/:@openzeppelin/=lib/lib_2/node_modules/@openzeppelin/
      ```
      
      ### CI Configuration
      
      ```bash
      # After clone, init submodules
      git submodule update --init --recursive
      
      # Or use --no-commit during install
      forge install OpenZeppelin/openzeppelin-contracts --no-commit
      ```
      
      ### Hardhat Compatibility
      
      ```bash
      # Enable node_modules support
      forge install --hh
      ```
      
      ## Troubleshooting
      
      **Submodule not found after clone:**
      ```bash
      git submodule update --init --recursive
      ```
      
      **Remapping not working:**
      ```bash
      forge remappings > remappings.txt
      ```
      
      **Soldeer package missing:**
      Check [soldeer.xyz](https://soldeer.xyz) or publish your own.
      
    • deployment.md 13.6 KB
      # Foundry Deployment Guide
      
      Complete guide to deploying and verifying smart contracts with Foundry.
      
      ## forge create (Single Contract)
      
      Quick deployment for single contracts.
      
      ### Basic Usage
      
      ```bash
      # Deploy with constructor args
      forge create src/Token.sol:Token \
          --rpc-url sepolia \
          --private-key $PRIVATE_KEY \
          --constructor-args "MyToken" "MTK" 18
      
      # Deploy and verify
      forge create src/Token.sol:Token \
          --rpc-url sepolia \
          --private-key $PRIVATE_KEY \
          --broadcast \
          --verify \
          --etherscan-api-key $ETHERSCAN_API_KEY \
          --constructor-args "MyToken" "MTK" 18
      
      # Deploy with value (payable constructor)
      forge create src/Vault.sol:Vault \
          --rpc-url sepolia \
          --private-key $PRIVATE_KEY \
          --value 1ether
      ```
      
      ### Using Ledger
      
      ```bash
      forge create src/Token.sol:Token \
          --rpc-url mainnet \
          --ledger \
          --mnemonic-derivation-path "m/44'/60'/0'/0/0"
      ```
      
      ## Solidity Scripts (Recommended)
      
      More powerful and flexible deployment method.
      
      ### Basic Deploy Script
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.30;
      
      import {Script, console} from "forge-std/Script.sol";
      import {Token} from "../src/Token.sol";
      
      contract DeployToken is Script {
          function run() external {
              // Load private key from environment
              uint256 deployerKey = vm.envUint("PRIVATE_KEY");
              address deployer = vm.addr(deployerKey);
      
              console.log("Deploying from:", deployer);
              console.log("Balance:", deployer.balance);
      
              vm.startBroadcast(deployerKey);
      
              Token token = new Token("MyToken", "MTK", 18);
              console.log("Token deployed to:", address(token));
      
              // Initial setup
              token.mint(deployer, 1_000_000e18);
      
              vm.stopBroadcast();
          }
      }
      ```
      
      ### Running Scripts
      
      ```bash
      # Dry run (simulation only)
      forge script script/Deploy.s.sol:DeployToken --rpc-url sepolia
      
      # Broadcast transactions
      forge script script/Deploy.s.sol:DeployToken \
          --rpc-url sepolia \
          --broadcast
      
      # Broadcast and verify
      forge script script/Deploy.s.sol:DeployToken \
          --rpc-url sepolia \
          --broadcast \
          --verify
      
      # Resume failed deployment
      forge script script/Deploy.s.sol:DeployToken \
          --rpc-url sepolia \
          --broadcast \
          --resume
      ```
      
      ### Script Execution Phases
      
      1. **Local Simulation**: Run script, collect `vm.broadcast()` transactions
      2. **On-chain Simulation**: (with `--rpc-url`) Simulate against chain state
      3. **Broadcasting**: (with `--broadcast`) Send transactions to network
      4. **Verification**: (with `--verify`) Verify contracts on Etherscan
      
      ### Complex Deployment
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.30;
      
      import {Script, console} from "forge-std/Script.sol";
      import {Token} from "../src/Token.sol";
      import {Staking} from "../src/Staking.sol";
      import {Governance} from "../src/Governance.sol";
      
      contract DeployProtocol is Script {
          function run() external {
              uint256 deployerKey = vm.envUint("PRIVATE_KEY");
              address admin = vm.envAddress("ADMIN_ADDRESS");
      
              vm.startBroadcast(deployerKey);
      
              // Deploy token
              Token token = new Token("Protocol Token", "PTK", 18);
              console.log("Token:", address(token));
      
              // Deploy staking with token reference
              Staking staking = new Staking(address(token));
              console.log("Staking:", address(staking));
      
              // Deploy governance with token and staking
              Governance gov = new Governance(
                  address(token),
                  address(staking),
                  admin
              );
              console.log("Governance:", address(gov));
      
              // Setup permissions
              token.grantRole(token.MINTER_ROLE(), address(staking));
              staking.setGovernance(address(gov));
      
              vm.stopBroadcast();
          }
      }
      ```
      
      ### Configuration-Based Deployment
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.30;
      
      import {Script, console} from "forge-std/Script.sol";
      import {Token} from "../src/Token.sol";
      
      contract DeployConfigured is Script {
          struct Config {
              string name;
              string symbol;
              uint256 initialSupply;
              address admin;
          }
      
          function run() external {
              Config memory config = getConfig();
      
              uint256 deployerKey = vm.envUint("PRIVATE_KEY");
      
              vm.startBroadcast(deployerKey);
      
              Token token = new Token(config.name, config.symbol, 18);
              token.mint(config.admin, config.initialSupply);
      
              if (config.admin != vm.addr(deployerKey)) {
                  token.transferOwnership(config.admin);
              }
      
              vm.stopBroadcast();
      
              console.log("Deployed:", address(token));
          }
      
          function getConfig() internal view returns (Config memory) {
              uint256 chainId = block.chainid;
      
              if (chainId == 1) {
                  return Config({
                      name: "Production Token",
                      symbol: "PROD",
                      initialSupply: 100_000_000e18,
                      admin: 0x1234567890123456789012345678901234567890
                  });
              } else if (chainId == 11155111) {
                  return Config({
                      name: "Test Token",
                      symbol: "TEST",
                      initialSupply: 1_000_000e18,
                      admin: vm.envAddress("TEST_ADMIN")
                  });
              } else {
                  revert("Unsupported chain");
              }
          }
      }
      ```
      
      ### Deterministic Deployment (CREATE2)
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.30;
      
      import {Script, console} from "forge-std/Script.sol";
      import {Token} from "../src/Token.sol";
      
      contract DeployDeterministic is Script {
          // Deterministic deployment factory (present on most chains)
          address constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
      
          function run() external {
              uint256 deployerKey = vm.envUint("PRIVATE_KEY");
              bytes32 salt = keccak256("my-token-v1");
      
              // Predict address
              bytes memory bytecode = abi.encodePacked(
                  type(Token).creationCode,
                  abi.encode("MyToken", "MTK", 18)
              );
              address predicted = computeCreate2Address(salt, keccak256(bytecode));
              console.log("Predicted address:", predicted);
      
              // Check if already deployed
              if (predicted.code.length > 0) {
                  console.log("Already deployed!");
                  return;
              }
      
              vm.startBroadcast(deployerKey);
      
              Token token = new Token{salt: salt}("MyToken", "MTK", 18);
              require(address(token) == predicted, "Address mismatch");
      
              vm.stopBroadcast();
      
              console.log("Deployed to:", address(token));
          }
      
          function computeCreate2Address(bytes32 salt, bytes32 initCodeHash)
              internal
              view
              returns (address)
          {
              return address(uint160(uint256(keccak256(abi.encodePacked(
                  bytes1(0xff),
                  address(this),
                  salt,
                  initCodeHash
              )))));
          }
      }
      ```
      
      ## Multi-Chain Deployment
      
      ### Sequential Deployment
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.30;
      
      import {Script, console} from "forge-std/Script.sol";
      import {Token} from "../src/Token.sol";
      
      contract DeployMultiChain is Script {
          function run() external {
              uint256 deployerKey = vm.envUint("PRIVATE_KEY");
      
              // Deploy to Ethereum
              vm.createSelectFork("mainnet");
              vm.startBroadcast(deployerKey);
              Token mainnetToken = new Token("Token", "TKN", 18);
              vm.stopBroadcast();
              console.log("Mainnet:", address(mainnetToken));
      
              // Deploy to Arbitrum
              vm.createSelectFork("arbitrum");
              vm.startBroadcast(deployerKey);
              Token arbitrumToken = new Token("Token", "TKN", 18);
              vm.stopBroadcast();
              console.log("Arbitrum:", address(arbitrumToken));
      
              // Deploy to Optimism
              vm.createSelectFork("optimism");
              vm.startBroadcast(deployerKey);
              Token optimismToken = new Token("Token", "TKN", 18);
              vm.stopBroadcast();
              console.log("Optimism:", address(optimismToken));
          }
      }
      ```
      
      Run with:
      
      ```bash
      forge script script/DeployMultiChain.s.sol \
          --broadcast \
          --multi \
          --slow \
          --verify
      ```
      
      ## Contract Verification
      
      ### Auto-Verification
      
      ```bash
      # During deployment
      forge create src/Token.sol:Token \
          --rpc-url sepolia \
          --private-key $KEY \
          --verify \
          --etherscan-api-key $ETHERSCAN_KEY \
          --constructor-args "Name" "SYM" 18
      
      # With script
      forge script script/Deploy.s.sol --broadcast --verify
      ```
      
      ### Manual Verification
      
      ```bash
      # Verify existing contract
      forge verify-contract \
          --chain sepolia \
          --compiler-version 0.8.30 \
          --num-of-optimizations 200 \
          --constructor-args $(cast abi-encode "constructor(string,string,uint8)" "Name" "SYM" 18) \
          0xYourContractAddress \
          src/Token.sol:Token
      
      # Check verification status
      forge verify-check \
          --chain sepolia \
          $GUID
      ```
      
      ### Verification with Libraries
      
      ```bash
      forge verify-contract \
          --chain mainnet \
          --libraries src/lib/Math.sol:Math:0xLibraryAddress \
          --libraries src/lib/Utils.sol:Utils:0xUtilsAddress \
          0xContractAddress \
          src/MyContract.sol:MyContract
      ```
      
      ### Configuration for Verification
      
      ```toml
      # foundry.toml
      [etherscan]
      mainnet = { key = "${ETHERSCAN_API_KEY}" }
      sepolia = { key = "${ETHERSCAN_API_KEY}" }
      arbitrum = { key = "${ARBISCAN_API_KEY}" }
      optimism = { key = "${OPTIMISTIC_ETHERSCAN_API_KEY}" }
      base = { key = "${BASESCAN_API_KEY}" }
      polygon = { key = "${POLYGONSCAN_API_KEY}" }
      ```
      
      ## Broadcast Artifacts
      
      Scripts save transaction data to `broadcast/` directory:
      
      ```
      broadcast/
      └── Deploy.s.sol/
          └── 11155111/          # Chain ID
              ├── run-latest.json     # Latest run
              ├── run-1699999999.json # Timestamped runs
              └── receipts/
                  └── ...
      ```
      
      ### Reading Artifacts in Scripts
      
      ```solidity
      function readDeployment() internal view returns (address) {
          string memory root = vm.projectRoot();
          string memory path = string.concat(
              root,
              "/broadcast/Deploy.s.sol/11155111/run-latest.json"
          );
      
          string memory json = vm.readFile(path);
          bytes memory contractAddr = json.parseRaw(".transactions[0].contractAddress");
          return abi.decode(contractAddr, (address));
      }
      ```
      
      ## Best Practices
      
      ### 1. Use Environment Variables
      
      ```solidity
      uint256 deployerKey = vm.envUint("PRIVATE_KEY");
      address admin = vm.envAddress("ADMIN_ADDRESS");
      string memory rpcUrl = vm.envString("RPC_URL");
      ```
      
      ### 2. Validate Before Broadcasting
      
      ```solidity
      function run() external {
          uint256 deployerKey = vm.envUint("PRIVATE_KEY");
          address deployer = vm.addr(deployerKey);
      
          // Pre-flight checks
          require(deployer.balance > 0.1 ether, "Insufficient balance");
          require(block.chainid == 11155111, "Wrong network");
      
          vm.startBroadcast(deployerKey);
          // ...
      }
      ```
      
      ### 3. Log Everything
      
      ```solidity
      vm.startBroadcast(deployerKey);
      
      Token token = new Token("Name", "SYM", 18);
      console.log("Token deployed to:", address(token));
      console.log("  - Name:", token.name());
      console.log("  - Symbol:", token.symbol());
      console.log("  - Owner:", token.owner());
      
      vm.stopBroadcast();
      ```
      
      ### 4. Use Numbered Scripts
      
      ```
      script/
      ├── 01_DeployToken.s.sol
      ├── 02_DeployStaking.s.sol
      ├── 03_ConfigurePermissions.s.sol
      └── 04_TransferOwnership.s.sol
      ```
      
      ### 5. Test Scripts
      
      ```solidity
      contract DeployTokenTest is Test {
          DeployToken deployer;
      
          function setUp() public {
              deployer = new DeployToken();
              // Set required env vars
              vm.setEnv("PRIVATE_KEY", vm.toString(uint256(1)));
          }
      
          function testDeploy() public {
              deployer.run();
              // Verify deployment
          }
      }
      ```
      
      ### 6. Handle Failures Gracefully
      
      ```solidity
      function run() external {
          uint256 deployerKey = vm.envUint("PRIVATE_KEY");
      
          vm.startBroadcast(deployerKey);
      
          try new Token("Name", "SYM", 18) returns (Token token) {
              console.log("Success:", address(token));
          } catch Error(string memory reason) {
              console.log("Failed:", reason);
          }
      
          vm.stopBroadcast();
      }
      ```
      
      ## Upgrade Patterns
      
      ### Transparent Proxy
      
      ```solidity
      import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
      import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
      
      contract DeployUpgradeable is Script {
          function run() external {
              uint256 deployerKey = vm.envUint("PRIVATE_KEY");
      
              vm.startBroadcast(deployerKey);
      
              // Deploy implementation
              TokenV1 impl = new TokenV1();
      
              // Deploy proxy admin
              ProxyAdmin admin = new ProxyAdmin(msg.sender);
      
              // Deploy proxy
              bytes memory initData = abi.encodeCall(TokenV1.initialize, ("Name", "SYM"));
              TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
                  address(impl),
                  address(admin),
                  initData
              );
      
              vm.stopBroadcast();
      
              console.log("Implementation:", address(impl));
              console.log("ProxyAdmin:", address(admin));
              console.log("Proxy:", address(proxy));
          }
      }
      ```
      
      ### UUPS Proxy
      
      ```solidity
      import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
      
      contract DeployUUPS is Script {
          function run() external {
              uint256 deployerKey = vm.envUint("PRIVATE_KEY");
      
              vm.startBroadcast(deployerKey);
      
              // Deploy implementation
              TokenV1 impl = new TokenV1();
      
              // Deploy proxy
              bytes memory initData = abi.encodeCall(TokenV1.initialize, ("Name", "SYM"));
              ERC1967Proxy proxy = new ERC1967Proxy(address(impl), initData);
      
              vm.stopBroadcast();
      
              console.log("Implementation:", address(impl));
              console.log("Proxy:", address(proxy));
          }
      }
      ```
      
    • forge-std-api.md 13 KB
      # forge-std API Reference
      
      Complete reference for the Forge Standard Library.
      
      ## Overview
      
      ```solidity
      import {Test, console} from "forge-std/Test.sol";
      import {Script} from "forge-std/Script.sol";
      ```
      
      `Test` inherits: `StdAssertions`, `StdChains`, `StdCheats`, `StdInvariant`, `StdUtils`
      
      ## StdAssertions
      
      ### Boolean
      
      ```solidity
      assertTrue(bool condition);
      assertTrue(bool condition, string memory err);
      
      assertFalse(bool condition);
      assertFalse(bool condition, string memory err);
      ```
      
      ### Equality
      
      ```solidity
      // Works with: bool, uint256, int256, address, bytes32, string, bytes
      assertEq(T left, T right);
      assertEq(T left, T right, string memory err);
      
      assertNotEq(T left, T right);
      assertNotEq(T left, T right, string memory err);
      
      // Arrays
      assertEq(T[] memory left, T[] memory right);
      ```
      
      ### Comparison
      
      ```solidity
      // Works with: uint256, int256
      assertLt(T left, T right);      // <
      assertLt(T left, T right, string memory err);
      
      assertGt(T left, T right);      // >
      assertGt(T left, T right, string memory err);
      
      assertLe(T left, T right);      // <=
      assertLe(T left, T right, string memory err);
      
      assertGe(T left, T right);      // >=
      assertGe(T left, T right, string memory err);
      ```
      
      ### Decimal Formatting
      
      ```solidity
      // Shows values with decimal places in error messages
      assertEqDecimal(uint256 left, uint256 right, uint256 decimals);
      assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals);
      assertLtDecimal(uint256 left, uint256 right, uint256 decimals);
      assertGtDecimal(uint256 left, uint256 right, uint256 decimals);
      assertLeDecimal(uint256 left, uint256 right, uint256 decimals);
      assertGeDecimal(uint256 left, uint256 right, uint256 decimals);
      
      // Example
      assertEqDecimal(1e18, 1e18, 18); // Shows "1.0" not "1000000000000000000"
      ```
      
      ### Approximation
      
      ```solidity
      // Absolute difference
      assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta);
      assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta, string memory err);
      
      // Relative difference (maxPercentDelta: 1e18 = 100%)
      assertApproxEqRel(uint256 left, uint256 right, uint256 maxPercentDelta);
      assertApproxEqRel(uint256 left, uint256 right, uint256 maxPercentDelta, string memory err);
      
      // Examples
      assertApproxEqAbs(1000, 1005, 10);     // Pass: |1000-1005| <= 10
      assertApproxEqRel(100, 101, 0.02e18);  // Pass: 1% diff <= 2%
      ```
      
      ### Call Comparison
      
      ```solidity
      assertEqCall(address target, bytes memory callDataA, bytes memory callDataB);
      assertEqCall(address targetA, bytes memory callDataA, address targetB, bytes memory callDataB);
      ```
      
      ### Failure
      
      ```solidity
      fail();
      fail(string memory err);
      bool hasFailed = failed();
      ```
      
      ## StdCheats
      
      ### Address Creation
      
      ```solidity
      // Create labeled address
      address alice = makeAddr("alice");
      
      // Create address with private key
      (address bob, uint256 bobKey) = makeAddrAndKey("bob");
      
      // Create account struct
      Account memory account = makeAccount("charlie");
      // account.addr, account.key
      ```
      
      ### Account Setup
      
      ```solidity
      // ETH
      deal(address to, uint256 amount);
      
      // ERC20
      deal(address token, address to, uint256 amount);
      deal(address token, address to, uint256 amount, bool adjustTotalSupply);
      
      // ERC721
      dealERC721(address token, address to, uint256 tokenId);
      
      // ERC1155
      dealERC1155(address token, address to, uint256 id, uint256 amount);
      dealERC1155(address token, address to, uint256 id, uint256 amount, bool adjustTotalSupply);
      ```
      
      ### Time Manipulation
      
      ```solidity
      skip(uint256 seconds);   // Move forward
      rewind(uint256 seconds); // Move backward
      
      // Examples
      skip(1 days);
      skip(1 hours);
      rewind(30 minutes);
      ```
      
      ### Prank with ETH (hoax)
      
      ```solidity
      // Single call as sender with ETH
      hoax(address sender);
      hoax(address sender, uint256 give);
      hoax(address sender, address origin);
      hoax(address sender, address origin, uint256 give);
      
      // Multiple calls
      startHoax(address sender);
      startHoax(address sender, uint256 give);
      // ... calls ...
      vm.stopPrank();
      
      // Example
      hoax(alice, 10 ether);
      vault.deposit{value: 1 ether}();
      ```
      
      ### Code Deployment
      
      ```solidity
      // Deploy from artifacts
      address deployed = deployCode("ContractName.sol");
      address deployed = deployCode("ContractName.sol:ContractName");
      address deployed = deployCode("ContractName.sol", constructorArgs);
      address deployed = deployCode("ContractName.sol", constructorArgs, value);
      
      // Deploy to specific address
      deployCodeTo("ContractName.sol", targetAddress);
      deployCodeTo("ContractName.sol", constructorArgs, targetAddress);
      ```
      
      ### Assumptions
      
      ```solidity
      // Address type checks
      assumeNotZeroAddress(address addr);
      assumeNotPrecompile(address addr);
      assumeNotPrecompile(address addr, uint256 chainId);
      assumeNotForgeAddress(address addr);
      assumePayable(address addr);
      assumeNotPayable(address addr);
      
      // Token blacklists
      assumeNotBlacklisted(address token, address addr);
      
      // Combined checks
      assumeAddressIsNot(address addr, AddressType t);
      assumeAddressIsNot(address addr, AddressType t1, AddressType t2);
      
      // AddressType enum: ZeroAddress, Precompile, ForgeAddress
      ```
      
      ### Fork Detection
      
      ```solidity
      bool forking = isFork();
      
      // Modifiers
      function testOnlyLocal() public skipWhenForking { }
      function testOnlyForked() public skipWhenNotForking { }
      ```
      
      ### Gas Metering
      
      ```solidity
      // Disable gas metering for expensive setup
      modifier noGasMetering;
      
      function testExpensiveSetup() public noGasMetering {
          // Gas not counted
      }
      ```
      
      ### Account Destruction
      
      ```solidity
      destroyAccount(address target, address beneficiary);
      ```
      
      ## StdStorage
      
      Dynamic storage slot finding and manipulation.
      
      ### Setup
      
      ```solidity
      using stdStorage for StdStorage;
      ```
      
      ### Finding Slots
      
      ```solidity
      // Simple variable
      uint256 slot = stdstore
          .target(address(contract))
          .sig("variableName()")
          .find();
      
      // Mapping
      uint256 slot = stdstore
          .target(address(contract))
          .sig("balances(address)")
          .with_key(user)
          .find();
      
      // Nested mapping
      uint256 slot = stdstore
          .target(address(contract))
          .sig("allowances(address,address)")
          .with_key(owner)
          .with_key(spender)
          .find();
      
      // Struct field
      uint256 slot = stdstore
          .target(address(contract))
          .sig("structVar()")
          .depth(0)  // Field index
          .find();
      ```
      
      ### Writing Values
      
      ```solidity
      stdstore
          .target(address(contract))
          .sig("balances(address)")
          .with_key(user)
          .checked_write(1000e18);
      
      // For int256
      stdstore
          .target(address(contract))
          .sig("delta()")
          .checked_write_int(-100);
      ```
      
      ### Example
      
      ```solidity
      function testSetBalance() public {
          // Set alice's balance to 1000 tokens
          stdstore
              .target(address(token))
              .sig("balanceOf(address)")
              .with_key(alice)
              .checked_write(1000e18);
      
          assertEq(token.balanceOf(alice), 1000e18);
      }
      ```
      
      ## StdUtils
      
      ### Bounded Randomness
      
      ```solidity
      // Constrain fuzz input to range
      uint256 bounded = bound(uint256 x, uint256 min, uint256 max);
      int256 bounded = bound(int256 x, int256 min, int256 max);
      
      // Constrain to valid private key range
      uint256 key = boundPrivateKey(uint256 pk);
      
      // Example
      function testFuzz(uint256 amount) public {
          amount = bound(amount, 1, 1000);
          // amount is now in [1, 1000]
      }
      ```
      
      ### Address Computation
      
      ```solidity
      // CREATE address
      address addr = computeCreateAddress(address deployer, uint256 nonce);
      
      // CREATE2 address
      address addr = computeCreate2Address(bytes32 salt, bytes32 initCodeHash, address deployer);
      address addr = computeCreate2Address(bytes32 salt, bytes32 initCodeHash); // Uses CREATE2_FACTORY
      
      // Init code hash
      bytes32 hash = hashInitCode(bytes memory creationCode);
      bytes32 hash = hashInitCode(bytes memory creationCode, bytes memory args);
      
      // Example
      bytes32 initHash = hashInitCode(type(MyContract).creationCode);
      address predicted = computeCreate2Address(salt, initHash, factory);
      ```
      
      ### Token Utilities
      
      ```solidity
      // Batch balance query (uses Multicall3)
      uint256[] memory balances = getTokenBalances(address token, address[] memory addresses);
      ```
      
      ### Byte Conversion
      
      ```solidity
      uint256 value = bytesToUint(bytes memory b);
      ```
      
      ## StdJson
      
      ### Reading
      
      ```solidity
      using stdJson for string;
      
      string memory json = vm.readFile("data.json");
      
      // Single values
      uint256 amount = json.readUint(".amount");
      int256 balance = json.readInt(".balance");
      address addr = json.readAddress(".recipient");
      bytes32 hash = json.readBytes32(".hash");
      string memory name = json.readString(".name");
      bytes memory data = json.readBytes(".data");
      bool flag = json.readBool(".enabled");
      
      // Arrays
      uint256[] memory amounts = json.readUintArray(".amounts");
      address[] memory addrs = json.readAddressArray(".addresses");
      string[] memory names = json.readStringArray(".names");
      
      // With defaults
      uint256 amount = json.readUintOr(".amount", 100);
      address addr = json.readAddressOr(".recipient", address(0));
      
      // Check existence
      bool exists = json.keyExists(".key");
      
      // Raw bytes
      bytes memory raw = json.parseRaw(".data");
      ```
      
      ### Writing
      
      ```solidity
      using stdJson for string;
      
      string memory json = "obj";
      json = json.serialize("amount", uint256(100));
      json = json.serialize("recipient", address(0x123));
      json = json.serialize("enabled", true);
      json = json.serialize("amounts", amounts);
      
      json.write("output.json");
      json.write("output.json", ".config");
      ```
      
      ## StdToml
      
      Identical API to StdJson:
      
      ```solidity
      using stdToml for string;
      
      string memory toml = vm.readFile("config.toml");
      uint256 runs = toml.readUint(".profile.default.fuzz_runs");
      ```
      
      ## StdChains
      
      Access chain configuration:
      
      ```solidity
      Chain memory chain = getChain("mainnet");
      // chain.name, chain.chainId, chain.rpcUrl
      
      Chain memory chain = getChain(1); // By chain ID
      
      // Set custom RPC
      setChain("custom", ChainData({
          name: "Custom Chain",
          chainId: 12345,
          rpcUrl: "https://..."
      }));
      ```
      
      ## StdInvariant
      
      For invariant testing targets:
      
      ```solidity
      // Target contracts for fuzzing
      targetContract(address);
      targetContracts(); // Returns address[]
      
      // Exclude from fuzzing
      excludeContract(address);
      excludeContracts(); // Returns address[]
      
      // Target senders
      targetSender(address);
      targetSenders(); // Returns address[]
      
      // Exclude senders
      excludeSender(address);
      excludeSenders(); // Returns address[]
      
      // Target specific selectors
      targetSelector(FuzzSelector memory);
      targetSelectors(); // Returns FuzzSelector[]
      
      // Target artifacts (deploy and fuzz)
      targetArtifact(string memory);
      targetArtifacts(); // Returns string[]
      
      // Target artifact selectors
      targetArtifactSelector(FuzzArtifactSelector memory);
      targetArtifactSelectors(); // Returns FuzzArtifactSelector[]
      ```
      
      ## StdError
      
      Common error selectors:
      
      ```solidity
      import {stdError} from "forge-std/StdError.sol";
      
      vm.expectRevert(stdError.arithmeticError);     // Overflow/underflow
      vm.expectRevert(stdError.assertionError);      // assert() failed
      vm.expectRevert(stdError.divisionError);       // Division by zero
      vm.expectRevert(stdError.encodeStorageError);  // Storage encoding
      vm.expectRevert(stdError.enumConversionError); // Invalid enum
      vm.expectRevert(stdError.indexOOBError);       // Array index out of bounds
      vm.expectRevert(stdError.memOverflowError);    // Memory overflow
      vm.expectRevert(stdError.popEmptyArrayError);  // Pop empty array
      vm.expectRevert(stdError.zeroVarError);        // Zero-initialized function pointer
      ```
      
      ## StdMath
      
      ```solidity
      import {stdMath} from "forge-std/StdMath.sol";
      
      uint256 absolute = stdMath.abs(int256 x);
      uint256 delta = stdMath.delta(uint256 a, uint256 b);
      uint256 delta = stdMath.delta(int256 a, int256 b);
      uint256 percent = stdMath.percentDelta(uint256 a, uint256 b);
      ```
      
      ## Console Logging
      
      ```solidity
      import {console} from "forge-std/console.sol";
      // or
      import {console2} from "forge-std/console2.sol"; // Smaller bytecode
      
      console.log("message");
      console.log("value:", value);
      console.log("a:", a, "b:", b);
      
      // Type-specific
      console.log(uint256 x);
      console.log(int256 x);
      console.log(address x);
      console.log(bool x);
      console.log(string memory x);
      console.log(bytes memory x);
      console.log(bytes32 x);
      
      // Formatted
      console.logBytes(bytes memory);
      console.logBytes1(bytes1);
      // ... up to logBytes32
      ```
      
      ## Script.sol
      
      Base for deployment scripts:
      
      ```solidity
      import {Script, console} from "forge-std/Script.sol";
      
      contract DeployScript is Script {
          function setUp() public {}
      
          function run() public {
              uint256 deployerKey = vm.envUint("PRIVATE_KEY");
      
              vm.startBroadcast(deployerKey);
      
              MyContract c = new MyContract();
              c.initialize();
      
              vm.stopBroadcast();
      
              console.log("Deployed:", address(c));
          }
      }
      ```
      
      ### Script vs Test
      
      | Feature | Test | Script |
      |---------|------|--------|
      | Base | `Test` | `Script` |
      | Cheats | Full `StdCheats` | `StdCheatsSafe` |
      | Purpose | Testing | Deployment |
      | Broadcast | No | Yes |
      | State changes | Local | On-chain |
      
      ## Constants
      
      ```solidity
      // From CommonBase
      address constant VM_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
      address constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67;
      address constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
      address constant DEFAULT_SENDER = 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38;
      address constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f;
      address constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11;
      ```
      
    • gas-optimization.md 7.2 KB
      # Solidity Gas Optimization Guide
      
      Comprehensive gas optimization techniques for Solidity 0.8.30 with Foundry profiling.
      
      ## Storage Optimization
      
      ### Variable Packing
      
      Pack multiple variables into single 32-byte storage slots:
      
      ```solidity
      // BAD: 3 storage slots (3 SSTORE operations)
      contract Unoptimized {
          uint256 amount;      // slot 0
          uint8 status;        // slot 1
          address owner;       // slot 2
      }
      
      // GOOD: 2 storage slots
      contract Optimized {
          uint128 amount;      // slot 0 (16 bytes)
          uint96 lockTime;     // slot 0 (12 bytes)
          uint8 status;        // slot 0 (1 byte)
          address owner;       // slot 1 (20 bytes)
      }
      ```
      
      **Savings:** 15,000+ gas per packed write
      
      ### Struct Packing
      
      Pack struct fields in declaration order:
      
      ```solidity
      // BAD: 3 slots
      struct BadPacking {
          uint256 a;           // slot 0
          uint8 b;             // slot 1
          uint256 c;           // slot 2
      }
      
      // GOOD: 2 slots
      struct GoodPacking {
          uint256 a;           // slot 0
          uint128 c;           // slot 1 (16 bytes)
          uint8 b;             // slot 1 (1 byte)
      }
      ```
      
      ### Storage vs Memory vs Calldata
      
      **Gas Costs:**
      | Operation | Gas |
      |-----------|-----|
      | SLOAD (cold) | 2,100 |
      | SLOAD (warm) | 100 |
      | SSTORE (0→non-0) | 20,000 |
      | SSTORE (non-0→non-0) | 5,000 |
      | MLOAD/MSTORE | 3 per word |
      | Calldata read | 3 per byte |
      
      **Cache storage reads:**
      
      ```solidity
      // BAD: 3 SLOAD operations
      function process() external returns (uint256) {
          return value * 2 + value * 3 + value * 4;
      }
      
      // GOOD: 1 SLOAD + memory operations
      function process() external returns (uint256) {
          uint256 v = value;
          return v * 2 + v * 3 + v * 4;
      }
      ```
      
      **Use calldata for external function arrays:**
      
      ```solidity
      // BAD: Copies to memory
      function batchProcess(uint256[] memory values) external { }
      
      // GOOD: Reads directly from calldata
      function batchProcess(uint256[] calldata values) external {
          for (uint256 i; i < values.length; ) {
              // Process values[i]
              unchecked { i++; }
          }
      }
      ```
      
      ## Arithmetic Optimization
      
      ### Unchecked Blocks
      
      Solidity 0.8+ adds overflow checks by default (~50-100 gas per operation):
      
      ```solidity
      // BAD: Overflow check on every increment
      for (uint256 i = 0; i < 100; i++) {
          result += i;
      }
      
      // GOOD: Unchecked increment (safe when bounded)
      for (uint256 i = 0; i < 100; ) {
          result += i;
          unchecked { i++; }
      }
      ```
      
      **Savings:** 50-100 gas per iteration
      
      ### Short-Circuit Logic
      
      Order conditions by likelihood to fail:
      
      ```solidity
      // Expensive check last (short-circuits if first fails)
      if (amount > 0 && expensiveValidation(amount)) {
          // ...
      }
      ```
      
      ### Bitwise Flag Packing
      
      Combine booleans into single uint256:
      
      ```solidity
      // BAD: 4 storage slots
      bool isActive;
      bool isVerified;
      bool isPaused;
      bool isBlocked;
      
      // GOOD: 1 storage slot, bit manipulation
      uint256 flags;
      
      function isActive() view returns (bool) {
          return (flags & (1 << 0)) != 0;
      }
      
      function setActive(bool _active) {
          if (_active) flags |= (1 << 0);
          else flags &= ~(1 << 0);
      }
      ```
      
      ## Variable Declaration
      
      ### Immutable vs Constant vs Storage
      
      ```solidity
      // Storage variable: 2,100 gas (cold read)
      uint256 public maxSupply = 1_000_000;
      
      // Constant: ~3 gas (embedded in bytecode)
      uint256 public constant MAX_SUPPLY = 1_000_000;
      
      // Immutable: ~100 gas (set once in constructor)
      uint256 public immutable maxSupply;
      constructor(uint256 _max) { maxSupply = _max; }
      ```
      
      **Use constant for compile-time values, immutable for constructor-set values.**
      
      ### Function Visibility
      
      ```solidity
      // Public creates getter (extra dispatch)
      uint256 public value;
      
      // External is cheaper for functions not called internally
      function getValue() external view returns (uint256) {
          return _value;
      }
      ```
      
      ## Error Handling
      
      ### Custom Errors vs Require Strings
      
      ```solidity
      // BAD: String stored in bytecode (~50+ gas)
      require(msg.sender == owner, "Unauthorized access");
      
      // GOOD: Custom error (~24 gas)
      error Unauthorized();
      if (msg.sender != owner) revert Unauthorized();
      ```
      
      **Savings:** 20-50 gas per revert
      
      ## Events vs Storage
      
      Store transient data in events instead of contract storage:
      
      ```solidity
      // BAD: 20,000+ gas per write
      uint256[] public transfers;
      function recordTransfer(uint256 amount) external {
          transfers.push(amount);
      }
      
      // GOOD: ~375 gas base + 8 gas per byte
      event Transfer(address indexed to, uint256 amount);
      function recordTransfer(address to, uint256 amount) external {
          emit Transfer(to, amount);
      }
      ```
      
      ## Compiler Settings
      
      ```toml
      # foundry.toml
      [profile.default]
      optimizer = true
      optimizer_runs = 200     # Balance size/runtime
      
      [profile.production]
      optimizer = true
      optimizer_runs = 1000000 # Optimize for runtime
      via_ir = true            # IR pipeline (slower compile, better optimization)
      ```
      
      **Guidance:**
      - `200` runs: Smaller deployment, higher runtime cost
      - `10000+` runs: Larger deployment, lower runtime cost
      - `via_ir = true`: Best optimization, slowest compilation
      
      ## EVM Opcode Reference
      
      | Operation | Gas | Notes |
      |-----------|-----|-------|
      | ADD/SUB/MUL | 3 | Basic arithmetic |
      | DIV/MOD | 5 | Division operations |
      | SLOAD (cold) | 2,100 | First storage read |
      | SLOAD (warm) | 100 | Subsequent reads |
      | SSTORE (0→non-0) | 20,000 | New storage slot |
      | SSTORE (non-0→non-0) | 5,000 | Update existing |
      | SSTORE (→0) | 5,000 | +15,000 refund |
      | CALL | 700+ | External call base |
      | KECCAK256 | 30 | +6 per word |
      | LOG0-LOG4 | 375-1,875 | Events |
      
      ## Foundry Gas Profiling
      
      ### Gas Reports
      
      ```bash
      # Generate gas report
      forge test --gas-report
      
      # Filter by contract
      forge test --match-contract MyContract --gas-report
      ```
      
      ### Gas Snapshots
      
      ```bash
      # Create snapshot
      forge snapshot
      
      # Compare against previous
      forge snapshot --diff
      
      # Check threshold
      forge snapshot --check --tolerance 5
      ```
      
      ### Inline Gas Measurement
      
      ```solidity
      function testGasUsage() public {
          uint256 gasBefore = gasleft();
      
          // Code to measure
          contract.doSomething();
      
          uint256 gasUsed = gasBefore - gasleft();
          console.log("Gas used:", gasUsed);
      }
      ```
      
      ### Section Snapshots
      
      ```solidity
      function testOptimization() public {
          vm.startSnapshotGas("operation");
      
          // Code to profile
          value = 1;
      
          uint256 gasUsed = vm.stopSnapshotGas();
      }
      ```
      
      ## Optimization Checklist
      
      **Storage:**
      - [ ] Pack related variables together
      - [ ] Use smaller integer types when possible
      - [ ] Cache frequently accessed storage variables
      - [ ] Use mappings instead of arrays for lookups
      - [ ] Use immutable/constant for fixed values
      
      **Functions:**
      - [ ] Mark external (not public) when not called internally
      - [ ] Use calldata for array parameters
      - [ ] Use unchecked blocks for safe arithmetic
      - [ ] Short-circuit expensive conditions
      - [ ] Use custom errors instead of require strings
      
      **Compilation:**
      - [ ] Enable optimizer
      - [ ] Set appropriate optimizer_runs
      - [ ] Consider via_ir for production
      
      **Testing:**
      - [ ] Use `forge test --gas-report` regularly
      - [ ] Create gas snapshots for regression detection
      - [ ] Profile critical functions
      
      ## Quick Wins
      
      | Pattern | Gas Saved | Effort |
      |---------|-----------|--------|
      | Custom errors | 20-50/revert | Low |
      | Unchecked loops | 50-100/iter | Low |
      | Calldata vs memory | 5,000+ | Low |
      | Variable packing | 15,000/write | Low |
      | Immutable vars | 2,000/read | Low |
      | Bitwise flags | 15,000 | Medium |
      | Events vs storage | 19,000+ | Medium |
      
    • patterns.md 13.3 KB
      # Solidity Patterns and Idioms
      
      Common patterns for modern Solidity 0.8.30 smart contract development.
      
      ## Access Control Patterns
      
      ### Ownable Pattern
      
      Single owner - simplest but centralized:
      
      ```solidity
      contract Ownable {
          address private _owner;
      
          error Unauthorized();
      
          modifier onlyOwner() {
              if (msg.sender != _owner) revert Unauthorized();
              _;
          }
      
          constructor() {
              _owner = msg.sender;
          }
      
          function owner() public view returns (address) {
              return _owner;
          }
      
          function transferOwnership(address newOwner) public onlyOwner {
              require(newOwner != address(0), "Invalid owner");
              _owner = newOwner;
          }
      }
      ```
      
      **Use:** Simple contracts, single admin
      **Pitfall:** Single point of failure
      
      ### Role-Based Access Control
      
      Fine-grained permissions:
      
      ```solidity
      contract AccessControl {
          mapping(bytes32 => mapping(address => bool)) private _roles;
      
          bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
          bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
      
          error AccessDenied(address account, bytes32 role);
      
          modifier onlyRole(bytes32 role) {
              if (!_roles[role][msg.sender])
                  revert AccessDenied(msg.sender, role);
              _;
          }
      
          function grantRole(bytes32 role, address account) public onlyRole(ADMIN_ROLE) {
              _roles[role][account] = true;
          }
      
          function hasRole(bytes32 role, address account) public view returns (bool) {
              return _roles[role][account];
          }
      }
      ```
      
      **Use:** DeFi protocols, complex permissions
      **Recommendation:** Use OpenZeppelin's AccessControl
      
      ### Multi-Signature Pattern
      
      Require multiple approvals for critical operations:
      
      ```solidity
      contract MultiSig {
          address[] public owners;
          uint256 public requiredSignatures;
      
          mapping(bytes32 => mapping(address => bool)) public confirmations;
          mapping(bytes32 => bool) public executed;
      
          function submitTransaction(
              address target,
              uint256 value,
              bytes calldata data
          ) external returns (bytes32) {
              bytes32 txHash = keccak256(abi.encode(target, value, data, block.timestamp));
              confirmations[txHash][msg.sender] = true;
              return txHash;
          }
      
          function executeTransaction(
              address target,
              uint256 value,
              bytes calldata data,
              bytes32 txHash
          ) external {
              require(!executed[txHash], "Already executed");
      
              uint256 count = 0;
              for (uint256 i = 0; i < owners.length; i++) {
                  if (confirmations[txHash][owners[i]]) count++;
              }
              require(count >= requiredSignatures, "Not enough signatures");
      
              executed[txHash] = true;
              (bool success,) = target.call{value: value}(data);
              require(success, "Execution failed");
          }
      }
      ```
      
      **Use:** Treasury, critical upgrades, governance
      
      ## Reentrancy Protection
      
      ### Checks-Effects-Interactions (CEI)
      
      Most important pattern - prevent state inconsistencies:
      
      ```solidity
      // BAD: State update AFTER external call
      function withdrawBad() external {
          uint256 amount = balances[msg.sender];
          (bool success,) = msg.sender.call{value: amount}("");
          require(success);
          balances[msg.sender] = 0; // Vulnerable!
      }
      
      // GOOD: CEI pattern
      function withdrawGood() external {
          uint256 amount = balances[msg.sender];
          balances[msg.sender] = 0;              // Effects first
          (bool success,) = msg.sender.call{value: amount}("");
          require(success);                       // Interactions last
      }
      ```
      
      ### Mutex Lock (ReentrancyGuard)
      
      Traditional approach using storage:
      
      ```solidity
      contract ReentrancyGuard {
          uint256 private constant NOT_ENTERED = 1;
          uint256 private constant ENTERED = 2;
          uint256 private _status = NOT_ENTERED;
      
          modifier nonReentrant() {
              require(_status != ENTERED, "ReentrancyGuard: reentrant call");
              _status = ENTERED;
              _;
              _status = NOT_ENTERED;
          }
      }
      ```
      
      **Cost:** ~5,000 gas (2 SSTOREs)
      
      ### Transient Storage Lock (0.8.28+)
      
      Gas-efficient using EIP-1153:
      
      ```solidity
      contract TransientReentrancyGuard {
          modifier nonReentrant() {
              assembly {
                  if tload(0) { revert(0, 0) }
                  tstore(0, 1)
              }
              _;
              assembly {
                  tstore(0, 0)
              }
          }
      }
      ```
      
      **Cost:** ~200 gas (TSTORE/TLOAD) - 25x cheaper
      
      ## Factory Patterns
      
      ### Basic Factory (CREATE)
      
      ```solidity
      contract TokenFactory {
          address[] public deployedTokens;
      
          event TokenCreated(address indexed token, address indexed creator);
      
          function createToken(string memory name, string memory symbol)
              external
              returns (address)
          {
              Token token = new Token(name, symbol, msg.sender);
              deployedTokens.push(address(token));
              emit TokenCreated(address(token), msg.sender);
              return address(token);
          }
      }
      ```
      
      **Use:** Simple deployments where address doesn't matter
      
      ### Deterministic Factory (CREATE2)
      
      Deploy to predictable addresses across chains:
      
      ```solidity
      contract DeterministicFactory {
          event Deployed(address indexed addr, bytes32 salt);
      
          function deploy(bytes32 salt, bytes memory bytecode)
              external
              returns (address addr)
          {
              assembly {
                  addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
                  if iszero(extcodesize(addr)) { revert(0, 0) }
              }
              emit Deployed(addr, salt);
          }
      
          function computeAddress(bytes32 salt, bytes32 bytecodeHash)
              external
              view
              returns (address)
          {
              return address(uint160(uint256(keccak256(abi.encodePacked(
                  bytes1(0xff),
                  address(this),
                  salt,
                  bytecodeHash
              )))));
          }
      }
      ```
      
      **Use:** Counterfactual deployments, cross-chain consistency
      
      ### Minimal Proxy (ERC-1167 Clones)
      
      Deploy cheap proxies (~45 bytes):
      
      ```solidity
      contract CloneFactory {
          function clone(address implementation) internal returns (address instance) {
              assembly {
                  mstore(0x00, or(
                      shr(0xe8, shl(0x60, implementation)),
                      0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000
                  ))
                  mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                  instance := create(0, 0x09, 0x37)
              }
          }
      }
      ```
      
      **Cost:** ~10K gas vs ~170K for full contract
      **Use:** Token factories, vault templates
      
      ## Payment Patterns
      
      ### Pull Over Push
      
      **Push (Anti-pattern):**
      ```solidity
      // BAD: Can fail if recipient reverts
      function sendRewards() external {
          for (uint256 i = 0; i < recipients.length; i++) {
              payable(recipients[i]).transfer(amounts[i]);
          }
      }
      ```
      
      **Pull (Recommended):**
      ```solidity
      contract PullPayments {
          mapping(address => uint256) public pendingWithdrawals;
      
          function claimReward() external {
              uint256 amount = pendingWithdrawals[msg.sender];
              require(amount > 0, "Nothing to claim");
      
              pendingWithdrawals[msg.sender] = 0;
              (bool success,) = msg.sender.call{value: amount}("");
              require(success, "Transfer failed");
          }
      }
      ```
      
      **Why pull is safer:**
      - User controls when to withdraw
      - Failing transfers don't block protocol
      - Prevents DoS via malicious fallbacks
      
      ## Emergency Controls
      
      ### Pausable Pattern
      
      ```solidity
      contract Pausable {
          bool public paused;
          address public owner;
      
          error ContractPaused();
      
          modifier whenNotPaused() {
              if (paused) revert ContractPaused();
              _;
          }
      
          function pause() external onlyOwner {
              paused = true;
          }
      
          function unpause() external onlyOwner {
              paused = false;
          }
      
          function transfer(address to, uint256 amount) external whenNotPaused {
              // Transfer logic
          }
      }
      ```
      
      **Use:** Security response, gradual rollouts
      
      ## Proxy Patterns
      
      ### UUPS (Recommended)
      
      Implementation controls upgrades:
      
      ```solidity
      import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
      
      contract MyContractV1 is UUPSUpgradeable {
          uint256 public value;
      
          function initialize(uint256 _value) public initializer {
              value = _value;
          }
      
          function _authorizeUpgrade(address newImplementation)
              internal
              override
              onlyOwner
          {}
      }
      ```
      
      ### Transparent Proxy
      
      Admin calls go to proxy, user calls go to implementation:
      
      ```solidity
      // Use OpenZeppelin's TransparentUpgradeableProxy
      import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
      ```
      
      **Trade-offs:**
      - UUPS: Smaller proxy, upgrade logic in implementation
      - Transparent: Larger proxy, clearer admin separation
      
      ## State Machine Pattern
      
      ```solidity
      contract Auction {
          enum Phase { Bidding, Reveal, Finished }
      
          Phase public currentPhase;
          uint256 public phaseDeadline;
      
          error WrongPhase(Phase expected, Phase actual);
      
          modifier atPhase(Phase expected) {
              _checkPhaseTransition();
              if (currentPhase != expected)
                  revert WrongPhase(expected, currentPhase);
              _;
          }
      
          function _checkPhaseTransition() internal {
              if (block.timestamp >= phaseDeadline) {
                  currentPhase = Phase(uint256(currentPhase) + 1);
                  phaseDeadline = block.timestamp + 1 days;
              }
          }
      
          function placeBid(bytes32 hashedBid) external atPhase(Phase.Bidding) {
              // Bidding logic
          }
      
          function revealBid(uint256 value, bytes32 secret) external atPhase(Phase.Reveal) {
              // Reveal logic
          }
      }
      ```
      
      ## Commit-Reveal Scheme
      
      Prevent front-running:
      
      ```solidity
      contract CommitReveal {
          mapping(address => bytes32) public commits;
          mapping(address => uint256) public commitTimes;
      
          uint256 public constant REVEAL_DELAY = 1 hours;
      
          function commit(bytes32 hash) external {
              commits[msg.sender] = hash;
              commitTimes[msg.sender] = block.timestamp;
          }
      
          function reveal(uint256 value, bytes32 salt) external {
              require(block.timestamp >= commitTimes[msg.sender] + REVEAL_DELAY, "Too early");
              require(commits[msg.sender] == keccak256(abi.encode(value, salt, msg.sender)), "Invalid reveal");
      
              delete commits[msg.sender];
              // Process revealed value
          }
      }
      ```
      
      **Use:** Auctions, voting, sealed-bid protocols
      
      ## Timelock Pattern
      
      ```solidity
      contract Timelock {
          uint256 public constant DELAY = 2 days;
      
          mapping(bytes32 => uint256) public queuedTransactions;
      
          function queue(address target, bytes calldata data) external returns (bytes32) {
              bytes32 txHash = keccak256(abi.encode(target, data, block.timestamp));
              queuedTransactions[txHash] = block.timestamp + DELAY;
              return txHash;
          }
      
          function execute(address target, bytes calldata data, bytes32 txHash) external {
              uint256 executeTime = queuedTransactions[txHash];
              require(executeTime != 0, "Not queued");
              require(block.timestamp >= executeTime, "Too early");
      
              delete queuedTransactions[txHash];
              (bool success,) = target.call(data);
              require(success, "Execution failed");
          }
      }
      ```
      
      **Use:** Governance, critical upgrades, parameter changes
      
      ## Error Handling
      
      ### Custom Errors (Preferred)
      
      ```solidity
      error InsufficientBalance(uint256 available, uint256 required);
      error Unauthorized(address caller);
      error ZeroAddress();
      
      function withdraw(uint256 amount) external {
          if (balances[msg.sender] < amount)
              revert InsufficientBalance(balances[msg.sender], amount);
          // ...
      }
      ```
      
      **Advantages:**
      - ~90% gas savings vs require strings
      - Structured error data for debugging
      - Type-safe parameters
      
      ## Initialization Patterns
      
      ### Constructor (Non-upgradeable)
      
      ```solidity
      contract Permanent {
          address public immutable owner;
          uint256 public immutable maxSupply;
      
          constructor(address _owner, uint256 _maxSupply) {
              owner = _owner;
              maxSupply = _maxSupply;
          }
      }
      ```
      
      ### Initializer (Upgradeable)
      
      ```solidity
      contract Upgradeable {
          bool private _initialized;
          address public owner;
      
          modifier initializer() {
              require(!_initialized, "Already initialized");
              _initialized = true;
              _;
          }
      
          function initialize(address _owner) external initializer {
              owner = _owner;
          }
      }
      ```
      
      **Critical:** Constructors don't run on proxies - use initializers
      
      ## Common Pitfalls
      
      ### tx.origin vs msg.sender
      
      ```solidity
      // WRONG: Vulnerable to phishing
      if (tx.origin == owner) { /* ... */ }
      
      // CORRECT
      if (msg.sender == owner) { /* ... */ }
      ```
      
      ### Storage Layout with Upgrades
      
      ```solidity
      // V1
      contract V1 {
          uint256 public value; // slot 0
      }
      
      // V2 - CORRECT: Append only
      contract V2 is V1 {
          uint256 public newValue; // slot 1
      }
      
      // V2 - WRONG: Inserting breaks layout
      contract V2Bad {
          uint256 public newValue; // slot 0 - collision!
          uint256 public value;    // slot 1
      }
      ```
      
      ### Unbounded Loops
      
      ```solidity
      // DANGEROUS: Can run out of gas
      function distributeAll() external {
          for (uint256 i = 0; i < recipients.length; i++) {
              payable(recipients[i]).transfer(amounts[i]);
          }
      }
      
      // SAFE: Batch processing
      function distributeBatch(uint256 start, uint256 end) external {
          require(end <= recipients.length && end - start <= 100);
          for (uint256 i = start; i < end; i++) {
              payable(recipients[i]).transfer(amounts[i]);
          }
      }
      ```
      
      ### Unchecked External Calls
      
      ```solidity
      // WRONG: Return value ignored
      token.transfer(user, amount);
      
      // CORRECT: Check return
      bool success = token.transfer(user, amount);
      require(success, "Transfer failed");
      
      // BEST: Use SafeERC20
      IERC20(token).safeTransfer(user, amount);
      ```
      
    • resources.md 4.5 KB
      # Foundry & Solidity Resources
      
      Authoritative resources for Foundry and Solidity development.
      
      ## Official Documentation
      
      ### Foundry
      - **Foundry Book**: https://book.getfoundry.sh - Comprehensive guide (forge, anvil, cast, chisel)
      - **Foundry GitHub**: https://github.com/foundry-rs/foundry - Source code and issues
      - **forge-std**: https://github.com/foundry-rs/forge-std - Standard library reference
      
      ### Solidity
      - **Solidity Docs**: https://docs.soliditylang.org - Language reference
      - **Solidity GitHub**: https://github.com/ethereum/solidity - Compiler source
      - **Solidity Blog**: https://soliditylang.org/blog - Release notes and features
      
      ### Ethereum
      - **Ethereum.org Developers**: https://ethereum.org/developers - Developer portal
      - **EIPs**: https://eips.ethereum.org - Ethereum Improvement Proposals
      - **Ethereum Stack Exchange**: https://ethereum.stackexchange.com - Q&A community
      
      ## Smart Contract Libraries
      
      ### Production-Ready
      - **OpenZeppelin Contracts**: https://docs.openzeppelin.com/contracts - Audited implementations
        - ERC-20, ERC-721, ERC-1155 tokens
        - Access control, pausable, upgradeable
        - Security utilities
      
      - **Solady**: https://github.com/Vectorized/solady - Gas-optimized utilities
        - Used by Coinbase, Optimism, Uniswap
        - Highly optimized ERC implementations
      
      - **Solmate**: https://github.com/transmissions11/solmate - Minimalist implementations
        - Gas-efficient ERC tokens
        - Auth and utility contracts
      
      ## Security Resources
      
      ### Audit Firms
      - **OpenZeppelin Security**: https://www.openzeppelin.com/security-audits
      - **Trail of Bits**: https://blog.trailofbits.com
      - **Consensys Diligence**: https://consensys.io/diligence
      
      ### Crowdsourced Audits
      - **Code4rena**: https://code4rena.com - Competitive audits
      - **Sherlock**: https://sherlock.xyz - Audit competitions
      - **Immunefi**: https://immunefi.com - Bug bounties
      
      ### Vulnerability Resources
      - **SWC Registry**: https://swcregistry.io - Smart Contract Weaknesses
      - **Rekt News**: https://rekt.news - Exploit post-mortems
      - **DeFiHackLabs**: https://github.com/SunWeb3Sec/DeFiHackLabs - Exploit reproductions
      
      ### Security Tools
      - **Slither**: https://github.com/crytic/slither - Static analysis
      - **Mythril**: https://mythril.ai - Symbolic execution
      - **Echidna**: https://github.com/crytic/echidna - Fuzzing
      - **Certora**: https://www.certora.com - Formal verification
      
      ## Learning Resources
      
      ### Interactive
      - **Solidity by Example**: https://solidity-by-example.org - Runnable code examples
      - **CryptoZombies**: https://cryptozombies.io - Gamified learning
      - **SpeedRun Ethereum**: https://speedrunethereum.com - Hands-on challenges
      
      ### Courses
      - **Cyfrin Updraft**: https://updraft.cyfrin.io - Patrick Collins courses
      - **Alchemy University**: https://university.alchemy.com - Web3 development
      
      ### Patterns & Best Practices
      - **Solidity Patterns**: https://docs.soliditylang.org/en/latest/common-patterns.html
      - **OpenZeppelin Docs**: https://docs.openzeppelin.com/contracts - Implementation guides
      
      ## Development Tools
      
      ### IDEs & Editors
      - **VS Code + Solidity Extension**: https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity
      - **Remix IDE**: https://remix.ethereum.org - Browser-based
      
      ### Testing & Debugging
      - **Tenderly**: https://tenderly.co - Transaction debugging
      - **Phalcon**: https://explorer.phalcon.xyz - Transaction analysis
      
      ### Infrastructure
      - **Alchemy**: https://www.alchemy.com - RPC provider
      - **Infura**: https://www.infura.io - RPC provider
      - **QuickNode**: https://www.quicknode.com - RPC provider
      
      ## Community
      
      ### Discussion
      - **Foundry Telegram**: https://t.me/foundry_rs
      - **Ethereum R&D Discord**: https://discord.gg/ethereum-r-d
      - **OpenZeppelin Forum**: https://forum.openzeppelin.com
      
      ### Research
      - **Paradigm Research**: https://www.paradigm.xyz/writing
      - **a]16z Crypto Research**: https://a16zcrypto.com/research
      
      ## Quick Reference
      
      ### Getting Started Path
      1. **Solidity by Example** - Learn syntax
      2. **Foundry Book** - Learn tooling
      3. **OpenZeppelin Docs** - Learn patterns
      4. **Code4rena Reports** - Learn security
      
      ### Testing Best Practices
      1. **forge-std** - Test utilities
      2. **Foundry Book Testing** - Fuzz and invariant testing
      3. **DeFiHackLabs** - Real exploit patterns
      
      ### Security Path
      1. **OpenZeppelin Audit Readiness** - Preparation
      2. **Trail of Bits Blog** - Deep dives
      3. **Code4rena** - Real audit reports
      
      ## Version Context
      
      Current versions (November 2025):
      - **Foundry**: v1.5.0
      - **Solidity**: 0.8.30
      - **OpenZeppelin Contracts**: v5.x
      - **forge-std**: v1.9.x
      
    • security.md 11.8 KB
      # Solidity Security & Audit Patterns
      
      Security vulnerabilities, defensive patterns, and Foundry testing strategies for smart contract audits.
      
      ## Critical Vulnerabilities
      
      ### Reentrancy
      
      **Risk:** External calls allow recursive entry before state updates, draining contracts.
      
      ```solidity
      // VULNERABLE: State update after external call
      function withdraw(uint256 amount) public {
          require(balances[msg.sender] >= amount);
          (bool sent,) = msg.sender.call{value: amount}("");
          require(sent);
          balances[msg.sender] -= amount; // Too late!
      }
      
      // SECURE: CEI pattern + reentrancy guard
      function withdraw(uint256 amount) public nonReentrant {
          require(balances[msg.sender] >= amount);
          balances[msg.sender] -= amount;    // Effects first
          (bool sent,) = msg.sender.call{value: amount}("");
          require(sent);                      // Interactions last
      }
      ```
      
      **Foundry Test:**
      ```solidity
      contract ReentrancyAttacker {
          Vault vault;
          uint256 count;
      
          receive() external payable {
              if (count++ < 5 && address(vault).balance > 0) {
                  vault.withdraw(1 ether);
              }
          }
      
          function attack() external {
              vault.withdraw(1 ether);
          }
      }
      
      function test_reentrancy_protected() public {
          ReentrancyAttacker attacker = new ReentrancyAttacker(vault);
          deal(address(attacker), 2 ether);
          attacker.deposit{value: 1 ether}();
      
          vm.expectRevert("ReentrancyGuard");
          attacker.attack();
      }
      ```
      
      ### Access Control
      
      **Risk:** Missing permission checks allow unauthorized privileged operations.
      
      ```solidity
      // VULNERABLE: No access control
      function drain() public {
          payable(owner).transfer(address(this).balance);
      }
      
      // VULNERABLE: Using tx.origin
      function authorize(address user) public {
          require(tx.origin == admin); // Phishing vulnerable!
      }
      
      // SECURE: Role-based access
      function drain() external onlyRole(ADMIN_ROLE) {
          payable(msg.sender).transfer(address(this).balance);
      }
      ```
      
      **Foundry Test:**
      ```solidity
      function test_accessControl_unauthorized() public {
          vm.prank(attacker);
          vm.expectRevert(
              abi.encodeWithSelector(
                  AccessControl.AccessControlUnauthorizedAccount.selector,
                  attacker,
                  vault.ADMIN_ROLE()
              )
          );
          vault.drain();
      }
      ```
      
      ### Oracle Manipulation
      
      **Risk:** Manipulated price feeds cause unfair liquidations or loan exploits.
      
      ```solidity
      // VULNERABLE: Spot price (flash-loanable)
      function getPrice() public view returns (uint256) {
          return dex.spotPrice(token);
      }
      
      // SECURE: Chainlink with staleness check
      function getPrice() public view returns (int256) {
          (
              uint80 roundId,
              int256 price,
              ,
              uint256 updatedAt,
              uint80 answeredInRound
          ) = priceFeed.latestRoundData();
      
          require(price > 0, "Invalid price");
          require(answeredInRound >= roundId, "Stale round");
          require(block.timestamp - updatedAt < 3600, "Price too old");
      
          return price;
      }
      ```
      
      **Foundry Test:**
      ```solidity
      function test_oracle_staleness() public {
          vm.warp(block.timestamp + 4000); // Past staleness threshold
      
          vm.expectRevert("Price too old");
          oracle.getPrice();
      }
      ```
      
      ### Integer Overflow/Underflow
      
      Solidity 0.8+ has built-in checks, but watch for:
      
      ```solidity
      // VULNERABLE: Unchecked block disables protection
      unchecked {
          balance -= amount; // Can underflow!
      }
      
      // VULNERABLE: Type casting
      uint8 small = uint8(largeNumber); // Truncates silently
      
      // SECURE: Explicit checks
      require(balance >= amount, "Insufficient balance");
      unchecked { balance -= amount; }
      ```
      
      ### Front-Running / MEV
      
      **Risk:** Attackers see pending transactions and extract value.
      
      **Mitigations:**
      - Commit-reveal schemes for sensitive operations
      - Flashbots for transaction privacy
      - Slippage protection in DEX trades
      - Batch auctions instead of first-come-first-served
      
      ```solidity
      // Commit-reveal pattern
      function commit(bytes32 hash) external {
          commits[msg.sender] = hash;
          commitTime[msg.sender] = block.timestamp;
      }
      
      function reveal(uint256 value, bytes32 salt) external {
          require(block.timestamp >= commitTime[msg.sender] + 1 hours);
          require(commits[msg.sender] == keccak256(abi.encode(value, salt)));
          // Process value
      }
      ```
      
      ### Flash Loan Attacks
      
      **Risk:** Attackers borrow large amounts to manipulate protocol state within single transaction.
      
      **Mitigations:**
      - Use TWAP instead of spot prices
      - Require multi-block operations
      - Over-collateralization requirements
      - Block flash loans if not needed
      
      ### Signature Malleability
      
      **Risk:** Attackers modify signatures to replay transactions.
      
      ```solidity
      // VULNERABLE: No nonce or deadline
      function permit(address owner, uint256 value, bytes calldata sig) external {
          // Can be replayed!
      }
      
      // SECURE: EIP-712 with nonce and deadline
      function permit(
          address owner,
          address spender,
          uint256 value,
          uint256 deadline,
          uint8 v, bytes32 r, bytes32 s
      ) external {
          require(block.timestamp <= deadline, "Expired");
          // Verify EIP-712 signature with nonce
          nonces[owner]++;
      }
      ```
      
      ### Storage Collision (Proxies)
      
      **Risk:** Proxy and implementation have different storage layouts.
      
      ```solidity
      // WRONG: Storage mismatch
      contract Proxy {
          address implementation; // slot 0
          address owner;          // slot 1
      }
      contract Implementation {
          address owner;          // slot 0 - COLLISION!
      }
      
      // CORRECT: Use EIP-1967 storage slots
      bytes32 constant IMPL_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
      ```
      
      ### Denial of Service
      
      **Patterns:**
      - Unbounded loops exhausting gas
      - External calls that always revert
      - Block gas limit exceeded
      
      ```solidity
      // VULNERABLE: Unbounded loop
      function distributeAll() external {
          for (uint256 i = 0; i < users.length; i++) {
              payable(users[i]).transfer(rewards[i]);
          }
      }
      
      // SECURE: Paginated distribution
      function distributeBatch(uint256 start, uint256 count) external {
          require(count <= 100, "Batch too large");
          uint256 end = start + count;
          if (end > users.length) end = users.length;
      
          for (uint256 i = start; i < end; i++) {
              payable(users[i]).transfer(rewards[i]);
          }
      }
      ```
      
      ## Foundry Security Testing
      
      ### Fuzz Testing
      
      Generate random inputs to find edge cases:
      
      ```solidity
      function testFuzz_transfer(address to, uint256 amount) public {
          vm.assume(to != address(0));
          vm.assume(to != address(token));
          amount = bound(amount, 0, token.balanceOf(address(this)));
      
          uint256 balanceBefore = token.balanceOf(to);
          token.transfer(to, amount);
      
          assertEq(token.balanceOf(to), balanceBefore + amount);
      }
      ```
      
      **Configuration:**
      ```toml
      [fuzz]
      runs = 10000
      seed = "0x1234"
      max_test_rejects = 65536
      ```
      
      ### Invariant Testing
      
      Test properties that must always hold:
      
      ```solidity
      contract VaultInvariant is Test {
          Vault vault;
          VaultHandler handler;
      
          function setUp() public {
              vault = new Vault();
              handler = new VaultHandler(vault);
              targetContract(address(handler));
          }
      
          // Total deposits must equal total shares value
          function invariant_solvency() public view {
              assertGe(
                  vault.totalAssets(),
                  vault.totalSupply(),
                  "Vault insolvent"
              );
          }
      
          // Sum of balances equals total supply
          function invariant_balanceSum() public view {
              uint256 sum = 0;
              address[] memory users = handler.getUsers();
              for (uint256 i = 0; i < users.length; i++) {
                  sum += vault.balanceOf(users[i]);
              }
              assertEq(sum, vault.totalSupply());
          }
      }
      ```
      
      ### Handler Pattern
      
      Control fuzz inputs and track state:
      
      ```solidity
      contract VaultHandler is Test {
          Vault vault;
          address[] public users;
          uint256 public ghost_totalDeposited;
      
          constructor(Vault _vault) {
              vault = _vault;
              users.push(makeAddr("alice"));
              users.push(makeAddr("bob"));
          }
      
          function deposit(uint256 userSeed, uint256 amount) external {
              address user = users[bound(userSeed, 0, users.length - 1)];
              amount = bound(amount, 1, 1e24);
      
              deal(address(vault.asset()), user, amount);
      
              vm.startPrank(user);
              vault.asset().approve(address(vault), amount);
              vault.deposit(amount, user);
              vm.stopPrank();
      
              ghost_totalDeposited += amount;
          }
      
          function withdraw(uint256 userSeed, uint256 shares) external {
              address user = users[bound(userSeed, 0, users.length - 1)];
              shares = bound(shares, 0, vault.balanceOf(user));
              if (shares == 0) return;
      
              vm.prank(user);
              uint256 assets = vault.redeem(shares, user, user);
      
              ghost_totalDeposited -= assets;
          }
      }
      ```
      
      **Configuration:**
      ```toml
      [invariant]
      runs = 256
      depth = 100
      fail_on_revert = false
      ```
      
      ### Fork Testing
      
      Test against real mainnet state:
      
      ```solidity
      function setUp() public {
          vm.createSelectFork(vm.envString("MAINNET_RPC_URL"), 18000000);
      }
      
      function testFork_usdcIntegration() public {
          IERC20 usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
          address whale = 0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503;
      
          uint256 balanceBefore = usdc.balanceOf(address(this));
      
          vm.prank(whale);
          usdc.transfer(address(this), 1_000_000e6);
      
          assertEq(usdc.balanceOf(address(this)), balanceBefore + 1_000_000e6);
      }
      ```
      
      ### Symbolic Execution (Halmos)
      
      Prove properties mathematically:
      
      ```solidity
      function check_transferPreservesSupply(address from, address to, uint256 amount) public {
          uint256 supplyBefore = token.totalSupply();
      
          token.transfer(from, to, amount);
      
          assert(token.totalSupply() == supplyBefore);
      }
      ```
      
      Run with: `halmos --contract MyTest`
      
      ## Pre-Audit Checklist
      
      ### Code Quality
      - [ ] NatSpec documentation on all public functions
      - [ ] No console.log or debug statements
      - [ ] All TODO/FIXME resolved
      - [ ] Consistent naming conventions
      
      ### Security Patterns
      - [ ] Reentrancy guards on external-calling functions
      - [ ] CEI pattern followed
      - [ ] Access control on privileged functions
      - [ ] No tx.origin for authentication
      - [ ] SafeERC20 for token transfers
      - [ ] Input validation on all parameters
      
      ### Testing
      - [ ] >95% code coverage
      - [ ] Fuzz tests with 10,000+ runs
      - [ ] Invariant tests for core properties
      - [ ] Edge case tests (zero, max values)
      - [ ] Fork tests for integrations
      
      ### Static Analysis
      - [ ] Slither: no HIGH/CRITICAL issues
      - [ ] Manual review of MEDIUM issues
      
      ### Documentation
      - [ ] README with security assumptions
      - [ ] Threat model documented
      - [ ] Known limitations listed
      - [ ] Admin functions documented
      
      ## Common Audit Findings
      
      ### Missing Return Value Check
      
      ```solidity
      // BAD
      token.transfer(user, amount);
      
      // GOOD
      require(token.transfer(user, amount), "Transfer failed");
      
      // BEST
      IERC20(token).safeTransfer(user, amount);
      ```
      
      ### Uninitialized Proxy
      
      ```solidity
      // BAD: Anyone can initialize
      function initialize(address _owner) external {
          owner = _owner;
      }
      
      // GOOD: Use initializer modifier
      function initialize(address _owner) external initializer {
          owner = _owner;
      }
      ```
      
      ### Precision Loss
      
      ```solidity
      // BAD: Division before multiplication
      uint256 share = (amount / total) * balance; // Rounds to 0
      
      // GOOD: Multiplication before division
      uint256 share = (amount * balance) / total;
      ```
      
      ### Unchecked Array Access
      
      ```solidity
      // BAD
      return users[index]; // Can revert
      
      // GOOD
      require(index < users.length, "Out of bounds");
      return users[index];
      ```
      
      ## Security Tools
      
      **Static Analysis:**
      - Slither: `slither . --json report.json`
      - Mythril: `myth analyze contracts/Vault.sol`
      
      **Fuzzing:**
      - Forge fuzz: Built-in
      - Echidna: Property-based fuzzing
      
      **Formal Verification:**
      - Certora Prover
      - Halmos
      
      ## Foundry Security Commands
      
      ```bash
      # Run all tests with coverage
      forge coverage --report lcov
      
      # Fuzz with high iterations
      forge test --fuzz-runs 50000
      
      # Invariant testing
      forge test --match-contract Invariant -vv
      
      # Fork testing
      forge test --fork-url $RPC_URL
      
      # Gas analysis
      forge test --gas-report
      
      # Debug failing test
      forge test --match-test testName -vvvv
      ```
      
    • solidity-modern.md 14.7 KB
      # Modern Solidity (0.8.30)
      
      Complete guide to Solidity 0.8.20-0.8.30 features, gas optimization, and security patterns.
      
      ## Version Feature Summary
      
      | Version | Key Features |
      |---------|-------------|
      | 0.8.30 | Prague EVM default, NatSpec for enums |
      | 0.8.29 | Custom storage layout (`layout at`) |
      | 0.8.28 | Transient storage for value types |
      | 0.8.27 | Transient storage parser support |
      | 0.8.26 | `require(bool, Error)` custom errors |
      | 0.8.25 | Cancun EVM default, MCOPY opcode |
      | 0.8.24 | `blobbasefee`, `blobhash()`, tload/tstore in Yul |
      | 0.8.22 | File-level events |
      | 0.8.21 | Relaxed immutable initialization |
      | 0.8.20 | Shanghai EVM default, PUSH0 |
      | 0.8.19 | Custom operators for user-defined types |
      | 0.8.18 | Named mapping parameters |
      
      ## Custom Errors (0.8.4+)
      
      Gas-efficient error handling.
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.30;
      
      error Unauthorized(address caller, address required);
      error InsufficientBalance(uint256 available, uint256 required);
      error InvalidAmount();
      error Expired(uint256 deadline, uint256 current);
      
      contract Token {
          mapping(address => uint256) public balanceOf;
          address public owner;
      
          constructor() {
              owner = msg.sender;
          }
      
          function transfer(address to, uint256 amount) external {
              if (amount == 0) revert InvalidAmount();
              if (amount > balanceOf[msg.sender]) {
                  revert InsufficientBalance(balanceOf[msg.sender], amount);
              }
      
              balanceOf[msg.sender] -= amount;
              balanceOf[to] += amount;
          }
      
          function adminFunction() external {
              if (msg.sender != owner) {
                  revert Unauthorized(msg.sender, owner);
              }
              // ...
          }
      }
      ```
      
      ### require with Custom Errors (0.8.26+)
      
      ```solidity
      function transfer(address to, uint256 amount) external {
          require(amount > 0, InvalidAmount());
          require(
              amount <= balanceOf[msg.sender],
              InsufficientBalance(balanceOf[msg.sender], amount)
          );
      
          balanceOf[msg.sender] -= amount;
          balanceOf[to] += amount;
      }
      ```
      
      ### Gas Comparison
      
      ```solidity
      // String error: ~4 bytes selector + string data
      require(x > 0, "Amount must be greater than zero");
      
      // Custom error: ~4 bytes selector only
      if (x == 0) revert InvalidAmount();
      
      // Custom error with params: ~4 bytes + encoded params
      if (x > balance) revert InsufficientBalance(balance, x);
      
      // Savings: 50-200+ gas per error
      ```
      
      ## Transient Storage (0.8.28+)
      
      Cheap temporary storage cleared after transaction.
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.28;
      
      contract ReentrancyGuard {
          bool transient locked;
      
          modifier nonReentrant() {
              require(!locked, "Reentrancy");
              locked = true;
              _;
              locked = false; // CRITICAL: Reset for composability
          }
      
          function withdraw() external nonReentrant {
              // Safe from reentrancy
          }
      }
      ```
      
      ### Flash Loan Example
      
      ```solidity
      contract FlashLender {
          IERC20 public token;
          bool transient flashLoanActive;
      
          function flashLoan(uint256 amount, address receiver) external {
              require(!flashLoanActive, "Flash loan active");
              flashLoanActive = true;
      
              uint256 balanceBefore = token.balanceOf(address(this));
              token.transfer(receiver, amount);
      
              IFlashBorrower(receiver).onFlashLoan(amount);
      
              require(
                  token.balanceOf(address(this)) >= balanceBefore,
                  "Flash loan not repaid"
              );
      
              flashLoanActive = false;
          }
      }
      ```
      
      ### Gas Comparison
      
      | Operation | Persistent Storage | Transient Storage |
      |-----------|-------------------|-------------------|
      | First write | 20,000+ gas | 100 gas |
      | Subsequent write | 2,900 gas | 100 gas |
      | Read | 100 gas | 100 gas |
      
      **Important**: Always reset transient storage at function exit for composability.
      
      ## Immutable Variables (Relaxed in 0.8.21+)
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.21;
      
      contract Config {
          address public immutable owner;
          uint256 public immutable deployTime;
          bytes32 public immutable configHash;
          uint256 public immutable maxSupply;
      
          constructor(uint256 _maxSupply, bytes32 _configHash) {
              owner = msg.sender;
              deployTime = block.timestamp;
      
              // Relaxed: can read/write immutables anywhere in constructor
              if (_maxSupply == 0) {
                  maxSupply = 1_000_000e18;
              } else {
                  maxSupply = _maxSupply;
              }
      
              // Can use other immutables
              configHash = _configHash != bytes32(0)
                  ? _configHash
                  : keccak256(abi.encode(owner, maxSupply));
          }
      }
      ```
      
      ### Gas Benefits
      
      - Immutable: **3 gas** (value inlined)
      - Storage: **100 gas** (SLOAD)
      - Constant: **0 gas** (compile-time constant)
      
      ## User-Defined Types with Operators (0.8.19+)
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      
      type Amount is uint256;
      type Price is uint256;
      
      using {add as +, sub as -, mul, eq as ==, lt as <} for Amount global;
      
      function add(Amount a, Amount b) pure returns (Amount) {
          return Amount.wrap(Amount.unwrap(a) + Amount.unwrap(b));
      }
      
      function sub(Amount a, Amount b) pure returns (Amount) {
          return Amount.wrap(Amount.unwrap(a) - Amount.unwrap(b));
      }
      
      function mul(Amount a, Price p) pure returns (uint256) {
          return Amount.unwrap(a) * Price.unwrap(p);
      }
      
      function eq(Amount a, Amount b) pure returns (bool) {
          return Amount.unwrap(a) == Amount.unwrap(b);
      }
      
      function lt(Amount a, Amount b) pure returns (bool) {
          return Amount.unwrap(a) < Amount.unwrap(b);
      }
      
      contract TypeSafe {
          function calculate(Amount a, Amount b, Price p) external pure returns (uint256) {
              Amount total = a + b;
              if (total < Amount.wrap(100)) {
                  return 0;
              }
              return total.mul(p);
          }
      }
      ```
      
      ## Named Mapping Parameters (0.8.18+)
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.18;
      
      contract Token {
          // Highly readable
          mapping(address account => uint256 balance) public balanceOf;
          mapping(address owner => mapping(address spender => uint256 amount)) public allowance;
      
          // Complex mappings
          mapping(uint256 tokenId => mapping(address operator => bool approved)) public operatorApproval;
      }
      ```
      
      ## File-Level Events (0.8.22+)
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.22;
      
      // File-level event
      event Transfer(address indexed from, address indexed to, uint256 value);
      event Approval(address indexed owner, address indexed spender, uint256 value);
      
      interface IERC20 {
          function transfer(address to, uint256 amount) external returns (bool);
      }
      
      contract Token is IERC20 {
          mapping(address => uint256) public balanceOf;
      
          function transfer(address to, uint256 amount) external returns (bool) {
              balanceOf[msg.sender] -= amount;
              balanceOf[to] += amount;
              emit Transfer(msg.sender, to, amount);
              return true;
          }
      }
      ```
      
      ## Custom Storage Layout (0.8.29+)
      
      For EIP-7702 delegate implementations:
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.29;
      
      // Delegate 1: Storage starts at 0x1000
      contract DelegateA layout at 0x1000 {
          uint256 public valueA;  // Slot 0x1000
      
          function setA(uint256 v) external {
              valueA = v;
          }
      }
      
      // Delegate 2: Storage starts at 0x2000
      contract DelegateB layout at 0x2000 {
          uint256 public valueB;  // Slot 0x2000
      
          function setB(uint256 v) external {
              valueB = v;
          }
      }
      
      // Both can be delegated to from same account without storage collision
      ```
      
      ## Checked vs Unchecked Arithmetic
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.30;
      
      contract Arithmetic {
          // Default: checked (reverts on overflow)
          function checkedAdd(uint256 a, uint256 b) public pure returns (uint256) {
              return a + b; // Reverts if overflow
          }
      
          // Explicit: unchecked (wraps on overflow)
          function uncheckedIncrement(uint256 i) public pure returns (uint256) {
              unchecked {
                  return i + 1; // Does not revert
              }
          }
      
          // Common pattern: loop counter
          function sum(uint256[] calldata arr) public pure returns (uint256 total) {
              for (uint256 i = 0; i < arr.length;) {
                  total += arr[i];
                  unchecked { ++i; } // Safe: i < arr.length guarantees no overflow
              }
          }
      }
      ```
      
      ### When to Use Unchecked
      
      Safe scenarios:
      - Loop counters bounded by array length
      - Incrementing from known safe values
      - Subtracting after explicit bounds check
      - Timestamp/block number arithmetic
      
      Never use unchecked:
      - User-provided inputs without validation
      - Token amounts in DeFi
      - Any value that could overflow
      
      ## Gas Optimization Techniques
      
      ### 1. Storage Packing
      
      ```solidity
      // Bad: 3 slots
      contract Unpacked {
          uint256 a;  // Slot 0
          uint128 b;  // Slot 1
          uint128 c;  // Slot 2
      }
      
      // Good: 2 slots
      contract Packed {
          uint256 a;  // Slot 0
          uint128 b;  // Slot 1 (first half)
          uint128 c;  // Slot 1 (second half)
      }
      ```
      
      ### 2. Caching Storage in Memory
      
      ```solidity
      // Bad: Multiple SLOADs
      function bad(uint256 amount) external {
          require(balances[msg.sender] >= amount);
          balances[msg.sender] -= amount;
          emit Transfer(msg.sender, amount, balances[msg.sender]);
      }
      
      // Good: Cache storage
      function good(uint256 amount) external {
          uint256 balance = balances[msg.sender];
          require(balance >= amount);
          uint256 newBalance = balance - amount;
          balances[msg.sender] = newBalance;
          emit Transfer(msg.sender, amount, newBalance);
      }
      ```
      
      ### 3. Use calldata for Read-Only Arrays
      
      ```solidity
      // Bad: Copies to memory
      function bad(uint256[] memory data) external { }
      
      // Good: Direct calldata access
      function good(uint256[] calldata data) external { }
      ```
      
      ### 4. Short-Circuit Conditionals
      
      ```solidity
      // Cheaper checks first
      function check(uint256 amount, address user) external view {
          // Cheap: comparison
          // Expensive: storage read
          if (amount > 0 && balances[user] >= amount) {
              // ...
          }
      }
      ```
      
      ### 5. Use Constants and Immutables
      
      ```solidity
      // Constant: 0 gas (inlined at compile time)
      uint256 constant MAX_SUPPLY = 1_000_000e18;
      
      // Immutable: 3 gas (inlined in bytecode)
      address immutable owner;
      
      // Storage: 100 gas (SLOAD)
      address _owner;
      ```
      
      ### 6. Increment Patterns
      
      ```solidity
      // Most efficient
      unchecked { ++i; }
      
      // Less efficient
      unchecked { i++; }
      
      // Least efficient (checked)
      i++;
      ```
      
      ### Gas Costs Reference
      
      | Operation | Gas Cost |
      |-----------|----------|
      | SLOAD (warm) | 100 |
      | SLOAD (cold) | 2,100 |
      | SSTORE (zero to non-zero) | 20,000 |
      | SSTORE (non-zero to non-zero) | 2,900 |
      | SSTORE (non-zero to zero) | Refund 4,800 |
      | TLOAD | 100 |
      | TSTORE | 100 |
      | Memory read/write | 3 |
      | Calldata read | 3 |
      
      ## Security Best Practices
      
      ### 1. Checks-Effects-Interactions
      
      ```solidity
      function withdraw(uint256 amount) external {
          // CHECKS
          require(amount <= balances[msg.sender], "Insufficient");
      
          // EFFECTS
          balances[msg.sender] -= amount;
      
          // INTERACTIONS
          (bool success,) = msg.sender.call{value: amount}("");
          require(success, "Transfer failed");
      }
      ```
      
      ### 2. Reentrancy Protection
      
      ```solidity
      // Using transient storage (0.8.28+)
      bool transient locked;
      
      modifier nonReentrant() {
          require(!locked);
          locked = true;
          _;
          locked = false;
      }
      
      // Or OpenZeppelin ReentrancyGuard
      ```
      
      ### 3. Access Control
      
      ```solidity
      error Unauthorized();
      
      modifier onlyOwner() {
          if (msg.sender != owner) revert Unauthorized();
          _;
      }
      
      // Use OpenZeppelin AccessControl for complex permissions
      ```
      
      ### 4. Safe External Calls
      
      ```solidity
      // Check return value
      (bool success, bytes memory data) = target.call(payload);
      require(success, "Call failed");
      
      // Or use SafeCall pattern
      function safeTransferETH(address to, uint256 amount) internal {
          (bool success,) = to.call{value: amount}("");
          if (!success) revert TransferFailed();
      }
      ```
      
      ### 5. Input Validation
      
      ```solidity
      function deposit(uint256 amount) external {
          if (amount == 0) revert InvalidAmount();
          if (amount > MAX_DEPOSIT) revert ExceedsMax(amount, MAX_DEPOSIT);
      
          // Proceed
      }
      ```
      
      ### 6. Integer Overflow Protection
      
      ```solidity
      // Built-in since 0.8.0, but be careful with unchecked
      function safeAdd(uint256 a, uint256 b) internal pure returns (uint256) {
          // This reverts on overflow (default behavior)
          return a + b;
      }
      ```
      
      ### 7. Timestamp Dependence
      
      ```solidity
      // Don't use for randomness
      // Small manipulations possible by miners
      
      // OK for time-based logic with tolerance
      require(block.timestamp >= deadline, "Not yet");
      
      // Bad: exact timing
      require(block.timestamp == exactTime, "Wrong time");
      ```
      
      ## EVM Version Features
      
      ### Prague (0.8.30 default)
      
      - All Cancun features
      - EIP-7702 preparation
      
      ### Cancun (0.8.25 default)
      
      - Transient storage (TLOAD, TSTORE)
      - MCOPY for memory copying
      - BLOBHASH, BLOBBASEFEE
      
      ### Shanghai (0.8.20 default)
      
      - PUSH0 opcode
      
      ### Block Properties by Version
      
      ```solidity
      // Always available
      block.number
      block.timestamp
      block.chainid
      block.coinbase
      block.gaslimit
      
      // Paris+
      block.prevrandao  // Replaces block.difficulty
      
      // Cancun+
      block.blobbasefee
      blobhash(index)
      ```
      
      ## Common Patterns
      
      ### Factory Pattern
      
      ```solidity
      contract Factory {
          event Created(address indexed instance);
      
          function create(bytes32 salt, bytes calldata initData) external returns (address) {
              address instance = address(new Contract{salt: salt}(initData));
              emit Created(instance);
              return instance;
          }
      
          function predictAddress(bytes32 salt, bytes calldata initData) external view returns (address) {
              bytes32 hash = keccak256(abi.encodePacked(
                  bytes1(0xff),
                  address(this),
                  salt,
                  keccak256(abi.encodePacked(
                      type(Contract).creationCode,
                      abi.encode(initData)
                  ))
              ));
              return address(uint160(uint256(hash)));
          }
      }
      ```
      
      ### Minimal Proxy (EIP-1167)
      
      ```solidity
      function clone(address implementation) internal returns (address instance) {
          assembly {
              let ptr := mload(0x40)
              mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
              mstore(add(ptr, 0x14), shl(0x60, implementation))
              mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
              instance := create(0, ptr, 0x37)
          }
          require(instance != address(0), "Clone failed");
      }
      ```
      
      ### Merkle Proof Verification
      
      ```solidity
      function verify(
          bytes32[] calldata proof,
          bytes32 root,
          bytes32 leaf
      ) public pure returns (bool) {
          bytes32 computedHash = leaf;
          for (uint256 i = 0; i < proof.length;) {
              bytes32 proofElement = proof[i];
              if (computedHash <= proofElement) {
                  computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
              } else {
                  computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
              }
              unchecked { ++i; }
          }
          return computedHash == root;
      }
      ```
      
    • testing.md 14.3 KB
      # Foundry Testing Guide
      
      Complete guide to testing smart contracts with Forge.
      
      ## Test Structure
      
      ### File Conventions
      
      - Test files: `ContractName.t.sol`
      - Script files: `ScriptName.s.sol`
      - Test contracts inherit from `Test`
      
      ### Function Prefixes
      
      | Prefix | Purpose |
      |--------|---------|
      | `test_` | Unit test |
      | `testFuzz_` | Fuzz test |
      | `testFork_` | Fork test |
      | `invariant_` | Invariant test |
      | `test_RevertIf_` | Expected revert |
      | `test_RevertWhen_` | Expected revert |
      
      ### Basic Test Contract
      
      ```solidity
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.30;
      
      import {Test, console} from "forge-std/Test.sol";
      import {Vault} from "../src/Vault.sol";
      
      contract VaultTest is Test {
          Vault public vault;
          address public alice;
          address public bob;
      
          function setUp() public {
              vault = new Vault();
              alice = makeAddr("alice");
              bob = makeAddr("bob");
              deal(alice, 100 ether);
              deal(bob, 100 ether);
          }
      
          function test_Deposit() public {
              vm.prank(alice);
              vault.deposit{value: 1 ether}();
              assertEq(vault.balanceOf(alice), 1 ether);
          }
      }
      ```
      
      ## Unit Testing
      
      ### Assertions
      
      ```solidity
      // Equality
      assertEq(actual, expected);
      assertEq(actual, expected, "custom message");
      assertNotEq(actual, expected);
      
      // Comparisons
      assertGt(a, b);    // a > b
      assertGe(a, b);    // a >= b
      assertLt(a, b);    // a < b
      assertLe(a, b);    // a <= b
      
      // Boolean
      assertTrue(condition);
      assertFalse(condition);
      
      // Approximation (for rounding)
      assertApproxEqAbs(actual, expected, maxDelta);
      assertApproxEqRel(actual, expected, maxPercentDelta); // 1e18 = 100%
      
      // Arrays
      assertEq(arr1, arr2);
      ```
      
      ### Testing Reverts
      
      ```solidity
      // Expect any revert
      vm.expectRevert();
      vault.withdraw(1000 ether);
      
      // Expect specific message
      vm.expectRevert("Insufficient balance");
      vault.withdraw(1000 ether);
      
      // Expect custom error (selector only)
      vm.expectRevert(InsufficientBalance.selector);
      vault.withdraw(1000 ether);
      
      // Expect custom error with parameters
      vm.expectRevert(
          abi.encodeWithSelector(
              InsufficientBalance.selector,
              0,      // available
              1000    // required
          )
      );
      vault.withdraw(1000 ether);
      
      // Partial revert (match selector only)
      vm.expectPartialRevert(InsufficientBalance.selector);
      vault.withdraw(1000 ether);
      ```
      
      ### Testing Events
      
      ```solidity
      // Expect event emission
      vm.expectEmit(true, true, false, true);
      //            topic1, topic2, topic3, data
      emit Transfer(alice, bob, 100);
      
      // Call that should emit the event
      token.transfer(bob, 100);
      
      // Record and inspect logs
      vm.recordLogs();
      token.transfer(bob, 100);
      Vm.Log[] memory logs = vm.getRecordedLogs();
      
      assertEq(logs[0].topics[0], keccak256("Transfer(address,address,uint256)"));
      ```
      
      ### Testing Access Control
      
      ```solidity
      function test_OnlyOwner() public {
          // Should succeed as owner
          vault.adminFunction();
      
          // Should fail as non-owner
          vm.expectRevert("Ownable: caller is not the owner");
          vm.prank(alice);
          vault.adminFunction();
      }
      ```
      
      ## Fuzz Testing
      
      Forge automatically generates random inputs for test parameters.
      
      ### Basic Fuzz Test
      
      ```solidity
      function testFuzz_Deposit(uint256 amount) public {
          // Bound input to valid range
          amount = bound(amount, 0.01 ether, 100 ether);
      
          deal(alice, amount);
          vm.prank(alice);
          vault.deposit{value: amount}();
      
          assertEq(vault.balanceOf(alice), amount);
      }
      ```
      
      ### Fuzz Configuration
      
      ```toml
      # foundry.toml
      [fuzz]
      runs = 256              # Number of fuzz runs
      seed = 42               # Deterministic seed (optional)
      max_test_rejects = 65536
      ```
      
      ### Fixtures (Pre-defined Values)
      
      ```solidity
      // Array fixture (must match parameter name)
      uint256[] public fixtureAmount = [0, 1, 100, type(uint256).max];
      
      // Function fixture
      function fixtureUser() public returns (address[] memory) {
          address[] memory users = new address[](3);
          users[0] = makeAddr("alice");
          users[1] = makeAddr("bob");
          users[2] = address(0);
          return users;
      }
      
      function testFuzz_Transfer(uint256 amount, address user) public {
          // amount will be one of: 0, 1, 100, max
          // user will be one of: alice, bob, address(0)
      }
      ```
      
      ### Using `vm.assume()`
      
      ```solidity
      function testFuzz_Transfer(address recipient) public {
          // Skip invalid inputs
          vm.assume(recipient != address(0));
          vm.assume(recipient != address(token));
      
          // Or use helper
          assumeNotZeroAddress(recipient);
          assumeNotPrecompile(recipient);
      }
      ```
      
      ## Invariant Testing
      
      Test that properties hold across random function call sequences.
      
      ### Basic Invariant Test
      
      ```solidity
      contract VaultInvariantTest is Test {
          Vault public vault;
          VaultHandler public handler;
      
          function setUp() public {
              vault = new Vault();
              handler = new VaultHandler(vault);
      
              // Only fuzz the handler
              targetContract(address(handler));
          }
      
          function invariant_SolvencyCheck() public view {
              assertGe(
                  address(vault).balance,
                  vault.totalDeposits(),
                  "Vault is insolvent"
              );
          }
      
          function invariant_TotalSupplyConsistent() public view {
              assertEq(
                  vault.totalSupply(),
                  sumAllBalances(),
                  "Supply mismatch"
              );
          }
      }
      ```
      
      ### Handler Pattern
      
      Wrap target contract to bound inputs and track state:
      
      ```solidity
      contract VaultHandler is Test {
          Vault public vault;
          uint256 public ghost_totalDeposited;
          address[] public actors;
      
          constructor(Vault _vault) {
              vault = _vault;
              actors.push(makeAddr("alice"));
              actors.push(makeAddr("bob"));
              actors.push(makeAddr("charlie"));
          }
      
          function deposit(uint256 actorIndex, uint256 amount) external {
              // Bound inputs
              actorIndex = bound(actorIndex, 0, actors.length - 1);
              amount = bound(amount, 0.01 ether, 10 ether);
      
              address actor = actors[actorIndex];
              deal(actor, amount);
      
              vm.prank(actor);
              vault.deposit{value: amount}();
      
              // Track ghost variable
              ghost_totalDeposited += amount;
          }
      
          function withdraw(uint256 actorIndex, uint256 amount) external {
              actorIndex = bound(actorIndex, 0, actors.length - 1);
              address actor = actors[actorIndex];
      
              uint256 balance = vault.balanceOf(actor);
              amount = bound(amount, 0, balance);
      
              vm.prank(actor);
              vault.withdraw(amount);
      
              ghost_totalDeposited -= amount;
          }
      }
      ```
      
      ### Target Configuration
      
      ```solidity
      function setUp() public {
          // Only fuzz specific contracts
          targetContract(address(handler));
      
          // Exclude contracts from fuzzing
          excludeContract(address(vault));
          excludeContract(address(token));
      
          // Only use specific senders
          targetSender(alice);
          targetSender(bob);
      
          // Exclude specific senders
          excludeSender(address(0));
      
          // Target specific functions
          bytes4[] memory selectors = new bytes4[](2);
          selectors[0] = handler.deposit.selector;
          selectors[1] = handler.withdraw.selector;
          targetSelector(FuzzSelector({
              addr: address(handler),
              selectors: selectors
          }));
      }
      ```
      
      ### Invariant Configuration
      
      ```toml
      # foundry.toml
      [invariant]
      runs = 256              # Number of sequences
      depth = 50              # Calls per sequence
      fail_on_revert = false  # Continue on reverts
      shrink_run_limit = 5000 # Shrinking iterations
      ```
      
      ## Fork Testing
      
      Test against live blockchain state.
      
      ### Basic Fork Test
      
      ```solidity
      contract ForkTest is Test {
          uint256 mainnetFork;
          address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
          address constant WHALE = 0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503;
      
          function setUp() public {
              mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL"));
              vm.selectFork(mainnetFork);
          }
      
          function testFork_USDCTransfer() public {
              vm.prank(WHALE);
              IERC20(USDC).transfer(address(this), 1000e6);
              assertEq(IERC20(USDC).balanceOf(address(this)), 1000e6);
          }
      }
      ```
      
      ### Fork Cheatcodes
      
      ```solidity
      // Create fork
      uint256 forkId = vm.createFork("https://eth-mainnet.alchemyapi.io/v2/...");
      uint256 forkId = vm.createFork("mainnet"); // Uses foundry.toml rpc_endpoints
      uint256 forkId = vm.createFork("mainnet", 18000000); // Pin to block
      
      // Create and select in one call
      vm.createSelectFork("mainnet");
      
      // Switch between forks
      vm.selectFork(mainnetFork);
      vm.selectFork(arbitrumFork);
      
      // Get active fork
      uint256 active = vm.activeFork();
      
      // Roll fork to different block
      vm.rollFork(18500000);
      vm.rollFork(arbitrumFork, 150000000);
      
      // Make account persist across forks
      vm.makePersistent(address(myContract));
      vm.makePersistent(alice, bob, charlie);
      
      // Check persistence
      bool isPersistent = vm.isPersistent(address(myContract));
      
      // Revoke persistence
      vm.revokePersistent(address(myContract));
      ```
      
      ### Multi-Fork Testing
      
      ```solidity
      function testFork_CrossChain() public {
          // Deploy on mainnet
          vm.selectFork(mainnetFork);
          address mainnetToken = address(new Token());
      
          // Deploy on Arbitrum
          vm.selectFork(arbitrumFork);
          address arbitrumToken = address(new Token());
      
          // Verify different deployments
          vm.selectFork(mainnetFork);
          assertEq(Token(mainnetToken).name(), "Token");
      
          vm.selectFork(arbitrumFork);
          assertEq(Token(arbitrumToken).name(), "Token");
      }
      ```
      
      ### Fork Configuration
      
      ```toml
      # foundry.toml
      [rpc_endpoints]
      mainnet = "${MAINNET_RPC_URL}"
      sepolia = "${SEPOLIA_RPC_URL}"
      arbitrum = "${ARBITRUM_RPC_URL}"
      optimism = "${OPTIMISM_RPC_URL}"
      ```
      
      ## Differential Testing
      
      Compare implementations against reference.
      
      ```solidity
      function testDifferential_MerkleRoot(bytes32[] memory leaves) public {
          vm.assume(leaves.length > 0 && leaves.length < 100);
      
          // Solidity implementation
          bytes32 solidityRoot = merkle.getRoot(leaves);
      
          // Reference implementation (via FFI)
          string[] memory cmd = new string[](3);
          cmd[0] = "node";
          cmd[1] = "scripts/merkle.js";
          cmd[2] = vm.toString(abi.encode(leaves));
      
          bytes memory result = vm.ffi(cmd);
          bytes32 jsRoot = abi.decode(result, (bytes32));
      
          assertEq(solidityRoot, jsRoot, "Merkle root mismatch");
      }
      ```
      
      ## Complete Cheatcode Reference
      
      ### State Manipulation
      
      ```solidity
      // ETH balance
      vm.deal(address, uint256);
      
      // Code at address
      vm.etch(address, bytes memory code);
      
      // Storage
      vm.store(address, bytes32 slot, bytes32 value);
      bytes32 value = vm.load(address, bytes32 slot);
      
      // Nonce
      vm.setNonce(address, uint64 nonce);
      vm.resetNonce(address);
      uint64 nonce = vm.getNonce(address);
      
      // Copy storage
      vm.copyStorage(address from, address to);
      ```
      
      ### Caller Context
      
      ```solidity
      // Single call
      vm.prank(address sender);
      vm.prank(address sender, address origin);
      
      // Multiple calls
      vm.startPrank(address sender);
      vm.startPrank(address sender, address origin);
      vm.stopPrank();
      ```
      
      ### Time & Block
      
      ```solidity
      vm.warp(uint256 timestamp);          // block.timestamp
      vm.roll(uint256 blockNumber);        // block.number
      vm.fee(uint256 gasPrice);            // tx.gasprice
      vm.difficulty(uint256 difficulty);   // block.difficulty
      vm.coinbase(address miner);          // block.coinbase
      vm.chainId(uint256 chainId);         // block.chainid
      vm.prevrandao(bytes32 prevrandao);   // block.prevrandao
      ```
      
      ### Expectations
      
      ```solidity
      // Reverts
      vm.expectRevert();
      vm.expectRevert(bytes memory message);
      vm.expectRevert(bytes4 selector);
      vm.expectPartialRevert(bytes4 selector);
      
      // Events
      vm.expectEmit(bool topic1, bool topic2, bool topic3, bool data);
      vm.expectEmit(bool topic1, bool topic2, bool topic3, bool data, address emitter);
      
      // Calls
      vm.expectCall(address target, bytes memory data);
      vm.expectCall(address target, uint256 value, bytes memory data);
      vm.expectCall(address target, bytes memory data, uint64 count);
      ```
      
      ### Snapshots
      
      ```solidity
      uint256 snapshot = vm.snapshot();
      vm.revertTo(snapshot);
      vm.revertToAndDelete(snapshot);
      ```
      
      ### Environment
      
      ```solidity
      // Read .env
      string memory value = vm.envString("KEY");
      uint256 value = vm.envUint("KEY");
      address value = vm.envAddress("KEY");
      bool value = vm.envBool("KEY");
      bytes32 value = vm.envBytes32("KEY");
      
      // With default
      uint256 value = vm.envOr("KEY", uint256(100));
      
      // Check existence
      bool exists = vm.envExists("KEY");
      ```
      
      ### Cryptography
      
      ```solidity
      // Sign message
      (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);
      (bytes32 r, bytes32 vs) = vm.signCompact(privateKey, digest);
      
      // Get address from private key
      address addr = vm.addr(privateKey);
      
      // Derive key from mnemonic
      uint256 key = vm.deriveKey(mnemonic, index);
      uint256 key = vm.deriveKey(mnemonic, path, index);
      
      // Create wallet
      Vm.Wallet memory wallet = vm.createWallet("label");
      Vm.Wallet memory wallet = vm.createWallet(privateKey);
      ```
      
      ### Labels & Debugging
      
      ```solidity
      vm.label(address, "name");
      string memory name = vm.getLabel(address);
      
      vm.breakpoint();       // Debugger breakpoint
      vm.breakpoint("name"); // Named breakpoint
      ```
      
      ### Gas Metering
      
      ```solidity
      vm.pauseGasMetering();
      // ... expensive operations ...
      vm.resumeGasMetering();
      
      Vm.Gas memory gas = vm.lastCallGas();
      ```
      
      ### File I/O
      
      ```solidity
      string memory content = vm.readFile("path/to/file");
      vm.writeFile("path/to/file", "content");
      bool exists = vm.exists("path/to/file");
      vm.removeFile("path/to/file");
      ```
      
      ## Test Verbosity Levels
      
      ```bash
      forge test           # Summary only
      forge test -v        # + Logs
      forge test -vv       # + Assertion errors
      forge test -vvv      # + Stack traces (failures)
      forge test -vvvv     # + Stack traces (all) + setup
      forge test -vvvvv    # + All traces always
      ```
      
      ## Best Practices
      
      ### Test Organization
      
      1. One test file per contract
      2. Group related tests in same contract
      3. Use descriptive function names
      4. Include failure reason in test name
      
      ### Fuzz Testing
      
      1. Always `bound()` numeric inputs
      2. Use `vm.assume()` sparingly (reduces coverage)
      3. Use fixtures for edge cases
      4. Set deterministic seed in CI
      
      ### Invariant Testing
      
      1. Use handler pattern for complex protocols
      2. Track ghost variables for assertions
      3. Start with simple invariants
      4. Increase depth/runs gradually
      
      ### Fork Testing
      
      1. Pin to specific block for reproducibility
      2. Configure RPC endpoints in foundry.toml
      3. Use `vm.makePersistent()` for deployed contracts
      4. Cache responses with deterministic seed
      
      ### General
      
      1. Test public interface, not internal state
      2. Use `makeAddr()` for labeled addresses
      3. Use `deal()` instead of minting
      4. Label important addresses for traces
      5. Test both success and failure paths
      
  • LICENSE.txt 8.9 KB
    Apache License
    Version 2.0, January 2004
    https://www.apache.org/licenses/
    
    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    
    1. Definitions.
    
    "License" shall mean the terms and conditions for use, reproduction, and
    distribution as defined by Sections 1 through 9 of this document.
    
    "Licensor" shall mean the copyright owner or entity authorized by the
    copyright owner that is granting the License.
    
    "Legal Entity" shall mean the union of the acting entity and all other
    entities that control, are controlled by, or are under common control with
    that entity. For the purposes of this definition, "control" means (i) the
    power, direct or indirect, to cause the direction or management of such
    entity, whether by contract or otherwise, or (ii) ownership of fifty percent
    (50%) or more of the outstanding shares, or (iii) beneficial ownership of
    such entity.
    
    "You" (or "Your") shall mean an individual or Legal Entity exercising
    permissions granted by this License.
    
    "Source" form shall mean the preferred form for making modifications,
    including but not limited to software source code, documentation source, and
    configuration files.
    
    "Object" form shall mean any form resulting from mechanical transformation or
    translation of a Source form, including but not limited to compiled object
    code, generated documentation, and conversions to other media types.
    
    "Work" shall mean the work of authorship, whether in Source or Object form,
    made available under the License, as indicated by a copyright notice that is
    included in or attached to the work (an example is provided in the Appendix
    below).
    
    "Derivative Works" shall mean any work, whether in Source or Object form,
    that is based on (or derived from) the Work and for which the editorial
    revisions, annotations, elaborations, or other modifications represent, as a
    whole, an original work of authorship. For the purposes of this License,
    Derivative Works shall not include works that remain separable from, or
    merely link (or bind by name) to the interfaces of, the Work and Derivative
    Works thereof.
    
    "Contribution" shall mean any work of authorship, including the original
    version of the Work and any modifications or additions to that Work or
    Derivative Works thereof, that is intentionally submitted to Licensor for
    inclusion in the Work by the copyright owner or by an individual or Legal
    Entity authorized to submit on behalf of the copyright owner. For the
    purposes of this definition, "submitted" means any form of electronic, verbal,
    or written communication sent to the Licensor or its representatives,
    including but not limited to communication on electronic mailing lists, source
    code control systems, and issue tracking systems that are managed by, or on
    behalf of, the Licensor for the purpose of discussing and improving the Work,
    but excluding communication that is conspicuously marked or otherwise
    designated in writing by the copyright owner as "Not a Contribution."
    
    "Contributor" shall mean Licensor and any individual or Legal Entity on
    behalf of whom a Contribution has been received by Licensor and subsequently
    incorporated within the Work.
    
    2. Grant of Copyright License. Subject to the terms and conditions of this
    License, each Contributor hereby grants to You a perpetual, worldwide,
    non-exclusive, no-charge, royalty-free, irrevocable copyright license to
    reproduce, prepare Derivative Works of, publicly display, publicly perform,
    sublicense, and distribute the Work and such Derivative Works in Source or
    Object form.
    
    3. Grant of Patent License. Subject to the terms and conditions of this
    License, each Contributor hereby grants to You a perpetual, worldwide,
    non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
    section) patent license to make, have made, use, offer to sell, sell, import,
    and otherwise transfer the Work, where such license applies only to those
    patent claims licensable by such Contributor that are necessarily infringed by
    their Contribution(s) alone or by combination of their Contribution(s) with
    the Work to which such Contribution(s) was submitted. If You institute patent
    litigation against any entity (including a cross-claim or counterclaim in a
    lawsuit) alleging that the Work or a Contribution incorporated within the Work
    constitutes direct or contributory patent infringement, then any patent
    licenses granted to You under this License for that Work shall terminate as of
    the date such litigation is filed.
    
    4. Redistribution. You may reproduce and distribute copies of the Work or
    Derivative Works thereof in any medium, with or without modifications, and in
    Source or Object form, provided that You meet the following conditions:
    
    (a) You must give any other recipients of the Work or Derivative Works a copy
    of this License; and
    
    (b) You must cause any modified files to carry prominent notices stating that
    You changed the files; and
    
    (c) You must retain, in the Source form of any Derivative Works that You
    distribute, all copyright, patent, trademark, and attribution notices from
    the Source form of the Work, excluding those notices that do not pertain to
    any part of the Derivative Works; and
    
    (d) If the Work includes a "NOTICE" text file as part of its distribution,
    then any Derivative Works that You distribute must include a readable copy of
    the attribution notices contained within such NOTICE file, excluding those
    notices that do not pertain to any part of the Derivative Works, in at least
    one of the following places: within a NOTICE text file distributed as part of
    the Derivative Works; within the Source form or documentation, if provided
    along with the Derivative Works; or, within a display generated by the
    Derivative Works, if and wherever such third-party notices normally appear.
    The contents of the NOTICE file are for informational purposes only and do not
    modify the License. You may add Your own attribution notices within Derivative
    Works that You distribute, alongside or as an addendum to the NOTICE text from
    the Work, provided that such additional attribution notices cannot be
    construed as modifying the License.
    
    You may add Your own copyright statement to Your modifications and may provide
    additional or different license terms and conditions for use, reproduction, or
    distribution of Your modifications, or for any such Derivative Works as a
    whole, provided Your use, reproduction, and distribution of the Work otherwise
    complies with the conditions stated in this License.
    
    5. Submission of Contributions. Unless You explicitly state otherwise, any
    Contribution intentionally submitted for inclusion in the Work by You to the
    Licensor shall be under the terms and conditions of this License, without any
    additional terms or conditions. Notwithstanding the above, nothing herein
    shall supersede or modify the terms of any separate license agreement you may
    have executed with Licensor regarding such Contributions.
    
    6. Trademarks. This License does not grant permission to use the trade names,
    trademarks, service marks, or product names of the Licensor, except as
    required for reasonable and customary use in describing the origin of the Work
    and reproducing the content of the NOTICE file.
    
    7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
    writing, Licensor provides the Work (and each Contributor provides its
    Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied, including, without limitation, any warranties
    or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
    PARTICULAR PURPOSE. You are solely responsible for determining the
    appropriateness of using or redistributing the Work and assume any risks
    associated with Your exercise of permissions under this License.
    
    8. Limitation of Liability. In no event and under no legal theory, whether in
    tort (including negligence), contract, or otherwise, unless required by
    applicable law (such as deliberate and grossly negligent acts) or agreed to in
    writing, shall any Contributor be liable to You for damages, including any
    direct, indirect, special, incidental, or consequential damages of any
    character arising as a result of this License or out of the use or inability to
    use the Work (including but not limited to damages for loss of goodwill, work
    stoppage, computer failure or malfunction, or any and all other commercial
    damages or losses), even if such Contributor has been advised of the
    possibility of such damages.
    
    9. Accepting Warranty or Additional Liability. While redistributing the Work
    or Derivative Works thereof, You may choose to offer, and charge a fee for,
    acceptance of support, warranty, indemnity, or other liability obligations
    and/or rights consistent with this License. However, in accepting such
    obligations, You may act only on Your own behalf and on Your sole
    responsibility, not on behalf of any other Contributor, and only if You agree
    to indemnify, defend, and hold each Contributor harmless for any liability
    incurred by, or claims asserted against, such Contributor by reason of your
    accepting any such warranty or additional liability.
    
    END OF TERMS AND CONDITIONS
    
  • SKILL.md 9.2 KB
    ---
    name: foundry-solidity
    description: Build and test Solidity contracts with Foundry. Use when developing Ethereum contracts, writing Forge tests, deploying with scripts, or debugging with Cast/Anvil. Triggers on forge, cast, anvil, foundry.toml, *.t.sol, or *.s.sol.
    metadata:
      version: "0.2.5"
      categories: "development"
      topics: "solidity, foundry, forge, smart-contracts, ethereum"
      openclaw:
        homepage: https://github.com/tenequm/skills/tree/main/skills/foundry-solidity
        emoji: "⚒️"
        primaryEnv: PRIVATE_KEY
        requires:
          anyBins:
            - forge
            - cast
        install:
          - kind: brew
            formula: foundry
            bins:
              - forge
              - cast
              - anvil
        envVars:
          - name: PRIVATE_KEY
            required: false
            description: Deployer signing key. Use throwaway/testnet keys.
          - name: ETHERSCAN_API_KEY
            required: false
            description: Etherscan verification API key.
          - name: MAINNET_RPC_URL
            required: false
            description: Ethereum mainnet RPC endpoint.
          - name: SEPOLIA_RPC_URL
            required: false
            description: Sepolia testnet RPC endpoint.
          - name: ARBITRUM_RPC_URL
            required: false
            description: Arbitrum RPC endpoint.
          - name: OPTIMISM_RPC_URL
            required: false
            description: Optimism RPC endpoint.
          - name: ARBISCAN_API_KEY
            required: false
            description: Arbiscan verification key.
          - name: OPTIMISTIC_ETHERSCAN_API_KEY
            required: false
            description: Optimistic Etherscan verification key.
          - name: POLYGONSCAN_API_KEY
            required: false
            description: Polygonscan verification key.
          - name: BASESCAN_API_KEY
            required: false
            description: Basescan verification key.
          - name: DEPLOYER_PRIVATE_KEY
            required: false
            description: Alternative deployer key alias.
          - name: TEST_ADMIN
            required: false
            description: Test admin address used in fork tests.
    ---
    
    # Foundry Solidity Development
    
    Complete guide for building secure, efficient smart contracts with **Foundry 1.5.0** and **Solidity 0.8.30**.
    
    ## When to Use This Skill
    
    - Developing Ethereum/EVM smart contracts
    - Writing Forge tests (unit, fuzz, invariant, fork)
    - Deploying contracts with scripts
    - Using Foundry tools (forge, cast, anvil, chisel)
    - Working with `foundry.toml`, `*.t.sol`, `*.s.sol` files
    - Debugging transactions and contract interactions
    
    ## Quick Start
    
    ```bash
    # Create new project
    forge init my-project && cd my-project
    
    # Build contracts
    forge build
    
    # Run tests
    forge test
    
    # Deploy (dry-run)
    forge script script/Deploy.s.sol --rpc-url sepolia
    
    # Deploy (broadcast)
    forge script script/Deploy.s.sol --rpc-url sepolia --broadcast --verify
    ```
    
    ## Project Structure
    
    ```
    my-project/
    ├── foundry.toml          # Configuration
    ├── src/                  # Contracts
    │   └── Counter.sol
    ├── test/                 # Tests (*.t.sol)
    │   └── Counter.t.sol
    ├── script/               # Deploy scripts (*.s.sol)
    │   └── Deploy.s.sol
    └── lib/                  # Dependencies
        └── forge-std/
    ```
    
    ## Core Commands
    
    ### Build & Test
    
    ```bash
    forge build                          # Compile
    forge test                           # Run all tests
    forge test -vvvv                     # With traces
    forge test --match-test testDeposit  # Filter by test name
    forge test --match-contract Vault    # Filter by contract
    forge test --fork-url $RPC_URL       # Fork testing
    forge test --gas-report              # Gas usage report
    ```
    
    ### Deployment
    
    ```bash
    # Single contract
    forge create src/Token.sol:Token --rpc-url sepolia --private-key $KEY --broadcast
    
    # Script deployment (recommended)
    forge script script/Deploy.s.sol:Deploy --rpc-url sepolia --broadcast --verify
    
    # Verify existing contract
    forge verify-contract $ADDRESS src/Token.sol:Token --chain sepolia
    ```
    
    ### Cast - Blockchain Interactions
    
    ```bash
    cast call $CONTRACT "balanceOf(address)" $USER --rpc-url mainnet
    cast send $CONTRACT "transfer(address,uint256)" $TO $AMOUNT --private-key $KEY
    cast decode-tx $TX_HASH
    cast storage $CONTRACT 0 --rpc-url mainnet
    ```
    
    ### Anvil - Local Node
    
    ```bash
    anvil                           # Start local node
    anvil --fork-url $RPC_URL       # Fork mainnet
    anvil --fork-block-number 18000000
    ```
    
    ## Basic Test Contract
    
    ```solidity
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.30;
    
    import {Test, console} from "forge-std/Test.sol";
    import {Counter} from "../src/Counter.sol";
    
    contract CounterTest is Test {
        Counter public counter;
        address public user;
    
        function setUp() public {
            counter = new Counter();
            user = makeAddr("user");
            deal(user, 10 ether);
        }
    
        function test_Increment() public {
            counter.increment();
            assertEq(counter.number(), 1);
        }
    
        function test_RevertWhen_Unauthorized() public {
            vm.expectRevert("Unauthorized");
            vm.prank(user);
            counter.adminFunction();
        }
    
        function testFuzz_SetNumber(uint256 x) public {
            x = bound(x, 0, 1000);
            counter.setNumber(x);
            assertEq(counter.number(), x);
        }
    }
    ```
    
    ## Essential Cheatcodes
    
    ```solidity
    // Identity & ETH
    address alice = makeAddr("alice");          // Create labeled address
    deal(alice, 10 ether);                      // Give ETH
    deal(address(token), alice, 1000e18);       // Give ERC20
    
    // Impersonation
    vm.prank(alice);                            // Next call as alice
    vm.startPrank(alice);                       // All calls as alice
    vm.stopPrank();
    
    // Time & Block
    vm.warp(block.timestamp + 1 days);          // Set timestamp
    vm.roll(block.number + 100);                // Set block number
    
    // Assertions
    vm.expectRevert("Error message");           // Expect revert
    vm.expectRevert(CustomError.selector);      // Custom error
    vm.expectEmit(true, true, false, true);     // Expect event
    emit Transfer(from, to, amount);            // Must match next emit
    
    // Storage
    vm.store(addr, slot, value);                // Write storage
    vm.load(addr, slot);                        // Read storage
    ```
    
    ## Deploy Script
    
    ```solidity
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.30;
    
    import {Script, console} from "forge-std/Script.sol";
    import {Counter} from "../src/Counter.sol";
    
    contract Deploy is Script {
        function run() external {
            uint256 deployerKey = vm.envUint("PRIVATE_KEY");
    
            vm.startBroadcast(deployerKey);
            Counter counter = new Counter();
            counter.setNumber(42);
            vm.stopBroadcast();
    
            console.log("Deployed to:", address(counter));
        }
    }
    ```
    
    ## Modern Solidity Patterns (0.8.30)
    
    ```solidity
    // Custom errors (gas efficient)
    error InsufficientBalance(uint256 available, uint256 required);
    
    // Transient storage (0.8.28+) - cheap reentrancy guard
    bool transient locked;
    modifier nonReentrant() {
        require(!locked, "Reentrancy");
        locked = true;
        _;
        locked = false;
    }
    
    // Immutable variables (cheap reads)
    address public immutable owner;
    
    // Named mapping parameters
    mapping(address user => uint256 balance) public balances;
    
    // require with custom error (0.8.26+)
    require(amount <= balance, InsufficientBalance(balance, amount));
    ```
    
    ## Configuration (foundry.toml)
    
    ```toml
    [profile.default]
    src = "src"
    out = "out"
    libs = ["lib"]
    solc = "0.8.30"
    optimizer = true
    optimizer_runs = 200
    evm_version = "prague"
    
    fuzz.runs = 256
    invariant.runs = 256
    invariant.depth = 50
    
    [rpc_endpoints]
    mainnet = "${MAINNET_RPC_URL}"
    sepolia = "${SEPOLIA_RPC_URL}"
    
    [etherscan]
    mainnet = { key = "${ETHERSCAN_API_KEY}" }
    sepolia = { key = "${ETHERSCAN_API_KEY}" }
    
    [profile.ci]
    fuzz.runs = 10000
    invariant.runs = 1000
    ```
    
    ## References
    
    For detailed guides, see:
    
    - **Testing**: See `references/testing.md` for complete testing patterns (unit, fuzz, invariant, fork), all cheatcodes, and best practices
    - **forge-std API**: See `references/forge-std-api.md` for complete library reference (150+ functions)
    - **Solidity 0.8.30**: See `references/solidity-modern.md` for new features and modern syntax
    - **Deployment**: See `references/deployment.md` for scripting, verification, and multi-chain deployment
    - **Configuration**: See `references/configuration.md` for all foundry.toml options
    - **Gas Optimization**: See `references/gas-optimization.md` for storage packing, compiler settings, and profiling
    - **Patterns**: See `references/patterns.md` for access control, reentrancy guards, factories, and common idioms
    - **Security**: See `references/security.md` for vulnerabilities, defensive patterns, and audit preparation
    - **Resources**: See `references/resources.md` for official docs, libraries, security tools, and learning paths
    - **Debugging**: See `references/debugging.md` for traces, breakpoints, console.log, and the interactive debugger
    - **Dependencies**: See `references/dependencies.md` for forge install, remappings, and Soldeer package manager
    - **CI/CD**: See `references/cicd.md` for GitHub Actions workflows, caching, and gas tracking
    - **Chisel**: See `references/chisel.md` for the interactive Solidity REPL
    - **Cast Advanced**: See `references/cast-advanced.md` for decoding, encoding, wallet management, and batch operations
    - **Anvil Advanced**: See `references/anvil-advanced.md` for impersonation, state manipulation, and mining modes
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related