github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/client/cli/tx.go (about)

     1  package cli
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"strconv"
     9  
    10  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client"
    11  	clientCtx "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context"
    12  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/flags"
    13  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    14  	codectypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec/types"
    15  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    16  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    17  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth"
    18  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/client/utils"
    19  	"github.com/fibonacci-chain/fbc/x/wasm/ioutils"
    20  	"github.com/fibonacci-chain/fbc/x/wasm/types"
    21  	"github.com/spf13/cobra"
    22  	flag "github.com/spf13/pflag"
    23  )
    24  
    25  const (
    26  	flagAmount                 = "amount"
    27  	flagLabel                  = "label"
    28  	flagAdmin                  = "admin"
    29  	flagNoAdmin                = "no-admin"
    30  	flagRunAs                  = "run-as"
    31  	flagInstantiateByEverybody = "instantiate-everybody"
    32  	flagInstantiateNobody      = "instantiate-nobody"
    33  	flagInstantiateByAddress   = "instantiate-only-address"
    34  	flagProposalType           = "type"
    35  )
    36  
    37  // NewTxCmd returns the transaction commands for wasm
    38  func NewTxCmd(cdc *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
    39  	txCmd := &cobra.Command{
    40  		Use:                        types.ModuleName,
    41  		Short:                      "Wasm transaction subcommands",
    42  		DisableFlagParsing:         true,
    43  		SuggestionsMinimumDistance: 2,
    44  		RunE:                       client.ValidateCmd,
    45  	}
    46  	txCmd.AddCommand(
    47  		NewStoreCodeCmd(cdc, reg),
    48  		NewInstantiateContractCmd(cdc, reg),
    49  		NewExecuteContractCmd(cdc, reg),
    50  		NewMigrateContractCmd(cdc, reg),
    51  		NewUpdateContractAdminCmd(cdc, reg),
    52  		NewClearContractAdminCmd(cdc, reg),
    53  	)
    54  	return txCmd
    55  }
    56  
    57  func NewStoreCodeCmd(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
    58  	cmd := &cobra.Command{
    59  		Use:     "store [wasm file]",
    60  		Short:   "Upload a wasm binary",
    61  		Aliases: []string{"upload", "st", "s"},
    62  		Args:    cobra.ExactArgs(1),
    63  		RunE: func(cmd *cobra.Command, args []string) error {
    64  			inBuf := bufio.NewReader(cmd.InOrStdin())
    65  			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(m.GetCdc()))
    66  			clientCtx := clientCtx.NewCLIContext().WithCodec(m.GetCdc()).WithInterfaceRegistry(reg)
    67  
    68  			msg, err := parseStoreCodeArgs(args[0], clientCtx.GetFromAddress(), cmd.Flags())
    69  			if err != nil {
    70  				return err
    71  			}
    72  			if err = msg.ValidateBasic(); err != nil {
    73  				return err
    74  			}
    75  			return utils.GenerateOrBroadcastMsgs(clientCtx, txBldr, []sdk.Msg{msg})
    76  		},
    77  	}
    78  
    79  	cmd.Flags().String(flagInstantiateByEverybody, "", "Everybody can instantiate a contract from the code, optional")
    80  	cmd.Flags().String(flagInstantiateNobody, "", "Nobody except the governance process can instantiate a contract from the code, optional")
    81  	cmd.Flags().String(flagInstantiateByAddress, "", "Only this address can instantiate a contract instance from the code, optional")
    82  	flags.AddTxFlagsToCmd(cmd)
    83  	return cmd
    84  }
    85  
    86  func NewInstantiateContractCmd(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
    87  	cmd := &cobra.Command{
    88  		Use:     "instantiate [code_id_int64] [json_encoded_init_args] --label [text] --admin [address,optional] --amount [coins,optional]",
    89  		Short:   "Instantiate a wasm contract",
    90  		Aliases: []string{"start", "init", "inst", "i"},
    91  		Args:    cobra.ExactArgs(2),
    92  		RunE: func(cmd *cobra.Command, args []string) error {
    93  			inBuf := bufio.NewReader(cmd.InOrStdin())
    94  			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(m.GetCdc()))
    95  			clientCtx := clientCtx.NewCLIContext().WithCodec(m.GetCdc()).WithInterfaceRegistry(reg)
    96  
    97  			msg, err := parseInstantiateArgs(args[0], args[1], clientCtx.GetFromAddress(), cmd.Flags())
    98  			if err != nil {
    99  				return err
   100  			}
   101  			if err := msg.ValidateBasic(); err != nil {
   102  				return err
   103  			}
   104  			return utils.GenerateOrBroadcastMsgs(clientCtx, txBldr, []sdk.Msg{msg})
   105  		},
   106  	}
   107  
   108  	cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation")
   109  	cmd.Flags().String(flagLabel, "", "A human-readable name for this contract in lists")
   110  	cmd.Flags().String(flagAdmin, "", "Address of an admin")
   111  	cmd.Flags().Bool(flagNoAdmin, false, "You must set this explicitly if you don't want an admin")
   112  	flags.AddTxFlagsToCmd(cmd)
   113  	return cmd
   114  }
   115  
   116  func NewExecuteContractCmd(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   117  	cmd := &cobra.Command{
   118  		Use:     "execute [contract_addr_bech32] [json_encoded_send_args] --amount [coins,optional]",
   119  		Short:   "Execute a command on a wasm contract",
   120  		Aliases: []string{"run", "call", "exec", "ex", "e"},
   121  		Args:    cobra.ExactArgs(2),
   122  		RunE: func(cmd *cobra.Command, args []string) error {
   123  			inBuf := bufio.NewReader(cmd.InOrStdin())
   124  			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(m.GetCdc()))
   125  			clientCtx := clientCtx.NewCLIContext().WithCodec(m.GetCdc()).WithInterfaceRegistry(reg)
   126  
   127  			msg, err := parseExecuteArgs(args[0], args[1], clientCtx.GetFromAddress(), cmd.Flags())
   128  			if err != nil {
   129  				return err
   130  			}
   131  			if err := msg.ValidateBasic(); err != nil {
   132  				return err
   133  			}
   134  			return utils.GenerateOrBroadcastMsgs(clientCtx, txBldr, []sdk.Msg{msg})
   135  		},
   136  	}
   137  
   138  	cmd.Flags().String(flagAmount, "", "Coins to send to the contract along with command")
   139  	flags.AddTxFlagsToCmd(cmd)
   140  	return cmd
   141  }
   142  
   143  func NewMigrateContractCmd(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   144  	cmd := &cobra.Command{
   145  		Use:     "migrate [contract_addr_bech32] [new_code_id_int64] [json_encoded_migration_args]",
   146  		Short:   "Migrate a wasm contract to a new code version",
   147  		Aliases: []string{"update", "mig", "m"},
   148  		Args:    cobra.ExactArgs(3),
   149  		RunE: func(cmd *cobra.Command, args []string) error {
   150  			inBuf := bufio.NewReader(cmd.InOrStdin())
   151  			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(m.GetCdc()))
   152  			clientCtx := clientCtx.NewCLIContext().WithCodec(m.GetCdc()).WithInterfaceRegistry(reg)
   153  
   154  			msg, err := parseMigrateContractArgs(args, clientCtx)
   155  			if err != nil {
   156  				return err
   157  			}
   158  			if err := msg.ValidateBasic(); err != nil {
   159  				return nil
   160  			}
   161  			return utils.GenerateOrBroadcastMsgs(clientCtx, txBldr, []sdk.Msg{msg})
   162  		},
   163  	}
   164  	flags.AddTxFlagsToCmd(cmd)
   165  	return cmd
   166  }
   167  
   168  func NewUpdateContractAdminCmd(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   169  	cmd := &cobra.Command{
   170  		Use:     "set-contract-admin [contract_addr_bech32] [new_admin_addr_bech32]",
   171  		Short:   "Set new admin for a contract",
   172  		Aliases: []string{"new-admin", "admin", "set-adm", "sa"},
   173  		Args:    cobra.ExactArgs(2),
   174  		RunE: func(cmd *cobra.Command, args []string) error {
   175  			inBuf := bufio.NewReader(cmd.InOrStdin())
   176  			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(m.GetCdc()))
   177  			clientCtx := clientCtx.NewCLIContext().WithCodec(m.GetCdc()).WithInterfaceRegistry(reg)
   178  
   179  			msg, err := parseUpdateContractAdminArgs(args, clientCtx)
   180  			if err != nil {
   181  				return err
   182  			}
   183  			if err := msg.ValidateBasic(); err != nil {
   184  				return err
   185  			}
   186  			return utils.GenerateOrBroadcastMsgs(clientCtx, txBldr, []sdk.Msg{msg})
   187  		},
   188  	}
   189  	flags.AddTxFlagsToCmd(cmd)
   190  	return cmd
   191  }
   192  
   193  func NewClearContractAdminCmd(m *codec.CodecProxy, reg codectypes.InterfaceRegistry) *cobra.Command {
   194  	cmd := &cobra.Command{
   195  		Use:     "clear-contract-admin [contract_addr_bech32]",
   196  		Short:   "Clears admin for a contract to prevent further migrations",
   197  		Aliases: []string{"clear-admin", "clr-adm"},
   198  		Args:    cobra.ExactArgs(1),
   199  		RunE: func(cmd *cobra.Command, args []string) error {
   200  			inBuf := bufio.NewReader(cmd.InOrStdin())
   201  			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(m.GetCdc()))
   202  			clientCtx := clientCtx.NewCLIContext().WithCodec(m.GetCdc()).WithInterfaceRegistry(reg)
   203  
   204  			msg := types.MsgClearAdmin{
   205  				Sender:   clientCtx.GetFromAddress().String(),
   206  				Contract: args[0],
   207  			}
   208  			if err := msg.ValidateBasic(); err != nil {
   209  				return err
   210  			}
   211  			return utils.GenerateOrBroadcastMsgs(clientCtx, txBldr, []sdk.Msg{msg})
   212  		},
   213  	}
   214  	flags.AddTxFlagsToCmd(cmd)
   215  	return cmd
   216  }
   217  
   218  func parseStoreCodeArgs(file string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgStoreCode, error) {
   219  	wasm, err := ioutil.ReadFile(file)
   220  	if err != nil {
   221  		return types.MsgStoreCode{}, err
   222  	}
   223  
   224  	// gzip the wasm file
   225  	if ioutils.IsWasm(wasm) {
   226  		wasm, err = ioutils.GzipIt(wasm)
   227  
   228  		if err != nil {
   229  			return types.MsgStoreCode{}, err
   230  		}
   231  	} else if !ioutils.IsGzip(wasm) {
   232  		return types.MsgStoreCode{}, fmt.Errorf("invalid input file. Use wasm binary or gzip")
   233  	}
   234  
   235  	var perm *types.AccessConfig
   236  	onlyAddrStr, err := flags.GetString(flagInstantiateByAddress)
   237  	if err != nil {
   238  		return types.MsgStoreCode{}, fmt.Errorf("instantiate by address: %s", err)
   239  	}
   240  	if onlyAddrStr != "" {
   241  		allowedAddr, err := sdk.AccAddressFromBech32(onlyAddrStr)
   242  		if err != nil {
   243  			return types.MsgStoreCode{}, sdkerrors.Wrap(err, flagInstantiateByAddress)
   244  		}
   245  		x := types.AccessTypeOnlyAddress.With(allowedAddr)
   246  		perm = &x
   247  	} else {
   248  		everybodyStr, err := flags.GetString(flagInstantiateByEverybody)
   249  		if err != nil {
   250  			return types.MsgStoreCode{}, fmt.Errorf("instantiate by everybody: %s", err)
   251  		}
   252  		if everybodyStr != "" {
   253  			ok, err := strconv.ParseBool(everybodyStr)
   254  			if err != nil {
   255  				return types.MsgStoreCode{}, fmt.Errorf("boolean value expected for instantiate by everybody: %s", err)
   256  			}
   257  			if ok {
   258  				perm = &types.AllowEverybody
   259  			}
   260  		}
   261  
   262  		nobodyStr, err := flags.GetString(flagInstantiateNobody)
   263  		if err != nil {
   264  			return types.MsgStoreCode{}, fmt.Errorf("instantiate by nobody: %s", err)
   265  		}
   266  		if nobodyStr != "" {
   267  			ok, err := strconv.ParseBool(nobodyStr)
   268  			if err != nil {
   269  				return types.MsgStoreCode{}, fmt.Errorf("boolean value expected for instantiate by nobody: %s", err)
   270  			}
   271  			if ok {
   272  				perm = &types.AllowNobody
   273  			}
   274  		}
   275  
   276  	}
   277  
   278  	msg := types.MsgStoreCode{
   279  		Sender:                sender.String(),
   280  		WASMByteCode:          wasm,
   281  		InstantiatePermission: perm,
   282  	}
   283  	return msg, nil
   284  }
   285  
   286  func parseInstantiateArgs(rawCodeID, initMsg string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgInstantiateContract, error) {
   287  	// get the id of the code to instantiate
   288  	codeID, err := strconv.ParseUint(rawCodeID, 10, 64)
   289  	if err != nil {
   290  		return types.MsgInstantiateContract{}, err
   291  	}
   292  
   293  	amountStr, err := flags.GetString(flagAmount)
   294  	if err != nil {
   295  		return types.MsgInstantiateContract{}, fmt.Errorf("amount: %s", err)
   296  	}
   297  	amount, err := sdk.ParseCoinsNormalized(amountStr)
   298  	if err != nil {
   299  		return types.MsgInstantiateContract{}, fmt.Errorf("amount: %s", err)
   300  	}
   301  	label, err := flags.GetString(flagLabel)
   302  	if err != nil {
   303  		return types.MsgInstantiateContract{}, fmt.Errorf("label: %s", err)
   304  	}
   305  	if label == "" {
   306  		return types.MsgInstantiateContract{}, errors.New("label is required on all contracts")
   307  	}
   308  	adminStr, err := flags.GetString(flagAdmin)
   309  	if err != nil {
   310  		return types.MsgInstantiateContract{}, fmt.Errorf("admin: %s", err)
   311  	}
   312  	noAdmin, err := flags.GetBool(flagNoAdmin)
   313  	if err != nil {
   314  		return types.MsgInstantiateContract{}, fmt.Errorf("no-admin: %s", err)
   315  	}
   316  
   317  	// ensure sensible admin is set (or explicitly immutable)
   318  	if adminStr == "" && !noAdmin {
   319  		return types.MsgInstantiateContract{}, fmt.Errorf("you must set an admin or explicitly pass --no-admin to make it immutible (wasmd issue #719)")
   320  	}
   321  	if adminStr != "" && noAdmin {
   322  		return types.MsgInstantiateContract{}, fmt.Errorf("you set an admin and passed --no-admin, those cannot both be true")
   323  	}
   324  
   325  	// build and sign the transaction, then broadcast to Tendermint
   326  	msg := types.MsgInstantiateContract{
   327  		Sender: sender.String(),
   328  		CodeID: codeID,
   329  		Label:  label,
   330  		Funds:  sdk.CoinsToCoinAdapters(amount),
   331  		Msg:    []byte(initMsg),
   332  		Admin:  adminStr,
   333  	}
   334  	return msg, nil
   335  }
   336  
   337  func parseExecuteArgs(contractAddr string, execMsg string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgExecuteContract, error) {
   338  	amountStr, err := flags.GetString(flagAmount)
   339  	if err != nil {
   340  		return types.MsgExecuteContract{}, fmt.Errorf("amount: %s", err)
   341  	}
   342  
   343  	amount, err := sdk.ParseCoinsNormalized(amountStr)
   344  	if err != nil {
   345  		return types.MsgExecuteContract{}, err
   346  	}
   347  
   348  	return types.MsgExecuteContract{
   349  		Sender:   sender.String(),
   350  		Contract: contractAddr,
   351  		Funds:    sdk.CoinsToCoinAdapters(amount),
   352  		Msg:      []byte(execMsg),
   353  	}, nil
   354  }
   355  
   356  func parseMigrateContractArgs(args []string, cliCtx clientCtx.CLIContext) (types.MsgMigrateContract, error) {
   357  	// get the id of the code to instantiate
   358  	codeID, err := strconv.ParseUint(args[1], 10, 64)
   359  	if err != nil {
   360  		return types.MsgMigrateContract{}, sdkerrors.Wrap(err, "code id")
   361  	}
   362  
   363  	migrateMsg := args[2]
   364  
   365  	msg := types.MsgMigrateContract{
   366  		Sender:   cliCtx.GetFromAddress().String(),
   367  		Contract: args[0],
   368  		CodeID:   codeID,
   369  		Msg:      []byte(migrateMsg),
   370  	}
   371  	return msg, nil
   372  }
   373  
   374  func parseUpdateContractAdminArgs(args []string, cliCtx clientCtx.CLIContext) (types.MsgUpdateAdmin, error) {
   375  	msg := types.MsgUpdateAdmin{
   376  		Sender:   cliCtx.GetFromAddress().String(),
   377  		Contract: args[0],
   378  		NewAdmin: args[1],
   379  	}
   380  	return msg, nil
   381  }