VM
w3vm.VM
is an easy-to-use Ethereum Virtual Machine (EVM), built on top of go-ethereum
’s vm.EVM
. It supports tracing, state forking via RPC, and can be used for simulation, debugging EVM execution, or testing Smart Contracts.
- State forking via RPC or custom state fetchers enables transaction simulations or Smart Contract tests on live, or historical chain state.
- Tracing of EVM execution is supported via
go-ethereum/core/tracing.Hooks
.
Get Started
Create a VM Instance
Create a VM instance, that forks the latest Mainnet state.
client, err := w3.Dial("https://rpc.ankr.com/eth")
if err != nil {
// ...
}
defer client.Close()
vm, err := w3vm.New(
w3vm.WithFork(client, nil),
w3vm.WithNoBaseFee(),
)
if err != nil {
// ...
}
Simulate a Simple Message
Transfer ETH from the zero address to a random recipient.
recipient := w3vm.RandA()
receipt, err := vm.Apply(&w3types.Message{
From: common.Address{},
To: &recipient,
Value: w3.I("1 ether"),
})
if err != nil {
// ...
}
Verify the Recipient’s Balance
Verify the recipient’s balance after the applied message.
balance, err := vm.Balance(recipient)
if err != nil {
// ...
}
fmt.Printf("Balance: %s ETH\n", w3.FromWei(balance, 18))
// Output: Balance: 1 ETH
Setup
A new VM instance is created using the w3vm.New
function, which accepts various options to customize the VM behavior:
WithChainConfig(cfg *params.ChainConfig)
: Sets the chain configuration. If not provided, the VM defaults to the Mainnet configuration.WithNoBaseFee()
: Forces the EIP-1559 base fee to 0.WithBlockContext(ctx *vm.BlockContext)
: Sets the block context for the VM.WithPrecompile(addr common.Address, contract vm.PrecompiledContract)
: Registers a precompile contract at the given address in the VM.WithHeader(header *types.Header)
: Configures the block context for the VM using the provided header.WithState(state w3types.State)
: Sets the pre-state of the VM. When used withWithFork
, the pre-state overrides the forked state.WithStateDB(db *state.StateDB)
: Specifies the state database for the VM, typically a snapshot fromVM.Snapshot
.WithFork(client *w3.Client, blockNumber *big.Int)
: Forks state from a live Ethereum client at the specified block number.WithFetcher(fetcher Fetcher)
: Assigns a fetcher to the VM.WithTB(tb testing.TB)
: Enables persistent state caching when used in conjunction withWithFork
.
Execution
Messages represent transactions or contract calls that can be executed by the VM.
go-ethereum/core/tracing.Hooks
. Learn more ➔ Apply
Method
Apply
applies a w3types.Message
to the VM and returns a Receipt
. If the execution doesn’t revert, the VM’s underlying state may change.
Example: Apply a Message
msg := &w3types.Message{
From: addrSender,
To: &addrRecipient,
Value: w3.I("1 ether"),
Gas: 21000,
}
receipt, err := vm.Apply(msg)
if err != nil {
// ...
}
fmt.Printf("Gas Used: %d\n", receipt.GasUsed)
ApplyTx
Method
ApplyTx
is like Apply
, but takes a types.Transaction
instead of a message. The given transaction is converted to a message internally, using a signer, that is derived from the VM’s chain configuration and fork block.
Call
Method
Call
is like Apply
, but any state changes during execution are reverted in the end, so the VM’s state is never modified.
Example: Call balanceOf
funcBalanceOf := w3.MustNewFunc("balanceOf(address)", "uint256")
msg := &w3types.Message{
To: &addrToken,
Func: funcBalanceOf,
Args: []any{addrOwner},
}
receipt, err := vm.Call(msg)
if err != nil {
// handle error
}
var balance *big.Int
if err := receipt.DecodeReturns(&balance); err != nil {
// handle error
}
fmt.Printf("Balance: %s\n", balance)
CallFunc
Method
CallFunc
is a helper, that greatly simplifies common usage of Call
. It is designed analogues to the eth.CallFunc
RPC client method.
Example: Call balanceOf
with CallFunc
This is a simplified version of the Call balanceOf
example.
funcBalanceOf := w3.MustNewFunc("balanceOf(address)", "uint256")
var balance *big.Int
err := vm.CallFunc(addrToken, funcBalanceOf, addrOwner).Returns(&balance)
if err != nil {
// handle error
}
fmt.Printf("Balance: %s\n", balance)
Receipt
Type
The Receipt
struct contains the result of an executed message.
Fields
GasUsed uint64
: Gas used for executing the message.GasRefund uint64
: Gas refunded after executing the message.Logs []*types.Log
: Logs emitted while executing the message.Output []byte
: Output of the executed message.ContractAddress *common.Address
: Address of the created contract, if any.Err error
: Execution error, if any.
Methods
DecodeReturns(returns ...any) error
: Decodes the return values. This method only works, if the executed message hadw3types.Message.Func
set.
State
The VM provides methods to read, and write account state.
Reading State
vm.Balance(addr common.Address) (*big.Int, error)
: Returns the balance of the given address.vm.Nonce(addr common.Address) (uint64, error)
: Returns the nonce of the given address.vm.Code(addr common.Address) ([]byte, error)
: Returns the code of the given address.vm.StorageAt(addr common.Address, slot common.Hash) (common.Hash, error)
: Returns the state of the given address at the given storage slot.
An error only can only occur, if the VM fails to fetch state via a w3vm.Fetcher
. Thus, it is safe to ignore the error, if no state fetcher is used by the VM.
Writing State
vm.SetBalance(addr common.Address, balance *big.Int)
: Sets the balance of the given address.vm.SetNonce(addr common.Address, nonce uint64)
: Sets the nonce of the given address.vm.SetCode(addr common.Address, code []byte)
: Sets the code of the given address.vm.SetStorageAt(addr common.Address, slot common.Hash, value common.Hash)
: Sets the state of the given address at the give storage slot.
Helper
w3vm.RandA() common.Address
: Returns a random address.WETHBalanceSlot(addr common.Address) common.Hash
: Returns the storage slot that stores the WETH balance of the given address.WETHAllowanceSlot(owner, spender common.Address) common.Hash
: Returns the storage slot that stores the WETH allowance of the given owner to the spender.Slot(pos, key common.Hash) common.Hash
: Returns the storage slot of a mapping with the given position and key.Slot2(pos, key, key2 common.Hash) common.Hash
: Returns the storage slot of a double mapping with the given position and keys.