accounts/abi/abigend: ensure that the bindings produced for a single contract are deterministic.

This commit is contained in:
Jared Wasinger 2025-02-04 16:13:58 -08:00
parent 484164968c
commit 14f84d35c8

View file

@ -23,6 +23,7 @@ import (
"reflect" "reflect"
"regexp" "regexp"
"slices" "slices"
"sort"
"strings" "strings"
"text/template" "text/template"
"unicode" "unicode"
@ -245,6 +246,23 @@ func parseLibraryDeps(unlinkedCode string) (res []string) {
return res return res
} }
// iterSorted iterates the map in the lexicographic order of the keys calling onItem on each. If the callback returns
// an error, iteration is halted and the error is returned from iterSorted.
func iterSorted[V any](inp map[string]V, onItem func(string, V) error) error {
var sortedKeys []string
for key := range inp {
sortedKeys = append(sortedKeys, key)
}
sort.Strings(sortedKeys)
for _, key := range sortedKeys {
if err := onItem(key, inp[key]); err != nil {
return err
}
}
return nil
}
// BindV2 generates a Go wrapper around a contract ABI. This wrapper isn't meant // BindV2 generates a Go wrapper around a contract ABI. This wrapper isn't meant
// to be used as is in client code, but rather as an intermediate struct which // to be used as is in client code, but rather as an intermediate struct which
// enforces compile time type safety and naming convention as opposed to having to // enforces compile time type safety and naming convention as opposed to having to
@ -269,21 +287,25 @@ func BindV2(types []string, abis []string, bytecodes []string, pkg string, libs
} }
cb := newContractBinder(&b) cb := newContractBinder(&b)
for _, original := range evmABI.Methods { err = iterSorted(evmABI.Methods, func(_ string, original abi.Method) error {
if err := cb.bindMethod(original); err != nil { return cb.bindMethod(original)
return "", err })
} if err != nil {
return "", err
} }
for _, original := range evmABI.Events { err = iterSorted(evmABI.Events, func(_ string, original abi.Event) error {
if err := cb.bindEvent(original); err != nil { return cb.bindEvent(original)
return "", err })
} if err != nil {
return "", err
} }
for _, original := range evmABI.Errors {
if err := cb.bindError(original); err != nil { err = iterSorted(evmABI.Errors, func(_ string, original abi.Error) error {
return "", err return cb.bindError(original)
} })
if err != nil {
return "", err
} }
b.contracts[types[i]] = newTmplContractV2(types[i], abis[i], bytecodes[i], evmABI.Constructor, cb) b.contracts[types[i]] = newTmplContractV2(types[i], abis[i], bytecodes[i], evmABI.Constructor, cb)