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.
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