github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/cmd/abigen/main.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  	"regexp"
    26  	"strings"
    27  
    28  	"gopkg.in/urfave/cli.v1"
    29  
    30  	"github.com/scroll-tech/go-ethereum/accounts/abi"
    31  	"github.com/scroll-tech/go-ethereum/accounts/abi/bind"
    32  	"github.com/scroll-tech/go-ethereum/cmd/utils"
    33  	"github.com/scroll-tech/go-ethereum/common/compiler"
    34  	"github.com/scroll-tech/go-ethereum/crypto"
    35  	"github.com/scroll-tech/go-ethereum/internal/flags"
    36  	"github.com/scroll-tech/go-ethereum/log"
    37  )
    38  
    39  var (
    40  	// Git SHA1 commit hash of the release (set via linker flags)
    41  	gitCommit = ""
    42  	gitDate   = ""
    43  
    44  	app *cli.App
    45  
    46  	// Flags needed by abigen
    47  	abiFlag = cli.StringFlag{
    48  		Name:  "abi",
    49  		Usage: "Path to the Ethereum contract ABI json to bind, - for STDIN",
    50  	}
    51  	binFlag = cli.StringFlag{
    52  		Name:  "bin",
    53  		Usage: "Path to the Ethereum contract bytecode (generate deploy method)",
    54  	}
    55  	typeFlag = cli.StringFlag{
    56  		Name:  "type",
    57  		Usage: "Struct name for the binding (default = package name)",
    58  	}
    59  	jsonFlag = cli.StringFlag{
    60  		Name:  "combined-json",
    61  		Usage: "Path to the combined-json file generated by compiler",
    62  	}
    63  	solFlag = cli.StringFlag{
    64  		Name:  "sol",
    65  		Usage: "Path to the Ethereum contract Solidity source to build and bind",
    66  	}
    67  	solcFlag = cli.StringFlag{
    68  		Name:  "solc",
    69  		Usage: "Solidity compiler to use if source builds are requested",
    70  		Value: "solc",
    71  	}
    72  	vyFlag = cli.StringFlag{
    73  		Name:  "vy",
    74  		Usage: "Path to the Ethereum contract Vyper source to build and bind",
    75  	}
    76  	vyperFlag = cli.StringFlag{
    77  		Name:  "vyper",
    78  		Usage: "Vyper compiler to use if source builds are requested",
    79  		Value: "vyper",
    80  	}
    81  	excFlag = cli.StringFlag{
    82  		Name:  "exc",
    83  		Usage: "Comma separated types to exclude from binding",
    84  	}
    85  	pkgFlag = cli.StringFlag{
    86  		Name:  "pkg",
    87  		Usage: "Package name to generate the binding into",
    88  	}
    89  	outFlag = cli.StringFlag{
    90  		Name:  "out",
    91  		Usage: "Output file for the generated binding (default = stdout)",
    92  	}
    93  	langFlag = cli.StringFlag{
    94  		Name:  "lang",
    95  		Usage: "Destination language for the bindings (go, java, objc)",
    96  		Value: "go",
    97  	}
    98  	aliasFlag = cli.StringFlag{
    99  		Name:  "alias",
   100  		Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2",
   101  	}
   102  	contractFlag = cli.StringFlag{
   103  		Name:  "contract",
   104  		Usage: "Name of the contract to generate the bindings for",
   105  	}
   106  	tmplFlag = cli.StringFlag{
   107  		Name:  "tmpl",
   108  		Usage: "Template file if a user wants to customize",
   109  	}
   110  )
   111  
   112  func init() {
   113  	app = flags.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
   114  	app.Flags = []cli.Flag{
   115  		abiFlag,
   116  		binFlag,
   117  		typeFlag,
   118  		jsonFlag,
   119  		solFlag,
   120  		solcFlag,
   121  		vyFlag,
   122  		vyperFlag,
   123  		excFlag,
   124  		pkgFlag,
   125  		outFlag,
   126  		langFlag,
   127  		aliasFlag,
   128  		contractFlag,
   129  		tmplFlag,
   130  	}
   131  	app.Action = utils.MigrateFlags(abigen)
   132  	cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
   133  }
   134  
   135  func abigen(c *cli.Context) error {
   136  	utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag, vyFlag) // Only one source can be selected.
   137  	if c.GlobalString(pkgFlag.Name) == "" {
   138  		utils.Fatalf("No destination package specified (--pkg)")
   139  	}
   140  	var lang bind.Lang
   141  	switch c.GlobalString(langFlag.Name) {
   142  	case "go":
   143  		lang = bind.LangGo
   144  	case "java":
   145  		lang = bind.LangJava
   146  	case "objc":
   147  		lang = bind.LangObjC
   148  		utils.Fatalf("Objc binding generation is uncompleted")
   149  	default:
   150  		utils.Fatalf("Unsupported destination language \"%s\" (--lang)", c.GlobalString(langFlag.Name))
   151  	}
   152  	// If the entire solidity code was specified, build and bind based on that
   153  	var (
   154  		abis    []string
   155  		bins    []string
   156  		types   []string
   157  		sigs    []map[string]string
   158  		libs    = make(map[string]string)
   159  		aliases = make(map[string]string)
   160  	)
   161  	if c.GlobalString(abiFlag.Name) != "" {
   162  		// Load up the ABI, optional bytecode and type name from the parameters
   163  		var (
   164  			abi []byte
   165  			err error
   166  		)
   167  		input := c.GlobalString(abiFlag.Name)
   168  		if input == "-" {
   169  			abi, err = ioutil.ReadAll(os.Stdin)
   170  		} else {
   171  			abi, err = ioutil.ReadFile(input)
   172  		}
   173  		if err != nil {
   174  			utils.Fatalf("Failed to read input ABI: %v", err)
   175  		}
   176  		abis = append(abis, string(abi))
   177  
   178  		var bin []byte
   179  		if binFile := c.GlobalString(binFlag.Name); binFile != "" {
   180  			if bin, err = ioutil.ReadFile(binFile); err != nil {
   181  				utils.Fatalf("Failed to read input bytecode: %v", err)
   182  			}
   183  			if strings.Contains(string(bin), "//") {
   184  				utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
   185  			}
   186  		}
   187  		bins = append(bins, string(bin))
   188  
   189  		kind := c.GlobalString(typeFlag.Name)
   190  		if kind == "" {
   191  			kind = c.GlobalString(pkgFlag.Name)
   192  		}
   193  		types = append(types, kind)
   194  	} else {
   195  		// Generate the list of types to exclude from binding
   196  		exclude := make(map[string]bool)
   197  		for _, kind := range strings.Split(c.GlobalString(excFlag.Name), ",") {
   198  			exclude[strings.ToLower(kind)] = true
   199  		}
   200  		var err error
   201  		var contracts map[string]*compiler.Contract
   202  
   203  		switch {
   204  		case c.GlobalIsSet(solFlag.Name):
   205  			contracts, err = compiler.CompileSolidity(c.GlobalString(solcFlag.Name), c.GlobalString(solFlag.Name))
   206  			if err != nil {
   207  				utils.Fatalf("Failed to build Solidity contract: %v", err)
   208  			}
   209  		case c.GlobalIsSet(vyFlag.Name):
   210  			output, err := compiler.CompileVyper(c.GlobalString(vyperFlag.Name), c.GlobalString(vyFlag.Name))
   211  			if err != nil {
   212  				utils.Fatalf("Failed to build Vyper contract: %v", err)
   213  			}
   214  			contracts = make(map[string]*compiler.Contract)
   215  			for n, contract := range output {
   216  				name := n
   217  				// Sanitize the combined json names to match the
   218  				// format expected by solidity.
   219  				if !strings.Contains(n, ":") {
   220  					// Remove extra path components
   221  					name = abi.ToCamelCase(strings.TrimSuffix(filepath.Base(name), ".vy"))
   222  				}
   223  				contracts[name] = contract
   224  			}
   225  
   226  		case c.GlobalIsSet(jsonFlag.Name):
   227  			jsonOutput, err := ioutil.ReadFile(c.GlobalString(jsonFlag.Name))
   228  			if err != nil {
   229  				utils.Fatalf("Failed to read combined-json from compiler: %v", err)
   230  			}
   231  			contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
   232  			if err != nil {
   233  				utils.Fatalf("Failed to read contract information from json output: %v", err)
   234  			}
   235  		}
   236  		// Gather all non-excluded contract for binding
   237  		for name, contract := range contracts {
   238  			// The fully qualified name is of the form <solFilePath>:<type>
   239  			nameParts := strings.Split(name, ":")
   240  			typeName := nameParts[len(nameParts)-1]
   241  			// If a contract name is provided then ignore all other contracts
   242  			if c.GlobalIsSet(contractFlag.Name) && c.GlobalString(contractFlag.Name) != typeName {
   243  				continue
   244  			}
   245  			if exclude[strings.ToLower(name)] {
   246  				continue
   247  			}
   248  			abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
   249  			if err != nil {
   250  				utils.Fatalf("Failed to parse ABIs from compiler output: %v", err)
   251  			}
   252  			abis = append(abis, string(abi))
   253  			bins = append(bins, contract.Code)
   254  			sigs = append(sigs, contract.Hashes)
   255  			types = append(types, typeName)
   256  
   257  			libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36]
   258  			libs[libPattern] = typeName
   259  		}
   260  	}
   261  	// Extract all aliases from the flags
   262  	if c.GlobalIsSet(aliasFlag.Name) {
   263  		// We support multi-versions for aliasing
   264  		// e.g.
   265  		//      foo=bar,foo2=bar2
   266  		//      foo:bar,foo2:bar2
   267  		re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
   268  		submatches := re.FindAllStringSubmatch(c.GlobalString(aliasFlag.Name), -1)
   269  		for _, match := range submatches {
   270  			aliases[match[1]] = match[2]
   271  		}
   272  	}
   273  	// Set customize template file.
   274  	if c.GlobalIsSet(tmplFlag.Name) {
   275  		tmplFile := c.GlobalString(tmplFlag.Name)
   276  		data, err := os.ReadFile(tmplFile)
   277  		if err != nil {
   278  			utils.Fatalf("Failed to read template file: %v", err)
   279  		}
   280  		bind.SetTmplSource(lang, string(data))
   281  	}
   282  	// Generate the contract binding
   283  	code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs, aliases)
   284  	if err != nil {
   285  		utils.Fatalf("Failed to generate ABI binding: %v", err)
   286  	}
   287  	// Either flush it out to a file or display on the standard output
   288  	if !c.GlobalIsSet(outFlag.Name) {
   289  		fmt.Printf("%s\n", code)
   290  		return nil
   291  	}
   292  	if err := ioutil.WriteFile(c.GlobalString(outFlag.Name), []byte(code), 0600); err != nil {
   293  		utils.Fatalf("Failed to write ABI binding: %v", err)
   294  	}
   295  	return nil
   296  }
   297  
   298  func main() {
   299  	log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
   300  
   301  	if err := app.Run(os.Args); err != nil {
   302  		fmt.Fprintln(os.Stderr, err)
   303  		os.Exit(1)
   304  	}
   305  }