Skip to content

Bundler Guide

Solidity imports simplify your tevm code via compiling contracts to ABI and bytecode consumable by JavaScript directly in your JavaScript build pipeline. Solidity imports are purely optional but highly recomended.

Requirements

To support solidity imports the following steps must be taken:

  1. Configure a bundler to turn your solidity imports into tevm scripts.
  2. Configure your TypeScript LSP to recognize solidity imports correctly as well
  3. Optionally Configure your tevm.config.json
  4. Some may need to configure their editor

Installation

All tevm build tool can be installed via the tevm package.

Terminal window
npm install tevm

After installing you can use any tevm build tool package via a deep import to it’s subpackage in tevm/bundler.

import { rollupPluginTevm } from "tevm/bundler/rollup-plugin";

It is also possible to install these subpackages as standalone packages if you prefer.

npm install @tevm/rollup-plugin

How it works

Core bundler

A JavaScript bundler is code that runs at buildtime to turn an import graph into a single file or multiple files. @tevm/base-bundler turns Solidity imports into tevm script instances. The core Tevm bundler code is reused to build every bundler integration.

  1. On initialization tevm bundler and LSP will load your tsconfig (to read basedir and paths), foundry remappings (if configured), and tevm.config.json if present

  2. Next it will look for import paths ending in .sol. When it sees one it will use node resolution to find the file. If a JavaScript file e.g. .sol.js file already exists it will immediately resolve that. Otherwise it kicks off the process of resolving the contract into it’s ABI and bytecode.

import { ERC20 } from "@openzeppelin/contracts/tokens/ERC20/ERC20.sol";
  1. Before it compiles the contracts it will first resolve the entire solidity import resolution graph and source code with @tevm/resolutions. This resolutions will continue resolving imports in solidity files based on node resolution, foundry configuration (if foundry is configured), and your tevm.config.json remappings and lib.

  2. If content hasn’t changed it will return the results from the cache

  3. If content has changed it will then pass in all the relavent contract code into solc

  4. Once it gets the artifacts it will then use the @tevm/contracts to turn the artifacts into a TevmContract or TevmScript via the [@tevm/runtime] package. The runtime code will look like the following:

import { createContract } from "@tevm/contract";
const _ERC20 = {
name: "ERC20",
humanReadableAbi: [
"constructor()",
"event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId)",
"event ApprovalForAll(address indexed owner, address indexed operator, bool approved)",
"event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)",
"function approve(address to, uint256 tokenId)",
"function balanceOf(address owner) view returns (uint256)",
"function getApproved(uint256 tokenId) view returns (address)",
"function isApprovedForAll(address owner, address operator) view returns (bool)",
"function mint()",
"function mint(uint256 tokenId)",
"function name() view returns (string)",
"function ownerOf(uint256 tokenId) view returns (address)",
"function safeTransferFrom(address from, address to, uint256 tokenId)",
"function safeTransferFrom(address from, address to, uint256 tokenId, bytes data)",
"function setApprovalForAll(address operator, bool approved)",
"function supportsInterface(bytes4 interfaceid) view returns (bool)",
"function symbol() view returns (string)",
"function tokenURI(uint256 tokenId) pure returns (string)",
"function totalSupply() view returns (uint256)",
"function transferFrom(address from, address to, uint256 tokenId)",
],
};
/**
* Jsdoc comments will be included
* @property mint() Allows an address to mint
*/
export const ERC20 = createContract(_wagmimintexample);

The TypeScript plugin generates a similar dts file.

Bundler integrations

The @tevm/base-bundler is used to create the following bundler integrations. Click on your bundler of choice to see the reference docs for your bundler.

If your bundler is not supported consider opening an issue as it is likely a small lift to add support.

LSP

Once you integrate a bundler your code will run correctly but you will still see diagnostics (red underlines) on your solidity imports. This is from your editor’s LSP. LSP (language server protocol) is a standard first created by VSCode that is now used by most editors including Vim, Neovim, Jetbrains, Sublime, and more. Tevm supports solidity imports via a custom typescript plugin called tevm/ts-plugin.

Configuring the LSP is easy just simply tevm/bundler/ts-plugin to your tsconfig plugins

{
"compilerOptions": {
"plugins": [{ "name": "@tevm/ts-plugin" }]
}
}

Tevm respects the baseUrl and paths property in the tsconfig.

If you configure your LSP and are still seeing issues importing solidity files you may need to configure your editor. Vim and Neovim should work out the box but VSCode users specifically will need to follow the below instructions.

Codegen

Tevm has the ability to codegen typescript files next to the solidity file. This can be helpful if you prefer to commit the TypeScript, wish to run the typechecker with tsc, or your build tool does not support plugins. To use install @tevm/ts-plugin and run the npx tevm-codegen or node node_modules/@tevm/ts-plugin/dist/bin/tevm-gen.js command.

By default the command will look for solidity files in src/**/*.sol relative to cwd.

VSCode

If you are using vscode you will need to configure typescript to use local version

  1. Open command pallette and type in following
> Typescript: Select Typescript version
  1. Then select workspace version
Use workspace version 5.x.x

Foundry integration

Tevm always compiles contracts itself but it has the ability to read foundry remappings lib etc. if you set foundry: true. Doing this will require having foundry installed as it uses foundry config --json to read the foundry remappings and lib options.

Tevm is currently unable to resolve nested foundry projects such as installing a foundry project with NPM. Support for automatic foundry and hardhat project detection is planned for future.

tevm.config.json

Tevm compiler offers some advanced configuration via the tevm.config.json file. This single configuration will be read by both your bundler and the typescript plugin. For configuration options see the CompilerConfig reference docs.

🚧 Typechecking

Language server plugins operate in your editor but not when doing command line typechecking. Command line typechecking similar to how volar based frameworks like vue or svelte do it is an important feature coming soon.

Tevm cache

Tevm caches it’s build artifacts to avoid wasted recompilations. The Tevm cache can be found in .tevm folder. It is a good resource for debugging when something goes wrong. Please add this folder to your git ignore.

Best practice

Importing solidity directly is a convenience for when you are developing scripts and contracts within the same codebase as your javascript. It is NOT recomended to copy paste contracts just to use them with the tevm bundler. Instead consider the following options if the contract isn’t in your code base.

If the contract you wish to use is external to your code base here are the options:

  1. If contract is on npm or github you can npm install the package and then import it from node_modules. The tevm compiler supports node_resolution to import from other monorepo packages and node_modules
  2. In future versions whatsabi integration will allow you to generate the contracts via pointing at a block explorer
  3. Finally the most manual way of creating a contract is to use human readable abi for any contract methods you need using createScript or createContract <- TODO link to reference docs. You only need to include the methods you wish to use