github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/utilities/common/compiler/solidity.go (about) 1 package compiler 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "os/exec" 9 "strconv" 10 "strings" 11 ) 12 13 type Solidity struct { 14 Path, Version, FullVersion string 15 Major, Minor, Patch int 16 } 17 18 type solcOutput struct { 19 Contracts map[string]struct { 20 BinRuntime string `json:"bin-runtime"` 21 SrcMapRuntime string `json:"srcmap-runtime"` 22 Bin, SrcMap, Abi, Devdoc, Userdoc, Metadata string 23 Hashes map[string]string 24 } 25 Version string 26 } 27 28 func (s *Solidity) makeArgs() []string { 29 p := []string{ 30 "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc", 31 "--optimize", 32 "--allow-paths", "., ./, ../", 33 } 34 if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { 35 p[1] += ",metadata,hashes" 36 } 37 return p 38 } 39 40 func SolidityVersion(solc string) (*Solidity, error) { 41 if solc == "" { 42 solc = "solc" 43 } 44 var out bytes.Buffer 45 cmd := exec.Command(solc, "--version") 46 cmd.Stdout = &out 47 err := cmd.Run() 48 if err != nil { 49 return nil, err 50 } 51 matches := versionRegexp.FindStringSubmatch(out.String()) 52 if len(matches) != 4 { 53 return nil, fmt.Errorf("can't parse solc version %q", out.String()) 54 } 55 s := &Solidity{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]} 56 if s.Major, err = strconv.Atoi(matches[1]); err != nil { 57 return nil, err 58 } 59 if s.Minor, err = strconv.Atoi(matches[2]); err != nil { 60 return nil, err 61 } 62 if s.Patch, err = strconv.Atoi(matches[3]); err != nil { 63 return nil, err 64 } 65 return s, nil 66 } 67 68 func CompileSolidityString(solc, source string) (map[string]*Contract, error) { 69 if len(source) == 0 { 70 return nil, errors.New("solc: empty source string") 71 } 72 s, err := SolidityVersion(solc) 73 if err != nil { 74 return nil, err 75 } 76 args := append(s.makeArgs(), "--") 77 cmd := exec.Command(s.Path, append(args, "-")...) 78 cmd.Stdin = strings.NewReader(source) 79 return s.run(cmd, source) 80 } 81 82 func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, error) { 83 if len(sourcefiles) == 0 { 84 return nil, errors.New("solc: no source files") 85 } 86 source, err := slurpFiles(sourcefiles) 87 if err != nil { 88 return nil, err 89 } 90 s, err := SolidityVersion(solc) 91 if err != nil { 92 return nil, err 93 } 94 args := append(s.makeArgs(), "--") 95 cmd := exec.Command(s.Path, append(args, sourcefiles...)...) 96 return s.run(cmd, source) 97 } 98 99 func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) { 100 var stderr, stdout bytes.Buffer 101 cmd.Stderr = &stderr 102 cmd.Stdout = &stdout 103 if err := cmd.Run(); err != nil { 104 return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) 105 } 106 107 return ParseCombinedJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) 108 } 109 110 func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { 111 var output solcOutput 112 if err := json.Unmarshal(combinedJSON, &output); err != nil { 113 return nil, err 114 } 115 116 contracts := make(map[string]*Contract) 117 for name, info := range output.Contracts { 118 119 var abi interface{} 120 if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { 121 return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) 122 } 123 var userdoc, devdoc interface{} 124 json.Unmarshal([]byte(info.Userdoc), &userdoc) 125 json.Unmarshal([]byte(info.Devdoc), &devdoc) 126 127 contracts[name] = &Contract{ 128 Code: "0x" + info.Bin, 129 RuntimeCode: "0x" + info.BinRuntime, 130 Hashes: info.Hashes, 131 Info: ContractInfo{ 132 Source: source, 133 Language: "Solidity", 134 LanguageVersion: languageVersion, 135 CompilerVersion: compilerVersion, 136 CompilerOptions: compilerOptions, 137 SrcMap: info.SrcMap, 138 SrcMapRuntime: info.SrcMapRuntime, 139 AbiDefinition: abi, 140 UserDoc: userdoc, 141 DeveloperDoc: devdoc, 142 Metadata: info.Metadata, 143 }, 144 } 145 } 146 return contracts, nil 147 }