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