// Copyright 2016 The go-ethereum Authors // This file is part of go-ethereum. // // go-ethereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // go-ethereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with go-ethereum. If not, see . package main import ( "encoding/json" "fmt" "io" "os" "path/filepath" "regexp" "strings" "github.com/ledgerwatch/log/v3" "github.com/urfave/cli/v2" "github.com/ledgerwatch/erigon/accounts/abi" "github.com/ledgerwatch/erigon/accounts/abi/bind" "github.com/ledgerwatch/erigon/cmd/utils" "github.com/ledgerwatch/erigon/common/compiler" "github.com/ledgerwatch/erigon/crypto" "github.com/ledgerwatch/erigon/params" cli2 "github.com/ledgerwatch/erigon/turbo/cli" ) var ( app *cli.App // Flags needed by abigen abiFlag = cli.StringFlag{ Name: "abi", Usage: "Path to the Ethereum contract ABI json to bind, - for STDIN", } binFlag = cli.StringFlag{ Name: "bin", Usage: "Path to the Ethereum contract bytecode (generate deploy method)", } typeFlag = cli.StringFlag{ Name: "type", Usage: "Struct name for the binding (default = package name)", } jsonFlag = cli.StringFlag{ Name: "combined-json", Usage: "Path to the combined-json file generated by compiler", } solFlag = cli.StringFlag{ Name: "sol", Usage: "Path to the Ethereum contract Solidity source to build and bind", } solcFlag = cli.StringFlag{ Name: "solc", Usage: "Solidity compiler to use if source builds are requested", Value: "solc", } vyFlag = cli.StringFlag{ Name: "vy", Usage: "Path to the Ethereum contract Vyper source to build and bind", } vyperFlag = cli.StringFlag{ Name: "vyper", Usage: "Vyper compiler to use if source builds are requested", Value: "vyper", } excFlag = cli.StringFlag{ Name: "exc", Usage: "Comma separated types to exclude from binding", } pkgFlag = cli.StringFlag{ Name: "pkg", Usage: "Package name to generate the binding into", } outFlag = cli.StringFlag{ Name: "out", Usage: "Output file for the generated binding (default = stdout)", } langFlag = cli.StringFlag{ Name: "lang", Usage: "Destination language for the bindings (go, java, objc)", Value: "go", } aliasFlag = cli.StringFlag{ Name: "alias", Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2", } ) func init() { app = cli2.NewApp(params.GitCommit, "ethereum checkpoint helper tool") app.Flags = []cli.Flag{ &abiFlag, &binFlag, &typeFlag, &jsonFlag, &solFlag, &solcFlag, &vyFlag, &vyperFlag, &excFlag, &pkgFlag, &outFlag, &langFlag, &aliasFlag, } app.Action = abigen } func abigen(c *cli.Context) error { //fmt.Printf("a? %s\n", c.FlagNames()) utils.CheckExclusive(c, &abiFlag, &jsonFlag, &solFlag, &vyFlag) // Only one source can be selected. if c.String(pkgFlag.Name) == "" { utils.Fatalf("No destination package specified (--pkg)") } var lang bind.Lang switch c.String(langFlag.Name) { case "go": lang = bind.LangGo case "java": lang = bind.LangJava case "objc": lang = bind.LangObjC utils.Fatalf("Objc binding generation is uncompleted") default: utils.Fatalf("Unsupported destination language \"%s\" (--lang)", c.String(langFlag.Name)) } // If the entire solidity code was specified, build and bind based on that var ( abis []string bins []string types []string sigs []map[string]string libs = make(map[string]string) aliases = make(map[string]string) ) if c.String(abiFlag.Name) != "" { // Load up the ABI, optional bytecode and type name from the parameters var ( abiBytes []byte err error ) input := c.String(abiFlag.Name) if input == "-" { abiBytes, err = io.ReadAll(os.Stdin) } else { abiBytes, err = os.ReadFile(input) } if err != nil { utils.Fatalf("Failed to read input ABI: %v", err) } abis = append(abis, string(abiBytes)) var bin []byte if binFile := c.String(binFlag.Name); binFile != "" { if bin, err = os.ReadFile(binFile); err != nil { utils.Fatalf("Failed to read input bytecode: %v", err) } if strings.Contains(string(bin), "//") { utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos") } } bins = append(bins, string(bin)) kind := c.String(typeFlag.Name) if kind == "" { kind = c.String(pkgFlag.Name) } types = append(types, kind) } else { // Generate the list of types to exclude from binding exclude := make(map[string]bool) for _, kind := range strings.Split(c.String(excFlag.Name), ",") { exclude[strings.ToLower(kind)] = true } var err error var contracts map[string]*compiler.Contract switch { case c.IsSet(solFlag.Name): contracts, err = compiler.CompileSolidity(c.String(solcFlag.Name), c.String(solFlag.Name)) if err != nil { utils.Fatalf("Failed to build Solidity contract: %v", err) } case c.IsSet(vyFlag.Name): output, err := compiler.CompileVyper(c.String(vyperFlag.Name), c.String(vyFlag.Name)) if err != nil { utils.Fatalf("Failed to build Vyper contract: %v", err) } contracts = make(map[string]*compiler.Contract) for n, contract := range output { name := n // Sanitize the combined json names to match the // format expected by solidity. if !strings.Contains(n, ":") { // Remove extra path components name = abi.ToCamelCase(strings.TrimSuffix(filepath.Base(name), ".vy")) } contracts[name] = contract } case c.IsSet(jsonFlag.Name): jsonOutput, err := os.ReadFile(c.String(jsonFlag.Name)) if err != nil { utils.Fatalf("Failed to read combined-json from compiler: %v", err) } contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "") if err != nil { utils.Fatalf("Failed to read contract information from json output: %v", err) } } // Gather all non-excluded contract for binding for name, contract := range contracts { if exclude[strings.ToLower(name)] { continue } abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse if err != nil { utils.Fatalf("Failed to parse ABIs from compiler output: %v", err) } abis = append(abis, string(abi)) bins = append(bins, contract.Code) sigs = append(sigs, contract.Hashes) nameParts := strings.Split(name, ":") types = append(types, nameParts[len(nameParts)-1]) libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36] libs[libPattern] = nameParts[len(nameParts)-1] } } // Extract all aliases from the flags if c.IsSet(aliasFlag.Name) { // We support multi-versions for aliasing // e.g. // foo=bar,foo2=bar2 // foo:bar,foo2:bar2 re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`) submatches := re.FindAllStringSubmatch(c.String(aliasFlag.Name), -1) for _, match := range submatches { aliases[match[1]] = match[2] } } // Generate the contract binding code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs, aliases) if err != nil { utils.Fatalf("Failed to generate ABI binding: %v", err) } // Either flush it out to a file or display on the standard output if !c.IsSet(outFlag.Name) { fmt.Printf("%s\n", code) return nil } if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0600); err != nil { utils.Fatalf("Failed to write ABI binding: %v", err) } return nil } func main() { log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StderrHandler)) if err := app.Run(os.Args); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }