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 }