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