Foundry – Ponder
Skip to content

Foundry

Use Ponder with Foundry

Ponder works with Anvil, the local development chain included with Foundry.

Foundry projects follow various development workflows (test-driven, deploy to a fresh chain, deploy to a fork, etc). This guide offers patterns that you can adapt to your specific requirements.

Configure the anvil chain

Disable caching

Ponder's RPC request cache works well for live chains where the chain is generally immutable, but causes issues when indexing a local chain that "resets".

Use the disableCache option to disable RPC request caching for the Anvil chain. With this option set to true, Ponder will clear the cache on start up and between hot reloads.

ponder.config.ts
import { createConfig } from "ponder";
 
export default createConfig({
  chains: {
    anvil: {
      id: 31337,
      rpc: "http://127.0.0.1:8545",
      disableCache: true,  
    },
  },
  // ...
});

Chain ID

We recommend using 31337 (the default Anvil chain ID) even when forking a live chain. This avoids common footguns when working with multiple chains.

Mining mode

We recommend using interval mining with a block time of ~2 seconds. This better simulates a live chain.

Generate ABI files

To enable end-to-end type safety, the contract ABIs generated by Foundry must be copied into TypeScript (.ts) source files.

Wagmi CLI

The Wagmi CLI Foundry plugin is an excellent tool to automate tedious ABI file management. For more information, visit the Wagmi CLI documentation.

Here is the Wagmi CLI config file used by the Foundry example project.

wagmi.config.ts
import { defineConfig } from "@wagmi/cli";
import { foundry } from "@wagmi/cli/plugins";
 
export default defineConfig({
  out: "abis/CounterAbi.ts",
  plugins: [
    foundry({
      project: "foundry",
      include: ["Counter.sol/**"],
    }),
  ],
});

Import broadcast files

Foundry scripts write transaction inputs and receipts to JSON files in the broadcast directory. You can import these files directly into ponder.config.ts to automate address management and enable hot reloading.

Automate address management

To read the contract address and deployment block number from a broadcast file, import the file directly into ponder.config.ts and access properties from the JSON object.

The ponder.config.ts file from the Foundry example project demonstrates this pattern. Here, the first transaction in the broadcast file deployed the Counter.sol contract. The location of the contract address and start block within the broadcast file depends on the order and number of transactions in your deployment script.

ponder.config.ts
import { createConfig } from "ponder";
import { http, getAddress, hexToNumber } from "viem";
import { counterABI } from "../abis/CounterAbi";
import CounterDeploy from "../foundry/broadcast/Deploy.s.sol/31337/run-latest.json"; 
 
const address = getAddress(CounterDeploy.transactions[0]!.contractAddress); 
const startBlock = hexToNumber(CounterDeploy.receipts[0]!.blockNumber); 
 
export default createConfig({
  chains: {
    anvil: {
      id: 31337,
      rpc: "http://127.0.0.1:8545",
      disableCache: true,
    },
  },
  contracts: {
    Counter: {
      chain: "anvil",
      abi: counterABI,
      address, 
      startBlock, 
    },
  },
});

Enable hot reloading

If you import a JSON broadcast file in ponder.config.ts, the dev server will reload each time that file changes. This is a simple way to ensure that Ponder reloads every time you run a Foundry deployment script.

ponder.config.ts
import { createConfig } from "ponder";
import CounterDeploy from "../foundry/broadcast/Deploy.s.sol/31337/run-latest.json"; 
 // // ^ The development server detects changes to this file and triggers a hot reload.
 
export default createConfig({
  // ...
});