Refactor community & inflation enhancements (#1428)

* refactor begin blocker to single func

* remove unused inflation keeper methods

* refactor to private keeper methods

* add testcase for failed mint due to invalid param

* add testcase for GetStakingApy()

* check for zero instead of empty

* actually test super long block time

* skip fund account for earn community proposals

* test x/community keeper GetModuleAccountBalance

* update x/kavamint begin block spec
This commit is contained in:
Robert Pirtle 2022-12-19 13:50:11 -08:00 committed by GitHub
parent dd856bb288
commit 4c1524d7bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 222 additions and 209 deletions

View File

@ -40,6 +40,7 @@ func (suite *KeeperTestSuite) TestCommunityPool() {
// check that community pool received balance
suite.App.CheckBalance(suite.T(), suite.Ctx, maccAddr, funds)
suite.Equal(funds, suite.Keeper.GetModuleAccountBalance(suite.Ctx))
// check that sender had balance deducted
suite.App.CheckBalance(suite.T(), suite.Ctx, sender, sdk.NewCoins())
})
@ -51,12 +52,14 @@ func (suite *KeeperTestSuite) TestCommunityPool() {
// community pool has funds deducted
suite.App.CheckBalance(suite.T(), suite.Ctx, maccAddr, sdk.NewCoins())
suite.Equal(sdk.NewCoins(), suite.Keeper.GetModuleAccountBalance(suite.Ctx))
// 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() {
suite.Equal(sdk.NewCoins(), suite.Keeper.GetModuleAccountBalance(suite.Ctx))
err := suite.Keeper.DistributeFromCommunityPool(suite.Ctx, sender, funds)
suite.Require().ErrorContains(err, "insufficient funds")
})

View File

@ -4,46 +4,15 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/earn/types"
kavadisttypes "github.com/kava-labs/kava/x/kavadist/types"
)
// HandleCommunityPoolDepositProposal is a handler for executing a passed community pool deposit proposal
func HandleCommunityPoolDepositProposal(ctx sdk.Context, k Keeper, p *types.CommunityPoolDepositProposal) error {
// move funds from community pool to the funding account
if err := k.bankKeeper.SendCoinsFromModuleToModule(
ctx,
k.communityPoolMaccName,
kavadisttypes.FundModuleAccount,
sdk.NewCoins(p.Amount),
); err != nil {
return err
}
err := k.DepositFromModuleAccount(ctx, kavadisttypes.FundModuleAccount, p.Amount, types.STRATEGY_TYPE_SAVINGS)
if err != nil {
return err
}
return nil
return k.DepositFromModuleAccount(ctx, k.communityPoolMaccName, p.Amount, types.STRATEGY_TYPE_SAVINGS)
}
// HandleCommunityPoolWithdrawProposal is a handler for executing a passed community pool withdraw proposal.
func HandleCommunityPoolWithdrawProposal(ctx sdk.Context, k Keeper, p *types.CommunityPoolWithdrawProposal) error {
// Withdraw to fund module account
withdrawAmount, err := k.WithdrawFromModuleAccount(ctx, kavadisttypes.FundModuleAccount, p.Amount, types.STRATEGY_TYPE_SAVINGS)
if err != nil {
return err
}
// Move funds to the community pool manually
if err := k.bankKeeper.SendCoinsFromModuleToModule(
ctx,
kavadisttypes.FundModuleAccount,
k.communityPoolMaccName,
sdk.NewCoins(withdrawAmount),
); err != nil {
return err
}
return nil
_, err := k.WithdrawFromModuleAccount(ctx, k.communityPoolMaccName, p.Amount, types.STRATEGY_TYPE_SAVINGS)
return err
}

View File

@ -51,7 +51,6 @@ func (suite *proposalTestSuite) TestCommunityWithdrawProposal() {
depositAmount := sdk.NewCoin("ukava", sdk.NewInt(10000000))
suite.Require().NoError(suite.App.FundModuleAccount(ctx, macc.GetName(), fundAmount))
// TODO update to STRATEGY_TYPE_SAVINGS once implemented
suite.CreateVault("ukava", types.StrategyTypes{types.STRATEGY_TYPE_SAVINGS}, false, nil)
deposit := types.NewCommunityPoolDepositProposal("test title",
"desc", depositAmount)

View File

@ -1,75 +1,13 @@
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 {
if err := k.AccumulateAndMintInflation(ctx); 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()),
),
)
}

View File

@ -103,6 +103,15 @@ func (suite *abciTestSuite) Test_BeginBlocker_MintsExpectedTokens() {
expCommunityPoolBalance: sdk.ZeroInt(),
expFeeCollectorBalance: sdk.ZeroInt(),
},
{
name: "mints no tokens if zero seconds passed",
blockTime: 0,
communityPoolInflation: sdk.NewDecWithPrec(50, 2),
stakingRewardsApy: sdk.NewDecWithPrec(20, 2),
bondedRatio: sdk.NewDecWithPrec(35, 2),
expCommunityPoolBalance: sdk.ZeroInt(),
expFeeCollectorBalance: sdk.ZeroInt(),
},
{
name: "mints community pool inflation and staking rewards",
blockTime: 6,
@ -121,8 +130,8 @@ func (suite *abciTestSuite) Test_BeginBlocker_MintsExpectedTokens() {
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
name: "handles long block time",
blockTime: 60, // a minute
communityPoolInflation: sdk.NewDecWithPrec(50, 2),
stakingRewardsApy: sdk.NewDecWithPrec(20, 2),
bondedRatio: sdk.NewDecWithPrec(35, 2),
@ -131,6 +140,17 @@ func (suite *abciTestSuite) Test_BeginBlocker_MintsExpectedTokens() {
// 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),
},
{
name: "handles extra long block time",
blockTime: 3 * 3600, // three hours
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%2C3*3600%5D-1%5C%2841%29*1e10
expCommunityPoolBalance: sdk.NewInt(1388675),
// https://www.wolframalpha.com/input?i2d=true&i=%5C%2840%29Power%5B%5C%2840%29Surd%5B1.20%2C31536000%5D%5C%2841%29%2C3*3600%5D-1%5C%2841%29*1e10*.35
expFeeCollectorBalance: sdk.NewInt(218542),
},
}
for _, tc := range testCases {

View File

@ -66,8 +66,8 @@ func (mq MintQueryServer) Inflation(
ctx := sdk.UnwrapSDKContext(c)
stakingApy := mq.keeper.GetParams(ctx).StakingRewardsApy
totalBonded := mq.keeper.TotalBondedTokens(ctx)
totalSupply := mq.keeper.TotalSupply(ctx)
totalBonded := mq.keeper.totalBondedTokens(ctx)
totalSupply := mq.keeper.totalSupply(ctx)
// inflation = staking_apy * total_bonded / total_supply
inflation := stakingApy.MulInt(totalBonded).QuoInt(totalSupply)

View File

@ -1,7 +1,11 @@
package keeper
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/kavamint/types"
)
// this is the same value used in the x/hard
@ -9,32 +13,46 @@ 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)
// Minter wraps the logic of a single source of inflation. It calculates the amount of coins to be
// minted to match a yearly `rate` of inflating the `basis`.
type Minter struct {
name string // name of the minting. used as event attribute to report how much is minted
rate sdk.Dec // the yearly apy of the mint. ex. 20% => 0.2
basis sdk.Int // the base amount of coins that is inflated
destMaccName string // the destination module account name to mint coins to
mintDenom string // denom of coin this minter is responsible for minting
}
// 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
// NewMinter returns creates a new source of minting inflation
func NewMinter(
name string, rate sdk.Dec, basis sdk.Int, mintDenom string, destMaccName string,
) Minter {
return Minter{
name: name,
rate: rate,
basis: basis,
destMaccName: destMaccName,
mintDenom: mintDenom,
}
}
amount := inflationRate.MulInt(basis).TruncateInt()
return sdk.NewCoins(sdk.NewCoin(bondDenom, amount)), nil
// AccumulateInflation calculates the number of coins that should be minted to match a yearly `rate`
// for interest compounded each second of the year over `secondsPassed` seconds.
// `basis` is the base amount of coins that is inflated.
func (m Minter) AccumulateInflation(secondsPassed uint64) (sdk.Coin, error) {
// calculate the rate factor based on apy & seconds passed since last block
inflationRate, err := m.CalculateInflationRate(secondsPassed)
if err != nil {
return sdk.Coin{}, err
}
amount := inflationRate.MulInt(m.basis).TruncateInt()
return sdk.NewCoin(m.mintDenom, 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()))
func (m Minter) CalculateInflationRate(secondsPassed uint64) (sdk.Dec, error) {
perSecondInterestRate, err := apyToSpy(m.rate.Add(sdk.OneDec()))
if err != nil {
return sdk.ZeroDec(), err
}
@ -42,6 +60,80 @@ func CalculateInflationRate(apy sdk.Dec, secondsPassed uint64) (sdk.Dec, error)
return rate.Sub(sdk.OneDec()), nil
}
// AccumulateAndMintInflation defines the sources of inflation, determines the seconds passed since
// the last mint, and then mints each source of inflation to the defined destination.
func (k Keeper) AccumulateAndMintInflation(ctx sdk.Context) error {
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)
bondDenom := k.bondDenom(ctx)
// define minters for each contributing piece of inflation
minters := []Minter{
// staking rewards
NewMinter(
types.AttributeKeyStakingRewardMint,
params.StakingRewardsApy,
totalBonded,
bondDenom,
k.stakingRewardsFeeCollectorName,
),
// community pool inflation
NewMinter(
types.AttributeKeyCommunityPoolMint,
params.CommunityPoolInflation,
totalSupply,
bondDenom,
k.communityPoolModuleAccountName,
),
}
mintEventAttrs := make([]sdk.Attribute, 0, len(minters))
for _, minter := range minters {
// calculate amount for time passed
amount, err := minter.AccumulateInflation(uint64(secondsPassed))
if err != nil {
return err
}
// mint & transfer coins
inflation := sdk.NewCoins(amount)
if err := k.mintCoinsToModule(ctx, inflation, minter.destMaccName); err != nil {
return err
}
// add attribute to event
mintEventAttrs = append(mintEventAttrs, sdk.NewAttribute(
minter.name,
inflation.String(),
))
}
// ------------- 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)),
).AppendAttributes(mintEventAttrs...),
)
return 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) {

View File

@ -116,7 +116,8 @@ func (suite *InflationTestSuite) TestCalculateInflationFactor() {
for _, tc := range testCases {
suite.Run(tc.name, func() {
actualRate, err := keeper.CalculateInflationRate(tc.apy, tc.secondsPassed)
minter := keeper.NewMinter(tc.name, tc.apy, sdk.ZeroInt(), "ukava", "ignored")
actualRate, err := minter.CalculateInflationRate(tc.secondsPassed)
suite.Require().NoError(err)
marginOfError := sdk.ZeroDec()
@ -127,11 +128,35 @@ func (suite *InflationTestSuite) TestCalculateInflationFactor() {
})
}
// 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)
//})
suite.Run("errors with out-of-bounds when rate is too high", func() {
// Dec.ApproxRoot will error w/ out-of-bounds when rate is >176.5
oob := sdk.NewDec(177)
minter := keeper.NewMinter(
"out-of-bounds-minter",
oob,
sdk.OneInt(),
"uakva",
"ignored",
)
_, err := minter.CalculateInflationRate(100)
suite.Error(err)
// ensure max mint rate is less than out-of-bounds value
suite.True(types.MaxMintingRate.LT(oob))
})
}
func (suite *InflationTestSuite) Test_AccumulateInflation_FailsWithInvalidRate() {
oob := sdk.NewDec(177)
minter := keeper.NewMinter(
"out-of-bounds-minter",
oob,
sdk.OneInt(),
"uakva",
"ignored",
)
_, err := minter.AccumulateInflation(1)
suite.ErrorContains(err, "out of bounds")
}
func (suite *InflationTestSuite) requireWithinError(expected, actual, margin sdk.Dec) {

View File

@ -12,28 +12,9 @@ import (
"github.com/kava-labs/kava/x/kavamint/types"
)
// KeeperI is the required keeper interface for x/kavamint's begin blocker
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)
AccumulateAndMintInflation(ctx sdk.Context) error
}
// Keeper of the kavamint store
@ -93,44 +74,40 @@ func (k Keeper) GetStakingApy(ctx sdk.Context) sdk.Dec {
return params.StakingRewardsApy
}
// BondDenom implements an alias call to the underlying staking keeper's BondDenom.
func (k Keeper) BondDenom(ctx sdk.Context) string {
// 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 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 {
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() {
// mintCoinsToModule mints teh desired coins to the x/kavamint module account and then
// transfers them to the designated module account.
// if `newCoins` is empty or zero, this method is a noop.
func (k Keeper) mintCoinsToModule(ctx sdk.Context, newCoins sdk.Coins, destMaccName string) error {
if newCoins.IsZero() {
// skip as no coins need to be minted
return nil
}
return k.bankKeeper.MintCoins(ctx, types.ModuleName, newCoins)
// mint the coins
err := k.bankKeeper.MintCoins(ctx, types.ModuleAccountName, newCoins)
if err != nil {
return nil
}
// transfer them to the desired destination module account
return k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleAccountName, destMaccName, 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
// 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) totalSupply(ctx sdk.Context) sdk.Int {
return k.bankKeeper.GetSupply(ctx, k.bondDenom(ctx)).Amount
}
func (k Keeper) CumulativeInflation(ctx sdk.Context) sdk.Dec {
@ -141,8 +118,8 @@ func (k Keeper) CumulativeInflation(ctx sdk.Context) sdk.Dec {
totalInflation = totalInflation.Add(params.CommunityPoolInflation)
// staking rewards contribution is the apy * bonded_ratio
bondedSupply := k.TotalBondedTokens(ctx)
totalSupply := k.TotalSupply(ctx)
bondedSupply := k.totalBondedTokens(ctx)
totalSupply := k.totalSupply(ctx)
bondedRatio := sdk.NewDecFromInt(bondedSupply).QuoInt(totalSupply)
inflationFromStakingRewards := params.StakingRewardsApy.Mul(bondedRatio)

View File

@ -51,3 +51,21 @@ func (suite keeperTestSuite) TestPreviousBlockTime_Persistance() {
keeper.SetPreviousBlockTime(suite.Ctx, newTime)
suite.Equal(keeper.GetPreviousBlockTime(suite.Ctx), newTime)
}
func (suite keeperTestSuite) Test_GetStakingApy() {
suite.SetupTest()
testCases := []sdk.Dec{
sdk.ZeroDec(),
sdk.MustNewDecFromStr("0.000005"),
sdk.MustNewDecFromStr("0.15"),
sdk.OneDec(),
types.MaxMintingRate,
}
for _, tc := range testCases {
params := types.NewParams(sdk.ZeroDec(), tc)
suite.Keeper.SetParams(suite.Ctx, params)
suite.Equal(tc, suite.Keeper.GetStakingApy(suite.Ctx))
}
}

View File

@ -9,41 +9,13 @@ 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())
if err := k.AccumulateAndMintInflation(ctx); err != nil {
panic(err)
}
}
```
`AccumulateInflation` determines the effective rate of the yearly interest rate assuming it is
`AccumulateAndMintInflation` defines all sources of inflation from yearly APYs set via the parameters.
Those rates are converted to 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.
more details on calculations & the defined sources of inflation.

View File

@ -31,7 +31,7 @@ func (suite *KavamintTestSuite) SetupTest() {
suite.Keeper = suite.App.GetKavamintKeeper()
suite.StakingKeeper = suite.App.GetStakingKeeper()
suite.BondDenom = suite.Keeper.BondDenom(suite.Ctx)
suite.BondDenom = suite.StakingKeeper.BondDenom(suite.Ctx)
}
// SetBondedTokenRatio mints the total supply to an account and creates a validator with a self