diff --git a/x/validator-vesting/alias.go b/x/validator-vesting/alias.go index dfd168bb..9246d614 100644 --- a/x/validator-vesting/alias.go +++ b/x/validator-vesting/alias.go @@ -1,8 +1,8 @@ // nolint // autogenerated code using github.com/rigelrozanski/multitool // aliases generated for the following subdirectories: -// ALIASGEN: github.com/kava-labs/kava/x/validator-vesting/internal/types/ -// ALIASGEN: github.com/kava-labs/kava/x/validator-vesting/internal/keeper/ +// ALIASGEN: github.com/kava-labs/kava/x/validator-vesting/internal/keeper +// ALIASGEN: github.com/kava-labs/kava/x/validator-vesting/internal/types package validatorvesting import ( @@ -11,35 +11,37 @@ import ( ) const ( - ModuleName = types.ModuleName - StoreKey = types.StoreKey + ModuleName = types.ModuleName + StoreKey = types.StoreKey + QuerierRoute = types.QuerierRoute + QueryCirculatingSupply = types.QueryCirculatingSupply + QueryTotalSupply = types.QueryTotalSupply ) var ( // functions aliases - RegisterCodec = types.RegisterCodec - NewGenesisState = types.NewGenesisState - DefaultGenesisState = types.DefaultGenesisState - ValidateGenesis = types.ValidateGenesis - ValidatorVestingAccountKey = types.ValidatorVestingAccountKey - CreateTestAddrs = types.CreateTestAddrs - TestAddr = types.TestAddr - CreateTestPubKeys = types.CreateTestPubKeys - NewPubKey = types.NewPubKey - NewValidatorVestingAccountRaw = types.NewValidatorVestingAccountRaw - NewValidatorVestingAccount = types.NewValidatorVestingAccount NewKeeper = keeper.NewKeeper + NewQuerier = keeper.NewQuerier MakeTestCodec = keeper.MakeTestCodec CreateTestInput = keeper.CreateTestInput ValidatorVestingTestAccount = keeper.ValidatorVestingTestAccount ValidatorVestingTestAccounts = keeper.ValidatorVestingTestAccounts ValidatorVestingDelegatorTestAccount = keeper.ValidatorVestingDelegatorTestAccount CreateValidators = keeper.CreateValidators + RegisterCodec = types.RegisterCodec + NewGenesisState = types.NewGenesisState + DefaultGenesisState = types.DefaultGenesisState + ValidateGenesis = types.ValidateGenesis + ValidatorVestingAccountKey = types.ValidatorVestingAccountKey + NewBaseQueryParams = types.NewBaseQueryParams + CreateTestAddrs = types.CreateTestAddrs + TestAddr = types.TestAddr + CreateTestPubKeys = types.CreateTestPubKeys + NewPubKey = types.NewPubKey + NewValidatorVestingAccountRaw = types.NewValidatorVestingAccountRaw + NewValidatorVestingAccount = types.NewValidatorVestingAccount // variable aliases - ModuleCdc = types.ModuleCdc - BlocktimeKey = types.BlocktimeKey - ValidatorVestingAccountPrefix = types.ValidatorVestingAccountPrefix ValOpPk1 = keeper.ValOpPk1 ValOpPk2 = keeper.ValOpPk2 ValOpPk3 = keeper.ValOpPk3 @@ -53,12 +55,17 @@ var ( ValConsAddr2 = keeper.ValConsAddr2 ValConsAddr3 = keeper.ValConsAddr3 TestAddrs = keeper.TestAddrs + ModuleCdc = types.ModuleCdc + BlocktimeKey = types.BlocktimeKey + ValidatorVestingAccountPrefix = types.ValidatorVestingAccountPrefix ) type ( + Keeper = keeper.Keeper GenesisState = types.GenesisState + BaseQueryParams = types.BaseQueryParams VestingProgress = types.VestingProgress CurrentPeriodProgress = types.CurrentPeriodProgress ValidatorVestingAccount = types.ValidatorVestingAccount - Keeper = keeper.Keeper + TotalCirculatingSupply = types.TotalCirculatingSupply ) diff --git a/x/validator-vesting/client/cli/query.go b/x/validator-vesting/client/cli/query.go new file mode 100644 index 00000000..1bf19c4a --- /dev/null +++ b/x/validator-vesting/client/cli/query.go @@ -0,0 +1,72 @@ +package cli + +import ( + "fmt" + + "github.com/kava-labs/kava/x/validator-vesting/internal/types" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { + // Group nameservice queries under a subcommand + queryValidatorVestingCmd := &cobra.Command{ + Use: "validator-vesting", + Short: "Querying commands for the validator vesting module", + } + + queryValidatorVestingCmd.AddCommand(client.GetCommands( + QueryCirculatingSupplyCmd(queryRoute, cdc), + QueryTotalSupplyCmd(queryRoute, cdc), + )...) + return queryValidatorVestingCmd +} + +// QueryCirculatingSupplyCmd queries the total circulating supply +func QueryCirculatingSupplyCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "circulating-supply", + Short: "Query circulating supply information", + + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryCirculatingSupply), nil) + if err != nil { + fmt.Printf("could not get total circulating supply\n") + return err + } + + var out sdk.Dec + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(out) + }, + } +} + +// QueryTotalSupplyCmd queries the total supply of ukava +func QueryTotalSupplyCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "total-supply", + Short: "Query total supply information", + + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryTotalSupply), nil) + if err != nil { + fmt.Printf("could not get total supply\n") + return err + } + + var out sdk.Dec + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(out) + }, + } +} diff --git a/x/validator-vesting/client/rest/query.go b/x/validator-vesting/client/rest/query.go new file mode 100644 index 00000000..3dd853c0 --- /dev/null +++ b/x/validator-vesting/client/rest/query.go @@ -0,0 +1,88 @@ +package rest + +import ( + "fmt" + "net/http" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/gorilla/mux" + "github.com/kava-labs/kava/x/validator-vesting/internal/types" +) + +// define routes that get registered by the main application +func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { + + r.HandleFunc("/vesting/circulatingsupply", getCirculatingSupplyHandlerFn(cliCtx)).Methods("GET") + r.HandleFunc("/vesting/totalsupply", getTotalSupplyHandlerFn(cliCtx)).Methods("GET") + +} + +func getTotalSupplyHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + params := types.NewBaseQueryParams(page, limit) + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err)) + return + } + + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTotalSupply) + res, height, err := cliCtx.QueryWithData(route, bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + // directly write output instead of putting in json + w.Write(res) + // rest.PostProcessResponse(w, cliCtx, res) + } +} + +func getCirculatingSupplyHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + params := types.NewBaseQueryParams(page, limit) + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err)) + return + } + + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCirculatingSupply) + res, height, err := cliCtx.QueryWithData(route, bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + // directly write output instead of putting in json + w.Write(res) + // rest.PostProcessResponse(w, cliCtx, res) + } + +} diff --git a/x/validator-vesting/client/rest/rest.go b/x/validator-vesting/client/rest/rest.go new file mode 100644 index 00000000..08620916 --- /dev/null +++ b/x/validator-vesting/client/rest/rest.go @@ -0,0 +1,13 @@ +package rest + +import ( + "github.com/cosmos/cosmos-sdk/client/context" + + "github.com/gorilla/mux" +) + +// RegisterRoutes - Central function to define routes that get registered by the main application +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { + registerQueryRoutes(cliCtx, r) + +} diff --git a/x/validator-vesting/internal/keeper/querier.go b/x/validator-vesting/internal/keeper/querier.go new file mode 100644 index 00000000..f9319008 --- /dev/null +++ b/x/validator-vesting/internal/keeper/querier.go @@ -0,0 +1,74 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" + "github.com/cosmos/cosmos-sdk/x/auth/vesting" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" + "github.com/kava-labs/kava/x/validator-vesting/internal/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// NewQuerier returns a new querier function +func NewQuerier(keeper Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case types.QueryCirculatingSupply: + return queryGetCirculatingSupply(ctx, req, keeper) + case types.QueryTotalSupply: + return queryGetTotalSupply(ctx, req, keeper) + default: + return nil, sdk.ErrUnknownRequest("unknown cdp query endpoint") + } + } +} + +func queryGetTotalSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + totalSupply := keeper.supplyKeeper.GetSupply(ctx).GetTotal().AmountOf("ukava") + supplyDec := sdk.NewDecFromInt(totalSupply).Mul(sdk.MustNewDecFromStr("0.000001")) + bz, err := codec.MarshalJSONIndent(keeper.cdc, supplyDec) + if err != nil { + return nil, sdk.ErrInternal(err.Error()) + } + return bz, nil +} + +func queryGetCirculatingSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + circulatingSupply := sdk.ZeroInt() + keeper.ak.IterateAccounts(ctx, + func(acc authexported.Account) (stop bool) { + // exclude module account + _, ok := acc.(supplyexported.ModuleAccountI) + if ok { + return false + } + + // periodic vesting account + vacc, ok := acc.(vesting.PeriodicVestingAccount) + if ok { + balance := vacc.GetCoins().AmountOf("ukava") + if balance.IsZero() { + return false + } + spendableBalance := vacc.SpendableCoins(ctx.BlockTime()).AmountOf("ukava") + circulatingSupply = circulatingSupply.Add(sdk.MinInt(balance, spendableBalance)) + return false + } + + // base account + bacc, ok := acc.(*auth.BaseAccount) + if ok { + // add all coins + circulatingSupply = circulatingSupply.Add(bacc.GetCoins().AmountOf("ukava")) + } + return false + }) + supplyDec := sdk.NewDecFromInt(circulatingSupply).Mul(sdk.MustNewDecFromStr("0.000001")) + bz, err := codec.MarshalJSONIndent(keeper.cdc, supplyDec) + if err != nil { + return nil, sdk.ErrInternal(err.Error()) + } + return bz, nil +} diff --git a/x/validator-vesting/internal/types/expected_keepers.go b/x/validator-vesting/internal/types/expected_keepers.go index 84b47ca9..74969143 100644 --- a/x/validator-vesting/internal/types/expected_keepers.go +++ b/x/validator-vesting/internal/types/expected_keepers.go @@ -14,6 +14,7 @@ type AccountKeeper interface { GetAccount(sdk.Context, sdk.AccAddress) authexported.Account SetAccount(sdk.Context, authexported.Account) GetAllAccounts(ctx sdk.Context) (accounts []authexported.Account) + IterateAccounts(ctx sdk.Context, cb func(account authexported.Account) (stop bool)) } // BankKeeper defines the expected bank keeper (noalias) @@ -35,4 +36,5 @@ type SupplyKeeper interface { SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error SetModuleAccount(sdk.Context, supplyexported.ModuleAccountI) + GetSupply(ctx sdk.Context) (supply supplyexported.SupplyI) } diff --git a/x/validator-vesting/internal/types/key.go b/x/validator-vesting/internal/types/key.go index 6b198440..c7459c18 100644 --- a/x/validator-vesting/internal/types/key.go +++ b/x/validator-vesting/internal/types/key.go @@ -10,6 +10,9 @@ const ( // StoreKey to be used when creating the KVStore StoreKey = ModuleName + + // QuerierRoute should be set to module name + QuerierRoute = ModuleName ) var ( diff --git a/x/validator-vesting/internal/types/querier.go b/x/validator-vesting/internal/types/querier.go new file mode 100644 index 00000000..a0f25d6a --- /dev/null +++ b/x/validator-vesting/internal/types/querier.go @@ -0,0 +1,17 @@ +package types + +// Querier routes for the validator vesting module +const ( + QueryCirculatingSupply = "circulating-supply" + QueryTotalSupply = "total-supply" +) + +// QueryCirculatingSupplyParams defines the parameters necessary for querying for all Evidence. +type BaseQueryParams struct { + Page int `json:"page" yaml:"page"` + Limit int `json:"limit" yaml:"limit"` +} + +func NewBaseQueryParams(page, limit int) BaseQueryParams { + return BaseQueryParams{Page: page, Limit: limit} +} diff --git a/x/validator-vesting/internal/types/validator_vesting_account.go b/x/validator-vesting/internal/types/validator_vesting_account.go index 19a414fb..b09521a4 100644 --- a/x/validator-vesting/internal/types/validator_vesting_account.go +++ b/x/validator-vesting/internal/types/validator_vesting_account.go @@ -68,6 +68,11 @@ type ValidatorVestingAccount struct { DebtAfterFailedVesting sdk.Coins `json:"debt_after_failed_vesting" yaml:"debt_after_failed_vesting"` } +// TotalCirculatingSupply represents the total circulating supply of Kava +type TotalCirculatingSupply struct { + TotalSupply uint64 `json:"total_supply" yaml:"total_supply"` // total circulating supply +} + // NewValidatorVestingAccountRaw creates a new ValidatorVestingAccount object from BaseVestingAccount func NewValidatorVestingAccountRaw(bva *vestingtypes.BaseVestingAccount, startTime int64, periods vestingtypes.Periods, validatorAddress sdk.ConsAddress, returnAddress sdk.AccAddress, signingThreshold int64) *ValidatorVestingAccount { diff --git a/x/validator-vesting/module.go b/x/validator-vesting/module.go index 075811e9..16a081e4 100644 --- a/x/validator-vesting/module.go +++ b/x/validator-vesting/module.go @@ -13,6 +13,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" sim "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/kava-labs/kava/x/validator-vesting/client/cli" + "github.com/kava-labs/kava/x/validator-vesting/client/rest" "github.com/kava-labs/kava/x/validator-vesting/internal/types" "github.com/kava-labs/kava/x/validator-vesting/simulation" ) @@ -52,13 +54,17 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { } // RegisterRESTRoutes registers no REST routes for the crisis module. -func (AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {} +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + rest.RegisterRoutes(ctx, rtr) +} // GetTxCmd returns no root tx command for the validator-vesting module. func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } // GetQueryCmd returns no root query command for the validator-vesting module. -func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetQueryCmd(StoreKey, cdc) +} // AppModuleSimulation defines the module simulation functions used by the auth module. type AppModuleSimulation struct{} @@ -112,12 +118,12 @@ func (AppModule) NewHandler() sdk.Handler { return nil } // QuerierRoute returns the auth module's querier route name. func (AppModule) QuerierRoute() string { - return "" + return ModuleName } // NewQuerierHandler returns the auth module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { - return nil + return NewQuerier(am.keeper) } // InitGenesis performs genesis initialization for the auth module. It returns