Familiarity with viem or a similar library like ethers.js
Creating your Tevm project
Create a new project directory.
Initialize your project
Install the runtime dependencies.
Install the buildtime dependencies.
TypeScript is the language we’re using.
Vite provides us a minimal setup to import TypeScript into our HTML and start a dev server.
Create a TypeScript configuration file.
Tevm has these requirements from the TypeScript configuration:
Use strict mode
Support bigint (ES2020 or later)
See the tsconfig docs for more information about these options.
You can use this file.
Create the index.html file.
The HTML file will be the entrypoint to our app.
Add a typescript file.
You will see the HTML file is importing a src/main.ts file in a script tag. Go ahead and add that too.
With the project created, the next step is to create a fork of a real-world blockchain.
The simplest way to do this is the use a MemoryClient, a Viem client that uses an in-memory transport.
Instead of sending requests to an RPC provider like Alchemy this client processes requests with tevm in a local EVM instance running in JavaScript.
Run special scripts that have advanced functionality.
Allow you to view and modify the chain state, so you can mint yourself ETH, run traces, modify storage, etc.
Be Extremely hackable. You can mint yourself ETH, run traces, modify storage, and more.
Replace src/main.ts with the file below, and see you can get the block number from Redstone.
Explanation
Import the functions we need.
Use the app element in index.html.
Create a MemoryClient that forks the Redstone network.
We use Redstone because it does not have throttling.
It is recomended you also pass in a Common chain object when forking.
This improves the performance of fork and guarantees tevm has all the correct chain information such as which EIPs and hardforks to use.
This function actually does the work and runs the app.
This sets the HTML inside the app element.
Specify the content of the content element.
The content is a table of the keys of memoryClient, and their types, and their values.
The table is created using MapReduce.
At this point we start running asynchronous functions and waiting for them to finish, so we change our status to “Working”.
Get the current block number at the time of the fork. Note that while the blockchain continues to update, the tevm fork is “frozen” and does not get those updates.
Update the block number, and change the status to done.
Run the async function.
When we fork a blockchain the block number will be pinned to the block number at the time of the fork.
Any future changes will not be reflected in tevm unless you create another fork.
Actions
As you can see when you expand memoryClient content actions, many of the Viem actions are available under the same name.
For example, you can modify src/main.ts to see how they work.
Explanation
We need an output panel.
A function to write to the output panel.
The second parameter of JSON.stringify lets us replace values that we don’t want JSON.stringify to display.
Here, the function replaces values of type bigint, which are not part of the JSON standard, with strings, which are.
Use the test action setBalance to “give” address 1 ETH.
Report the change on the output panel.
Calling tevm
We want to create transactions and see how they affect the local copy (and therefore how they would affect the blockchain).
To do this we need to call several functions:
setBalance to give an address ETH (only locally, of course).
This is the part of the Greeter contract’s ABI we need.
On a production system you might want to serve it from a separate file, but this is simpler.
Create our source address and provide it with ETH to run transactions.
Use tevmContract to issue a call to a view function.
Normally, we would need to provide the ABI, the address, and name of the function, and the arguments.
However, greet() does not take any arguments, so we can either provide an empty list or just omit the parameter.
The same tevmContract function is also used to send transactions.
setGreeting takes one argument, a string, so we provide that in the args list.
The ability to specify from lets us figure the results of actions by other users, and to anticipate the actions of our user without a need to ask the wallet extension for a signature.
createTransaction lets us specify if tevm should create a transaction.
Here we are modifying the blockchain state, so we need one.
Transactions only modify the blockchain state when they are mined into a block that is then added to the blockchain.
The remainder of the code should be self-explanatory.
We call greet() again to see the new greeting, and then read the block we mined and the transaction inside it.
The transaction data lets us verify that the transaction really did come from address.
Conclusion
At this point you should be able to use tevmClient for the basics, to fork a blockchain and then observe the results of user actions before sending a real-life transaction.
This is just the basic use, there are more advanced things you can do with tevm: