Skip to content

Commit 3d0330d

Browse files
committed
accounts/abi: abigen v2
1 parent e1b98f4 commit 3d0330d

File tree

6 files changed

+464
-49
lines changed

6 files changed

+464
-49
lines changed

accounts/abi/bind/base.go

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,6 @@ func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend Co
149149
// returns, a slice of interfaces for anonymous returns and a struct for named
150150
// returns.
151151
func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error {
152-
// Don't crash on a lazy user
153-
if opts == nil {
154-
opts = new(CallOpts)
155-
}
156152
if results == nil {
157153
results = new([]interface{})
158154
}
@@ -161,51 +157,64 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
161157
if err != nil {
162158
return err
163159
}
160+
output, err := c.call(opts, input)
161+
if err != nil {
162+
return err
163+
}
164+
165+
if len(*results) == 0 {
166+
res, err := c.abi.Unpack(method, output)
167+
*results = res
168+
return err
169+
}
170+
res := *results
171+
return c.abi.UnpackIntoInterface(res[0], method, output)
172+
}
173+
174+
func (c *BoundContract) call(opts *CallOpts, input []byte) ([]byte, error) {
175+
// Don't crash on a lazy user
176+
if opts == nil {
177+
opts = new(CallOpts)
178+
}
164179
var (
165180
msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
166181
ctx = ensureContext(opts.Context)
167182
code []byte
168183
output []byte
184+
err error
169185
)
170186
if opts.Pending {
171187
pb, ok := c.caller.(PendingContractCaller)
172188
if !ok {
173-
return ErrNoPendingState
189+
return nil, ErrNoPendingState
174190
}
175191
output, err = pb.PendingCallContract(ctx, msg)
176192
if err != nil {
177-
return err
193+
return nil, err
178194
}
179195
if len(output) == 0 {
180196
// Make sure we have a contract to operate on, and bail out otherwise.
181197
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
182-
return err
198+
return nil, err
183199
} else if len(code) == 0 {
184-
return ErrNoCode
200+
return nil, ErrNoCode
185201
}
186202
}
187203
} else {
188204
output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
189205
if err != nil {
190-
return err
206+
return nil, err
191207
}
192208
if len(output) == 0 {
193209
// Make sure we have a contract to operate on, and bail out otherwise.
194210
if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
195-
return err
211+
return nil, err
196212
} else if len(code) == 0 {
197-
return ErrNoCode
213+
return nil, ErrNoCode
198214
}
199215
}
200216
}
201-
202-
if len(*results) == 0 {
203-
res, err := c.abi.Unpack(method, output)
204-
*results = res
205-
return err
206-
}
207-
res := *results
208-
return c.abi.UnpackIntoInterface(res[0], method, output)
217+
return output, nil
209218
}
210219

211220
// Transact invokes the (paid) contract method with params as input values.
@@ -409,13 +418,16 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
409418
// FilterLogs filters contract logs for past blocks, returning the necessary
410419
// channels to construct a strongly typed bound iterator on top of them.
411420
func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
421+
return c.filterLogs(opts, c.abi.Events[name].ID, query...)
422+
}
423+
424+
func (c *BoundContract) filterLogs(opts *FilterOpts, eventID common.Hash, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
412425
// Don't crash on a lazy user
413426
if opts == nil {
414427
opts = new(FilterOpts)
415428
}
416429
// Append the event selector to the query parameters and construct the topic set
417-
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
418-
430+
query = append([][]interface{}{{eventID}}, query...)
419431
topics, err := abi.MakeTopics(query...)
420432
if err != nil {
421433
return nil, nil, err
@@ -458,12 +470,16 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
458470
// WatchLogs filters subscribes to contract logs for future blocks, returning a
459471
// subscription object that can be used to tear down the watcher.
460472
func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
473+
return c.watchLogs(opts, c.abi.Events[name].ID, query...)
474+
}
475+
476+
func (c *BoundContract) watchLogs(opts *WatchOpts, eventID common.Hash, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
461477
// Don't crash on a lazy user
462478
if opts == nil {
463479
opts = new(WatchOpts)
464480
}
465481
// Append the event selector to the query parameters and construct the topic set
466-
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
482+
query = append([][]interface{}{{eventID}}, query...)
467483

468484
topics, err := abi.MakeTopics(query...)
469485
if err != nil {

accounts/abi/bind/bind.go

Lines changed: 99 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,100 @@ func isKeyWord(arg string) bool {
8282
// enforces compile time type safety and naming convention opposed to having to
8383
// manually maintain hard coded strings that break on runtime.
8484
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
85+
data, err := bind(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases)
86+
if err != nil {
87+
return "", err
88+
}
89+
buffer := new(bytes.Buffer)
90+
91+
funcs := map[string]interface{}{
92+
"bindtype": bindType[lang],
93+
"bindtopictype": bindTopicType[lang],
94+
"namedtype": namedType[lang],
95+
"capitalise": capitalise,
96+
"decapitalise": decapitalise,
97+
}
98+
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
99+
if err := tmpl.Execute(buffer, data); err != nil {
100+
return "", err
101+
}
102+
// For Go bindings pass the code through gofmt to clean it up
103+
if lang == LangGo {
104+
code, err := format.Source(buffer.Bytes())
105+
if err != nil {
106+
return "", fmt.Errorf("%v\n%s", err, buffer)
107+
}
108+
return string(code), nil
109+
}
110+
// For all others just return as is for now
111+
return buffer.String(), nil
112+
}
113+
114+
func BindV2(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
115+
data, err := bind(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases)
116+
if err != nil {
117+
return "", err
118+
}
119+
for _, c := range data.Contracts {
120+
// We want pack/unpack methods for all existing methods.
121+
for name, t := range c.Transacts {
122+
c.Calls[name] = t
123+
}
124+
c.Transacts = nil
125+
126+
// Make sure we return one argument. If multiple exist
127+
// merge them into a struct.
128+
for _, call := range c.Calls {
129+
if call.Structured {
130+
continue
131+
}
132+
if len(call.Normalized.Outputs) == 1 {
133+
continue
134+
}
135+
// Build up dictionary of existing arg names.
136+
keys := make(map[string]struct{})
137+
for _, o := range call.Normalized.Outputs {
138+
if o.Name != "" {
139+
keys[strings.ToLower(o.Name)] = struct{}{}
140+
}
141+
}
142+
// Assign names to anonymous fields.
143+
for i, o := range call.Normalized.Outputs {
144+
if o.Name != "" {
145+
continue
146+
}
147+
o.Name = capitalise(abi.ResolveNameConflict("arg", func(name string) bool { _, ok := keys[name]; return ok }))
148+
call.Normalized.Outputs[i] = o
149+
keys[strings.ToLower(o.Name)] = struct{}{}
150+
}
151+
call.Structured = true
152+
}
153+
}
154+
buffer := new(bytes.Buffer)
155+
funcs := map[string]interface{}{
156+
"bindtype": bindType[lang],
157+
"bindtopictype": bindTopicType[lang],
158+
"namedtype": namedType[lang],
159+
"capitalise": capitalise,
160+
"decapitalise": decapitalise,
161+
}
162+
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSourceV2[lang]))
163+
if err := tmpl.Execute(buffer, data); err != nil {
164+
return "", err
165+
}
166+
// For Go bindings pass the code through gofmt to clean it up
167+
if lang == LangGo {
168+
code, err := format.Source(buffer.Bytes())
169+
if err != nil {
170+
return "", fmt.Errorf("%v\n%s", err, buffer)
171+
}
172+
return string(code), nil
173+
}
174+
// For all others just return as is for now
175+
return buffer.String(), nil
176+
}
177+
178+
func bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (*tmplData, error) {
85179
var (
86180
// contracts is the map of each individual contract requested binding
87181
contracts = make(map[string]*tmplContract)
@@ -96,7 +190,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
96190
// Parse the actual ABI to generate the binding for
97191
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
98192
if err != nil {
99-
return "", err
193+
return nil, err
100194
}
101195
// Strip any whitespace from the JSON ABI
102196
strippedABI := strings.Map(func(r rune) rune {
@@ -140,7 +234,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
140234
identifiers = transactIdentifiers
141235
}
142236
if identifiers[normalizedName] {
143-
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
237+
return nil, fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
144238
}
145239
identifiers[normalizedName] = true
146240

@@ -183,7 +277,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
183277
// Ensure there is no duplicated identifier
184278
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
185279
if eventIdentifiers[normalizedName] {
186-
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
280+
return nil, fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
187281
}
188282
eventIdentifiers[normalizedName] = true
189283
normalized.Name = normalizedName
@@ -218,6 +312,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
218312
if evmABI.HasReceive() {
219313
receive = &tmplMethod{Original: evmABI.Receive}
220314
}
315+
221316
contracts[types[i]] = &tmplContract{
222317
Type: capitalise(types[i]),
223318
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
@@ -262,29 +357,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
262357
Libraries: libs,
263358
Structs: structs,
264359
}
265-
buffer := new(bytes.Buffer)
266-
267-
funcs := map[string]interface{}{
268-
"bindtype": bindType[lang],
269-
"bindtopictype": bindTopicType[lang],
270-
"namedtype": namedType[lang],
271-
"capitalise": capitalise,
272-
"decapitalise": decapitalise,
273-
}
274-
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
275-
if err := tmpl.Execute(buffer, data); err != nil {
276-
return "", err
277-
}
278-
// For Go bindings pass the code through gofmt to clean it up
279-
if lang == LangGo {
280-
code, err := format.Source(buffer.Bytes())
281-
if err != nil {
282-
return "", fmt.Errorf("%v\n%s", err, buffer)
283-
}
284-
return string(code), nil
285-
}
286-
// For all others just return as is for now
287-
return buffer.String(), nil
360+
return data, nil
288361
}
289362

290363
// bindType is a set of type binders that convert Solidity types to some supported

0 commit comments

Comments
 (0)