diff --git a/accounts/abi/abigen/bindv2.go b/accounts/abi/abigen/bindv2.go index ef4b769bb4..d534a4608f 100644 --- a/accounts/abi/abigen/bindv2.go +++ b/accounts/abi/abigen/bindv2.go @@ -294,7 +294,7 @@ func iterSorted[V any](inp map[string]V, onItem func(string, V) error) error { // 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 // manually maintain hard coded strings that break on runtime. -func BindV2(types []string, abis []string, bytecodes []string, pkg string, libs map[string]string, aliases map[string]string) (string, error) { +func BindV2(types []string, abis []string, bytecodes []string, pkg string, libs map[string]string, aliases map[string]string, templateContent string) (string, error) { b := binder{ contracts: make(map[string]*tmplContractV2), structs: make(map[string]*tmplStruct), @@ -360,7 +360,7 @@ func BindV2(types []string, abis []string, bytecodes []string, pkg string, libs "ispointertype": isPointerType, "underlyingbindtype": underlyingBindType, } - tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSourceV2)) + tmpl := template.Must(template.New("").Funcs(funcs).Parse(templateContent)) if err := tmpl.Execute(buffer, data); err != nil { return "", err } diff --git a/accounts/abi/abigen/bindv2_test.go b/accounts/abi/abigen/bindv2_test.go index 2756ba0835..933dac859d 100644 --- a/accounts/abi/abigen/bindv2_test.go +++ b/accounts/abi/abigen/bindv2_test.go @@ -58,7 +58,7 @@ func bindCombinedJSON(test *bindV2Test) (string, error) { if test.aliases == nil { test.aliases = make(map[string]string) } - code, err := BindV2(types, abis, bins, "bindtests", libs, test.aliases) + code, err := BindV2(types, abis, bins, "bindtests", libs, test.aliases, TmplSourceV2) if err != nil { return "", fmt.Errorf("error creating bindings: %v", err) } diff --git a/accounts/abi/abigen/template.go b/accounts/abi/abigen/template.go index cbb21037a6..5797eba66e 100644 --- a/accounts/abi/abigen/template.go +++ b/accounts/abi/abigen/template.go @@ -133,4 +133,4 @@ var tmplSource string // for abigen v2 is based on. // //go:embed source2.go.tpl -var tmplSourceV2 string +var TmplSourceV2 string diff --git a/accounts/abi/bind/v2/generate_test.go b/accounts/abi/bind/v2/generate_test.go index ae35e0b475..a24b3f6b8d 100644 --- a/accounts/abi/bind/v2/generate_test.go +++ b/accounts/abi/bind/v2/generate_test.go @@ -40,6 +40,26 @@ import ( // TestBindingGeneration tests that re-running generation of bindings does not result in // mutations to the binding code. func TestBindingGeneration(t *testing.T) { + BindGenerationWithExpectedOutput(t, abigen.TmplSourceV2, getCodeFromBindingsDotGo) +} + +func TestBindingGenerationWithDifferentTemplate(t *testing.T) { + BindGenerationWithExpectedOutput(t, "package {{.Package}} //test template", getHarcodedCodeFromBindingsDotGo) +} + +func getHarcodedCodeFromBindingsDotGo(t *testing.T, basePath string) string { + return "package " + strings.Replace(basePath, "internal/contracts/", "", -1) + " //test template\n" +} + +func getCodeFromBindingsDotGo(t *testing.T, basePath string) string { + existingBindings, err := os.ReadFile(filepath.Join(basePath, "bindings.go")) + if err != nil { + t.Fatalf("ReadFile returned error: %v", err) + } + return string(existingBindings) +} + +func BindGenerationWithExpectedOutput(t *testing.T, templateContent string, getExpectedCodeFunc func(t *testing.T, basePath string) string) { matches, _ := filepath.Glob("internal/contracts/*") var dirs []string for _, match := range matches { @@ -86,16 +106,13 @@ func TestBindingGeneration(t *testing.T) { libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36] // the first 2 chars are 0x libs[libPattern] = typeName } - code, err := abigen.BindV2(types, abis, bins, dir, libs, make(map[string]string)) + code, err := abigen.BindV2(types, abis, bins, dir, libs, make(map[string]string), templateContent) if err != nil { t.Fatalf("error creating bindings for package %s: %v", dir, err) } - existingBindings, err := os.ReadFile(filepath.Join(basePath, "bindings.go")) - if err != nil { - t.Fatalf("ReadFile returned error: %v", err) - } - if code != string(existingBindings) { + expectedCode := getExpectedCodeFunc(t, basePath) + if code != expectedCode { t.Fatalf("code mismatch for %s", dir) } } diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index c82358be49..f03cd401fc 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -67,6 +67,10 @@ var ( Name: "alias", Usage: "Comma separated aliases for function and event renaming. If --v2 is set, errors are aliased as well. e.g. original1=alias1, original2=alias2", } + templateFlag = &cli.StringFlag{ + Name: "template", + Usage: "Input file to use as template for generating bindings", + } v2Flag = &cli.BoolFlag{ Name: "v2", Usage: "Generates v2 bindings", @@ -86,6 +90,7 @@ func init() { pkgFlag, outFlag, aliasFlag, + templateFlag, v2Flag, } app.Action = generate @@ -100,6 +105,9 @@ func generate(c *cli.Context) error { if c.String(abiFlag.Name) == "" && c.String(jsonFlag.Name) == "" { utils.Fatalf("Either contract ABI source (--abi) or combined-json (--combined-json) are required") } + if !c.Bool(v2Flag.Name) && c.IsSet(templateFlag.Name) { + utils.Fatalf("Template configurabion only works with abigen v2") + } // If the entire solidity code was specified, build and bind based on that var ( abis []string @@ -216,7 +224,16 @@ func generate(c *cli.Context) error { err error ) if c.IsSet(v2Flag.Name) { - code, err = abigen.BindV2(types, abis, bins, c.String(pkgFlag.Name), libs, aliases) + template := abigen.TmplSourceV2 + if c.IsSet(templateFlag.Name) { + templateLoc := c.String(templateFlag.Name) + templateData, err := os.ReadFile(templateLoc) + if err != nil { + utils.Fatalf("Error reading template file: %s, error: %", templateLoc, err) + } + template = string(templateData) + } + code, err = abigen.BindV2(types, abis, bins, c.String(pkgFlag.Name), libs, aliases, template) } else { code, err = abigen.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), libs, aliases) }