abigen: adding support for configurable template file for generating bindings

This commit is contained in:
Pablo La Greca 2025-05-05 11:35:32 -03:00
parent b135da2eac
commit ec72537db5
No known key found for this signature in database
5 changed files with 45 additions and 11 deletions

View file

@ -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
}

View file

@ -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)
}

View file

@ -133,4 +133,4 @@ var tmplSource string
// for abigen v2 is based on.
//
//go:embed source2.go.tpl
var tmplSourceV2 string
var TmplSourceV2 string

View file

@ -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)
}
}

View file

@ -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)
}