Skip to content

Commit 5fa5705

Browse files
authored
add contract compile command for ioctl (iotexproject#2132)
1 parent 670bbc1 commit 5fa5705

File tree

5 files changed

+162
-0
lines changed

5 files changed

+162
-0
lines changed

ioctl/cmd/contract/contract.go

+48
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,22 @@
77
package contract
88

99
import (
10+
"fmt"
11+
12+
"github.com/ethereum/go-ethereum/common/compiler"
1013
"github.com/spf13/cobra"
1114

1215
"github.com/iotexproject/iotex-core/ioctl/config"
16+
"github.com/iotexproject/iotex-core/ioctl/flag"
17+
"github.com/iotexproject/iotex-core/ioctl/output"
18+
)
19+
20+
const solCompiler = "solc"
21+
22+
// Flags
23+
var (
24+
sourceFlag = flag.NewStringVar("source", "",
25+
config.TranslateInLang(flagSourceUsage, config.UILanguage))
1326
)
1427

1528
// Multi-language support
@@ -30,6 +43,14 @@ var (
3043
config.English: "insecure connection for once",
3144
config.Chinese: "一次不安全的连接",
3245
}
46+
flagSourceUsage = map[config.Language]string{
47+
config.English: "set source code file path",
48+
config.Chinese: "设定代码文件路径",
49+
}
50+
flagVersionUsage = map[config.Language]string{
51+
config.English: "set solidity version",
52+
config.Chinese: "设定solidity版本",
53+
}
3354
)
3455

3556
// ContractCmd represents the contract command
@@ -39,8 +60,35 @@ var ContractCmd = &cobra.Command{
3960
}
4061

4162
func init() {
63+
ContractCmd.AddCommand(ContractCompileCmd)
4264
ContractCmd.PersistentFlags().StringVar(&config.ReadConfig.Endpoint, "endpoint",
4365
config.ReadConfig.Endpoint, config.TranslateInLang(flagEndpointUsages, config.UILanguage))
4466
ContractCmd.PersistentFlags().BoolVar(&config.Insecure, "insecure", config.Insecure,
4567
config.TranslateInLang(flagInsecureUsages, config.UILanguage))
4668
}
69+
70+
// Compile compiles smart contract from source code
71+
func Compile(sourceFiles ...string) (map[string]*compiler.Contract, error) {
72+
solc, err := compiler.SolidityVersion(solCompiler)
73+
if err != nil {
74+
return nil, output.NewError(output.CompilerError, "solidity compiler not ready", err)
75+
}
76+
if !checkCompilerVersion(solc) {
77+
return nil, output.NewError(output.CompilerError,
78+
fmt.Sprintf("unsupported solc version %d.%d.%d", solc.Major, solc.Minor, solc.Patch), nil)
79+
}
80+
81+
contracts, err := compiler.CompileSolidity(solCompiler, sourceFiles...)
82+
if err != nil {
83+
return nil, output.NewError(output.CompilerError, "failed to compile", err)
84+
}
85+
return contracts, nil
86+
}
87+
88+
func checkCompilerVersion(solc *compiler.Solidity) bool {
89+
// TODO: may support 0.5.0 later, need to make sure range of valid version
90+
if solc.Major == 0 && solc.Minor == 4 && solc.Patch >= 24 {
91+
return true
92+
}
93+
return false
94+
}

ioctl/cmd/contract/contractcompile.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (c) 2020 IoTeX Foundation
2+
// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no
3+
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
4+
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
5+
// License 2.0 that can be found in the LICENSE file.
6+
7+
package contract
8+
9+
import (
10+
"encoding/json"
11+
"fmt"
12+
"io/ioutil"
13+
14+
"github.com/spf13/cobra"
15+
16+
"github.com/iotexproject/iotex-core/ioctl/config"
17+
"github.com/iotexproject/iotex-core/ioctl/output"
18+
)
19+
20+
var (
21+
abiOut string
22+
binOut string
23+
)
24+
25+
// Multi-language support
26+
var (
27+
contractCompileCmdUses = map[config.Language]string{
28+
config.English: "compile CONTRACT_NAME --abi-out ABI_PATH --bin-out BIN_PATH",
29+
config.Chinese: "compile 合约名 --abi-out ABI路径 --bin-out BIN路径",
30+
}
31+
contractCompileCmdShorts = map[config.Language]string{
32+
config.English: "Compile smart contract of IoTeX blockchain from source code",
33+
config.Chinese: "编译IoTeX区块链的智能合约代码",
34+
}
35+
flagAbiOutUsage = map[config.Language]string{
36+
config.English: "set abi file output path",
37+
config.Chinese: "设置abi文件输出路径",
38+
}
39+
flagBinOutUsage = map[config.Language]string{
40+
config.English: "set bin file output path",
41+
config.Chinese: "设置bin文件输出路径",
42+
}
43+
)
44+
45+
// ContractCompileCmd represents the contract compile command
46+
var ContractCompileCmd = &cobra.Command{
47+
Use: config.TranslateInLang(contractCompileCmdUses, config.UILanguage),
48+
Short: config.TranslateInLang(contractCompileCmdShorts, config.UILanguage),
49+
Args: cobra.ExactArgs(1),
50+
RunE: func(cmd *cobra.Command, args []string) error {
51+
cmd.SilenceUsage = true
52+
err := compile(args)
53+
return output.PrintError(err)
54+
},
55+
}
56+
57+
func init() {
58+
sourceFlag.RegisterCommand(ContractCompileCmd)
59+
sourceFlag.MarkFlagRequired(ContractCompileCmd)
60+
ContractCompileCmd.Flags().StringVar(&abiOut, "abi-out", "",
61+
config.TranslateInLang(flagAbiOutUsage, config.UILanguage))
62+
ContractCompileCmd.Flags().StringVar(&binOut, "bin-out", "",
63+
config.TranslateInLang(flagBinOutUsage, config.UILanguage))
64+
ContractCompileCmd.MarkFlagRequired("abi-out")
65+
ContractCompileCmd.MarkFlagRequired("bin-out")
66+
}
67+
68+
func compile(args []string) error {
69+
contractName := sourceFlag.Value().(string) + ":" + args[0]
70+
71+
contracts, err := Compile(sourceFlag.Value().(string))
72+
if err != nil {
73+
return output.NewError(0, "failed to compile", err)
74+
}
75+
76+
contract, ok := contracts[contractName]
77+
if !ok {
78+
return output.NewError(output.CompilerError, fmt.Sprintf("failed to get contract from %s", contractName), nil)
79+
}
80+
81+
abiByte, err := json.Marshal(contract.Info.AbiDefinition)
82+
if err != nil {
83+
return output.NewError(output.SerializationError, "failed to marshal abi", err)
84+
}
85+
86+
if err := ioutil.WriteFile(abiOut, abiByte, 0600); err != nil {
87+
return output.NewError(output.WriteFileError, "failed to write abi file", err)
88+
}
89+
// bin file starts with "0x" prefix
90+
if err := ioutil.WriteFile(binOut, []byte(contract.Code), 0600); err != nil {
91+
return output.NewError(output.WriteFileError, "failed to write bin file", err)
92+
}
93+
return nil
94+
}

ioctl/cmd/root.go

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/iotexproject/iotex-core/ioctl/cmd/action"
1414
"github.com/iotexproject/iotex-core/ioctl/cmd/alias"
1515
"github.com/iotexproject/iotex-core/ioctl/cmd/bc"
16+
"github.com/iotexproject/iotex-core/ioctl/cmd/contract"
1617
"github.com/iotexproject/iotex-core/ioctl/cmd/node"
1718
"github.com/iotexproject/iotex-core/ioctl/cmd/update"
1819
"github.com/iotexproject/iotex-core/ioctl/cmd/version"
@@ -71,6 +72,7 @@ func NewIoctl() *cobra.Command {
7172
rootCmd.AddCommand(node.NodeCmd)
7273
rootCmd.AddCommand(version.VersionCmd)
7374
rootCmd.AddCommand(update.UpdateCmd)
75+
rootCmd.AddCommand(contract.ContractCmd)
7476

7577
rootCmd.PersistentFlags().StringVarP(&output.Format, "output-format", "o", "",
7678
config.TranslateInLang(flagOutputFormatUsages, config.UILanguage))
@@ -93,6 +95,7 @@ func NewXctl() *cobra.Command {
9395
rootCmd.AddCommand(action.Xrc20Cmd)
9496
rootCmd.AddCommand(bc.BCCmd)
9597
rootCmd.AddCommand(version.VersionCmd)
98+
rootCmd.AddCommand(contract.ContractCmd)
9699
// TODO: add xctl's UpdateCmd
97100

98101
rootCmd.PersistentFlags().StringVarP(&output.Format, "output-format", "o", "",

ioctl/flag/flag.go

+15
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,21 @@ func NewStringVarP(
6767
}
6868
}
6969

70+
// NewStringVar creates a new stringVar flag
71+
func NewStringVar(
72+
label string,
73+
defaultValue string,
74+
description string,
75+
) Flag {
76+
return &stringVarP{
77+
flagBase: flagBase{
78+
label: label,
79+
description: description,
80+
},
81+
defaultValue: defaultValue,
82+
}
83+
}
84+
7085
func (f *stringVarP) Value() interface{} {
7186
return f.value
7287
}

ioctl/output/format.go

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ const (
5353
ConfigError
5454
// InstantiationError used when an error during instantiation
5555
InstantiationError
56+
// CompilerError used when an error occurs when using the solidity compiler
57+
CompilerError
5658
)
5759

5860
// MessageType marks the type of output message

0 commit comments

Comments
 (0)