# Foundry

This guide walks you through setting up a complete local Ethereum development environment for Substreams development using Foundry. You'll deploy a sample Counter contract, generate transactions, and stream the events using Substreams.

**Estimated time:** 15-20 minutes

## What You'll Build

* Local Ethereum node (Geth in dev mode)
* Firehose integration for block streaming
* Counter smart contract with events
* Substreams module to extract contract events
* Complete Docker Compose orchestration

## Prerequisites

Ensure you have the following installed:

* **Docker 20.10+** with Docker Compose v2.0+
* **Foundry** (forge, cast, anvil) - [Installation guide](https://book.getfoundry.sh/getting-started/installation)
* **Substreams CLI** v1.7.0+ ([installation guide](/how-to-guides/installing-the-cli.md))
* **Rust** with `wasm32-unknown-unknown` target
* **curl** for testing endpoints

## Architecture Overview

The local environment consists of:

* **Geth** (port 8545/8546) - Ethereum node in dev mode with 1-second block time
* **Substreams** (port 9000) - Substreams Tier1 service providing gRPC streaming
* **Docker network** - Connecting all services

```
┌─────────────────┐    ┌─────────────────────┐    ┌─────────────────┐
│   Your App      │    │     Substreams      │    │     Foundry     │
│                 │    │                     │    │                 │
│ ┌─────────────┐ │    │ ┌─────────────────┐ │    │ ┌─────────────┐ │
│ │ Substreams  │─┼────┼►│   Substreams    │ │    │ │   Deploy    │ │
│ │    CLI      │ │    │ │   (port 9000)   │ │    │ │  Contracts  │ │
│ └─────────────┘ │    │ └─────────────────┘ │    │ └─────────────┘ │
└─────────────────┘    │          │          │    └─────────────────┘
                       │ ┌─────────────────┐ │
                       │ │      Geth       │ │
                       │ │   (port 8545)   │ │
                       │ └─────────────────┘ │
                       └─────────────────────┘
```

## Setup Instructions

### 1. Create Project Directory

```bash
mkdir substreams-ethereum-foundry
cd substreams-ethereum-foundry
```

### 2. Create Docker Compose Configuration

Create a `docker-compose.yml` file:

```yaml
services:
  ethereum-node:
    image: ghcr.io/streamingfast/go-ethereum:geth-v1.16.7-fh3.0
    container_name: ethereum-dev-node
    entrypoint: ["/app/fireeth"]
    command:
      - start
      - reader-node,relayer,merger,firehose,substreams-tier1,substreams-tier2
      - --config-file=
      - --log-format=text
      - --log-to-file=false
      - --data-dir=/data
      - --advertise-block-id-encoding=hex
      - --advertise-chain-name=local-ethereum
      - --common-first-streamable-block=0
      - --reader-node-path=geth
      - --reader-node-arguments=--dev --dev.period=1 --vmtrace=firehose --http --http.addr=0.0.0.0 --http.port=8545 --http.api=eth,net,web3,debug,txpool --datadir=/data/geth
      - --firehose-grpc-listen-addr=:8089
      - --substreams-tier1-grpc-listen-addr=:9000
      - --substreams-tier1-block-type=sf.ethereum.type.v2.Block
    ports:
      - "8545:8545"
      - "8546:8546"
      - "8089:8089"
      - "9000:9000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8545", "-X", "POST", "-H", "Content-Type: application/json", "-d", '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}']
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    volumes:
      - ethereum_data:/data
    networks:
      - ethereum_network

  fund-address:
    image: ghcr.io/streamingfast/go-ethereum:geth-v1.16.7-fh3.0
    volumes:
      - ethereum_data:/data
    entrypoint: ["geth"]
    command:
      - attach
      - --datadir=/data/geth
      - '--exec=[
        "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
        "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
        "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc",
        "0x90f79bf6eb2c4f870365e785982e1f101e93b906",
        "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65",
        "0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc",
        "0x976ea74026e726554db657fa54763abd0c3a0aa9",
        "0x14dc79964da2c08b23698b3d3cc7ca32193d9955",
        "0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f",
        "0xa0ee7a142d267c1f36714e4a8f75612f20a79720"
        ].forEach(to =>
        eth.sendTransaction({from: eth.accounts[0], to, value: web3.toWei(10000, "ether")})
        );'
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
        delay: 2s

volumes:
  ethereum_data:

networks:
  ethereum_network:
    driver: bridge
```

### 3. Start the Environment

```bash
docker compose up -d
```

{% hint style="warning" %}
To restart everything from scratch, use `docker compose down --volumes` to remove all data and start fresh.
{% endhint %}

## Validation Commands

### 1. Check Docker Services

Verify all containers are running and healthy:

```bash
docker compose ps
```

Expected output:

```
NAME                   COMMAND                  SERVICE           STATUS              PORTS
ethereum-dev-node      "/app/fireeth start …"   ethereum-node     Up (healthy)        0.0.0.0:8089->8089/tcp, 0.0.0.0:8545->8545/tcp, 0.0.0.0:8546->8546/tcp, 0.0.0.0:9000->9000/tcp
fund-address           "geth attach --datad…"   fund-address      Exited (0)
```

### 2. Test Substreams Connectivity

Test Substreams Tier1 gRPC connectivity:

```bash
substreams run -e localhost:9000 --plaintext common@v0.1.0 -o clock -s -1
```

Expected output:

```
Writing clock information only (no data)
----------- BLOCK #24 (6c3dbc20ae11cb856bed9789f7845359e98de71b830f0d9599d0061ed4e962d2) age=1.959195s ---------------
...
```

{% hint style="success" %}
If all validation commands succeed, your environment is ready!
{% endhint %}

## Deploy Counter Contract with Foundry

### 1. Install Foundry

If you haven't installed Foundry yet:

```bash
curl -L https://foundry.paradigm.xyz | bash
foundryup
```

### 2. Initialize Foundry Project

```bash
forge init --no-git --force
```

### 3. Configure Foundry

Add the following network configuration to `foundry.toml` (the file already exists from `forge init`):

```toml
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
solc_version = "0.8.20"

[rpc_endpoints]
local = "http://localhost:8545"

[etherscan]
# No API key needed for local development
```

### 4. Set Environment Variables

Set up the private key environment variable for easier command usage:

```bash
# Using HardHat's default test account 0 private key (pre-funded in dev environment)
export PKEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
```

{% hint style="info" %}
This makes subsequent `forge` and `cast` commands cleaner and easier to read. You'll need to run this in each new terminal session.
{% endhint %}

### 5. Use Default Counter Contract

Foundry already created a suitable Counter contract in `src/Counter.sol`. The default contract contains:

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Counter {
    uint256 public number;

    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    function increment() public {
        number++;
    }
}
```

No need to modify this file - we'll use the default contract as-is.

### 6. Compile and Deploy

```bash
# Compile contracts
forge build

# Deploy to local network
forge create --rpc-url local --private-key $PKEY --broadcast src/Counter.sol:Counter
```

Example output:

```
...
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Transaction hash: 0x83dba3bc167c38e36b79b075552f67cd6c3f924bf8efb09ab6686ee50ed4816a
```

Export the deployed contract address for easy reference:

```bash
export CONTRACT=<DEPLOYED_ADDRESS>
```

Replace `<DEPLOYED_ADDRESS>` with the actual address from the deployment output (look for the `Deployed to:` field).

### 7. Verify Deployment

Test the deployed contract with the following sequence:

```bash
# 1. Read the current counter value
cast call $CONTRACT "number()(uint256)" --rpc-url local

# 2. Set a number using setNumber
cast send $CONTRACT "setNumber(uint256)" 42 --rpc-url local --private-key $PKEY

# 3. Increment the counter
cast send $CONTRACT "increment()" --rpc-url local --private-key $PKEY

# 4. Verify the value changed
cast call $CONTRACT "number()(uint256)" --rpc-url local
```

Expected output from the final call: `43` (42 + 1 from increment)

## Create Substreams Module

### 1. Initialize Substreams Project

```bash
substreams init
```

Follow the interactive prompts:

* **Chosen protocol**: `EVM`
* **Chosen generator**: `evm-events-calls`
* **Please enter the project name**: `counter`
* **Please select the chain**: `Ethereum Mainnet` (or the chain you are targeting)
* **Please enter the contract address**: Use your deployed address (from `$CONTRACT` variable)
* **How do you want to provide the JSON ABI?**: `JSON in a local file`
* **Input the full path of the JSON ABI**: `./out/Counter.sol/Counter.json`
* **Please enter the contract initial block number**: `0`
* **Choose a short name for the contract**: `counter`
* **What do you want to track for this contract?**: `Both events and calls`
* **Is this contract a factory**: `No`
* **Add another contract?**: `No`
* **In which directory do you want to download the project?**: `./substreams`
* **How would you like to consume the Substreams?**: `To Postgres` (or choose any other one)

{% hint style="info" %}
The IDL file at `./out/Counter.sol/Counter.json` was automatically generated by Foundry during the build process. We reference this file when initializing the Substreams module.
{% endhint %}

This will generate the basic Substreams module structure with the necessary configuration for tracking your Counter program.

### 2. Build and Test Substreams

```bash
cd substreams
substreams build
substreams run -e localhost:9000 --plaintext counter-v0.1.0.spkg
```

{% hint style="info" %}
Look for the deployment block as it's the one that will contain some actual data. You can scan a specific range using `-s <DEPLOYMENT_BLOCK> -t +10` to scan 10 blocks starting from the deployment block.

You can also leave the substreams run command running and open another terminal to run `cast send $CONTRACT "increment()" --rpc-url local --private-key $PKEY` to increment the counter and see the events appear live in your Substreams output.
{% endhint %}

You should see the Increment events from your contract deployment!

## Cleanup

Congratulations! You've completed the tutorial and have a working local Foundry development environment for Substreams.

When you're done, you can clean up the Docker environment with:

```bash
docker compose down --volumes
```

This will stop all containers and remove all data, allowing you to start fresh if needed.

## Troubleshooting

For common issues with Docker Compose, RPC connectivity, and Substreams, see the [Local Development Troubleshooting](/how-to-guides/develop-your-own-substreams/general/local-development/troubleshooting.md) guide.

## Next Steps

Now that you have a working local environment:

1. **Try Other Platforms** - Explore [HardHat](/how-to-guides/develop-your-own-substreams/on-evm/local-development/hardhat.md) or [Solana](/how-to-guides/develop-your-own-substreams/solana/local-development/anchor.md) local development
2. **Advanced Substreams** - Learn about [modules](/reference-material/core-concepts/modules.md), [manifests](/reference-material/manifest-and-components/manifests.md), and [data transformations](/how-to-guides/develop-your-own-substreams/general/using-rust-proto.md)
3. **Consuming Substreams** - Connect to [databases](/how-to-guides/sinks/sql.md) or [streaming platforms](/how-to-guides/sinks/stream.md)
4. **Production Deployment** - Move to [production endpoints](/reference-material/chain-support/chains-and-endpoints.md)

## Additional Resources

* [Foundry Book](https://book.getfoundry.sh/)
* [Substreams Ethereum Reference](https://github.com/streamingfast/substreams-ethereum)
* [Substreams CLI Reference](/reference-material/command-line-interface.md)
* [Creating Protobuf Schemas](/how-to-guides/develop-your-own-substreams/general/creating-protobuf-schemas.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.substreams.dev/how-to-guides/develop-your-own-substreams/on-evm/local-development/foundry.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
