mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 00:07:51 +00:00 
			
		
		
		
	add community multi-spend proposal (#915)
* feat: add community multi-spend proposal type * feat: add handler for community multi-spend proposals * chore: register new community multi-spend proposal * feat: define client for community multi-spend proposal * fix typos in example cli json * fix: register now proposal type with module codec * fix: register community multi-spend proposal with gov router, not committee * fix: define kavadist keeper before referencing it * nit: include deposit in example proposal * nit: update comment * nit: fix error codes * nit: update comments
This commit is contained in:
		
							parent
							
								
									ffbf31742f
								
							
						
					
					
						commit
						fc85052522
					
				
							
								
								
									
										19
									
								
								app/app.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								app/app.go
									
									
									
									
									
								
							@ -68,7 +68,7 @@ var (
 | 
			
		||||
		distr.AppModuleBasic{},
 | 
			
		||||
		gov.NewAppModuleBasic(
 | 
			
		||||
			paramsclient.ProposalHandler, distr.ProposalHandler, committee.ProposalHandler,
 | 
			
		||||
			upgradeclient.ProposalHandler,
 | 
			
		||||
			upgradeclient.ProposalHandler, kavadist.ProposalHandler,
 | 
			
		||||
		),
 | 
			
		||||
		params.AppModuleBasic{},
 | 
			
		||||
		crisis.AppModuleBasic{},
 | 
			
		||||
@ -300,6 +300,14 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
 | 
			
		||||
		committeeGovRouter,
 | 
			
		||||
		app.paramsKeeper,
 | 
			
		||||
	)
 | 
			
		||||
	app.kavadistKeeper = kavadist.NewKeeper(
 | 
			
		||||
		app.cdc,
 | 
			
		||||
		keys[kavadist.StoreKey],
 | 
			
		||||
		kavadistSubspace,
 | 
			
		||||
		app.supplyKeeper,
 | 
			
		||||
		app.distrKeeper,
 | 
			
		||||
		app.ModuleAccountAddrs(),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// create gov keeper with router
 | 
			
		||||
	govRouter := gov.NewRouter()
 | 
			
		||||
@ -308,7 +316,8 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
 | 
			
		||||
		AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
 | 
			
		||||
		AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)).
 | 
			
		||||
		AddRoute(upgrade.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.upgradeKeeper)).
 | 
			
		||||
		AddRoute(committee.RouterKey, committee.NewProposalHandler(app.committeeKeeper))
 | 
			
		||||
		AddRoute(committee.RouterKey, committee.NewProposalHandler(app.committeeKeeper)).
 | 
			
		||||
		AddRoute(kavadist.RouterKey, kavadist.NewCommunityPoolMultiSpendProposalHandler(app.kavadistKeeper))
 | 
			
		||||
	app.govKeeper = gov.NewKeeper(
 | 
			
		||||
		app.cdc,
 | 
			
		||||
		keys[gov.StoreKey],
 | 
			
		||||
@ -365,12 +374,6 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
 | 
			
		||||
		app.pricefeedKeeper,
 | 
			
		||||
		app.auctionKeeper,
 | 
			
		||||
	)
 | 
			
		||||
	app.kavadistKeeper = kavadist.NewKeeper(
 | 
			
		||||
		app.cdc,
 | 
			
		||||
		keys[kavadist.StoreKey],
 | 
			
		||||
		kavadistSubspace,
 | 
			
		||||
		app.supplyKeeper,
 | 
			
		||||
	)
 | 
			
		||||
	app.incentiveKeeper = incentive.NewKeeper(
 | 
			
		||||
		app.cdc,
 | 
			
		||||
		keys[incentive.StoreKey],
 | 
			
		||||
 | 
			
		||||
@ -1,56 +1,69 @@
 | 
			
		||||
// nolint
 | 
			
		||||
// autogenerated code using github.com/rigelrozanski/multitool
 | 
			
		||||
// aliases generated for the following subdirectories:
 | 
			
		||||
// ALIASGEN: github.com/kava-labs/kava/x/kavadist/types
 | 
			
		||||
// ALIASGEN: github.com/kava-labs/kava/x/kavadist/keeper
 | 
			
		||||
// ALIASGEN: github.com/kava-labs/kava/x/kavadist/client
 | 
			
		||||
package kavadist
 | 
			
		||||
 | 
			
		||||
// DO NOT EDIT - generated by aliasgen tool (github.com/rhuairahrighairidh/aliasgen)
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist/client"
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	AttributeKeyInflation  = types.AttributeKeyInflation
 | 
			
		||||
	AttributeKeyStatus     = types.AttributeKeyStatus
 | 
			
		||||
	AttributeValueInactive = types.AttributeValueInactive
 | 
			
		||||
	DefaultParamspace      = types.DefaultParamspace
 | 
			
		||||
	EventTypeKavaDist      = types.EventTypeKavaDist
 | 
			
		||||
	KavaDistMacc           = types.KavaDistMacc
 | 
			
		||||
	ModuleName             = types.ModuleName
 | 
			
		||||
	QuerierRoute           = types.QuerierRoute
 | 
			
		||||
	QueryGetBalance        = types.QueryGetBalance
 | 
			
		||||
	QueryGetParams         = types.QueryGetParams
 | 
			
		||||
	RouterKey              = types.RouterKey
 | 
			
		||||
	StoreKey               = types.StoreKey
 | 
			
		||||
	EventTypeKavaDist                   = types.EventTypeKavaDist
 | 
			
		||||
	AttributeKeyInflation               = types.AttributeKeyInflation
 | 
			
		||||
	AttributeKeyStatus                  = types.AttributeKeyStatus
 | 
			
		||||
	AttributeValueInactive              = types.AttributeValueInactive
 | 
			
		||||
	ModuleName                          = types.ModuleName
 | 
			
		||||
	StoreKey                            = types.StoreKey
 | 
			
		||||
	RouterKey                           = types.RouterKey
 | 
			
		||||
	QuerierRoute                        = types.QuerierRoute
 | 
			
		||||
	DefaultParamspace                   = types.DefaultParamspace
 | 
			
		||||
	KavaDistMacc                        = types.KavaDistMacc
 | 
			
		||||
	ProposalTypeCommunityPoolMultiSpend = types.ProposalTypeCommunityPoolMultiSpend
 | 
			
		||||
	QueryGetParams                      = types.QueryGetParams
 | 
			
		||||
	QueryGetBalance                     = types.QueryGetBalance
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// function aliases
 | 
			
		||||
	NewKeeper           = keeper.NewKeeper
 | 
			
		||||
	NewQuerier          = keeper.NewQuerier
 | 
			
		||||
	DefaultGenesisState = types.DefaultGenesisState
 | 
			
		||||
	DefaultParams       = types.DefaultParams
 | 
			
		||||
	NewGenesisState     = types.NewGenesisState
 | 
			
		||||
	NewParams           = types.NewParams
 | 
			
		||||
	NewPeriod           = types.NewPeriod
 | 
			
		||||
	ParamKeyTable       = types.ParamKeyTable
 | 
			
		||||
	RegisterCodec       = types.RegisterCodec
 | 
			
		||||
	// functions aliases
 | 
			
		||||
	RegisterCodec                         = types.RegisterCodec
 | 
			
		||||
	NewGenesisState                       = types.NewGenesisState
 | 
			
		||||
	DefaultGenesisState                   = types.DefaultGenesisState
 | 
			
		||||
	NewPeriod                             = types.NewPeriod
 | 
			
		||||
	NewParams                             = types.NewParams
 | 
			
		||||
	DefaultParams                         = types.DefaultParams
 | 
			
		||||
	ParamKeyTable                         = types.ParamKeyTable
 | 
			
		||||
	NewCommunityPoolMultiSpendProposal    = types.NewCommunityPoolMultiSpendProposal
 | 
			
		||||
	NewKeeper                             = keeper.NewKeeper
 | 
			
		||||
	HandleCommunityPoolMultiSpendProposal = keeper.HandleCommunityPoolMultiSpendProposal
 | 
			
		||||
	NewQuerier                            = keeper.NewQuerier
 | 
			
		||||
 | 
			
		||||
	// variable aliases
 | 
			
		||||
	CurrentDistPeriodKey     = types.CurrentDistPeriodKey
 | 
			
		||||
	DefaultActive            = types.DefaultActive
 | 
			
		||||
	DefaultPeriods           = types.DefaultPeriods
 | 
			
		||||
	DefaultPreviousBlockTime = types.DefaultPreviousBlockTime
 | 
			
		||||
	GovDenom                 = types.GovDenom
 | 
			
		||||
	KeyActive                = types.KeyActive
 | 
			
		||||
	KeyPeriods               = types.KeyPeriods
 | 
			
		||||
	ModuleCdc                = types.ModuleCdc
 | 
			
		||||
	PreviousBlockTimeKey     = types.PreviousBlockTimeKey
 | 
			
		||||
	ModuleCdc                 = types.ModuleCdc
 | 
			
		||||
	ErrInvalidProposalAmount  = types.ErrInvalidProposalAmount
 | 
			
		||||
	ErrEmptyProposalRecipient = types.ErrEmptyProposalRecipient
 | 
			
		||||
	CurrentDistPeriodKey      = types.CurrentDistPeriodKey
 | 
			
		||||
	PreviousBlockTimeKey      = types.PreviousBlockTimeKey
 | 
			
		||||
	KeyActive                 = types.KeyActive
 | 
			
		||||
	KeyPeriods                = types.KeyPeriods
 | 
			
		||||
	DefaultActive             = types.DefaultActive
 | 
			
		||||
	DefaultPeriods            = types.DefaultPeriods
 | 
			
		||||
	DefaultPreviousBlockTime  = types.DefaultPreviousBlockTime
 | 
			
		||||
	GovDenom                  = types.GovDenom
 | 
			
		||||
	ProposalHandler           = client.ProposalHandler
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	Keeper       = keeper.Keeper
 | 
			
		||||
	GenesisState = types.GenesisState
 | 
			
		||||
	Params       = types.Params
 | 
			
		||||
	Period       = types.Period
 | 
			
		||||
	Periods      = types.Periods
 | 
			
		||||
	SupplyKeeper = types.SupplyKeeper
 | 
			
		||||
	GenesisState                    = types.GenesisState
 | 
			
		||||
	Params                          = types.Params
 | 
			
		||||
	Period                          = types.Period
 | 
			
		||||
	Periods                         = types.Periods
 | 
			
		||||
	CommunityPoolMultiSpendProposal = types.CommunityPoolMultiSpendProposal
 | 
			
		||||
	MultiSpendRecipient             = types.MultiSpendRecipient
 | 
			
		||||
	MultiSpendRecipients            = types.MultiSpendRecipients
 | 
			
		||||
	Keeper                          = keeper.Keeper
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										93
									
								
								x/kavadist/client/cli/tx.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								x/kavadist/client/cli/tx.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
			
		||||
package cli
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/context"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/codec"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/version"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/gov"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetCmdSubmitProposal implements the command to submit a community-pool multi-spend proposal
 | 
			
		||||
func GetCmdSubmitProposal(cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "community-pool-multi-spend [proposal-file]",
 | 
			
		||||
		Args:  cobra.ExactArgs(1),
 | 
			
		||||
		Short: "Submit a community pool multi-spend proposal",
 | 
			
		||||
		Long: strings.TrimSpace(
 | 
			
		||||
			fmt.Sprintf(`Submit a community pool multi-spend proposal along with an initial deposit.
 | 
			
		||||
The proposal details must be supplied via a JSON file.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
$ %s tx gov submit-proposal community-pool-multi-spend <path/to/proposal.json> --from=<key_or_address>
 | 
			
		||||
 | 
			
		||||
Where proposal.json contains:
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
  "title": "Community Pool Multi-Spend",
 | 
			
		||||
  "description": "Pay many users some KAVA!",
 | 
			
		||||
  "recipient_list": [
 | 
			
		||||
		{
 | 
			
		||||
			"address": "kava1mz2003lathm95n5vnlthmtfvrzrjkrr53j4464",
 | 
			
		||||
			"amount": [
 | 
			
		||||
				{
 | 
			
		||||
					"denom": "ukava",
 | 
			
		||||
					"amount": "1000000"
 | 
			
		||||
				}
 | 
			
		||||
			]
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"address": "kava1zqezafa0luyetvtj8j67g336vaqtuudnsjq7vm",
 | 
			
		||||
			"amount": [
 | 
			
		||||
				{
 | 
			
		||||
					"denom": "ukava",
 | 
			
		||||
					"amount": "1000000"
 | 
			
		||||
				}
 | 
			
		||||
			]
 | 
			
		||||
		}
 | 
			
		||||
	],
 | 
			
		||||
	"deposit": [
 | 
			
		||||
		{
 | 
			
		||||
			"denom": "ukava",
 | 
			
		||||
			"amount": "1000000000"
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
`,
 | 
			
		||||
				version.ClientName,
 | 
			
		||||
			),
 | 
			
		||||
		),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			inBuf := bufio.NewReader(cmd.InOrStdin())
 | 
			
		||||
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
 | 
			
		||||
			cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
 | 
			
		||||
 | 
			
		||||
			proposal, err := ParseCommunityPoolMultiSpendProposalJSON(cdc, args[0])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			from := cliCtx.GetFromAddress()
 | 
			
		||||
			content := types.NewCommunityPoolMultiSpendProposal(proposal.Title, proposal.Description, proposal.RecipientList)
 | 
			
		||||
 | 
			
		||||
			msg := gov.NewMsgSubmitProposal(content, proposal.Deposit, from)
 | 
			
		||||
			if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								x/kavadist/client/cli/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								x/kavadist/client/cli/utils.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
package cli
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/codec"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	// CommunityPoolMultiSpendProposalJSON defines a CommunityPoolMultiSpendProposal with a deposit
 | 
			
		||||
	CommunityPoolMultiSpendProposalJSON struct {
 | 
			
		||||
		Title         string                     `json:"title" yaml:"title"`
 | 
			
		||||
		Description   string                     `json:"description" yaml:"description"`
 | 
			
		||||
		RecipientList types.MultiSpendRecipients `json:"recipient_list" yaml:"recipient_list"`
 | 
			
		||||
		Deposit       sdk.Coins                  `json:"deposit" yaml:"deposit"`
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ParseCommunityPoolMultiSpendProposalJSON reads and parses a CommunityPoolMultiSpendProposalJSON from a file.
 | 
			
		||||
func ParseCommunityPoolMultiSpendProposalJSON(cdc *codec.Codec, proposalFile string) (CommunityPoolMultiSpendProposalJSON, error) {
 | 
			
		||||
	proposal := CommunityPoolMultiSpendProposalJSON{}
 | 
			
		||||
 | 
			
		||||
	contents, err := ioutil.ReadFile(proposalFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return proposal, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := cdc.UnmarshalJSON(contents, &proposal); err != nil {
 | 
			
		||||
		return proposal, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return proposal, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								x/kavadist/client/proposal_handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								x/kavadist/client/proposal_handler.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	govclient "github.com/cosmos/cosmos-sdk/x/gov/client"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist/client/cli"
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist/client/rest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// community-pool multi-spend proposal handler
 | 
			
		||||
var (
 | 
			
		||||
	ProposalHandler = govclient.NewProposalHandler(cli.GetCmdSubmitProposal, rest.ProposalRESTHandler)
 | 
			
		||||
)
 | 
			
		||||
@ -1,12 +1,48 @@
 | 
			
		||||
package rest
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/context"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/types/rest"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/gov"
 | 
			
		||||
	govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RegisterRoutes registers kavadist-related REST handlers to a router
 | 
			
		||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
 | 
			
		||||
	registerQueryRoutes(cliCtx, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProposalRESTHandler returns a ProposalRESTHandler that exposes the community pool multi-spend REST handler with a given sub-route.
 | 
			
		||||
func ProposalRESTHandler(cliCtx context.CLIContext) govrest.ProposalRESTHandler {
 | 
			
		||||
	return govrest.ProposalRESTHandler{
 | 
			
		||||
		SubRoute: types.ProposalTypeCommunityPoolMultiSpend,
 | 
			
		||||
		Handler:  postProposalHandlerFn(cliCtx),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
func postProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var req CommunityPoolMultiSpendProposalReq
 | 
			
		||||
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		req.BaseReq = req.BaseReq.Sanitize()
 | 
			
		||||
		if !req.BaseReq.ValidateBasic(w) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		content := types.NewCommunityPoolMultiSpendProposal(req.Title, req.Description, req.RecipientList)
 | 
			
		||||
		msg := gov.NewMsgSubmitProposal(content, req.Deposit, req.Proposer)
 | 
			
		||||
		if err := msg.ValidateBasic(); err != nil {
 | 
			
		||||
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								x/kavadist/client/rest/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								x/kavadist/client/rest/utils.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
package rest
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/types/rest"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	// CommunityPoolMultiSpendProposalReq defines a community pool multi-spend proposal request body.
 | 
			
		||||
	CommunityPoolMultiSpendProposalReq struct {
 | 
			
		||||
		BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
 | 
			
		||||
 | 
			
		||||
		Title         string                     `json:"title" yaml:"title"`
 | 
			
		||||
		Description   string                     `json:"description" yaml:"description"`
 | 
			
		||||
		RecipientList types.MultiSpendRecipients `json:"recipient_list" yaml:"recipient_list"`
 | 
			
		||||
		Deposit       sdk.Coins                  `json:"deposit" yaml:"deposit"`
 | 
			
		||||
		Proposer      sdk.AccAddress             `json:"proposer" yaml:"proposer"`
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
@ -3,6 +3,9 @@ package kavadist
 | 
			
		||||
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/kavadist/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewHandler creates an sdk.Handler for kavadist messages
 | 
			
		||||
@ -15,3 +18,15 @@ func NewHandler(k Keeper) sdk.Handler {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewCommunityPoolMultiSpendProposalHandler(k Keeper) govtypes.Handler {
 | 
			
		||||
	return func(ctx sdk.Context, content govtypes.Content) error {
 | 
			
		||||
		switch c := content.(type) {
 | 
			
		||||
		case types.CommunityPoolMultiSpendProposal:
 | 
			
		||||
			return keeper.HandleCommunityPoolMultiSpendProposal(ctx, k, c)
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized kavadist proposal content type: %T", c)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -17,19 +17,27 @@ type Keeper struct {
 | 
			
		||||
	cdc           *codec.Codec
 | 
			
		||||
	paramSubspace subspace.Subspace
 | 
			
		||||
	supplyKeeper  types.SupplyKeeper
 | 
			
		||||
	distKeeper    types.DistKeeper
 | 
			
		||||
 | 
			
		||||
	blacklistedAddrs map[string]bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewKeeper creates a new keeper
 | 
			
		||||
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, sk types.SupplyKeeper) Keeper {
 | 
			
		||||
func NewKeeper(
 | 
			
		||||
	cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, sk types.SupplyKeeper,
 | 
			
		||||
	dk types.DistKeeper, blacklistedAddrs map[string]bool,
 | 
			
		||||
) Keeper {
 | 
			
		||||
	if !paramstore.HasKeyTable() {
 | 
			
		||||
		paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Keeper{
 | 
			
		||||
		key:           key,
 | 
			
		||||
		cdc:           cdc,
 | 
			
		||||
		paramSubspace: paramstore,
 | 
			
		||||
		supplyKeeper:  sk,
 | 
			
		||||
		key:              key,
 | 
			
		||||
		cdc:              cdc,
 | 
			
		||||
		paramSubspace:    paramstore,
 | 
			
		||||
		supplyKeeper:     sk,
 | 
			
		||||
		distKeeper:       dk,
 | 
			
		||||
		blacklistedAddrs: blacklistedAddrs,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								x/kavadist/keeper/proposal_handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								x/kavadist/keeper/proposal_handler.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
package keeper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HandleCommunityPoolMultiSpendProposal is a handler for executing a passed community multi-spend proposal
 | 
			
		||||
func HandleCommunityPoolMultiSpendProposal(ctx sdk.Context, k Keeper, p types.CommunityPoolMultiSpendProposal) error {
 | 
			
		||||
	for _, receiverInfo := range p.RecipientList {
 | 
			
		||||
		if k.blacklistedAddrs[receiverInfo.Address.String()] {
 | 
			
		||||
			return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is blacklisted from receiving external funds", receiverInfo.Address)
 | 
			
		||||
		}
 | 
			
		||||
		err := k.distKeeper.DistributeFromFeePool(ctx, receiverInfo.Amount, receiverInfo.Address)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@ -14,4 +14,5 @@ func init() {
 | 
			
		||||
 | 
			
		||||
// RegisterCodec registers the necessary types for cdp module
 | 
			
		||||
func RegisterCodec(cdc *codec.Codec) {
 | 
			
		||||
	cdc.RegisterConcrete(CommunityPoolMultiSpendProposal{}, "kava/CommunityPoolMultiSpendProposal", nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								x/kavadist/types/errors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								x/kavadist/types/errors.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// x/kavadist errors
 | 
			
		||||
var (
 | 
			
		||||
	ErrInvalidProposalAmount  = sdkerrors.Register(ModuleName, 2, "invalid community pool multi-spend proposal amount")
 | 
			
		||||
	ErrEmptyProposalRecipient = sdkerrors.Register(ModuleName, 3, "invalid community pool multi-spend proposal recipient")
 | 
			
		||||
)
 | 
			
		||||
@ -13,3 +13,8 @@ type SupplyKeeper interface {
 | 
			
		||||
	SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error
 | 
			
		||||
	MintCoins(ctx sdk.Context, name string, amt sdk.Coins) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DistKeeper defines the expected distribution keeper interface
 | 
			
		||||
type DistKeeper interface {
 | 
			
		||||
	DistributeFromFeePool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										120
									
								
								x/kavadist/types/proposal.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								x/kavadist/types/proposal.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,120 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// ProposalTypeCommunityPoolMultiSpend defines the type for a CommunityPoolMultiSpendProposal
 | 
			
		||||
	ProposalTypeCommunityPoolMultiSpend = "CommunityPoolMultiSpend"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Assert CommunityPoolMultiSpendProposal implements govtypes.Content at compile-time
 | 
			
		||||
var _ govtypes.Content = CommunityPoolMultiSpendProposal{}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	govtypes.RegisterProposalType(ProposalTypeCommunityPoolMultiSpend)
 | 
			
		||||
	govtypes.RegisterProposalTypeCodec(CommunityPoolMultiSpendProposal{}, "kava/CommunityPoolMultiSpendProposal")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CommunityPoolMultiSpendProposal spends from the community pool by sending to one or more addresses
 | 
			
		||||
type CommunityPoolMultiSpendProposal struct {
 | 
			
		||||
	Title         string               `json:"title" yaml:"title"`
 | 
			
		||||
	Description   string               `json:"description" yaml:"description"`
 | 
			
		||||
	RecipientList MultiSpendRecipients `json:"recipient_list" yaml:"recipient_list"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCommunityPoolMultiSpendProposal creates a new community pool multi-spend proposal.
 | 
			
		||||
func NewCommunityPoolMultiSpendProposal(title, description string, recipientList MultiSpendRecipients) CommunityPoolMultiSpendProposal {
 | 
			
		||||
	return CommunityPoolMultiSpendProposal{
 | 
			
		||||
		Title:         title,
 | 
			
		||||
		Description:   description,
 | 
			
		||||
		RecipientList: recipientList}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTitle returns the title of a community pool multi-spend proposal.
 | 
			
		||||
func (csp CommunityPoolMultiSpendProposal) GetTitle() string { return csp.Title }
 | 
			
		||||
 | 
			
		||||
// GetDescription returns the description of a community pool multi-spend proposal.
 | 
			
		||||
func (csp CommunityPoolMultiSpendProposal) GetDescription() string { return csp.Description }
 | 
			
		||||
 | 
			
		||||
// GetDescription returns the routing key of a community pool multi-spend proposal.
 | 
			
		||||
func (csp CommunityPoolMultiSpendProposal) ProposalRoute() string { return RouterKey }
 | 
			
		||||
 | 
			
		||||
// ProposalType returns the type of a community pool multi-spend proposal.
 | 
			
		||||
func (csp CommunityPoolMultiSpendProposal) ProposalType() string {
 | 
			
		||||
	return ProposalTypeCommunityPoolMultiSpend
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateBasic stateless validation of a community pool multi-spend proposal.
 | 
			
		||||
func (csp CommunityPoolMultiSpendProposal) ValidateBasic() error {
 | 
			
		||||
	err := govtypes.ValidateAbstract(csp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := csp.RecipientList.Validate(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (csp CommunityPoolMultiSpendProposal) String() string {
 | 
			
		||||
	var b strings.Builder
 | 
			
		||||
	b.WriteString(fmt.Sprintf(`Community Pool Multi Spend Proposal:
 | 
			
		||||
  Title:       %s
 | 
			
		||||
  Description: %s
 | 
			
		||||
  Recipient List:   %s
 | 
			
		||||
`, csp.Title, csp.Description, csp.RecipientList))
 | 
			
		||||
	return b.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MultiSpendRecipient defines a recipient and the amount of coins they are receiving
 | 
			
		||||
type MultiSpendRecipient struct {
 | 
			
		||||
	Address sdk.AccAddress `json:"address" yaml:"address"`
 | 
			
		||||
	Amount  sdk.Coins      `json:"amount" yaml:"amount"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate stateless validation of MultiSpendRecipient
 | 
			
		||||
func (msr MultiSpendRecipient) Validate() error {
 | 
			
		||||
	if !msr.Amount.IsValid() {
 | 
			
		||||
		return ErrInvalidProposalAmount
 | 
			
		||||
	}
 | 
			
		||||
	if msr.Address.Empty() {
 | 
			
		||||
		return ErrEmptyProposalRecipient
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (msr MultiSpendRecipient) String() string {
 | 
			
		||||
	return fmt.Sprintf(`Receiver: %s
 | 
			
		||||
	Amount: %s
 | 
			
		||||
	`, msr.Address, msr.Amount)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MultiSpendRecipients slice of MultiSpendRecipient
 | 
			
		||||
type MultiSpendRecipients []MultiSpendRecipient
 | 
			
		||||
 | 
			
		||||
// Validate stateless validation of MultiSpendRecipients
 | 
			
		||||
func (msrs MultiSpendRecipients) Validate() error {
 | 
			
		||||
	for _, msr := range msrs {
 | 
			
		||||
		if err := msr.Validate(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String implements fmt.Stringer
 | 
			
		||||
func (msrs MultiSpendRecipients) String() string {
 | 
			
		||||
	out := ""
 | 
			
		||||
	for _, msr := range msrs {
 | 
			
		||||
		out += msr.String()
 | 
			
		||||
	}
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user