Skip to content

Commit

Permalink
Merge pull request #88 from terra-money/feature/donate-vesting-token
Browse files Browse the repository at this point in the history
feature: add vesting token donation feature
  • Loading branch information
yun-yeo authored Jul 11, 2022
2 parents 13e1bff + 78e0a19 commit d16bab8
Show file tree
Hide file tree
Showing 12 changed files with 657 additions and 49 deletions.
34 changes: 32 additions & 2 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,8 @@
- [MsgCreatePeriodicVestingAccountResponse](#cosmos.vesting.v1beta1.MsgCreatePeriodicVestingAccountResponse)
- [MsgCreateVestingAccount](#cosmos.vesting.v1beta1.MsgCreateVestingAccount)
- [MsgCreateVestingAccountResponse](#cosmos.vesting.v1beta1.MsgCreateVestingAccountResponse)
- [MsgDonateAllVestingTokens](#cosmos.vesting.v1beta1.MsgDonateAllVestingTokens)
- [MsgDonateAllVestingTokensResponse](#cosmos.vesting.v1beta1.MsgDonateAllVestingTokensResponse)

- [Msg](#cosmos.vesting.v1beta1.Msg)

Expand Down Expand Up @@ -8458,7 +8460,7 @@ Since: cosmos-sdk 0.43
<a name="cosmos.vesting.v1beta1.MsgCreatePeriodicVestingAccount"></a>

### MsgCreatePeriodicVestingAccount
MsgCreateVestingAccount defines a message that enables creating a vesting
MsgCreatePeriodicVestingAccount defines a message that enables creating a vesting
account.


Expand All @@ -8477,7 +8479,7 @@ account.
<a name="cosmos.vesting.v1beta1.MsgCreatePeriodicVestingAccountResponse"></a>

### MsgCreatePeriodicVestingAccountResponse
MsgCreateVestingAccountResponse defines the Msg/CreatePeriodicVestingAccount
MsgCreatePeriodicVestingAccountResponse defines the Msg/CreatePeriodicVestingAccount
response type.


Expand Down Expand Up @@ -8514,6 +8516,33 @@ MsgCreateVestingAccountResponse defines the Msg/CreateVestingAccount response ty




<a name="cosmos.vesting.v1beta1.MsgDonateAllVestingTokens"></a>

### MsgDonateAllVestingTokens
MsgDonateAllVestingTokens defines a message that enables donating all vesting
token to community pool.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `from_address` | [string](#string) | | |






<a name="cosmos.vesting.v1beta1.MsgDonateAllVestingTokensResponse"></a>

### MsgDonateAllVestingTokensResponse
MsgDonateAllVestingTokensResponse defines the Msg/MsgDonateAllVestingTokens
response type.





<!-- end messages -->

<!-- end enums -->
Expand All @@ -8530,6 +8559,7 @@ Msg defines the bank Msg service.
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `CreateVestingAccount` | [MsgCreateVestingAccount](#cosmos.vesting.v1beta1.MsgCreateVestingAccount) | [MsgCreateVestingAccountResponse](#cosmos.vesting.v1beta1.MsgCreateVestingAccountResponse) | CreateVestingAccount defines a method that enables creating a vesting account. | |
| `CreatePeriodicVestingAccount` | [MsgCreatePeriodicVestingAccount](#cosmos.vesting.v1beta1.MsgCreatePeriodicVestingAccount) | [MsgCreatePeriodicVestingAccountResponse](#cosmos.vesting.v1beta1.MsgCreatePeriodicVestingAccountResponse) | CreatePeriodicVestingAccount defines a method that enables creating a periodic vesting account. | |
| `DonateAllVestingTokens` | [MsgDonateAllVestingTokens](#cosmos.vesting.v1beta1.MsgDonateAllVestingTokens) | [MsgDonateAllVestingTokensResponse](#cosmos.vesting.v1beta1.MsgDonateAllVestingTokensResponse) | DonateAllVestingTokens defines a method that enables donating all vesting tokens to community pool | |

<!-- end services -->

Expand Down
19 changes: 17 additions & 2 deletions proto/cosmos/vesting/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ service Msg {
// CreatePeriodicVestingAccount defines a method that enables creating a
// periodic vesting account.
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
Expand All @@ -35,7 +38,7 @@ message MsgCreateVestingAccount {
message MsgCreateVestingAccountResponse {}


// MsgCreateVestingAccount defines a message that enables creating a vesting
// MsgCreatePeriodicVestingAccount defines a message that enables creating a vesting
// account.
message MsgCreatePeriodicVestingAccount {
option (gogoproto.equal) = false;
Expand All @@ -46,6 +49,18 @@ message MsgCreatePeriodicVestingAccount {
repeated Period vesting_periods = 4 [(gogoproto.nullable) = false];
}

// MsgCreateVestingAccountResponse defines the Msg/CreatePeriodicVestingAccount
// MsgCreatePeriodicVestingAccountResponse defines the Msg/CreatePeriodicVestingAccount
// response type.
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 {}
2 changes: 1 addition & 1 deletion simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func NewSimApp(
encodingConfig.TxConfig,
),
auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts),
vesting.NewAppModule(app.AccountKeeper, app.BankKeeper),
vesting.NewAppModule(app.AccountKeeper, app.BankKeeper, app.DistrKeeper),
bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper),
capability.NewAppModule(appCodec, *app.CapabilityKeeper),
crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants),
Expand Down
29 changes: 29 additions & 0 deletions x/auth/vesting/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func GetTxCmd() *cobra.Command {
txCmd.AddCommand(
NewMsgCreateVestingAccountCmd(),
NewMsgCreatePeriodicVestingAccountCmd(),
NewMsgDonateAllVestingTokensCmd(),
)

return txCmd
Expand Down Expand Up @@ -171,3 +172,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
}
8 changes: 6 additions & 2 deletions x/auth/vesting/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
)

// NewHandler returns a handler for x/auth message types.
func NewHandler(ak keeper.AccountKeeper, bk types.BankKeeper) sdk.Handler {
msgServer := NewMsgServerImpl(ak, bk)
func NewHandler(ak keeper.AccountKeeper, bk types.BankKeeper, dk types.DistrKeeper) sdk.Handler {
msgServer := NewMsgServerImpl(ak, bk, dk)

return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
ctx = ctx.WithEventManager(sdk.NewEventManager())
Expand All @@ -23,6 +23,10 @@ func NewHandler(ak keeper.AccountKeeper, bk types.BankKeeper) sdk.Handler {
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)
}
Expand Down
79 changes: 78 additions & 1 deletion x/auth/vesting/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (

"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/cosmos/cosmos-sdk/x/auth/vesting"
"github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
)
Expand All @@ -23,7 +25,7 @@ func (suite *HandlerTestSuite) SetupTest() {
checkTx := false
app := simapp.Setup(checkTx)

suite.handler = vesting.NewHandler(app.AccountKeeper, app.BankKeeper)
suite.handler = vesting.NewHandler(app.AccountKeeper, app.BankKeeper, app.DistrKeeper)
suite.app = app
}

Expand Down Expand Up @@ -91,6 +93,81 @@ func (suite *HandlerTestSuite) TestMsgCreateVestingAccount() {
}
}

func (suite *HandlerTestSuite) TestMsgDonateVestingToken() {
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))

acc2 := types.NewPermanentLockedAccount(
suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr2).(*authtypes.BaseAccount), balances,
)
acc2.DelegatedVesting = balances
suite.app.AccountKeeper.SetAccount(ctx, acc2)
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 form 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
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))
}
9 changes: 6 additions & 3 deletions x/auth/vesting/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"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"
)
Expand Down Expand Up @@ -78,13 +79,15 @@ type AppModule struct {

accountKeeper keeper.AccountKeeper
bankKeeper types.BankKeeper
distrKeeper types.DistrKeeper
}

func NewAppModule(ak keeper.AccountKeeper, bk types.BankKeeper) AppModule {
func NewAppModule(ak keeper.AccountKeeper, bk types.BankKeeper, dk types.DistrKeeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
accountKeeper: ak,
bankKeeper: bk,
distrKeeper: dk,
}
}

Expand All @@ -93,7 +96,7 @@ func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}

// Route returns the module's message router and handler.
func (am AppModule) Route() sdk.Route {
return sdk.NewRoute(types.RouterKey, NewHandler(am.accountKeeper, am.bankKeeper))
return sdk.NewRoute(types.RouterKey, NewHandler(am.accountKeeper, am.bankKeeper, am.distrKeeper))
}

// QuerierRoute returns an empty string as the module contains no query
Expand All @@ -102,7 +105,7 @@ func (AppModule) QuerierRoute() string { return "" }

// 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))
}

// LegacyQuerierHandler performs a no-op.
Expand Down
Loading

0 comments on commit d16bab8

Please sign in to comment.