Foundry projects
If your contracts already live in a Foundry project, you do not need to restate
its remappings and library paths. Turn on foundryProject and Tevm asks forge
for them.
{
"foundryProject": true
}At config-resolution time this runs:
forge config --jsonand merges the resulting remappings and libs into the Tevm config. Your own
remappings in tevm.config.json are layered on top and win on conflict.
Pointing at a specific forge binary
Set foundryProject to a path instead of true:
{
"foundryProject": "/opt/homebrew/bin/forge"
}This is worth doing in CI, where forge may not be on PATH for the process
that runs your bundler.
A complete Foundry + Vite project
my-dapp/
├── foundry.toml
├── remappings.txt
├── lib/
│ └── openzeppelin-contracts/
├── src/
│ └── Token.sol
├── tevm.config.json
├── tsconfig.json
├── vite.config.ts
└── app/
└── main.ts
[profile.default]
src = "src"
out = "out"
libs = ["lib"]@openzeppelin/=lib/openzeppelin-contracts/{
"foundryProject": true
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Token is ERC20 {
constructor() ERC20("Token", "TKN") {
_mint(msg.sender, 1_000_000e18);
}
}import { vitePluginTevm } from '@tevm/vite-plugin'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [vitePluginTevm()],
})import { createMemoryClient } from 'tevm'
import { Token } from '../src/Token.sol'
const client = createMemoryClient()
const token = Token.withAddress('0x1234567890123456789012345678901234567890')
await client.setCode({
address: token.address,
bytecode: token.deployedBytecode,
})
console.log(await client.readContract(token.read.symbol()))Note that src/Token.sol is a plain .sol file, so the import carries the ABI
but no creation bytecode. If you want to deploy it from JavaScript, rename it to
Token.s.sol or add a separate .s.sol wrapper — Foundry ignores the extra
extension segment and still compiles it.
Living alongside forge build
The two toolchains do not share an artifact directory. Foundry writes to out/,
Tevm writes to .tevm/. They can both be present; they simply compile the same
sources independently.
If you want a single source of truth for the solc version, set it in both places:
[profile.default]
solc = "0.8.30"vitePluginTevm({ solc: '0.8.30' })Tevm does not read foundry.toml's solc field — the compiler version is a
plugin option, so it must be set explicitly.
Errors you might see
FoundryNotFoundError—forge config --jsoncould not be executed. Either install Foundry, pointfoundryProjectat the binary, or dropfoundryProjectand listremappingsandlibsintevm.config.jsonyourself.FoundryConfigError—forgeran but its output could not be parsed. Runforge config --jsonby hand to see what it emitted.InvalidRemappingsError— the remappings fromforge(or fromremappings.txt) were not inprefix=targetform.

