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.
npm install @hellaweb3/foundryanvil-testcontainers-nodejsThis 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:
``shell`
pnpm add -D @hellaweb3/foundryanvil-testcontainers-nodejs
Use the AnvilContainer module to start up a new Anvil testcontainer in your
test suite.
- Set up the container in a beforeAll hook.afterAll
- Add an hook to stop the container.
`ts
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.addresses()
- Use container test helpers like and sendEthTransaction().
`ts
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:
`tshttps://mainnet.infura.io/v3/${INFURA_KEY}
const options = new AnvilOptions().fork
.withForkUrl()
.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. |
`ts
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. |
`ts`
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. |
`ts`
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. |
`ts`
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. |
`ts`
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. |
`ts`
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. |
`ts`
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. |
`ts`
const options = new AnvilOptions().state
.withLoadState("path/to/state.json")
.state.withDumpState("path/to/new-state.json");
---
- Bunchee - Zero-config bundler for npm
packages
- Jest - Testing framework
This library exports both ESM and CommonJS formats, with full TypeScript
support:
- dist/index.js - ESMdist/index.cjs
- - CommonJSdist/index.d.ts
- - TypeScript declarations
`bashBuild the package
pnpm run build
This will trigger the release.yml and publish.yml workflows.
---
Contracts
WETH: https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code
---
$3
Build the docker image:
`shell
docker build -t hellaweb3/foundry-anvil:1.6 .
`Run the docker image:
`shell
docker run -p 8545:8545 hellaweb3/foundry-anvil:1.6
`Push the docker image:
`shell
docker push hellaweb3/foundry-anvil:1.6
`Use cast to test the connection:
`shell
cast block-number
`Use script to test the connection:
`shell
node ./scripts/get-block-number.ts
``Maintained by Hella Labs.
MIT