Documentation ¶
Overview ¶
Package w3vm provides a VM for executing EVM messages.
Index ¶
- Variables
- func RandA() (addr common.Address)
- func Slot(pos, key common.Hash) common.Hash
- func Slot2(pos, key, key2 common.Hash) common.Hash
- func WETHAllowanceSlot(owner, spender common.Address) common.Hash
- func WETHBalanceSlot(addr common.Address) common.Hash
- type AccessList
- type CallFuncFactory
- type Fetcher
- type Option
- func WithBlockContext(ctx *vm.BlockContext) Option
- func WithChainConfig(cfg *params.ChainConfig) Option
- func WithFetcher(fetcher Fetcher) Option
- func WithFork(client *w3.Client, blockNumber *big.Int) Option
- func WithHeader(header *types.Header) Option
- func WithNoBaseFee() Option
- func WithState(state w3types.State) Option
- func WithTB(tb testing.TB) Option
- type Receipt
- type VM
- func (vm *VM) Apply(msg *w3types.Message, hooks ...*tracing.Hooks) (*Receipt, error)
- func (vm *VM) ApplyTx(tx *types.Transaction, hooks ...*tracing.Hooks) (*Receipt, error)
- func (vm *VM) Balance(addr common.Address) (*big.Int, error)
- func (vm *VM) Call(msg *w3types.Message, hooks ...*tracing.Hooks) (*Receipt, error)
- func (vm *VM) CallFunc(contract common.Address, f w3types.Func, args ...any) *CallFuncFactory
- func (vm *VM) Code(addr common.Address) ([]byte, error)
- func (vm *VM) CreateAccessList(tx *w3types.Message) (*types.AccessList, error)
- func (vm *VM) CreateAccessListRPC(msg *w3types.Message, blockNumber *big.Int) (*types.AccessList, error)
- func (vm *VM) Nonce(addr common.Address) (uint64, error)
- func (vm *VM) Rollback(snapshot *state.StateDB)
- func (vm *VM) Snapshot() *state.StateDB
- func (vm *VM) StorageAt(addr common.Address, slot common.Hash) (common.Hash, error)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrFetch = errors.New("fetching failed") ErrRevert = errors.New("execution reverted") )
var ErrMissingFunc = errors.New("missing function")
Functions ¶
func WETHAllowanceSlot ¶
WETHAllowanceSlot returns the storage slot that stores the WETH allowance of the given owner and spender.
func WETHBalanceSlot ¶
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/meltingclock/w3" "github.com/meltingclock/w3/w3types" "github.com/meltingclock/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 AccessList ¶
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 { // Account fetches the account of the given address. Account(common.Address) (*types.StateAccount, error) // Code fetches the code of the given code hash. Code(common.Hash) ([]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(uint64) (common.Hash, error) }
Fetcher is the interface to access account state of a blockchain.
func NewRPCFetcher ¶
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.
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.
If not explicitly set, the chain config is set to params.MainnetChainConfig.
func WithFetcher ¶
WithFetcher sets the fetcher for the VM.
func WithFork ¶
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 ¶
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.
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 ¶
DecodeReturns is like w3types.Func.DecodeReturns, but returns ErrMissingFunc if the underlying w3types.Message.Func is nil.
type VM ¶
type VM struct {
// contains filtered or unexported fields
}
Example ¶
package main import ( "fmt" "math/big" "time" _ "embed" "github.com/ethereum/go-ethereum/common" "github.com/meltingclock/w3" "github.com/meltingclock/w3/w3types" "github.com/meltingclock/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 (*VM) Apply ¶
Apply the given message to the VM and return its receipt. Multiple tracing hooks can be given to trace the execution of the message.
func (*VM) Call ¶
Call calls the given message on the VM and returns a receipt. Any state changes of a call are reverted. Multiple tracing hooks 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/meltingclock/w3" "github.com/meltingclock/w3/w3types" "github.com/meltingclock/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 ¶
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) CreateAccessList ¶
CreateAccessList generates an access list for the given contract call.
func (*VM) CreateAccessListRPC ¶
func (vm *VM) CreateAccessListRPC(msg *w3types.Message, blockNumber *big.Int) (*types.AccessList, error)
CreateAccessList generates an access list for the given contract call using eth_createAccessList RPC method.
func (*VM) Snapshot ¶
Snapshot the current state of the VM. The returned state can only be rolled back to once. Use state.StateDB.Copy if you need to rollback multiple times.