Foundry
This guide describes how to integrate Ponder and Foundry during local development.
Foundry projects follow various development workflows (test-driven, deploy to a fresh chain, deploy to a fork, etc). Rather than a one-size-fits-all integration, this page offers patterns that you can adapt to your workflow.
Configure the anvil
network
Disable caching
Ponder's RPC request cache works well for live networks 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 network. With this option set to true, Ponder will clear the cache on start up and between hot reloads.
import { createConfig } from "@ponder/core";
import { http } from "viem";
export default createConfig({
networks: {
anvil: {
chainId: 31337,
transport: http("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 networks.
Mining mode
We recommend using interval mining with a block time of ~2 seconds. This better simulates a live network.
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.
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.
forge script
submits transactions to Anvil.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.
import { createConfig } from "@ponder/core";
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({
networks: {
anvil: {
chainId: 31337,
transport: http("http://127.0.0.1:8545"),
disableCache: true,
},
},
contracts: {
Counter: {
network: "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.
import { createConfig } from "@ponder/core";
import { http } from "viem";
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({
// ...
});