diff --git a/app/app.go b/app/app.go index 3b07e7da..152b5f8e 100644 --- a/app/app.go +++ b/app/app.go @@ -107,6 +107,7 @@ import ( committeekeeper "github.com/kava-labs/kava/x/committee/keeper" committeetypes "github.com/kava-labs/kava/x/committee/types" earn "github.com/kava-labs/kava/x/earn" + earnclient "github.com/kava-labs/kava/x/earn/client" earnkeeper "github.com/kava-labs/kava/x/earn/keeper" earntypes "github.com/kava-labs/kava/x/earn/types" evmutil "github.com/kava-labs/kava/x/evmutil" @@ -171,6 +172,8 @@ var ( ibcclientclient.UpgradeProposalHandler, kavadistclient.ProposalHandler, committeeclient.ProposalHandler, + earnclient.DepositProposalHandler, + earnclient.WithdrawProposalHandler, ), params.AppModuleBasic{}, crisis.AppModuleBasic{}, @@ -225,6 +228,7 @@ var ( savingstypes.ModuleAccountName: nil, liquidtypes.ModuleAccountName: {authtypes.Minter, authtypes.Burner}, earntypes.ModuleAccountName: nil, + kavadisttypes.FundModuleAccount: nil, } ) @@ -613,6 +617,7 @@ func NewApp( &app.liquidKeeper, &hardKeeper, &savingsKeeper, + app.distrKeeper, ) app.incentiveKeeper = incentivekeeper.NewKeeper( @@ -652,26 +657,6 @@ func NewApp( app.accountKeeper, app.bankKeeper, ) - // create gov keeper with router - // NOTE this must be done after any keepers referenced in the gov router (ie committee) are defined - govRouter := govtypes.NewRouter() - govRouter. - AddRoute(govtypes.RouterKey, govtypes.ProposalHandler). - AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)). - AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.upgradeKeeper)). - AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.ibcKeeper.ClientKeeper)). - AddRoute(distrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)). - AddRoute(kavadisttypes.RouterKey, kavadist.NewCommunityPoolMultiSpendProposalHandler(app.kavadistKeeper)). - AddRoute(committeetypes.RouterKey, committee.NewProposalHandler(app.committeeKeeper)) - app.govKeeper = govkeeper.NewKeeper( - appCodec, - keys[govtypes.StoreKey], - govSubspace, - app.accountKeeper, - app.bankKeeper, - &app.stakingKeeper, - govRouter, - ) // register the staking hooks // NOTE: These keepers are passed by reference above, so they will contain these hooks. @@ -684,6 +669,28 @@ func NewApp( app.savingsKeeper = *savingsKeeper.SetHooks(savingstypes.NewMultiSavingsHooks(app.incentiveKeeper.Hooks())) app.earnKeeper = *earnKeeper.SetHooks(app.incentiveKeeper.Hooks()) + // create gov keeper with router + // NOTE this must be done after any keepers referenced in the gov router (ie committee) are defined + govRouter := govtypes.NewRouter() + govRouter. + AddRoute(govtypes.RouterKey, govtypes.ProposalHandler). + AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)). + AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.upgradeKeeper)). + AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.ibcKeeper.ClientKeeper)). + AddRoute(distrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)). + AddRoute(kavadisttypes.RouterKey, kavadist.NewCommunityPoolMultiSpendProposalHandler(app.kavadistKeeper)). + AddRoute(earntypes.RouterKey, earn.NewCommunityPoolProposalHandler(app.earnKeeper)). + AddRoute(committeetypes.RouterKey, committee.NewProposalHandler(app.committeeKeeper)) + app.govKeeper = govkeeper.NewKeeper( + appCodec, + keys[govtypes.StoreKey], + govSubspace, + app.accountKeeper, + app.bankKeeper, + &app.stakingKeeper, + govRouter, + ) + // override x/gov tally handler with custom implementation tallyHandler := NewTallyHandler( app.govKeeper, app.stakingKeeper, app.savingsKeeper, app.earnKeeper, @@ -1023,10 +1030,11 @@ func (app *App) loadBlockedMaccAddrs() map[string]bool { kavadistMaccAddr := app.accountKeeper.GetModuleAddress(kavadisttypes.ModuleName) earnMaccAddr := app.accountKeeper.GetModuleAddress(earntypes.ModuleName) liquidMaccAddr := app.accountKeeper.GetModuleAddress(liquidtypes.ModuleName) + kavadistFundMaccAddr := app.accountKeeper.GetModuleAddress(kavadisttypes.FundModuleAccount) for addr := range modAccAddrs { // Set the kavadist and earn module account address as unblocked - if addr == kavadistMaccAddr.String() || addr == earnMaccAddr.String() || addr == liquidMaccAddr.String() { + if addr == kavadistMaccAddr.String() || addr == earnMaccAddr.String() || addr == liquidMaccAddr.String() || addr == kavadistFundMaccAddr.String() { modAccAddrs[addr] = false } } diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index b0dff491..5df5ca12 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -193,6 +193,12 @@ - [kava/earn/v1beta1/genesis.proto](#kava/earn/v1beta1/genesis.proto) - [GenesisState](#kava.earn.v1beta1.GenesisState) +- [kava/earn/v1beta1/proposal.proto](#kava/earn/v1beta1/proposal.proto) + - [CommunityPoolDepositProposal](#kava.earn.v1beta1.CommunityPoolDepositProposal) + - [CommunityPoolDepositProposalJSON](#kava.earn.v1beta1.CommunityPoolDepositProposalJSON) + - [CommunityPoolWithdrawProposal](#kava.earn.v1beta1.CommunityPoolWithdrawProposal) + - [CommunityPoolWithdrawProposalJSON](#kava.earn.v1beta1.CommunityPoolWithdrawProposalJSON) + - [kava/earn/v1beta1/query.proto](#kava/earn/v1beta1/query.proto) - [DepositResponse](#kava.earn.v1beta1.DepositResponse) - [QueryDepositsRequest](#kava.earn.v1beta1.QueryDepositsRequest) @@ -2950,6 +2956,92 @@ GenesisState defines the earn module's genesis state. + + + + + + + + + + + +

Top

+ +## kava/earn/v1beta1/proposal.proto + + + + + +### CommunityPoolDepositProposal +CommunityPoolDepositProposal deposits from the community pool into an earn vault + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `title` | [string](#string) | | | +| `description` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | | + + + + + + + + +### CommunityPoolDepositProposalJSON +CommunityPoolDepositProposalJSON defines a CommunityPoolDepositProposal with a deposit + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `title` | [string](#string) | | | +| `description` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | | +| `deposit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | + + + + + + + + +### CommunityPoolWithdrawProposal +CommunityPoolWithdrawProposal withdraws from an earn vault back to community pool + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `title` | [string](#string) | | | +| `description` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | | + + + + + + + + +### CommunityPoolWithdrawProposalJSON +CommunityPoolWithdrawProposalJSON defines a CommunityPoolWithdrawProposal with a deposit + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `title` | [string](#string) | | | +| `description` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | | +| `deposit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | + + + + + diff --git a/proto/kava/earn/v1beta1/proposal.proto b/proto/kava/earn/v1beta1/proposal.proto new file mode 100644 index 00000000..a3c66ecf --- /dev/null +++ b/proto/kava/earn/v1beta1/proposal.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; +package kava.earn.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/kava-labs/kava/x/earn/types"; + +// CommunityPoolDepositProposal deposits from the community pool into an earn vault +message CommunityPoolDepositProposal { + option (gogoproto.goproto_stringer) = false; + option (gogoproto.goproto_getters) = false; + + string title = 1; + string description = 2; + cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false]; +} + +// CommunityPoolDepositProposalJSON defines a CommunityPoolDepositProposal with a deposit +message CommunityPoolDepositProposalJSON { + option (gogoproto.goproto_stringer) = true; + option (gogoproto.goproto_getters) = false; + + string title = 1; + string description = 2; + cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin deposit = 4 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// CommunityPoolWithdrawProposal withdraws from an earn vault back to community pool +message CommunityPoolWithdrawProposal { + option (gogoproto.goproto_stringer) = false; + option (gogoproto.goproto_getters) = false; + + string title = 1; + string description = 2; + cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false]; +} + +// CommunityPoolWithdrawProposalJSON defines a CommunityPoolWithdrawProposal with a deposit +message CommunityPoolWithdrawProposalJSON { + option (gogoproto.goproto_stringer) = true; + option (gogoproto.goproto_getters) = false; + + string title = 1; + string description = 2; + cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin deposit = 4 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} \ No newline at end of file diff --git a/x/earn/client/cli/tx.go b/x/earn/client/cli/tx.go index 681f5053..9d667303 100644 --- a/x/earn/client/cli/tx.go +++ b/x/earn/client/cli/tx.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "strings" "github.com/spf13/cobra" @@ -10,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/kava-labs/kava/x/earn/types" ) @@ -109,3 +111,119 @@ func getCmdWithdraw() *cobra.Command { }, } } + +// GetCmdSubmitCommunityPoolDepositProposal implements the command to submit a community-pool deposit proposal +func GetCmdSubmitCommunityPoolDepositProposal() *cobra.Command { + cmd := &cobra.Command{ + Use: "community-pool-deposit [proposal-file]", + Args: cobra.ExactArgs(1), + Short: "Submit a community pool deposit proposal", + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a community pool deposit proposal along with an initial deposit. +The proposal details must be supplied via a JSON file. +Example: +$ %s tx gov submit-proposal community-pool-deposit --from= +Where proposal.json contains: +{ + "title": "Community Pool Deposit", + "description": "Deposit some KAVA from community pool!", + "amount": + { + "denom": "ukava", + "amount": "100000000000" + }, + "deposit": [ + { + "denom": "ukava", + "amount": "1000000000" + } + ] +} +`, + version.AppName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + proposal, err := ParseCommunityPoolDepositProposalJSON(clientCtx.Codec, args[0]) + if err != nil { + return err + } + + from := clientCtx.GetFromAddress() + content := types.NewCommunityPoolDepositProposal(proposal.Title, proposal.Description, proposal.Amount) + msg, err := govtypes.NewMsgSubmitProposal(content, proposal.Deposit, from) + if err != nil { + return err + } + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + return cmd +} + +// GetCmdSubmitCommunityPoolWithdrawProposal implements the command to submit a community-pool withdraw proposal +func GetCmdSubmitCommunityPoolWithdrawProposal() *cobra.Command { + cmd := &cobra.Command{ + Use: "community-pool-withdraw [proposal-file]", + Args: cobra.ExactArgs(1), + Short: "Submit a community pool withdraw proposal", + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a community pool withdraw proposal along with an initial deposit. +The proposal details must be supplied via a JSON file. +Example: +$ %s tx gov submit-proposal community-pool-withdraw --from= +Where proposal.json contains: +{ + "title": "Community Pool Withdraw", + "description": "Withdraw some KAVA from community pool!", + "amount": + { + "denom": "ukava", + "amount": "100000000000" + }, + "deposit": [ + { + "denom": "ukava", + "amount": "1000000000" + } + ] +} +`, + version.AppName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + proposal, err := ParseCommunityPoolWithdrawProposalJSON(clientCtx.Codec, args[0]) + if err != nil { + return err + } + + from := clientCtx.GetFromAddress() + content := types.NewCommunityPoolWithdrawProposal(proposal.Title, proposal.Description, proposal.Amount) + msg, err := govtypes.NewMsgSubmitProposal(content, proposal.Deposit, from) + if err != nil { + return err + } + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + return cmd +} diff --git a/x/earn/client/cli/utils.go b/x/earn/client/cli/utils.go new file mode 100644 index 00000000..c8693ec4 --- /dev/null +++ b/x/earn/client/cli/utils.go @@ -0,0 +1,39 @@ +package cli + +import ( + "os" + + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/kava-labs/kava/x/earn/types" +) + +// ParseCommunityPoolDepositProposalJSON reads and parses a CommunityPoolDepositProposalJSON from a file. +func ParseCommunityPoolDepositProposalJSON(cdc codec.JSONCodec, proposalFile string) (types.CommunityPoolDepositProposalJSON, error) { + proposal := types.CommunityPoolDepositProposalJSON{} + contents, err := os.ReadFile(proposalFile) + if err != nil { + return proposal, err + } + + if err := cdc.UnmarshalJSON(contents, &proposal); err != nil { + return proposal, err + } + + return proposal, nil +} + +// ParseCommunityPoolWithdrawProposalJSON reads and parses a CommunityPoolWithdrawProposalJSON from a file. +func ParseCommunityPoolWithdrawProposalJSON(cdc codec.JSONCodec, proposalFile string) (types.CommunityPoolWithdrawProposalJSON, error) { + proposal := types.CommunityPoolWithdrawProposalJSON{} + contents, err := os.ReadFile(proposalFile) + if err != nil { + return proposal, err + } + + if err := cdc.UnmarshalJSON(contents, &proposal); err != nil { + return proposal, err + } + + return proposal, nil +} diff --git a/x/earn/client/proposal_handler.go b/x/earn/client/proposal_handler.go new file mode 100644 index 00000000..9d76d960 --- /dev/null +++ b/x/earn/client/proposal_handler.go @@ -0,0 +1,14 @@ +package client + +import ( + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + + "github.com/kava-labs/kava/x/earn/client/cli" + "github.com/kava-labs/kava/x/earn/client/rest" +) + +// community-pool deposit/withdraw proposal handlers +var ( + DepositProposalHandler = govclient.NewProposalHandler(cli.GetCmdSubmitCommunityPoolDepositProposal, rest.DepositProposalRESTHandler) + WithdrawProposalHandler = govclient.NewProposalHandler(cli.GetCmdSubmitCommunityPoolWithdrawProposal, rest.WithdrawProposalRESTHandler) +) diff --git a/x/earn/client/rest/rest.go b/x/earn/client/rest/rest.go new file mode 100644 index 00000000..bff5052f --- /dev/null +++ b/x/earn/client/rest/rest.go @@ -0,0 +1,97 @@ +package rest + +import ( + "net/http" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" + govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "github.com/kava-labs/kava/x/earn/types" +) + +type ( + // CommunityPoolDepositProposalReq defines a community pool deposit proposal request body. + CommunityPoolDepositProposalReq struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + + Title string `json:"title" yaml:"title"` + Description string `json:"description" yaml:"description"` + Amount sdk.Coin `json:"amount" yaml:"amount"` + Deposit sdk.Coins `json:"deposit" yaml:"deposit"` + Proposer sdk.AccAddress `json:"proposer" yaml:"proposer"` + } + // CommunityPoolWithdrawProposalReq defines a community pool deposit proposal request body. + CommunityPoolWithdrawProposalReq struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + + Title string `json:"title" yaml:"title"` + Description string `json:"description" yaml:"description"` + Amount sdk.Coin `json:"amount" yaml:"amount"` + Deposit sdk.Coins `json:"deposit" yaml:"deposit"` + Proposer sdk.AccAddress `json:"proposer" yaml:"proposer"` + } +) + +// DepositProposalRESTHandler returns a ProposalRESTHandler that exposes the community pool deposit REST handler with a given sub-route. +func DepositProposalRESTHandler(cliCtx client.Context) govrest.ProposalRESTHandler { + return govrest.ProposalRESTHandler{ + SubRoute: types.ProposalTypeCommunityPoolDeposit, + Handler: postDepositProposalHandlerFn(cliCtx), + } +} + +func postDepositProposalHandlerFn(cliCtx client.Context) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req CommunityPoolDepositProposalReq + if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) { + return + } + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + content := types.NewCommunityPoolDepositProposal(req.Title, req.Description, req.Amount) + msg, err := govtypes.NewMsgSubmitProposal(content, req.Deposit, req.Proposer) + if rest.CheckBadRequestError(w, err) { + return + } + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { + return + } + tx.WriteGeneratedTxResponse(cliCtx, w, req.BaseReq, msg) + } +} + +// WithdrawProposalRESTHandler returns a ProposalRESTHandler that exposes the community pool deposit REST handler with a given sub-route. +func WithdrawProposalRESTHandler(cliCtx client.Context) govrest.ProposalRESTHandler { + return govrest.ProposalRESTHandler{ + SubRoute: types.ProposalTypeCommunityPoolWithdraw, + Handler: postWithdrawProposalHandlerFn(cliCtx), + } +} + +func postWithdrawProposalHandlerFn(cliCtx client.Context) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req CommunityPoolWithdrawProposalReq + if !rest.ReadRESTReq(w, r, cliCtx.LegacyAmino, &req) { + return + } + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + content := types.NewCommunityPoolWithdrawProposal(req.Title, req.Description, req.Amount) + msg, err := govtypes.NewMsgSubmitProposal(content, req.Deposit, req.Proposer) + if rest.CheckBadRequestError(w, err) { + return + } + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { + return + } + tx.WriteGeneratedTxResponse(cliCtx, w, req.BaseReq, msg) + } +} diff --git a/x/earn/handler.go b/x/earn/handler.go new file mode 100644 index 00000000..b6eac9a5 --- /dev/null +++ b/x/earn/handler.go @@ -0,0 +1,24 @@ +package earn + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "github.com/kava-labs/kava/x/earn/keeper" + "github.com/kava-labs/kava/x/earn/types" +) + +// NewCommunityPoolProposalHandler +func NewCommunityPoolProposalHandler(k keeper.Keeper) govtypes.Handler { + return func(ctx sdk.Context, content govtypes.Content) error { + switch c := content.(type) { + case *types.CommunityPoolDepositProposal: + return keeper.HandleCommunityPoolDepositProposal(ctx, k, c) + case *types.CommunityPoolWithdrawProposal: + return keeper.HandleCommunityPoolWithdrawProposal(ctx, k, c) + default: + return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized earn proposal content type: %T", c) + } + } +} diff --git a/x/earn/keeper/deposit.go b/x/earn/keeper/deposit.go index 894e829f..143f2224 100644 --- a/x/earn/keeper/deposit.go +++ b/x/earn/keeper/deposit.go @@ -4,6 +4,7 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/kava-labs/kava/x/earn/types" ) @@ -112,3 +113,15 @@ func (k *Keeper) Deposit( return nil } + +// DepositFromModuleAccount adds the provided amount from a depositor module +// account to a vault. The vault is specified by the denom in the amount. +func (k *Keeper) DepositFromModuleAccount( + ctx sdk.Context, + from string, + wantAmount sdk.Coin, + withdrawStrategy types.StrategyType, +) error { + addr := k.accountKeeper.GetModuleAddress(from) + return k.Deposit(ctx, addr, wantAmount, withdrawStrategy) +} diff --git a/x/earn/keeper/hooks_test.go b/x/earn/keeper/hooks_test.go index 1bfb2252..63ef4aa4 100644 --- a/x/earn/keeper/hooks_test.go +++ b/x/earn/keeper/hooks_test.go @@ -172,7 +172,7 @@ func (suite *hookTestSuite) TestHooks_DepositAndWithdraw() { acc.GetAddress(), shareRecord.AmountOf(deposit1Amount.Denom), ).Once() - err = suite.Keeper.Withdraw( + _, err = suite.Keeper.Withdraw( suite.Ctx, acc.GetAddress(), // 3 deposits, multiply original deposit amount by 3 @@ -196,7 +196,7 @@ func (suite *hookTestSuite) TestHooks_DepositAndWithdraw() { acc.GetAddress(), shareRecord.AmountOf(deposit2Amount.Denom), ).Once() - err = suite.Keeper.Withdraw( + _, err = suite.Keeper.Withdraw( suite.Ctx, acc.GetAddress(), deposit2Amount, @@ -219,7 +219,7 @@ func (suite *hookTestSuite) TestHooks_DepositAndWithdraw() { acc.GetAddress(), shareRecord.AmountOf(deposit2Amount.Denom), ).Once() - err = suite.Keeper.Withdraw( + _, err = suite.Keeper.Withdraw( suite.Ctx, acc.GetAddress(), deposit2Amount, @@ -242,7 +242,7 @@ func (suite *hookTestSuite) TestHooks_DepositAndWithdraw() { acc.GetAddress(), shareRecord.AmountOf(deposit2Amount.Denom), ).Once() - err = suite.Keeper.Withdraw( + _, err = suite.Keeper.Withdraw( suite.Ctx, acc.GetAddress(), deposit2Amount, @@ -274,7 +274,7 @@ func (suite *hookTestSuite) TestHooks_NoPanicsOnNilHooks() { suite.Require().NoError(err) // BeforeVaultDepositModified should not panic if no hooks are registered - err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) } @@ -317,6 +317,6 @@ func (suite *hookTestSuite) TestHooks_HookOrdering() { suite.Require().True(found, "expected after hook to be called after shares are updated") suite.Require().Equal(depositAmount.Amount.MulRaw(2).ToDec(), shares.AmountOf(depositAmount.Denom)) }) - err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) } diff --git a/x/earn/keeper/keeper.go b/x/earn/keeper/keeper.go index 22f55e1c..b9ffa3f0 100644 --- a/x/earn/keeper/keeper.go +++ b/x/earn/keeper/keeper.go @@ -21,6 +21,9 @@ type Keeper struct { // Keepers used for strategies hardKeeper types.HardKeeper savingsKeeper types.SavingsKeeper + + // Keeper for community pool transfers + distKeeper types.DistributionKeeper } // NewKeeper creates a new keeper @@ -33,6 +36,7 @@ func NewKeeper( liquidKeeper types.LiquidKeeper, hardKeeper types.HardKeeper, savingsKeeper types.SavingsKeeper, + distKeeper types.DistributionKeeper, ) Keeper { if !paramstore.HasKeyTable() { paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) @@ -47,6 +51,7 @@ func NewKeeper( liquidKeeper: liquidKeeper, hardKeeper: hardKeeper, savingsKeeper: savingsKeeper, + distKeeper: distKeeper, } } diff --git a/x/earn/keeper/msg_server.go b/x/earn/keeper/msg_server.go index 755cea85..1e719b99 100644 --- a/x/earn/keeper/msg_server.go +++ b/x/earn/keeper/msg_server.go @@ -53,7 +53,8 @@ func (m msgServer) Withdraw(goCtx context.Context, msg *types.MsgWithdraw) (*typ return nil, err } - if err := m.keeper.Withdraw(ctx, from, msg.Amount, msg.Strategy); err != nil { + _, err = m.keeper.Withdraw(ctx, from, msg.Amount, msg.Strategy) + if err != nil { return nil, err } diff --git a/x/earn/keeper/proposal_handler.go b/x/earn/keeper/proposal_handler.go new file mode 100644 index 00000000..03b08c7c --- /dev/null +++ b/x/earn/keeper/proposal_handler.go @@ -0,0 +1,49 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/kava-labs/kava/x/earn/types" + kavadisttypes "github.com/kava-labs/kava/x/kavadist/types" +) + +// HandleCommunityPoolDepositProposal is a handler for executing a passed community pool deposit proposal +func HandleCommunityPoolDepositProposal(ctx sdk.Context, k Keeper, p *types.CommunityPoolDepositProposal) error { + fundAcc := k.accountKeeper.GetModuleAccount(ctx, kavadisttypes.FundModuleAccount) + if err := k.distKeeper.DistributeFromFeePool(ctx, sdk.NewCoins(p.Amount), fundAcc.GetAddress()); err != nil { + return err + } + + err := k.DepositFromModuleAccount(ctx, kavadisttypes.FundModuleAccount, p.Amount, types.STRATEGY_TYPE_SAVINGS) + if err != nil { + return err + } + + return nil + +} + +// HandleCommunityPoolWithdrawProposal is a handler for executing a passed community pool withdraw proposal. +func HandleCommunityPoolWithdrawProposal(ctx sdk.Context, k Keeper, p *types.CommunityPoolWithdrawProposal) error { + // Withdraw to fund module account + withdrawAmount, err := k.WithdrawFromModuleAccount(ctx, kavadisttypes.FundModuleAccount, p.Amount, types.STRATEGY_TYPE_SAVINGS) + if err != nil { + return err + } + + // Move funds to the community pool manually + err = k.bankKeeper.SendCoinsFromModuleToModule( + ctx, + kavadisttypes.FundModuleAccount, + k.distKeeper.GetDistributionAccount(ctx).GetName(), + sdk.NewCoins(withdrawAmount), + ) + if err != nil { + return err + } + feePool := k.distKeeper.GetFeePool(ctx) + newCommunityPool := feePool.CommunityPool.Add(sdk.NewDecCoinFromCoin(withdrawAmount)) + feePool.CommunityPool = newCommunityPool + k.distKeeper.SetFeePool(ctx, feePool) + return nil +} diff --git a/x/earn/keeper/proposal_handler_test.go b/x/earn/keeper/proposal_handler_test.go new file mode 100644 index 00000000..23856736 --- /dev/null +++ b/x/earn/keeper/proposal_handler_test.go @@ -0,0 +1,80 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/kava-labs/kava/x/earn/keeper" + "github.com/kava-labs/kava/x/earn/testutil" + "github.com/kava-labs/kava/x/earn/types" + "github.com/stretchr/testify/suite" +) + +type proposalTestSuite struct { + testutil.Suite +} + +func (suite *proposalTestSuite) SetupTest() { + suite.Suite.SetupTest() + suite.Keeper.SetParams(suite.Ctx, types.DefaultParams()) +} + +func TestProposalTestSuite(t *testing.T) { + suite.Run(t, new(proposalTestSuite)) +} + +func (suite *proposalTestSuite) TestCommunityDepositProposal() { + distKeeper := suite.App.GetDistrKeeper() + ctx := suite.Ctx + macc := distKeeper.GetDistributionAccount(ctx) + fundAmount := sdk.NewCoins(sdk.NewInt64Coin("ukava", 100000000)) + depositAmount := sdk.NewCoin("ukava", sdk.NewInt(10000000)) + suite.Require().NoError(suite.App.FundModuleAccount(ctx, macc.GetName(), fundAmount)) + feePool := distKeeper.GetFeePool(ctx) + feePool.CommunityPool = sdk.NewDecCoinsFromCoins(fundAmount...) + distKeeper.SetFeePool(ctx, feePool) + suite.CreateVault("ukava", types.StrategyTypes{types.STRATEGY_TYPE_SAVINGS}, false, nil) + prop := types.NewCommunityPoolDepositProposal("test title", + "desc", depositAmount) + err := keeper.HandleCommunityPoolDepositProposal(ctx, suite.Keeper, prop) + suite.Require().NoError(err) + + balance := suite.BankKeeper.GetAllBalances(ctx, macc.GetAddress()) + suite.Require().Equal(fundAmount.Sub(sdk.NewCoins(depositAmount)), balance) + feePool = distKeeper.GetFeePool(ctx) + communityPoolBalance, change := feePool.CommunityPool.TruncateDecimal() + suite.Require().Equal(fundAmount.Sub(sdk.NewCoins(depositAmount)), communityPoolBalance) + suite.Require().True(change.Empty()) +} + +func (suite *proposalTestSuite) TestCommunityWithdrawProposal() { + distKeeper := suite.App.GetDistrKeeper() + ctx := suite.Ctx + macc := distKeeper.GetDistributionAccount(ctx) + fundAmount := sdk.NewCoins(sdk.NewInt64Coin("ukava", 100000000)) + depositAmount := sdk.NewCoin("ukava", sdk.NewInt(10000000)) + suite.Require().NoError(suite.App.FundModuleAccount(ctx, macc.GetName(), fundAmount)) + feePool := distKeeper.GetFeePool(ctx) + feePool.CommunityPool = sdk.NewDecCoinsFromCoins(fundAmount...) + distKeeper.SetFeePool(ctx, feePool) + // TODO update to STRATEGY_TYPE_SAVINGS once implemented + suite.CreateVault("ukava", types.StrategyTypes{types.STRATEGY_TYPE_SAVINGS}, false, nil) + deposit := types.NewCommunityPoolDepositProposal("test title", + "desc", depositAmount) + err := keeper.HandleCommunityPoolDepositProposal(ctx, suite.Keeper, deposit) + suite.Require().NoError(err) + + balance := suite.BankKeeper.GetAllBalances(ctx, macc.GetAddress()) + suite.Require().Equal(fundAmount.Sub(sdk.NewCoins(depositAmount)), balance) + + withdraw := types.NewCommunityPoolWithdrawProposal("test title", + "desc", depositAmount) + err = keeper.HandleCommunityPoolWithdrawProposal(ctx, suite.Keeper, withdraw) + suite.Require().NoError(err) + balance = suite.BankKeeper.GetAllBalances(ctx, macc.GetAddress()) + suite.Require().Equal(fundAmount, balance) + feePool = distKeeper.GetFeePool(ctx) + communityPoolBalance, change := feePool.CommunityPool.TruncateDecimal() + suite.Require().Equal(fundAmount, communityPoolBalance) + suite.Require().True(change.Empty()) +} diff --git a/x/earn/keeper/strategy_hard_test.go b/x/earn/keeper/strategy_hard_test.go index bbf6a6ba..871a9394 100644 --- a/x/earn/keeper/strategy_hard_test.go +++ b/x/earn/keeper/strategy_hard_test.go @@ -184,7 +184,7 @@ func (suite *strategyHardTestSuite) TestWithdraw() { suite.Equal(depositAmount, totalValue) // Withdraw - err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) suite.HardDepositAmountEqual(sdk.NewCoins()) @@ -196,7 +196,7 @@ func (suite *strategyHardTestSuite) TestWithdraw() { suite.Equal(sdk.NewInt64Coin(vaultDenom, 0), totalValue) // Withdraw again - err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_HARD) suite.Require().Error(err) suite.Require().ErrorIs(err, types.ErrVaultRecordNotFound, "vault should be deleted when no more supply") } @@ -218,11 +218,11 @@ func (suite *strategyHardTestSuite) TestWithdraw_OnlyWithdrawOwnSupply() { suite.Require().NoError(err) // Withdraw - err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount, types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) // Withdraw again - err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount, types.STRATEGY_TYPE_HARD) suite.Require().Error(err) suite.Require().ErrorIs( err, @@ -263,11 +263,11 @@ func (suite *strategyHardTestSuite) TestWithdraw_WithAccumulatedHard() { suite.Equal(depositAmount.AddAmount(sdk.NewInt(10)), accValue) // Withdraw 100, 10 remaining - err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount, types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) // Withdraw 100 again -- too much - err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount, types.STRATEGY_TYPE_HARD) suite.Require().Error(err) suite.Require().ErrorIs( err, @@ -276,11 +276,11 @@ func (suite *strategyHardTestSuite) TestWithdraw_WithAccumulatedHard() { ) // Half of remaining 10, 5 remaining - err = suite.Keeper.Withdraw(suite.Ctx, acc, sdk.NewCoin(vaultDenom, sdk.NewInt(5)), types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc, sdk.NewCoin(vaultDenom, sdk.NewInt(5)), types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) // Withdraw all - err = suite.Keeper.Withdraw(suite.Ctx, acc, sdk.NewCoin(vaultDenom, sdk.NewInt(5)), types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc, sdk.NewCoin(vaultDenom, sdk.NewInt(5)), types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) accValue, err = suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc) @@ -401,7 +401,7 @@ func (suite *strategyHardTestSuite) TestWithdraw_AccumulatedAmount() { suite.Require().NoError(err) // 3. Withdraw all from acc1 - including accumulated amount - err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount.AddAmount(sdk.NewInt(10)), types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount.AddAmount(sdk.NewInt(10)), types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) _, found = suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) @@ -444,7 +444,7 @@ func (suite *strategyHardTestSuite) TestWithdraw_AccumulatedTruncated() { suite.Equal(depositAmount.AddAmount(sdk.NewInt(5)), accBal, "acc1 should have 105 usdx") // 3. Withdraw all from acc1 - including accumulated amount - err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount.AddAmount(sdk.NewInt(5)), types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount.AddAmount(sdk.NewInt(5)), types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) acc1Shares, found = suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) @@ -485,7 +485,7 @@ func (suite *strategyHardTestSuite) TestWithdraw_ExpensiveShares() { suite.Equal(sdk.NewInt(2000), accBal.Amount, "acc1 should have 2000 usdx") // 3. Withdraw all from acc1 - including accumulated amount - err = suite.Keeper.Withdraw(suite.Ctx, acc1, sdk.NewInt64Coin(vaultDenom, 2000), types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc1, sdk.NewInt64Coin(vaultDenom, 2000), types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) acc1Shares, found = suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) diff --git a/x/earn/keeper/strategy_savings_test.go b/x/earn/keeper/strategy_savings_test.go index 992f5308..19c13e55 100644 --- a/x/earn/keeper/strategy_savings_test.go +++ b/x/earn/keeper/strategy_savings_test.go @@ -180,7 +180,7 @@ func (suite *strategySavingsTestSuite) TestWithdraw() { suite.Equal(depositAmount, totalValue) // Withdraw - err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_SAVINGS) suite.Require().NoError(err) suite.SavingsDepositAmountEqual(sdk.NewCoins()) @@ -192,7 +192,7 @@ func (suite *strategySavingsTestSuite) TestWithdraw() { suite.Equal(sdk.NewInt64Coin(savingsVaultDenom, 0), totalValue) // Withdraw again - err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_SAVINGS) suite.Require().Error(err) suite.Require().ErrorIs(err, types.ErrVaultRecordNotFound, "vault should be deleted when no more supply") } @@ -213,11 +213,11 @@ func (suite *strategySavingsTestSuite) TestWithdraw_OnlyWithdrawOwnSupply() { suite.Require().NoError(err) // Withdraw - err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount, types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount, types.STRATEGY_TYPE_SAVINGS) suite.Require().NoError(err) // Withdraw again - err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount, types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount, types.STRATEGY_TYPE_SAVINGS) suite.Require().Error(err) suite.Require().ErrorIs( err, @@ -257,11 +257,11 @@ func (suite *strategySavingsTestSuite) TestWithdraw_WithAccumulatedSavings() { suite.Equal(depositAmount.AddAmount(sdk.NewInt(10)), accValue) // Withdraw 100, 10 remaining - err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount, types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount, types.STRATEGY_TYPE_SAVINGS) suite.Require().NoError(err) // Withdraw 100 again -- too much - err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount, types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount, types.STRATEGY_TYPE_SAVINGS) suite.Require().Error(err) suite.Require().ErrorIs( err, @@ -270,11 +270,11 @@ func (suite *strategySavingsTestSuite) TestWithdraw_WithAccumulatedSavings() { ) // Half of remaining 10, 5 remaining - err = suite.Keeper.Withdraw(suite.Ctx, acc, sdk.NewCoin(savingsVaultDenom, sdk.NewInt(5)), types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc, sdk.NewCoin(savingsVaultDenom, sdk.NewInt(5)), types.STRATEGY_TYPE_SAVINGS) suite.Require().NoError(err) // Withdraw all - err = suite.Keeper.Withdraw(suite.Ctx, acc, sdk.NewCoin(savingsVaultDenom, sdk.NewInt(5)), types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc, sdk.NewCoin(savingsVaultDenom, sdk.NewInt(5)), types.STRATEGY_TYPE_SAVINGS) suite.Require().NoError(err) accValue, err = suite.Keeper.GetVaultAccountValue(suite.Ctx, savingsVaultDenom, acc) @@ -393,7 +393,7 @@ func (suite *strategySavingsTestSuite) TestWithdraw_AccumulatedAmount() { suite.Require().NoError(err) // 3. Withdraw all from acc1 - including accumulated amount - err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount.AddAmount(sdk.NewInt(10)), types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount.AddAmount(sdk.NewInt(10)), types.STRATEGY_TYPE_SAVINGS) suite.Require().NoError(err) _, found = suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) @@ -435,7 +435,7 @@ func (suite *strategySavingsTestSuite) TestWithdraw_AccumulatedTruncated() { suite.Equal(depositAmount.AddAmount(sdk.NewInt(5)), accBal, "acc1 should have 105 usdx") // 3. Withdraw all from acc1 - including accumulated amount - err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount.AddAmount(sdk.NewInt(5)), types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount.AddAmount(sdk.NewInt(5)), types.STRATEGY_TYPE_SAVINGS) suite.Require().NoError(err) acc1Shares, found = suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) @@ -475,7 +475,7 @@ func (suite *strategySavingsTestSuite) TestWithdraw_ExpensiveShares() { suite.Equal(sdk.NewInt(2000), accBal.Amount, "acc1 should have 2000 usdx") // 3. Withdraw all from acc1 - including accumulated amount - err = suite.Keeper.Withdraw(suite.Ctx, acc1, sdk.NewInt64Coin(savingsVaultDenom, 2000), types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc1, sdk.NewInt64Coin(savingsVaultDenom, 2000), types.STRATEGY_TYPE_SAVINGS) suite.Require().NoError(err) acc1Shares, found = suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) diff --git a/x/earn/keeper/withdraw.go b/x/earn/keeper/withdraw.go index d149aa24..122e570b 100644 --- a/x/earn/keeper/withdraw.go +++ b/x/earn/keeper/withdraw.go @@ -16,43 +16,43 @@ func (k *Keeper) Withdraw( from sdk.AccAddress, wantAmount sdk.Coin, withdrawStrategy types.StrategyType, -) error { +) (sdk.Coin, error) { // Get AllowedVault, if not found (not a valid vault), return error allowedVault, found := k.GetAllowedVault(ctx, wantAmount.Denom) if !found { - return types.ErrInvalidVaultDenom + return sdk.Coin{}, types.ErrInvalidVaultDenom } if wantAmount.IsZero() { - return types.ErrInsufficientAmount + return sdk.Coin{}, types.ErrInsufficientAmount } // Check if withdraw strategy is supported by vault if !allowedVault.IsStrategyAllowed(withdrawStrategy) { - return types.ErrInvalidVaultStrategy + return sdk.Coin{}, types.ErrInvalidVaultStrategy } // Check if VaultRecord exists vaultRecord, found := k.GetVaultRecord(ctx, wantAmount.Denom) if !found { - return types.ErrVaultRecordNotFound + return sdk.Coin{}, types.ErrVaultRecordNotFound } // Get account share record for the vault vaultShareRecord, found := k.GetVaultShareRecord(ctx, from) if !found { - return types.ErrVaultShareRecordNotFound + return sdk.Coin{}, types.ErrVaultShareRecordNotFound } withdrawShares, err := k.ConvertToShares(ctx, wantAmount) if err != nil { - return fmt.Errorf("failed to convert assets to shares: %w", err) + return sdk.Coin{}, fmt.Errorf("failed to convert assets to shares: %w", err) } accCurrentShares := vaultShareRecord.Shares.AmountOf(wantAmount.Denom) // Check if account is not withdrawing more shares than they have if accCurrentShares.LT(withdrawShares.Amount) { - return sdkerrors.Wrapf( + return sdk.Coin{}, sdkerrors.Wrapf( types.ErrInsufficientValue, "account has less %s vault shares than withdraw shares, %s < %s", wantAmount.Denom, @@ -64,17 +64,17 @@ func (k *Keeper) Withdraw( // Convert shares to amount to get truncated true share value withdrawAmount, err := k.ConvertToAssets(ctx, withdrawShares) if err != nil { - return fmt.Errorf("failed to convert shares to assets: %w", err) + return sdk.Coin{}, fmt.Errorf("failed to convert shares to assets: %w", err) } accountValue, err := k.GetVaultAccountValue(ctx, wantAmount.Denom, from) if err != nil { - return fmt.Errorf("failed to get account value: %w", err) + return sdk.Coin{}, fmt.Errorf("failed to get account value: %w", err) } // Check if withdrawAmount > account value if withdrawAmount.Amount.GT(accountValue.Amount) { - return sdkerrors.Wrapf( + return sdk.Coin{}, sdkerrors.Wrapf( types.ErrInsufficientValue, "account has less %s vault value than withdraw amount, %s < %s", withdrawAmount.Denom, @@ -86,7 +86,7 @@ func (k *Keeper) Withdraw( // Get the strategy for the vault strategy, err := k.GetStrategy(allowedVault.Strategies[0]) if err != nil { - return err + return sdk.Coin{}, err } // Not necessary to check if amount denom is allowed for the strategy, as @@ -94,7 +94,7 @@ func (k *Keeper) Withdraw( // Withdraw the withdrawAmount from the strategy if err := strategy.Withdraw(ctx, withdrawAmount); err != nil { - return fmt.Errorf("failed to withdraw from strategy: %w", err) + return sdk.Coin{}, fmt.Errorf("failed to withdraw from strategy: %w", err) } // Send coins back to account, must withdraw from strategy first or the @@ -105,7 +105,7 @@ func (k *Keeper) Withdraw( from, sdk.NewCoins(withdrawAmount), ); err != nil { - return err + return sdk.Coin{}, err } // Check if new account balance of shares results in account share value @@ -116,7 +116,7 @@ func (k *Keeper) Withdraw( vaultShareRecord.Shares.GetShare(withdrawAmount.Denom).Sub(withdrawShares), ) if err != nil { - return err + return sdk.Coin{}, err } if isDust { @@ -148,5 +148,21 @@ func (k *Keeper) Withdraw( ), ) - return nil + return withdrawAmount, nil +} + +// WithdrawFromModuleAccount removes the amount of supplied tokens from a vault and transfers it +// back to the module account. The module account must be unblocked from receiving transfers. +func (k *Keeper) WithdrawFromModuleAccount( + ctx sdk.Context, + from string, + wantAmount sdk.Coin, + withdrawStrategy types.StrategyType, +) (sdk.Coin, error) { + // Ensure the module account exists to prevent SendCoins from creating a new non-module account. + acc := k.accountKeeper.GetModuleAccount(ctx, from) + if acc == nil { + return sdk.Coin{}, fmt.Errorf("module account not found: %s", from) + } + return k.Withdraw(ctx, acc.GetAddress(), wantAmount, withdrawStrategy) } diff --git a/x/earn/keeper/withdraw_test.go b/x/earn/keeper/withdraw_test.go index e4db67f1..0bb14a91 100644 --- a/x/earn/keeper/withdraw_test.go +++ b/x/earn/keeper/withdraw_test.go @@ -33,7 +33,7 @@ func (suite *withdrawTestSuite) TestWithdraw_NoVaultRecord() { acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) // Withdraw without having any prior deposits - err := suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) + _, err := suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) suite.Require().Error(err) suite.Require().ErrorIs(err, types.ErrVaultRecordNotFound) @@ -65,7 +65,7 @@ func (suite *withdrawTestSuite) TestWithdraw_NoVaultShareRecord() { acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 1) // Withdraw from acc2 without having any prior deposits - err = suite.Keeper.Withdraw(suite.Ctx, acc2.GetAddress(), acc2WithdrawAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc2.GetAddress(), acc2WithdrawAmount, types.STRATEGY_TYPE_HARD) suite.Require().Error(err) suite.Require().ErrorIs(err, types.ErrVaultShareRecordNotFound) @@ -94,7 +94,7 @@ func (suite *withdrawTestSuite) TestWithdraw_ExceedBalance() { err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) - err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) suite.Require().Error(err) suite.Require().ErrorIs(err, types.ErrInsufficientValue) @@ -119,7 +119,7 @@ func (suite *withdrawTestSuite) TestWithdraw_Zero() { acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) - err := suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) + _, err := suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) suite.Require().Error(err) suite.Require().ErrorIs(err, types.ErrInsufficientAmount) @@ -144,7 +144,7 @@ func (suite *withdrawTestSuite) TestWithdraw_InvalidVault() { acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) - err := suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) + _, err := suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) suite.Require().Error(err) suite.Require().ErrorIs(err, types.ErrInvalidVaultDenom) @@ -169,7 +169,7 @@ func (suite *withdrawTestSuite) TestWithdraw_InvalidStrategy() { acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) - err := suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_SAVINGS) + _, err := suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_SAVINGS) suite.Require().Error(err) suite.Require().ErrorIs(err, types.ErrInvalidVaultStrategy) } @@ -187,7 +187,7 @@ func (suite *withdrawTestSuite) TestWithdraw_FullBalance() { err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) - err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount, types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) // No net changes in balances @@ -214,7 +214,7 @@ func (suite *withdrawTestSuite) TestWithdraw_Partial() { err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) - err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), partialWithdrawAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), partialWithdrawAmount, types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) suite.AccountBalanceEqual( @@ -223,11 +223,11 @@ func (suite *withdrawTestSuite) TestWithdraw_Partial() { ) // Second withdraw for remaining 50 - err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), partialWithdrawAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), partialWithdrawAmount, types.STRATEGY_TYPE_HARD) suite.Require().NoError(err) // No more balance to withdraw - err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), partialWithdrawAmount, types.STRATEGY_TYPE_HARD) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), partialWithdrawAmount, types.STRATEGY_TYPE_HARD) suite.Require().Error(err) suite.Require().ErrorIs(err, types.ErrVaultRecordNotFound, "vault record should be deleted after no more supplied") @@ -265,7 +265,7 @@ func (suite *withdrawTestSuite) TestWithdraw_bKava() { "should be able to deposit bkava derivative denom in bkava vault", ) - err = suite.Keeper.Withdraw(suite.Ctx, acc1.GetAddress(), depositAmount, types.STRATEGY_TYPE_SAVINGS) + _, err = suite.Keeper.Withdraw(suite.Ctx, acc1.GetAddress(), depositAmount, types.STRATEGY_TYPE_SAVINGS) suite.Require().NoError( err, "should be able to withdraw bkava derivative denom from bkava vault", diff --git a/x/earn/types/codec.go b/x/earn/types/codec.go index 38e6eeb3..c4bcabcc 100644 --- a/x/earn/types/codec.go +++ b/x/earn/types/codec.go @@ -7,6 +7,7 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) // RegisterLegacyAminoCodec registers all the necessary types and interfaces for the @@ -14,6 +15,8 @@ import ( func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgDeposit{}, "earn/MsgDeposit", nil) cdc.RegisterConcrete(&MsgWithdraw{}, "earn/MsgWithdraw", nil) + cdc.RegisterConcrete(&CommunityPoolDepositProposal{}, "kava/CommunityPoolDepositProposal", nil) + cdc.RegisterConcrete(&CommunityPoolWithdrawProposal{}, "kava/CommunityPoolWithdrawProposal", nil) } // RegisterInterfaces registers proto messages under their interfaces for unmarshalling, @@ -23,6 +26,10 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { &MsgDeposit{}, &MsgWithdraw{}, ) + registry.RegisterImplementations((*govtypes.Content)(nil), + &CommunityPoolDepositProposal{}, + &CommunityPoolWithdrawProposal{}, + ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } diff --git a/x/earn/types/expected_keepers.go b/x/earn/types/expected_keepers.go index 99dd28da..e2533c88 100644 --- a/x/earn/types/expected_keepers.go +++ b/x/earn/types/expected_keepers.go @@ -3,6 +3,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/types" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" hardtypes "github.com/kava-labs/kava/x/hard/types" savingstypes "github.com/kava-labs/kava/x/savings/types" @@ -25,6 +26,14 @@ type BankKeeper interface { SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error } +// DistributionKeeper defines the expected interface needed for community-pool deposits to earn vaults +type DistributionKeeper interface { + GetFeePool(ctx sdk.Context) (feePool disttypes.FeePool) + SetFeePool(ctx sdk.Context, feePool disttypes.FeePool) + GetDistributionAccount(ctx sdk.Context) types.ModuleAccountI + DistributeFromFeePool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress) error +} + // LiquidKeeper defines the expected interface needed for derivative to staked token conversions. type LiquidKeeper interface { GetStakedTokensForDerivatives(ctx sdk.Context, derivatives sdk.Coins) (sdk.Coin, error) diff --git a/x/earn/types/proposal.go b/x/earn/types/proposal.go new file mode 100644 index 00000000..95800245 --- /dev/null +++ b/x/earn/types/proposal.go @@ -0,0 +1,117 @@ +package types + +import ( + fmt "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +const ( + // ProposalTypeCommunityPoolDeposit defines the type for a CommunityPoolDepositProposal + ProposalTypeCommunityPoolDeposit = "CommunityPoolDeposit" + // ProposalTypeCommunityPoolWithdraw defines the type for a CommunityPoolDepositProposal + ProposalTypeCommunityPoolWithdraw = "CommunityPoolWithdraw" +) + +// Assert CommunityPoolDepositProposal implements govtypes.Content at compile-time +var ( + _ govtypes.Content = &CommunityPoolDepositProposal{} + _ govtypes.Content = &CommunityPoolWithdrawProposal{} +) + +func init() { + govtypes.RegisterProposalType(ProposalTypeCommunityPoolDeposit) + govtypes.RegisterProposalTypeCodec(&CommunityPoolDepositProposal{}, "kava/CommunityPoolDepositProposal") + govtypes.RegisterProposalType(ProposalTypeCommunityPoolWithdraw) + govtypes.RegisterProposalTypeCodec(&CommunityPoolWithdrawProposal{}, "kava/CommunityPoolWithdrawProposal") +} + +// NewCommunityPoolDepositProposal creates a new community pool deposit proposal. +func NewCommunityPoolDepositProposal(title, description string, amount sdk.Coin) *CommunityPoolDepositProposal { + return &CommunityPoolDepositProposal{ + Title: title, + Description: description, + Amount: amount, + } +} + +// GetTitle returns the title of a community pool deposit proposal. +func (cdp *CommunityPoolDepositProposal) GetTitle() string { return cdp.Title } + +// GetDescription returns the description of a community pool deposit proposal. +func (cdp *CommunityPoolDepositProposal) GetDescription() string { return cdp.Description } + +// GetDescription returns the routing key of a community pool deposit proposal. +func (cdp *CommunityPoolDepositProposal) ProposalRoute() string { return RouterKey } + +// ProposalType returns the type of a community pool deposit proposal. +func (cdp *CommunityPoolDepositProposal) ProposalType() string { + return ProposalTypeCommunityPoolDeposit +} + +// String implements fmt.Stringer +func (cdp *CommunityPoolDepositProposal) String() string { + + var b strings.Builder + b.WriteString(fmt.Sprintf(`Community Pool Deposit Proposal: + Title: %s + Description: %s + Amount: %s +`, cdp.Title, cdp.Description, cdp.Amount)) + return b.String() +} + +// ValidateBasic stateless validation of a community pool multi-spend proposal. +func (cdp *CommunityPoolDepositProposal) ValidateBasic() error { + err := govtypes.ValidateAbstract(cdp) + if err != nil { + return err + } + return cdp.Amount.Validate() +} + +// NewCommunityPoolWithdrawProposal creates a new community pool deposit proposal. +func NewCommunityPoolWithdrawProposal(title, description string, amount sdk.Coin) *CommunityPoolWithdrawProposal { + return &CommunityPoolWithdrawProposal{ + Title: title, + Description: description, + Amount: amount, + } +} + +// GetTitle returns the title of a community pool withdraw proposal. +func (cdp *CommunityPoolWithdrawProposal) GetTitle() string { return cdp.Title } + +// GetDescription returns the description of a community pool withdraw proposal. +func (cdp *CommunityPoolWithdrawProposal) GetDescription() string { return cdp.Description } + +// GetDescription returns the routing key of a community pool withdraw proposal. +func (cdp *CommunityPoolWithdrawProposal) ProposalRoute() string { return RouterKey } + +// ProposalType returns the type of a community pool withdraw proposal. +func (cdp *CommunityPoolWithdrawProposal) ProposalType() string { + return ProposalTypeCommunityPoolWithdraw +} + +// String implements fmt.Stringer +func (cdp *CommunityPoolWithdrawProposal) String() string { + + var b strings.Builder + b.WriteString(fmt.Sprintf(`Community Pool Withdraw Proposal: + Title: %s + Description: %s + Amount: %s +`, cdp.Title, cdp.Description, cdp.Amount)) + return b.String() +} + +// ValidateBasic stateless validation of a community pool multi-spend proposal. +func (cdp *CommunityPoolWithdrawProposal) ValidateBasic() error { + err := govtypes.ValidateAbstract(cdp) + if err != nil { + return err + } + return cdp.Amount.Validate() +} diff --git a/x/earn/types/proposal.pb.go b/x/earn/types/proposal.pb.go new file mode 100644 index 00000000..ed39c173 --- /dev/null +++ b/x/earn/types/proposal.pb.go @@ -0,0 +1,1285 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: kava/earn/v1beta1/proposal.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// CommunityPoolDepositProposal deposits from the community pool into an earn vault +type CommunityPoolDepositProposal struct { + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Amount types.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount"` +} + +func (m *CommunityPoolDepositProposal) Reset() { *m = CommunityPoolDepositProposal{} } +func (*CommunityPoolDepositProposal) ProtoMessage() {} +func (*CommunityPoolDepositProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_c374f1a8c57e13e2, []int{0} +} +func (m *CommunityPoolDepositProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommunityPoolDepositProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommunityPoolDepositProposal.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 *CommunityPoolDepositProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommunityPoolDepositProposal.Merge(m, src) +} +func (m *CommunityPoolDepositProposal) XXX_Size() int { + return m.Size() +} +func (m *CommunityPoolDepositProposal) XXX_DiscardUnknown() { + xxx_messageInfo_CommunityPoolDepositProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_CommunityPoolDepositProposal proto.InternalMessageInfo + +// CommunityPoolDepositProposalJSON defines a CommunityPoolDepositProposal with a deposit +type CommunityPoolDepositProposalJSON struct { + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Amount types.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount"` + Deposit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=deposit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"deposit"` +} + +func (m *CommunityPoolDepositProposalJSON) Reset() { *m = CommunityPoolDepositProposalJSON{} } +func (m *CommunityPoolDepositProposalJSON) String() string { return proto.CompactTextString(m) } +func (*CommunityPoolDepositProposalJSON) ProtoMessage() {} +func (*CommunityPoolDepositProposalJSON) Descriptor() ([]byte, []int) { + return fileDescriptor_c374f1a8c57e13e2, []int{1} +} +func (m *CommunityPoolDepositProposalJSON) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommunityPoolDepositProposalJSON) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommunityPoolDepositProposalJSON.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 *CommunityPoolDepositProposalJSON) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommunityPoolDepositProposalJSON.Merge(m, src) +} +func (m *CommunityPoolDepositProposalJSON) XXX_Size() int { + return m.Size() +} +func (m *CommunityPoolDepositProposalJSON) XXX_DiscardUnknown() { + xxx_messageInfo_CommunityPoolDepositProposalJSON.DiscardUnknown(m) +} + +var xxx_messageInfo_CommunityPoolDepositProposalJSON proto.InternalMessageInfo + +// CommunityPoolWithdrawProposal withdraws from an earn vault back to community pool +type CommunityPoolWithdrawProposal struct { + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Amount types.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount"` +} + +func (m *CommunityPoolWithdrawProposal) Reset() { *m = CommunityPoolWithdrawProposal{} } +func (*CommunityPoolWithdrawProposal) ProtoMessage() {} +func (*CommunityPoolWithdrawProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_c374f1a8c57e13e2, []int{2} +} +func (m *CommunityPoolWithdrawProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommunityPoolWithdrawProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommunityPoolWithdrawProposal.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 *CommunityPoolWithdrawProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommunityPoolWithdrawProposal.Merge(m, src) +} +func (m *CommunityPoolWithdrawProposal) XXX_Size() int { + return m.Size() +} +func (m *CommunityPoolWithdrawProposal) XXX_DiscardUnknown() { + xxx_messageInfo_CommunityPoolWithdrawProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_CommunityPoolWithdrawProposal proto.InternalMessageInfo + +// CommunityPoolWithdrawProposalJSON defines a CommunityPoolWithdrawProposal with a deposit +type CommunityPoolWithdrawProposalJSON struct { + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Amount types.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount"` + Deposit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=deposit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"deposit"` +} + +func (m *CommunityPoolWithdrawProposalJSON) Reset() { *m = CommunityPoolWithdrawProposalJSON{} } +func (m *CommunityPoolWithdrawProposalJSON) String() string { return proto.CompactTextString(m) } +func (*CommunityPoolWithdrawProposalJSON) ProtoMessage() {} +func (*CommunityPoolWithdrawProposalJSON) Descriptor() ([]byte, []int) { + return fileDescriptor_c374f1a8c57e13e2, []int{3} +} +func (m *CommunityPoolWithdrawProposalJSON) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommunityPoolWithdrawProposalJSON) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommunityPoolWithdrawProposalJSON.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 *CommunityPoolWithdrawProposalJSON) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommunityPoolWithdrawProposalJSON.Merge(m, src) +} +func (m *CommunityPoolWithdrawProposalJSON) XXX_Size() int { + return m.Size() +} +func (m *CommunityPoolWithdrawProposalJSON) XXX_DiscardUnknown() { + xxx_messageInfo_CommunityPoolWithdrawProposalJSON.DiscardUnknown(m) +} + +var xxx_messageInfo_CommunityPoolWithdrawProposalJSON proto.InternalMessageInfo + +func init() { + proto.RegisterType((*CommunityPoolDepositProposal)(nil), "kava.earn.v1beta1.CommunityPoolDepositProposal") + proto.RegisterType((*CommunityPoolDepositProposalJSON)(nil), "kava.earn.v1beta1.CommunityPoolDepositProposalJSON") + proto.RegisterType((*CommunityPoolWithdrawProposal)(nil), "kava.earn.v1beta1.CommunityPoolWithdrawProposal") + proto.RegisterType((*CommunityPoolWithdrawProposalJSON)(nil), "kava.earn.v1beta1.CommunityPoolWithdrawProposalJSON") +} + +func init() { proto.RegisterFile("kava/earn/v1beta1/proposal.proto", fileDescriptor_c374f1a8c57e13e2) } + +var fileDescriptor_c374f1a8c57e13e2 = []byte{ + // 371 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xc8, 0x4e, 0x2c, 0x4b, + 0xd4, 0x4f, 0x4d, 0x2c, 0xca, 0xd3, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x2f, 0x28, + 0xca, 0x2f, 0xc8, 0x2f, 0x4e, 0xcc, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x04, 0xa9, + 0xd0, 0x03, 0xa9, 0xd0, 0x83, 0xaa, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xea, 0x83, + 0x58, 0x10, 0x85, 0x52, 0x72, 0xc9, 0xf9, 0xc5, 0xb9, 0xf9, 0xc5, 0xfa, 0x49, 0x89, 0xc5, 0xa9, + 0x70, 0xc3, 0x92, 0xf3, 0x33, 0xf3, 0x20, 0xf2, 0x4a, 0x33, 0x19, 0xb9, 0x64, 0x9c, 0xf3, 0x73, + 0x73, 0x4b, 0xf3, 0x32, 0x4b, 0x2a, 0x03, 0xf2, 0xf3, 0x73, 0x5c, 0x52, 0x0b, 0xf2, 0x8b, 0x33, + 0x4b, 0x02, 0xa0, 0xf6, 0x09, 0x89, 0x70, 0xb1, 0x96, 0x64, 0x96, 0xe4, 0xa4, 0x4a, 0x30, 0x2a, + 0x30, 0x6a, 0x70, 0x06, 0x41, 0x38, 0x42, 0x0a, 0x5c, 0xdc, 0x29, 0xa9, 0xc5, 0xc9, 0x45, 0x99, + 0x05, 0x25, 0x99, 0xf9, 0x79, 0x12, 0x4c, 0x60, 0x39, 0x64, 0x21, 0x21, 0x73, 0x2e, 0xb6, 0xc4, + 0xdc, 0xfc, 0xd2, 0xbc, 0x12, 0x09, 0x66, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x49, 0x3d, 0x88, 0x4b, + 0xf4, 0x40, 0x2e, 0x81, 0x39, 0x5a, 0xcf, 0x39, 0x3f, 0x33, 0xcf, 0x89, 0xe5, 0xc4, 0x3d, 0x79, + 0x86, 0x20, 0xa8, 0x72, 0x2b, 0x8e, 0x8e, 0x05, 0xf2, 0x0c, 0x33, 0x16, 0xc8, 0x33, 0x28, 0xb5, + 0x30, 0x71, 0x29, 0xe0, 0x73, 0x9b, 0x57, 0xb0, 0xbf, 0x1f, 0xdd, 0xdd, 0x27, 0x94, 0xca, 0xc5, + 0x9e, 0x02, 0x71, 0x87, 0x04, 0x8b, 0x02, 0x33, 0x7e, 0x9d, 0x06, 0x20, 0x9d, 0xab, 0xee, 0xcb, + 0x6b, 0xa4, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x43, 0x23, 0x04, 0x42, + 0xe9, 0x16, 0xa7, 0x64, 0xeb, 0x97, 0x54, 0x16, 0xa4, 0x16, 0x83, 0x35, 0x14, 0x07, 0xc1, 0xcc, + 0x86, 0x07, 0x03, 0xa3, 0xd2, 0x2c, 0x46, 0x2e, 0x59, 0x94, 0x60, 0x08, 0xcf, 0x2c, 0xc9, 0x48, + 0x29, 0x4a, 0x2c, 0x1f, 0x0c, 0x71, 0xd4, 0xca, 0xc4, 0xa5, 0x88, 0xd7, 0x71, 0x23, 0x23, 0x92, + 0x9c, 0x1c, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, + 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x0d, 0xc9, 0x58, + 0x50, 0xae, 0xd5, 0xcd, 0x49, 0x4c, 0x2a, 0x06, 0xb3, 0xf4, 0x2b, 0x20, 0x79, 0x1c, 0x6c, 0x74, + 0x12, 0x1b, 0x38, 0x43, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xd3, 0xb6, 0x0b, 0x2a, 0xfd, + 0x03, 0x00, 0x00, +} + +func (m *CommunityPoolDepositProposal) 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 *CommunityPoolDepositProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommunityPoolDepositProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProposal(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintProposal(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintProposal(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CommunityPoolDepositProposalJSON) 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 *CommunityPoolDepositProposalJSON) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommunityPoolDepositProposalJSON) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Deposit) > 0 { + for iNdEx := len(m.Deposit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Deposit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProposal(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProposal(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintProposal(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintProposal(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CommunityPoolWithdrawProposal) 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 *CommunityPoolWithdrawProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommunityPoolWithdrawProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProposal(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintProposal(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintProposal(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CommunityPoolWithdrawProposalJSON) 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 *CommunityPoolWithdrawProposalJSON) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommunityPoolWithdrawProposalJSON) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Deposit) > 0 { + for iNdEx := len(m.Deposit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Deposit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProposal(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProposal(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintProposal(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintProposal(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintProposal(dAtA []byte, offset int, v uint64) int { + offset -= sovProposal(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *CommunityPoolDepositProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovProposal(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovProposal(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovProposal(uint64(l)) + return n +} + +func (m *CommunityPoolDepositProposalJSON) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovProposal(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovProposal(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovProposal(uint64(l)) + if len(m.Deposit) > 0 { + for _, e := range m.Deposit { + l = e.Size() + n += 1 + l + sovProposal(uint64(l)) + } + } + return n +} + +func (m *CommunityPoolWithdrawProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovProposal(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovProposal(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovProposal(uint64(l)) + return n +} + +func (m *CommunityPoolWithdrawProposalJSON) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovProposal(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovProposal(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovProposal(uint64(l)) + if len(m.Deposit) > 0 { + for _, e := range m.Deposit { + l = e.Size() + n += 1 + l + sovProposal(uint64(l)) + } + } + return n +} + +func sovProposal(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozProposal(x uint64) (n int) { + return sovProposal(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *CommunityPoolDepositProposal) 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 ErrIntOverflowProposal + } + 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: CommunityPoolDepositProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommunityPoolDepositProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + 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 ErrInvalidLengthProposal + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + 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 ErrInvalidLengthProposal + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProposal + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProposal(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProposal + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommunityPoolDepositProposalJSON) 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 ErrIntOverflowProposal + } + 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: CommunityPoolDepositProposalJSON: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommunityPoolDepositProposalJSON: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + 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 ErrInvalidLengthProposal + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + 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 ErrInvalidLengthProposal + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProposal + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Deposit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProposal + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Deposit = append(m.Deposit, types.Coin{}) + if err := m.Deposit[len(m.Deposit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProposal(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProposal + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommunityPoolWithdrawProposal) 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 ErrIntOverflowProposal + } + 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: CommunityPoolWithdrawProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommunityPoolWithdrawProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + 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 ErrInvalidLengthProposal + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + 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 ErrInvalidLengthProposal + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProposal + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProposal(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProposal + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommunityPoolWithdrawProposalJSON) 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 ErrIntOverflowProposal + } + 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: CommunityPoolWithdrawProposalJSON: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommunityPoolWithdrawProposalJSON: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + 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 ErrInvalidLengthProposal + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + 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 ErrInvalidLengthProposal + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProposal + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Deposit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProposal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProposal + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProposal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Deposit = append(m.Deposit, types.Coin{}) + if err := m.Deposit[len(m.Deposit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProposal(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProposal + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipProposal(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowProposal + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowProposal + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowProposal + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthProposal + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupProposal + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthProposal + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthProposal = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowProposal = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupProposal = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/hard/keeper/interest_test.go b/x/hard/keeper/interest_test.go index fdc20c8c..1c55e267 100644 --- a/x/hard/keeper/interest_test.go +++ b/x/hard/keeper/interest_test.go @@ -445,6 +445,22 @@ func (suite *InterestTestSuite) TestAPYToSPY() { }, false, }, + { + "5% apy", + args{ + apy: sdk.MustNewDecFromStr("1.05"), + expectedValue: sdk.MustNewDecFromStr("1.000000001547125958"), + }, + false, + }, + { + "25% apy", + args{ + apy: sdk.MustNewDecFromStr("1.25"), + expectedValue: sdk.MustNewDecFromStr("1.000000007075835620"), + }, + false, + }, { "medium-high apy", args{ diff --git a/x/kavadist/types/keys.go b/x/kavadist/types/keys.go index 82ed8361..bb246cd3 100644 --- a/x/kavadist/types/keys.go +++ b/x/kavadist/types/keys.go @@ -18,6 +18,9 @@ const ( // KavaDistMacc module account for kavadist KavaDistMacc = ModuleName + + // Treasury + FundModuleAccount = "kava-fund" ) var ( diff --git a/x/router/keeper/msg_server.go b/x/router/keeper/msg_server.go index 72015426..76032968 100644 --- a/x/router/keeper/msg_server.go +++ b/x/router/keeper/msg_server.go @@ -129,12 +129,12 @@ func (m msgServer) WithdrawBurn(goCtx context.Context, msg *types.MsgWithdrawBur return nil, err } - err = m.keeper.earnKeeper.Withdraw(ctx, depositor, tokenAmount, earntypes.STRATEGY_TYPE_SAVINGS) + withdrawnAmount, err := m.keeper.earnKeeper.Withdraw(ctx, depositor, tokenAmount, earntypes.STRATEGY_TYPE_SAVINGS) if err != nil { return nil, err } - _, err = m.keeper.liquidKeeper.BurnDerivative(ctx, depositor, val, tokenAmount) + _, err = m.keeper.liquidKeeper.BurnDerivative(ctx, depositor, val, withdrawnAmount) if err != nil { return nil, err } @@ -169,12 +169,12 @@ func (m msgServer) WithdrawBurnUndelegate(goCtx context.Context, msg *types.MsgW return nil, err } - err = m.keeper.earnKeeper.Withdraw(ctx, depositor, tokenAmount, earntypes.STRATEGY_TYPE_SAVINGS) + withdrawnAmount, err := m.keeper.earnKeeper.Withdraw(ctx, depositor, tokenAmount, earntypes.STRATEGY_TYPE_SAVINGS) if err != nil { return nil, err } - sharesReturned, err := m.keeper.liquidKeeper.BurnDerivative(ctx, depositor, val, tokenAmount) + sharesReturned, err := m.keeper.liquidKeeper.BurnDerivative(ctx, depositor, val, withdrawnAmount) if err != nil { return nil, err } diff --git a/x/router/types/expected_keepers.go b/x/router/types/expected_keepers.go index 4a3dd382..8cc01636 100644 --- a/x/router/types/expected_keepers.go +++ b/x/router/types/expected_keepers.go @@ -30,5 +30,5 @@ type LiquidKeeper interface { type EarnKeeper interface { Deposit(ctx sdk.Context, depositor sdk.AccAddress, amount sdk.Coin, depositStrategy earntypes.StrategyType) error - Withdraw(ctx sdk.Context, from sdk.AccAddress, wantAmount sdk.Coin, withdrawStrategy earntypes.StrategyType) error + Withdraw(ctx sdk.Context, from sdk.AccAddress, wantAmount sdk.Coin, withdrawStrategy earntypes.StrategyType) (sdk.Coin, error) }