add x/kavamint & x/community modules (#1400)

* add `x/kavamint` module (#1376)

* stub out the x/kavamint module

* mint staking rewards in x/kavamint BeginBlocker

* calculate cumulative inflation from x/kavamint

* cleanup, abstract bond denom, grpc query tests

* store & use previous block time to calculate mints

* move test suite to testutil

* add test of x/kavamint BeginBlocker

* cleanup & comments from @DracoLi review

* fix x/incentive integration test

* remove rest client grpc test

* add x/kavamint module spec

* track previous block time in genesis

* suggested cleanup from PR review

* bugfix incorrect module account check

* add x/community module (#1388)

* add x/community module

* refactor and setup fund-community-pool tx

* remove unused features (params, genesis state)

* test x/community query and message

* update with master branch deps

* updates from review

* indicate deprecated methods on module (still needed to fulfill interface)
* remove legacy querier
* derive msg Route() and Type() from sdk.MsgTypeURL(&msg)

* use module name for message Route()

* mint community pool inflation in x/kavamint (#1389)

* mint community pool inflation in x/kavamint

* refactor x/kavamint abci inflation test

* test inflation from x/kavamint

* default kavamint rates to 0%

* rename kavamint account name variables

* panic if genesis kavamint previous block time not set

* emit event on kavamint begin blocker

* add kavamint year of inflation sanity check test

* update fund_community_pool message event

* add x/community spec

* refactor inflation accumulation into single func

* use new comm pool in kavadist multispend proposal (#1403)

the x/community module houses the new community pool. this commit
points the CommunityPoolMultiSpendProposal at the new community pool
so that multispend proposals can continue to be processed once original
fee pool funds are move to x/community

* updates from proto lint changes

* update earn proposals to use new community pool

originally the CommunityPoolDepositProposal & CommunityPoolWithdrawProposal
used the vanilla community pool fee pool. this commit updates them both
to use the new x/community module account.

* spec formatting update

* add x/community & x/kavamint to internal testnet genesis

* increase test coverage of x/kavamint

* increase test coverage of x/community

* use string type for sdk.Dec proto fields; disable generation of getters
for genesis and params (and other store objects)

* follow sdk conventions for params endpoint naming

* add kavamint to swagger; update swagger & proto docs

* remove legacy querier; touch up module to match others

* reduce max minting rate; refactor genesis to allow nil/zero block time;
repace proto stringers and remove getters; keep sdk.Dec values strings
in protos; increase validation of sdk.Dec values in params; increase
coverage; add module account permission checks; ensure import and export
of genesis does not change state

* fix scaling of rate values -- should be yearly rates

* revert key change

* fix typo in test name

* fix regression in running tests -- rename method

Co-authored-by: Nick DeLuca <nickdeluca08@gmail.com>
This commit is contained in:
Robert Pirtle 2022-12-09 13:24:35 -08:00 committed by GitHub
parent aa5e9f6382
commit f37321839b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 7147 additions and 67 deletions

View File

@ -106,6 +106,9 @@ import (
committeeclient "github.com/kava-labs/kava/x/committee/client" committeeclient "github.com/kava-labs/kava/x/committee/client"
committeekeeper "github.com/kava-labs/kava/x/committee/keeper" committeekeeper "github.com/kava-labs/kava/x/committee/keeper"
committeetypes "github.com/kava-labs/kava/x/committee/types" committeetypes "github.com/kava-labs/kava/x/committee/types"
"github.com/kava-labs/kava/x/community"
communitykeeper "github.com/kava-labs/kava/x/community/keeper"
communitytypes "github.com/kava-labs/kava/x/community/types"
earn "github.com/kava-labs/kava/x/earn" earn "github.com/kava-labs/kava/x/earn"
earnclient "github.com/kava-labs/kava/x/earn/client" earnclient "github.com/kava-labs/kava/x/earn/client"
earnkeeper "github.com/kava-labs/kava/x/earn/keeper" earnkeeper "github.com/kava-labs/kava/x/earn/keeper"
@ -126,6 +129,9 @@ import (
kavadistclient "github.com/kava-labs/kava/x/kavadist/client" kavadistclient "github.com/kava-labs/kava/x/kavadist/client"
kavadistkeeper "github.com/kava-labs/kava/x/kavadist/keeper" kavadistkeeper "github.com/kava-labs/kava/x/kavadist/keeper"
kavadisttypes "github.com/kava-labs/kava/x/kavadist/types" kavadisttypes "github.com/kava-labs/kava/x/kavadist/types"
"github.com/kava-labs/kava/x/kavamint"
kavamintkeeper "github.com/kava-labs/kava/x/kavamint/keeper"
kavaminttypes "github.com/kava-labs/kava/x/kavamint/types"
"github.com/kava-labs/kava/x/liquid" "github.com/kava-labs/kava/x/liquid"
liquidkeeper "github.com/kava-labs/kava/x/liquid/keeper" liquidkeeper "github.com/kava-labs/kava/x/liquid/keeper"
liquidtypes "github.com/kava-labs/kava/x/liquid/types" liquidtypes "github.com/kava-labs/kava/x/liquid/types"
@ -202,6 +208,8 @@ var (
liquid.AppModuleBasic{}, liquid.AppModuleBasic{},
earn.AppModuleBasic{}, earn.AppModuleBasic{},
router.AppModuleBasic{}, router.AppModuleBasic{},
kavamint.AppModuleBasic{},
community.AppModuleBasic{},
) )
// module account permissions // module account permissions
@ -229,6 +237,8 @@ var (
liquidtypes.ModuleAccountName: {authtypes.Minter, authtypes.Burner}, liquidtypes.ModuleAccountName: {authtypes.Minter, authtypes.Burner},
earntypes.ModuleAccountName: nil, earntypes.ModuleAccountName: nil,
kavadisttypes.FundModuleAccount: nil, kavadisttypes.FundModuleAccount: nil,
kavaminttypes.ModuleAccountName: {authtypes.Minter},
communitytypes.ModuleName: nil,
} }
) )
@ -300,6 +310,8 @@ type App struct {
liquidKeeper liquidkeeper.Keeper liquidKeeper liquidkeeper.Keeper
earnKeeper earnkeeper.Keeper earnKeeper earnkeeper.Keeper
routerKeeper routerkeeper.Keeper routerKeeper routerkeeper.Keeper
kavamintKeeper kavamintkeeper.Keeper
communityKeeper communitykeeper.Keeper
// make scoped keepers public for test purposes // make scoped keepers public for test purposes
ScopedIBCKeeper capabilitykeeper.ScopedKeeper ScopedIBCKeeper capabilitykeeper.ScopedKeeper
@ -353,7 +365,7 @@ func NewApp(
issuancetypes.StoreKey, bep3types.StoreKey, pricefeedtypes.StoreKey, issuancetypes.StoreKey, bep3types.StoreKey, pricefeedtypes.StoreKey,
swaptypes.StoreKey, cdptypes.StoreKey, hardtypes.StoreKey, swaptypes.StoreKey, cdptypes.StoreKey, hardtypes.StoreKey,
committeetypes.StoreKey, incentivetypes.StoreKey, evmutiltypes.StoreKey, committeetypes.StoreKey, incentivetypes.StoreKey, evmutiltypes.StoreKey,
savingstypes.StoreKey, earntypes.StoreKey, savingstypes.StoreKey, earntypes.StoreKey, kavaminttypes.StoreKey,
) )
tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey)
memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)
@ -399,6 +411,7 @@ func NewApp(
evmSubspace := app.paramsKeeper.Subspace(evmtypes.ModuleName) evmSubspace := app.paramsKeeper.Subspace(evmtypes.ModuleName)
evmutilSubspace := app.paramsKeeper.Subspace(evmutiltypes.ModuleName) evmutilSubspace := app.paramsKeeper.Subspace(evmutiltypes.ModuleName)
earnSubspace := app.paramsKeeper.Subspace(earntypes.ModuleName) earnSubspace := app.paramsKeeper.Subspace(earntypes.ModuleName)
kavamintSubspace := app.paramsKeeper.Subspace(kavaminttypes.ModuleName)
bApp.SetParamStore( bApp.SetParamStore(
app.paramsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramskeeper.ConsensusParamsKeyTable()), app.paramsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramskeeper.ConsensusParamsKeyTable()),
@ -511,13 +524,17 @@ func NewApp(
app.evmutilKeeper.SetEvmKeeper(app.evmKeeper) app.evmutilKeeper.SetEvmKeeper(app.evmKeeper)
app.communityKeeper = communitykeeper.NewKeeper(
app.accountKeeper,
app.bankKeeper,
)
app.kavadistKeeper = kavadistkeeper.NewKeeper( app.kavadistKeeper = kavadistkeeper.NewKeeper(
appCodec, appCodec,
keys[kavadisttypes.StoreKey], keys[kavadisttypes.StoreKey],
kavadistSubspace, kavadistSubspace,
app.bankKeeper, app.bankKeeper,
app.accountKeeper, app.accountKeeper,
app.distrKeeper, app.communityKeeper,
app.loadBlockedMaccAddrs(), app.loadBlockedMaccAddrs(),
) )
@ -617,7 +634,17 @@ func NewApp(
&app.liquidKeeper, &app.liquidKeeper,
&hardKeeper, &hardKeeper,
&savingsKeeper, &savingsKeeper,
app.distrKeeper, communitytypes.ModuleAccountName,
)
app.kavamintKeeper = kavamintkeeper.NewKeeper(
appCodec,
keys[kavaminttypes.StoreKey],
kavamintSubspace,
app.stakingKeeper,
app.accountKeeper,
app.bankKeeper,
authtypes.FeeCollectorName, // same fee collector as vanilla sdk
communitytypes.ModuleAccountName,
) )
app.incentiveKeeper = incentivekeeper.NewKeeper( app.incentiveKeeper = incentivekeeper.NewKeeper(
@ -739,6 +766,8 @@ func NewApp(
liquid.NewAppModule(app.liquidKeeper), liquid.NewAppModule(app.liquidKeeper),
earn.NewAppModule(app.earnKeeper, app.accountKeeper, app.bankKeeper), earn.NewAppModule(app.earnKeeper, app.accountKeeper, app.bankKeeper),
router.NewAppModule(app.routerKeeper), router.NewAppModule(app.routerKeeper),
kavamint.NewAppModule(appCodec, app.kavamintKeeper, app.accountKeeper),
community.NewAppModule(app.communityKeeper, app.accountKeeper),
) )
// Warning: Some begin blockers must run before others. Ensure the dependencies are understood before modifying this list. // Warning: Some begin blockers must run before others. Ensure the dependencies are understood before modifying this list.
@ -751,6 +780,9 @@ func NewApp(
// Run before to ensure params are updated together before state changes. // Run before to ensure params are updated together before state changes.
committeetypes.ModuleName, committeetypes.ModuleName,
minttypes.ModuleName, minttypes.ModuleName,
// Kavamint registers with the vanilla mint module.
// Must be run before distribution module in order to generate block staking rewards.
kavaminttypes.ModuleName,
distrtypes.ModuleName, distrtypes.ModuleName,
// During begin block slashing happens after distr.BeginBlocker so that // During begin block slashing happens after distr.BeginBlocker so that
// there is nothing left over in the validator fee pool, so as to keep the // there is nothing left over in the validator fee pool, so as to keep the
@ -761,6 +793,7 @@ func NewApp(
feemarkettypes.ModuleName, feemarkettypes.ModuleName,
evmtypes.ModuleName, evmtypes.ModuleName,
kavadisttypes.ModuleName, kavadisttypes.ModuleName,
communitytypes.ModuleName,
// Auction begin blocker will close out expired auctions and pay debt back to cdp. // Auction begin blocker will close out expired auctions and pay debt back to cdp.
// It should be run before cdp begin blocker which cancels out debt with stable and starts more auctions. // It should be run before cdp begin blocker which cancels out debt with stable and starts more auctions.
auctiontypes.ModuleName, auctiontypes.ModuleName,
@ -829,6 +862,8 @@ func NewApp(
liquidtypes.ModuleName, liquidtypes.ModuleName,
earntypes.ModuleName, earntypes.ModuleName,
routertypes.ModuleName, routertypes.ModuleName,
kavaminttypes.ModuleName,
communitytypes.ModuleName,
) )
// Warning: Some init genesis methods must run before others. Ensure the dependencies are understood before modifying this list // Warning: Some init genesis methods must run before others. Ensure the dependencies are understood before modifying this list
@ -841,6 +876,7 @@ func NewApp(
slashingtypes.ModuleName, // iterates over validators, run after staking slashingtypes.ModuleName, // iterates over validators, run after staking
govtypes.ModuleName, govtypes.ModuleName,
minttypes.ModuleName, minttypes.ModuleName,
kavaminttypes.ModuleName,
ibchost.ModuleName, ibchost.ModuleName,
evidencetypes.ModuleName, evidencetypes.ModuleName,
authz.ModuleName, authz.ModuleName,
@ -860,6 +896,7 @@ func NewApp(
committeetypes.ModuleName, committeetypes.ModuleName,
evmutiltypes.ModuleName, evmutiltypes.ModuleName,
earntypes.ModuleName, earntypes.ModuleName,
communitytypes.ModuleName,
genutiltypes.ModuleName, // runs arbitrary txs included in genisis state, so run after modules have been initialized genutiltypes.ModuleName, // runs arbitrary txs included in genisis state, so run after modules have been initialized
crisistypes.ModuleName, // runs the invariants at genesis, should run after other modules crisistypes.ModuleName, // runs the invariants at genesis, should run after other modules
// Add all remaining modules with an empty InitGenesis below since cosmos 0.45.0 requires it // Add all remaining modules with an empty InitGenesis below since cosmos 0.45.0 requires it

View File

@ -8,6 +8,7 @@ import (
"github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
@ -22,6 +23,7 @@ import (
paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper"
slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
@ -34,12 +36,14 @@ import (
bep3keeper "github.com/kava-labs/kava/x/bep3/keeper" bep3keeper "github.com/kava-labs/kava/x/bep3/keeper"
cdpkeeper "github.com/kava-labs/kava/x/cdp/keeper" cdpkeeper "github.com/kava-labs/kava/x/cdp/keeper"
committeekeeper "github.com/kava-labs/kava/x/committee/keeper" committeekeeper "github.com/kava-labs/kava/x/committee/keeper"
communitykeeper "github.com/kava-labs/kava/x/community/keeper"
earnkeeper "github.com/kava-labs/kava/x/earn/keeper" earnkeeper "github.com/kava-labs/kava/x/earn/keeper"
evmutilkeeper "github.com/kava-labs/kava/x/evmutil/keeper" evmutilkeeper "github.com/kava-labs/kava/x/evmutil/keeper"
hardkeeper "github.com/kava-labs/kava/x/hard/keeper" hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
incentivekeeper "github.com/kava-labs/kava/x/incentive/keeper" incentivekeeper "github.com/kava-labs/kava/x/incentive/keeper"
issuancekeeper "github.com/kava-labs/kava/x/issuance/keeper" issuancekeeper "github.com/kava-labs/kava/x/issuance/keeper"
kavadistkeeper "github.com/kava-labs/kava/x/kavadist/keeper" kavadistkeeper "github.com/kava-labs/kava/x/kavadist/keeper"
kavamintkeeper "github.com/kava-labs/kava/x/kavamint/keeper"
liquidkeeper "github.com/kava-labs/kava/x/liquid/keeper" liquidkeeper "github.com/kava-labs/kava/x/liquid/keeper"
pricefeedkeeper "github.com/kava-labs/kava/x/pricefeed/keeper" pricefeedkeeper "github.com/kava-labs/kava/x/pricefeed/keeper"
routerkeeper "github.com/kava-labs/kava/x/router/keeper" routerkeeper "github.com/kava-labs/kava/x/router/keeper"
@ -65,7 +69,7 @@ var (
// However this leads to a lot of duplicated logic similar to InitGenesis methods. // However this leads to a lot of duplicated logic similar to InitGenesis methods.
// So TestApp.InitializeFromGenesisStates() will call InitGenesis with the default genesis state. // So TestApp.InitializeFromGenesisStates() will call InitGenesis with the default genesis state.
// and TestApp.InitializeFromGenesisStates(authState, cdpState) will do the same but overwrite the auth and cdp sections of the default genesis state // and TestApp.InitializeFromGenesisStates(authState, cdpState) will do the same but overwrite the auth and cdp sections of the default genesis state
// Creating the genesis states can be combersome, but helper methods can make it easier such as NewAuthGenStateFromAccounts below. // Creating the genesis states can be cumbersome, but helper methods can make it easier such as NewAuthGenStateFromAccounts below.
type TestApp struct { type TestApp struct {
App App
} }
@ -117,6 +121,8 @@ func (tApp TestApp) GetFeeMarketKeeper() feemarketkeeper.Keeper { return tApp.fe
func (tApp TestApp) GetLiquidKeeper() liquidkeeper.Keeper { return tApp.liquidKeeper } func (tApp TestApp) GetLiquidKeeper() liquidkeeper.Keeper { return tApp.liquidKeeper }
func (tApp TestApp) GetEarnKeeper() earnkeeper.Keeper { return tApp.earnKeeper } func (tApp TestApp) GetEarnKeeper() earnkeeper.Keeper { return tApp.earnKeeper }
func (tApp TestApp) GetRouterKeeper() routerkeeper.Keeper { return tApp.routerKeeper } func (tApp TestApp) GetRouterKeeper() routerkeeper.Keeper { return tApp.routerKeeper }
func (tApp TestApp) GetKavamintKeeper() kavamintkeeper.Keeper { return tApp.kavamintKeeper }
func (tApp TestApp) GetCommunityKeeper() communitykeeper.Keeper { return tApp.communityKeeper }
func (tApp TestApp) GetKeys() map[string]*sdk.KVStoreKey { func (tApp TestApp) GetKeys() map[string]*sdk.KVStoreKey {
return tApp.keys return tApp.keys
@ -197,6 +203,13 @@ func (tApp TestApp) CheckBalance(t *testing.T, ctx sdk.Context, owner sdk.AccAdd
require.Equal(t, expectedCoins, coins) require.Equal(t, expectedCoins, coins)
} }
// GetModuleAccountBalance gets the current balance of the denom for a module account
func (tApp TestApp) GetModuleAccountBalance(ctx sdk.Context, moduleName string, denom string) sdk.Int {
moduleAcc := tApp.accountKeeper.GetModuleAccount(ctx, moduleName)
balance := tApp.bankKeeper.GetBalance(ctx, moduleAcc.GetAddress(), denom)
return balance.Amount
}
// FundAccount is a utility function that funds an account by minting and sending the coins to the address. // FundAccount is a utility function that funds an account by minting and sending the coins to the address.
func (tApp TestApp) FundAccount(ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins) error { func (tApp TestApp) FundAccount(ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins) error {
if err := tApp.bankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts); err != nil { if err := tApp.bankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts); err != nil {
@ -220,6 +233,26 @@ func (tApp TestApp) FundModuleAccount(ctx sdk.Context, recipientMod string, amou
return tApp.bankKeeper.SendCoinsFromModuleToModule(ctx, minttypes.ModuleName, recipientMod, amounts) return tApp.bankKeeper.SendCoinsFromModuleToModule(ctx, minttypes.ModuleName, recipientMod, amounts)
} }
// CreateNewUnbondedValidator creates a new validator in the staking module.
// New validators are unbonded until the end blocker is run.
func (tApp TestApp) CreateNewUnbondedValidator(ctx sdk.Context, valAddress sdk.ValAddress, selfDelegation sdk.Int) error {
msg, err := stakingtypes.NewMsgCreateValidator(
valAddress,
ed25519.GenPrivKey().PubKey(),
sdk.NewCoin(tApp.stakingKeeper.BondDenom(ctx), selfDelegation),
stakingtypes.Description{},
stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
sdk.NewInt(1e6),
)
if err != nil {
return err
}
msgServer := stakingkeeper.NewMsgServerImpl(tApp.stakingKeeper)
_, err = msgServer.CreateValidator(sdk.WrapSDKContext(ctx), msg)
return err
}
// GeneratePrivKeyAddressPairsFromRand generates (deterministically) a total of n private keys and addresses. // GeneratePrivKeyAddressPairsFromRand generates (deterministically) a total of n private keys and addresses.
func GeneratePrivKeyAddressPairs(n int) (keys []cryptotypes.PrivKey, addrs []sdk.AccAddress) { func GeneratePrivKeyAddressPairs(n int) (keys []cryptotypes.PrivKey, addrs []sdk.AccAddress) {
r := rand.New(rand.NewSource(12345)) // make the generation deterministic r := rand.New(rand.NewSource(12345)) // make the generation deterministic

View File

@ -933,6 +933,9 @@
"proposals": [], "proposals": [],
"votes": [] "votes": []
}, },
"community": {
"params": {}
},
"crisis": { "crisis": {
"constant_fee": { "constant_fee": {
"denom": "ukava", "denom": "ukava",
@ -941,7 +944,7 @@
}, },
"distribution": { "distribution": {
"params": { "params": {
"community_tax": "0.020000000000000000", "community_tax": "0.000000000000000000",
"base_proposer_reward": "0.010000000000000000", "base_proposer_reward": "0.010000000000000000",
"bonus_proposer_reward": "0.040000000000000000", "bonus_proposer_reward": "0.040000000000000000",
"withdraw_addr_enabled": true "withdraw_addr_enabled": true
@ -2306,19 +2309,26 @@
}, },
"previous_block_time": "1970-01-01T00:00:01Z" "previous_block_time": "1970-01-01T00:00:01Z"
}, },
"kavamint": {
"params": {
"community_pool_inflation": "0.200000000000000000",
"staking_rewards_apy": "0.150000000000000000"
},
"previous_block_time": "2022-05-25T17:00:00Z"
},
"liquid": { "liquid": {
"params": {} "params": {}
}, },
"mint": { "mint": {
"minter": { "minter": {
"inflation": "0.130000000000000000", "inflation": "0.000000000000000000",
"annual_provisions": "0.000000000000000000" "annual_provisions": "0.000000000000000000"
}, },
"params": { "params": {
"mint_denom": "ukava", "mint_denom": "ukava",
"inflation_rate_change": "0.130000000000000000", "inflation_rate_change": "0.130000000000000000",
"inflation_max": "0.200000000000000000", "inflation_max": "0.000000000000000000",
"inflation_min": "0.070000000000000000", "inflation_min": "0.000000000000000000",
"goal_bonded": "0.670000000000000000", "goal_bonded": "0.670000000000000000",
"blocks_per_year": "6311520" "blocks_per_year": "6311520"
} }

View File

@ -216,6 +216,23 @@
] ]
} }
}, },
{
"url": "./out/swagger/kava/kavamint/v1beta1/query.swagger.json",
"tags": {
"rename": {
"Query": "Kavamint"
}
},
"operationIds": {
"rename": [
{
"type": "regex",
"from": "(.*)",
"to": "Kavamint$1"
}
]
}
},
{ {
"url": "./client/docs/cosmos-swagger.yml", "url": "./client/docs/cosmos-swagger.yml",
"dereference": { "dereference": {

View File

@ -12033,6 +12033,101 @@ paths:
format: byte format: byte
tags: tags:
- Savings - Savings
/kava/kavamint/v1beta1/inflation:
get:
summary: >-
Inflation queries x/kavamint for the overall cumulative inflation rate
of KAVA.
operationId: KavamintInflation
responses:
'200':
description: A successful response.
schema:
type: object
properties:
inflation:
type: string
title: |-
inflation is the current minting inflation value.
example "0.990000000000000000" - 99% inflation
description: >-
QueryInflationResponse is the response type for the
Query/Inflation RPC method.
default:
description: An unexpected error response.
schema:
type: object
properties:
error:
type: string
code:
type: integer
format: int32
message:
type: string
details:
type: array
items:
type: object
properties:
type_url:
type: string
value:
type: string
format: byte
tags:
- Kavamint
/kava/kavamint/v1beta1/params:
get:
summary: Params queries the parameters of x/kavamint module.
operationId: KavamintParams
responses:
'200':
description: A successful response.
schema:
type: object
properties:
params:
type: object
properties:
community_pool_inflation:
type: string
description: >-
yearly inflation of total token supply minted to the
community pool.
staking_rewards_apy:
type: string
description: >-
yearly inflation of bonded tokens minted for staking
rewards to validators.
title: Params wraps the governance parameters for the kavamint module
description: >-
QueryParamsResponse defines the response type for querying
x/kavamint parameters.
default:
description: An unexpected error response.
schema:
type: object
properties:
error:
type: string
code:
type: integer
format: int32
message:
type: string
details:
type: array
items:
type: object
properties:
type_url:
type: string
value:
type: string
format: byte
tags:
- Kavamint
/node_info: /node_info:
get: get:
description: Information about the connected node description: Information about the connected node
@ -57671,6 +57766,49 @@ definitions:
description: >- description: >-
TotalSupplyResponse defines the response type for the Query/TotalSupply TotalSupplyResponse defines the response type for the Query/TotalSupply
method. method.
kava.kavamint.v1beta1.Params:
type: object
properties:
community_pool_inflation:
type: string
description: yearly inflation of total token supply minted to the community pool.
staking_rewards_apy:
type: string
description: >-
yearly inflation of bonded tokens minted for staking rewards to
validators.
title: Params wraps the governance parameters for the kavamint module
kava.kavamint.v1beta1.QueryInflationResponse:
type: object
properties:
inflation:
type: string
title: |-
inflation is the current minting inflation value.
example "0.990000000000000000" - 99% inflation
description: >-
QueryInflationResponse is the response type for the Query/Inflation RPC
method.
kava.kavamint.v1beta1.QueryParamsResponse:
type: object
properties:
params:
type: object
properties:
community_pool_inflation:
type: string
description: >-
yearly inflation of total token supply minted to the community
pool.
staking_rewards_apy:
type: string
description: >-
yearly inflation of bonded tokens minted for staking rewards to
validators.
title: Params wraps the governance parameters for the kavamint module
description: >-
QueryParamsResponse defines the response type for querying x/kavamint
parameters.
CheckTxResult: CheckTxResult:
type: object type: object
properties: properties:

View File

@ -178,6 +178,18 @@
- [Msg](#kava.committee.v1beta1.Msg) - [Msg](#kava.committee.v1beta1.Msg)
- [kava/community/v1beta1/query.proto](#kava/community/v1beta1/query.proto)
- [QueryBalanceRequest](#kava.community.v1beta1.QueryBalanceRequest)
- [QueryBalanceResponse](#kava.community.v1beta1.QueryBalanceResponse)
- [Query](#kava.community.v1beta1.Query)
- [kava/community/v1beta1/tx.proto](#kava/community/v1beta1/tx.proto)
- [MsgFundCommunityPool](#kava.community.v1beta1.MsgFundCommunityPool)
- [MsgFundCommunityPoolResponse](#kava.community.v1beta1.MsgFundCommunityPoolResponse)
- [Msg](#kava.community.v1beta1.Msg)
- [kava/earn/v1beta1/strategy.proto](#kava/earn/v1beta1/strategy.proto) - [kava/earn/v1beta1/strategy.proto](#kava/earn/v1beta1/strategy.proto)
- [StrategyType](#kava.earn.v1beta1.StrategyType) - [StrategyType](#kava.earn.v1beta1.StrategyType)
@ -405,6 +417,20 @@
- [Query](#kava.kavadist.v1beta1.Query) - [Query](#kava.kavadist.v1beta1.Query)
- [kava/kavamint/v1beta1/kavamint.proto](#kava/kavamint/v1beta1/kavamint.proto)
- [Params](#kava.kavamint.v1beta1.Params)
- [kava/kavamint/v1beta1/genesis.proto](#kava/kavamint/v1beta1/genesis.proto)
- [GenesisState](#kava.kavamint.v1beta1.GenesisState)
- [kava/kavamint/v1beta1/query.proto](#kava/kavamint/v1beta1/query.proto)
- [QueryInflationRequest](#kava.kavamint.v1beta1.QueryInflationRequest)
- [QueryInflationResponse](#kava.kavamint.v1beta1.QueryInflationResponse)
- [QueryParamsRequest](#kava.kavamint.v1beta1.QueryParamsRequest)
- [QueryParamsResponse](#kava.kavamint.v1beta1.QueryParamsResponse)
- [Query](#kava.kavamint.v1beta1.Query)
- [kava/liquid/v1beta1/query.proto](#kava/liquid/v1beta1/query.proto) - [kava/liquid/v1beta1/query.proto](#kava/liquid/v1beta1/query.proto)
- [QueryDelegatedBalanceRequest](#kava.liquid.v1beta1.QueryDelegatedBalanceRequest) - [QueryDelegatedBalanceRequest](#kava.liquid.v1beta1.QueryDelegatedBalanceRequest)
- [QueryDelegatedBalanceResponse](#kava.liquid.v1beta1.QueryDelegatedBalanceResponse) - [QueryDelegatedBalanceResponse](#kava.liquid.v1beta1.QueryDelegatedBalanceResponse)
@ -2812,6 +2838,109 @@ Msg defines the committee Msg service
<a name="kava/community/v1beta1/query.proto"></a>
<p align="right"><a href="#top">Top</a></p>
## kava/community/v1beta1/query.proto
<a name="kava.community.v1beta1.QueryBalanceRequest"></a>
### QueryBalanceRequest
QueryBalanceRequest defines the request type for querying x/community balance.
<a name="kava.community.v1beta1.QueryBalanceResponse"></a>
### QueryBalanceResponse
QueryBalanceResponse defines the response type for querying x/community balance.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `coins` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | |
<!-- end messages -->
<!-- end enums -->
<!-- end HasExtensions -->
<a name="kava.community.v1beta1.Query"></a>
### Query
Query defines the gRPC querier service for x/community.
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `Balance` | [QueryBalanceRequest](#kava.community.v1beta1.QueryBalanceRequest) | [QueryBalanceResponse](#kava.community.v1beta1.QueryBalanceResponse) | Balance queries the balance of all coins of x/community module. | GET|/kava/community/v1beta1/balance|
<!-- end services -->
<a name="kava/community/v1beta1/tx.proto"></a>
<p align="right"><a href="#top">Top</a></p>
## kava/community/v1beta1/tx.proto
<a name="kava.community.v1beta1.MsgFundCommunityPool"></a>
### MsgFundCommunityPool
MsgFundCommunityPool allows an account to directly fund the community module account.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | |
| `depositor` | [string](#string) | | |
<a name="kava.community.v1beta1.MsgFundCommunityPoolResponse"></a>
### MsgFundCommunityPoolResponse
MsgFundCommunityPoolResponse defines the Msg/FundCommunityPool response type.
<!-- end messages -->
<!-- end enums -->
<!-- end HasExtensions -->
<a name="kava.community.v1beta1.Msg"></a>
### Msg
Msg defines the community Msg service.
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `FundCommunityPool` | [MsgFundCommunityPool](#kava.community.v1beta1.MsgFundCommunityPool) | [MsgFundCommunityPoolResponse](#kava.community.v1beta1.MsgFundCommunityPoolResponse) | FundCommunityPool defines a method to allow an account to directly fund the community module account. | |
<!-- end services -->
<a name="kava/earn/v1beta1/strategy.proto"></a> <a name="kava/earn/v1beta1/strategy.proto"></a>
<p align="right"><a href="#top">Top</a></p> <p align="right"><a href="#top">Top</a></p>
@ -5738,6 +5867,147 @@ Query defines the gRPC querier service.
<a name="kava/kavamint/v1beta1/kavamint.proto"></a>
<p align="right"><a href="#top">Top</a></p>
## kava/kavamint/v1beta1/kavamint.proto
<a name="kava.kavamint.v1beta1.Params"></a>
### Params
Params wraps the governance parameters for the kavamint module
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `community_pool_inflation` | [string](#string) | | yearly inflation of total token supply minted to the community pool. |
| `staking_rewards_apy` | [string](#string) | | yearly inflation of bonded tokens minted for staking rewards to validators. |
<!-- end messages -->
<!-- end enums -->
<!-- end HasExtensions -->
<!-- end services -->
<a name="kava/kavamint/v1beta1/genesis.proto"></a>
<p align="right"><a href="#top">Top</a></p>
## kava/kavamint/v1beta1/genesis.proto
<a name="kava.kavamint.v1beta1.GenesisState"></a>
### GenesisState
GenesisState defines the kavamint module's genesis state.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `params` | [Params](#kava.kavamint.v1beta1.Params) | | Params defines all the parameters of the module. |
| `previous_block_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | PreviousBlockTime holds the last last time tokens were minted. On first block, tokens will be minted for total number of seconds passed since this time. |
<!-- end messages -->
<!-- end enums -->
<!-- end HasExtensions -->
<!-- end services -->
<a name="kava/kavamint/v1beta1/query.proto"></a>
<p align="right"><a href="#top">Top</a></p>
## kava/kavamint/v1beta1/query.proto
<a name="kava.kavamint.v1beta1.QueryInflationRequest"></a>
### QueryInflationRequest
QueryInflationRequest is the request type for the Query/Inflation RPC method.
<a name="kava.kavamint.v1beta1.QueryInflationResponse"></a>
### QueryInflationResponse
QueryInflationResponse is the response type for the Query/Inflation RPC method.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `inflation` | [string](#string) | | inflation is the current minting inflation value. example "0.990000000000000000" - 99% inflation |
<a name="kava.kavamint.v1beta1.QueryParamsRequest"></a>
### QueryParamsRequest
QueryParamsRequest defines the request type for querying x/kavamint parameters.
<a name="kava.kavamint.v1beta1.QueryParamsResponse"></a>
### QueryParamsResponse
QueryParamsResponse defines the response type for querying x/kavamint parameters.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `params` | [Params](#kava.kavamint.v1beta1.Params) | | |
<!-- end messages -->
<!-- end enums -->
<!-- end HasExtensions -->
<a name="kava.kavamint.v1beta1.Query"></a>
### Query
Query defines the gRPC querier service for kavamint.
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `Params` | [QueryParamsRequest](#kava.kavamint.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#kava.kavamint.v1beta1.QueryParamsResponse) | Params queries the parameters of x/kavamint module. | GET|/kava/kavamint/v1beta1/params|
| `Inflation` | [QueryInflationRequest](#kava.kavamint.v1beta1.QueryInflationRequest) | [QueryInflationResponse](#kava.kavamint.v1beta1.QueryInflationResponse) | Inflation queries x/kavamint for the overall cumulative inflation rate of KAVA. | GET|/kava/kavamint/v1beta1/inflation|
<!-- end services -->
<a name="kava/liquid/v1beta1/query.proto"></a> <a name="kava/liquid/v1beta1/query.proto"></a>
<p align="right"><a href="#top">Top</a></p> <p align="right"><a href="#top">Top</a></p>

View File

@ -0,0 +1,27 @@
syntax = "proto3";
package kava.community.v1beta1;
import "cosmos/base/v1beta1/coin.proto";
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
option go_package = "github.com/kava-labs/kava/x/community/types";
// Query defines the gRPC querier service for x/community.
service Query {
// Balance queries the balance of all coins of x/community module.
rpc Balance(QueryBalanceRequest) returns (QueryBalanceResponse) {
option (google.api.http).get = "/kava/community/v1beta1/balance";
}
}
// QueryBalanceRequest defines the request type for querying x/community balance.
message QueryBalanceRequest {}
// QueryBalanceResponse defines the response type for querying x/community balance.
message QueryBalanceResponse {
repeated cosmos.base.v1beta1.Coin coins = 1 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}

View File

@ -0,0 +1,29 @@
syntax = "proto3";
package kava.community.v1beta1;
import "cosmos/base/v1beta1/coin.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
option go_package = "github.com/kava-labs/kava/x/community/types";
option (gogoproto.equal_all) = true;
// Msg defines the community Msg service.
service Msg {
// FundCommunityPool defines a method to allow an account to directly fund the community module account.
rpc FundCommunityPool(MsgFundCommunityPool) returns (MsgFundCommunityPoolResponse);
}
// MsgFundCommunityPool allows an account to directly fund the community module account.
message MsgFundCommunityPool {
option (gogoproto.goproto_getters) = false;
repeated cosmos.base.v1beta1.Coin amount = 1 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
string depositor = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
}
// MsgFundCommunityPoolResponse defines the Msg/FundCommunityPool response type.
message MsgFundCommunityPoolResponse {}

View File

@ -0,0 +1,23 @@
syntax = "proto3";
package kava.kavamint.v1beta1;
import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";
import "kava/kavamint/v1beta1/kavamint.proto";
option go_package = "github.com/kava-labs/kava/x/kavamint/types";
// GenesisState defines the kavamint module's genesis state.
message GenesisState {
option (gogoproto.goproto_getters) = false;
// Params defines all the parameters of the module.
Params params = 1 [(gogoproto.nullable) = false];
// PreviousBlockTime holds the last last time tokens were minted.
// On first block, tokens will be minted for total number of seconds passed since this time.
google.protobuf.Timestamp previous_block_time = 2 [
(gogoproto.stdtime) = true,
(gogoproto.nullable) = false
];
}

View File

@ -0,0 +1,26 @@
syntax = "proto3";
package kava.kavamint.v1beta1;
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
option go_package = "github.com/kava-labs/kava/x/kavamint/types";
// Params wraps the governance parameters for the kavamint module
message Params {
option (gogoproto.goproto_stringer) = false;
option (gogoproto.goproto_getters) = false;
// yearly inflation of total token supply minted to the community pool.
string community_pool_inflation = 1 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// yearly inflation of bonded tokens minted for staking rewards to validators.
string staking_rewards_apy = 2 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}

View File

@ -0,0 +1,44 @@
syntax = "proto3";
package kava.kavamint.v1beta1;
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "kava/kavamint/v1beta1/kavamint.proto";
option go_package = "github.com/kava-labs/kava/x/kavamint/types";
// Query defines the gRPC querier service for kavamint.
service Query {
// Params queries the parameters of x/kavamint module.
rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
option (google.api.http).get = "/kava/kavamint/v1beta1/params";
}
// Inflation queries x/kavamint for the overall cumulative inflation rate of KAVA.
rpc Inflation(QueryInflationRequest) returns (QueryInflationResponse) {
option (google.api.http).get = "/kava/kavamint/v1beta1/inflation";
}
}
// QueryParamsRequest defines the request type for querying x/kavamint parameters.
message QueryParamsRequest {}
// QueryParamsResponse defines the response type for querying x/kavamint parameters.
message QueryParamsResponse {
Params params = 1 [(gogoproto.nullable) = false];
}
// QueryInflationRequest is the request type for the Query/Inflation RPC method.
message QueryInflationRequest {}
// QueryInflationResponse is the response type for the Query/Inflation RPC method.
message QueryInflationResponse {
// inflation is the current minting inflation value.
// example "0.990000000000000000" - 99% inflation
string inflation = 1 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}

12
x/community/abci.go Normal file
View File

@ -0,0 +1,12 @@
package community
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/community/keeper"
abci "github.com/tendermint/tendermint/abci/types"
)
// BeginBlocker runs at the start of every block.
func BeginBlocker(ctx sdk.Context, _ abci.RequestBeginBlock, k keeper.Keeper) {
// TODO: process proposals here
}

View File

@ -0,0 +1,55 @@
package cli
import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/kava-labs/kava/x/community/types"
)
// GetQueryCmd returns the cli query commands for the community module.
func GetQueryCmd() *cobra.Command {
communityQueryCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Querying commands for the community module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
commands := []*cobra.Command{
GetCmdQueryBalance(),
}
for _, cmd := range commands {
flags.AddQueryFlagsToCmd(cmd)
}
communityQueryCmd.AddCommand(commands...)
return communityQueryCmd
}
// GetCmdQueryBalance implements a command to return the current community pool balance.
func GetCmdQueryBalance() *cobra.Command {
return &cobra.Command{
Use: "balance",
Short: "Query the current balance of the community module account",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.Balance(cmd.Context(), &types.QueryBalanceRequest{})
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
}
}

View File

@ -0,0 +1,66 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/kava-labs/kava/x/community/types"
)
// GetTxCmd returns the transaction commands for this module
func GetTxCmd() *cobra.Command {
communityTxCmd := &cobra.Command{
Use: types.ModuleName,
Short: "community module transactions subcommands",
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
cmds := []*cobra.Command{
getCmdFundCommunityPool(),
}
for _, cmd := range cmds {
flags.AddTxFlagsToCmd(cmd)
}
communityTxCmd.AddCommand(cmds...)
return communityTxCmd
}
func getCmdFundCommunityPool() *cobra.Command {
return &cobra.Command{
Use: "fund-community-pool [coins]",
Short: "funds the community pool",
Long: "Fund community pool removes the listed coins from the sender's account and send them to the community module account.",
Args: cobra.ExactArgs(1),
Example: fmt.Sprintf(
`%s tx %s fund-community-module 10000000ukava --from <key>`, version.AppName, types.ModuleName,
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
coins, err := sdk.ParseCoinsNormalized(args[0])
if err != nil {
return err
}
msg := types.NewMsgFundCommunityPool(clientCtx.GetFromAddress(), coins)
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
},
}
}

19
x/community/genesis.go Normal file
View File

@ -0,0 +1,19 @@
// the community module has no genesis state but must init its module account on init
package community
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/community/keeper"
"github.com/kava-labs/kava/x/community/types"
)
// InitGenesis initializes the community module account
func InitGenesis(ctx sdk.Context, k keeper.Keeper, ak types.AccountKeeper) {
// check if the module account exists
if moduleAcc := ak.GetModuleAccount(ctx, types.ModuleAccountName); moduleAcc == nil {
panic(fmt.Sprintf("%s module account has not been set", types.ModuleAccountName))
}
}

View File

@ -0,0 +1,44 @@
package community_test
import (
"testing"
"github.com/stretchr/testify/suite"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/kava-labs/kava/x/community"
"github.com/kava-labs/kava/x/community/testutil"
"github.com/kava-labs/kava/x/community/types"
)
type genesisTestSuite struct {
testutil.Suite
}
func (suite *genesisTestSuite) SetupTest() {
suite.Suite.SetupTest()
}
func TestGenesisTestSuite(t *testing.T) {
suite.Run(t, new(genesisTestSuite))
}
func (suite *genesisTestSuite) TestInitGenesis() {
suite.SetupTest()
accountKeeper := suite.App.GetAccountKeeper()
suite.NotPanics(func() {
community.InitGenesis(suite.Ctx, suite.Keeper, accountKeeper)
})
communityPoolAddress := accountKeeper.GetModuleAddress(types.ModuleAccountName)
// hello, greppers!
suite.Equal("kava17d2wax0zhjrrecvaszuyxdf5wcu5a0p4qlx3t5", communityPoolAddress.String())
// check for module account this way b/c GetModuleAccount creates if not existing.
acc := accountKeeper.GetAccount(suite.Ctx, communityPoolAddress)
suite.NotNil(acc)
_, ok := acc.(authtypes.ModuleAccountI)
suite.True(ok)
}

View File

@ -0,0 +1,26 @@
package keeper
import (
"context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/community/types"
)
type queryServer struct {
keeper Keeper
}
var _ types.QueryServer = queryServer{}
// NewQueryServerImpl creates a new server for handling gRPC queries.
func NewQueryServerImpl(k Keeper) types.QueryServer {
return &queryServer{keeper: k}
}
func (s queryServer) Balance(c context.Context, _ *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
return &types.QueryBalanceResponse{
Coins: s.keeper.GetModuleAccountBalance(ctx),
}, nil
}

View File

@ -0,0 +1,66 @@
package keeper_test
import (
"context"
"testing"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"
"github.com/kava-labs/kava/x/community/keeper"
"github.com/kava-labs/kava/x/community/types"
)
type grpcQueryTestSuite struct {
KeeperTestSuite
queryClient types.QueryClient
}
func (suite *grpcQueryTestSuite) SetupTest() {
suite.KeeperTestSuite.SetupTest()
queryHelper := baseapp.NewQueryServerTestHelper(suite.Ctx, suite.App.InterfaceRegistry())
types.RegisterQueryServer(queryHelper, keeper.NewQueryServerImpl(suite.Keeper))
suite.queryClient = types.NewQueryClient(queryHelper)
}
func TestGrpcQueryTestSuite(t *testing.T) {
suite.Run(t, new(grpcQueryTestSuite))
}
func (suite *grpcQueryTestSuite) TestGrpcQueryBalance() {
var expCoins sdk.Coins
testCases := []struct {
name string
setup func()
}{
{
name: "handles response with no balance",
setup: func() { expCoins = sdk.Coins{} },
},
{
name: "handles response with balance",
setup: func() {
expCoins = sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(100)),
sdk.NewCoin("usdx", sdk.NewInt(1000)),
)
suite.App.FundModuleAccount(suite.Ctx, types.ModuleName, expCoins)
},
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest()
tc.setup()
res, err := suite.queryClient.Balance(context.Background(), &types.QueryBalanceRequest{})
suite.Require().NoError(err)
suite.Require().True(expCoins.IsEqual(res.Coins))
})
}
}

View File

@ -0,0 +1,50 @@
package keeper
import (
"fmt"
"github.com/tendermint/tendermint/libs/log"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/community/types"
)
// Keeper of the community store
type Keeper struct {
bankKeeper types.BankKeeper
moduleAddress sdk.AccAddress
}
// NewKeeper creates a new community Keeper instance
func NewKeeper(ak types.AccountKeeper, bk types.BankKeeper) Keeper {
// ensure community module account is set
addr := ak.GetModuleAddress(types.ModuleAccountName)
if addr == nil {
panic(fmt.Sprintf("%s module account has not been set", types.ModuleAccountName))
}
return Keeper{
bankKeeper: bk,
moduleAddress: addr,
}
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", "x/"+types.ModuleName)
}
// GetModuleAccountBalance returns all the coins held by the community module account
func (k Keeper) GetModuleAccountBalance(ctx sdk.Context) sdk.Coins {
return k.bankKeeper.GetAllBalances(ctx, k.moduleAddress)
}
// FundCommunityPool transfers coins from the sender to the community module account.
func (k Keeper) FundCommunityPool(ctx sdk.Context, sender sdk.AccAddress, amount sdk.Coins) error {
return k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleAccountName, amount)
}
// DistributeFromCommunityPool transfers coins from the community pool to recipient.
func (k Keeper) DistributeFromCommunityPool(ctx sdk.Context, recipient sdk.AccAddress, amount sdk.Coins) error {
return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleAccountName, recipient, amount)
}

View File

@ -0,0 +1,63 @@
package keeper_test
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"
"github.com/kava-labs/kava/x/community/testutil"
"github.com/kava-labs/kava/x/community/types"
)
// Test suite used for all keeper tests
type KeeperTestSuite struct {
testutil.Suite
}
// The default state used by each test
func (suite *KeeperTestSuite) SetupTest() {
suite.Suite.SetupTest()
}
func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}
func (suite *KeeperTestSuite) TestCommunityPool() {
suite.SetupTest()
maccAddr := suite.App.GetAccountKeeper().GetModuleAddress(types.ModuleAccountName)
funds := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10000)),
sdk.NewCoin("usdx", sdk.NewInt(100)),
)
sender := suite.CreateFundedAccount(funds)
suite.Run("FundCommunityPool", func() {
err := suite.Keeper.FundCommunityPool(suite.Ctx, sender, funds)
suite.Require().NoError(err)
// check that community pool received balance
suite.App.CheckBalance(suite.T(), suite.Ctx, maccAddr, funds)
// check that sender had balance deducted
suite.App.CheckBalance(suite.T(), suite.Ctx, sender, sdk.NewCoins())
})
// send it back
suite.Run("DistributeFromCommunityPool - valid", func() {
err := suite.Keeper.DistributeFromCommunityPool(suite.Ctx, sender, funds)
suite.Require().NoError(err)
// community pool has funds deducted
suite.App.CheckBalance(suite.T(), suite.Ctx, maccAddr, sdk.NewCoins())
// receiver receives the funds
suite.App.CheckBalance(suite.T(), suite.Ctx, sender, funds)
})
// can't send more than we have!
suite.Run("DistributeFromCommunityPool - insufficient funds", func() {
err := suite.Keeper.DistributeFromCommunityPool(suite.Ctx, sender, funds)
suite.Require().ErrorContains(err, "insufficient funds")
})
}

View File

@ -0,0 +1,49 @@
package keeper
import (
"context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/community/types"
)
type msgServer struct {
keeper Keeper
}
// NewMsgServerImpl returns an implementation of the community MsgServer interface
// for the provided Keeper.
func NewMsgServerImpl(keeper Keeper) types.MsgServer {
return &msgServer{keeper: keeper}
}
var _ types.MsgServer = msgServer{}
// FundCommunityPool handles FundCommunityPool msgs.
func (s msgServer) FundCommunityPool(goCtx context.Context, msg *types.MsgFundCommunityPool) (*types.MsgFundCommunityPoolResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
if err := msg.ValidateBasic(); err != nil {
return nil, err
}
// above validation will fail if depositor is invalid
depositor := sdk.MustAccAddressFromBech32(msg.Depositor)
if err := s.keeper.FundCommunityPool(ctx, depositor, msg.Amount); err != nil {
return nil, err
}
ctx.EventManager().EmitEvent(
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeyAction, types.AttributeValueFundCommunityPool),
sdk.NewAttribute(sdk.AttributeKeySender, msg.Depositor),
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
),
)
return &types.MsgFundCommunityPoolResponse{}, nil
}

View File

@ -0,0 +1,111 @@
package keeper_test
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/community/keeper"
"github.com/kava-labs/kava/x/community/testutil"
"github.com/kava-labs/kava/x/community/types"
)
type msgServerTestSuite struct {
testutil.Suite
communityPool sdk.AccAddress
msgServer types.MsgServer
}
func (suite *msgServerTestSuite) SetupTest() {
suite.Suite.SetupTest()
suite.communityPool = suite.App.GetAccountKeeper().GetModuleAddress(types.ModuleAccountName)
suite.msgServer = keeper.NewMsgServerImpl(suite.Keeper)
}
func TestMsgServerTestSuite(t *testing.T) {
suite.Run(t, new(msgServerTestSuite))
}
func (suite *msgServerTestSuite) TestMsgFundCommunityPool() {
singleCoin := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(2e6)))
multipleCoins := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(3e6)),
sdk.NewCoin("usdx", sdk.NewInt(1e7)),
)
testCases := []struct {
name string
setup func() *types.MsgFundCommunityPool
expectedBalance sdk.Coins
shouldPass bool
}{
{
name: "valid funding of single coin",
setup: func() *types.MsgFundCommunityPool {
sender := app.RandomAddress()
suite.App.FundAccount(suite.Ctx, sender, singleCoin)
return &types.MsgFundCommunityPool{
Amount: singleCoin,
Depositor: sender.String(),
}
},
expectedBalance: singleCoin,
shouldPass: true,
},
{
name: "valid funding of multiple coins",
setup: func() *types.MsgFundCommunityPool {
sender := app.RandomAddress()
suite.App.FundAccount(suite.Ctx, sender, multipleCoins)
return &types.MsgFundCommunityPool{
Amount: multipleCoins,
Depositor: sender.String(),
}
},
expectedBalance: multipleCoins,
shouldPass: true,
},
{
name: "invalid - failing message validation",
setup: func() *types.MsgFundCommunityPool {
return &types.MsgFundCommunityPool{
Amount: sdk.NewCoins(),
Depositor: app.RandomAddress().String(),
}
},
expectedBalance: sdk.NewCoins(),
shouldPass: false,
},
{
name: "invalid - failing tx, insufficient funds",
setup: func() *types.MsgFundCommunityPool {
return &types.MsgFundCommunityPool{
Amount: singleCoin,
Depositor: app.RandomAddress().String(),
}
},
expectedBalance: sdk.NewCoins(),
shouldPass: false,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest()
msg := tc.setup()
_, err := suite.msgServer.FundCommunityPool(sdk.WrapSDKContext(suite.Ctx), msg)
if tc.shouldPass {
suite.NoError(err)
} else {
suite.Error(err)
}
balance := suite.Keeper.GetModuleAccountBalance(suite.Ctx)
suite.App.CheckBalance(suite.T(), suite.Ctx, suite.communityPool, balance)
})
}
}

146
x/community/module.go Normal file
View File

@ -0,0 +1,146 @@
package community
import (
"context"
"encoding/json"
"github.com/gorilla/mux"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/kava-labs/kava/x/community/client/cli"
"github.com/kava-labs/kava/x/community/keeper"
"github.com/kava-labs/kava/x/community/types"
)
var (
_ module.AppModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
)
// AppModuleBasic app module basics object
type AppModuleBasic struct{}
// Name returns the module name
func (AppModuleBasic) Name() string {
return types.ModuleName
}
// RegisterLegacyAminoCodec register module codec
// Deprecated: unused but necessary to fulfill AppModuleBasic interface
func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
types.RegisterLegacyAminoCodec(cdc)
}
// DefaultGenesis default genesis state
func (AppModuleBasic) DefaultGenesis(_ codec.JSONCodec) json.RawMessage {
return []byte("{}")
}
// ValidateGenesis module validate genesis
func (AppModuleBasic) ValidateGenesis(_ codec.JSONCodec, _ client.TxEncodingConfig, _ json.RawMessage) error {
return nil
}
// RegisterInterfaces implements InterfaceModule.RegisterInterfaces
func (a AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
types.RegisterInterfaces(registry)
}
// RegisterRESTRoutes registers REST routes for the module.
// Deprecated: unused but necessary to fulfill AppModuleBasic interface
func (a AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {}
// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module.
func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil {
panic(err)
}
}
// GetTxCmd returns the root tx command for the module.
func (AppModuleBasic) GetTxCmd() *cobra.Command {
return cli.GetTxCmd()
}
// GetQueryCmd returns no root query command for the module.
func (AppModuleBasic) GetQueryCmd() *cobra.Command {
return cli.GetQueryCmd()
}
//____________________________________________________________________________
// AppModule app module type
type AppModule struct {
AppModuleBasic
keeper keeper.Keeper
accountKeeper types.AccountKeeper
}
// NewAppModule creates a new AppModule object
func NewAppModule(keeper keeper.Keeper, ak types.AccountKeeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
keeper: keeper,
accountKeeper: ak,
}
}
// Name module name
func (am AppModule) Name() string {
return am.AppModuleBasic.Name()
}
// RegisterInvariants register module invariants
func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
// Route module message route name
// Deprecated: unused but necessary to fulfill AppModule interface
func (am AppModule) Route() sdk.Route { return sdk.Route{} }
// QuerierRoute module querier route name
// Deprecated: unused but necessary to fulfill AppModule interface
func (AppModule) QuerierRoute() string { return "" }
// LegacyQuerierHandler returns no sdk.Querier.
// Deprecated: unused but necessary to fulfill AppModule interface
func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier {
return nil
}
// ConsensusVersion implements AppModule/ConsensusVersion.
func (AppModule) ConsensusVersion() uint64 { return 1 }
// RegisterServices registers module services.
func (am AppModule) RegisterServices(cfg module.Configurator) {
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
types.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServerImpl(am.keeper))
}
// InitGenesis module init-genesis
func (am AppModule) InitGenesis(ctx sdk.Context, _ codec.JSONCodec, _ json.RawMessage) []abci.ValidatorUpdate {
InitGenesis(ctx, am.keeper, am.accountKeeper)
return []abci.ValidatorUpdate{}
}
// ExportGenesis module export genesis
func (am AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONCodec) json.RawMessage {
return am.DefaultGenesis(cdc)
}
// BeginBlock module begin-block
func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
// EndBlock module end-block
func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
return []abci.ValidatorUpdate{}
}

View File

@ -0,0 +1,22 @@
package community_test
import (
"testing"
"github.com/stretchr/testify/require"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/community/types"
)
func TestItCreatesModuleAccountOnInitBlock(t *testing.T) {
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, tmproto.Header{Height: 1})
tApp.InitializeFromGenesisStates()
accKeeper := tApp.GetAccountKeeper()
acc := accKeeper.GetAccount(ctx, authtypes.NewModuleAddress(types.ModuleName))
require.NotNil(t, acc)
}

View File

@ -0,0 +1,19 @@
<!--
order: 1
-->
# Concepts
## Community Pool
The community pool is the module account of the x/community module. It replaces the functionality of the community pool fee collector account of the auth module in the vanilla SDK.
### Funding
The community pool can be funded every block from the community pool inflation of the x/kavamint module.
Additionally, the pool can be funded by any account sending a community/FundCommunityPool message.
### Spending
The community pool funds are spent via government proposals. The x/kavadist module includes a CommunityPoolMultiSpendProposal that, upon approval, distributes funds to a list of accounts.

View File

@ -0,0 +1,7 @@
<!--
order: 2
-->
# State
The x/community module has no state.

View File

@ -0,0 +1,17 @@
<!--
order: 3
-->
# Messages
## FundCommunityPool
This message sends coins directly from the sender to the community module account.
The transaction fails if the amount cannot be transferred from the sender to the community module account.
```go
func (k Keeper) FundCommunityPool(ctx sdk.Context, sender sdk.AccAddress, amount sdk.Coins) error {
return k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleAccountName, amount)
}
```

View File

@ -0,0 +1,18 @@
<!--
order: 4
-->
# Events
The community module emits the following events:
## Handlers
### MsgFundCommunityPool
| Type | Attribute Key | Attribute Value |
| ------- | ------------- | ------------------- |
| message | module | community |
| message | action | fund_community_pool |
| message | sender | {senderAddress} |
| message | amount | {amountCoins} |

View File

@ -0,0 +1,19 @@
<!--
order: 0
title: "Community Overview"
parent:
title: "community"
-->
# `community`
<!-- TOC -->
1. **[Concepts](01_concepts.md)**
2. **[State](02_state.md)**
3. **[Messages](03_messages.md)**
4. **[Events](04_events.md)**
## Abstract
`x/community` is an implementation of a Cosmos SDK Module that provides for functionality and governance for a community pool of funds controlled by Kava DAO.

View File

@ -0,0 +1,41 @@
package testutil
import (
"github.com/stretchr/testify/suite"
sdk "github.com/cosmos/cosmos-sdk/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/community/keeper"
)
// Test suite used for all community tests
type Suite struct {
suite.Suite
App app.TestApp
Ctx sdk.Context
Keeper keeper.Keeper
}
// The default state used by each test
func (suite *Suite) SetupTest() {
app.SetSDKConfig()
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
tApp.InitializeFromGenesisStates()
suite.App = tApp
suite.Ctx = ctx
suite.Keeper = tApp.GetCommunityKeeper()
}
// CreateFundedAccount creates a random account and mints `coins` to it.
func (suite *Suite) CreateFundedAccount(coins sdk.Coins) sdk.AccAddress {
addr := app.RandomAddress()
err := suite.App.FundAccount(suite.Ctx, addr, coins)
suite.Require().NoError(err)
return addr
}

View File

@ -0,0 +1,36 @@
package types
import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/types/msgservice"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// RegisterLegacyAminoCodec registers all the necessary types and interfaces for the module.
func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&MsgFundCommunityPool{}, "community/MsgFundCommunityPool", nil)
}
// RegisterInterfaces registers proto messages under their interfaces for unmarshalling,
// in addition to registering the msg service for handling tx msgs.
func RegisterInterfaces(registry types.InterfaceRegistry) {
registry.RegisterImplementations((*sdk.Msg)(nil),
&MsgFundCommunityPool{},
)
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
}
var (
amino = codec.NewLegacyAmino()
ModuleCdc = codec.NewAminoCodec(amino)
)
func init() {
RegisterLegacyAminoCodec(amino)
cryptocodec.RegisterCrypto(amino)
}

View File

@ -0,0 +1,7 @@
package types
// Community module event types
const (
AttributeValueFundCommunityPool = "fund_community_pool"
AttributeValueCategory = ModuleName
)

View File

@ -0,0 +1,20 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)
// AccountKeeper defines the contract required for account APIs.
type AccountKeeper interface {
GetModuleAccount(ctx sdk.Context, moduleName string) authtypes.ModuleAccountI
GetModuleAddress(name string) sdk.AccAddress
}
// BankKeeper defines the contract needed to be fulfilled for banking dependencies.
type BankKeeper interface {
GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
}

12
x/community/types/keys.go Normal file
View File

@ -0,0 +1,12 @@
package types
const (
// module name
ModuleName = "community"
// ModuleAccountName is the name of the module's account
ModuleAccountName = ModuleName
// Query endpoints supported by community
QueryBalance = "balance"
)

56
x/community/types/msg.go Normal file
View File

@ -0,0 +1,56 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
)
// ensure Msg interface compliance at compile time
var (
_ sdk.Msg = &MsgFundCommunityPool{}
_ legacytx.LegacyMsg = &MsgFundCommunityPool{}
)
// NewMsgFundCommunityPool returns a new MsgFundCommunityPool
func NewMsgFundCommunityPool(depositor sdk.AccAddress, amount sdk.Coins) MsgFundCommunityPool {
return MsgFundCommunityPool{
Depositor: depositor.String(),
Amount: amount,
}
}
// Route return the message type used for routing the message.
func (msg MsgFundCommunityPool) Route() string { return ModuleName }
// Type returns a human-readable string for the message, intended for utilization within tags.
func (msg MsgFundCommunityPool) Type() string { return sdk.MsgTypeURL(&msg) }
// ValidateBasic does a simple validation check that doesn't require access to any other information.
func (msg MsgFundCommunityPool) ValidateBasic() error {
_, err := sdk.AccAddressFromBech32(msg.Depositor)
if err != nil {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, err.Error())
}
if msg.Amount.IsAnyNil() || !msg.Amount.IsValid() || msg.Amount.IsZero() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "'%s'", msg.Amount)
}
return nil
}
// GetSignBytes gets the canonical byte representation of the Msg.
func (msg MsgFundCommunityPool) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(&msg)
return sdk.MustSortJSON(bz)
}
// GetSigners returns the addresses of signers that must sign.
func (msg MsgFundCommunityPool) GetSigners() []sdk.AccAddress {
depositor, err := sdk.AccAddressFromBech32(msg.Depositor)
if err != nil {
panic(err)
}
return []sdk.AccAddress{depositor}
}

View File

@ -0,0 +1,93 @@
package types_test
import (
"testing"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/community/types"
)
func TestFundCommunityPool_ValidateBasic(t *testing.T) {
validCoins := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewIntFromUint64(1e6)),
sdk.NewCoin("some-denom", sdk.NewIntFromUint64(1e4)),
)
testCases := []struct {
name string
shouldPass bool
message types.MsgFundCommunityPool
}{
{
name: "valid message",
shouldPass: true,
message: types.NewMsgFundCommunityPool(app.RandomAddress(), validCoins),
},
{
name: "invalid - bad depositor",
shouldPass: false,
message: types.MsgFundCommunityPool{
Depositor: "not-an-address",
Amount: validCoins,
},
},
{
name: "invalid - empty coins",
shouldPass: false,
message: types.MsgFundCommunityPool{
Depositor: app.RandomAddress().String(),
Amount: sdk.NewCoins(),
},
},
{
name: "invalid - nil coins",
shouldPass: false,
message: types.MsgFundCommunityPool{
Depositor: app.RandomAddress().String(),
Amount: nil,
},
},
{
name: "invalid - zero coins",
shouldPass: false,
message: types.MsgFundCommunityPool{
Depositor: app.RandomAddress().String(),
Amount: sdk.NewCoins(
sdk.NewCoin("ukava", sdk.ZeroInt()),
),
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.message.ValidateBasic()
if tc.shouldPass {
require.NoError(t, err)
} else {
require.Error(t, err)
}
})
}
}
func TestFundCommunityPool_GetSigners(t *testing.T) {
t.Run("valid", func(t *testing.T) {
address := app.RandomAddress()
signers := types.MsgFundCommunityPool{
Depositor: address.String(),
}.GetSigners()
require.Len(t, signers, 1)
require.Equal(t, address, signers[0])
})
t.Run("panics when depositor is invalid", func(t *testing.T) {
require.Panics(t, func() {
types.MsgFundCommunityPool{
Depositor: "not-an-address",
}.GetSigners()
})
})
}

View File

@ -0,0 +1,550 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: kava/community/v1beta1/query.proto
package types
import (
context "context"
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"
grpc1 "github.com/gogo/protobuf/grpc"
proto "github.com/gogo/protobuf/proto"
_ "google.golang.org/genproto/googleapis/api/annotations"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
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
// QueryBalanceRequest defines the request type for querying x/community balance.
type QueryBalanceRequest struct {
}
func (m *QueryBalanceRequest) Reset() { *m = QueryBalanceRequest{} }
func (m *QueryBalanceRequest) String() string { return proto.CompactTextString(m) }
func (*QueryBalanceRequest) ProtoMessage() {}
func (*QueryBalanceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_f236f06c43149273, []int{0}
}
func (m *QueryBalanceRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *QueryBalanceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_QueryBalanceRequest.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 *QueryBalanceRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_QueryBalanceRequest.Merge(m, src)
}
func (m *QueryBalanceRequest) XXX_Size() int {
return m.Size()
}
func (m *QueryBalanceRequest) XXX_DiscardUnknown() {
xxx_messageInfo_QueryBalanceRequest.DiscardUnknown(m)
}
var xxx_messageInfo_QueryBalanceRequest proto.InternalMessageInfo
// QueryBalanceResponse defines the response type for querying x/community balance.
type QueryBalanceResponse struct {
Coins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=coins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"coins"`
}
func (m *QueryBalanceResponse) Reset() { *m = QueryBalanceResponse{} }
func (m *QueryBalanceResponse) String() string { return proto.CompactTextString(m) }
func (*QueryBalanceResponse) ProtoMessage() {}
func (*QueryBalanceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_f236f06c43149273, []int{1}
}
func (m *QueryBalanceResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *QueryBalanceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_QueryBalanceResponse.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 *QueryBalanceResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_QueryBalanceResponse.Merge(m, src)
}
func (m *QueryBalanceResponse) XXX_Size() int {
return m.Size()
}
func (m *QueryBalanceResponse) XXX_DiscardUnknown() {
xxx_messageInfo_QueryBalanceResponse.DiscardUnknown(m)
}
var xxx_messageInfo_QueryBalanceResponse proto.InternalMessageInfo
func (m *QueryBalanceResponse) GetCoins() github_com_cosmos_cosmos_sdk_types.Coins {
if m != nil {
return m.Coins
}
return nil
}
func init() {
proto.RegisterType((*QueryBalanceRequest)(nil), "kava.community.v1beta1.QueryBalanceRequest")
proto.RegisterType((*QueryBalanceResponse)(nil), "kava.community.v1beta1.QueryBalanceResponse")
}
func init() {
proto.RegisterFile("kava/community/v1beta1/query.proto", fileDescriptor_f236f06c43149273)
}
var fileDescriptor_f236f06c43149273 = []byte{
// 325 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x51, 0x4f, 0x4a, 0xf3, 0x40,
0x14, 0x4f, 0xbe, 0x8f, 0x2a, 0xc4, 0x5d, 0xac, 0xa2, 0x45, 0xa6, 0x9a, 0x8d, 0x85, 0xda, 0x19,
0x5b, 0x6f, 0x50, 0xf1, 0x00, 0x76, 0xe9, 0x6e, 0x26, 0x0e, 0x71, 0x68, 0x3b, 0x2f, 0xed, 0x9b,
0x14, 0xb3, 0x75, 0x2f, 0x08, 0x2e, 0xbc, 0x83, 0x27, 0xe9, 0xb2, 0xe0, 0xc6, 0x95, 0x4a, 0xeb,
0x41, 0x64, 0x32, 0xb1, 0x54, 0xa8, 0xe0, 0x2a, 0x8f, 0x97, 0xdf, 0xdf, 0x37, 0x41, 0xd4, 0xe7,
0x13, 0xce, 0x62, 0x18, 0x0e, 0x33, 0xad, 0x4c, 0xce, 0x26, 0x6d, 0x21, 0x0d, 0x6f, 0xb3, 0x51,
0x26, 0xc7, 0x39, 0x4d, 0xc7, 0x60, 0x20, 0xdc, 0xb5, 0x18, 0xba, 0xc4, 0xd0, 0x12, 0x53, 0x23,
0x31, 0xe0, 0x10, 0x90, 0x09, 0x8e, 0x72, 0x49, 0x8c, 0x41, 0x69, 0xc7, 0xab, 0x55, 0x13, 0x48,
0xa0, 0x18, 0x99, 0x9d, 0xca, 0xed, 0x41, 0x02, 0x90, 0x0c, 0x24, 0xe3, 0xa9, 0x62, 0x5c, 0x6b,
0x30, 0xdc, 0x28, 0xd0, 0xe8, 0xfe, 0x46, 0x3b, 0xc1, 0xf6, 0xa5, 0xb5, 0xee, 0xf2, 0x01, 0xd7,
0xb1, 0xec, 0xc9, 0x51, 0x26, 0xd1, 0x44, 0x79, 0x50, 0xfd, 0xb9, 0xc6, 0x14, 0x34, 0xca, 0x90,
0x07, 0x15, 0x6b, 0x88, 0x7b, 0xfe, 0xe1, 0xff, 0xc6, 0x56, 0x67, 0x9f, 0xba, 0x48, 0xd4, 0x46,
0xfa, 0xce, 0x49, 0xcf, 0x41, 0xe9, 0xee, 0xe9, 0xf4, 0xad, 0xee, 0x3d, 0xbf, 0xd7, 0x1b, 0x89,
0x32, 0x37, 0x99, 0xb0, 0x75, 0x58, 0x99, 0xdf, 0x7d, 0x5a, 0x78, 0xdd, 0x67, 0x26, 0x4f, 0x25,
0x16, 0x04, 0xec, 0x39, 0xe5, 0xce, 0x93, 0x1f, 0x54, 0x0a, 0xef, 0xf0, 0xde, 0x0f, 0x36, 0xcb,
0x00, 0x61, 0x93, 0xae, 0x3f, 0x0a, 0x5d, 0x93, 0xbe, 0x76, 0xf2, 0x37, 0xb0, 0xeb, 0x14, 0x1d,
0xdf, 0xbd, 0x7c, 0x3e, 0xfe, 0x3b, 0x0a, 0xeb, 0xec, 0x97, 0xb7, 0x11, 0x8e, 0xd0, 0xbd, 0x98,
0xce, 0x89, 0x3f, 0x9b, 0x13, 0xff, 0x63, 0x4e, 0xfc, 0x87, 0x05, 0xf1, 0x66, 0x0b, 0xe2, 0xbd,
0x2e, 0x88, 0x77, 0xd5, 0x5c, 0x29, 0x69, 0x45, 0x5a, 0x03, 0x2e, 0xd0, 0xc9, 0xdd, 0xae, 0x08,
0x16, 0x6d, 0xc5, 0x46, 0x71, 0xf9, 0xb3, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x8f, 0x7f,
0x0d, 0x0b, 0x02, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// QueryClient is the client API for Query service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type QueryClient interface {
// Balance queries the balance of all coins of x/community module.
Balance(ctx context.Context, in *QueryBalanceRequest, opts ...grpc.CallOption) (*QueryBalanceResponse, error)
}
type queryClient struct {
cc grpc1.ClientConn
}
func NewQueryClient(cc grpc1.ClientConn) QueryClient {
return &queryClient{cc}
}
func (c *queryClient) Balance(ctx context.Context, in *QueryBalanceRequest, opts ...grpc.CallOption) (*QueryBalanceResponse, error) {
out := new(QueryBalanceResponse)
err := c.cc.Invoke(ctx, "/kava.community.v1beta1.Query/Balance", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// QueryServer is the server API for Query service.
type QueryServer interface {
// Balance queries the balance of all coins of x/community module.
Balance(context.Context, *QueryBalanceRequest) (*QueryBalanceResponse, error)
}
// UnimplementedQueryServer can be embedded to have forward compatible implementations.
type UnimplementedQueryServer struct {
}
func (*UnimplementedQueryServer) Balance(ctx context.Context, req *QueryBalanceRequest) (*QueryBalanceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Balance not implemented")
}
func RegisterQueryServer(s grpc1.Server, srv QueryServer) {
s.RegisterService(&_Query_serviceDesc, srv)
}
func _Query_Balance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryBalanceRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryServer).Balance(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kava.community.v1beta1.Query/Balance",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryServer).Balance(ctx, req.(*QueryBalanceRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Query_serviceDesc = grpc.ServiceDesc{
ServiceName: "kava.community.v1beta1.Query",
HandlerType: (*QueryServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Balance",
Handler: _Query_Balance_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "kava/community/v1beta1/query.proto",
}
func (m *QueryBalanceRequest) 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 *QueryBalanceRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *QueryBalanceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
return len(dAtA) - i, nil
}
func (m *QueryBalanceResponse) 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 *QueryBalanceResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *QueryBalanceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Coins) > 0 {
for iNdEx := len(m.Coins) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Coins[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintQuery(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func encodeVarintQuery(dAtA []byte, offset int, v uint64) int {
offset -= sovQuery(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *QueryBalanceRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
return n
}
func (m *QueryBalanceResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Coins) > 0 {
for _, e := range m.Coins {
l = e.Size()
n += 1 + l + sovQuery(uint64(l))
}
}
return n
}
func sovQuery(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozQuery(x uint64) (n int) {
return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *QueryBalanceRequest) 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 ErrIntOverflowQuery
}
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: QueryBalanceRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: QueryBalanceRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipQuery(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthQuery
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *QueryBalanceResponse) 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 ErrIntOverflowQuery
}
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: QueryBalanceResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: QueryBalanceResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Coins", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowQuery
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthQuery
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthQuery
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Coins = append(m.Coins, types.Coin{})
if err := m.Coins[len(m.Coins)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipQuery(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthQuery
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipQuery(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, ErrIntOverflowQuery
}
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, ErrIntOverflowQuery
}
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, ErrIntOverflowQuery
}
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, ErrInvalidLengthQuery
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupQuery
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthQuery
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group")
)

View File

@ -0,0 +1,153 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: kava/community/v1beta1/query.proto
/*
Package types is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package types
import (
"context"
"io"
"net/http"
"github.com/golang/protobuf/descriptor"
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = descriptor.ForMessage
var _ = metadata.Join
func request_Query_Balance_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq QueryBalanceRequest
var metadata runtime.ServerMetadata
msg, err := client.Balance(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Query_Balance_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq QueryBalanceRequest
var metadata runtime.ServerMetadata
msg, err := server.Balance(ctx, &protoReq)
return msg, metadata, err
}
// RegisterQueryHandlerServer registers the http handlers for service Query to "mux".
// UnaryRPC :call QueryServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead.
func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error {
mux.Handle("GET", pattern_Query_Balance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Query_Balance_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Query_Balance_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterQueryHandler(ctx, mux, conn)
}
// RegisterQueryHandler registers the http handlers for service Query to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn))
}
// RegisterQueryHandlerClient registers the http handlers for service Query
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "QueryClient" to call the correct interceptors.
func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error {
mux.Handle("GET", pattern_Query_Balance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Query_Balance_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Query_Balance_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Query_Balance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"kava", "community", "v1beta1", "balance"}, "", runtime.AssumeColonVerbOpt(false)))
)
var (
forward_Query_Balance_0 = runtime.ForwardResponseMessage
)

640
x/community/types/tx.pb.go Normal file
View File

@ -0,0 +1,640 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: kava/community/v1beta1/tx.proto
package types
import (
context "context"
fmt "fmt"
_ "github.com/cosmos/cosmos-proto"
github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
types "github.com/cosmos/cosmos-sdk/types"
_ "github.com/gogo/protobuf/gogoproto"
grpc1 "github.com/gogo/protobuf/grpc"
proto "github.com/gogo/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
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
// MsgFundCommunityPool allows an account to directly fund the community module account.
type MsgFundCommunityPool struct {
Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"`
Depositor string `protobuf:"bytes,2,opt,name=depositor,proto3" json:"depositor,omitempty"`
}
func (m *MsgFundCommunityPool) Reset() { *m = MsgFundCommunityPool{} }
func (m *MsgFundCommunityPool) String() string { return proto.CompactTextString(m) }
func (*MsgFundCommunityPool) ProtoMessage() {}
func (*MsgFundCommunityPool) Descriptor() ([]byte, []int) {
return fileDescriptor_e81067e0fbdaca18, []int{0}
}
func (m *MsgFundCommunityPool) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MsgFundCommunityPool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MsgFundCommunityPool.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 *MsgFundCommunityPool) XXX_Merge(src proto.Message) {
xxx_messageInfo_MsgFundCommunityPool.Merge(m, src)
}
func (m *MsgFundCommunityPool) XXX_Size() int {
return m.Size()
}
func (m *MsgFundCommunityPool) XXX_DiscardUnknown() {
xxx_messageInfo_MsgFundCommunityPool.DiscardUnknown(m)
}
var xxx_messageInfo_MsgFundCommunityPool proto.InternalMessageInfo
// MsgFundCommunityPoolResponse defines the Msg/FundCommunityPool response type.
type MsgFundCommunityPoolResponse struct {
}
func (m *MsgFundCommunityPoolResponse) Reset() { *m = MsgFundCommunityPoolResponse{} }
func (m *MsgFundCommunityPoolResponse) String() string { return proto.CompactTextString(m) }
func (*MsgFundCommunityPoolResponse) ProtoMessage() {}
func (*MsgFundCommunityPoolResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_e81067e0fbdaca18, []int{1}
}
func (m *MsgFundCommunityPoolResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MsgFundCommunityPoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MsgFundCommunityPoolResponse.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 *MsgFundCommunityPoolResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MsgFundCommunityPoolResponse.Merge(m, src)
}
func (m *MsgFundCommunityPoolResponse) XXX_Size() int {
return m.Size()
}
func (m *MsgFundCommunityPoolResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MsgFundCommunityPoolResponse.DiscardUnknown(m)
}
var xxx_messageInfo_MsgFundCommunityPoolResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*MsgFundCommunityPool)(nil), "kava.community.v1beta1.MsgFundCommunityPool")
proto.RegisterType((*MsgFundCommunityPoolResponse)(nil), "kava.community.v1beta1.MsgFundCommunityPoolResponse")
}
func init() { proto.RegisterFile("kava/community/v1beta1/tx.proto", fileDescriptor_e81067e0fbdaca18) }
var fileDescriptor_e81067e0fbdaca18 = []byte{
// 348 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcf, 0x4e, 0x2c, 0x4b,
0xd4, 0x4f, 0xce, 0xcf, 0xcd, 0x2d, 0xcd, 0xcb, 0x2c, 0xa9, 0xd4, 0x2f, 0x33, 0x4c, 0x4a, 0x2d,
0x49, 0x34, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x03, 0x29, 0xd0,
0x83, 0x2b, 0xd0, 0x83, 0x2a, 0x90, 0x92, 0x4b, 0xce, 0x2f, 0xce, 0xcd, 0x2f, 0xd6, 0x4f, 0x4a,
0x2c, 0x4e, 0x85, 0xeb, 0x4a, 0xce, 0xcf, 0xcc, 0x83, 0xe8, 0x93, 0x92, 0x84, 0xc8, 0xc7, 0x83,
0x79, 0xfa, 0x10, 0x0e, 0x54, 0x4a, 0x24, 0x3d, 0x3f, 0x3d, 0x1f, 0x22, 0x0e, 0x62, 0x41, 0x44,
0x95, 0x76, 0x32, 0x72, 0x89, 0xf8, 0x16, 0xa7, 0xbb, 0x95, 0xe6, 0xa5, 0x38, 0xc3, 0x6c, 0x0b,
0xc8, 0xcf, 0xcf, 0x11, 0x4a, 0xe6, 0x62, 0x4b, 0xcc, 0xcd, 0x2f, 0xcd, 0x2b, 0x91, 0x60, 0x54,
0x60, 0xd6, 0xe0, 0x36, 0x92, 0xd4, 0x83, 0x9a, 0x06, 0xb2, 0x1a, 0xe6, 0x1e, 0x3d, 0xe7, 0xfc,
0xcc, 0x3c, 0x27, 0x83, 0x13, 0xf7, 0xe4, 0x19, 0x56, 0xdd, 0x97, 0xd7, 0x48, 0xcf, 0x2c, 0xc9,
0x28, 0x4d, 0x02, 0x39, 0x1b, 0x6a, 0x35, 0x94, 0xd2, 0x2d, 0x4e, 0xc9, 0xd6, 0x2f, 0xa9, 0x2c,
0x48, 0x2d, 0x06, 0x6b, 0x28, 0x0e, 0x82, 0x1a, 0x2d, 0x64, 0xc6, 0xc5, 0x99, 0x92, 0x5a, 0x90,
0x5f, 0x9c, 0x59, 0x92, 0x5f, 0x24, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0xe9, 0x24, 0x71, 0x69, 0x8b,
0xae, 0x08, 0xd4, 0x2a, 0xc7, 0x94, 0x94, 0xa2, 0xd4, 0xe2, 0xe2, 0xe0, 0x92, 0xa2, 0xcc, 0xbc,
0xf4, 0x20, 0x84, 0x52, 0x2b, 0x96, 0x8e, 0x05, 0xf2, 0x0c, 0x4a, 0x72, 0x5c, 0x32, 0xd8, 0x9c,
0x1e, 0x94, 0x5a, 0x5c, 0x90, 0x9f, 0x57, 0x9c, 0x6a, 0x54, 0xc7, 0xc5, 0xec, 0x5b, 0x9c, 0x2e,
0x54, 0xce, 0x25, 0x88, 0xe9, 0x3d, 0x1d, 0x3d, 0xec, 0x21, 0xac, 0x87, 0xcd, 0x44, 0x29, 0x13,
0x52, 0x54, 0xc3, 0xec, 0x77, 0xf2, 0x5c, 0xf1, 0x48, 0x8e, 0xf1, 0xc4, 0x23, 0x39, 0xc6, 0x0b,
0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86,
0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0xb4, 0x91, 0x42, 0x0b, 0x64, 0xba, 0x6e, 0x4e, 0x62, 0x52, 0x31,
0x98, 0xa5, 0x5f, 0x81, 0x94, 0x34, 0xc0, 0xc1, 0x96, 0xc4, 0x06, 0x8e, 0x2d, 0x63, 0x40, 0x00,
0x00, 0x00, 0xff, 0xff, 0xc2, 0xe0, 0xfc, 0x03, 0x39, 0x02, 0x00, 0x00,
}
func (this *MsgFundCommunityPool) Equal(that interface{}) bool {
if that == nil {
return this == nil
}
that1, ok := that.(*MsgFundCommunityPool)
if !ok {
that2, ok := that.(MsgFundCommunityPool)
if ok {
that1 = &that2
} else {
return false
}
}
if that1 == nil {
return this == nil
} else if this == nil {
return false
}
if len(this.Amount) != len(that1.Amount) {
return false
}
for i := range this.Amount {
if !this.Amount[i].Equal(&that1.Amount[i]) {
return false
}
}
if this.Depositor != that1.Depositor {
return false
}
return true
}
func (this *MsgFundCommunityPoolResponse) Equal(that interface{}) bool {
if that == nil {
return this == nil
}
that1, ok := that.(*MsgFundCommunityPoolResponse)
if !ok {
that2, ok := that.(MsgFundCommunityPoolResponse)
if ok {
that1 = &that2
} else {
return false
}
}
if that1 == nil {
return this == nil
} else if this == nil {
return false
}
return true
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// MsgClient is the client API for Msg service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type MsgClient interface {
// FundCommunityPool defines a method to allow an account to directly fund the community module account.
FundCommunityPool(ctx context.Context, in *MsgFundCommunityPool, opts ...grpc.CallOption) (*MsgFundCommunityPoolResponse, error)
}
type msgClient struct {
cc grpc1.ClientConn
}
func NewMsgClient(cc grpc1.ClientConn) MsgClient {
return &msgClient{cc}
}
func (c *msgClient) FundCommunityPool(ctx context.Context, in *MsgFundCommunityPool, opts ...grpc.CallOption) (*MsgFundCommunityPoolResponse, error) {
out := new(MsgFundCommunityPoolResponse)
err := c.cc.Invoke(ctx, "/kava.community.v1beta1.Msg/FundCommunityPool", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// MsgServer is the server API for Msg service.
type MsgServer interface {
// FundCommunityPool defines a method to allow an account to directly fund the community module account.
FundCommunityPool(context.Context, *MsgFundCommunityPool) (*MsgFundCommunityPoolResponse, error)
}
// UnimplementedMsgServer can be embedded to have forward compatible implementations.
type UnimplementedMsgServer struct {
}
func (*UnimplementedMsgServer) FundCommunityPool(ctx context.Context, req *MsgFundCommunityPool) (*MsgFundCommunityPoolResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method FundCommunityPool not implemented")
}
func RegisterMsgServer(s grpc1.Server, srv MsgServer) {
s.RegisterService(&_Msg_serviceDesc, srv)
}
func _Msg_FundCommunityPool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MsgFundCommunityPool)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MsgServer).FundCommunityPool(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kava.community.v1beta1.Msg/FundCommunityPool",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MsgServer).FundCommunityPool(ctx, req.(*MsgFundCommunityPool))
}
return interceptor(ctx, in, info, handler)
}
var _Msg_serviceDesc = grpc.ServiceDesc{
ServiceName: "kava.community.v1beta1.Msg",
HandlerType: (*MsgServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "FundCommunityPool",
Handler: _Msg_FundCommunityPool_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "kava/community/v1beta1/tx.proto",
}
func (m *MsgFundCommunityPool) 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 *MsgFundCommunityPool) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MsgFundCommunityPool) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Depositor) > 0 {
i -= len(m.Depositor)
copy(dAtA[i:], m.Depositor)
i = encodeVarintTx(dAtA, i, uint64(len(m.Depositor)))
i--
dAtA[i] = 0x12
}
if len(m.Amount) > 0 {
for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintTx(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func (m *MsgFundCommunityPoolResponse) 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 *MsgFundCommunityPoolResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MsgFundCommunityPoolResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
return len(dAtA) - i, nil
}
func encodeVarintTx(dAtA []byte, offset int, v uint64) int {
offset -= sovTx(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *MsgFundCommunityPool) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Amount) > 0 {
for _, e := range m.Amount {
l = e.Size()
n += 1 + l + sovTx(uint64(l))
}
}
l = len(m.Depositor)
if l > 0 {
n += 1 + l + sovTx(uint64(l))
}
return n
}
func (m *MsgFundCommunityPoolResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
return n
}
func sovTx(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozTx(x uint64) (n int) {
return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *MsgFundCommunityPool) 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 ErrIntOverflowTx
}
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: MsgFundCommunityPool: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MsgFundCommunityPool: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
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 ErrIntOverflowTx
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthTx
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthTx
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Amount = append(m.Amount, types.Coin{})
if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Depositor", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTx
}
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 ErrInvalidLengthTx
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthTx
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Depositor = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipTx(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthTx
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MsgFundCommunityPoolResponse) 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 ErrIntOverflowTx
}
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: MsgFundCommunityPoolResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MsgFundCommunityPoolResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipTx(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthTx
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipTx(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, ErrIntOverflowTx
}
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, ErrIntOverflowTx
}
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, ErrIntOverflowTx
}
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, ErrInvalidLengthTx
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupTx
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthTx
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowTx = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group")
)

View File

@ -22,8 +22,8 @@ type Keeper struct {
hardKeeper types.HardKeeper hardKeeper types.HardKeeper
savingsKeeper types.SavingsKeeper savingsKeeper types.SavingsKeeper
// Keeper for community pool transfers // name of module account the community pool deposit/withdraw proposals use
distKeeper types.DistributionKeeper communityPoolMaccName string
} }
// NewKeeper creates a new keeper // NewKeeper creates a new keeper
@ -36,7 +36,7 @@ func NewKeeper(
liquidKeeper types.LiquidKeeper, liquidKeeper types.LiquidKeeper,
hardKeeper types.HardKeeper, hardKeeper types.HardKeeper,
savingsKeeper types.SavingsKeeper, savingsKeeper types.SavingsKeeper,
distKeeper types.DistributionKeeper, communityPoolMaccName string,
) Keeper { ) Keeper {
if !paramstore.HasKeyTable() { if !paramstore.HasKeyTable() {
paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
@ -51,7 +51,8 @@ func NewKeeper(
liquidKeeper: liquidKeeper, liquidKeeper: liquidKeeper,
hardKeeper: hardKeeper, hardKeeper: hardKeeper,
savingsKeeper: savingsKeeper, savingsKeeper: savingsKeeper,
distKeeper: distKeeper,
communityPoolMaccName: communityPoolMaccName,
} }
} }

View File

@ -9,8 +9,13 @@ import (
// HandleCommunityPoolDepositProposal is a handler for executing a passed community pool deposit proposal // HandleCommunityPoolDepositProposal is a handler for executing a passed community pool deposit proposal
func HandleCommunityPoolDepositProposal(ctx sdk.Context, k Keeper, p *types.CommunityPoolDepositProposal) error { func HandleCommunityPoolDepositProposal(ctx sdk.Context, k Keeper, p *types.CommunityPoolDepositProposal) error {
fundAcc := k.accountKeeper.GetModuleAccount(ctx, kavadisttypes.FundModuleAccount) // move funds from community pool to the funding account
if err := k.distKeeper.DistributeFromFeePool(ctx, sdk.NewCoins(p.Amount), fundAcc.GetAddress()); err != nil { if err := k.bankKeeper.SendCoinsFromModuleToModule(
ctx,
k.communityPoolMaccName,
kavadisttypes.FundModuleAccount,
sdk.NewCoins(p.Amount),
); err != nil {
return err return err
} }
@ -32,18 +37,13 @@ func HandleCommunityPoolWithdrawProposal(ctx sdk.Context, k Keeper, p *types.Com
} }
// Move funds to the community pool manually // Move funds to the community pool manually
err = k.bankKeeper.SendCoinsFromModuleToModule( if err := k.bankKeeper.SendCoinsFromModuleToModule(
ctx, ctx,
kavadisttypes.FundModuleAccount, kavadisttypes.FundModuleAccount,
k.distKeeper.GetDistributionAccount(ctx).GetName(), k.communityPoolMaccName,
sdk.NewCoins(withdrawAmount), sdk.NewCoins(withdrawAmount),
) ); err != nil {
if err != nil {
return err return err
} }
feePool := k.distKeeper.GetFeePool(ctx)
newCommunityPool := feePool.CommunityPool.Add(sdk.NewDecCoinFromCoin(withdrawAmount))
feePool.CommunityPool = newCommunityPool
k.distKeeper.SetFeePool(ctx, feePool)
return nil return nil
} }

View File

@ -4,6 +4,7 @@ import (
"testing" "testing"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
communitytypes "github.com/kava-labs/kava/x/community/types"
"github.com/kava-labs/kava/x/earn/keeper" "github.com/kava-labs/kava/x/earn/keeper"
"github.com/kava-labs/kava/x/earn/testutil" "github.com/kava-labs/kava/x/earn/testutil"
"github.com/kava-labs/kava/x/earn/types" "github.com/kava-labs/kava/x/earn/types"
@ -24,15 +25,12 @@ func TestProposalTestSuite(t *testing.T) {
} }
func (suite *proposalTestSuite) TestCommunityDepositProposal() { func (suite *proposalTestSuite) TestCommunityDepositProposal() {
distKeeper := suite.App.GetDistrKeeper()
ctx := suite.Ctx ctx := suite.Ctx
macc := distKeeper.GetDistributionAccount(ctx) macc := suite.App.GetAccountKeeper().GetModuleAccount(ctx, communitytypes.ModuleAccountName)
fundAmount := sdk.NewCoins(sdk.NewInt64Coin("ukava", 100000000)) fundAmount := sdk.NewCoins(sdk.NewInt64Coin("ukava", 100000000))
depositAmount := sdk.NewCoin("ukava", sdk.NewInt(10000000)) depositAmount := sdk.NewCoin("ukava", sdk.NewInt(10000000))
suite.Require().NoError(suite.App.FundModuleAccount(ctx, macc.GetName(), fundAmount)) 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) suite.CreateVault("ukava", types.StrategyTypes{types.STRATEGY_TYPE_SAVINGS}, false, nil)
prop := types.NewCommunityPoolDepositProposal("test title", prop := types.NewCommunityPoolDepositProposal("test title",
"desc", depositAmount) "desc", depositAmount)
@ -41,22 +39,18 @@ func (suite *proposalTestSuite) TestCommunityDepositProposal() {
balance := suite.BankKeeper.GetAllBalances(ctx, macc.GetAddress()) balance := suite.BankKeeper.GetAllBalances(ctx, macc.GetAddress())
suite.Require().Equal(fundAmount.Sub(sdk.NewCoins(depositAmount)), balance) suite.Require().Equal(fundAmount.Sub(sdk.NewCoins(depositAmount)), balance)
feePool = distKeeper.GetFeePool(ctx)
communityPoolBalance, change := feePool.CommunityPool.TruncateDecimal() communityPoolBalance := suite.App.GetCommunityKeeper().GetModuleAccountBalance(ctx)
suite.Require().Equal(fundAmount.Sub(sdk.NewCoins(depositAmount)), communityPoolBalance) suite.Require().Equal(fundAmount.Sub(sdk.NewCoins(depositAmount)), communityPoolBalance)
suite.Require().True(change.Empty())
} }
func (suite *proposalTestSuite) TestCommunityWithdrawProposal() { func (suite *proposalTestSuite) TestCommunityWithdrawProposal() {
distKeeper := suite.App.GetDistrKeeper()
ctx := suite.Ctx ctx := suite.Ctx
macc := distKeeper.GetDistributionAccount(ctx) macc := suite.App.GetAccountKeeper().GetModuleAccount(ctx, communitytypes.ModuleAccountName)
fundAmount := sdk.NewCoins(sdk.NewInt64Coin("ukava", 100000000)) fundAmount := sdk.NewCoins(sdk.NewInt64Coin("ukava", 100000000))
depositAmount := sdk.NewCoin("ukava", sdk.NewInt(10000000)) depositAmount := sdk.NewCoin("ukava", sdk.NewInt(10000000))
suite.Require().NoError(suite.App.FundModuleAccount(ctx, macc.GetName(), fundAmount)) 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 // TODO update to STRATEGY_TYPE_SAVINGS once implemented
suite.CreateVault("ukava", types.StrategyTypes{types.STRATEGY_TYPE_SAVINGS}, false, nil) suite.CreateVault("ukava", types.StrategyTypes{types.STRATEGY_TYPE_SAVINGS}, false, nil)
deposit := types.NewCommunityPoolDepositProposal("test title", deposit := types.NewCommunityPoolDepositProposal("test title",
@ -73,8 +67,7 @@ func (suite *proposalTestSuite) TestCommunityWithdrawProposal() {
suite.Require().NoError(err) suite.Require().NoError(err)
balance = suite.BankKeeper.GetAllBalances(ctx, macc.GetAddress()) balance = suite.BankKeeper.GetAllBalances(ctx, macc.GetAddress())
suite.Require().Equal(fundAmount, balance) suite.Require().Equal(fundAmount, balance)
feePool = distKeeper.GetFeePool(ctx)
communityPoolBalance, change := feePool.CommunityPool.TruncateDecimal() communityPoolBalance := suite.App.GetCommunityKeeper().GetModuleAccountBalance(ctx)
suite.Require().Equal(fundAmount, communityPoolBalance) suite.Require().Equal(fundAmount, communityPoolBalance)
suite.Require().True(change.Empty())
} }

View File

@ -3,7 +3,6 @@ package types
import ( import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/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" hardtypes "github.com/kava-labs/kava/x/hard/types"
savingstypes "github.com/kava-labs/kava/x/savings/types" savingstypes "github.com/kava-labs/kava/x/savings/types"
@ -26,14 +25,6 @@ type BankKeeper interface {
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error 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. // LiquidKeeper defines the expected interface needed for derivative to staked token conversions.
type LiquidKeeper interface { type LiquidKeeper interface {
GetStakedTokensForDerivatives(ctx sdk.Context, derivatives sdk.Coins) (sdk.Coin, error) GetStakedTokensForDerivatives(ctx sdk.Context, derivatives sdk.Coins) (sdk.Coin, error)

View File

@ -69,6 +69,8 @@ func (suite *EarnStakingRewardsIntegrationTestSuite) SetupTest() {
WithMinter(sdk.OneDec(), sdk.ZeroDec()). WithMinter(sdk.OneDec(), sdk.ZeroDec()).
WithMintDenom("ukava") WithMintDenom("ukava")
kavamintBuilder := testutil.NewKavamintGenesisBuilder()
suite.StartChainWithBuilders( suite.StartChainWithBuilders(
authBuilder, authBuilder,
incentiveBuilder, incentiveBuilder,
@ -76,6 +78,7 @@ func (suite *EarnStakingRewardsIntegrationTestSuite) SetupTest() {
earnBuilder, earnBuilder,
stakingBuilder, stakingBuilder,
mintBuilder, mintBuilder,
kavamintBuilder,
) )
} }

View File

@ -0,0 +1,40 @@
package testutil
import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/app"
kavaminttypes "github.com/kava-labs/kava/x/kavamint/types"
)
// KavamintGenesisBuilder is a tool for creating a mint genesis state.
// Helper methods add values onto a default genesis state.
// All methods are immutable and return updated copies of the builder.
type KavamintGenesisBuilder struct {
kavaminttypes.GenesisState
}
var _ GenesisBuilder = (*KavamintGenesisBuilder)(nil)
func NewKavamintGenesisBuilder() KavamintGenesisBuilder {
gen := kavaminttypes.DefaultGenesisState()
gen.Params.CommunityPoolInflation = sdk.ZeroDec()
gen.Params.StakingRewardsApy = sdk.ZeroDec()
return KavamintGenesisBuilder{
GenesisState: *gen,
}
}
func (builder KavamintGenesisBuilder) Build() kavaminttypes.GenesisState {
return builder.GenesisState
}
func (builder KavamintGenesisBuilder) BuildMarshalled(cdc codec.JSONCodec) app.GenesisState {
built := builder.Build()
return app.GenesisState{
kavaminttypes.ModuleName: cdc.MustMarshalJSON(&built),
}
}

View File

@ -17,8 +17,8 @@ type Keeper struct {
cdc codec.BinaryCodec cdc codec.BinaryCodec
paramSubspace paramtypes.Subspace paramSubspace paramtypes.Subspace
bankKeeper types.BankKeeper bankKeeper types.BankKeeper
distKeeper types.DistKeeper
accountKeeper types.AccountKeeper accountKeeper types.AccountKeeper
communityKeeper types.CommunityKeeper
blacklistedAddrs map[string]bool blacklistedAddrs map[string]bool
} }
@ -26,7 +26,7 @@ type Keeper struct {
// NewKeeper creates a new keeper // NewKeeper creates a new keeper
func NewKeeper( func NewKeeper(
cdc codec.BinaryCodec, key sdk.StoreKey, paramstore paramtypes.Subspace, bk types.BankKeeper, ak types.AccountKeeper, cdc codec.BinaryCodec, key sdk.StoreKey, paramstore paramtypes.Subspace, bk types.BankKeeper, ak types.AccountKeeper,
dk types.DistKeeper, blacklistedAddrs map[string]bool, ck types.CommunityKeeper, blacklistedAddrs map[string]bool,
) Keeper { ) Keeper {
if !paramstore.HasKeyTable() { if !paramstore.HasKeyTable() {
paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
@ -37,8 +37,8 @@ func NewKeeper(
cdc: cdc, cdc: cdc,
paramSubspace: paramstore, paramSubspace: paramstore,
bankKeeper: bk, bankKeeper: bk,
distKeeper: dk,
accountKeeper: ak, accountKeeper: ak,
communityKeeper: ck,
blacklistedAddrs: blacklistedAddrs, blacklistedAddrs: blacklistedAddrs,
} }
} }

View File

@ -13,7 +13,7 @@ func HandleCommunityPoolMultiSpendProposal(ctx sdk.Context, k Keeper, p *types.C
if k.blacklistedAddrs[receiverInfo.Address] { if k.blacklistedAddrs[receiverInfo.Address] {
return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is blacklisted from receiving external funds", receiverInfo.Address) return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is blacklisted from receiving external funds", receiverInfo.Address)
} }
err := k.distKeeper.DistributeFromFeePool(ctx, receiverInfo.Amount, receiverInfo.GetAddress()) err := k.communityKeeper.DistributeFromCommunityPool(ctx, receiverInfo.GetAddress(), receiverInfo.Amount)
if err != nil { if err != nil {
return err return err
} }

View File

@ -3,21 +3,22 @@ package keeper_test
import ( import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
communitytypes "github.com/kava-labs/kava/x/community/types"
"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"
) )
func (suite *keeperTestSuite) TestHandleCommunityPoolMultiSpendProposal() { func (suite *keeperTestSuite) TestHandleCommunityPoolMultiSpendProposal() {
addr, distrKeeper, ctx := suite.Addrs[0], suite.App.GetDistrKeeper(), suite.Ctx addr, communityKeeper, ctx := suite.Addrs[0], suite.App.GetCommunityKeeper(), suite.Ctx
initBalances := suite.BankKeeper.GetAllBalances(ctx, addr) initBalances := suite.BankKeeper.GetAllBalances(ctx, addr)
// add coins to the module account and fund fee pool // add coins to the module account and fund community pool
macc := distrKeeper.GetDistributionAccount(ctx) initialFunds := int64(1000000)
fundAmount := sdk.NewCoins(sdk.NewInt64Coin("ukava", 1000000)) fundAmount := sdk.NewCoins(sdk.NewInt64Coin("ukava", initialFunds))
suite.Require().NoError(suite.App.FundModuleAccount(ctx, macc.GetName(), fundAmount)) suite.Require().NoError(suite.App.FundModuleAccount(ctx, communitytypes.ModuleAccountName, fundAmount))
feePool := distrKeeper.GetFeePool(ctx) // expect funds to start in community pool
feePool.CommunityPool = sdk.NewDecCoinsFromCoins(fundAmount...) commPoolFunds := communityKeeper.GetModuleAccountBalance(ctx)
distrKeeper.SetFeePool(ctx, feePool) suite.Require().True(fundAmount.IsEqual(commPoolFunds))
proposalAmount1 := int64(1100) proposalAmount1 := int64(1100)
proposalAmount2 := int64(1200) proposalAmount2 := int64(1200)
@ -35,6 +36,12 @@ func (suite *keeperTestSuite) TestHandleCommunityPoolMultiSpendProposal() {
suite.Require().Nil(err) suite.Require().Nil(err)
balances := suite.BankKeeper.GetAllBalances(ctx, addr) balances := suite.BankKeeper.GetAllBalances(ctx, addr)
// expect funds to be transferred to recipient
expected := initBalances.AmountOf("ukava").Add(sdk.NewInt(proposalAmount1 + proposalAmount2)) expected := initBalances.AmountOf("ukava").Add(sdk.NewInt(proposalAmount1 + proposalAmount2))
suite.Require().Equal(expected, balances.AmountOf("ukava")) suite.Require().Equal(expected, balances.AmountOf("ukava"))
// expect funds to be deducted from community pool
expectedCommPool := commPoolFunds.AmountOf("ukava").SubRaw(proposalAmount1 + proposalAmount2)
suite.Require().Equal(expectedCommPool, communityKeeper.GetModuleAccountBalance(ctx).AmountOf("ukava"))
} }

View File

@ -5,9 +5,9 @@ import (
authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" authTypes "github.com/cosmos/cosmos-sdk/x/auth/types"
) )
// DistKeeper defines the expected distribution keeper interface // CommunityKeeper defines the expected community keeper interface
type DistKeeper interface { type CommunityKeeper interface {
DistributeFromFeePool(ctx sdk.Context, amount sdk.Coins, receiveAddr sdk.AccAddress) error DistributeFromCommunityPool(ctx sdk.Context, sender sdk.AccAddress, amount sdk.Coins) error
} }
// AccountKeeper defines the expected account keeper interface // AccountKeeper defines the expected account keeper interface

75
x/kavamint/abci.go Normal file
View File

@ -0,0 +1,75 @@
package kavamint
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/kavamint/keeper"
"github.com/kava-labs/kava/x/kavamint/types"
)
// BeginBlocker mints & distributes new tokens for the previous block.
func BeginBlocker(ctx sdk.Context, k keeper.KeeperI) {
params := k.GetParams(ctx)
// determine seconds since last mint
previousBlockTime := k.GetPreviousBlockTime(ctx)
if previousBlockTime.IsZero() {
previousBlockTime = ctx.BlockTime()
}
secondsPassed := ctx.BlockTime().Sub(previousBlockTime).Seconds()
// calculate totals before any minting is done to prevent new mints affecting the values
totalSupply := k.TotalSupply(ctx)
totalBonded := k.TotalBondedTokens(ctx)
// ------------- Staking Rewards -------------
stakingRewardCoins, err := k.AccumulateInflation(
ctx, params.StakingRewardsApy, totalBonded, secondsPassed,
)
if err != nil {
panic(err)
}
// mint staking rewards
if err := k.MintCoins(ctx, stakingRewardCoins); err != nil {
panic(err)
}
// send staking rewards to auth fee collector for distribution to validators
if err := k.AddCollectedFees(ctx, stakingRewardCoins); err != nil {
panic(err)
}
// ------------- Community Pool -------------
communityPoolInflation, err := k.AccumulateInflation(
ctx, params.CommunityPoolInflation, totalSupply, secondsPassed,
)
if err != nil {
panic(err)
}
// mint community pool inflation
if err := k.MintCoins(ctx, communityPoolInflation); err != nil {
panic(err)
}
// send inflation coins to the community pool (x/community module account)
if err := k.FundCommunityPool(ctx, communityPoolInflation); err != nil {
panic(err)
}
// ------------- Bookkeeping -------------
// bookkeep the previous block time
k.SetPreviousBlockTime(ctx, ctx.BlockTime())
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeMint,
sdk.NewAttribute(types.AttributeKeyTotalSupply, totalSupply.String()),
sdk.NewAttribute(types.AttributeKeyTotalBonded, totalBonded.String()),
sdk.NewAttribute(types.AttributeSecondsPassed, fmt.Sprintf("%f", secondsPassed)),
sdk.NewAttribute(types.AttributeKeyCommunityPoolMint, communityPoolInflation.String()),
sdk.NewAttribute(types.AttributeKeyStakingRewardMint, stakingRewardCoins.String()),
),
)
}

196
x/kavamint/abci_test.go Normal file
View File

@ -0,0 +1,196 @@
package kavamint_test
import (
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/stretchr/testify/suite"
communitytypes "github.com/kava-labs/kava/x/community/types"
"github.com/kava-labs/kava/x/kavamint"
"github.com/kava-labs/kava/x/kavamint/keeper"
"github.com/kava-labs/kava/x/kavamint/testutil"
"github.com/kava-labs/kava/x/kavamint/types"
)
type abciTestSuite struct {
testutil.KavamintTestSuite
}
func (suite *abciTestSuite) SetupTest() {
suite.KavamintTestSuite.SetupTest()
}
func (suite abciTestSuite) CheckModuleBalance(ctx sdk.Context, moduleName string, expectedAmount sdk.Int) {
denom := suite.StakingKeeper.BondDenom(ctx)
amount := suite.App.GetModuleAccountBalance(ctx, moduleName, denom)
suite.Require().Equal(expectedAmount, amount)
}
func (suite *abciTestSuite) CheckFeeCollectorBalance(ctx sdk.Context, expectedAmount sdk.Int) {
suite.CheckModuleBalance(ctx, authtypes.FeeCollectorName, expectedAmount)
}
func (suite *abciTestSuite) CheckKavamintBalance(ctx sdk.Context, expectedAmount sdk.Int) {
suite.CheckModuleBalance(ctx, types.ModuleName, expectedAmount)
}
func (suite *abciTestSuite) CheckCommunityPoolBalance(ctx sdk.Context, expectedAmount sdk.Int) {
suite.CheckModuleBalance(ctx, communitytypes.ModuleAccountName, expectedAmount)
}
func TestABCITestSuite(t *testing.T) {
suite.Run(t, new(abciTestSuite))
}
func (suite *abciTestSuite) Test_BeginBlocker_MintsExpectedTokens() {
testCases := []struct {
name string
blockTime uint64
communityPoolInflation sdk.Dec
stakingRewardsApy sdk.Dec
bondedRatio sdk.Dec
setup func()
expCommunityPoolBalance sdk.Int
expFeeCollectorBalance sdk.Int
}{
{
name: "sanity check: a year of seconds mints total yearly inflation",
blockTime: keeper.SecondsPerYear,
communityPoolInflation: sdk.NewDecWithPrec(20, 2),
stakingRewardsApy: sdk.NewDecWithPrec(20, 2),
bondedRatio: sdk.NewDecWithPrec(50, 2),
// 20% inflation on 1e10 tokens -> 2e9 minted
expCommunityPoolBalance: sdk.NewInt(2e9),
// 20% APY, 50% bonded (5e9) -> 1e9 minted
expFeeCollectorBalance: sdk.NewInt(1e9),
},
{
name: "mints staking rewards, handles 0 community pool inflation",
blockTime: 6,
communityPoolInflation: sdk.ZeroDec(),
stakingRewardsApy: sdk.NewDecWithPrec(20, 2),
bondedRatio: sdk.NewDecWithPrec(40, 2),
expCommunityPoolBalance: sdk.ZeroInt(),
// 20% APY for 6 seconds
// bond ratio is 40%, so total supply * ratio = 1e10 * .4
// https://www.wolframalpha.com/input?i2d=true&i=%5C%2840%29Power%5B%5C%2840%29Surd%5B1.20%2C31536000%5D%5C%2841%29%2C6%5D-1%5C%2841%29*1e10*.4
// => 138.75 => truncated to 138 tokens.
expFeeCollectorBalance: sdk.NewInt(138),
},
{
name: "mints community pool inflation, handles 0 staking rewards",
blockTime: 6,
communityPoolInflation: sdk.NewDecWithPrec(80, 2),
stakingRewardsApy: sdk.ZeroDec(),
bondedRatio: sdk.NewDecWithPrec(40, 2),
// 80% APY for 6 seconds
// total supply = 1e10
// https://www.wolframalpha.com/input?i2d=true&i=%5C%2840%29Power%5B%5C%2840%29Surd%5B1.80%2C31536000%5D%5C%2841%29%2C6%5D-1%5C%2841%29*1e10
// => 1118.32 => truncated to 1118 tokens.
expCommunityPoolBalance: sdk.NewInt(1118),
expFeeCollectorBalance: sdk.ZeroInt(),
},
{
name: "mints no tokens if all inflation is zero",
blockTime: 6,
communityPoolInflation: sdk.ZeroDec(),
stakingRewardsApy: sdk.ZeroDec(),
bondedRatio: sdk.NewDecWithPrec(40, 2),
expCommunityPoolBalance: sdk.ZeroInt(),
expFeeCollectorBalance: sdk.ZeroInt(),
},
{
name: "mints community pool inflation and staking rewards",
blockTime: 6,
communityPoolInflation: sdk.NewDecWithPrec(50, 2),
stakingRewardsApy: sdk.NewDecWithPrec(20, 2),
bondedRatio: sdk.NewDecWithPrec(35, 2),
// 50% APY for 6 seconds
// total supply = 1e10
// https://www.wolframalpha.com/input?i2d=true&i=%5C%2840%29Power%5B%5C%2840%29Surd%5B1.5%2C31536000%5D%5C%2841%29%2C6%5D-1%5C%2841%29*1e10
// => 771.43 => truncated to 771 tokens.
expCommunityPoolBalance: sdk.NewInt(771),
// 20% APY for 6 seconds
// total bonded = 1e10 * 35%
// https://www.wolframalpha.com/input?i2d=true&i=%5C%2840%29Power%5B%5C%2840%29Surd%5B1.20%2C31536000%5D%5C%2841%29%2C6%5D-1%5C%2841%29*1e10*.35
// => 121.41 => truncated to 121 tokens.
expFeeCollectorBalance: sdk.NewInt(121),
},
{
name: "handles extra long block time",
blockTime: 60, // like if we're upgrading the network & it takes an hour to get back up
communityPoolInflation: sdk.NewDecWithPrec(50, 2),
stakingRewardsApy: sdk.NewDecWithPrec(20, 2),
bondedRatio: sdk.NewDecWithPrec(35, 2),
// https://www.wolframalpha.com/input?i2d=true&i=%5C%2840%29Power%5B%5C%2840%29Surd%5B1.5%2C31536000%5D%5C%2841%29%2C60%5D-1%5C%2841%29*1e10
expCommunityPoolBalance: sdk.NewInt(7714),
// https://www.wolframalpha.com/input?i2d=true&i=%5C%2840%29Power%5B%5C%2840%29Surd%5B1.20%2C31536000%5D%5C%2841%29%2C60%5D-1%5C%2841%29*1e10*.35
expFeeCollectorBalance: sdk.NewInt(1214),
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest()
// set store and params
suite.Keeper.SetPreviousBlockTime(suite.Ctx, suite.Ctx.BlockTime())
suite.Keeper.SetParams(
suite.Ctx,
types.NewParams(tc.communityPoolInflation, tc.stakingRewardsApy),
)
// set bonded token ratio
suite.SetBondedTokenRatio(tc.bondedRatio)
staking.EndBlocker(suite.Ctx, suite.StakingKeeper)
// run begin blocker
kavamint.BeginBlocker(suite.Ctx, suite.Keeper)
// expect everything empty to start
suite.CheckFeeCollectorBalance(suite.Ctx, sdk.ZeroInt())
suite.CheckKavamintBalance(suite.Ctx, sdk.ZeroInt())
suite.CheckCommunityPoolBalance(suite.Ctx, sdk.ZeroInt())
// expect initial block time set
startBlockTime := suite.Keeper.GetPreviousBlockTime(suite.Ctx)
suite.Require().False(startBlockTime.IsZero())
suite.Require().Equal(suite.Ctx.BlockTime(), startBlockTime)
// run begin blocker again to mint inflation
ctx2 := suite.Ctx.WithBlockTime(suite.Ctx.BlockTime().Add(time.Second * time.Duration(tc.blockTime)))
kavamint.BeginBlocker(ctx2, suite.Keeper)
// check expected balances
suite.CheckCommunityPoolBalance(ctx2, tc.expCommunityPoolBalance)
suite.CheckFeeCollectorBalance(ctx2, tc.expFeeCollectorBalance)
// x/kavamint balance should always be 0 because 100% should be transferred out every block
suite.CheckKavamintBalance(ctx2, sdk.ZeroInt())
// expect time to be updated
endBlockTime := suite.Keeper.GetPreviousBlockTime(ctx2)
suite.Require().False(endBlockTime.IsZero())
suite.Require().Equal(ctx2.BlockTime(), endBlockTime)
})
}
}
func (suite *abciTestSuite) Test_BeginBlocker_DefaultsToBlockTime() {
suite.SetupTest()
// unset previous block time
suite.Keeper.SetPreviousBlockTime(suite.Ctx, time.Time{})
// run begin blocker
kavamint.BeginBlocker(suite.Ctx, suite.Keeper)
// ensure block time gets set
blockTime := suite.Keeper.GetPreviousBlockTime(suite.Ctx)
suite.False(blockTime.IsZero())
suite.Equal(suite.Ctx.BlockTime(), blockTime)
}

View File

@ -0,0 +1,89 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/kava-labs/kava/x/kavamint/types"
)
// GetQueryCmd returns the cli query commands for the minting module.
func GetQueryCmd() *cobra.Command {
mintingQueryCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Querying commands for the minting module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
mintingQueryCmd.AddCommand(
GetCmdQueryParams(),
GetCmdQueryInflation(),
)
return mintingQueryCmd
}
// GetCmdQueryParams implements a command to return the current minting
// parameters.
func GetCmdQueryParams() *cobra.Command {
cmd := &cobra.Command{
Use: "params",
Short: "Query the current minting parameters",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
params := &types.QueryParamsRequest{}
res, err := queryClient.Params(cmd.Context(), params)
if err != nil {
return err
}
return clientCtx.PrintProto(&res.Params)
},
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}
// GetCmdQueryInflation implements a command to return the current minting
// inflation value.
func GetCmdQueryInflation() *cobra.Command {
cmd := &cobra.Command{
Use: "inflation",
Short: "Query the current cumulative minting inflation value",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
params := &types.QueryInflationRequest{}
res, err := queryClient.Inflation(cmd.Context(), params)
if err != nil {
return err
}
return clientCtx.PrintString(fmt.Sprintf("%s\n", res.Inflation))
},
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}

38
x/kavamint/genesis.go Normal file
View File

@ -0,0 +1,38 @@
package kavamint
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/kava-labs/kava/x/kavamint/keeper"
"github.com/kava-labs/kava/x/kavamint/types"
)
// InitGenesis new mint genesis
func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, ak types.AccountKeeper, gs *types.GenesisState) {
// guard against invalid genesis
if err := gs.Validate(); err != nil {
panic(fmt.Sprintf("failed to validate %s genesis state: %s", types.ModuleName, err))
}
// get module account -- creates one with allowed permissions if it does not exist
macc := ak.GetModuleAccount(ctx, types.ModuleName)
if macc == nil {
panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
}
// check module account has minter permissions
if !macc.HasPermission(authtypes.Minter) {
panic(fmt.Sprintf("%s module account does not have %s permissions", types.ModuleName, authtypes.Minter))
}
// set store state from genesis
keeper.SetParams(ctx, gs.Params)
keeper.SetPreviousBlockTime(ctx, gs.PreviousBlockTime)
}
// ExportGenesis returns a GenesisState for a given context and keeper.
func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) *types.GenesisState {
return types.NewGenesisState(keeper.GetParams(ctx), keeper.GetPreviousBlockTime(ctx))
}

117
x/kavamint/genesis_test.go Normal file
View File

@ -0,0 +1,117 @@
package kavamint_test
import (
"testing"
"time"
"github.com/kava-labs/kava/x/kavamint"
"github.com/kava-labs/kava/x/kavamint/testutil"
"github.com/kava-labs/kava/x/kavamint/types"
"github.com/stretchr/testify/suite"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)
type genesisTestSuite struct {
testutil.KavamintTestSuite
}
func (suite *genesisTestSuite) Test_InitGenesis_ValidationPanic() {
invalidState := types.NewGenesisState(
types.NewParams(sdk.OneDec(), types.MaxMintingRate.Add(sdk.OneDec())), // rate over max
time.Time{},
)
suite.Panics(func() {
kavamint.InitGenesis(suite.Ctx, suite.Keeper, suite.App.GetAccountKeeper(), invalidState)
}, "expected init genesis to panic with invalid state")
}
func (suite *genesisTestSuite) Test_InitGenesis_ModuleAccountDoesNotHaveMinterPerms() {
gs := types.DefaultGenesisState()
ak := suite.App.GetAccountKeeper()
macc := ak.GetModuleAccount(suite.Ctx, types.ModuleName)
suite.Require().NotNil(macc)
m, ok := macc.(*authtypes.ModuleAccount)
suite.Require().True(ok)
m.Permissions = []string{}
ak.SetAccount(suite.Ctx, m)
suite.PanicsWithValue("kavamint module account does not have minter permissions", func() {
kavamint.InitGenesis(suite.Ctx, suite.Keeper, suite.App.GetAccountKeeper(), gs)
})
}
func (suite *genesisTestSuite) Test_InitGenesis_CreatesModuleAccountWithPermissions() {
gs := types.DefaultGenesisState()
ak := suite.App.GetAccountKeeper()
kavamint.InitGenesis(suite.Ctx, suite.Keeper, ak, gs)
// by pass auto creation of module accounts
addr, _ := ak.GetModuleAddressAndPermissions(types.ModuleName)
acc := suite.App.GetAccountKeeper().GetAccount(suite.Ctx, addr)
suite.Require().NotNil(acc)
macc, ok := acc.(authtypes.ModuleAccountI)
suite.Require().True(ok)
suite.True(macc.HasPermission(authtypes.Minter))
}
func (suite *genesisTestSuite) Test_InitAndExportGenesis_DefaultValues() {
state := types.DefaultGenesisState()
kavamint.InitGenesis(suite.Ctx, suite.Keeper, suite.App.GetAccountKeeper(), state)
suite.Equal(types.DefaultParams(), suite.Keeper.GetParams(suite.Ctx), "expected default params to bet set in store")
storeTime := suite.Keeper.GetPreviousBlockTime(suite.Ctx)
suite.Equal(types.DefaultPreviousBlockTime, storeTime, "expected default previous block time to be set in store")
exportedState := kavamint.ExportGenesis(suite.Ctx, suite.Keeper)
suite.Equal(state, exportedState, "expected exported state to match imported state")
}
func (suite *genesisTestSuite) Test_InitAndExportGenesis_SetValues() {
prevBlockTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC)
state := types.NewGenesisState(
types.NewParams(sdk.MustNewDecFromStr("0.000000000000000001"), sdk.MustNewDecFromStr("0.000000000000000002")),
prevBlockTime,
)
kavamint.InitGenesis(suite.Ctx, suite.Keeper, suite.App.GetAccountKeeper(), state)
suite.Equal(state.Params, suite.Keeper.GetParams(suite.Ctx), "expected params to bet set in store")
storeTime := suite.Keeper.GetPreviousBlockTime(suite.Ctx)
suite.Equal(state.PreviousBlockTime, storeTime, "expected previous block time to be set in store")
exportedState := kavamint.ExportGenesis(suite.Ctx, suite.Keeper)
suite.Equal(state, exportedState, "expected exported state to match imported state")
}
func (suite *genesisTestSuite) Test_InitAndExportGenesis_ZeroValues() {
state := types.NewGenesisState(
types.NewParams(sdk.ZeroDec(), sdk.ZeroDec()),
time.Time{},
)
kavamint.InitGenesis(suite.Ctx, suite.Keeper, suite.App.GetAccountKeeper(), state)
suite.Equal(state.Params, suite.Keeper.GetParams(suite.Ctx), "expected params to bet set in store")
storeTime := suite.Keeper.GetPreviousBlockTime(suite.Ctx)
suite.Equal(state.PreviousBlockTime, storeTime, "expected previous block time to be set in store")
exportedState := kavamint.ExportGenesis(suite.Ctx, suite.Keeper)
suite.Equal(state, exportedState, "expected exported state to match imported state")
}
func TestGenesisTestSuite(t *testing.T) {
suite.Run(t, new(genesisTestSuite))
}

View File

@ -0,0 +1,26 @@
package keeper
import (
"context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/kavamint/types"
)
var _ types.QueryServer = Keeper{}
// Params returns params of the mint module.
func (k Keeper) Params(c context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
params := k.GetParams(ctx)
return &types.QueryParamsResponse{Params: params}, nil
}
// Inflation returns minter.Inflation of the mint module.
func (k Keeper) Inflation(c context.Context, _ *types.QueryInflationRequest) (*types.QueryInflationResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
inflation := k.CumulativeInflation(ctx)
return &types.QueryInflationResponse{Inflation: inflation}, nil
}

View File

@ -0,0 +1,121 @@
package keeper_test
import (
"context"
"testing"
"github.com/stretchr/testify/suite"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/kava-labs/kava/x/kavamint/testutil"
"github.com/kava-labs/kava/x/kavamint/types"
)
type grpcQueryTestSuite struct {
testutil.KavamintTestSuite
queryClient types.QueryClient
}
func (suite *grpcQueryTestSuite) SetupTest() {
suite.KavamintTestSuite.SetupTest()
queryHelper := baseapp.NewQueryServerTestHelper(suite.Ctx, suite.App.InterfaceRegistry())
types.RegisterQueryServer(queryHelper, suite.Keeper)
suite.queryClient = types.NewQueryClient(queryHelper)
}
func TestGRPCQueryTestSuite(t *testing.T) {
suite.Run(t, new(grpcQueryTestSuite))
}
func (suite *grpcQueryTestSuite) TestGRPCQueryParams() {
app, ctx, queryClient := suite.App, suite.Ctx, suite.queryClient
kavamintKeeper := app.GetKavamintKeeper()
params, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{})
suite.Require().NoError(err)
suite.Require().Equal(params.Params, kavamintKeeper.GetParams(ctx))
}
func (suite *grpcQueryTestSuite) TestGRPCInflationQuery() {
testCases := []struct {
name string
communityInflation sdk.Dec
stakingApy sdk.Dec
bondedRatio sdk.Dec
expectedInflation sdk.Dec
}{
{
name: "no community inflation, no staking apy = no inflation",
communityInflation: sdk.NewDec(0),
stakingApy: sdk.NewDec(0),
bondedRatio: sdk.NewDecWithPrec(40, 2),
expectedInflation: sdk.NewDec(0),
},
{
name: "no community inflation means only staking contributes",
communityInflation: sdk.NewDec(0),
stakingApy: sdk.NewDec(1),
bondedRatio: sdk.NewDecWithPrec(34, 2),
expectedInflation: sdk.NewDecWithPrec(34, 2),
},
{
name: "no staking apy means only inflation contributes",
communityInflation: sdk.NewDecWithPrec(75, 2),
stakingApy: sdk.NewDec(0),
bondedRatio: sdk.NewDecWithPrec(40, 2),
expectedInflation: sdk.NewDecWithPrec(75, 2),
},
{
name: "staking and community inflation combines (100 percent bonded)",
communityInflation: sdk.NewDec(1),
stakingApy: sdk.NewDecWithPrec(50, 2),
bondedRatio: sdk.NewDec(1),
expectedInflation: sdk.NewDecWithPrec(150, 2),
},
{
name: "staking and community inflation combines (40 percent bonded)",
communityInflation: sdk.NewDecWithPrec(90, 2),
stakingApy: sdk.NewDecWithPrec(25, 2),
bondedRatio: sdk.NewDecWithPrec(40, 2),
// 90 + .4*25 = 100
expectedInflation: sdk.NewDec(1),
},
{
name: "staking and community inflation combines (25 percent bonded)",
communityInflation: sdk.NewDecWithPrec(90, 2),
stakingApy: sdk.NewDecWithPrec(20, 2),
bondedRatio: sdk.NewDecWithPrec(25, 2),
// 90 + .25*20 = 95
expectedInflation: sdk.NewDecWithPrec(95, 2),
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest()
app, ctx, queryClient := suite.App, suite.Ctx, suite.queryClient
kavamintKeeper := app.GetKavamintKeeper()
// set desired params
kavamintKeeper.SetParams(ctx, types.NewParams(tc.communityInflation, tc.stakingApy))
// set bonded token ratio
suite.SetBondedTokenRatio(tc.bondedRatio)
staking.EndBlocker(ctx, suite.StakingKeeper)
// query inflation & check for expected results
inflation, err := queryClient.Inflation(context.Background(), &types.QueryInflationRequest{})
suite.Require().NoError(err)
suite.Require().Equal(inflation.Inflation, kavamintKeeper.CumulativeInflation(ctx))
suite.Require().Equal(inflation.Inflation, tc.expectedInflation)
})
}
}

View File

@ -0,0 +1,54 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// this is the same value used in the x/hard
const (
SecondsPerYear = uint64(31536000)
)
// AccumulateInflation calculates the number of coins that should be minted to match a yearly `rate`
// for interest compounded each second of the year over `secondsSinceLastMint` seconds.
// `basis` is the base amount of coins that is inflated.
func (k Keeper) AccumulateInflation(
ctx sdk.Context,
rate sdk.Dec,
basis sdk.Int,
secondsSinceLastMint float64,
) (sdk.Coins, error) {
bondDenom := k.BondDenom(ctx)
// calculate the rate factor based on apy & seconds passed since last block
inflationRate, err := CalculateInflationRate(rate, uint64(secondsSinceLastMint))
if err != nil {
return sdk.NewCoins(), err
}
amount := inflationRate.MulInt(basis).TruncateInt()
return sdk.NewCoins(sdk.NewCoin(bondDenom, amount)), nil
}
// CalculateInflationRate converts an APY into the factor corresponding with that APY's accumulation
// over a period of secondsPassed seconds.
func CalculateInflationRate(apy sdk.Dec, secondsPassed uint64) (sdk.Dec, error) {
perSecondInterestRate, err := apyToSpy(apy.Add(sdk.OneDec()))
if err != nil {
return sdk.ZeroDec(), err
}
rate := perSecondInterestRate.Power(secondsPassed)
return rate.Sub(sdk.OneDec()), nil
}
// apyToSpy converts the input annual interest rate. For example, 10% apy would be passed as 1.10.
// SPY = Per second compounded interest rate is how cosmos mathematically represents APY.
func apyToSpy(apy sdk.Dec) (sdk.Dec, error) {
// Note: any APY greater than 176.5 will cause an out-of-bounds error
root, err := apy.ApproxRoot(SecondsPerYear)
if err != nil {
return sdk.ZeroDec(), err
}
return root, nil
}

View File

@ -0,0 +1,142 @@
package keeper_test
import (
"fmt"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/kavamint/keeper"
"github.com/kava-labs/kava/x/kavamint/types"
"github.com/stretchr/testify/suite"
)
type InflationTestSuite struct {
suite.Suite
}
func TestInflationTestSuite(t *testing.T) {
suite.Run(t, new(InflationTestSuite))
}
func (suite *InflationTestSuite) TestCalculateInflationFactor() {
testCases := []struct {
name string
apy sdk.Dec
secondsPassed uint64
expectedRate sdk.Dec
// preciseToDecimal is the decimal place after which error is present. if >=18, number is exact.
// ex. precise to 3 decimals means accurate to +/-0.0005
preciseToDecimal int
}{
{
name: "any apy over 0 seconds is 0",
apy: sdk.OneDec(),
secondsPassed: 0,
expectedRate: sdk.ZeroDec(),
preciseToDecimal: 19,
},
{
name: "zero apy for any number of seconds is 0",
apy: sdk.ZeroDec(),
secondsPassed: 100,
expectedRate: sdk.ZeroDec(),
preciseToDecimal: 19,
},
{
name: "an apy over a year's worth of seconds should be the apy",
apy: sdk.NewDecWithPrec(10, 2),
secondsPassed: keeper.SecondsPerYear,
expectedRate: sdk.NewDecWithPrec(10, 2),
preciseToDecimal: 10,
},
{
name: "example: 22 percent for 6 seconds, precise to 17 decimals",
apy: sdk.NewDecWithPrec(22, 2),
secondsPassed: 6,
expectedRate: sdk.MustNewDecFromStr("0.000000037833116915"),
preciseToDecimal: 17,
},
{
name: "example: 3 percent for 10 seconds, precise to 17 decimals",
apy: sdk.NewDecWithPrec(3, 2),
secondsPassed: 10,
expectedRate: sdk.MustNewDecFromStr("0.000000009373034748"),
preciseToDecimal: 17,
},
{
name: "example: 150 percent for 10 seconds, precise to 17 decimals",
apy: sdk.NewDecWithPrec(150, 2),
secondsPassed: 10,
expectedRate: sdk.MustNewDecFromStr("0.000000290553927255"),
preciseToDecimal: 17,
},
{
name: "example: 10 percent for 10 seconds, precise to 16 decimals",
apy: sdk.NewDecWithPrec(10, 2),
secondsPassed: 10,
expectedRate: sdk.MustNewDecFromStr("0.000000030222660212"),
preciseToDecimal: 16,
},
{
name: "example: 1,000 percent for 10 seconds, precise to 16 decimals",
apy: sdk.NewDecWithPrec(1000, 2),
secondsPassed: 10,
expectedRate: sdk.MustNewDecFromStr("0.000000760367892072"),
preciseToDecimal: 16,
},
{
name: "example: 10 percent for 30 seconds, precise to 16 decimals",
apy: sdk.NewDecWithPrec(10, 2),
secondsPassed: 30,
expectedRate: sdk.MustNewDecFromStr("0.00000009066798338"),
preciseToDecimal: 16,
},
{
name: "example: 95 percent for 15 seconds, precise to 16 decimals",
apy: sdk.NewDecWithPrec(95, 2),
secondsPassed: 15,
expectedRate: sdk.MustNewDecFromStr("0.000000317651007726"),
preciseToDecimal: 16,
},
{
name: "example: 10,000 percent for 10 seconds, precise to 13 decimals",
apy: sdk.NewDecWithPrec(10000, 2),
secondsPassed: 10,
expectedRate: sdk.MustNewDecFromStr("0.000001463446186527"),
preciseToDecimal: 13,
},
{
name: "can handle upper bound of APY (but w/ large error)",
apy: types.MaxMintingRate,
secondsPassed: 10,
expectedRate: sdk.MustNewDecFromStr("0.000001642242155465"),
preciseToDecimal: 4, // NOTE: error is really large here
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
actualRate, err := keeper.CalculateInflationRate(tc.apy, tc.secondsPassed)
suite.Require().NoError(err)
marginOfError := sdk.ZeroDec()
if tc.preciseToDecimal < 18 {
marginOfError = sdk.NewDecWithPrec(5, int64(tc.preciseToDecimal+1))
}
suite.requireWithinError(tc.expectedRate, actualRate, marginOfError)
})
}
// TODO: nick bring back
//suite.Run("errors when rate is too high", func() {
// _, err := keeper.CalculateInflationRate(types.MaxMintingRate.Add(sdk.OneDec()), 100)
// suite.Error(err)
//})
}
func (suite *InflationTestSuite) requireWithinError(expected, actual, margin sdk.Dec) {
suite.Require().Truef(
actual.Sub(expected).Abs().LTE(margin),
fmt.Sprintf("precision is outside desired margin of error %s\nexpected: %s\nactual : %s", margin, expected, actual),
)
}

178
x/kavamint/keeper/keeper.go Normal file
View File

@ -0,0 +1,178 @@
package keeper
import (
"time"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/kava-labs/kava/x/kavamint/types"
)
type KeeperI interface {
GetParams(ctx sdk.Context) (params types.Params)
SetParams(ctx sdk.Context, params types.Params)
BondDenom(ctx sdk.Context) string
TotalSupply(ctx sdk.Context) sdk.Int
TotalBondedTokens(ctx sdk.Context) sdk.Int
MintCoins(ctx sdk.Context, newCoins sdk.Coins) error
AddCollectedFees(ctx sdk.Context, fees sdk.Coins) error
FundCommunityPool(ctx sdk.Context, funds sdk.Coins) error
GetPreviousBlockTime(ctx sdk.Context) (blockTime time.Time)
SetPreviousBlockTime(ctx sdk.Context, blockTime time.Time)
CumulativeInflation(ctx sdk.Context) sdk.Dec
AccumulateInflation(
ctx sdk.Context,
rate sdk.Dec,
basis sdk.Int,
secondsSinceLastMint float64,
) (sdk.Coins, error)
}
// Keeper of the kavamint store
type Keeper struct {
cdc codec.BinaryCodec
storeKey sdk.StoreKey
paramSpace paramtypes.Subspace
stakingKeeper types.StakingKeeper
bankKeeper types.BankKeeper
stakingRewardsFeeCollectorName string
communityPoolModuleAccountName string
}
var _ KeeperI = Keeper{}
// NewKeeper creates a new kavamint Keeper instance
func NewKeeper(
cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace,
sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper,
stakingRewardsFeeCollectorName string, communityPoolModuleAccountName string,
) Keeper {
// set KeyTable if it has not already been set
if !paramSpace.HasKeyTable() {
paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
}
return Keeper{
cdc: cdc,
storeKey: key,
paramSpace: paramSpace,
stakingKeeper: sk,
bankKeeper: bk,
stakingRewardsFeeCollectorName: stakingRewardsFeeCollectorName,
communityPoolModuleAccountName: communityPoolModuleAccountName,
}
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", "x/"+types.ModuleName)
}
// GetParams returns the total set of minting parameters.
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
k.paramSpace.GetParamSet(ctx, &params)
return params
}
// SetParams sets the total set of minting parameters.
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
k.paramSpace.SetParamSet(ctx, &params)
}
// BondDenom implements an alias call to the underlying staking keeper's BondDenom.
func (k Keeper) BondDenom(ctx sdk.Context) string {
return k.stakingKeeper.BondDenom(ctx)
}
// TotalBondedTokens implements an alias call to the underlying staking keeper's
// TotalBondedTokens to be used in BeginBlocker.
func (k Keeper) TotalBondedTokens(ctx sdk.Context) sdk.Int {
return k.stakingKeeper.TotalBondedTokens(ctx)
}
// MintCoins implements an alias call to the underlying supply keeper's
// MintCoins to be used in BeginBlocker.
func (k Keeper) MintCoins(ctx sdk.Context, newCoins sdk.Coins) error {
if newCoins.Empty() {
// skip as no coins need to be minted
return nil
}
return k.bankKeeper.MintCoins(ctx, types.ModuleName, newCoins)
}
// AddCollectedFees implements an alias call to the underlying supply keeper's
// AddCollectedFees to be used in BeginBlocker.
func (k Keeper) AddCollectedFees(ctx sdk.Context, fees sdk.Coins) error {
return k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, k.stakingRewardsFeeCollectorName, fees)
}
// FundCommunityPool implements an alias call to the underlying supply keeper's
// FundCommunityPool to be used in BeginBlocker.
func (k Keeper) FundCommunityPool(ctx sdk.Context, funds sdk.Coins) error {
return k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, k.communityPoolModuleAccountName, funds)
}
// TotalSupply implements an alias call to the underlying supply keeper's
// GetSupply for the mint denom to be used in calculating cumulative inflation.
func (k Keeper) TotalSupply(ctx sdk.Context) sdk.Int {
return k.bankKeeper.GetSupply(ctx, k.BondDenom(ctx)).Amount
}
func (k Keeper) CumulativeInflation(ctx sdk.Context) sdk.Dec {
params := k.GetParams(ctx)
totalInflation := sdk.NewDec(0)
// community pool contribution is simply the inflation param
totalInflation = totalInflation.Add(params.CommunityPoolInflation)
// staking rewards contribution is the apy * bonded_ratio
bondedSupply := k.TotalBondedTokens(ctx)
totalSupply := k.TotalSupply(ctx)
bondedRatio := sdk.NewDecFromInt(bondedSupply).QuoInt(totalSupply)
inflationFromStakingRewards := params.StakingRewardsApy.Mul(bondedRatio)
totalInflation = totalInflation.Add(inflationFromStakingRewards)
return totalInflation
}
// GetPreviousBlockTime get the blocktime for the previous block
func (k Keeper) GetPreviousBlockTime(ctx sdk.Context) (blockTime time.Time) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.PreviousBlockTimeKey)
b := store.Get(types.PreviousBlockTimeKey)
if b == nil {
return blockTime
}
if err := blockTime.UnmarshalBinary(b); err != nil {
panic(err)
}
return
}
// SetPreviousBlockTime set the time of the previous block
func (k Keeper) SetPreviousBlockTime(ctx sdk.Context, blockTime time.Time) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.PreviousBlockTimeKey)
if blockTime.IsZero() {
store.Delete(types.PreviousBlockTimeKey)
return
}
b, err := blockTime.MarshalBinary()
if err != nil {
panic(err)
}
store.Set(types.PreviousBlockTimeKey, b)
}

View File

@ -0,0 +1,53 @@
package keeper_test
import (
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/kavamint/testutil"
"github.com/kava-labs/kava/x/kavamint/types"
"github.com/stretchr/testify/suite"
)
type keeperTestSuite struct {
testutil.KavamintTestSuite
}
func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(keeperTestSuite))
}
func (suite keeperTestSuite) TestParams_Persistance() {
keeper := suite.Keeper
params := types.NewParams(
sdk.MustNewDecFromStr("0.000000000000000001"),
sdk.MustNewDecFromStr("0.000000000000000002"),
)
keeper.SetParams(suite.Ctx, params)
suite.Equal(keeper.GetParams(suite.Ctx), params)
oldParams := params
params = types.NewParams(
sdk.MustNewDecFromStr("0.000000000000000011"),
sdk.MustNewDecFromStr("0.000000000000000022"),
)
keeper.SetParams(suite.Ctx, params)
suite.NotEqual(keeper.GetParams(suite.Ctx), oldParams)
suite.Equal(keeper.GetParams(suite.Ctx), params)
}
func (suite keeperTestSuite) TestPreviousBlockTime_Persistance() {
keeper := suite.Keeper
zeroTime := time.Time{}
keeper.SetPreviousBlockTime(suite.Ctx, zeroTime)
suite.Equal(keeper.GetPreviousBlockTime(suite.Ctx), zeroTime)
newTime := suite.Ctx.BlockTime()
suite.Require().False(newTime.IsZero())
keeper.SetPreviousBlockTime(suite.Ctx, newTime)
suite.Equal(keeper.GetPreviousBlockTime(suite.Ctx), newTime)
}

152
x/kavamint/module.go Normal file
View File

@ -0,0 +1,152 @@
package kavamint
import (
"context"
"encoding/json"
"fmt"
"github.com/gorilla/mux"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/spf13/cobra"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/kava-labs/kava/x/kavamint/client/cli"
"github.com/kava-labs/kava/x/kavamint/keeper"
"github.com/kava-labs/kava/x/kavamint/types"
)
var (
_ module.AppModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
)
// AppModuleBasic defines the basic application module used by the kavamint module.
type AppModuleBasic struct {
cdc codec.Codec
}
var _ module.AppModuleBasic = AppModuleBasic{}
// Name returns the mint module's name.
func (AppModuleBasic) Name() string {
return types.ModuleName
}
// RegisterLegacyAminoCodec registers the kavamint module's types on the given LegacyAmino codec.
func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {}
// RegisterInterfaces registers the module's interface types
func (b AppModuleBasic) RegisterInterfaces(_ cdctypes.InterfaceRegistry) {}
// DefaultGenesis returns default genesis state as raw bytes for the mint
// module.
func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
return cdc.MustMarshalJSON(types.DefaultGenesisState())
}
// ValidateGenesis performs genesis state validation for the kavamint module.
func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error {
var gs types.GenesisState
if err := cdc.UnmarshalJSON(bz, &gs); err != nil {
return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
}
return gs.Validate()
}
// RegisterRESTRoutes registers the REST routes for the kavamint module.
func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) {}
// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the kavamint module.
func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx))
}
// GetTxCmd returns no root tx command for the kavamint module.
func (AppModuleBasic) GetTxCmd() *cobra.Command { return nil }
// GetQueryCmd returns the root query command for the kavamint module.
func (AppModuleBasic) GetQueryCmd() *cobra.Command {
return cli.GetQueryCmd()
}
// AppModule implements an application module for the kavamint module.
type AppModule struct {
AppModuleBasic
keeper keeper.Keeper
authKeeper types.AccountKeeper
}
// NewAppModule creates a new AppModule object
func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, ak types.AccountKeeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{cdc: cdc},
keeper: keeper,
authKeeper: ak,
}
}
// Name returns the kavamint module's name.
func (AppModule) Name() string {
return types.ModuleName
}
// RegisterInvariants registers the kavamint module invariants.
func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
// Route returns the message routing key for the kavamint module.
func (AppModule) Route() sdk.Route { return sdk.Route{} }
// QuerierRoute returns the kavamint module's querier route name.
func (AppModule) QuerierRoute() string {
return types.QuerierRoute
}
// LegacyQuerierHandler returns the kavamint module sdk.Querier.
func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier {
return nil
}
// RegisterServices registers a gRPC query service to respond to the
// module-specific gRPC queries.
func (am AppModule) RegisterServices(cfg module.Configurator) {
types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
}
// InitGenesis performs genesis initialization for the kavamint module. It returns
// no validator updates.
func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate {
var genesisState types.GenesisState
cdc.MustUnmarshalJSON(data, &genesisState)
InitGenesis(ctx, am.keeper, am.authKeeper, &genesisState)
return []abci.ValidatorUpdate{}
}
// ExportGenesis returns the exported genesis state as raw bytes for the mint
// module.
func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
gs := ExportGenesis(ctx, am.keeper)
return cdc.MustMarshalJSON(gs)
}
// ConsensusVersion implements AppModule/ConsensusVersion.
func (AppModule) ConsensusVersion() uint64 { return 1 }
// BeginBlock returns the begin blocker for the kavamint module.
func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {
BeginBlocker(ctx, am.keeper)
}
// EndBlock returns the end blocker for the kavamint module. It returns no validator
// updates.
func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
return []abci.ValidatorUpdate{}
}

31
x/kavamint/module_test.go Normal file
View File

@ -0,0 +1,31 @@
package kavamint_test
import (
"testing"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/stretchr/testify/require"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/kavamint/types"
)
func TestKavaMintModuleAccountWithPermissionsOnAppInit(t *testing.T) {
tApp := app.NewTestApp()
ctx := tApp.NewContext(true, tmproto.Header{Height: 1})
tApp.InitializeFromGenesisStates()
ak := tApp.GetAccountKeeper()
// by pass auto creation of module accounts
addr, _ := ak.GetModuleAddressAndPermissions(types.ModuleName)
acc := ak.GetAccount(ctx, addr)
require.NotNil(t, acc, "expected module account to exist")
macc, ok := acc.(authtypes.ModuleAccountI)
require.True(t, ok)
require.True(t, macc.HasPermission(authtypes.Minter), "expected module account to have mint permissions")
}

View File

@ -0,0 +1,45 @@
<!--
order: 1
-->
# Concepts
## Singular Source of Inflation
The kavamint module mints new tokens each block. How many tokens are minted is determined by rates set in kavamint's parameters. Where the newly minted tokens are transferred to is dictated by which parameter. The kavamint module supports minting for the following purposes:
| Purpose | Parameter | Rate Based On | Transferred To |
| --------------- | ------------------------ | ----------------------- | --------------- |
| Community Pool | `CommunityPoolInflation` | TotalSupply | x/community |
| Staking Rewards | `StakingRewardsApy` | TotalSupply*BondedRatio | x/auth fee pool |
`TotalSupply` is the total number of tokens. `BondedRatio` is the percentage of those tokens that are delegated to a bonded validator.
### How Amount is Calculated
The number of seconds since the previous block is determined: `secondsSinceLastBlock`.
For each yearly rate set in the parameters, an equivalent rate for the number of seconds passed is determined assuming the rate compounds every second and, for simplicity, there are a fixed number of seconds per year (`365 x 24 x 60 x 60 = 31,536,000`). The rate compounded over `secondsSinceLastBlock` seconds is the `31,536,000`th root of `rate + 1` to the `secondsSinceLastBlock` power (minus 100% to determine number of new tokens). This number is truncated to an `sdk.Int`.
**example**:
Assume `CommunityPoolInflation` is 20%, the `TotalSupply` is 1e10 tokens and `secondsSinceLastBlock` is 5 seconds. The number of tokens minted is determined to be 289 tokens.
```math
({\sqrt[secondsPerYear]{1 + CommunityPoolInflation}}^{secondsSinceLastBlock} - 1)*TotalSupply
= ({\sqrt[31,536,000]{1.2}}^{5} - 1)*1e10
\approx 289
```
## Staking Reward Distribution
It helps to understand how newly minted tokens are created and distributed as validator rewards by default in a Cosmos SDK blockchain. In the standard Cosmos SDK mint module, tokens are minted each block based on a variable inflation rate determined by the parameters and the desired bonded token ratio. The tokens are then transferred to the auth module's fee pool to be distributed as staking rewards after a portion of the total fee pool is transferred to the community pool (determined by the CommunityTax parameter of the distribution module).
The kavamint module separates the default community pool inflation from staking rewards. For staking rewards, it uses an APY determined by StakingRewardsApy. The basis is the total bonded tokens. The tokens are distributed to the same fee pool as above so they can be distributed as rewards to delegators with the default distribution module.
## Community Pool Inflation
Every block, tokens are distributed to KAVA's community pool, the module account of x/community. The number of tokens distributed is determined by the total supply.
## Disabling Default Inflation
The intention of this module is to replace the default minting of the x/mint module. To make that the case, the minimum and maximum inflation parameters of the mint module should be set to 0. Additionally, it's recommended that the community tax parameter of the distribution module should be set to 0 as the logic for funding the community pool is controlled by kavamint.

View File

@ -0,0 +1,36 @@
<!--
order: 2
-->
# State
## Parameters and Genesis State
`Parameters` define the governance parameters that control inflation of KAVA.
Each parameter is an APY/inflation used in conjunction with the block time to
calculate how much KAVA to mint for what purposes. See [params](./05_params.md) for a description of each param.
```go
// Params wraps the governance parameters for the kavamint module
type Params struct {
// yearly inflation of total token supply minted to the community pool.
CommunityPoolInflation sdk.Dec `json:"community_pool_inflation" yaml:"community_pool_inflation"`
// yearly inflation of bonded tokens minted for staking rewards to validators.
StakingRewardsApy sdk.Dec `json:"staking_rewards_apy" yaml:"staking_rewards_apy"`
}
```
`GenesisState` defines the state that must be persisted when the blockchain stops/restarts in order for normal function of the kavamint module to resume.
```go
// GenesisState defines the kavamint module's genesis state.
type GenesisState struct {
// params defines all the parameters of the module.
Params Params `json:"params" yaml:"params"`
}
```
## Previous Block Time
The `PreviousBlockTime` is stored in the keeper and updated each block. In the BeginBlocker, the number of seconds between the current and previous block is determined and used to calculate how much KAVA should be minted for each

View File

@ -0,0 +1,7 @@
<!--
order: 3
-->
# Messages
The kavadist module contains no messages. All state transitions are controlled by parameters, which can be updated via parameter change governance proposals.

View File

@ -0,0 +1,17 @@
<!--
order: 4
-->
# Events
The kavamint module emits the following events:
## BeginBlocker
| Type | Attribute Key | Attribute Value |
| -------- | ------------------------------- | ------------------------ |
| kavamint | total_supply | {totalSupply} |
| kavamint | bonded_ratio | {bondedRatio} |
| kavamint | seconds_since_last_mint | {secondsPassed} |
| kavamint | minted_community_pool_inflation | {communityPoolInflation} |
| kavamint | minted_staking_rewards | {stakingRewardCoins} |

View File

@ -0,0 +1,12 @@
<!--
order: 5
-->
# Parameters
Example parameters for the kavamint module:
| Key | Type | Example | Description |
| ---------------------- | ------- | ---------------------- | ------------------------------------------------------------------------ |
| CommunityPoolInflation | sdk.Dec | "0.250000000000000000" | Yearly inflation rate of total token supply minted to the community pool |
| StakingRewardsAPY | sdk.Dec | "0.200000000000000000" | APY of bonded tokens minted for staking reward distribution |

View File

@ -0,0 +1,49 @@
<!--
order: 6
-->
# Begin Block
At the start of each block, new KAVA tokens are minted and distributed
```go
// BeginBlocker mints & distributes new tokens for the previous block.
func BeginBlocker(ctx sdk.Context, k Keeper) {
params := k.GetParams(ctx)
// fetch the last block time from state
previousBlockTime := k.GetPreviousBlockTime(ctx)
secondsPassed := ctx.BlockTime().Sub(previousBlockTime).Seconds()
// determine totals before any new mints
totalSupply := k.TotalSupply(ctx)
totalBonded := k.TotalBondedTokens(ctx)
// ------------- Staking Rewards -------------
// determine amount of the bond denom to mint for staking rewards
stakingRewardCoins, err := k.AccumulateInflation(
ctx, params.StakingRewardsApy, totalBonded, secondsPassed,
)
// mint the staking rewards
k.MintCoins(ctx, stakingRewardCoins)
// distribute them to the fee pool for distribution by x/distribution
k.AddCollectedFees(ctx, stakingRewardCoins)
// ------------- Community Pool -------------
// determine amount of the bond denom to mint for community pool inflation
communityPoolInflation, err := k.AccumulateInflation(
ctx, params.CommunityPoolInflation, totalSupply, secondsPassed,
)
// mint the community pool tokens
k.MintCoins(ctx, communityPoolCoins)
// send them to the community module account (the community pool)
k.AddCommunityPoolFunds(ctx, communityPoolCoins)
// ------------- Bookkeeping -------------
// set block time for next iteration's minting
k.SetPreviousBlockTime(ctx, ctx.BlockTime())
}
```
`AccumulateInflation` determines the effective rate of the yearly interest rate assuming it is
compounded once per second, for the number of seconds since the previous mint. See concepts for
more details.

21
x/kavamint/spec/README.md Normal file
View File

@ -0,0 +1,21 @@
<!--
order: 0
title: "Kavamint Overview"
parent:
title: "kavamint"
-->
# `kavamint`
<!-- TOC -->
1. **[Concepts](01_concepts.md)**
2. **[State](02_state.md)**
3. **[Messages](03_messages.md)**
4. **[Events](04_events.md)**
5. **[Params](05_params.md)**
6. **[BeginBlock](06_begin_block.md)**
## Abstract
`x/kavamint` is an implementation of a Cosmos SDK Module that provides for functionality and governance of overall inflation of the KAVA staking token.

View File

@ -0,0 +1,61 @@
package testutil
import (
"github.com/stretchr/testify/suite"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/kavamint/keeper"
)
type KavamintTestSuite struct {
suite.Suite
App app.TestApp
Ctx sdk.Context
Keeper keeper.Keeper
StakingKeeper stakingkeeper.Keeper
BondDenom string
}
func (suite *KavamintTestSuite) SetupTest() {
app.SetSDKConfig()
suite.App = app.NewTestApp()
suite.App.InitializeFromGenesisStates()
suite.Ctx = suite.App.BaseApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
suite.Keeper = suite.App.GetKavamintKeeper()
suite.StakingKeeper = suite.App.GetStakingKeeper()
suite.BondDenom = suite.Keeper.BondDenom(suite.Ctx)
}
// SetBondedTokenRatio mints the total supply to an account and creates a validator with a self
// delegation that makes the total staked token ratio set as desired.
// EndBlocker must be run in order for tokens to become bonded.
// returns total supply coins
func (suite *KavamintTestSuite) SetBondedTokenRatio(ratio sdk.Dec) sdk.Coins {
address := app.RandomAddress()
supplyAmount := sdk.NewInt(1e10)
totalSupply := sdk.NewCoins(sdk.NewCoin(suite.BondDenom, supplyAmount))
amountToBond := ratio.MulInt(supplyAmount).TruncateInt()
// fund account that will create validator with total supply
err := suite.App.FundAccount(suite.Ctx, address, totalSupply)
suite.Require().NoError(err)
if ratio.IsZero() {
return totalSupply
}
// create a validator with self delegation such that ratio is achieved
err = suite.App.CreateNewUnbondedValidator(suite.Ctx, sdk.ValAddress(address), amountToBond)
suite.Require().NoError(err)
return totalSupply
}

View File

@ -0,0 +1,12 @@
package types
// Minting module event types
const (
EventTypeMint = ModuleName
AttributeKeyTotalSupply = "total_supply"
AttributeKeyTotalBonded = "total_bonded"
AttributeSecondsPassed = "seconds_since_last_mint"
AttributeKeyCommunityPoolMint = "minted_community_pool_inflation"
AttributeKeyStakingRewardMint = "minted_staking_rewards"
)

View File

@ -0,0 +1,26 @@
package types // noalias
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/types"
)
// StakingKeeper defines the expected staking keeper
type StakingKeeper interface {
BondDenom(sdk.Context) string
TotalBondedTokens(sdk.Context) sdk.Int
}
// AccountKeeper defines the contract required for account APIs.
type AccountKeeper interface {
GetModuleAddress(name string) sdk.AccAddress
GetModuleAccount(ctx sdk.Context, moduleName string) types.ModuleAccountI
}
// BankKeeper defines the contract needed to be fulfilled for banking and supply
// dependencies.
type BankKeeper interface {
GetSupply(ctx sdk.Context, denom string) sdk.Coin
SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error
MintCoins(ctx sdk.Context, name string, amt sdk.Coins) error
}

View File

@ -0,0 +1,32 @@
package types
import (
time "time"
)
var (
// DefaultPreviousBlockTime represents a time that is unset -- no previous block has occured
DefaultPreviousBlockTime = time.Time{}
)
// NewGenesisState creates a new GenesisState object
func NewGenesisState(params Params, previousBlockTime time.Time) *GenesisState {
return &GenesisState{
Params: params,
PreviousBlockTime: previousBlockTime,
}
}
// DefaultGenesisState creates a default GenesisState object
func DefaultGenesisState() *GenesisState {
return &GenesisState{
Params: DefaultParams(),
PreviousBlockTime: DefaultPreviousBlockTime,
}
}
// Validate validates the provided genesis state to ensure the
// expected invariants holds.
func (gs GenesisState) Validate() error {
return gs.Params.Validate()
}

View File

@ -0,0 +1,372 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: kava/kavamint/v1beta1/genesis.proto
package types
import (
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
github_com_gogo_protobuf_types "github.com/gogo/protobuf/types"
_ "google.golang.org/protobuf/types/known/timestamppb"
io "io"
math "math"
math_bits "math/bits"
time "time"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
var _ = time.Kitchen
// 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
// GenesisState defines the kavamint module's genesis state.
type GenesisState struct {
// Params defines all the parameters of the module.
Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
// PreviousBlockTime holds the last last time tokens were minted.
// On first block, tokens will be minted for total number of seconds passed since this time.
PreviousBlockTime time.Time `protobuf:"bytes,2,opt,name=previous_block_time,json=previousBlockTime,proto3,stdtime" json:"previous_block_time"`
}
func (m *GenesisState) Reset() { *m = GenesisState{} }
func (m *GenesisState) String() string { return proto.CompactTextString(m) }
func (*GenesisState) ProtoMessage() {}
func (*GenesisState) Descriptor() ([]byte, []int) {
return fileDescriptor_179d81c8ae95ec37, []int{0}
}
func (m *GenesisState) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_GenesisState.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 *GenesisState) XXX_Merge(src proto.Message) {
xxx_messageInfo_GenesisState.Merge(m, src)
}
func (m *GenesisState) XXX_Size() int {
return m.Size()
}
func (m *GenesisState) XXX_DiscardUnknown() {
xxx_messageInfo_GenesisState.DiscardUnknown(m)
}
var xxx_messageInfo_GenesisState proto.InternalMessageInfo
func init() {
proto.RegisterType((*GenesisState)(nil), "kava.kavamint.v1beta1.GenesisState")
}
func init() {
proto.RegisterFile("kava/kavamint/v1beta1/genesis.proto", fileDescriptor_179d81c8ae95ec37)
}
var fileDescriptor_179d81c8ae95ec37 = []byte{
// 286 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xce, 0x4e, 0x2c, 0x4b,
0xd4, 0x07, 0x11, 0xb9, 0x99, 0x79, 0x25, 0xfa, 0x65, 0x86, 0x49, 0xa9, 0x25, 0x89, 0x86, 0xfa,
0xe9, 0xa9, 0x79, 0xa9, 0xc5, 0x99, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xa2, 0x20,
0x79, 0x3d, 0x98, 0x22, 0x3d, 0xa8, 0x22, 0x29, 0x91, 0xf4, 0xfc, 0xf4, 0x7c, 0xb0, 0x0a, 0x7d,
0x10, 0x0b, 0xa2, 0x58, 0x4a, 0x3e, 0x3d, 0x3f, 0x3f, 0x3d, 0x27, 0x55, 0x1f, 0xcc, 0x4b, 0x2a,
0x4d, 0xd3, 0x2f, 0xc9, 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0x80, 0x2a, 0x50, 0xc1, 0x6e,
0x25, 0xdc, 0x78, 0xb0, 0x2a, 0xa5, 0xe5, 0x8c, 0x5c, 0x3c, 0xee, 0x10, 0x57, 0x04, 0x97, 0x24,
0x96, 0xa4, 0x0a, 0x59, 0x73, 0xb1, 0x15, 0x24, 0x16, 0x25, 0xe6, 0x16, 0x4b, 0x30, 0x2a, 0x30,
0x6a, 0x70, 0x1b, 0xc9, 0xea, 0x61, 0x75, 0x95, 0x5e, 0x00, 0x58, 0x91, 0x13, 0xcb, 0x89, 0x7b,
0xf2, 0x0c, 0x41, 0x50, 0x2d, 0x42, 0x21, 0x5c, 0xc2, 0x05, 0x45, 0xa9, 0x65, 0x99, 0xf9, 0xa5,
0xc5, 0xf1, 0x49, 0x39, 0xf9, 0xc9, 0xd9, 0xf1, 0x20, 0x57, 0x49, 0x30, 0x81, 0x4d, 0x92, 0xd2,
0x83, 0x38, 0x59, 0x0f, 0xe6, 0x64, 0xbd, 0x10, 0x98, 0x93, 0x9d, 0x38, 0x40, 0xc6, 0x4c, 0xb8,
0x2f, 0xcf, 0x18, 0x24, 0x08, 0x33, 0xc0, 0x09, 0xa4, 0x1f, 0xa4, 0xc2, 0x8a, 0xa5, 0x63, 0x81,
0x3c, 0x83, 0x93, 0xcb, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7,
0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x69, 0xa5,
0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0x82, 0xbd, 0xa7, 0x9b, 0x93, 0x98, 0x54,
0x0c, 0x66, 0xe9, 0x57, 0x20, 0x02, 0xa0, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0x6c, 0xb9,
0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x48, 0xff, 0xce, 0x91, 0x01, 0x00, 0x00,
}
func (m *GenesisState) 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 *GenesisState) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.PreviousBlockTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.PreviousBlockTime):])
if err1 != nil {
return 0, err1
}
i -= n1
i = encodeVarintGenesis(dAtA, i, uint64(n1))
i--
dAtA[i] = 0x12
{
size, err := m.Params.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenesis(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int {
offset -= sovGenesis(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *GenesisState) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = m.Params.Size()
n += 1 + l + sovGenesis(uint64(l))
l = github_com_gogo_protobuf_types.SizeOfStdTime(m.PreviousBlockTime)
n += 1 + l + sovGenesis(uint64(l))
return n
}
func sovGenesis(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozGenesis(x uint64) (n int) {
return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *GenesisState) 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 ErrIntOverflowGenesis
}
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: GenesisState: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenesis
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenesis
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenesis
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PreviousBlockTime", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenesis
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenesis
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenesis
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.PreviousBlockTime, dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenesis(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthGenesis
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipGenesis(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, ErrIntOverflowGenesis
}
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, ErrIntOverflowGenesis
}
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, ErrIntOverflowGenesis
}
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, ErrInvalidLengthGenesis
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupGenesis
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthGenesis
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group")
)

View File

@ -0,0 +1,149 @@
package types_test
import (
"encoding/json"
"testing"
time "time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/yaml"
"github.com/kava-labs/kava/x/kavamint/types"
)
func TestGenesis_Default(t *testing.T) {
defaultGenesis := types.DefaultGenesisState()
require.NoError(t, defaultGenesis.Validate())
defaultParams := types.DefaultParams()
assert.Equal(t, defaultParams, defaultGenesis.Params)
}
func TestGenesis_Validation(t *testing.T) {
testCases := []struct {
name string
gs *types.GenesisState
expectedErr string
}{
{
"valid - default genesis is valid",
types.DefaultGenesisState(),
"",
},
{
"valid - valid genesis",
types.NewGenesisState(
newValidParams(t),
time.Now(),
),
"",
},
{
"valid - no inflation",
types.NewGenesisState(
types.NewParams(sdk.ZeroDec(), sdk.ZeroDec()),
time.Now(),
),
"",
},
{
"valid - no time set",
types.NewGenesisState(
newValidParams(t),
time.Time{},
),
"",
},
{
"invalid - community inflation param too big",
types.NewGenesisState(
types.NewParams(types.MaxMintingRate.Add(sdk.NewDecWithPrec(1, 18)), sdk.ZeroDec()),
time.Now(),
),
"invalid rate: 100.000000000000000001",
},
{
"invalid - staking reward inflation param too big",
types.NewGenesisState(
// inflation is larger than is allowed!
types.NewParams(sdk.ZeroDec(), types.MaxMintingRate.Add(sdk.NewDecWithPrec(1, 18))),
time.Now(),
),
"invalid rate: 100.000000000000000001",
},
{
"invalid - negative community inflation param",
types.NewGenesisState(
types.NewParams(sdk.OneDec().MulInt64(-1), sdk.ZeroDec()),
time.Now(),
),
"invalid rate: -1.000000000000000000",
},
{
"invalid - negative staking inflation param",
types.NewGenesisState(
types.NewParams(sdk.ZeroDec(), sdk.OneDec().MulInt64(-1)),
time.Now(),
),
"invalid rate: -1.000000000000000000",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.gs.Validate()
if tc.expectedErr == "" {
assert.Nil(t, err)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestGenesis_JSONEncoding(t *testing.T) {
raw := `{
"params": {
"community_pool_inflation": "0.000000000000000001",
"staking_rewards_apy": "0.000000000000000002"
},
"previous_block_time": "2022-12-01T11:30:55.000000001Z"
}`
var state types.GenesisState
err := json.Unmarshal([]byte(raw), &state)
require.NoError(t, err)
assert.Equal(t, sdk.MustNewDecFromStr("0.000000000000000001"), state.Params.CommunityPoolInflation)
assert.Equal(t, sdk.MustNewDecFromStr("0.000000000000000002"), state.Params.StakingRewardsApy)
prevBlockTime, err := time.Parse(time.RFC3339, "2022-12-01T11:30:55.000000001Z")
require.NoError(t, err)
assert.Equal(t, prevBlockTime, state.PreviousBlockTime)
}
func TestGenesis_YAMLEncoding(t *testing.T) {
expected := `params:
community_pool_inflation: "0.000000000000000001"
staking_rewards_apy: "0.000000000000000002"
previous_block_time: "2022-12-01T11:30:55.000000001Z"
`
prevBlockTime, err := time.Parse(time.RFC3339, "2022-12-01T11:30:55.000000001Z")
require.NoError(t, err)
state := types.NewGenesisState(
types.NewParams(sdk.MustNewDecFromStr("0.000000000000000001"), sdk.MustNewDecFromStr("0.000000000000000002")),
prevBlockTime,
)
data, err := yaml.Marshal(state)
require.NoError(t, err)
assert.Equal(t, expected, string(data))
}

View File

@ -0,0 +1,373 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: kava/kavamint/v1beta1/kavamint.proto
package types
import (
fmt "fmt"
_ "github.com/cosmos/cosmos-proto"
github_com_cosmos_cosmos_sdk_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
// Params wraps the governance parameters for the kavamint module
type Params struct {
// yearly inflation of total token supply minted to the community pool.
CommunityPoolInflation github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=community_pool_inflation,json=communityPoolInflation,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"community_pool_inflation"`
// yearly inflation of bonded tokens minted for staking rewards to validators.
StakingRewardsApy github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=staking_rewards_apy,json=stakingRewardsApy,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"staking_rewards_apy"`
}
func (m *Params) Reset() { *m = Params{} }
func (*Params) ProtoMessage() {}
func (*Params) Descriptor() ([]byte, []int) {
return fileDescriptor_bdfe36d50f127f04, []int{0}
}
func (m *Params) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) {
xxx_messageInfo_Params.Merge(m, src)
}
func (m *Params) XXX_Size() int {
return m.Size()
}
func (m *Params) XXX_DiscardUnknown() {
xxx_messageInfo_Params.DiscardUnknown(m)
}
var xxx_messageInfo_Params proto.InternalMessageInfo
func init() {
proto.RegisterType((*Params)(nil), "kava.kavamint.v1beta1.Params")
}
func init() {
proto.RegisterFile("kava/kavamint/v1beta1/kavamint.proto", fileDescriptor_bdfe36d50f127f04)
}
var fileDescriptor_bdfe36d50f127f04 = []byte{
// 293 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xc9, 0x4e, 0x2c, 0x4b,
0xd4, 0x07, 0x11, 0xb9, 0x99, 0x79, 0x25, 0xfa, 0x65, 0x86, 0x49, 0xa9, 0x25, 0x89, 0x86, 0x70,
0x01, 0xbd, 0x82, 0xa2, 0xfc, 0x92, 0x7c, 0x21, 0x51, 0x10, 0x5f, 0x0f, 0x2e, 0x08, 0x55, 0x25,
0x25, 0x99, 0x9c, 0x5f, 0x9c, 0x9b, 0x5f, 0x1c, 0x0f, 0x56, 0xa4, 0x0f, 0xe1, 0x40, 0x74, 0x48,
0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x43, 0xc4, 0x41, 0x2c, 0x88, 0xa8, 0xd2, 0x0f, 0x46, 0x2e, 0xb6,
0x80, 0xc4, 0xa2, 0xc4, 0xdc, 0x62, 0xa1, 0x32, 0x2e, 0x89, 0xe4, 0xfc, 0xdc, 0xdc, 0xd2, 0xbc,
0xcc, 0x92, 0xca, 0xf8, 0x82, 0xfc, 0xfc, 0x9c, 0xf8, 0xcc, 0xbc, 0xb4, 0x9c, 0xc4, 0x92, 0xcc,
0xfc, 0x3c, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x4e, 0x27, 0x9b, 0x13, 0xf7, 0xe4, 0x19, 0x6e, 0xdd,
0x93, 0x57, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0x85, 0xda, 0x01, 0xa5,
0x74, 0x8b, 0x53, 0xb2, 0xf5, 0x4b, 0x2a, 0x0b, 0x52, 0x8b, 0xf5, 0x5c, 0x52, 0x93, 0x2f, 0x6d,
0xd1, 0xe5, 0x82, 0x3a, 0xc1, 0x25, 0x35, 0x39, 0x48, 0x0c, 0x6e, 0x7a, 0x40, 0x7e, 0x7e, 0x8e,
0x27, 0xcc, 0x6c, 0xa1, 0x1c, 0x2e, 0xe1, 0xe2, 0x92, 0xc4, 0xec, 0xcc, 0xbc, 0xf4, 0xf8, 0xa2,
0xd4, 0xf2, 0xc4, 0xa2, 0x94, 0xe2, 0xf8, 0xc4, 0x82, 0x4a, 0x09, 0x26, 0x2a, 0x58, 0x29, 0x08,
0x35, 0x38, 0x08, 0x62, 0xae, 0x63, 0x41, 0xa5, 0x15, 0x47, 0xc7, 0x02, 0x79, 0x86, 0x19, 0x0b,
0xe4, 0x19, 0x9c, 0x5c, 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, 0x0b,
0xc9, 0x32, 0x50, 0x10, 0xeb, 0xe6, 0x24, 0x26, 0x15, 0x83, 0x59, 0xfa, 0x15, 0x88, 0x98, 0x01,
0x5b, 0x9a, 0xc4, 0x06, 0x0e, 0x47, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8a, 0xce, 0xb7,
0xff, 0xb7, 0x01, 0x00, 0x00,
}
func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
{
size := m.StakingRewardsApy.Size()
i -= size
if _, err := m.StakingRewardsApy.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i = encodeVarintKavamint(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
{
size := m.CommunityPoolInflation.Size()
i -= size
if _, err := m.CommunityPoolInflation.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i = encodeVarintKavamint(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func encodeVarintKavamint(dAtA []byte, offset int, v uint64) int {
offset -= sovKavamint(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *Params) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = m.CommunityPoolInflation.Size()
n += 1 + l + sovKavamint(uint64(l))
l = m.StakingRewardsApy.Size()
n += 1 + l + sovKavamint(uint64(l))
return n
}
func sovKavamint(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozKavamint(x uint64) (n int) {
return sovKavamint(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *Params) 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 ErrIntOverflowKavamint
}
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: Params: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CommunityPoolInflation", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKavamint
}
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 ErrInvalidLengthKavamint
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthKavamint
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.CommunityPoolInflation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field StakingRewardsApy", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowKavamint
}
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 ErrInvalidLengthKavamint
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthKavamint
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.StakingRewardsApy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipKavamint(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthKavamint
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipKavamint(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, ErrIntOverflowKavamint
}
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, ErrIntOverflowKavamint
}
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, ErrIntOverflowKavamint
}
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, ErrInvalidLengthKavamint
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupKavamint
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthKavamint
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthKavamint = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowKavamint = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupKavamint = fmt.Errorf("proto: unexpected end of group")
)

18
x/kavamint/types/keys.go Normal file
View File

@ -0,0 +1,18 @@
package types
const (
// module name
ModuleName = "kavamint"
// ModuleAccountName is the name of the module's account
ModuleAccountName = ModuleName
// StoreKey is the default store key for mint
StoreKey = ModuleName
// QuerierRoute is the querier route for the minting store.
QuerierRoute = StoreKey
)
// PreviousBlockTimeKey is the store key for the previous block time
var PreviousBlockTimeKey = []byte{0x00}

View File

@ -0,0 +1,87 @@
package types
import (
fmt "fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
)
// Parameter keys and default values
var (
KeyCommunityPoolInflation = []byte("CommunityPoolInflation")
KeyStakingRewardsApy = []byte("StakingRewardsApy")
// default inflation values are zero
DefaultCommunityPoolInflation = sdk.ZeroDec()
DefaultStakingRewardsApy = sdk.ZeroDec()
// MaxMintingRate returns the per second rate equivalent to 10,000% per year
MaxMintingRate = sdk.NewDec(100)
)
// NewParams returns new Params with inflation rates set
func NewParams(communityPoolInflation sdk.Dec, stakingRewardsApy sdk.Dec) Params {
return Params{
CommunityPoolInflation: communityPoolInflation,
StakingRewardsApy: stakingRewardsApy,
}
}
// ParamKeyTable returns the key table for the kavamint module
func ParamKeyTable() paramtypes.KeyTable {
return paramtypes.NewKeyTable().RegisterParamSet(&Params{})
}
// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs
func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
return paramtypes.ParamSetPairs{
paramtypes.NewParamSetPair(KeyCommunityPoolInflation, &p.CommunityPoolInflation, validateCommunityPoolInflation),
paramtypes.NewParamSetPair(KeyStakingRewardsApy, &p.StakingRewardsApy, validateStakingRewardsApy),
}
}
// DefaultParams returns default valid parameters for the kavamint module
func DefaultParams() Params {
return NewParams(DefaultCommunityPoolInflation, DefaultStakingRewardsApy)
}
// Validate checks that the parameters have valid values.
func (p *Params) Validate() error {
if err := validateCommunityPoolInflation(p.CommunityPoolInflation); err != nil {
return err
}
return validateStakingRewardsApy(p.StakingRewardsApy)
}
// String implements fmt.Stringer
func (p Params) String() string {
return fmt.Sprintf(`Params:
CommunityPoolInflation: %s
StakingRewardsApy: %s`,
p.CommunityPoolInflation, p.StakingRewardsApy)
}
func validateCommunityPoolInflation(i interface{}) error {
rate, ok := i.(sdk.Dec)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
return validateRate(rate)
}
func validateStakingRewardsApy(i interface{}) error {
rate, ok := i.(sdk.Dec)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
return validateRate(rate)
}
// validateRate ensures rate is properly initialized (non-nil), not negative, and not greater than the max rate
func validateRate(rate sdk.Dec) error {
if rate.IsNil() || rate.IsNegative() || rate.GT(MaxMintingRate) {
return fmt.Errorf(fmt.Sprintf("invalid rate: %s", rate))
}
return nil
}

View File

@ -0,0 +1,297 @@
package types_test
import (
"bytes"
"encoding/json"
fmt "fmt"
"reflect"
"testing"
"github.com/kava-labs/kava/x/kavamint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/yaml"
)
const secondsPerYear = 31536000
func newValidParams(t *testing.T) types.Params {
// 50%
poolRate := sdk.MustNewDecFromStr("0.5")
// 10%
stakingRate := sdk.MustNewDecFromStr("0.1")
params := types.NewParams(poolRate, stakingRate)
require.NoError(t, params.Validate())
return params
}
func TestParams_Default(t *testing.T) {
defaultParams := types.DefaultParams()
require.NoError(t, defaultParams.Validate(), "default parameters must be valid")
assert.Equal(t, types.DefaultCommunityPoolInflation, defaultParams.CommunityPoolInflation, "expected default pool inflation to match exported default value")
assert.Equal(t, defaultParams.CommunityPoolInflation, sdk.ZeroDec(), "expected default pool inflation to be zero")
assert.Equal(t, types.DefaultStakingRewardsApy, defaultParams.StakingRewardsApy, "expected default staking inflation to match exported default value")
assert.Equal(t, defaultParams.CommunityPoolInflation, sdk.ZeroDec(), "expected default staking inflation to be zero")
}
func TestParams_MaxInflationRate_ApproxRootDoesNotPanic(t *testing.T) {
require.Equal(t, sdk.NewDec(100), types.MaxMintingRate) // 10,000%, should never be a reason to exceed this value
maxYearlyRate := types.MaxMintingRate
require.NotPanics(t, func() {
expectedMaxRate, err := maxYearlyRate.ApproxRoot(secondsPerYear)
require.NoError(t, err)
expectedMaxRate = expectedMaxRate.Sub(sdk.OneDec())
})
}
func TestParams_MaxInflationRate_DoesNotOverflow(t *testing.T) {
maxRate := types.MaxMintingRate // use the max minting rate
totalSupply := sdk.NewDec(1e14) // 100 trillion starting supply
years := uint64(25) // calculate over 50 years
perSecondMaxRate, err := maxRate.ApproxRoot(secondsPerYear)
require.NoError(t, err)
perSecondMaxRate = perSecondMaxRate.Sub(sdk.OneDec())
var finalSupply sdk.Int
require.NotPanics(t, func() {
compoundedRate := perSecondMaxRate.Power(years * secondsPerYear)
finalSupply = totalSupply.Mul(compoundedRate).RoundInt()
})
require.Less(t, finalSupply.BigInt().BitLen(), 256)
}
func TestParams_ParamSetPairs_CommunityPoolInflation(t *testing.T) {
assert.Equal(t, []byte("CommunityPoolInflation"), types.KeyCommunityPoolInflation)
defaultParams := types.DefaultParams()
var paramSetPair *paramstypes.ParamSetPair
for _, pair := range defaultParams.ParamSetPairs() {
if bytes.Equal(pair.Key, types.KeyCommunityPoolInflation) {
paramSetPair = &pair
break
}
}
require.NotNil(t, paramSetPair)
pairs, ok := paramSetPair.Value.(*sdk.Dec)
require.True(t, ok)
assert.Equal(t, pairs, &defaultParams.CommunityPoolInflation)
assert.Nil(t, paramSetPair.ValidatorFn(*pairs))
assert.EqualError(t, paramSetPair.ValidatorFn(struct{}{}), "invalid parameter type: struct {}")
}
func TestParams_ParamSetPairs_StakingRewardsApy(t *testing.T) {
assert.Equal(t, []byte("StakingRewardsApy"), types.KeyStakingRewardsApy)
defaultParams := types.DefaultParams()
var paramSetPair *paramstypes.ParamSetPair
for _, pair := range defaultParams.ParamSetPairs() {
if bytes.Equal(pair.Key, types.KeyStakingRewardsApy) {
paramSetPair = &pair
break
}
}
require.NotNil(t, paramSetPair)
pairs, ok := paramSetPair.Value.(*sdk.Dec)
require.True(t, ok)
assert.Equal(t, pairs, &defaultParams.StakingRewardsApy)
assert.Nil(t, paramSetPair.ValidatorFn(*pairs))
assert.EqualError(t, paramSetPair.ValidatorFn(struct{}{}), "invalid parameter type: struct {}")
}
func TestParams_Validation(t *testing.T) {
testCases := []struct {
name string
key []byte
testFn func(params *types.Params)
expectedErr string
}{
{
name: "nil community pool inflation",
key: types.KeyCommunityPoolInflation,
testFn: func(params *types.Params) {
params.CommunityPoolInflation = sdk.Dec{}
},
expectedErr: "invalid rate: <nil>",
},
{
name: "negative community pool inflation",
key: types.KeyCommunityPoolInflation,
testFn: func(params *types.Params) {
params.CommunityPoolInflation = sdk.MustNewDecFromStr("-0.000000000011111111")
},
expectedErr: "invalid rate: -0.000000000011111111",
},
{
name: "0 community pool inflation",
key: types.KeyCommunityPoolInflation,
testFn: func(params *types.Params) {
params.CommunityPoolInflation = sdk.ZeroDec()
},
expectedErr: "", // ok
},
{
name: "community pool inflation 1e-18 less than max rate",
key: types.KeyCommunityPoolInflation,
testFn: func(params *types.Params) {
params.CommunityPoolInflation = types.MaxMintingRate.Sub(sdk.NewDecWithPrec(1, 18))
},
expectedErr: "", // ok
},
{
name: "community pool inflation equal to max rate",
key: types.KeyCommunityPoolInflation,
testFn: func(params *types.Params) {
params.CommunityPoolInflation = types.MaxMintingRate
},
expectedErr: "", // ok
},
{
name: "community pool inflation 1e-18 greater than max rate",
key: types.KeyCommunityPoolInflation,
testFn: func(params *types.Params) {
params.CommunityPoolInflation = types.MaxMintingRate.Add(sdk.NewDecWithPrec(1, 18))
},
expectedErr: "invalid rate: 100.000000000000000001",
},
{
name: "nil staking inflation",
key: types.KeyStakingRewardsApy,
testFn: func(params *types.Params) {
params.StakingRewardsApy = sdk.Dec{}
},
expectedErr: "invalid rate: <nil>",
},
{
name: "negative staking inflation",
key: types.KeyStakingRewardsApy,
testFn: func(params *types.Params) {
params.StakingRewardsApy = sdk.MustNewDecFromStr("-0.000000002222222222")
},
expectedErr: "invalid rate: -0.000000002222222222",
},
{
name: "0 staking inflation",
key: types.KeyStakingRewardsApy,
testFn: func(params *types.Params) {
params.StakingRewardsApy = sdk.ZeroDec()
},
expectedErr: "", // ok
},
{
name: "staking inflation 1e-18 less than max rate",
key: types.KeyStakingRewardsApy,
testFn: func(params *types.Params) {
params.StakingRewardsApy = types.MaxMintingRate.Sub(sdk.NewDecWithPrec(1, 18))
},
expectedErr: "", // ok
},
{
name: "staking inflation equal to max rate",
key: types.KeyStakingRewardsApy,
testFn: func(params *types.Params) {
params.StakingRewardsApy = types.MaxMintingRate
},
expectedErr: "", // ok
},
{
name: "staking inflation 1e-18 greater than max rate",
key: types.KeyStakingRewardsApy,
testFn: func(params *types.Params) {
params.StakingRewardsApy = types.MaxMintingRate.Add(sdk.NewDecWithPrec(1, 18))
},
expectedErr: "invalid rate: 100.000000000000000001",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
params := types.DefaultParams()
tc.testFn(&params)
err := params.Validate()
if tc.expectedErr == "" {
assert.Nil(t, err)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
var paramSetPair *paramstypes.ParamSetPair
for _, pair := range params.ParamSetPairs() {
if bytes.Equal(pair.Key, tc.key) {
paramSetPair = &pair
break
}
}
require.NotNil(t, paramSetPair)
value := reflect.ValueOf(paramSetPair.Value).Elem().Interface()
// assert validation error is same as param set validation
assert.Equal(t, err, paramSetPair.ValidatorFn(value))
})
}
}
func TestParams_String(t *testing.T) {
params := newValidParams(t)
output := params.String()
assert.Contains(t, output, fmt.Sprintf("CommunityPoolInflation: %s", params.CommunityPoolInflation.String()))
assert.Contains(t, output, fmt.Sprintf("StakingRewardsApy: %s", params.StakingRewardsApy.String()))
}
func TestParams_UnmarshalJSON(t *testing.T) {
params := newValidParams(t)
poolRateData, err := json.Marshal(params.CommunityPoolInflation)
require.NoError(t, err)
stakingRateData, err := json.Marshal(params.StakingRewardsApy)
require.NoError(t, err)
data := fmt.Sprintf(`{
"community_pool_inflation": %s,
"staking_rewards_apy": %s
}`, string(poolRateData), string(stakingRateData))
var parsedParams types.Params
err = json.Unmarshal([]byte(data), &parsedParams)
require.NoError(t, err)
assert.Equal(t, params.CommunityPoolInflation, parsedParams.CommunityPoolInflation)
assert.Equal(t, params.StakingRewardsApy, parsedParams.StakingRewardsApy)
}
func TestParams_MarshalYAML(t *testing.T) {
p := newValidParams(t)
data, err := yaml.Marshal(p)
require.NoError(t, err)
var params map[string]interface{}
err = yaml.Unmarshal(data, &params)
require.NoError(t, err)
_, ok := params["community_pool_inflation"]
require.True(t, ok)
_, ok = params["staking_rewards_apy"]
require.True(t, ok)
}

View File

@ -0,0 +1,870 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: kava/kavamint/v1beta1/query.proto
package types
import (
context "context"
fmt "fmt"
_ "github.com/cosmos/cosmos-proto"
github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
_ "github.com/gogo/protobuf/gogoproto"
grpc1 "github.com/gogo/protobuf/grpc"
proto "github.com/gogo/protobuf/proto"
_ "google.golang.org/genproto/googleapis/api/annotations"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
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
// QueryParamsRequest defines the request type for querying x/kavamint parameters.
type QueryParamsRequest struct {
}
func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} }
func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) }
func (*QueryParamsRequest) ProtoMessage() {}
func (*QueryParamsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_3a66171519c59de7, []int{0}
}
func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_QueryParamsRequest.Merge(m, src)
}
func (m *QueryParamsRequest) XXX_Size() int {
return m.Size()
}
func (m *QueryParamsRequest) XXX_DiscardUnknown() {
xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m)
}
var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo
// QueryParamsResponse defines the response type for querying x/kavamint parameters.
type QueryParamsResponse struct {
Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
}
func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} }
func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) }
func (*QueryParamsResponse) ProtoMessage() {}
func (*QueryParamsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_3a66171519c59de7, []int{1}
}
func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_QueryParamsResponse.Merge(m, src)
}
func (m *QueryParamsResponse) XXX_Size() int {
return m.Size()
}
func (m *QueryParamsResponse) XXX_DiscardUnknown() {
xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m)
}
var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo
func (m *QueryParamsResponse) GetParams() Params {
if m != nil {
return m.Params
}
return Params{}
}
// QueryInflationRequest is the request type for the Query/Inflation RPC method.
type QueryInflationRequest struct {
}
func (m *QueryInflationRequest) Reset() { *m = QueryInflationRequest{} }
func (m *QueryInflationRequest) String() string { return proto.CompactTextString(m) }
func (*QueryInflationRequest) ProtoMessage() {}
func (*QueryInflationRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_3a66171519c59de7, []int{2}
}
func (m *QueryInflationRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *QueryInflationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_QueryInflationRequest.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 *QueryInflationRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_QueryInflationRequest.Merge(m, src)
}
func (m *QueryInflationRequest) XXX_Size() int {
return m.Size()
}
func (m *QueryInflationRequest) XXX_DiscardUnknown() {
xxx_messageInfo_QueryInflationRequest.DiscardUnknown(m)
}
var xxx_messageInfo_QueryInflationRequest proto.InternalMessageInfo
// QueryInflationResponse is the response type for the Query/Inflation RPC method.
type QueryInflationResponse struct {
// inflation is the current minting inflation value.
// example "0.990000000000000000" - 99% inflation
Inflation github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=inflation,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"inflation"`
}
func (m *QueryInflationResponse) Reset() { *m = QueryInflationResponse{} }
func (m *QueryInflationResponse) String() string { return proto.CompactTextString(m) }
func (*QueryInflationResponse) ProtoMessage() {}
func (*QueryInflationResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_3a66171519c59de7, []int{3}
}
func (m *QueryInflationResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *QueryInflationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_QueryInflationResponse.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 *QueryInflationResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_QueryInflationResponse.Merge(m, src)
}
func (m *QueryInflationResponse) XXX_Size() int {
return m.Size()
}
func (m *QueryInflationResponse) XXX_DiscardUnknown() {
xxx_messageInfo_QueryInflationResponse.DiscardUnknown(m)
}
var xxx_messageInfo_QueryInflationResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*QueryParamsRequest)(nil), "kava.kavamint.v1beta1.QueryParamsRequest")
proto.RegisterType((*QueryParamsResponse)(nil), "kava.kavamint.v1beta1.QueryParamsResponse")
proto.RegisterType((*QueryInflationRequest)(nil), "kava.kavamint.v1beta1.QueryInflationRequest")
proto.RegisterType((*QueryInflationResponse)(nil), "kava.kavamint.v1beta1.QueryInflationResponse")
}
func init() { proto.RegisterFile("kava/kavamint/v1beta1/query.proto", fileDescriptor_3a66171519c59de7) }
var fileDescriptor_3a66171519c59de7 = []byte{
// 397 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xcc, 0x4e, 0x2c, 0x4b,
0xd4, 0x07, 0x11, 0xb9, 0x99, 0x79, 0x25, 0xfa, 0x65, 0x86, 0x49, 0xa9, 0x25, 0x89, 0x86, 0xfa,
0x85, 0xa5, 0xa9, 0x45, 0x95, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xa2, 0x20, 0x59, 0x3d,
0x98, 0x12, 0x3d, 0xa8, 0x12, 0x29, 0xc9, 0xe4, 0xfc, 0xe2, 0xdc, 0xfc, 0xe2, 0x78, 0xb0, 0x22,
0x7d, 0x08, 0x07, 0xa2, 0x43, 0x4a, 0x24, 0x3d, 0x3f, 0x3d, 0x1f, 0x22, 0x0e, 0x62, 0x41, 0x45,
0x65, 0xd2, 0xf3, 0xf3, 0xd3, 0x73, 0x52, 0xf5, 0x13, 0x0b, 0x32, 0xf5, 0x13, 0xf3, 0xf2, 0xf2,
0x4b, 0x12, 0x4b, 0x32, 0xf3, 0xf3, 0x60, 0x7a, 0x54, 0xb0, 0x3b, 0x04, 0x6e, 0x2d, 0x58, 0x95,
0x92, 0x08, 0x97, 0x50, 0x20, 0xc8, 0x69, 0x01, 0x89, 0x45, 0x89, 0xb9, 0xc5, 0x41, 0xa9, 0x85,
0xa5, 0xa9, 0xc5, 0x25, 0x4a, 0x41, 0x5c, 0xc2, 0x28, 0xa2, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9,
0x42, 0xd6, 0x5c, 0x6c, 0x05, 0x60, 0x11, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x59, 0x3d,
0xac, 0x3e, 0xd1, 0x83, 0x68, 0x73, 0x62, 0x39, 0x71, 0x4f, 0x9e, 0x21, 0x08, 0xaa, 0x45, 0x49,
0x9c, 0x4b, 0x14, 0x6c, 0xa6, 0x67, 0x5e, 0x5a, 0x0e, 0xd8, 0xa1, 0x30, 0xcb, 0x4a, 0xb8, 0xc4,
0xd0, 0x25, 0xa0, 0xf6, 0x45, 0x71, 0x71, 0x66, 0xc2, 0x04, 0xc1, 0x56, 0x72, 0x3a, 0xd9, 0x80,
0xcc, 0xbc, 0x75, 0x4f, 0x5e, 0x2d, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x17,
0x1a, 0x54, 0x50, 0x4a, 0xb7, 0x38, 0x25, 0x5b, 0xbf, 0xa4, 0xb2, 0x20, 0xb5, 0x58, 0xcf, 0x25,
0x35, 0xf9, 0xd2, 0x16, 0x5d, 0x2e, 0x68, 0x48, 0xba, 0xa4, 0x26, 0x07, 0x21, 0x8c, 0x33, 0x5a,
0xca, 0xc4, 0xc5, 0x0a, 0xb6, 0x56, 0xa8, 0x8d, 0x91, 0x8b, 0x0d, 0xe2, 0x62, 0x21, 0x4d, 0x1c,
0x1e, 0xc2, 0x0c, 0x22, 0x29, 0x2d, 0x62, 0x94, 0x42, 0xfc, 0xa1, 0xa4, 0xda, 0x74, 0xf9, 0xc9,
0x64, 0x26, 0x79, 0x21, 0x59, 0x7d, 0xec, 0x71, 0x02, 0x09, 0x21, 0xa1, 0x49, 0x8c, 0x5c, 0x9c,
0xf0, 0x40, 0x10, 0xd2, 0xc1, 0x67, 0x01, 0x7a, 0x20, 0x4a, 0xe9, 0x12, 0xa9, 0x1a, 0xea, 0x22,
0x0d, 0xb0, 0x8b, 0x94, 0x84, 0x14, 0x70, 0xb8, 0x08, 0x1e, 0x4e, 0x4e, 0x2e, 0x27, 0x1e, 0xc9,
0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e,
0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x85, 0x14, 0x05, 0x20, 0x03, 0x74, 0x73, 0x12,
0x93, 0x8a, 0x21, 0xe6, 0x55, 0x20, 0x4c, 0x04, 0x47, 0x45, 0x12, 0x1b, 0x38, 0xb5, 0x19, 0x03,
0x02, 0x00, 0x00, 0xff, 0xff, 0xfd, 0x9b, 0x02, 0x7d, 0x1e, 0x03, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// QueryClient is the client API for Query service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type QueryClient interface {
// Params queries the parameters of x/kavamint module.
Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error)
// Inflation queries x/kavamint for the overall cumulative inflation rate of KAVA.
Inflation(ctx context.Context, in *QueryInflationRequest, opts ...grpc.CallOption) (*QueryInflationResponse, error)
}
type queryClient struct {
cc grpc1.ClientConn
}
func NewQueryClient(cc grpc1.ClientConn) QueryClient {
return &queryClient{cc}
}
func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) {
out := new(QueryParamsResponse)
err := c.cc.Invoke(ctx, "/kava.kavamint.v1beta1.Query/Params", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryClient) Inflation(ctx context.Context, in *QueryInflationRequest, opts ...grpc.CallOption) (*QueryInflationResponse, error) {
out := new(QueryInflationResponse)
err := c.cc.Invoke(ctx, "/kava.kavamint.v1beta1.Query/Inflation", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// QueryServer is the server API for Query service.
type QueryServer interface {
// Params queries the parameters of x/kavamint module.
Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error)
// Inflation queries x/kavamint for the overall cumulative inflation rate of KAVA.
Inflation(context.Context, *QueryInflationRequest) (*QueryInflationResponse, error)
}
// UnimplementedQueryServer can be embedded to have forward compatible implementations.
type UnimplementedQueryServer struct {
}
func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Params not implemented")
}
func (*UnimplementedQueryServer) Inflation(ctx context.Context, req *QueryInflationRequest) (*QueryInflationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Inflation not implemented")
}
func RegisterQueryServer(s grpc1.Server, srv QueryServer) {
s.RegisterService(&_Query_serviceDesc, srv)
}
func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryParamsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryServer).Params(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kava.kavamint.v1beta1.Query/Params",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Query_Inflation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryInflationRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryServer).Inflation(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kava.kavamint.v1beta1.Query/Inflation",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryServer).Inflation(ctx, req.(*QueryInflationRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Query_serviceDesc = grpc.ServiceDesc{
ServiceName: "kava.kavamint.v1beta1.Query",
HandlerType: (*QueryServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Params",
Handler: _Query_Params_Handler,
},
{
MethodName: "Inflation",
Handler: _Query_Inflation_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "kava/kavamint/v1beta1/query.proto",
}
func (m *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
return len(dAtA) - i, nil
}
func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
{
size, err := m.Params.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintQuery(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func (m *QueryInflationRequest) 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 *QueryInflationRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *QueryInflationRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
return len(dAtA) - i, nil
}
func (m *QueryInflationResponse) 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 *QueryInflationResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *QueryInflationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
{
size := m.Inflation.Size()
i -= size
if _, err := m.Inflation.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i = encodeVarintQuery(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func encodeVarintQuery(dAtA []byte, offset int, v uint64) int {
offset -= sovQuery(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *QueryParamsRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
return n
}
func (m *QueryParamsResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = m.Params.Size()
n += 1 + l + sovQuery(uint64(l))
return n
}
func (m *QueryInflationRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
return n
}
func (m *QueryInflationResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = m.Inflation.Size()
n += 1 + l + sovQuery(uint64(l))
return n
}
func sovQuery(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozQuery(x uint64) (n int) {
return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *QueryParamsRequest) 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 ErrIntOverflowQuery
}
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: QueryParamsRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipQuery(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthQuery
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *QueryParamsResponse) 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 ErrIntOverflowQuery
}
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: QueryParamsResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowQuery
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthQuery
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthQuery
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipQuery(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthQuery
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *QueryInflationRequest) 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 ErrIntOverflowQuery
}
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: QueryInflationRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: QueryInflationRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipQuery(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthQuery
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *QueryInflationResponse) 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 ErrIntOverflowQuery
}
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: QueryInflationResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: QueryInflationResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Inflation", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowQuery
}
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 ErrInvalidLengthQuery
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthQuery
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.Inflation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipQuery(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthQuery
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipQuery(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, ErrIntOverflowQuery
}
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, ErrIntOverflowQuery
}
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, ErrIntOverflowQuery
}
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, ErrInvalidLengthQuery
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupQuery
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthQuery
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group")
)

View File

@ -0,0 +1,218 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: kava/kavamint/v1beta1/query.proto
/*
Package types is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package types
import (
"context"
"io"
"net/http"
"github.com/golang/protobuf/descriptor"
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = descriptor.ForMessage
var _ = metadata.Join
func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq QueryParamsRequest
var metadata runtime.ServerMetadata
msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq QueryParamsRequest
var metadata runtime.ServerMetadata
msg, err := server.Params(ctx, &protoReq)
return msg, metadata, err
}
func request_Query_Inflation_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq QueryInflationRequest
var metadata runtime.ServerMetadata
msg, err := client.Inflation(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Query_Inflation_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq QueryInflationRequest
var metadata runtime.ServerMetadata
msg, err := server.Inflation(ctx, &protoReq)
return msg, metadata, err
}
// RegisterQueryHandlerServer registers the http handlers for service Query to "mux".
// UnaryRPC :call QueryServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead.
func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error {
mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Query_Inflation_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Query_Inflation_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Query_Inflation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterQueryHandler(ctx, mux, conn)
}
// RegisterQueryHandler registers the http handlers for service Query to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn))
}
// RegisterQueryHandlerClient registers the http handlers for service Query
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "QueryClient" to call the correct interceptors.
func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error {
mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Query_Inflation_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Query_Inflation_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Query_Inflation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"kava", "kavamint", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false)))
pattern_Query_Inflation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"kava", "kavamint", "v1beta1", "inflation"}, "", runtime.AssumeColonVerbOpt(false)))
)
var (
forward_Query_Params_0 = runtime.ForwardResponseMessage
forward_Query_Inflation_0 = runtime.ForwardResponseMessage
)