@hellaweb3/foundryanvil-testcontainers-nodejs
v0.2.3
Published
This Typescript module provides a Fluent API style method of configuring and starting a Anvil testcontainers node. During your test execution, the module provides a viem test client and streamlined helper methods to interact with the node.
Maintainers
Readme
Description
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
Usage
Install the module:
pnpm add -D @hellaweb3/foundryanvil-testcontainers-nodejsSetup
Use 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();
});
});Test
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");
});Scripts
| 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 |
Forking Options
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();Configuration Options
The AnvilContainer can be highly customized using the AnvilOptions class.
Options are organized into logical modules to make configuration intuitive.
Account Options
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();EVM Options
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();Forking Options
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);Mining Options
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();Logging Options
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();Network Options
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();Server Options
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();State Options
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");Tools
Module Formats
This library exports both ESM and CommonJS formats, with full TypeScript support:
dist/index.js- ESMdist/index.cjs- CommonJSdist/index.d.ts- TypeScript declarations
Publishing
# 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.
Contracts
WETH: https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code
Using a Custom Docker Image
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.tsMaintainers
Maintained by Hella Labs.
License
MIT
