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 }