cosmos-sdk/x/staking/keeper/msg_server.go

375 lines
11 KiB
Go
Raw Normal View History

package keeper
import (
"context"
"time"
metrics "github.com/armon/go-metrics"
tmstrings "github.com/tendermint/tendermint/libs/strings"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
type msgServer struct {
Keeper
}
// NewMsgServerImpl returns an implementation of the bank MsgServer interface
// for the provided Keeper.
func NewMsgServerImpl(keeper Keeper) types.MsgServer {
return &msgServer{Keeper: keeper}
}
var _ types.MsgServer = msgServer{}
// CreateValidator defines a method for creating a new validator
func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateValidator) (*types.MsgCreateValidatorResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
if err != nil {
return nil, err
}
// check to see if the pubkey or sender has been registered before
if _, found := k.GetValidator(ctx, valAddr); found {
return nil, types.ErrValidatorOwnerExists
}
pk, ok := msg.Pubkey.GetCachedValue().(cryptotypes.PubKey)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "Expecting cryptotypes.PubKey, got %T", pk)
}
if _, found := k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)); found {
return nil, types.ErrValidatorPubKeyExists
}
bondDenom := k.BondDenom(ctx)
if msg.Value.Denom != bondDenom {
return nil, sdkerrors.Wrapf(
sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Value.Denom, bondDenom,
)
}
if _, err := msg.Description.EnsureLength(); err != nil {
return nil, err
}
cp := ctx.ConsensusParams()
if cp != nil && cp.Validator != nil {
if !tmstrings.StringInSlice(pk.Type(), cp.Validator.PubKeyTypes) {
return nil, sdkerrors.Wrapf(
types.ErrValidatorPubKeyTypeNotSupported,
"got: %s, expected: %s", pk.Type(), cp.Validator.PubKeyTypes,
)
}
}
Use any as validator pubkey (#7597) * protobuf pubkey type update * wip2 * wip3 * solving types.NewValidator issues * remove bech32 from validator type assignment * update Validator interface * Changelog update * wip4 * update genutil * fix simapp & x/ibc/testing tests * update staking * changelog update * fix import cycle in tests * fix amino panic on TestValidatorMarshalUnmarshalJSON * fix TestValidatorMarshalUnmarshalJSON consensus_pubkey check * Add UnpackInterfaces to HistoricalInfo * fix TestHistoricalInfo * update todos * fix: Expecting ed25519.PubKey to implement proto.Message * fix linter issues * Fix migrate test * Update CHANGELOG.md Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com> * review comments * cosmetic changes * add UnpackInterfaces got GenesisRandomized test * Validator.Equal reuses Validator.MinEqual * fix test * use Validator.Equal in tests * Fix staking simulation TestRandomizedGenState * Remove TODO * use HistoricalInfo.Equal * use proto.Equal * rename Validator.GetConsPubKey to TmConsPubKey * prefer require.Equal over reflect.DeepEqual * SetHistoricalInfo using a pointer * Fix TestQueryDelegation test * Fix TestQueryValidators test * Fix TestSimulateMsgUnjail test * experiement with LegacyAmino instances * Register codecs in all simapp tests * Fix cli_test compilation problems * fix typo sdk -> std * fix typo * fix TestPlanStringer * Rename to MakeEncodingConfig * Remove RegisterCodecsTests * Use gRPC in GetCmdQueryValidators * Empty status * fix info log check * linter fixes * rename simapparams to simappparams * Update simapp/test_helpers.go Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com> * comments updates * use valAddr1 instead of sdk.ValAddress(pk1.Address().Bytes()) Co-authored-by: Cory Levinson <cjlevinson@gmail.com> Co-authored-by: Amaury Martiny <amaury.martiny@protonmail.com> Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>
2020-10-23 05:07:52 -07:00
validator, err := types.NewValidator(valAddr, pk, msg.Description)
if err != nil {
return nil, err
}
commission := types.NewCommissionWithTime(
msg.Commission.Rate, msg.Commission.MaxRate,
msg.Commission.MaxChangeRate, ctx.BlockHeader().Time,
)
validator, err = validator.SetInitialCommission(commission)
if err != nil {
return nil, err
}
delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress)
if err != nil {
return nil, err
}
validator.MinSelfDelegation = msg.MinSelfDelegation
k.SetValidator(ctx, validator)
k.SetValidatorByConsAddr(ctx, validator)
k.SetNewValidatorByPowerIndex(ctx, validator)
// call the after-creation hook
k.AfterValidatorCreated(ctx, validator.GetOperator())
// move coins from the msg.Address account to a (self-delegation) delegator account
// the validator account and global shares are updated within here
// NOTE source will always be from a wallet which are unbonded
_, err = k.Keeper.Delegate(ctx, delegatorAddress, msg.Value.Amount, types.Unbonded, validator, true)
if err != nil {
return nil, err
}
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeCreateValidator,
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress),
fix: fixed missing coin denomination in msg server logs (backport #9786) (#9844) * fix: fixed missing coin denomination in msg server logs (#9786) <!-- The default pull request template is for types feat, fix, or refactor. For other templates, add one of the following parameters to the url: - template=docs.md - template=other.md --> ## Description Closes: [#9785](https://github.com/cosmos/cosmos-sdk/issues/9785) This PR modifies _x/staking/keeper/msg_server.go_ file and fixes coin denomination issues in the emitted events. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [x] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [x] reviewed state machine logic - [x] reviewed API design and naming - [x] reviewed documentation is accurate - [x] reviewed tests and test coverage - [ ] manually tested (if applicable) (cherry picked from commit 18eea3a7c8dca75278a9c3ef9b7e1247ad9a357b) # Conflicts: # CHANGELOG.md * Fix conflicts * move to new section Co-authored-by: Slav Keremidchiev <skeremidch@gmail.com> Co-authored-by: Amaury M <1293565+amaurym@users.noreply.github.com>
2021-08-04 02:54:28 -07:00
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Value.String()),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
),
})
return &types.MsgCreateValidatorResponse{}, nil
}
// EditValidator defines a method for editing an existing validator
func (k msgServer) EditValidator(goCtx context.Context, msg *types.MsgEditValidator) (*types.MsgEditValidatorResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
if err != nil {
return nil, err
}
// validator must already be registered
validator, found := k.GetValidator(ctx, valAddr)
if !found {
return nil, types.ErrNoValidatorFound
}
// replace all editable fields (clients should autofill existing values)
description, err := validator.Description.UpdateDescription(msg.Description)
if err != nil {
return nil, err
}
validator.Description = description
if msg.CommissionRate != nil {
commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate)
if err != nil {
return nil, err
}
// call the before-modification hook since we're about to update the commission
k.BeforeValidatorModified(ctx, valAddr)
validator.Commission = commission
}
if msg.MinSelfDelegation != nil {
if !msg.MinSelfDelegation.GT(validator.MinSelfDelegation) {
return nil, types.ErrMinSelfDelegationDecreased
}
if msg.MinSelfDelegation.GT(validator.Tokens) {
return nil, types.ErrSelfDelegationBelowMinimum
}
validator.MinSelfDelegation = (*msg.MinSelfDelegation)
}
k.SetValidator(ctx, validator)
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeEditValidator,
sdk.NewAttribute(types.AttributeKeyCommissionRate, validator.Commission.String()),
sdk.NewAttribute(types.AttributeKeyMinSelfDelegation, validator.MinSelfDelegation.String()),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, msg.ValidatorAddress),
),
})
return &types.MsgEditValidatorResponse{}, nil
}
// Delegate defines a method for performing a delegation of coins from a delegator to a validator
func (k msgServer) Delegate(goCtx context.Context, msg *types.MsgDelegate) (*types.MsgDelegateResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
valAddr, valErr := sdk.ValAddressFromBech32(msg.ValidatorAddress)
if valErr != nil {
return nil, valErr
}
validator, found := k.GetValidator(ctx, valAddr)
if !found {
return nil, types.ErrNoValidatorFound
}
delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress)
if err != nil {
return nil, err
}
bondDenom := k.BondDenom(ctx)
if msg.Amount.Denom != bondDenom {
return nil, sdkerrors.Wrapf(
sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Amount.Denom, bondDenom,
)
}
// NOTE: source funds are always unbonded
newShares, err := k.Keeper.Delegate(ctx, delegatorAddress, msg.Amount.Amount, types.Unbonded, validator, true)
if err != nil {
return nil, err
}
if msg.Amount.Amount.IsInt64() {
defer func() {
telemetry.IncrCounter(1, types.ModuleName, "delegate")
telemetry.SetGaugeWithLabels(
[]string{"tx", "msg", msg.Type()},
float32(msg.Amount.Amount.Int64()),
[]metrics.Label{telemetry.NewLabel("denom", msg.Amount.Denom)},
)
}()
}
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeDelegate,
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress),
fix: fixed missing coin denomination in msg server logs (backport #9786) (#9844) * fix: fixed missing coin denomination in msg server logs (#9786) <!-- The default pull request template is for types feat, fix, or refactor. For other templates, add one of the following parameters to the url: - template=docs.md - template=other.md --> ## Description Closes: [#9785](https://github.com/cosmos/cosmos-sdk/issues/9785) This PR modifies _x/staking/keeper/msg_server.go_ file and fixes coin denomination issues in the emitted events. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [x] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [x] reviewed state machine logic - [x] reviewed API design and naming - [x] reviewed documentation is accurate - [x] reviewed tests and test coverage - [ ] manually tested (if applicable) (cherry picked from commit 18eea3a7c8dca75278a9c3ef9b7e1247ad9a357b) # Conflicts: # CHANGELOG.md * Fix conflicts * move to new section Co-authored-by: Slav Keremidchiev <skeremidch@gmail.com> Co-authored-by: Amaury M <1293565+amaurym@users.noreply.github.com>
2021-08-04 02:54:28 -07:00
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
sdk.NewAttribute(types.AttributeKeyNewShares, newShares.String()),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
),
})
return &types.MsgDelegateResponse{}, nil
}
// BeginRedelegate defines a method for performing a redelegation of coins from a delegator and source validator to a destination validator
func (k msgServer) BeginRedelegate(goCtx context.Context, msg *types.MsgBeginRedelegate) (*types.MsgBeginRedelegateResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
valSrcAddr, err := sdk.ValAddressFromBech32(msg.ValidatorSrcAddress)
if err != nil {
return nil, err
}
delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress)
if err != nil {
return nil, err
}
shares, err := k.ValidateUnbondAmount(
ctx, delegatorAddress, valSrcAddr, msg.Amount.Amount,
)
if err != nil {
return nil, err
}
bondDenom := k.BondDenom(ctx)
if msg.Amount.Denom != bondDenom {
return nil, sdkerrors.Wrapf(
sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Amount.Denom, bondDenom,
)
}
valDstAddr, err := sdk.ValAddressFromBech32(msg.ValidatorDstAddress)
if err != nil {
return nil, err
}
completionTime, err := k.BeginRedelegation(
ctx, delegatorAddress, valSrcAddr, valDstAddr, shares,
)
if err != nil {
return nil, err
}
if msg.Amount.Amount.IsInt64() {
defer func() {
telemetry.IncrCounter(1, types.ModuleName, "redelegate")
telemetry.SetGaugeWithLabels(
[]string{"tx", "msg", msg.Type()},
float32(msg.Amount.Amount.Int64()),
[]metrics.Label{telemetry.NewLabel("denom", msg.Amount.Denom)},
)
}()
}
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeRedelegate,
sdk.NewAttribute(types.AttributeKeySrcValidator, msg.ValidatorSrcAddress),
sdk.NewAttribute(types.AttributeKeyDstValidator, msg.ValidatorDstAddress),
fix: fixed missing coin denomination in msg server logs (backport #9786) (#9844) * fix: fixed missing coin denomination in msg server logs (#9786) <!-- The default pull request template is for types feat, fix, or refactor. For other templates, add one of the following parameters to the url: - template=docs.md - template=other.md --> ## Description Closes: [#9785](https://github.com/cosmos/cosmos-sdk/issues/9785) This PR modifies _x/staking/keeper/msg_server.go_ file and fixes coin denomination issues in the emitted events. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [x] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [x] reviewed state machine logic - [x] reviewed API design and naming - [x] reviewed documentation is accurate - [x] reviewed tests and test coverage - [ ] manually tested (if applicable) (cherry picked from commit 18eea3a7c8dca75278a9c3ef9b7e1247ad9a357b) # Conflicts: # CHANGELOG.md * Fix conflicts * move to new section Co-authored-by: Slav Keremidchiev <skeremidch@gmail.com> Co-authored-by: Amaury M <1293565+amaurym@users.noreply.github.com>
2021-08-04 02:54:28 -07:00
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
),
})
return &types.MsgBeginRedelegateResponse{
CompletionTime: completionTime,
}, nil
}
// Undelegate defines a method for performing an undelegation from a delegate and a validator
func (k msgServer) Undelegate(goCtx context.Context, msg *types.MsgUndelegate) (*types.MsgUndelegateResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
addr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
if err != nil {
return nil, err
}
delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress)
if err != nil {
return nil, err
}
shares, err := k.ValidateUnbondAmount(
ctx, delegatorAddress, addr, msg.Amount.Amount,
)
if err != nil {
return nil, err
}
bondDenom := k.BondDenom(ctx)
if msg.Amount.Denom != bondDenom {
return nil, sdkerrors.Wrapf(
sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Amount.Denom, bondDenom,
)
}
completionTime, err := k.Keeper.Undelegate(ctx, delegatorAddress, addr, shares)
if err != nil {
return nil, err
}
if msg.Amount.Amount.IsInt64() {
defer func() {
telemetry.IncrCounter(1, types.ModuleName, "undelegate")
telemetry.SetGaugeWithLabels(
[]string{"tx", "msg", msg.Type()},
float32(msg.Amount.Amount.Int64()),
[]metrics.Label{telemetry.NewLabel("denom", msg.Amount.Denom)},
)
}()
}
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeUnbond,
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress),
fix: fixed missing coin denomination in msg server logs (backport #9786) (#9844) * fix: fixed missing coin denomination in msg server logs (#9786) <!-- The default pull request template is for types feat, fix, or refactor. For other templates, add one of the following parameters to the url: - template=docs.md - template=other.md --> ## Description Closes: [#9785](https://github.com/cosmos/cosmos-sdk/issues/9785) This PR modifies _x/staking/keeper/msg_server.go_ file and fixes coin denomination issues in the emitted events. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [x] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [x] reviewed state machine logic - [x] reviewed API design and naming - [x] reviewed documentation is accurate - [x] reviewed tests and test coverage - [ ] manually tested (if applicable) (cherry picked from commit 18eea3a7c8dca75278a9c3ef9b7e1247ad9a357b) # Conflicts: # CHANGELOG.md * Fix conflicts * move to new section Co-authored-by: Slav Keremidchiev <skeremidch@gmail.com> Co-authored-by: Amaury M <1293565+amaurym@users.noreply.github.com>
2021-08-04 02:54:28 -07:00
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
),
})
return &types.MsgUndelegateResponse{
CompletionTime: completionTime,
}, nil
}