github.com/lmittmann/w3@v0.20.0/module/eth/call.go (about)

     1  package eth
     2  
     3  import (
     4  	"encoding/json"
     5  	"math/big"
     6  
     7  	"github.com/ethereum/go-ethereum/common"
     8  	"github.com/ethereum/go-ethereum/common/hexutil"
     9  	"github.com/ethereum/go-ethereum/core/types"
    10  	"github.com/ethereum/go-ethereum/rpc"
    11  	"github.com/lmittmann/w3/internal/module"
    12  	"github.com/lmittmann/w3/w3types"
    13  )
    14  
    15  // Call requests the output data of the given message at the given blockNumber.
    16  // If blockNumber is nil, the output of the message at the latest known block is
    17  // requested.
    18  func Call(msg *w3types.Message, blockNumber *big.Int, overrides w3types.State) w3types.RPCCallerFactory[[]byte] {
    19  	args := []any{msg, module.BlockNumberArg(blockNumber)}
    20  	if overrides != nil {
    21  		args = append(args, overrides)
    22  	}
    23  
    24  	return module.NewFactory(
    25  		"eth_call",
    26  		args,
    27  		module.WithArgsWrapper[[]byte](msgArgsWrapper),
    28  		module.WithRetWrapper(module.HexBytesRetWrapper),
    29  	)
    30  }
    31  
    32  // EstimateGas requests the estimated gas cost of the given message at the given
    33  // blockNumber. If blockNumber is nil, the estimated gas cost of the message at
    34  // the latest block is requested.
    35  func EstimateGas(msg *w3types.Message, blockNumber *big.Int) w3types.RPCCallerFactory[uint64] {
    36  	return module.NewFactory(
    37  		"eth_estimateGas",
    38  		[]any{msg, module.BlockNumberArg(blockNumber)},
    39  		module.WithArgsWrapper[uint64](msgArgsWrapper),
    40  		module.WithRetWrapper(module.HexUint64RetWrapper),
    41  	)
    42  }
    43  
    44  // AccessList requests the access list of the given message at the given
    45  // blockNumber. If blockNumber is nil, the access list of the message at the
    46  // latest block is requested.
    47  func AccessList(msg *w3types.Message, blockNumber *big.Int) w3types.RPCCallerFactory[*AccessListResponse] {
    48  	return module.NewFactory(
    49  		"eth_createAccessList",
    50  		[]any{msg, module.BlockNumberArg(blockNumber)},
    51  		module.WithArgsWrapper[*AccessListResponse](msgArgsWrapper),
    52  	)
    53  }
    54  
    55  type AccessListResponse struct {
    56  	AccessList types.AccessList
    57  	GasUsed    uint64
    58  }
    59  
    60  // UnmarshalJSON implements the [json.Unmarshaler].
    61  func (resp *AccessListResponse) UnmarshalJSON(data []byte) error {
    62  	type accessListResponse struct {
    63  		AccessList types.AccessList `json:"accessList"`
    64  		GasUsed    hexutil.Uint64   `json:"gasUsed"`
    65  	}
    66  
    67  	var alResp accessListResponse
    68  	if err := json.Unmarshal(data, &alResp); err != nil {
    69  		return err
    70  	}
    71  
    72  	resp.AccessList = alResp.AccessList
    73  	resp.GasUsed = uint64(alResp.GasUsed)
    74  	return nil
    75  }
    76  
    77  func msgArgsWrapper(slice []any) ([]any, error) {
    78  	msg := slice[0].(*w3types.Message)
    79  	if msg.Input != nil || msg.Func == nil {
    80  		return slice, nil
    81  	}
    82  
    83  	input, err := msg.Func.EncodeArgs(msg.Args...)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	msg.Input = input
    88  	slice[0] = msg
    89  	return slice, nil
    90  }
    91  
    92  // CallFunc requests the returns of Func f at common.Address contract with the
    93  // given args.
    94  func CallFunc(contract common.Address, f w3types.Func, args ...any) *CallFuncFactory {
    95  	return &CallFuncFactory{msg: &w3types.Message{
    96  		To:   &contract,
    97  		Func: f,
    98  		Args: args,
    99  	}}
   100  }
   101  
   102  type CallFuncFactory struct {
   103  	// args
   104  	msg       *w3types.Message
   105  	atBlock   *big.Int
   106  	overrides w3types.State
   107  
   108  	// returns
   109  	result  []byte
   110  	returns []any
   111  }
   112  
   113  func (f *CallFuncFactory) Returns(returns ...any) w3types.RPCCaller {
   114  	f.returns = returns
   115  	return f
   116  }
   117  
   118  func (f *CallFuncFactory) From(from common.Address) *CallFuncFactory {
   119  	f.msg.From = from
   120  	return f
   121  }
   122  
   123  func (f *CallFuncFactory) Value(value *big.Int) *CallFuncFactory {
   124  	f.msg.Value = value
   125  	return f
   126  }
   127  
   128  func (f *CallFuncFactory) AtBlock(blockNumber *big.Int) *CallFuncFactory {
   129  	f.atBlock = blockNumber
   130  	return f
   131  }
   132  
   133  func (f *CallFuncFactory) Overrides(overrides w3types.State) *CallFuncFactory {
   134  	f.overrides = overrides
   135  	return f
   136  }
   137  
   138  func (f *CallFuncFactory) CreateRequest() (rpc.BatchElem, error) {
   139  	input, err := f.msg.Func.EncodeArgs(f.msg.Args...)
   140  	if err != nil {
   141  		return rpc.BatchElem{}, err
   142  	}
   143  	f.msg.Input = input
   144  
   145  	args := []any{
   146  		f.msg,
   147  		module.BlockNumberArg(f.atBlock),
   148  	}
   149  	if len(f.overrides) > 0 {
   150  		args = append(args, f.overrides)
   151  	}
   152  
   153  	return rpc.BatchElem{
   154  		Method: "eth_call",
   155  		Args:   args,
   156  		Result: (*hexutil.Bytes)(&f.result),
   157  	}, nil
   158  }
   159  
   160  func (f *CallFuncFactory) HandleResponse(elem rpc.BatchElem) error {
   161  	if err := elem.Error; err != nil {
   162  		return err
   163  	}
   164  
   165  	if err := f.msg.Func.DecodeReturns(f.result, f.returns...); err != nil {
   166  		return err
   167  	}
   168  	return nil
   169  }