diff --git a/ante/ante.go b/ante/ante.go index 21de25d6a1e..611ff38835c 100644 --- a/ante/ante.go +++ b/ante/ante.go @@ -1,7 +1,7 @@ package ante import ( - "cosmossdk.io/errors" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -25,26 +25,26 @@ type HandlerOptions struct { func NewAnteHandler(opts HandlerOptions) (sdk.AnteHandler, error) { if opts.AccountKeeper == nil { - return nil, errors.Wrap(sdkerrors.ErrLogic, "account keeper is required for AnteHandler") + return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "account keeper is required for AnteHandler") } if opts.BankKeeper == nil { - return nil, errors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler") + return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler") } if opts.SignModeHandler == nil { - return nil, errors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for AnteHandler") + return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for AnteHandler") } if opts.IBCkeeper == nil { - return nil, errors.Wrap(sdkerrors.ErrLogic, "IBC keeper is required for AnteHandler") + return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "IBC keeper is required for AnteHandler") } // TODO: Enable with Globalfee // if opts.GlobalFeeSubspace.Name() == "" { - // return nil, sdkerrors.Wrap(sdkerrors.ErrNotFound, "globalfee param store is required for AnteHandler") - //} + // return nil, errorsmod.Wrap(sdkerrors.ErrNotFound, "globalfee param store is required for AnteHandler") + // } if opts.StakingSubspace.Name() == "" { - return nil, errors.Wrap(sdkerrors.ErrNotFound, "staking param store is required for AnteHandler") + return nil, errorsmod.Wrap(sdkerrors.ErrNotFound, "staking param store is required for AnteHandler") } if opts.GovKeeper == nil { - return nil, errors.Wrap(sdkerrors.ErrLogic, "gov keeper is required for AnteHandler") + return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "gov keeper is required for AnteHandler") } sigGasConsumer := opts.SigGasConsumer diff --git a/ante/gov_ante.go b/ante/gov_ante.go index 7645f201df6..13930843de1 100644 --- a/ante/gov_ante.go +++ b/ante/gov_ante.go @@ -1,7 +1,7 @@ package ante import ( - "cosmossdk.io/errors" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -50,7 +50,7 @@ func (g GovPreventSpamDecorator) ValidateGovMsgs(ctx sdk.Context, msgs []sdk.Msg params := g.govKeeper.GetParams(ctx) minInitialDeposit := g.calcMinInitialDeposit(params.MinDeposit) if msg.InitialDeposit.IsAllLT(minInitialDeposit) { - return errors.Wrapf(sdkerrors.ErrInsufficientFunds, "insufficient initial deposit amount - required: %v", minInitialDeposit) + return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, "insufficient initial deposit amount - required: %v", minInitialDeposit) } } @@ -61,7 +61,7 @@ func (g GovPreventSpamDecorator) ValidateGovMsgs(ctx sdk.Context, msgs []sdk.Msg for _, v := range execMsg.Msgs { var innerMsg sdk.Msg if err := g.cdc.UnpackAny(v, &innerMsg); err != nil { - return errors.Wrapf(sdkerrors.ErrUnauthorized, "cannot unmarshal authz exec msgs") + return errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "cannot unmarshal authz exec msgs") } if err := validMsg(innerMsg); err != nil { return err diff --git a/go.mod b/go.mod index 64193503bc0..bd62126501c 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/cosmos/gogoproto v1.4.8 github.com/cosmos/ibc-go/v7 v7.0.0 github.com/cosmos/interchain-security v1.0.1-0.20230522154154-1bb8d39e691a + cosmossdk.io/errors v1.0.0-beta.7 cosmossdk.io/math v1.0.1 github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/interchain-security/v2 v2.0.0-rc2 diff --git a/x/globalfee/ante/fee.go b/x/globalfee/ante/fee.go index 621a708079e..cc44edb1cb8 100644 --- a/x/globalfee/ante/fee.go +++ b/x/globalfee/ante/fee.go @@ -4,11 +4,12 @@ package ante // "errors" // "fmt" -// sdk "github.com/cosmos/cosmos-sdk/types" -// sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -// paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" -// stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -// tmstrings "github.com/tendermint/tendermint/libs/strings" +// errorsmod "cosmossdk.io/errors" +// sdk "github.com/cosmos/cosmos-sdk/types" +// sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +// paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +// stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +// tmstrings "github.com/tendermint/tendermint/libs/strings" // "github.com/cosmos/gaia/v11/x/globalfee" // "github.com/cosmos/gaia/v11/x/globalfee/types" @@ -47,11 +48,11 @@ package ante // } // } -// // AnteHandle implements the AnteDecorator interface +// AnteHandle implements the AnteDecorator interface // func (mfd FeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { // feeTx, ok := tx.(sdk.FeeTx) // if !ok { -// return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must implement the sdk.FeeTx interface") +// return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must implement the sdk.FeeTx interface") // } // // Do not check minimum-gas-prices and global fees during simulations @@ -65,11 +66,12 @@ package ante // return ctx, err // } -// // reject the transaction early if the feeCoins have more denoms than the fee requirement -// // feeRequired cannot be empty -// if feeTx.GetFee().Len() > feeRequired.Len() { -// return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "fee is not a subset of required fees; got %s, required: %s", feeTx.GetFee().String(), feeRequired.String()) -// } +// reject the transaction early if the feeCoins have more denoms than the fee requirement + +// feeRequired cannot be empty +// if feeTx.GetFee().Len() > feeRequired.Len() { +// return ctx, errorsmod.Wrapf(sdkerrors.ErrInvalidCoins, "fee is not a subset of required fees; got %s, required: %s", feeTx.GetFee().String(), feeRequired.String()) +// } // // Sort fee tx's coins, zero coins in feeCoins are already removed // feeCoins := feeTx.GetFee().Sort() @@ -112,18 +114,18 @@ package ante // return next(ctx, tx, simulate) // } -// // if the msg does not satisfy bypass condition and the feeCoins denoms are subset of feeRequired, -// // check the feeCoins amount against feeRequired -// // -// // when feeCoins=[] -// // special case: and there is zero coin in fee requirement, pass, -// // otherwise, err -// if len(feeCoins) == 0 { -// if len(zeroCoinFeesDenomReq) != 0 { -// return next(ctx, tx, simulate) -// } -// return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins.String(), feeRequired.String()) +// if the msg does not satisfy bypass condition and the feeCoins denoms are subset of feeRequired, +// check the feeCoins amount against feeRequired +// +// when feeCoins=[] +// special case: and there is zero coin in fee requirement, pass, +// otherwise, err +// if len(feeCoins) == 0 { +// if len(zeroCoinFeesDenomReq) != 0 { +// return next(ctx, tx, simulate) // } +// return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins.String(), feeRequired.String()) +// } // // when feeCoins != [] // // special case: if TX has at least one of the zeroCoinFeesDenomReq, then it should pass diff --git a/x/globalfee/module.go b/x/globalfee/module.go index b2920bfcd6c..e1e367455f2 100644 --- a/x/globalfee/module.go +++ b/x/globalfee/module.go @@ -5,17 +5,17 @@ package globalfee // "encoding/json" // "fmt" -// "github.com/cosmos/cosmos-sdk/client" -// "github.com/cosmos/cosmos-sdk/codec" -// codectypes "github.com/cosmos/cosmos-sdk/codec/types" -// sdk "github.com/cosmos/cosmos-sdk/types" -// sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -// "github.com/cosmos/cosmos-sdk/types/module" -// paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" -// "github.com/gorilla/mux" -// "github.com/grpc-ecosystem/grpc-gateway/runtime" -// "github.com/spf13/cobra" -// abci "github.com/tendermint/tendermint/abci/types" +// errorsmod "cosmossdk.io/errors" +// "github.com/cosmos/cosmos-sdk/client" +// "github.com/cosmos/cosmos-sdk/codec" +// codectypes "github.com/cosmos/cosmos-sdk/codec/types" +// sdk "github.com/cosmos/cosmos-sdk/types" +// "github.com/cosmos/cosmos-sdk/types/module" +// paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" +// "github.com/gorilla/mux" +// "github.com/grpc-ecosystem/grpc-gateway/runtime" +// "github.com/spf13/cobra" +// abci "github.com/tendermint/tendermint/abci/types" // "github.com/cosmos/gaia/v11/x/globalfee/client/cli" // "github.com/cosmos/gaia/v11/x/globalfee/keeper" @@ -48,7 +48,7 @@ package globalfee // return err // } // if err := data.Params.ValidateBasic(); err != nil { -// return sdkerrors.Wrap(err, "params") +// return errorsmod.Wrap(err, "params") // } // return nil // } diff --git a/x/globalfee/types/genesis.go b/x/globalfee/types/genesis.go index b37e6be61f1..5d045dd9b54 100644 --- a/x/globalfee/types/genesis.go +++ b/x/globalfee/types/genesis.go @@ -1,41 +1,40 @@ package types -// // import ( -// "encoding/json" -// -// "github.com/cosmos/cosmos-sdk/codec" -// sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -//) -// -//// NewGenesisState - Create a new genesis state +// "encoding/json" + +// errorsmod "cosmossdk.io/errors" +// "github.com/cosmos/cosmos-sdk/codec" +// ) + +// // NewGenesisState - Create a new genesis state // func NewGenesisState(params Params) *GenesisState { -// return &GenesisState{ -// Params: params, -// } -//} -// -//// DefaultGenesisState - Return a default genesis state +// return &GenesisState{ +// Params: params, +// } +// } + +// // DefaultGenesisState - Return a default genesis state // func DefaultGenesisState() *GenesisState { -// return NewGenesisState(DefaultParams()) -//} -// -//// GetGenesisStateFromAppState returns x/auth GenesisState given raw application -//// genesis state. +// return NewGenesisState(DefaultParams()) +// } + +// // GetGenesisStateFromAppState returns x/auth GenesisState given raw application +// // genesis state. // func GetGenesisStateFromAppState(cdc codec.Codec, appState map[string]json.RawMessage) *GenesisState { -// var genesisState GenesisState -// -// if appState[ModuleName] != nil { -// cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) -// } -// -// return &genesisState -//} -// +// var genesisState GenesisState + +// if appState[ModuleName] != nil { +// cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) +// } + +// return &genesisState +// } + // func ValidateGenesis(data GenesisState) error { -// if err := data.Params.ValidateBasic(); err != nil { -// return sdkerrors.Wrap(err, "globalfee params") -// } -// -// return nil -//} +// if err := data.Params.ValidateBasic(); err != nil { +// return errorsmod.Wrap(err, "globalfee params") +// } + +// return nil +// } diff --git a/x/globalfee/types/params.go b/x/globalfee/types/params.go index cfa6bc8e085..78298f52192 100644 --- a/x/globalfee/types/params.go +++ b/x/globalfee/types/params.go @@ -1,155 +1,155 @@ package types -// // import ( -// "fmt" -// "strings" -// -// sdk "github.com/cosmos/cosmos-sdk/types" -// sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -// paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" -// ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" -// ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" -//) -// +// "fmt" +// "strings" + +// errorsmod "cosmossdk.io/errors" +// sdk "github.com/cosmos/cosmos-sdk/types" +// sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +// paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +// ibcclienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" +// ibcchanneltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" +// ) + // var ( -// // ParamStoreKeyMinGasPrices store key -// ParamStoreKeyMinGasPrices = []byte("MinimumGasPricesParam") -// ParamStoreKeyBypassMinFeeMsgTypes = []byte("BypassMinFeeMsgTypes") -// ParamStoreKeyMaxTotalBypassMinFeeMsgGasUsage = []byte("MaxTotalBypassMinFeeMsgGasUsage") -// -// // DefaultMinGasPrices is set at runtime to the staking token with zero amount i.e. "0uatom" -// // see DefaultZeroGlobalFee method in gaia/x/globalfee/ante/fee.go. -// DefaultMinGasPrices = sdk.DecCoins{} -// DefaultBypassMinFeeMsgTypes = []string{ -// sdk.MsgTypeURL(&ibcchanneltypes.MsgRecvPacket{}), -// sdk.MsgTypeURL(&ibcchanneltypes.MsgAcknowledgement{}), -// sdk.MsgTypeURL(&ibcclienttypes.MsgUpdateClient{}), -// sdk.MsgTypeURL(&ibcchanneltypes.MsgTimeout{}), -// sdk.MsgTypeURL(&ibcchanneltypes.MsgTimeoutOnClose{}), -// } -// -// // maxTotalBypassMinFeeMsgGasUsage is the allowed maximum gas usage -// // for all the bypass msgs in a transactions. -// // A transaction that contains only bypass message types and the gas usage does not -// // exceed maxTotalBypassMinFeeMsgGasUsage can be accepted with a zero fee. -// // For details, see gaiafeeante.NewFeeDecorator() -// DefaultmaxTotalBypassMinFeeMsgGasUsage uint64 = 1_000_000 -//) -// -//// DefaultParams returns default parameters +// // ParamStoreKeyMinGasPrices store key +// ParamStoreKeyMinGasPrices = []byte("MinimumGasPricesParam") +// ParamStoreKeyBypassMinFeeMsgTypes = []byte("BypassMinFeeMsgTypes") +// ParamStoreKeyMaxTotalBypassMinFeeMsgGasUsage = []byte("MaxTotalBypassMinFeeMsgGasUsage") + +// // DefaultMinGasPrices is set at runtime to the staking token with zero amount i.e. "0uatom" +// // see DefaultZeroGlobalFee method in gaia/x/globalfee/ante/fee.go. +// DefaultMinGasPrices = sdk.DecCoins{} +// DefaultBypassMinFeeMsgTypes = []string{ +// sdk.MsgTypeURL(&ibcchanneltypes.MsgRecvPacket{}), +// sdk.MsgTypeURL(&ibcchanneltypes.MsgAcknowledgement{}), +// sdk.MsgTypeURL(&ibcclienttypes.MsgUpdateClient{}), +// sdk.MsgTypeURL(&ibcchanneltypes.MsgTimeout{}), +// sdk.MsgTypeURL(&ibcchanneltypes.MsgTimeoutOnClose{}), +// } + +// // maxTotalBypassMinFeeMsgGasUsage is the allowed maximum gas usage +// // for all the bypass msgs in a transactions. +// // A transaction that contains only bypass message types and the gas usage does not +// // exceed maxTotalBypassMinFeeMsgGasUsage can be accepted with a zero fee. +// // For details, see gaiafeeante.NewFeeDecorator() +// DefaultmaxTotalBypassMinFeeMsgGasUsage uint64 = 1_000_000 +// ) + +// // DefaultParams returns default parameters // func DefaultParams() Params { -// return Params{ -// MinimumGasPrices: DefaultMinGasPrices, -// BypassMinFeeMsgTypes: DefaultBypassMinFeeMsgTypes, -// MaxTotalBypassMinFeeMsgGasUsage: DefaultmaxTotalBypassMinFeeMsgGasUsage, -// } -//} -// +// return Params{ +// MinimumGasPrices: DefaultMinGasPrices, +// BypassMinFeeMsgTypes: DefaultBypassMinFeeMsgTypes, +// MaxTotalBypassMinFeeMsgGasUsage: DefaultmaxTotalBypassMinFeeMsgGasUsage, +// } +// } + // func ParamKeyTable() paramtypes.KeyTable { -// return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) -//} -// -//// ValidateBasic performs basic validation. +// return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +// } + +// // ValidateBasic performs basic validation. // func (p Params) ValidateBasic() error { -// if err := validateMinimumGasPrices(p.MinimumGasPrices); err != nil { -// return err -// } -// -// if err := validateBypassMinFeeMsgTypes(p.BypassMinFeeMsgTypes); err != nil { -// return err -// } -// -// return validateMaxTotalBypassMinFeeMsgGasUsage(p.MaxTotalBypassMinFeeMsgGasUsage) -//} -// -//// ParamSetPairs returns the parameter set pairs. +// if err := validateMinimumGasPrices(p.MinimumGasPrices); err != nil { +// return err +// } + +// if err := validateBypassMinFeeMsgTypes(p.BypassMinFeeMsgTypes); err != nil { +// return err +// } + +// return validateMaxTotalBypassMinFeeMsgGasUsage(p.MaxTotalBypassMinFeeMsgGasUsage) +// } + +// // ParamSetPairs returns the parameter set pairs. // func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { -// return paramtypes.ParamSetPairs{ -// paramtypes.NewParamSetPair( -// ParamStoreKeyMinGasPrices, &p.MinimumGasPrices, validateMinimumGasPrices, -// ), -// paramtypes.NewParamSetPair( -// ParamStoreKeyBypassMinFeeMsgTypes, &p.BypassMinFeeMsgTypes, validateBypassMinFeeMsgTypes, -// ), -// paramtypes.NewParamSetPair( -// ParamStoreKeyMaxTotalBypassMinFeeMsgGasUsage, &p.MaxTotalBypassMinFeeMsgGasUsage, validateMaxTotalBypassMinFeeMsgGasUsage, -// ), -// } -//} -// +// return paramtypes.ParamSetPairs{ +// paramtypes.NewParamSetPair( +// ParamStoreKeyMinGasPrices, &p.MinimumGasPrices, validateMinimumGasPrices, +// ), +// paramtypes.NewParamSetPair( +// ParamStoreKeyBypassMinFeeMsgTypes, &p.BypassMinFeeMsgTypes, validateBypassMinFeeMsgTypes, +// ), +// paramtypes.NewParamSetPair( +// ParamStoreKeyMaxTotalBypassMinFeeMsgGasUsage, &p.MaxTotalBypassMinFeeMsgGasUsage, validateMaxTotalBypassMinFeeMsgGasUsage, +// ), +// } +// } + // func validateMinimumGasPrices(i interface{}) error { -// v, ok := i.(sdk.DecCoins) -// if !ok { -// return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "type: %T, expected sdk.DecCoins", i) -// } -// -// dec := DecCoins(v) -// return dec.Validate() -//} -// +// v, ok := i.(sdk.DecCoins) +// if !ok { +// return errorsmod.Wrapf(sdkerrors.ErrInvalidType, "type: %T, expected sdk.DecCoins", i) +// } + +// dec := DecCoins(v) +// return dec.Validate() +// } + // type BypassMinFeeMsgTypes []string -// -//// validateBypassMinFeeMsgTypes checks that bypass msg types aren't empty + +// // validateBypassMinFeeMsgTypes checks that bypass msg types aren't empty // func validateBypassMinFeeMsgTypes(i interface{}) error { -// bypassMinFeeMsgTypes, ok := i.([]string) -// if !ok { -// return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "type: %T, expected []sdk.Msg", i) -// } -// -// for _, msgType := range bypassMinFeeMsgTypes { -// if msgType == "" { -// return fmt.Errorf("invalid empty bypass msg type") -// } -// -// if !strings.HasPrefix(msgType, sdk.MsgTypeURL(nil)) { -// return fmt.Errorf("invalid bypass msg type name %s", msgType) -// } -// } -// -// return nil -//} -// +// bypassMinFeeMsgTypes, ok := i.([]string) +// if !ok { +// return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "type: %T, expected []sdk.Msg", i) +// } + +// for _, msgType := range bypassMinFeeMsgTypes { +// if msgType == "" { +// return fmt.Errorf("invalid empty bypass msg type") +// } + +// if !strings.HasPrefix(msgType, sdk.MsgTypeURL(nil)) { +// return fmt.Errorf("invalid bypass msg type name %s", msgType) +// } +// } + +// return nil +// } + // func validateMaxTotalBypassMinFeeMsgGasUsage(i interface{}) error { -// _, ok := i.(uint64) -// if !ok { -// return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "type: %T, expected uint64", i) -// } -// -// return nil -//} -// +// _, ok := i.(uint64) +// if !ok { +// return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "type: %T, expected uint64", i) +// } + +// return nil +// } + // type DecCoins sdk.DecCoins -// -//// Validate checks that the DecCoins are sorted, have nonnegtive amount, with a valid and unique -//// denomination (i.e no duplicates). Otherwise, it returns an error. + +// // Validate checks that the DecCoins are sorted, have nonnegtive amount, with a valid and unique +// // denomination (i.e no duplicates). Otherwise, it returns an error. // func (coins DecCoins) Validate() error { -// if len(coins) == 0 { -// return nil -// } -// -// lowDenom := "" -// seenDenoms := make(map[string]bool) -// -// for i, coin := range coins { -// if seenDenoms[coin.Denom] { -// return fmt.Errorf("duplicate denomination %s", coin.Denom) -// } -// if err := sdk.ValidateDenom(coin.Denom); err != nil { -// return err -// } -// // skip the denom order check for the first denom in the coins list -// if i != 0 && coin.Denom <= lowDenom { -// return fmt.Errorf("denomination %s is not sorted", coin.Denom) -// } -// if coin.IsNegative() { -// return fmt.Errorf("coin %s amount is negative", coin.Amount) -// } -// -// // we compare each coin against the last denom -// lowDenom = coin.Denom -// seenDenoms[coin.Denom] = true -// } -// -// return nil -//} +// if len(coins) == 0 { +// return nil +// } + +// lowDenom := "" +// seenDenoms := make(map[string]bool) + +// for i, coin := range coins { +// if seenDenoms[coin.Denom] { +// return fmt.Errorf("duplicate denomination %s", coin.Denom) +// } +// if err := sdk.ValidateDenom(coin.Denom); err != nil { +// return err +// } +// // skip the denom order check for the first denom in the coins list +// if i != 0 && coin.Denom <= lowDenom { +// return fmt.Errorf("denomination %s is not sorted", coin.Denom) +// } +// if coin.IsNegative() { +// return fmt.Errorf("coin %s amount is negative", coin.Amount) +// } + +// // we compare each coin against the last denom +// lowDenom = coin.Denom +// seenDenoms[coin.Denom] = true +// } + +// return nil +// }