Skip to content

Contract action creators

Contract action creators

Note: this guide is out of date and will be updated soon

The @tevm/contracts package is an optional module in tevm for cleaning up your contract code. It represents a contract or script and provides a typesafe api for creating actions

In the following diff the added code shows how to dispatch a script action with a contract action creator. The removed code is both how you would do it without an action creator and also the returned value of the action creator.

- const scriptResult = await tevm.script({
- abi: HelloWorld.abi,
- functionName: 'greet',
- args: ['Vitalik'],
- deployedBytecode: HelloWorld.deployedBytecode
- });
+ const scriptResult = await tevm.script(
+ HelloWorld.read.greet('Vitalik')
+ );

Creating a contract with createScript or createContract

There are two ways to create a contract.

  1. Using the createScript or createContract utils.
  2. Automatically generating scripts with the tevm bundler

To create a contract instance pass in it’s human readable ABI and name into createContract. If creating a script you will also need to pass bytecode and deployedBytecode

import { createScript} from 'tevm/contract'
const script = createScript({
name: 'MyScript',
humanReadableAbi: ['function exampleRead() returns (uint256)', ...],
bytecode: '0x123...',
deployedBytecode: '0x123...',
})

Contracts and scripts are created with humanReadableAbi but you can also use a JSON abi via the formatAbi utility.

import { createScript, formatAbi } from 'tevm/contract'
const abi = [
...
] as const
const script = createScript({
name: 'MyScript',
humanReadableAbi: formatAbi(abi),
bytecode: '0x123...',
deployedBytecode: '0x123...',
})

See the contract reference docs for more information on creating contracts.

Contracts vs scripts

There are two types of contracts.

  1. Contracts which are created with createContract
  2. Scripts which are created with createScript

The only difference between the two is Scripts have bytecode and can run without being deployed first. Contracts can only run with the bytecode already deployed to the chain. Contract actions require a to address that is used by the EVM to fetch the bytecode from storage.

Scripts are a superset of contracts and can be used both as scripts (e.g. using their own bytecode) and as contracts (e.g. using their abi against a deployed contract).

Addresses

Contracts by default don’t have any address thus you need to add an address when using a contract action.

await client.contract({
...MyContract.read.balanceOf(address),
to: contractAddress,
});

Tevm contracts have a withAddress method that cleans this up.

await client.contract(
MyContract.withAddress(contractAddress).read.balanceOf(address),
);

If you want to consistently associate this address with this contract you can export the addressed contract from a typescript file.

export {
MyContract: MyContract.withAddress(contractAddress)
}

Usage with other libraries

Contracts and their action creators are intentionally designed to compose well with other libraries outside of Tevm including ethers viem and wagmi.

Because the tevm api tries to stay maximally consistent with the viem api the integration with wagmi and viem is first-class. For example, the below example shows how a read action creator can compose with viem readContract action.

import {createClient, http} from 'viem'
import { createContract } from 'tevm/contract'
const client = createClient({transport: http('https://mainnet.optimism.io')})
const contract = createContract({
name: 'MyScript',
humanReadableAbi: ['function balanceOf(address owner) returns (uint256)', ...],
bytecode: '0x123...',
deployedBytecode: '0x123...',
})
const balance = client.readContract(
contract.read.balanceOf('0x122...')
)