github.com/iotexproject/iotex-core@v1.14.1-rc1/ioctl/newcmd/contract/contract.go (about)

     1  // Copyright (c) 2022 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package contract
     7  
     8  import (
     9  	"encoding/hex"
    10  	"os"
    11  	"path/filepath"
    12  
    13  	"github.com/ethereum/go-ethereum/accounts/abi"
    14  	"github.com/ethereum/go-ethereum/common/compiler"
    15  	"github.com/pkg/errors"
    16  	"github.com/spf13/cobra"
    17  
    18  	"github.com/iotexproject/iotex-core/ioctl"
    19  	"github.com/iotexproject/iotex-core/ioctl/config"
    20  	"github.com/iotexproject/iotex-core/ioctl/util"
    21  )
    22  
    23  const _solCompiler = "solc"
    24  
    25  // Multi-language support
    26  var (
    27  	_contractCmdShorts = map[config.Language]string{
    28  		config.English: "Deal with smart contract of IoTeX blockchain",
    29  		config.Chinese: "处理IoTeX区块链的智能合约",
    30  	}
    31  	_flagInitialAmountUsage = map[config.Language]string{
    32  		config.English: "transfer an initial amount to the new deployed contract",
    33  		config.Chinese: "为部署的新合约转入一笔初始资金",
    34  	}
    35  )
    36  
    37  // NewContractCmd represents the contract command
    38  func NewContractCmd(client ioctl.Client) *cobra.Command {
    39  	short, _ := client.SelectTranslation(_contractCmdShorts)
    40  	cmd := &cobra.Command{
    41  		Use:   "contract",
    42  		Short: short,
    43  	}
    44  
    45  	// TODO add sub commands
    46  	// cmd.AddCommand(NewContractPrepareCmd)
    47  	// cmd.AddCommand(NewContractCompileCmd)
    48  	// cmd.AddCommand(NewContractDeployCmd)
    49  	// cmd.AddCommand(NewContractInvokeCmd)
    50  	// cmd.AddCommand(NewContractTestCmd)
    51  	// cmd.AddCommand(NewContractShareCmd)
    52  	cmd.AddCommand(NewContractCompileCmd(client))
    53  	client.SetEndpointWithFlag(cmd.PersistentFlags().StringVar)
    54  	client.SetInsecureWithFlag(cmd.PersistentFlags().BoolVar)
    55  
    56  	return cmd
    57  }
    58  
    59  // Compile compiles smart contract from source code
    60  func Compile(sourceFiles ...string) (map[string]*compiler.Contract, error) {
    61  	solc, err := util.SolidityVersion(_solCompiler)
    62  	if err != nil {
    63  		return nil, errors.Wrap(err, "solidity compiler not ready")
    64  	}
    65  	if !checkCompilerVersion(solc) {
    66  		return nil, errors.Errorf("unsupported solc version %d.%d.%d", solc.Major, solc.Minor, solc.Patch)
    67  	}
    68  
    69  	contracts, err := util.CompileSolidity(_solCompiler, sourceFiles...)
    70  	if err != nil {
    71  		return nil, errors.Wrap(err, "failed to compile")
    72  	}
    73  	return contracts, nil
    74  }
    75  
    76  func checkCompilerVersion(solc *util.Solidity) bool {
    77  	if solc.Major == 0 && solc.Minor == 8 {
    78  		return true
    79  	}
    80  	return false
    81  }
    82  
    83  func decodeBytecode(bytecode string) ([]byte, error) {
    84  	return hex.DecodeString(util.TrimHexPrefix(bytecode))
    85  }
    86  
    87  func readAbiFile(abiFile string) (*abi.ABI, error) {
    88  	abiBytes, err := os.ReadFile(filepath.Clean(abiFile))
    89  	if err != nil {
    90  		return nil, errors.Wrap(err, "failed to read abi file")
    91  	}
    92  
    93  	return parseAbi(abiBytes)
    94  }
    95  
    96  func packArguments(targetAbi *abi.ABI, targetMethod string, rowInput string) ([]byte, error) {
    97  	var method abi.Method
    98  	var ok bool
    99  
   100  	if rowInput == "" {
   101  		rowInput = "{}"
   102  	}
   103  
   104  	rowArguments, err := parseInput(rowInput)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	if targetMethod == "" {
   110  		method = targetAbi.Constructor
   111  	} else {
   112  		method, ok = targetAbi.Methods[targetMethod]
   113  		if !ok {
   114  			return nil, errors.New("invalid method name")
   115  		}
   116  	}
   117  
   118  	arguments := make([]interface{}, 0, len(method.Inputs))
   119  	for _, param := range method.Inputs {
   120  		if param.Name == "" {
   121  			param.Name = "_"
   122  		}
   123  
   124  		rowArg, ok := rowArguments[param.Name]
   125  		if !ok {
   126  			return nil, errors.Errorf("failed to parse argument \"%s\"", param.Name)
   127  		}
   128  
   129  		arg, err := parseInputArgument(&param.Type, rowArg)
   130  		if err != nil {
   131  			return nil, errors.Wrapf(err, "failed to parse argument \"%s\"", param.Name)
   132  		}
   133  		arguments = append(arguments, arg)
   134  	}
   135  	return targetAbi.Pack(targetMethod, arguments...)
   136  }