github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/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"
    23  	"os"
    24  	"regexp"
    25  	"strings"
    26  
    27  	"gopkg.in/urfave/cli.v1"
    28  
    29  	"github.com/unicornultrafoundation/go-u2u/accounts/abi/bind"
    30  	"github.com/unicornultrafoundation/go-u2u/cmd/utils"
    31  	"github.com/unicornultrafoundation/go-u2u/common/compiler"
    32  	"github.com/unicornultrafoundation/go-u2u/crypto"
    33  	"github.com/unicornultrafoundation/go-u2u/flags"
    34  	"github.com/unicornultrafoundation/go-u2u/log"
    35  )
    36  
    37  var (
    38  	// Flags needed by abigen
    39  	abiFlag = &cli.StringFlag{
    40  		Name:  "abi",
    41  		Usage: "Path to the Ethereum contract ABI json to bind, - for STDIN",
    42  	}
    43  	binFlag = &cli.StringFlag{
    44  		Name:  "bin",
    45  		Usage: "Path to the Ethereum contract bytecode (generate deploy method)",
    46  	}
    47  	typeFlag = &cli.StringFlag{
    48  		Name:  "type",
    49  		Usage: "Struct name for the binding (default = package name)",
    50  	}
    51  	jsonFlag = &cli.StringFlag{
    52  		Name:  "combined-json",
    53  		Usage: "Path to the combined-json file generated by compiler, - for STDIN",
    54  	}
    55  	excFlag = &cli.StringFlag{
    56  		Name:  "exc",
    57  		Usage: "Comma separated types to exclude from binding",
    58  	}
    59  	pkgFlag = &cli.StringFlag{
    60  		Name:  "pkg",
    61  		Usage: "Package name to generate the binding into",
    62  	}
    63  	outFlag = &cli.StringFlag{
    64  		Name:  "out",
    65  		Usage: "Output file for the generated binding (default = stdout)",
    66  	}
    67  	langFlag = &cli.StringFlag{
    68  		Name:  "lang",
    69  		Usage: "Destination language for the bindings (go)",
    70  		Value: "go",
    71  	}
    72  	aliasFlag = &cli.StringFlag{
    73  		Name:  "alias",
    74  		Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2",
    75  	}
    76  )
    77  
    78  var (
    79  	// Git SHA1 commit hash of the release (set via linker flags).
    80  	gitCommit = ""
    81  	gitDate   = ""
    82  	app       = flags.NewApp(gitCommit, gitDate, "U2U ABI wrapper code generator")
    83  )
    84  
    85  func init() {
    86  	app.Name = "abigen"
    87  	app.Flags = []cli.Flag{
    88  		abiFlag,
    89  		binFlag,
    90  		typeFlag,
    91  		jsonFlag,
    92  		excFlag,
    93  		pkgFlag,
    94  		outFlag,
    95  		langFlag,
    96  		aliasFlag,
    97  	}
    98  	app.Action = abigen
    99  }
   100  
   101  func abigen(c *cli.Context) error {
   102  	utils.CheckExclusive(c, abiFlag, jsonFlag) // Only one source can be selected.
   103  
   104  	if c.String(pkgFlag.Name) == "" {
   105  		utils.Fatalf("No destination package specified (--pkg)")
   106  	}
   107  	var lang bind.Lang
   108  	switch c.String(langFlag.Name) {
   109  	case "go":
   110  		lang = bind.LangGo
   111  	default:
   112  		utils.Fatalf("Unsupported destination language \"%s\" (--lang)", c.String(langFlag.Name))
   113  	}
   114  	// If the entire solidity code was specified, build and bind based on that
   115  	var (
   116  		abis    []string
   117  		bins    []string
   118  		types   []string
   119  		sigs    []map[string]string
   120  		libs    = make(map[string]string)
   121  		aliases = make(map[string]string)
   122  	)
   123  	if c.String(abiFlag.Name) != "" {
   124  		// Load up the ABI, optional bytecode and type name from the parameters
   125  		var (
   126  			abi []byte
   127  			err error
   128  		)
   129  		input := c.String(abiFlag.Name)
   130  		if input == "-" {
   131  			abi, err = io.ReadAll(os.Stdin)
   132  		} else {
   133  			abi, err = os.ReadFile(input)
   134  		}
   135  		if err != nil {
   136  			utils.Fatalf("Failed to read input ABI: %v", err)
   137  		}
   138  		abis = append(abis, string(abi))
   139  
   140  		var bin []byte
   141  		if binFile := c.String(binFlag.Name); binFile != "" {
   142  			if bin, err = os.ReadFile(binFile); err != nil {
   143  				utils.Fatalf("Failed to read input bytecode: %v", err)
   144  			}
   145  			if strings.Contains(string(bin), "//") {
   146  				utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
   147  			}
   148  		}
   149  		bins = append(bins, string(bin))
   150  
   151  		kind := c.String(typeFlag.Name)
   152  		if kind == "" {
   153  			kind = c.String(pkgFlag.Name)
   154  		}
   155  		types = append(types, kind)
   156  	} else {
   157  		// Generate the list of types to exclude from binding
   158  		var exclude *nameFilter
   159  		if c.IsSet(excFlag.Name) {
   160  			var err error
   161  			if exclude, err = newNameFilter(strings.Split(c.String(excFlag.Name), ",")...); err != nil {
   162  				utils.Fatalf("Failed to parse excludes: %v", err)
   163  			}
   164  		}
   165  		var contracts map[string]*compiler.Contract
   166  
   167  		if c.IsSet(jsonFlag.Name) {
   168  			var (
   169  				input      = c.String(jsonFlag.Name)
   170  				jsonOutput []byte
   171  				err        error
   172  			)
   173  			if input == "-" {
   174  				jsonOutput, err = io.ReadAll(os.Stdin)
   175  			} else {
   176  				jsonOutput, err = os.ReadFile(input)
   177  			}
   178  			if err != nil {
   179  				utils.Fatalf("Failed to read combined-json: %v", err)
   180  			}
   181  			contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
   182  			if err != nil {
   183  				utils.Fatalf("Failed to read contract information from json output: %v", err)
   184  			}
   185  		}
   186  		// Gather all non-excluded contract for binding
   187  		for name, contract := range contracts {
   188  			// fully qualified name is of the form <solFilePath>:<type>
   189  			nameParts := strings.Split(name, ":")
   190  			typeName := nameParts[len(nameParts)-1]
   191  			if exclude != nil && exclude.Matches(name) {
   192  				fmt.Fprintf(os.Stderr, "excluding: %v\n", name)
   193  				continue
   194  			}
   195  			abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
   196  			if err != nil {
   197  				utils.Fatalf("Failed to parse ABIs from compiler output: %v", err)
   198  			}
   199  			abis = append(abis, string(abi))
   200  			bins = append(bins, contract.Code)
   201  			sigs = append(sigs, contract.Hashes)
   202  			types = append(types, typeName)
   203  
   204  			// Derive the library placeholder which is a 34 character prefix of the
   205  			// hex encoding of the keccak256 hash of the fully qualified library name.
   206  			// Note that the fully qualified library name is the path of its source
   207  			// file and the library name separated by ":".
   208  			libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36] // the first 2 chars are 0x
   209  			libs[libPattern] = typeName
   210  		}
   211  	}
   212  	// Extract all aliases from the flags
   213  	if c.IsSet(aliasFlag.Name) {
   214  		// We support multi-versions for aliasing
   215  		// e.g.
   216  		//      foo=bar,foo2=bar2
   217  		//      foo:bar,foo2:bar2
   218  		re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
   219  		submatches := re.FindAllStringSubmatch(c.String(aliasFlag.Name), -1)
   220  		for _, match := range submatches {
   221  			aliases[match[1]] = match[2]
   222  		}
   223  	}
   224  	// Generate the contract binding
   225  	code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs, aliases)
   226  	if err != nil {
   227  		utils.Fatalf("Failed to generate ABI binding: %v", err)
   228  	}
   229  	// Either flush it out to a file or display on the standard output
   230  	if !c.IsSet(outFlag.Name) {
   231  		fmt.Printf("%s\n", code)
   232  		return nil
   233  	}
   234  	if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0600); err != nil {
   235  		utils.Fatalf("Failed to write ABI binding: %v", err)
   236  	}
   237  	return nil
   238  }
   239  
   240  func main() {
   241  	log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
   242  
   243  	if err := app.Run(os.Args); err != nil {
   244  		fmt.Fprintln(os.Stderr, err)
   245  		os.Exit(1)
   246  	}
   247  }