w3vm

package
v0.0.0-...-599e13e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 2, 2024 License: MIT Imports: 37 Imported by: 0

Documentation

Overview

Package w3vm provides a VM for executing EVM messages.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrFetch  = errors.New("fetching failed")
	ErrRevert = errors.New("execution reverted")
)
View Source
var ErrMissingFunc = errors.New("missing function")

Functions

func RandA

func RandA() (addr common.Address)

RandA returns a random address.

func Slot

func Slot(pos, key common.Hash) common.Hash

Slot returns the storage slot of a mapping with the given position and key.

func Slot2

func Slot2(pos, key, key2 common.Hash) common.Hash

Slot2 returns the storage slot of a double mapping with the given position and keys.

func WETHAllowanceSlot

func WETHAllowanceSlot(owner, spender common.Address) common.Hash

WETHAllowanceSlot returns the storage slot that stores the WETH allowance of the given owner and spender.

func WETHBalanceSlot

func WETHBalanceSlot(addr common.Address) common.Hash

WETHBalanceSlot returns the storage slot that stores the WETH balance of the given addr.

Example
package main

import (
	"fmt"
	"math/big"

	"github.com/ethereum/go-ethereum/common"
	"github.com/temuera/w3"
	"github.com/temuera/w3/w3types"
	"github.com/temuera/w3/w3vm"
)

func main() {
	client := w3.MustDial("https://rpc.ankr.com/eth")
	defer client.Close()

	addrC0fe := w3.A("0x000000000000000000000000000000000000c0Fe")
	addrWETH := w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
	funcBalanceOf := w3.MustNewFunc("balanceOf(address)", "uint256")

	vm, err := w3vm.New(
		w3vm.WithFork(client, nil),
		w3vm.WithState(w3types.State{
			addrWETH: {
				Storage: map[common.Hash]common.Hash{
					w3vm.WETHBalanceSlot(addrC0fe): common.BigToHash(w3.I("100 ether")),
				},
			},
		}),
	)
	if err != nil {
		// ...
	}

	var balance *big.Int
	err = vm.CallFunc(addrWETH, funcBalanceOf, addrC0fe).Returns(&balance)
	if err != nil {
		// ...
	}
	fmt.Printf("%s: %s WETH", addrC0fe, w3.FromWei(balance, 18))
}
Output:

0x000000000000000000000000000000000000c0Fe: 100 WETH

Types

type CallFuncFactory

type CallFuncFactory struct {
	// contains filtered or unexported fields
}

func (*CallFuncFactory) Returns

func (cff *CallFuncFactory) Returns(returns ...any) error

type Fetcher

type Fetcher interface {

	// Nonce fetches the nonce of the given address.
	Nonce(common.Address) (uint64, error)

	// Balance fetches the balance of the given address.
	Balance(common.Address) (*big.Int, error)

	// Code fetches the code of the given address.
	Code(common.Address) ([]byte, error)

	// StorageAt fetches the state of the given address and storage slot.
	StorageAt(common.Address, common.Hash) (common.Hash, error)

	// HeaderHash fetches the hash of the header with the given number.
	HeaderHash(*big.Int) (common.Hash, error)
}

Fetcher is the interface to access account state of a blockchain.

func NewRPCFetcher

func NewRPCFetcher(client *w3.Client, blockNumber *big.Int) Fetcher

NewRPCFetcher returns a new Fetcher that fetches account state from the given RPC client for the given block number.

Note, that the returned state for a given block number is the state after the execution of that block.

func NewTestingRPCFetcher

func NewTestingRPCFetcher(tb testing.TB, client *w3.Client, blockNumber *big.Int) Fetcher

NewTestingRPCFetcher returns a new Fetcher like NewRPCFetcher, but caches the fetched state on disk in the testdata directory of the tests package.

type Option

type Option func(*VM)

An Option configures a VM.

func WithBlockContext

func WithBlockContext(ctx *vm.BlockContext) Option

WithBlockContext sets the block context for the VM.

func WithChainConfig

func WithChainConfig(cfg *params.ChainConfig) Option

WithChainConfig sets the chain config for the VM.

func WithFetcher

func WithFetcher(fetcher Fetcher) Option

WithFetcher sets the fetcher for the VM.

func WithFork

func WithFork(client *w3.Client, blockNumber *big.Int) Option

WithFork sets the client and block number to fetch state from and sets the block context for the VM. If the block number is nil, the latest state is fetched and the pending block is used for constructing the block context.

If used together with WithTB, fetched state is stored in the testdata directory of the tests package.

func WithHeader

func WithHeader(header *types.Header) Option

WithHeader sets the block context for the VM based on the given header

func WithNoBaseFee

func WithNoBaseFee() Option

WithNoBaseFee forces the EIP-1559 base fee to 0 for the VM.

func WithState

func WithState(state w3types.State) Option

WithState sets the pre state of the VM.

WithState can be used together with WithFork to only set the state of some accounts, or partially overwrite the storage of an account.

func WithTB

func WithTB(tb testing.TB) Option

WithTB enables persistent state caching when used together with WithFork. State is stored in the testdata directory of the tests package.

type Receipt

type Receipt struct {
	GasUsed         uint64          // Gas used for executing the message
	GasRefund       uint64          // Gas refunded after executing the message
	GasLimit        uint64          // Deprecated: Minimum required gas limit (gas used without refund)
	Logs            []*types.Log    // Logs emitted by the message
	Output          []byte          // Output bytes of the applied message
	ContractAddress *common.Address // Contract address created by a contract creation transaction

	Err error // Revert reason
	// contains filtered or unexported fields
}

Receipt represents the result of an applied w3types.Message.

func (Receipt) DecodeReturns

func (r Receipt) DecodeReturns(returns ...any) error

DecodeReturns is like w3types.Func.DecodeReturns, but returns ErrMissingFunc if the underlying w3types.Message.Func is nil.

type VM

type VM struct {
	DB *gethState.StateDB
	// contains filtered or unexported fields
}
Example
package main

import (
	"fmt"
	"math/big"
	"time"

	_ "embed"
	"github.com/ethereum/go-ethereum/common"
	"github.com/temuera/w3"
	"github.com/temuera/w3/w3types"
	"github.com/temuera/w3/w3vm"
)

func main() {
	var (
		addrEOA    = w3.A("0x000000000000000000000000000000000000c0Fe")
		addrWETH   = w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
		addrUNI    = w3.A("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984")
		addrRouter = w3.A("0xE592427A0AEce92De3Edee1F18E0157C05861564")

		funcExactInput = w3.MustNewFunc(`exactInput(
			(
			bytes path,
			address recipient,
			uint256 deadline,
			uint256 amountIn,
			uint256 amountOutMinimum
			) params
		)`, "uint256 amountOut")
	)

	type ExactInputParams struct {
		Path             []byte
		Recipient        common.Address
		Deadline         *big.Int
		AmountIn         *big.Int
		AmountOutMinimum *big.Int
	}

	encodePath := func(tokenA common.Address, fee uint32, tokenB common.Address) []byte {
		path := make([]byte, 43)
		copy(path, tokenA[:])
		path[20], path[21], path[22] = byte(fee>>16), byte(fee>>8), byte(fee)
		copy(path[23:], tokenB[:])
		return path
	}

	client, err := w3.Dial("https://rpc.ankr.com/eth")
	if err != nil {
		// handle error
	}
	defer client.Close()

	// 1. Create a VM that forks the Mainnet state from the latest block,
	// disables the base fee, and has a fake WETH balance and approval for the router
	vm, err := w3vm.New(
		w3vm.WithFork(client, nil),
		w3vm.WithNoBaseFee(),
		w3vm.WithState(w3types.State{
			addrWETH: {Storage: map[common.Hash]common.Hash{
				w3vm.WETHBalanceSlot(addrEOA):               common.BigToHash(w3.I("1 ether")),
				w3vm.WETHAllowanceSlot(addrEOA, addrRouter): common.BigToHash(w3.I("1 ether")),
			}},
		}),
	)
	if err != nil {
		// handle error
	}

	// 2. Simulate a UniSwap v3 swap
	receipt, err := vm.Apply(&w3types.Message{
		From: addrEOA,
		To:   &addrRouter,
		Func: funcExactInput,
		Args: []any{&ExactInputParams{
			Path:             encodePath(addrWETH, 500, addrUNI),
			Recipient:        addrEOA,
			Deadline:         big.NewInt(time.Now().Unix()),
			AmountIn:         w3.I("1 ether"),
			AmountOutMinimum: w3.Big0,
		}},
	})
	if err != nil {
		// handle error
	}

	// 3. Decode output amount
	var amountOut *big.Int
	if err := receipt.DecodeReturns(&amountOut); err != nil {
		// handle error
	}

	fmt.Printf("amount out: %s UNI\n", w3.FromWei(amountOut, 18))
}
Output:

func New

func New(opts ...Option) (*VM, error)

New creates a new VM, that is configured with the given options.

func (*VM) Apply

func (vm *VM) Apply(msg *w3types.Message, tracers ...vm.EVMLogger) (*Receipt, error)

Apply the given message to the VM and return its receipt. Multiple tracers can be given to trace the execution of the message.

func (*VM) ApplyTx

func (vm *VM) ApplyTx(tx *types.Transaction, tracers ...vm.EVMLogger) (*Receipt, error)

ApplyTx is like VM.Apply, but takes a transaction instead of a message.

func (*VM) Balance

func (vm *VM) Balance(addr common.Address) (*big.Int, error)

Balance returns the balance of the given address.

func (*VM) Call

func (vm *VM) Call(msg *w3types.Message, tracers ...vm.EVMLogger) (*Receipt, error)

Call calls the given message on the VM and returns a receipt. Any state changes of a call are reverted. Multiple tracers can be passed to trace the execution of the message.

Example
package main

import (
	"fmt"
	"math/big"

	_ "embed"
	"github.com/ethereum/go-ethereum/common"
	"github.com/temuera/w3"
	"github.com/temuera/w3/w3types"
	"github.com/temuera/w3/w3vm"
)

func main() {
	client := w3.MustDial("https://rpc.ankr.com/eth")
	defer client.Close()

	addrWETH := w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
	addrEOA := w3.A("0x000000000000000000000000000000000000c0Fe")

	vm, err := w3vm.New(
		w3vm.WithFork(client, nil),
		w3vm.WithState(w3types.State{
			addrWETH: {Storage: map[common.Hash]common.Hash{
				w3vm.WETHBalanceSlot(addrEOA): common.BigToHash(w3.I("1 ether")),
			}},
		}),
	)
	if err != nil {
		// handle error
	}

	balanceOf := w3.MustNewFunc("balanceOf(address)", "uint256")
	var balance *big.Int
	if err := vm.CallFunc(addrWETH, balanceOf, addrEOA).Returns(&balance); err != nil {
		// handle error
	}
	fmt.Printf("%s: Balance: %s WETH\n", addrEOA, w3.FromWei(balance, 18))
}
Output:

0x000000000000000000000000000000000000c0Fe: Balance: 1 WETH

func (*VM) CallFunc

func (vm *VM) CallFunc(contract common.Address, f w3types.Func, args ...any) *CallFuncFactory

CallFunc is a utility function for VM.Call that calls the given function on the given contract address with the given arguments and parses the output into the given returns.

Example:

funcBalanceOf := w3.MustNewFunc("balanceOf(address)", "uint256")

var balance *big.Int
err := vm.CallFunc(contractAddr, funcBalanceOf, addr).Returns(&balance)
if err != nil {
	// ...
}

func (*VM) Code

func (vm *VM) Code(addr common.Address) ([]byte, error)

Code returns the code of the given address.

func (*VM) FastForward

func (vm *VM) FastForward(bn, ts uint64)

func (*VM) Nonce

func (vm *VM) Nonce(addr common.Address) (uint64, error)

Nonce returns the nonce of the given address.

func (*VM) StorageAt

func (vm *VM) StorageAt(addr common.Address, slot common.Hash) (common.Hash, error)

StorageAt returns the state of the given address at the give storage slot.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL