mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 00:05:18 +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{},
|
distr.AppModuleBasic{},
|
||||||
gov.NewAppModuleBasic(
|
gov.NewAppModuleBasic(
|
||||||
paramsclient.ProposalHandler, distr.ProposalHandler, committee.ProposalHandler,
|
paramsclient.ProposalHandler, distr.ProposalHandler, committee.ProposalHandler,
|
||||||
upgradeclient.ProposalHandler,
|
upgradeclient.ProposalHandler, kavadist.ProposalHandler,
|
||||||
),
|
),
|
||||||
params.AppModuleBasic{},
|
params.AppModuleBasic{},
|
||||||
crisis.AppModuleBasic{},
|
crisis.AppModuleBasic{},
|
||||||
@ -300,6 +300,14 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
|
|||||||
committeeGovRouter,
|
committeeGovRouter,
|
||||||
app.paramsKeeper,
|
app.paramsKeeper,
|
||||||
)
|
)
|
||||||
|
app.kavadistKeeper = kavadist.NewKeeper(
|
||||||
|
app.cdc,
|
||||||
|
keys[kavadist.StoreKey],
|
||||||
|
kavadistSubspace,
|
||||||
|
app.supplyKeeper,
|
||||||
|
app.distrKeeper,
|
||||||
|
app.ModuleAccountAddrs(),
|
||||||
|
)
|
||||||
|
|
||||||
// create gov keeper with router
|
// create gov keeper with router
|
||||||
govRouter := gov.NewRouter()
|
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(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
|
||||||
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)).
|
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)).
|
||||||
AddRoute(upgrade.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.upgradeKeeper)).
|
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.govKeeper = gov.NewKeeper(
|
||||||
app.cdc,
|
app.cdc,
|
||||||
keys[gov.StoreKey],
|
keys[gov.StoreKey],
|
||||||
@ -365,12 +374,6 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
|
|||||||
app.pricefeedKeeper,
|
app.pricefeedKeeper,
|
||||||
app.auctionKeeper,
|
app.auctionKeeper,
|
||||||
)
|
)
|
||||||
app.kavadistKeeper = kavadist.NewKeeper(
|
|
||||||
app.cdc,
|
|
||||||
keys[kavadist.StoreKey],
|
|
||||||
kavadistSubspace,
|
|
||||||
app.supplyKeeper,
|
|
||||||
)
|
|
||||||
app.incentiveKeeper = incentive.NewKeeper(
|
app.incentiveKeeper = incentive.NewKeeper(
|
||||||
app.cdc,
|
app.cdc,
|
||||||
keys[incentive.StoreKey],
|
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
|
package kavadist
|
||||||
|
|
||||||
// DO NOT EDIT - generated by aliasgen tool (github.com/rhuairahrighairidh/aliasgen)
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/kava-labs/kava/x/kavadist/client"
|
||||||
"github.com/kava-labs/kava/x/kavadist/keeper"
|
"github.com/kava-labs/kava/x/kavadist/keeper"
|
||||||
"github.com/kava-labs/kava/x/kavadist/types"
|
"github.com/kava-labs/kava/x/kavadist/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
EventTypeKavaDist = types.EventTypeKavaDist
|
||||||
AttributeKeyInflation = types.AttributeKeyInflation
|
AttributeKeyInflation = types.AttributeKeyInflation
|
||||||
AttributeKeyStatus = types.AttributeKeyStatus
|
AttributeKeyStatus = types.AttributeKeyStatus
|
||||||
AttributeValueInactive = types.AttributeValueInactive
|
AttributeValueInactive = types.AttributeValueInactive
|
||||||
DefaultParamspace = types.DefaultParamspace
|
|
||||||
EventTypeKavaDist = types.EventTypeKavaDist
|
|
||||||
KavaDistMacc = types.KavaDistMacc
|
|
||||||
ModuleName = types.ModuleName
|
ModuleName = types.ModuleName
|
||||||
QuerierRoute = types.QuerierRoute
|
|
||||||
QueryGetBalance = types.QueryGetBalance
|
|
||||||
QueryGetParams = types.QueryGetParams
|
|
||||||
RouterKey = types.RouterKey
|
|
||||||
StoreKey = types.StoreKey
|
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 (
|
var (
|
||||||
// function aliases
|
// functions 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
|
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
|
// variable aliases
|
||||||
|
ModuleCdc = types.ModuleCdc
|
||||||
|
ErrInvalidProposalAmount = types.ErrInvalidProposalAmount
|
||||||
|
ErrEmptyProposalRecipient = types.ErrEmptyProposalRecipient
|
||||||
CurrentDistPeriodKey = types.CurrentDistPeriodKey
|
CurrentDistPeriodKey = types.CurrentDistPeriodKey
|
||||||
|
PreviousBlockTimeKey = types.PreviousBlockTimeKey
|
||||||
|
KeyActive = types.KeyActive
|
||||||
|
KeyPeriods = types.KeyPeriods
|
||||||
DefaultActive = types.DefaultActive
|
DefaultActive = types.DefaultActive
|
||||||
DefaultPeriods = types.DefaultPeriods
|
DefaultPeriods = types.DefaultPeriods
|
||||||
DefaultPreviousBlockTime = types.DefaultPreviousBlockTime
|
DefaultPreviousBlockTime = types.DefaultPreviousBlockTime
|
||||||
GovDenom = types.GovDenom
|
GovDenom = types.GovDenom
|
||||||
KeyActive = types.KeyActive
|
ProposalHandler = client.ProposalHandler
|
||||||
KeyPeriods = types.KeyPeriods
|
|
||||||
ModuleCdc = types.ModuleCdc
|
|
||||||
PreviousBlockTimeKey = types.PreviousBlockTimeKey
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Keeper = keeper.Keeper
|
|
||||||
GenesisState = types.GenesisState
|
GenesisState = types.GenesisState
|
||||||
Params = types.Params
|
Params = types.Params
|
||||||
Period = types.Period
|
Period = types.Period
|
||||||
Periods = types.Periods
|
Periods = types.Periods
|
||||||
SupplyKeeper = types.SupplyKeeper
|
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
|
package rest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"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
|
// RegisterRoutes registers kavadist-related REST handlers to a router
|
||||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
||||||
registerQueryRoutes(cliCtx, r)
|
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 (
|
import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
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
|
// 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,10 +17,16 @@ type Keeper struct {
|
|||||||
cdc *codec.Codec
|
cdc *codec.Codec
|
||||||
paramSubspace subspace.Subspace
|
paramSubspace subspace.Subspace
|
||||||
supplyKeeper types.SupplyKeeper
|
supplyKeeper types.SupplyKeeper
|
||||||
|
distKeeper types.DistKeeper
|
||||||
|
|
||||||
|
blacklistedAddrs map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKeeper creates a new keeper
|
// 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() {
|
if !paramstore.HasKeyTable() {
|
||||||
paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
|
paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
|
||||||
}
|
}
|
||||||
@ -30,6 +36,8 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace,
|
|||||||
cdc: cdc,
|
cdc: cdc,
|
||||||
paramSubspace: paramstore,
|
paramSubspace: paramstore,
|
||||||
supplyKeeper: sk,
|
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
|
// RegisterCodec registers the necessary types for cdp module
|
||||||
func RegisterCodec(cdc *codec.Codec) {
|
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
|
SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error
|
||||||
MintCoins(ctx sdk.Context, name 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