Skip to content

Commit

Permalink
Fix simulation (#247)
Browse files Browse the repository at this point in the history
  • Loading branch information
nghuyenthevinh2000 committed Jun 6, 2023
1 parent 6d90bd6 commit afa5dff
Show file tree
Hide file tree
Showing 10 changed files with 284 additions and 68 deletions.
3 changes: 3 additions & 0 deletions custom/auth/ante/tax.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ func FilterMsgAndComputeTax(ctx sdk.Context, tk TreasuryKeeper, msgs ...sdk.Msg)
case *wasm.MsgInstantiateContract:
taxes = taxes.Add(computeTax(ctx, tk, msg.Funds)...)

case *wasm.MsgInstantiateContract2:
taxes = taxes.Add(computeTax(ctx, tk, msg.Funds)...)

case *wasm.MsgExecuteContract:
taxes = taxes.Add(computeTax(ctx, tk, msg.Funds)...)

Expand Down
8 changes: 8 additions & 0 deletions custom/auth/client/utils/feeutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,14 @@ func FilterMsgAndComputeTax(clientCtx client.Context, msgs ...sdk.Msg) (taxes sd

taxes = taxes.Add(tax...)

case *wasmexported.MsgInstantiateContract2:
tax, err := computeTax(clientCtx, taxRate, msg.Funds)
if err != nil {
return nil, err
}

taxes = taxes.Add(tax...)

case *wasmexported.MsgExecuteContract:
tax, err := computeTax(clientCtx, taxRate, msg.Funds)
if err != nil {
Expand Down
234 changes: 232 additions & 2 deletions custom/wasm/client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package cli

import (
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"strconv"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"

Expand All @@ -17,7 +22,10 @@ import (
)

const (
flagAmount = "amount"
flagAmount = "amount"
flagAdmin = "admin"
flagNoAdmin = "no-admin"
flagFixMsg = "fix-msg"
)

// GetTxCmd returns the transaction commands for this module
Expand All @@ -32,7 +40,7 @@ func GetTxCmd() *cobra.Command {
}
txCmd.AddCommand(
cli.StoreCodeCmd(),
cli.InstantiateContractCmd(),
InstantiateContractCmd(),
cli.InstantiateContract2Cmd(),
ExecuteContractCmd(),
cli.MigrateContractCmd(),
Expand Down Expand Up @@ -92,6 +100,142 @@ func ExecuteContractCmd() *cobra.Command {
return cmd
}

// InstantiateContractCmd will instantiate a contract from previously uploaded code.
func InstantiateContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "instantiate [code_id_int64] [json_encoded_init_args] --admin [address,optional] --amount [coins,optional] ",
Short: "Instantiate a wasm contract",
Long: fmt.Sprintf(`Creates a new instance of an uploaded wasm code with the given 'constructor' message.
Each contract instance has a unique address assigned.
Example:
$ %s tx wasm instantiate 1 '{"foo":"bar"}' --admin="$(%s keys show mykey -a)" \
--from mykey --amount="100ustake"
`, version.AppName, version.AppName),
Aliases: []string{"start", "init", "inst", "i"},
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
msg, err := parseInstantiateArgs(args[0], args[1], clientCtx.GetFromAddress(), cmd.Flags())
if err != nil {
return err
}
if err := msg.ValidateBasic(); err != nil {
return err
}

// Generate transaction factory for gas simulation
txf := tx.NewFactoryCLI(clientCtx, cmd.Flags())

if !clientCtx.GenerateOnly && txf.Fees().IsZero() {
// estimate tax and gas
stdFee, err := feeutils.ComputeFeesWithCmd(clientCtx, cmd.Flags(), msg)
if err != nil {
return err
}

// override gas and fees
txf = txf.
WithFees(stdFee.Amount.String()).
WithGas(stdFee.Gas).
WithSimulateAndExecute(false).
WithGasPrices("")
}

return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg)
},
SilenceUsage: true,
}

cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation")
cmd.Flags().String(flagAdmin, "", "Address of an admin")
cmd.Flags().Bool(flagNoAdmin, false, "You must set this explicitly if you don't want an admin")
flags.AddTxFlagsToCmd(cmd)
return cmd
}

// InstantiateContract2Cmd will instantiate a contract from previously uploaded code with predicable address generated
func InstantiateContract2Cmd() *cobra.Command {
decoder := newArgDecoder(hex.DecodeString)
cmd := &cobra.Command{
Use: "instantiate2 [code_id_int64] [json_encoded_init_args] [salt] --admin [address,optional] --amount [coins,optional] " +
"--fix-msg [bool,optional]",
Short: "Instantiate a wasm contract with predictable address",
Long: fmt.Sprintf(`Creates a new instance of an uploaded wasm code with the given 'constructor' message.
Each contract instance has a unique address assigned. They are assigned automatically but in order to have predictable addresses
for special use cases, the given 'salt' argument and '--fix-msg' parameters can be used to generate a custom address.
Predictable address example (also see '%s query wasm build-address -h'):
$ %s tx wasm instantiate2 1 '{"foo":"bar"}' $(echo -n "testing" | xxd -ps) --admin="$(%s keys show mykey -a)" \
--from mykey --amount="100ustake" \
--fix-msg
`, version.AppName, version.AppName, version.AppName),
Aliases: []string{"start", "init", "inst", "i"},
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
salt, err := decoder.DecodeString(args[2])
if err != nil {
return fmt.Errorf("salt: %w", err)
}
fixMsg, err := cmd.Flags().GetBool(flagFixMsg)
if err != nil {
return fmt.Errorf("fix msg: %w", err)
}
data, err := parseInstantiateArgs(args[0], args[1], clientCtx.GetFromAddress(), cmd.Flags())
if err != nil {
return err
}
msg := &types.MsgInstantiateContract2{
Sender: data.Sender,
Admin: data.Admin,
CodeID: data.CodeID,
Msg: data.Msg,
Funds: data.Funds,
Salt: salt,
FixMsg: fixMsg,
}
if err := msg.ValidateBasic(); err != nil {
return err
}

// Generate transaction factory for gas simulation
txf := tx.NewFactoryCLI(clientCtx, cmd.Flags())

if !clientCtx.GenerateOnly && txf.Fees().IsZero() {
// estimate tax and gas
stdFee, err := feeutils.ComputeFeesWithCmd(clientCtx, cmd.Flags(), msg)
if err != nil {
return err
}

// override gas and fees
txf = txf.
WithFees(stdFee.Amount.String()).
WithGas(stdFee.Gas).
WithSimulateAndExecute(false).
WithGasPrices("")
}

return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg)
},
SilenceUsage: true,
}

cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation")
cmd.Flags().String(flagAdmin, "", "Address of an admin")
cmd.Flags().Bool(flagNoAdmin, false, "You must set this explicitly if you don't want an admin")
cmd.Flags().Bool(flagFixMsg, false, "An optional flag to include the json_encoded_init_args for the predictable address generation mode")
decoder.RegisterFlags(cmd.PersistentFlags(), "salt")
flags.AddTxFlagsToCmd(cmd)
return cmd
}

func parseExecuteArgs(contractAddr string, execMsg string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgExecuteContract, error) {
amountStr, err := flags.GetString(flagAmount)
if err != nil {
Expand All @@ -110,3 +254,89 @@ func parseExecuteArgs(contractAddr string, execMsg string, sender sdk.AccAddress
Msg: []byte(execMsg),
}, nil
}

func parseInstantiateArgs(rawCodeID, initMsg string, sender sdk.AccAddress, flags *flag.FlagSet) (*types.MsgInstantiateContract, error) {
// get the id of the code to instantiate
codeID, err := strconv.ParseUint(rawCodeID, 10, 64)
if err != nil {
return nil, err
}

amountStr, err := flags.GetString(flagAmount)
if err != nil {
return nil, fmt.Errorf("amount: %s", err)
}
amount, err := sdk.ParseCoinsNormalized(amountStr)
if err != nil {
return nil, fmt.Errorf("amount: %s", err)
}
adminStr, err := flags.GetString(flagAdmin)
if err != nil {
return nil, fmt.Errorf("admin: %s", err)
}
noAdmin, err := flags.GetBool(flagNoAdmin)
if err != nil {
return nil, fmt.Errorf("no-admin: %s", err)
}

// ensure sensible admin is set (or explicitly immutable)
if adminStr == "" && !noAdmin {
return nil, fmt.Errorf("you must set an admin or explicitly pass --no-admin to make it immutible (wasmd issue #719)")
}
if adminStr != "" && noAdmin {
return nil, fmt.Errorf("you set an admin and passed --no-admin, those cannot both be true")
}

// build and sign the transaction, then broadcast to Tendermint
msg := types.MsgInstantiateContract{
Sender: sender.String(),
CodeID: codeID,
Funds: amount,
Msg: []byte(initMsg),
Admin: adminStr,
}
return &msg, nil
}

type argumentDecoder struct {
// dec is the default decoder
dec func(string) ([]byte, error)
asciiF, hexF, b64F bool
}

func newArgDecoder(def func(string) ([]byte, error)) *argumentDecoder {
return &argumentDecoder{dec: def}
}

func (a *argumentDecoder) RegisterFlags(f *flag.FlagSet, argName string) {
f.BoolVar(&a.asciiF, "ascii", false, "ascii encoded "+argName)
f.BoolVar(&a.hexF, "hex", false, "hex encoded "+argName)
f.BoolVar(&a.b64F, "b64", false, "base64 encoded "+argName)
}

func (a *argumentDecoder) DecodeString(s string) ([]byte, error) {
found := -1
for i, v := range []*bool{&a.asciiF, &a.hexF, &a.b64F} {
if !*v {
continue
}
if found != -1 {
return nil, errors.New("multiple decoding flags used")
}
found = i
}
switch found {
case 0:
return asciiDecodeString(s)
case 1:
return hex.DecodeString(s)
case 2:
return base64.StdEncoding.DecodeString(s)
default:
return a.dec(s)
}
}

func asciiDecodeString(s string) ([]byte, error) {
return []byte(s), nil
}
2 changes: 0 additions & 2 deletions custom/wasm/client/rest/rest.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// Deprecated: the rest package will be removed. You can use the GRPC gateway instead
package rest

import (
Expand All @@ -7,7 +6,6 @@ import (
)

// RegisterRoutes registers staking-related REST handlers to a router
// Deprecated: the rest package will be removed. You can use the GRPC gateway instead
func RegisterRoutes(cliCtx client.Context, r *mux.Router) {
registerQueryRoutes(cliCtx, r)
registerTxRoutes(cliCtx, r)
Expand Down
25 changes: 25 additions & 0 deletions custom/wasm/client/rest/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/CosmWasm/wasmd/x/wasm/ioutils"
"github.com/CosmWasm/wasmd/x/wasm/types"
feeutils "github.com/classic-terra/core/v2/custom/auth/client/utils"
)

func registerTxRoutes(cliCtx client.Context, r *mux.Router) {
Expand Down Expand Up @@ -112,6 +113,18 @@ func instantiateContractHandlerFn(cliCtx client.Context) http.HandlerFunc {
return
}

if req.BaseReq.Fees.IsZero() {
stdFee, err := feeutils.ComputeFeesWithBaseReq(cliCtx, req.BaseReq, &msg)
if rest.CheckBadRequestError(w, err) {
return
}

// override gas and fees
req.BaseReq.Gas = strconv.FormatUint(stdFee.Gas, 10)
req.BaseReq.Fees = stdFee.Amount
req.BaseReq.GasPrices = sdk.DecCoins{}
}

tx.WriteGeneratedTxResponse(cliCtx, w, req.BaseReq, &msg)
}
}
Expand Down Expand Up @@ -142,6 +155,18 @@ func executeContractHandlerFn(cliCtx client.Context) http.HandlerFunc {
return
}

if req.BaseReq.Fees.IsZero() {
stdFee, err := feeutils.ComputeFeesWithBaseReq(cliCtx, req.BaseReq, &msg)
if rest.CheckBadRequestError(w, err) {
return
}

// override gas and fees
req.BaseReq.Gas = strconv.FormatUint(stdFee.Gas, 10)
req.BaseReq.Fees = stdFee.Amount
req.BaseReq.GasPrices = sdk.DecCoins{}
}

tx.WriteGeneratedTxResponse(cliCtx, w, req.BaseReq, &msg)
}
}
8 changes: 8 additions & 0 deletions custom/wasm/module.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package wasm

import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/gorilla/mux"
"github.com/spf13/cobra"

"github.com/CosmWasm/wasmd/x/wasm"

customcli "github.com/classic-terra/core/v2/custom/wasm/client/cli"
customrest "github.com/classic-terra/core/v2/custom/wasm/client/rest"
)

var (
Expand All @@ -18,6 +21,11 @@ type AppModuleBasic struct {
wasm.AppModuleBasic
}

// RegisterRESTRoutes registers the REST routes for the wasm module.
func (AppModuleBasic) RegisterRESTRoutes(cliCtx client.Context, rtr *mux.Router) {
customrest.RegisterRoutes(cliCtx, rtr)
}

// GetTxCmd returns the root tx command for the wasm module.
func (b AppModuleBasic) GetTxCmd() *cobra.Command {
return customcli.GetTxCmd()
Expand Down
Loading

0 comments on commit afa5dff

Please sign in to comment.