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.
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.
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.
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.
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({
// ...
});