This repository provides a Testcontainers module for Node.js to run a customized Anvil node in your E2E tests.
This Typescript module provides a Fluent API style method of configuring and starting the Anvil node. And during your test execution, the module provides a viem test client and streamlined helper methods to interact with the node.
Base image: https://github.com/foundry-rs/foundry/blob/master/Dockerfile
Built using Anvil: https://getfoundry.sh/anvil/reference/anvil
Foundry image: ghcr.io/foundry-rs/foundry:v1.6.0-rc1
Custom image: hellaweb3/foundry-anvil:1.6
Install the module:
pnpm add -D @hellaweb3/foundryanvil-testcontainers-nodejsUse the AnvilContainer module to start up a new Anvil testcontainer in your
test suite.
- Set up the container in a
beforeAllhook. - Add an
afterAllhook to stop the container.
describe("AnvilContainer", () => {
let container: StartedAnvilContainer;
beforeAll(async () => {
const options = new AnvilOptions()
.logs.verboseLogs(LogVerbosity.Five)
.logs.jsonLogFormat()
.account.withRandomMnemonic()
.evm.autoImpersonate();
container = await new AnvilContainer(options).start();
}, 60000);
afterAll(async () => {
if (container) await container.stop();
});
});The StartedAnvilContainer provides a viem test client that you can use to
interact with the node.
- Access the viem test client via
container.client. - Use container test helpers like
addresses()andsendEthTransaction().
it("test send transaction", async () => {
let addresses = await container.addresses();
const receipt: TransactionReceipt = await container.sendEthTransaction(
addresses[0],
addresses[1],
"1",
);
expect(receipt.status).toBe("success");
});| Script | Description |
|---|---|
pnpm dev |
Start development mode with watch |
pnpm build |
Build for production |
pnpm test |
Run tests |
pnpm eslint |
Lint code |
pnpm typecheck |
Run TypeScript type checking |
Configure the Anvil node to fork from a remote RPC URL:
const options = new AnvilOptions().fork
.withForkUrl(`https://mainnet.infura.io/v3/${INFURA_KEY}`)
.fork.withForkBlockNumber(17500000);
const container = await new AnvilContainer(options).start();The AnvilContainer can be highly customized using the AnvilOptions class.
Options are organized into logical modules to make configuration intuitive.
Review the Foundry Docs for more details.
Configure development accounts, balances, and mnemonics.
- Use-case: Setup specific pre-funded accounts or use a known mnemonic to ensure predictable addresses across test runs.
| Option | Description |
|---|---|
withAccounts(count: number) |
Sets the number of dev accounts to generate and configure. |
withBalance(balance: number) |
Sets the balance of every dev account in Ether. |
withDerivationPath(path: string) |
Sets the derivation path of the child key to be derived. |
withMnemonic(mnemonic: string) |
Sets the BIP39 mnemonic phrase used for generating accounts. |
withRandomMnemonic(words?: number) |
Automatically generates a BIP39 mnemonic phrase. |
withMnemonicSeedUnsafe(seed: string) |
Generates a BIP39 mnemonic phrase from a given seed. |
const options = new AnvilOptions().account
.withAccounts(10)
.account.withBalance(1000)
.account.withRandomMnemonic();
const container = await new AnvilContainer(options).start();Fine-tune the EVM behavior, gas limits, and hardforks.
- Use-case: Test contract deployments that exceed default code size limits or simulate specific Ethereum hardforks.
| Option | Description |
|---|---|
withHardfork(hardfork: Hardfork) |
Sets the EVM hardfork to use. |
autoImpersonate(enabled?: boolean) |
Enables automatic impersonation on startup. |
withBlockBaseFeePerGas(fee: bigint | number) |
Sets the base fee in a block. |
withChainId(chainId: number) |
Sets the chain ID. |
withCodeSizeLimit(size: number) |
EIP-170: Contract code size limit in bytes. |
disableBlockGasLimit(enabled?: boolean) |
Disable the call.gas_limit <= block.gas_limit constraint. |
disableCodeSizeLimit(enabled?: boolean) |
Disable EIP-170: Contract code size limit. |
disableMinPriorityFee(enabled?: boolean) |
Disable the enforcement of a minimum suggested priority fee. |
withGasLimit(limit: bigint | number) |
Sets the block gas limit. |
withGasPrice(price: bigint | number) |
Sets the gas price. |
disableDefaultCreate2Deployer(enabled?: boolean) |
Disable the default create2 deployer. |
disablePoolBalanceChecks(enabled?: boolean) |
Disable pool balance checks. |
withMemoryLimit(limit: number) |
The memory limit per EVM execution in bytes. |
withPrintTraces(enabled?: boolean) |
Enable printing of traces for executed transactions and eth_call to stdout. |
withStepsTracing(enabled?: boolean) |
Enable steps tracing used for debug calls returning geth-style traces. |
const options = new AnvilOptions().evm
.withHardfork(Hardfork.London)
.evm.withCodeSizeLimit(32128)
.evm.autoImpersonate();Fork from a remote RPC endpoint to test against real-world state.
- Use-case: Integration tests that interact with existing protocols (e.g., Uniswap, Aave) on Mainnet or L2s.
| Option | Description |
|---|---|
withComputeUnitsPerSecond(cups: number) |
Sets the number of assumed available compute units per second for this provider. |
withForkUrl(url: string) |
Fetch state over a remote endpoint instead of starting from an empty state. |
withForkBlockNumber(blockNumber: number) |
Fetch state from a specific block number over a remote endpoint. |
withForkChainId(chainId: number) |
Specify chain id to skip fetching it from remote endpoint. |
withForkHeader(header: string) |
Headers to use for the rpc client. |
withForkRetryBackoff(backoff: number) |
Initial retry backoff on encountering errors. |
withForkTransactionHash(hash: string) |
Fetch state from after a specific transaction hash has been applied. |
noRateLimit(enabled?: boolean) |
Disables rate limiting for this node's provider. |
noStorageCaching(enabled?: boolean) |
Explicitly disables the use of RPC caching. |
withRetries(retries: number) |
Number of retry requests for spurious networks. |
withTimeout(timeout: number) |
Timeout in ms for requests sent to remote JSON-RPC server. |
const options = new AnvilOptions().fork
.withForkUrl("https://mainnet.infura.io/v3/YOUR_KEY")
.fork.withForkBlockNumber(18000000);Control block production and mining behavior.
- Use-case: Simulate a real-time mining interval to test frontend polling logic or time-dependent contract features.
| Option | Description |
|---|---|
withBlockTime(seconds: number) |
Sets the block time in seconds for interval mining. |
withMixedMining(enabled?: boolean) |
Enable mixed mining. |
withNoMining(enabled?: boolean) |
Disable auto and interval mining, and mine on demand instead. |
withBlockNumber(number: number) |
Sets the number of the genesis block. |
withSlotsInAnEpoch(slots: number) |
Slots in an epoch. |
const options = new AnvilOptions().mining
.withBlockTime(1) // Mine a block every second
.mining.withMixedMining();Adjust output verbosity and format for better debugging.
- Use-case: Enable JSON logging for automated log analysis or increase verbosity to debug failing transactions.
| Option | Description |
|---|---|
withColor(color: Color) |
The color of the log messages. |
withMarkdownFormat(enabled?: boolean) |
Format log messages as Markdown. |
quiet(enabled?: boolean) |
Do not print log messages. |
verboseLogs(logVerbosity: LogVerbosity) |
Sets the verbosity level of the log messages. |
jsonLogFormat(enabled?: boolean) |
Format log messages as JSON. |
disableConsoleLog(enabled?: boolean) |
Disable printing of console.log invocations to stdout. |
const options = new AnvilOptions().logs
.verboseLogs(LogVerbosity.Three)
.logs.jsonLogFormat();Enable features specific to certain networks like Celo or Optimism.
- Use-case: E2E tests for cross-chain applications or protocols deployed on Optimism or Celo.
| Option | Description |
|---|---|
withCelo(enabled?: boolean) |
Enable Celo network features. |
withOptimism(enabled?: boolean) |
Enable Optimism network features. |
const options = new AnvilOptions().network
.withOptimism();Configure the RPC server settings, CORS, and IPC.
- Use-case: Testing IPC connections or adjusting CORS settings for local web application development.
| Option | Description |
|---|---|
withIpc(path?: string) |
Launch an ipc server at the given path or default path. |
withThreads(threads: number) |
Number of threads to use. |
withAllowOrigin(origin: string) |
The cors allow_origin header. |
withCachePath(path: string) |
Path to the cache directory where persisted states are stored. |
noCors(enabled?: boolean) |
Disable CORS. |
noRequestSizeLimit(enabled?: boolean) |
Disable the default request body size limit. |
const options = new AnvilOptions().server
.withAllowOrigin("*")
.server.noCors();Manage chain state, persistence, and snapshots.
- Use-case: Speed up test suites by loading a pre-configured state instead of re-deploying contracts every time.
| Option | Description |
|---|---|
withConfigOut(path: string) |
Writes output of anvil as json to user-specified file. |
withDumpState(path: string) |
Dump the state and block environment of chain on exit. |
withInit(path: string) |
Initialize the genesis block with the given genesis.json file. |
withLoadState(path: string) |
Initialize the chain from a previously saved state snapshot. |
withMaxPersistedStates(count: number) |
Max number of states to persist on disk. |
withOrder(order: Order) |
How transactions are sorted in the mempool. |
withPreserveHistoricalStates(enabled?: boolean) |
Preserve historical state snapshots when dumping the state. |
withPruneHistory(count?: number) |
Don't keep full chain history. |
withStateInterval(seconds: number) |
Interval in seconds at which the state is to be dumped to disk. |
withState(path: string) |
Alias for both --load-state and --dump-state. |
withTimestamp(timestamp: number) |
The timestamp of the genesis block. |
withTransactionBlockKeeper(count: number) |
Number of blocks with transactions to keep in memory. |
const options = new AnvilOptions().state
.withLoadState("path/to/state.json")
.state.withDumpState("path/to/new-state.json");This library exports both ESM and CommonJS formats, with full TypeScript support:
dist/index.js- ESMdist/index.cjs- CommonJSdist/index.d.ts- TypeScript declarations
# Build the package
pnpm run build
# Publish to npm
np --no-publish
# Trigger GitHub release workflow
git push origin --tagsThis will trigger the release.yml and publish.yml workflows.
WETH: https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code
Build the docker image:
docker build -t hellaweb3/foundry-anvil:1.6 .Run the docker image:
docker run -p 8545:8545 hellaweb3/foundry-anvil:1.6Push the docker image:
docker push hellaweb3/foundry-anvil:1.6Use cast to test the connection:
cast block-numberUse script to test the connection:
node ./scripts/get-block-number.tsMaintained by Hella Labs.
MIT