From 452a30ec73969bd13b7702184d91f755281a27de Mon Sep 17 00:00:00 2001 From: javiersuweijie Date: Tue, 28 Feb 2023 13:39:34 +0800 Subject: [PATCH 1/5] feat: pick vesting --- proto/cosmos/vesting/v1beta1/tx.proto | 19 +- scripts/protocgen.sh | 4 - simapp/app.go | 2 +- x/auth/migrations/v2/store_test.go | 8 +- x/auth/vesting/client/cli/tx.go | 56 ++- x/auth/vesting/handler.go | 39 +++ x/auth/vesting/handler_test.go | 188 +++++++++++ x/auth/vesting/module.go | 26 +- x/auth/vesting/msg_server.go | 70 +++- x/auth/vesting/types/codec.go | 13 +- x/auth/vesting/types/expected_keepers.go | 13 + x/auth/vesting/types/msgs.go | 49 ++- x/auth/vesting/types/tx.pb.go | 337 ++++++++++++++++++- x/auth/vesting/types/vesting_account.go | 5 + x/auth/vesting/types/vesting_account_test.go | 12 +- 15 files changed, 797 insertions(+), 44 deletions(-) create mode 100644 x/auth/vesting/handler.go create mode 100644 x/auth/vesting/handler_test.go diff --git a/proto/cosmos/vesting/v1beta1/tx.proto b/proto/cosmos/vesting/v1beta1/tx.proto index 42d3213f7b26..e8a5e6f6c2f1 100644 --- a/proto/cosmos/vesting/v1beta1/tx.proto +++ b/proto/cosmos/vesting/v1beta1/tx.proto @@ -27,6 +27,9 @@ service Msg { // // Since: cosmos-sdk 0.46 rpc CreatePeriodicVestingAccount(MsgCreatePeriodicVestingAccount) returns (MsgCreatePeriodicVestingAccountResponse); + // DonateAllVestingTokens defines a method that enables donating all vesting + // tokens to community pool + rpc DonateAllVestingTokens(MsgDonateAllVestingTokens) returns (MsgDonateAllVestingTokensResponse); } // MsgCreateVestingAccount defines a message that enables creating a vesting @@ -76,7 +79,7 @@ message MsgCreatePermanentLockedAccount { // Since: cosmos-sdk 0.46 message MsgCreatePermanentLockedAccountResponse {} -// MsgCreateVestingAccount defines a message that enables creating a vesting +// MsgCreatePeriodicVestingAccount defines a message that enables creating a vesting // account. // // Since: cosmos-sdk 0.46 @@ -93,8 +96,20 @@ message MsgCreatePeriodicVestingAccount { repeated Period vesting_periods = 4 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; } -// MsgCreateVestingAccountResponse defines the Msg/CreatePeriodicVestingAccount +// MsgCreatePeriodicVestingAccountResponse defines the Msg/CreatePeriodicVestingAccount // response type. // // Since: cosmos-sdk 0.46 message MsgCreatePeriodicVestingAccountResponse {} + +// MsgDonateAllVestingTokens defines a message that enables donating all vesting +// token to community pool. +message MsgDonateAllVestingTokens { + option (gogoproto.equal) = false; + + string from_address = 1 [(gogoproto.moretags) = "yaml:\"from_address\""]; +} + +// MsgDonateAllVestingTokensResponse defines the Msg/MsgDonateAllVestingTokens +// response type. +message MsgDonateAllVestingTokensResponse {} diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index ca667a44a04e..be064af94797 100755 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -31,7 +31,3 @@ cd .. # move proto files to the right places cp -r github.com/cosmos/cosmos-sdk/* ./ rm -rf github.com - -go mod tidy - -./scripts/protocgen-pulsar.sh diff --git a/simapp/app.go b/simapp/app.go index 078bda019336..60e1e90ee32d 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -400,7 +400,7 @@ func NewSimApp( encodingConfig.TxConfig, ), auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), - vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), + vesting.NewAppModule(app.AccountKeeper, app.BankKeeper, app.DistrKeeper, app.StakingKeeper), bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), capability.NewAppModule(appCodec, *app.CapabilityKeeper, false), crisis.NewAppModule(app.CrisisKeeper, skipGenesisInvariants, app.GetSubspace(crisistypes.ModuleName)), diff --git a/x/auth/migrations/v2/store_test.go b/x/auth/migrations/v2/store_test.go index fd6e80b37108..9924bdb37ea2 100644 --- a/x/auth/migrations/v2/store_test.go +++ b/x/auth/migrations/v2/store_test.go @@ -384,7 +384,7 @@ func TestMigrateVestingAccounts(t *testing.T) { 3666666670000, 3666666670000, 0, - 1601042400 + 1, + 1601042400, }, { "periodic vesting account, all has vested", @@ -430,7 +430,7 @@ func TestMigrateVestingAccounts(t *testing.T) { 3666666670000, 0, 3666666670000, - 1601042400 + 31536000 + 15897600 + 15897600 + 1, + 1601042400 + 31536000 + 15897600 + 15897600, }, { "periodic vesting account, first period has vested", @@ -476,7 +476,7 @@ func TestMigrateVestingAccounts(t *testing.T) { 3666666670000, 3666666670000 - 1833333335000, 1833333335000, - 1601042400 + 31536000 + 1, + 1601042400 + 31536000, }, { "periodic vesting account, first 2 period has vested", @@ -522,7 +522,7 @@ func TestMigrateVestingAccounts(t *testing.T) { 3666666670000, 3666666670000 - 1833333335000 - 916666667500, 1833333335000 + 916666667500, - 1601042400 + 31536000 + 15638400 + 1, + 1601042400 + 31536000 + 15638400, }, { "vesting account has unbonding delegations in place", diff --git a/x/auth/vesting/client/cli/tx.go b/x/auth/vesting/client/cli/tx.go index f2ef3ba67fe7..03586792f714 100644 --- a/x/auth/vesting/client/cli/tx.go +++ b/x/auth/vesting/client/cli/tx.go @@ -34,6 +34,7 @@ func GetTxCmd() *cobra.Command { NewMsgCreateVestingAccountCmd(), NewMsgCreatePermanentLockedAccountCmd(), NewMsgCreatePeriodicVestingAccountCmd(), + NewMsgDonateAllVestingTokensCmd(), ) return txCmd @@ -141,19 +142,20 @@ func NewMsgCreatePeriodicVestingAccountCmd() *cobra.Command { Where periods.json contains: An array of coin strings and unix epoch times for coins to vest -{ "start_time": 1625204910, -"periods":[ - { - "coins": "10test", - "length_seconds":2592000 //30 days - }, - { - "coins": "10test", - "length_seconds":2592000 //30 days - }, -] - } - `, +{ + "start_time": 1625204910, + "periods":[ + { + "coins": "10test", + "length_seconds":2592000 + }, + { + "coins": "10test", + "length_seconds":2592000 //30 days + } + ] +} +`, Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) @@ -207,3 +209,31 @@ func NewMsgCreatePeriodicVestingAccountCmd() *cobra.Command { return cmd } + +// NewMsgDonateAllVestingTokensCmd returns a CLI command handler for creating a +// MsgDonateAllVestingTokens transaction. +func NewMsgDonateAllVestingTokensCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "donate-all-vesting-tokens", + Short: "Donate all vesting tokens of a vesting account to community pool.", + Long: `Donate all vesting tokens of a vesting account to community pool. + The account must not have any delegated vesting tokens to prevent complex + vesting logic changes. After donation, the account will be changed to normal + "BaseAccount".`, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + msg := types.NewMsgDonateAllVestingTokens(clientCtx.GetFromAddress()) + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/auth/vesting/handler.go b/x/auth/vesting/handler.go new file mode 100644 index 000000000000..cd48e43c9d9d --- /dev/null +++ b/x/auth/vesting/handler.go @@ -0,0 +1,39 @@ +package vesting + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" +) + +// NewHandler returns a handler for x/auth message types. +func NewHandler( + ak keeper.AccountKeeper, + bk types.BankKeeper, + dk types.DistrKeeper, + sk types.StakingKeeper, +) sdk.Handler { + msgServer := NewMsgServerImpl(ak, bk, dk, sk) + + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + + switch msg := msg.(type) { + case *types.MsgCreateVestingAccount: + res, err := msgServer.CreateVestingAccount(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *types.MsgCreatePeriodicVestingAccount: + res, err := msgServer.CreatePeriodicVestingAccount(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *types.MsgDonateAllVestingTokens: + res, err := msgServer.DonateAllVestingTokens(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) + } + } +} diff --git a/x/auth/vesting/handler_test.go b/x/auth/vesting/handler_test.go new file mode 100644 index 000000000000..0ce86120aac3 --- /dev/null +++ b/x/auth/vesting/handler_test.go @@ -0,0 +1,188 @@ +package vesting_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "cosmossdk.io/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/cosmos/cosmos-sdk/x/auth/vesting" + "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" +) + +type HandlerTestSuite struct { + suite.Suite + + handler sdk.Handler + app *simapp.SimApp +} + +func (suite *HandlerTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(suite.T(), checkTx) + + suite.handler = vesting.NewHandler( + app.AccountKeeper, + app.BankKeeper, + app.DistrKeeper, + app.StakingKeeper, + ) + suite.app = app +} + +func (suite *HandlerTestSuite) TestMsgCreateVestingAccount() { + ctx := suite.app.BaseApp.NewContext(false, tmproto.Header{Height: suite.app.LastBlockHeight() + 1}) + + balances := sdk.NewCoins(sdk.NewInt64Coin("test", 1000)) + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + addr3 := sdk.AccAddress([]byte("addr3_______________")) + + acc1 := suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + suite.app.AccountKeeper.SetAccount(ctx, acc1) + suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr1, balances)) + + testCases := []struct { + name string + msg *types.MsgCreateVestingAccount + expectErr bool + }{ + { + name: "create delayed vesting account", + msg: types.NewMsgCreateVestingAccount(addr1, addr2, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, true), + expectErr: false, + }, + { + name: "create continuous vesting account", + msg: types.NewMsgCreateVestingAccount(addr1, addr3, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, false), + expectErr: false, + }, + { + name: "continuous vesting account already exists", + msg: types.NewMsgCreateVestingAccount(addr1, addr3, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, false), + expectErr: true, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + res, err := suite.handler(ctx, tc.msg) + if tc.expectErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().NotNil(res) + + toAddr, err := sdk.AccAddressFromBech32(tc.msg.ToAddress) + suite.Require().NoError(err) + accI := suite.app.AccountKeeper.GetAccount(ctx, toAddr) + suite.Require().NotNil(accI) + + if tc.msg.Delayed { + acc, ok := accI.(*types.DelayedVestingAccount) + suite.Require().True(ok) + suite.Require().Equal(tc.msg.Amount, acc.GetVestingCoins(ctx.BlockTime())) + } else { + acc, ok := accI.(*types.ContinuousVestingAccount) + suite.Require().True(ok) + suite.Require().Equal(tc.msg.Amount, acc.GetVestingCoins(ctx.BlockTime())) + } + } + }) + } +} + +func (suite *HandlerTestSuite) TestMsgDonateVestingToken() { + ctx := suite.app.BaseApp.NewContext(false, tmproto.Header{Height: suite.app.LastBlockHeight() + 1}) + + prevCommunityFund := suite.app.DistrKeeper.GetFeePool(ctx).CommunityPool + + balances := sdk.NewCoins(sdk.NewInt64Coin("test", 1000)) + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + addr3 := sdk.AccAddress([]byte("addr3_______________")) + + valAddr := sdk.ValAddress([]byte("validator___________")) + + acc1 := suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + suite.app.AccountKeeper.SetAccount(ctx, acc1) + suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr1, balances)) + + acc2 := types.NewPermanentLockedAccount( + suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr2).(*authtypes.BaseAccount), balances, + ) + acc2.DelegatedVesting = balances + suite.app.AccountKeeper.SetAccount(ctx, acc2) + suite.app.StakingKeeper.SetDelegation(ctx, stakingtypes.Delegation{ + DelegatorAddress: addr2.String(), + ValidatorAddress: valAddr.String(), + Shares: sdk.OneDec(), + }) + suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr2, balances)) + + acc3 := types.NewPermanentLockedAccount( + suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr3).(*authtypes.BaseAccount), balances, + ) + suite.app.AccountKeeper.SetAccount(ctx, acc3) + suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr3, balances)) + + testCases := []struct { + name string + msg *types.MsgDonateAllVestingTokens + expectErr bool + }{ + { + name: "donate from normal account", + msg: types.NewMsgDonateAllVestingTokens(addr1), + expectErr: true, + }, + { + name: "donate from vesting account with delegated vesting", + msg: types.NewMsgDonateAllVestingTokens(addr2), + expectErr: true, + }, + { + name: "donate from vesting account", + msg: types.NewMsgDonateAllVestingTokens(addr3), + expectErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + res, err := suite.handler(ctx, tc.msg) + if tc.expectErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().NotNil(res) + + feePool := suite.app.DistrKeeper.GetFeePool(ctx).CommunityPool.Sub(prevCommunityFund) + communityFund, _ := feePool.TruncateDecimal() + suite.Require().Equal(balances, communityFund) + + fromAddr, err := sdk.AccAddressFromBech32(tc.msg.FromAddress) + suite.Require().NoError(err) + accI := suite.app.AccountKeeper.GetAccount(ctx, fromAddr) + suite.Require().NotNil(accI) + _, ok := accI.(*authtypes.BaseAccount) + suite.Require().True(ok) + balance := suite.app.BankKeeper.GetAllBalances(ctx, fromAddr) + suite.Require().Empty(balance) + } + }) + } +} + +func TestHandlerTestSuite(t *testing.T) { + suite.Run(t, new(HandlerTestSuite)) +} diff --git a/x/auth/vesting/module.go b/x/auth/vesting/module.go index 985b51bb6251..381c300e6d2e 100644 --- a/x/auth/vesting/module.go +++ b/x/auth/vesting/module.go @@ -19,6 +19,7 @@ import ( "cosmossdk.io/core/appmodule" "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/vesting/client/cli" "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" ) @@ -80,13 +81,22 @@ type AppModule struct { accountKeeper keeper.AccountKeeper bankKeeper types.BankKeeper + distrKeeper types.DistrKeeper + stakingKeeper types.StakingKeeper } -func NewAppModule(ak keeper.AccountKeeper, bk types.BankKeeper) AppModule { +func NewAppModule( + ak keeper.AccountKeeper, + bk types.BankKeeper, + dk types.DistrKeeper, + sk types.StakingKeeper, +) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, accountKeeper: ak, bankKeeper: bk, + distrKeeper: dk, + stakingKeeper: sk, } } @@ -103,7 +113,15 @@ func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // RegisterServices registers module services. func (am AppModule) RegisterServices(cfg module.Configurator) { - types.RegisterMsgServer(cfg.MsgServer(), NewMsgServerImpl(am.accountKeeper, am.bankKeeper)) + types.RegisterMsgServer( + cfg.MsgServer(), + NewMsgServerImpl( + am.accountKeeper, + am.bankKeeper, + am.distrKeeper, + am.stakingKeeper, + ), + ) } // InitGenesis performs a no-op. @@ -134,6 +152,8 @@ type VestingInputs struct { AccountKeeper keeper.AccountKeeper BankKeeper types.BankKeeper + DistrKeeper types.DistrKeeper + StakingKeeper types.StakingKeeper } type VestingOutputs struct { @@ -143,7 +163,7 @@ type VestingOutputs struct { } func ProvideModule(in VestingInputs) VestingOutputs { - m := NewAppModule(in.AccountKeeper, in.BankKeeper) + m := NewAppModule(in.AccountKeeper, in.BankKeeper, in.DistrKeeper, in.StakingKeeper) return VestingOutputs{Module: m} } diff --git a/x/auth/vesting/msg_server.go b/x/auth/vesting/msg_server.go index 33dd56861cb1..3704cc76428c 100644 --- a/x/auth/vesting/msg_server.go +++ b/x/auth/vesting/msg_server.go @@ -10,18 +10,26 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported" "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" ) type msgServer struct { keeper.AccountKeeper types.BankKeeper + types.DistrKeeper + types.StakingKeeper } // NewMsgServerImpl returns an implementation of the vesting MsgServer interface, // wrapping the corresponding AccountKeeper and BankKeeper. -func NewMsgServerImpl(k keeper.AccountKeeper, bk types.BankKeeper) types.MsgServer { - return &msgServer{AccountKeeper: k, BankKeeper: bk} +func NewMsgServerImpl( + k keeper.AccountKeeper, + bk types.BankKeeper, + dk types.DistrKeeper, + sk types.StakingKeeper, +) types.MsgServer { + return &msgServer{AccountKeeper: k, BankKeeper: bk, DistrKeeper: dk, StakingKeeper: sk} } var _ types.MsgServer = msgServer{} @@ -193,3 +201,61 @@ func (s msgServer) CreatePeriodicVestingAccount(goCtx context.Context, msg *type return &types.MsgCreatePeriodicVestingAccountResponse{}, nil } + +func (s msgServer) DonateAllVestingTokens(goCtx context.Context, msg *types.MsgDonateAllVestingTokens) (*types.MsgDonateAllVestingTokensResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + ak := s.AccountKeeper + dk := s.DistrKeeper + sk := s.StakingKeeper + + from, err := sdk.AccAddressFromBech32(msg.FromAddress) + if err != nil { + return nil, err + } + + acc := ak.GetAccount(ctx, from) + if acc == nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s not exists", msg.FromAddress) + } + + // check whether an account has any type of staking entry + if len(sk.GetDelegatorDelegations(ctx, acc.GetAddress(), 1)) != 0 || + len(sk.GetUnbondingDelegations(ctx, acc.GetAddress(), 1)) != 0 || + len(sk.GetRedelegations(ctx, acc.GetAddress(), 1)) != 0 { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s has staking entry", msg.FromAddress) + } + + vestingAcc, ok := acc.(exported.VestingAccount) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s is not a vesting account", msg.FromAddress) + } + + vestingCoins := vestingAcc.GetVestingCoins(ctx.BlockTime()) + if vestingCoins.IsZero() { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s has no vesting tokens", msg.FromAddress) + } + + // Change the account as normal account + ak.SetAccount(ctx, + authtypes.NewBaseAccount( + acc.GetAddress(), + acc.GetPubKey(), + acc.GetAccountNumber(), + acc.GetSequence(), + ), + ) + + if err := dk.FundCommunityPool(ctx, vestingCoins, from); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + ) + + return &types.MsgDonateAllVestingTokensResponse{}, nil +} diff --git a/x/auth/vesting/types/codec.go b/x/auth/vesting/types/codec.go index 2dd2c5a8196f..8701e629d677 100644 --- a/x/auth/vesting/types/codec.go +++ b/x/auth/vesting/types/codec.go @@ -2,7 +2,6 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/codec/legacy" "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -23,9 +22,12 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&DelayedVestingAccount{}, "cosmos-sdk/DelayedVestingAccount", nil) cdc.RegisterConcrete(&PeriodicVestingAccount{}, "cosmos-sdk/PeriodicVestingAccount", nil) cdc.RegisterConcrete(&PermanentLockedAccount{}, "cosmos-sdk/PermanentLockedAccount", nil) - legacy.RegisterAminoMsg(cdc, &MsgCreateVestingAccount{}, "cosmos-sdk/MsgCreateVestingAccount") - legacy.RegisterAminoMsg(cdc, &MsgCreatePermanentLockedAccount{}, "cosmos-sdk/MsgCreatePermLockedAccount") - legacy.RegisterAminoMsg(cdc, &MsgCreatePeriodicVestingAccount{}, "cosmos-sdk/MsgCreatePeriodVestAccount") + cdc.RegisterConcrete(&MsgCreatePermanentLockedAccount{}, "cosmos-sdk/MsgCreatePermLockedAccount", nil) + + // msg registration + cdc.RegisterConcrete(&MsgCreateVestingAccount{}, "cosmos-sdk/MsgCreateVestingAccount", nil) + cdc.RegisterConcrete(&MsgCreatePeriodicVestingAccount{}, "cosmos-sdk/MsgCreatePeriodicVestingAccount", nil) + cdc.RegisterConcrete(&MsgDonateAllVestingTokens{}, "cosmos-sdk/MsgDonateAllVestingTokens", nil) } // RegisterInterface associates protoName with AccountI and VestingAccount @@ -61,7 +63,8 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { registry.RegisterImplementations( (*sdk.Msg)(nil), &MsgCreateVestingAccount{}, - &MsgCreatePermanentLockedAccount{}, + &MsgCreatePeriodicVestingAccount{}, + &MsgDonateAllVestingTokens{}, ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) diff --git a/x/auth/vesting/types/expected_keepers.go b/x/auth/vesting/types/expected_keepers.go index 5705eea30baf..a799cd5e7387 100644 --- a/x/auth/vesting/types/expected_keepers.go +++ b/x/auth/vesting/types/expected_keepers.go @@ -2,6 +2,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // BankKeeper defines the expected interface contract the vesting module requires @@ -11,3 +12,15 @@ type BankKeeper interface { SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error BlockedAddr(addr sdk.AccAddress) bool } + +// DistrKeeper defines the expected interface for distribution keeper +type DistrKeeper interface { + FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error +} + +// StakingKeeper defines the exported interface for staking keeper +type StakingKeeper interface { + GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (delegations []stakingtypes.Delegation) + GetUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (unbondingDelegations []stakingtypes.UnbondingDelegation) + GetRedelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (redelegations []stakingtypes.Redelegation) +} diff --git a/x/auth/vesting/types/msgs.go b/x/auth/vesting/types/msgs.go index f2194af5690b..df97106a6608 100644 --- a/x/auth/vesting/types/msgs.go +++ b/x/auth/vesting/types/msgs.go @@ -16,15 +16,18 @@ const TypeMsgCreatePermanentLockedAccount = "msg_create_permanent_locked_account // TypeMsgCreatePeriodicVestingAccount defines the type value for a MsgCreateVestingAccount. const TypeMsgCreatePeriodicVestingAccount = "msg_create_periodic_vesting_account" +// TypeMsgDonateAllVestingTokens defines the type value for a MsgDonateAllVestingTokens. +const TypeMsgDonateAllVestingTokens = "msg_donate_all_vesting_tokens" + var _ sdk.Msg = &MsgCreateVestingAccount{} var _ sdk.Msg = &MsgCreatePermanentLockedAccount{} var _ sdk.Msg = &MsgCreatePeriodicVestingAccount{} +var _ sdk.Msg = &MsgDonateAllVestingTokens{} + // NewMsgCreateVestingAccount returns a reference to a new MsgCreateVestingAccount. -// -//nolint:interfacer func NewMsgCreateVestingAccount(fromAddr, toAddr sdk.AccAddress, amount sdk.Coins, endTime int64, delayed bool) *MsgCreateVestingAccount { return &MsgCreateVestingAccount{ FromAddress: fromAddr.String(), @@ -197,3 +200,45 @@ func (msg MsgCreatePeriodicVestingAccount) ValidateBasic() error { return nil } + +// NewMsgDonateAllVestingTokens returns a reference to a new MsgDonateAllVestingTokens. +func NewMsgDonateAllVestingTokens(fromAddr sdk.AccAddress) *MsgDonateAllVestingTokens { + return &MsgDonateAllVestingTokens{ + FromAddress: fromAddr.String(), + } +} + +// Route returns the message route for a MsgDonateAllVestingTokens. +func (msg MsgDonateAllVestingTokens) Route() string { return RouterKey } + +// Type returns the message type for a MsgDonateAllVestingTokens. +func (msg MsgDonateAllVestingTokens) Type() string { return TypeMsgDonateAllVestingTokens } + +// ValidateBasic Implements Msg. +func (msg MsgDonateAllVestingTokens) ValidateBasic() error { + from, err := sdk.AccAddressFromBech32(msg.FromAddress) + if err != nil { + return err + } + + if err := sdk.VerifyAddressFormat(from); err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid sender address: %s", err) + } + + return nil +} + +// GetSignBytes returns the bytes all expected signers must sign over for a +// MsgDonateAllVestingTokens. +func (msg MsgDonateAllVestingTokens) GetSignBytes() []byte { + return sdk.MustSortJSON(amino.MustMarshalJSON(&msg)) +} + +// GetSigners returns the expected signers for a MsgDonateAllVestingTokens. +func (msg MsgDonateAllVestingTokens) GetSigners() []sdk.AccAddress { + from, err := sdk.AccAddressFromBech32(msg.FromAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{from} +} diff --git a/x/auth/vesting/types/tx.pb.go b/x/auth/vesting/types/tx.pb.go index 463804f25cfd..a89deed95273 100644 --- a/x/auth/vesting/types/tx.pb.go +++ b/x/auth/vesting/types/tx.pb.go @@ -254,7 +254,7 @@ func (m *MsgCreatePermanentLockedAccountResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCreatePermanentLockedAccountResponse proto.InternalMessageInfo -// MsgCreateVestingAccount defines a message that enables creating a vesting +// MsgCreatePeriodicVestingAccount defines a message that enables creating a vesting // account. // // Since: cosmos-sdk 0.46 @@ -327,7 +327,7 @@ func (m *MsgCreatePeriodicVestingAccount) GetVestingPeriods() []Period { return nil } -// MsgCreateVestingAccountResponse defines the Msg/CreatePeriodicVestingAccount +// MsgCreatePeriodicVestingAccountResponse defines the Msg/CreatePeriodicVestingAccount // response type. // // Since: cosmos-sdk 0.46 @@ -369,6 +369,90 @@ func (m *MsgCreatePeriodicVestingAccountResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCreatePeriodicVestingAccountResponse proto.InternalMessageInfo +// MsgDonateAllVestingTokens defines a message that enables donating all vesting +// token to community pool. +type MsgDonateAllVestingTokens struct { + FromAddress string `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty" yaml:"from_address"` +} + +func (m *MsgDonateAllVestingTokens) Reset() { *m = MsgDonateAllVestingTokens{} } +func (m *MsgDonateAllVestingTokens) String() string { return proto.CompactTextString(m) } +func (*MsgDonateAllVestingTokens) ProtoMessage() {} +func (*MsgDonateAllVestingTokens) Descriptor() ([]byte, []int) { + return fileDescriptor_5338ca97811f9792, []int{6} +} +func (m *MsgDonateAllVestingTokens) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDonateAllVestingTokens) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDonateAllVestingTokens.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDonateAllVestingTokens) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDonateAllVestingTokens.Merge(m, src) +} +func (m *MsgDonateAllVestingTokens) XXX_Size() int { + return m.Size() +} +func (m *MsgDonateAllVestingTokens) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDonateAllVestingTokens.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDonateAllVestingTokens proto.InternalMessageInfo + +func (m *MsgDonateAllVestingTokens) GetFromAddress() string { + if m != nil { + return m.FromAddress + } + return "" +} + +// MsgDonateAllVestingTokensResponse defines the Msg/MsgDonateAllVestingTokens +// response type. +type MsgDonateAllVestingTokensResponse struct { +} + +func (m *MsgDonateAllVestingTokensResponse) Reset() { *m = MsgDonateAllVestingTokensResponse{} } +func (m *MsgDonateAllVestingTokensResponse) String() string { return proto.CompactTextString(m) } +func (*MsgDonateAllVestingTokensResponse) ProtoMessage() {} +func (*MsgDonateAllVestingTokensResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5338ca97811f9792, []int{7} +} +func (m *MsgDonateAllVestingTokensResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDonateAllVestingTokensResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDonateAllVestingTokensResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDonateAllVestingTokensResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDonateAllVestingTokensResponse.Merge(m, src) +} +func (m *MsgDonateAllVestingTokensResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgDonateAllVestingTokensResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDonateAllVestingTokensResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDonateAllVestingTokensResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgCreateVestingAccount)(nil), "cosmos.vesting.v1beta1.MsgCreateVestingAccount") proto.RegisterType((*MsgCreateVestingAccountResponse)(nil), "cosmos.vesting.v1beta1.MsgCreateVestingAccountResponse") @@ -376,6 +460,8 @@ func init() { proto.RegisterType((*MsgCreatePermanentLockedAccountResponse)(nil), "cosmos.vesting.v1beta1.MsgCreatePermanentLockedAccountResponse") proto.RegisterType((*MsgCreatePeriodicVestingAccount)(nil), "cosmos.vesting.v1beta1.MsgCreatePeriodicVestingAccount") proto.RegisterType((*MsgCreatePeriodicVestingAccountResponse)(nil), "cosmos.vesting.v1beta1.MsgCreatePeriodicVestingAccountResponse") + proto.RegisterType((*MsgDonateAllVestingTokens)(nil), "cosmos.vesting.v1beta1.MsgDonateAllVestingTokens") + proto.RegisterType((*MsgDonateAllVestingTokensResponse)(nil), "cosmos.vesting.v1beta1.MsgDonateAllVestingTokensResponse") } func init() { proto.RegisterFile("cosmos/vesting/v1beta1/tx.proto", fileDescriptor_5338ca97811f9792) } @@ -528,6 +614,9 @@ type MsgClient interface { // // Since: cosmos-sdk 0.46 CreatePeriodicVestingAccount(ctx context.Context, in *MsgCreatePeriodicVestingAccount, opts ...grpc.CallOption) (*MsgCreatePeriodicVestingAccountResponse, error) + // DonateAllVestingTokens defines a method that enables donating all vesting + // tokens to community pool + DonateAllVestingTokens(ctx context.Context, in *MsgDonateAllVestingTokens, opts ...grpc.CallOption) (*MsgDonateAllVestingTokensResponse, error) } type msgClient struct { @@ -565,6 +654,15 @@ func (c *msgClient) CreatePeriodicVestingAccount(ctx context.Context, in *MsgCre return out, nil } +func (c *msgClient) DonateAllVestingTokens(ctx context.Context, in *MsgDonateAllVestingTokens, opts ...grpc.CallOption) (*MsgDonateAllVestingTokensResponse, error) { + out := new(MsgDonateAllVestingTokensResponse) + err := c.cc.Invoke(ctx, "/cosmos.vesting.v1beta1.Msg/DonateAllVestingTokens", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { // CreateVestingAccount defines a method that enables creating a vesting @@ -580,6 +678,9 @@ type MsgServer interface { // // Since: cosmos-sdk 0.46 CreatePeriodicVestingAccount(context.Context, *MsgCreatePeriodicVestingAccount) (*MsgCreatePeriodicVestingAccountResponse, error) + // DonateAllVestingTokens defines a method that enables donating all vesting + // tokens to community pool + DonateAllVestingTokens(context.Context, *MsgDonateAllVestingTokens) (*MsgDonateAllVestingTokensResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -595,6 +696,9 @@ func (*UnimplementedMsgServer) CreatePermanentLockedAccount(ctx context.Context, func (*UnimplementedMsgServer) CreatePeriodicVestingAccount(ctx context.Context, req *MsgCreatePeriodicVestingAccount) (*MsgCreatePeriodicVestingAccountResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CreatePeriodicVestingAccount not implemented") } +func (*UnimplementedMsgServer) DonateAllVestingTokens(ctx context.Context, req *MsgDonateAllVestingTokens) (*MsgDonateAllVestingTokensResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DonateAllVestingTokens not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -654,6 +758,24 @@ func _Msg_CreatePeriodicVestingAccount_Handler(srv interface{}, ctx context.Cont return interceptor(ctx, in, info, handler) } +func _Msg_DonateAllVestingTokens_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgDonateAllVestingTokens) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).DonateAllVestingTokens(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.vesting.v1beta1.Msg/DonateAllVestingTokens", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).DonateAllVestingTokens(ctx, req.(*MsgDonateAllVestingTokens)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "cosmos.vesting.v1beta1.Msg", HandlerType: (*MsgServer)(nil), @@ -670,6 +792,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "CreatePeriodicVestingAccount", Handler: _Msg_CreatePeriodicVestingAccount_Handler, }, + { + MethodName: "DonateAllVestingTokens", + Handler: _Msg_DonateAllVestingTokens_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "cosmos/vesting/v1beta1/tx.proto", @@ -917,6 +1043,59 @@ func (m *MsgCreatePeriodicVestingAccountResponse) MarshalToSizedBuffer(dAtA []by return len(dAtA) - i, nil } +func (m *MsgDonateAllVestingTokens) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDonateAllVestingTokens) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDonateAllVestingTokens) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FromAddress) > 0 { + i -= len(m.FromAddress) + copy(dAtA[i:], m.FromAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.FromAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgDonateAllVestingTokensResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDonateAllVestingTokensResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDonateAllVestingTokensResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -1033,6 +1212,28 @@ func (m *MsgCreatePeriodicVestingAccountResponse) Size() (n int) { return n } +func (m *MsgDonateAllVestingTokens) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FromAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgDonateAllVestingTokensResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1691,6 +1892,138 @@ func (m *MsgCreatePeriodicVestingAccountResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgDonateAllVestingTokens) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDonateAllVestingTokens: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDonateAllVestingTokens: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FromAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FromAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgDonateAllVestingTokensResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDonateAllVestingTokensResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDonateAllVestingTokensResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/auth/vesting/types/vesting_account.go b/x/auth/vesting/types/vesting_account.go index 892794f669cc..59d84cd7c94e 100644 --- a/x/auth/vesting/types/vesting_account.go +++ b/x/auth/vesting/types/vesting_account.go @@ -364,6 +364,11 @@ func (pva PeriodicVestingAccount) GetVestedCoins(blockTime time.Time) sdk.Coins for _, period := range pva.VestingPeriods { x := blockTime.Unix() - currentPeriodStartTime if x < period.Length { + coins, _ := sdk.NewDecCoinsFromCoins(period.Amount...). + MulDec(sdk.NewDec(x).QuoInt64(period.Length)). + TruncateDecimal() + vestedCoins = vestedCoins.Add(coins...) + break } diff --git a/x/auth/vesting/types/vesting_account_test.go b/x/auth/vesting/types/vesting_account_test.go index 8d8a2f6a3d33..c7e225f32816 100644 --- a/x/auth/vesting/types/vesting_account_test.go +++ b/x/auth/vesting/types/vesting_account_test.go @@ -369,17 +369,17 @@ func TestGetVestedCoinsPeriodicVestingAcc(t *testing.T) { vestedCoins = pva.GetVestedCoins(endTime) require.Equal(t, origCoins, vestedCoins) - // require no coins vested during first vesting period + // require 50% of first period coins vested during first vesting period vestedCoins = pva.GetVestedCoins(now.Add(6 * time.Hour)) - require.Nil(t, vestedCoins) + require.Equal(t, vestedCoins, sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}) // require 50% of coins vested after period 1 vestedCoins = pva.GetVestedCoins(now.Add(12 * time.Hour)) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestedCoins) - // require period 2 coins don't vest until period is over + // require 50% of period 2 coins vested until 15 hours vestedCoins = pva.GetVestedCoins(now.Add(15 * time.Hour)) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestedCoins) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 625), sdk.NewInt64Coin(stakeDenom, 62)}, vestedCoins) // require 75% of coins vested after period 2 vestedCoins = pva.GetVestedCoins(now.Add(18 * time.Hour)) @@ -417,9 +417,9 @@ func TestGetVestingCoinsPeriodicVestingAcc(t *testing.T) { vestingCoins = pva.GetVestingCoins(now.Add(12 * time.Hour)) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestingCoins) - // require 50% of coins vesting after period 1, but before period 2 completes. + // require 37.5% of coins vesting after period 1, but before period 2 completes. vestingCoins = pva.GetVestingCoins(now.Add(15 * time.Hour)) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestingCoins) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 375), sdk.NewInt64Coin(stakeDenom, 38)}, vestingCoins) // require 25% of coins vesting after period 2 vestingCoins = pva.GetVestingCoins(now.Add(18 * time.Hour)) From ab5b0bb82f0907fcde9652fc5342a16c4f5a1da8 Mon Sep 17 00:00:00 2001 From: Javier Su Date: Tue, 28 Feb 2023 17:03:15 +0800 Subject: [PATCH 2/5] Update x/auth/vesting/msg_server.go Co-authored-by: emidev98 <49301655+emidev98@users.noreply.github.com> --- x/auth/vesting/msg_server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/auth/vesting/msg_server.go b/x/auth/vesting/msg_server.go index 3704cc76428c..2f6f3f7d3b52 100644 --- a/x/auth/vesting/msg_server.go +++ b/x/auth/vesting/msg_server.go @@ -254,6 +254,7 @@ func (s msgServer) DonateAllVestingTokens(goCtx context.Context, msg *types.MsgD sdk.NewEvent( sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAmount, vestingCoins.String()), ), ) From b854247260b5dcec744a097ebca431ed3ae8bf6c Mon Sep 17 00:00:00 2001 From: javiersuweijie Date: Thu, 16 Mar 2023 21:01:04 +0800 Subject: [PATCH 3/5] temp disable dependencies review --- .../{dependencies-review.yml => dependencies-review.yml.disable} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{dependencies-review.yml => dependencies-review.yml.disable} (100%) diff --git a/.github/workflows/dependencies-review.yml b/.github/workflows/dependencies-review.yml.disable similarity index 100% rename from .github/workflows/dependencies-review.yml rename to .github/workflows/dependencies-review.yml.disable From 307b519b3ac7951fcf792cd5063375333dbb5ccc Mon Sep 17 00:00:00 2001 From: javiersuweijie Date: Thu, 22 Jun 2023 11:48:22 +0800 Subject: [PATCH 4/5] fix: donate all vesting token to work with dust delegations --- x/auth/vesting/handler_test.go | 38 ++++++++++++++++++++++++ x/auth/vesting/msg_server.go | 32 ++++++++++++++++++-- x/auth/vesting/types/expected_keepers.go | 4 +++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/x/auth/vesting/handler_test.go b/x/auth/vesting/handler_test.go index 0ce86120aac3..b81acb017874 100644 --- a/x/auth/vesting/handler_test.go +++ b/x/auth/vesting/handler_test.go @@ -1,7 +1,9 @@ package vesting_test import ( + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "testing" + "time" "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -108,8 +110,22 @@ func (suite *HandlerTestSuite) TestMsgDonateVestingToken() { addr1 := sdk.AccAddress([]byte("addr1_______________")) addr2 := sdk.AccAddress([]byte("addr2_______________")) addr3 := sdk.AccAddress([]byte("addr3_______________")) + addr4 := sdk.AccAddress([]byte("addr4_______________")) valAddr := sdk.ValAddress([]byte("validator___________")) + suite.app.StakingKeeper.SetValidator(ctx, stakingtypes.Validator{ + OperatorAddress: valAddr.String(), + ConsensusPubkey: nil, + Jailed: false, + Status: 0, + Tokens: sdk.NewInt(2), + DelegatorShares: sdk.MustNewDecFromStr("1.1"), + Description: stakingtypes.Description{}, + UnbondingHeight: 0, + UnbondingTime: time.Time{}, + Commission: stakingtypes.Commission{}, + MinSelfDelegation: sdk.NewInt(1), + }) acc1 := suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr1) suite.app.AccountKeeper.SetAccount(ctx, acc1) @@ -133,6 +149,18 @@ func (suite *HandlerTestSuite) TestMsgDonateVestingToken() { suite.app.AccountKeeper.SetAccount(ctx, acc3) suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr3, balances)) + acc4 := types.NewPermanentLockedAccount( + suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr4).(*authtypes.BaseAccount), balances, + ) + acc4.DelegatedVesting = balances + suite.app.AccountKeeper.SetAccount(ctx, acc4) + suite.app.StakingKeeper.SetDelegation(ctx, stakingtypes.Delegation{ + DelegatorAddress: addr4.String(), + ValidatorAddress: valAddr.String(), + Shares: sdk.MustNewDecFromStr("0.1"), + }) + suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr4, balances)) + testCases := []struct { name string msg *types.MsgDonateAllVestingTokens @@ -153,12 +181,19 @@ func (suite *HandlerTestSuite) TestMsgDonateVestingToken() { msg: types.NewMsgDonateAllVestingTokens(addr3), expectErr: false, }, + { + name: "donate from vesting account with dust delegation", + msg: types.NewMsgDonateAllVestingTokens(addr4), + expectErr: false, + }, } for _, tc := range testCases { tc := tc suite.Run(tc.name, func() { + // Rollback context after every test case + ctx, _ := ctx.CacheContext() res, err := suite.handler(ctx, tc.msg) if tc.expectErr { suite.Require().Error(err) @@ -178,6 +213,9 @@ func (suite *HandlerTestSuite) TestMsgDonateVestingToken() { suite.Require().True(ok) balance := suite.app.BankKeeper.GetAllBalances(ctx, fromAddr) suite.Require().Empty(balance) + + _, broken := stakingkeeper.DelegatorSharesInvariant(suite.app.StakingKeeper)(ctx) + suite.Require().False(broken) } }) } diff --git a/x/auth/vesting/msg_server.go b/x/auth/vesting/msg_server.go index 2f6f3f7d3b52..8dfb87da2efe 100644 --- a/x/auth/vesting/msg_server.go +++ b/x/auth/vesting/msg_server.go @@ -2,6 +2,8 @@ package vesting import ( "context" + "fmt" + "math" "github.com/armon/go-metrics" @@ -219,9 +221,33 @@ func (s msgServer) DonateAllVestingTokens(goCtx context.Context, msg *types.MsgD return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s not exists", msg.FromAddress) } - // check whether an account has any type of staking entry - if len(sk.GetDelegatorDelegations(ctx, acc.GetAddress(), 1)) != 0 || - len(sk.GetUnbondingDelegations(ctx, acc.GetAddress(), 1)) != 0 || + // get all delegations of an account and undust those that have less than 1 uluna + delegations := sk.GetDelegatorDelegations(ctx, acc.GetAddress(), math.MaxUint16) + for _, delegation := range delegations { + validatorAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress) + if err != nil { + return nil, err + } + validator, found := sk.GetValidator(ctx, validatorAddr) + if !found { + return nil, fmt.Errorf("validator not found") + } + // Try to delete the dust delegation + _, removedTokens := sk.RemoveValidatorTokensAndShares(ctx, validator, delegation.Shares) + // If the delegation is not dust, return an error and stop the donation flow + if !removedTokens.IsZero() { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s has a non-zero staking entry", msg.FromAddress) + } + + // Remove the dust delegation shares from the validator + err = sk.RemoveDelegation(ctx, delegation) + if err != nil { + return nil, err + } + } + + // check whether an account has any other type of staking entries + if len(sk.GetUnbondingDelegations(ctx, acc.GetAddress(), 1)) != 0 || len(sk.GetRedelegations(ctx, acc.GetAddress(), 1)) != 0 { return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s has staking entry", msg.FromAddress) } diff --git a/x/auth/vesting/types/expected_keepers.go b/x/auth/vesting/types/expected_keepers.go index a799cd5e7387..2e6d39089ac8 100644 --- a/x/auth/vesting/types/expected_keepers.go +++ b/x/auth/vesting/types/expected_keepers.go @@ -1,6 +1,7 @@ package types import ( + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -23,4 +24,7 @@ type StakingKeeper interface { GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (delegations []stakingtypes.Delegation) GetUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (unbondingDelegations []stakingtypes.UnbondingDelegation) GetRedelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (redelegations []stakingtypes.Redelegation) + RemoveValidatorTokensAndShares(ctx sdk.Context, validator stakingtypes.Validator, sharesToRemove sdk.Dec) (valOut stakingtypes.Validator, removedTokens math.Int) + GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, found bool) + RemoveDelegation(ctx sdk.Context, delegation stakingtypes.Delegation) error } From 76f624d932560d91ffd1778fe2445e38846eb120 Mon Sep 17 00:00:00 2001 From: emidev98 Date: Wed, 16 Aug 2023 15:16:09 +0300 Subject: [PATCH 5/5] fix: lint, test and build --- x/auth/vesting/handler_test.go | 226 ---------------- x/auth/vesting/module.go | 4 +- x/auth/vesting/msg_server.go | 8 - x/auth/vesting/msg_server_test.go | 6 +- .../testutil/expected_keepers_mocks.go | 241 +++++++++++++++++- x/auth/vesting/types/expected_keepers.go | 7 + x/auth/vesting/types/msgs.go | 2 +- 7 files changed, 253 insertions(+), 241 deletions(-) delete mode 100644 x/auth/vesting/handler_test.go diff --git a/x/auth/vesting/handler_test.go b/x/auth/vesting/handler_test.go deleted file mode 100644 index b81acb017874..000000000000 --- a/x/auth/vesting/handler_test.go +++ /dev/null @@ -1,226 +0,0 @@ -package vesting_test - -import ( - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "testing" - "time" - - "github.com/stretchr/testify/suite" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - "cosmossdk.io/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - "github.com/cosmos/cosmos-sdk/x/auth/vesting" - "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" -) - -type HandlerTestSuite struct { - suite.Suite - - handler sdk.Handler - app *simapp.SimApp -} - -func (suite *HandlerTestSuite) SetupTest() { - checkTx := false - app := simapp.Setup(suite.T(), checkTx) - - suite.handler = vesting.NewHandler( - app.AccountKeeper, - app.BankKeeper, - app.DistrKeeper, - app.StakingKeeper, - ) - suite.app = app -} - -func (suite *HandlerTestSuite) TestMsgCreateVestingAccount() { - ctx := suite.app.BaseApp.NewContext(false, tmproto.Header{Height: suite.app.LastBlockHeight() + 1}) - - balances := sdk.NewCoins(sdk.NewInt64Coin("test", 1000)) - addr1 := sdk.AccAddress([]byte("addr1_______________")) - addr2 := sdk.AccAddress([]byte("addr2_______________")) - addr3 := sdk.AccAddress([]byte("addr3_______________")) - - acc1 := suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - suite.app.AccountKeeper.SetAccount(ctx, acc1) - suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr1, balances)) - - testCases := []struct { - name string - msg *types.MsgCreateVestingAccount - expectErr bool - }{ - { - name: "create delayed vesting account", - msg: types.NewMsgCreateVestingAccount(addr1, addr2, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, true), - expectErr: false, - }, - { - name: "create continuous vesting account", - msg: types.NewMsgCreateVestingAccount(addr1, addr3, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, false), - expectErr: false, - }, - { - name: "continuous vesting account already exists", - msg: types.NewMsgCreateVestingAccount(addr1, addr3, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, false), - expectErr: true, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - res, err := suite.handler(ctx, tc.msg) - if tc.expectErr { - suite.Require().Error(err) - } else { - suite.Require().NoError(err) - suite.Require().NotNil(res) - - toAddr, err := sdk.AccAddressFromBech32(tc.msg.ToAddress) - suite.Require().NoError(err) - accI := suite.app.AccountKeeper.GetAccount(ctx, toAddr) - suite.Require().NotNil(accI) - - if tc.msg.Delayed { - acc, ok := accI.(*types.DelayedVestingAccount) - suite.Require().True(ok) - suite.Require().Equal(tc.msg.Amount, acc.GetVestingCoins(ctx.BlockTime())) - } else { - acc, ok := accI.(*types.ContinuousVestingAccount) - suite.Require().True(ok) - suite.Require().Equal(tc.msg.Amount, acc.GetVestingCoins(ctx.BlockTime())) - } - } - }) - } -} - -func (suite *HandlerTestSuite) TestMsgDonateVestingToken() { - ctx := suite.app.BaseApp.NewContext(false, tmproto.Header{Height: suite.app.LastBlockHeight() + 1}) - - prevCommunityFund := suite.app.DistrKeeper.GetFeePool(ctx).CommunityPool - - balances := sdk.NewCoins(sdk.NewInt64Coin("test", 1000)) - addr1 := sdk.AccAddress([]byte("addr1_______________")) - addr2 := sdk.AccAddress([]byte("addr2_______________")) - addr3 := sdk.AccAddress([]byte("addr3_______________")) - addr4 := sdk.AccAddress([]byte("addr4_______________")) - - valAddr := sdk.ValAddress([]byte("validator___________")) - suite.app.StakingKeeper.SetValidator(ctx, stakingtypes.Validator{ - OperatorAddress: valAddr.String(), - ConsensusPubkey: nil, - Jailed: false, - Status: 0, - Tokens: sdk.NewInt(2), - DelegatorShares: sdk.MustNewDecFromStr("1.1"), - Description: stakingtypes.Description{}, - UnbondingHeight: 0, - UnbondingTime: time.Time{}, - Commission: stakingtypes.Commission{}, - MinSelfDelegation: sdk.NewInt(1), - }) - - acc1 := suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - suite.app.AccountKeeper.SetAccount(ctx, acc1) - suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr1, balances)) - - acc2 := types.NewPermanentLockedAccount( - suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr2).(*authtypes.BaseAccount), balances, - ) - acc2.DelegatedVesting = balances - suite.app.AccountKeeper.SetAccount(ctx, acc2) - suite.app.StakingKeeper.SetDelegation(ctx, stakingtypes.Delegation{ - DelegatorAddress: addr2.String(), - ValidatorAddress: valAddr.String(), - Shares: sdk.OneDec(), - }) - suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr2, balances)) - - acc3 := types.NewPermanentLockedAccount( - suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr3).(*authtypes.BaseAccount), balances, - ) - suite.app.AccountKeeper.SetAccount(ctx, acc3) - suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr3, balances)) - - acc4 := types.NewPermanentLockedAccount( - suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr4).(*authtypes.BaseAccount), balances, - ) - acc4.DelegatedVesting = balances - suite.app.AccountKeeper.SetAccount(ctx, acc4) - suite.app.StakingKeeper.SetDelegation(ctx, stakingtypes.Delegation{ - DelegatorAddress: addr4.String(), - ValidatorAddress: valAddr.String(), - Shares: sdk.MustNewDecFromStr("0.1"), - }) - suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr4, balances)) - - testCases := []struct { - name string - msg *types.MsgDonateAllVestingTokens - expectErr bool - }{ - { - name: "donate from normal account", - msg: types.NewMsgDonateAllVestingTokens(addr1), - expectErr: true, - }, - { - name: "donate from vesting account with delegated vesting", - msg: types.NewMsgDonateAllVestingTokens(addr2), - expectErr: true, - }, - { - name: "donate from vesting account", - msg: types.NewMsgDonateAllVestingTokens(addr3), - expectErr: false, - }, - { - name: "donate from vesting account with dust delegation", - msg: types.NewMsgDonateAllVestingTokens(addr4), - expectErr: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - // Rollback context after every test case - ctx, _ := ctx.CacheContext() - res, err := suite.handler(ctx, tc.msg) - if tc.expectErr { - suite.Require().Error(err) - } else { - suite.Require().NoError(err) - suite.Require().NotNil(res) - - feePool := suite.app.DistrKeeper.GetFeePool(ctx).CommunityPool.Sub(prevCommunityFund) - communityFund, _ := feePool.TruncateDecimal() - suite.Require().Equal(balances, communityFund) - - fromAddr, err := sdk.AccAddressFromBech32(tc.msg.FromAddress) - suite.Require().NoError(err) - accI := suite.app.AccountKeeper.GetAccount(ctx, fromAddr) - suite.Require().NotNil(accI) - _, ok := accI.(*authtypes.BaseAccount) - suite.Require().True(ok) - balance := suite.app.BankKeeper.GetAllBalances(ctx, fromAddr) - suite.Require().Empty(balance) - - _, broken := stakingkeeper.DelegatorSharesInvariant(suite.app.StakingKeeper)(ctx) - suite.Require().False(broken) - } - }) - } -} - -func TestHandlerTestSuite(t *testing.T) { - suite.Run(t, new(HandlerTestSuite)) -} diff --git a/x/auth/vesting/module.go b/x/auth/vesting/module.go index 381c300e6d2e..a795698b2281 100644 --- a/x/auth/vesting/module.go +++ b/x/auth/vesting/module.go @@ -152,7 +152,7 @@ type VestingInputs struct { AccountKeeper keeper.AccountKeeper BankKeeper types.BankKeeper - DistrKeeper types.DistrKeeper + // DistrKeeper types.DistrKeeper StakingKeeper types.StakingKeeper } @@ -163,7 +163,7 @@ type VestingOutputs struct { } func ProvideModule(in VestingInputs) VestingOutputs { - m := NewAppModule(in.AccountKeeper, in.BankKeeper, in.DistrKeeper, in.StakingKeeper) + m := NewAppModule(in.AccountKeeper, in.BankKeeper, nil, in.StakingKeeper) return VestingOutputs{Module: m} } diff --git a/x/auth/vesting/msg_server.go b/x/auth/vesting/msg_server.go index 8dfb87da2efe..79307c16ac2a 100644 --- a/x/auth/vesting/msg_server.go +++ b/x/auth/vesting/msg_server.go @@ -276,13 +276,5 @@ func (s msgServer) DonateAllVestingTokens(goCtx context.Context, msg *types.MsgD return nil, err } - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeyAmount, vestingCoins.String()), - ), - ) - return &types.MsgDonateAllVestingTokensResponse{}, nil } diff --git a/x/auth/vesting/msg_server_test.go b/x/auth/vesting/msg_server_test.go index 18205f705b2a..c55df14e03dc 100644 --- a/x/auth/vesting/msg_server_test.go +++ b/x/auth/vesting/msg_server_test.go @@ -34,6 +34,8 @@ type VestingTestSuite struct { ctx sdk.Context accountKeeper authkeeper.AccountKeeper bankKeeper *vestingtestutil.MockBankKeeper + distrKeeper *vestingtestutil.MockDistrKeeper + stakingKeeper *vestingtestutil.MockStakingKeeper msgServer vestingtypes.MsgServer } @@ -47,6 +49,8 @@ func (s *VestingTestSuite) SetupTest() { ctrl := gomock.NewController(s.T()) s.bankKeeper = vestingtestutil.NewMockBankKeeper(ctrl) + s.distrKeeper = vestingtestutil.NewMockDistrKeeper(ctrl) + s.stakingKeeper = vestingtestutil.NewMockStakingKeeper(ctrl) s.accountKeeper = authkeeper.NewAccountKeeper( encCfg.Codec, key, @@ -58,7 +62,7 @@ func (s *VestingTestSuite) SetupTest() { vestingtypes.RegisterInterfaces(encCfg.InterfaceRegistry) authtypes.RegisterInterfaces(encCfg.InterfaceRegistry) - s.msgServer = vesting.NewMsgServerImpl(s.accountKeeper, s.bankKeeper) + s.msgServer = vesting.NewMsgServerImpl(s.accountKeeper, s.bankKeeper, s.distrKeeper, s.stakingKeeper) } func (s *VestingTestSuite) TestCreateVestingAccount() { diff --git a/x/auth/vesting/testutil/expected_keepers_mocks.go b/x/auth/vesting/testutil/expected_keepers_mocks.go index fd1fe6140d9c..582d9104f9b2 100644 --- a/x/auth/vesting/testutil/expected_keepers_mocks.go +++ b/x/auth/vesting/testutil/expected_keepers_mocks.go @@ -1,14 +1,17 @@ // Code generated by MockGen. DO NOT EDIT. // Source: x/auth/vesting/types/expected_keepers.go -// Package testutil is a generated GoMock package. -package testutil +// Package mock_types is a generated GoMock package. +package mock_types import ( reflect "reflect" + math "cosmossdk.io/math" + log "github.com/cometbft/cometbft/libs/log" types "github.com/cosmos/cosmos-sdk/types" - gomock "github.com/golang/mock/gomock" + types0 "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/golang/mock/gomock" ) // MockBankKeeper is a mock of BankKeeper interface. @@ -80,3 +83,235 @@ func (mr *MockBankKeeperMockRecorder) SendCoins(ctx, fromAddr, toAddr, amt inter mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoins", reflect.TypeOf((*MockBankKeeper)(nil).SendCoins), ctx, fromAddr, toAddr, amt) } + +// MockDistrKeeper is a mock of DistrKeeper interface. +type MockDistrKeeper struct { + ctrl *gomock.Controller + recorder *MockDistrKeeperMockRecorder +} + +// MockDistrKeeperMockRecorder is the mock recorder for MockDistrKeeper. +type MockDistrKeeperMockRecorder struct { + mock *MockDistrKeeper +} + +// NewMockDistrKeeper creates a new mock instance. +func NewMockDistrKeeper(ctrl *gomock.Controller) *MockDistrKeeper { + mock := &MockDistrKeeper{ctrl: ctrl} + mock.recorder = &MockDistrKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDistrKeeper) EXPECT() *MockDistrKeeperMockRecorder { + return m.recorder +} + +// FundCommunityPool mocks base method. +func (m *MockDistrKeeper) FundCommunityPool(ctx types.Context, amount types.Coins, sender types.AccAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FundCommunityPool", ctx, amount, sender) + ret0, _ := ret[0].(error) + return ret0 +} + +// FundCommunityPool indicates an expected call of FundCommunityPool. +func (mr *MockDistrKeeperMockRecorder) FundCommunityPool(ctx, amount, sender interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FundCommunityPool", reflect.TypeOf((*MockDistrKeeper)(nil).FundCommunityPool), ctx, amount, sender) +} + +// GetAuthority mocks base method. +func (m *MockDistrKeeper) GetAuthority() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAuthority") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetAuthority indicates an expected call of GetAuthority. +func (mr *MockDistrKeeperMockRecorder) GetAuthority() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthority", reflect.TypeOf((*MockDistrKeeper)(nil).GetAuthority)) +} + +// GetTotalRewards mocks base method. +func (m *MockDistrKeeper) GetTotalRewards(ctx types.Context) types.DecCoins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTotalRewards", ctx) + ret0, _ := ret[0].(types.DecCoins) + return ret0 +} + +// GetTotalRewards indicates an expected call of GetTotalRewards. +func (mr *MockDistrKeeperMockRecorder) GetTotalRewards(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTotalRewards", reflect.TypeOf((*MockDistrKeeper)(nil).GetTotalRewards), ctx) +} + +// Logger mocks base method. +func (m *MockDistrKeeper) Logger(ctx types.Context) log.Logger { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Logger", ctx) + ret0, _ := ret[0].(log.Logger) + return ret0 +} + +// Logger indicates an expected call of Logger. +func (mr *MockDistrKeeperMockRecorder) Logger(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Logger", reflect.TypeOf((*MockDistrKeeper)(nil).Logger), ctx) +} + +// SetWithdrawAddr mocks base method. +func (m *MockDistrKeeper) SetWithdrawAddr(ctx types.Context, delegatorAddr, withdrawAddr types.AccAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetWithdrawAddr", ctx, delegatorAddr, withdrawAddr) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetWithdrawAddr indicates an expected call of SetWithdrawAddr. +func (mr *MockDistrKeeperMockRecorder) SetWithdrawAddr(ctx, delegatorAddr, withdrawAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWithdrawAddr", reflect.TypeOf((*MockDistrKeeper)(nil).SetWithdrawAddr), ctx, delegatorAddr, withdrawAddr) +} + +// WithdrawDelegationRewards mocks base method. +func (m *MockDistrKeeper) WithdrawDelegationRewards(ctx types.Context, delAddr types.AccAddress, valAddr types.ValAddress) (types.Coins, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WithdrawDelegationRewards", ctx, delAddr, valAddr) + ret0, _ := ret[0].(types.Coins) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WithdrawDelegationRewards indicates an expected call of WithdrawDelegationRewards. +func (mr *MockDistrKeeperMockRecorder) WithdrawDelegationRewards(ctx, delAddr, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithdrawDelegationRewards", reflect.TypeOf((*MockDistrKeeper)(nil).WithdrawDelegationRewards), ctx, delAddr, valAddr) +} + +// WithdrawValidatorCommission mocks base method. +func (m *MockDistrKeeper) WithdrawValidatorCommission(ctx types.Context, valAddr types.ValAddress) (types.Coins, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WithdrawValidatorCommission", ctx, valAddr) + ret0, _ := ret[0].(types.Coins) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WithdrawValidatorCommission indicates an expected call of WithdrawValidatorCommission. +func (mr *MockDistrKeeperMockRecorder) WithdrawValidatorCommission(ctx, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithdrawValidatorCommission", reflect.TypeOf((*MockDistrKeeper)(nil).WithdrawValidatorCommission), ctx, valAddr) +} + +// MockStakingKeeper is a mock of StakingKeeper interface. +type MockStakingKeeper struct { + ctrl *gomock.Controller + recorder *MockStakingKeeperMockRecorder +} + +// MockStakingKeeperMockRecorder is the mock recorder for MockStakingKeeper. +type MockStakingKeeperMockRecorder struct { + mock *MockStakingKeeper +} + +// NewMockStakingKeeper creates a new mock instance. +func NewMockStakingKeeper(ctrl *gomock.Controller) *MockStakingKeeper { + mock := &MockStakingKeeper{ctrl: ctrl} + mock.recorder = &MockStakingKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStakingKeeper) EXPECT() *MockStakingKeeperMockRecorder { + return m.recorder +} + +// GetDelegatorDelegations mocks base method. +func (m *MockStakingKeeper) GetDelegatorDelegations(ctx types.Context, delegator types.AccAddress, maxRetrieve uint16) []types0.Delegation { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDelegatorDelegations", ctx, delegator, maxRetrieve) + ret0, _ := ret[0].([]types0.Delegation) + return ret0 +} + +// GetDelegatorDelegations indicates an expected call of GetDelegatorDelegations. +func (mr *MockStakingKeeperMockRecorder) GetDelegatorDelegations(ctx, delegator, maxRetrieve interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDelegatorDelegations", reflect.TypeOf((*MockStakingKeeper)(nil).GetDelegatorDelegations), ctx, delegator, maxRetrieve) +} + +// GetRedelegations mocks base method. +func (m *MockStakingKeeper) GetRedelegations(ctx types.Context, delegator types.AccAddress, maxRetrieve uint16) []types0.Redelegation { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRedelegations", ctx, delegator, maxRetrieve) + ret0, _ := ret[0].([]types0.Redelegation) + return ret0 +} + +// GetRedelegations indicates an expected call of GetRedelegations. +func (mr *MockStakingKeeperMockRecorder) GetRedelegations(ctx, delegator, maxRetrieve interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRedelegations", reflect.TypeOf((*MockStakingKeeper)(nil).GetRedelegations), ctx, delegator, maxRetrieve) +} + +// GetUnbondingDelegations mocks base method. +func (m *MockStakingKeeper) GetUnbondingDelegations(ctx types.Context, delegator types.AccAddress, maxRetrieve uint16) []types0.UnbondingDelegation { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUnbondingDelegations", ctx, delegator, maxRetrieve) + ret0, _ := ret[0].([]types0.UnbondingDelegation) + return ret0 +} + +// GetUnbondingDelegations indicates an expected call of GetUnbondingDelegations. +func (mr *MockStakingKeeperMockRecorder) GetUnbondingDelegations(ctx, delegator, maxRetrieve interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnbondingDelegations", reflect.TypeOf((*MockStakingKeeper)(nil).GetUnbondingDelegations), ctx, delegator, maxRetrieve) +} + +// GetValidator mocks base method. +func (m *MockStakingKeeper) GetValidator(ctx types.Context, addr types.ValAddress) (types0.Validator, bool) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetValidator", ctx, addr) + ret0, _ := ret[0].(types0.Validator) + ret1, _ := ret[1].(bool) + return ret0, ret1 +} + +// GetValidator indicates an expected call of GetValidator. +func (mr *MockStakingKeeperMockRecorder) GetValidator(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidator", reflect.TypeOf((*MockStakingKeeper)(nil).GetValidator), ctx, addr) +} + +// RemoveDelegation mocks base method. +func (m *MockStakingKeeper) RemoveDelegation(ctx types.Context, delegation types0.Delegation) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveDelegation", ctx, delegation) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveDelegation indicates an expected call of RemoveDelegation. +func (mr *MockStakingKeeperMockRecorder) RemoveDelegation(ctx, delegation interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveDelegation", reflect.TypeOf((*MockStakingKeeper)(nil).RemoveDelegation), ctx, delegation) +} + +// RemoveValidatorTokensAndShares mocks base method. +func (m *MockStakingKeeper) RemoveValidatorTokensAndShares(ctx types.Context, validator types0.Validator, sharesToRemove types.Dec) (types0.Validator, math.Int) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveValidatorTokensAndShares", ctx, validator, sharesToRemove) + ret0, _ := ret[0].(types0.Validator) + ret1, _ := ret[1].(math.Int) + return ret0, ret1 +} + +// RemoveValidatorTokensAndShares indicates an expected call of RemoveValidatorTokensAndShares. +func (mr *MockStakingKeeperMockRecorder) RemoveValidatorTokensAndShares(ctx, validator, sharesToRemove interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveValidatorTokensAndShares", reflect.TypeOf((*MockStakingKeeper)(nil).RemoveValidatorTokensAndShares), ctx, validator, sharesToRemove) +} diff --git a/x/auth/vesting/types/expected_keepers.go b/x/auth/vesting/types/expected_keepers.go index 2e6d39089ac8..1a1bd981fe09 100644 --- a/x/auth/vesting/types/expected_keepers.go +++ b/x/auth/vesting/types/expected_keepers.go @@ -2,6 +2,7 @@ package types import ( "cosmossdk.io/math" + "github.com/cometbft/cometbft/libs/log" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -16,6 +17,12 @@ type BankKeeper interface { // DistrKeeper defines the expected interface for distribution keeper type DistrKeeper interface { + GetAuthority() string + Logger(ctx sdk.Context) log.Logger + SetWithdrawAddr(ctx sdk.Context, delegatorAddr sdk.AccAddress, withdrawAddr sdk.AccAddress) error + WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) + WithdrawValidatorCommission(ctx sdk.Context, valAddr sdk.ValAddress) (sdk.Coins, error) + GetTotalRewards(ctx sdk.Context) (totalRewards sdk.DecCoins) FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error } diff --git a/x/auth/vesting/types/msgs.go b/x/auth/vesting/types/msgs.go index df97106a6608..9441a809557c 100644 --- a/x/auth/vesting/types/msgs.go +++ b/x/auth/vesting/types/msgs.go @@ -17,7 +17,7 @@ const TypeMsgCreatePermanentLockedAccount = "msg_create_permanent_locked_account const TypeMsgCreatePeriodicVestingAccount = "msg_create_periodic_vesting_account" // TypeMsgDonateAllVestingTokens defines the type value for a MsgDonateAllVestingTokens. -const TypeMsgDonateAllVestingTokens = "msg_donate_all_vesting_tokens" +const TypeMsgDonateAllVestingTokens = "msg_donate_all_vesting_tokens" //nolint: gosec var _ sdk.Msg = &MsgCreateVestingAccount{}