diff --git a/x/mint/abci.go b/x/mint/abci.go index ec76de4c..f8206ad7 100644 --- a/x/mint/abci.go +++ b/x/mint/abci.go @@ -10,22 +10,27 @@ import ( ) // BeginBlocker mints new tokens for the previous block. -func BeginBlocker(ctx sdk.Context, k keeper.Keeper, ic types.InflationCalculationFn) { +func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) // fetch stored minter & params minter := k.GetMinter(ctx) params := k.GetParams(ctx) - // recalculate inflation rate - totalStakingSupply := k.StakingTokenSupply(ctx) - bondedRatio := k.BondedRatio(ctx) - minter.Inflation = ic(ctx, minter, params, bondedRatio) - minter.AnnualProvisions = minter.NextAnnualProvisions(params, totalStakingSupply) - k.SetMinter(ctx, minter) + totalSupply := k.TokenSupply(ctx, params.MintDenom) + targetSupply := minter.TargetSupply + + // If we have reached the end of the year by reaching the targeted supply for the year + // We need to re-calculate the next inflation for the next year. + if totalSupply.GTE(targetSupply) { + minter.Inflation = minter.NextInflation() + minter.AnnualProvisions = minter.NextAnnualProvisions(params, totalSupply) + minter.TargetSupply = totalSupply.Add(minter.AnnualProvisions.TruncateInt()) + k.SetMinter(ctx, minter) + } // mint coins, update supply - mintedCoin := minter.BlockProvision(params) + mintedCoin := minter.BlockProvision(params, totalSupply) mintedCoins := sdk.NewCoins(mintedCoin) err := k.MintCoins(ctx, mintedCoins) @@ -46,7 +51,6 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper, ic types.InflationCalculatio ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeMint, - sdk.NewAttribute(types.AttributeKeyBondedRatio, bondedRatio.String()), sdk.NewAttribute(types.AttributeKeyInflation, minter.Inflation.String()), sdk.NewAttribute(types.AttributeKeyAnnualProvisions, minter.AnnualProvisions.String()), sdk.NewAttribute(sdk.AttributeKeyAmount, mintedCoin.Amount.String()), diff --git a/x/mint/keeper/keeper.go b/x/mint/keeper/keeper.go index 7eddc418..471d4ffd 100644 --- a/x/mint/keeper/keeper.go +++ b/x/mint/keeper/keeper.go @@ -7,8 +7,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/okp4/okp4d/x/mint/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/okp4/okp4d/x/mint/types" ) // Keeper of the mint store @@ -82,6 +82,12 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { k.paramSpace.SetParamSet(ctx, ¶ms) } +// TokenSupply implements an alias call to the underlying bank keeper's +// TokenSupply to be used in BeginBlocker. +func (k Keeper) TokenSupply(ctx sdk.Context, denom string) math.Int { + return k.bankKeeper.GetSupply(ctx, denom).Amount +} + // StakingTokenSupply implements an alias call to the underlying staking keeper's // StakingTokenSupply to be used in BeginBlocker. func (k Keeper) StakingTokenSupply(ctx sdk.Context) math.Int { diff --git a/x/mint/module.go b/x/mint/module.go index 8ccce7b4..065b2fe6 100644 --- a/x/mint/module.go +++ b/x/mint/module.go @@ -83,23 +83,14 @@ type AppModule struct { keeper keeper.Keeper authKeeper types.AccountKeeper - - // inflationCalculator is used to calculate the inflation rate during BeginBlock. - // If inflationCalculator is nil, the default inflation calculation logic is used. - inflationCalculator types.InflationCalculationFn } -// NewAppModule creates a new AppModule object. If the InflationCalculationFn -// argument is nil, then the SDK's default inflation function will be used. -func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, ak types.AccountKeeper, ic types.InflationCalculationFn) AppModule { - if ic == nil { - ic = types.DefaultInflationCalculationFn - } +// 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, - inflationCalculator: ic, + AppModuleBasic: AppModuleBasic{cdc: cdc}, + keeper: keeper, + authKeeper: ak, } } @@ -152,7 +143,7 @@ func (AppModule) ConsensusVersion() uint64 { return 1 } // BeginBlock returns the begin blocker for the mint module. func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { - BeginBlocker(ctx, am.keeper, am.inflationCalculator) + BeginBlocker(ctx, am.keeper) } // AppModuleSimulation functions diff --git a/x/mint/simulation/genesis.go b/x/mint/simulation/genesis.go index 0b6f1d4a..786ed099 100644 --- a/x/mint/simulation/genesis.go +++ b/x/mint/simulation/genesis.go @@ -7,6 +7,7 @@ import ( "fmt" "math/rand" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/okp4/okp4d/x/mint/types" @@ -82,9 +83,9 @@ func RandomizedGenState(simState *module.SimulationState) { mintDenom := sdk.DefaultBondDenom blocksPerYear := uint64(60 * 60 * 8766 / 5) - params := types.NewParams(mintDenom, inflationRateChange, inflationMax, inflationMin, goalBonded, blocksPerYear) + params := types.NewParams(mintDenom, blocksPerYear) - mintGenesis := types.NewGenesisState(types.InitialMinter(inflation), params) + mintGenesis := types.NewGenesisState(types.InitialMinter(inflation, sdk.NewDecWithPrec(20, 2), math.NewInt(230)), params) bz, err := json.MarshalIndent(&mintGenesis, "", " ") if err != nil { diff --git a/x/mint/types/expected_keepers.go b/x/mint/types/expected_keepers.go index 68fb5765..d89d0b14 100644 --- a/x/mint/types/expected_keepers.go +++ b/x/mint/types/expected_keepers.go @@ -24,6 +24,7 @@ type AccountKeeper interface { // BankKeeper defines the contract needed to be fulfilled for banking and supply // dependencies. type BankKeeper interface { + GetSupply(ctx sdk.Context, denom string) sdk.Coin SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error MintCoins(ctx sdk.Context, name string, amt sdk.Coins) error diff --git a/x/mint/types/genesis.go b/x/mint/types/genesis.go index 75c69502..3d2f761b 100644 --- a/x/mint/types/genesis.go +++ b/x/mint/types/genesis.go @@ -1,21 +1,5 @@ package types -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// InflationCalculationFn defines the function required to calculate inflation rate during -// BeginBlock. It receives the minter and params stored in the keeper, along with the current -// bondedRatio and returns the newly calculated inflation rate. -// It can be used to specify a custom inflation calculation logic, instead of relying on the -// default logic provided by the sdk. -type InflationCalculationFn func(ctx sdk.Context, minter Minter, params Params, bondedRatio sdk.Dec) sdk.Dec - -// DefaultInflationCalculationFn is the default function used to calculate inflation. -func DefaultInflationCalculationFn(_ sdk.Context, minter Minter, params Params, bondedRatio sdk.Dec) sdk.Dec { - return minter.NextInflationRate(params, bondedRatio) -} - // NewGenesisState creates a new GenesisState object func NewGenesisState(minter Minter, params Params) *GenesisState { return &GenesisState{ diff --git a/x/mint/types/minter.go b/x/mint/types/minter.go index 38f0b77c..9a600da3 100644 --- a/x/mint/types/minter.go +++ b/x/mint/types/minter.go @@ -7,28 +7,34 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// NewMinter returns a new Minter object with the given inflation and annual -// provisions values. -func NewMinter(inflation, annualProvisions sdk.Dec) Minter { +// NewMinter returns a new Minter object with the given inflation, annual +// provisions values and annual reduction factor. +func NewMinter(inflation, annualProvisions, annualReductionFactor sdk.Dec, targetSupply math.Int) Minter { return Minter{ - Inflation: inflation, - AnnualProvisions: annualProvisions, + Inflation: inflation, + AnnualProvisions: annualProvisions, + AnnualReductionFactor: annualReductionFactor, + TargetSupply: targetSupply, } } -// InitialMinter returns an initial Minter object with a given inflation value. -func InitialMinter(inflation sdk.Dec) Minter { +// InitialMinter returns an initial Minter object with a given inflation value and annual reduction factor. +func InitialMinter(inflation, annualReductionFactor sdk.Dec, targetSupply math.Int) Minter { return NewMinter( inflation, sdk.NewDec(0), + annualReductionFactor, + targetSupply, ) } // DefaultInitialMinter returns a default initial Minter object for a new chain -// which uses an inflation rate of 13%. +// which uses an inflation rate of 15%. func DefaultInitialMinter() Minter { return InitialMinter( - sdk.NewDecWithPrec(13, 2), + sdk.NewDecWithPrec(15, 2), + sdk.NewDecWithPrec(20, 2), + math.NewInt(230000000000000), ) } @@ -41,30 +47,10 @@ func ValidateMinter(minter Minter) error { return nil } -// NextInflationRate returns the new inflation rate for the next hour. -func (m Minter) NextInflationRate(params Params, bondedRatio sdk.Dec) sdk.Dec { - // The target annual inflation rate is recalculated for each previsions cycle. The - // inflation is also subject to a rate change (positive or negative) depending on - // the distance from the desired ratio (67%). The maximum rate change possible is - // defined to be 13% per year, however the annual inflation is capped as between - // 7% and 20%. - - // (1 - bondedRatio/GoalBonded) * InflationRateChange - inflationRateChangePerYear := sdk.OneDec(). - Sub(bondedRatio.Quo(params.GoalBonded)). - Mul(params.InflationRateChange) - inflationRateChange := inflationRateChangePerYear.Quo(sdk.NewDec(int64(params.BlocksPerYear))) - - // adjust the new annual inflation for this next cycle - inflation := m.Inflation.Add(inflationRateChange) // note inflationRateChange may be negative - if inflation.GT(params.InflationMax) { - inflation = params.InflationMax - } - if inflation.LT(params.InflationMin) { - inflation = params.InflationMin - } - - return inflation +// NextInflation return the new inflation rate for the next year +// Get the current inflation and multiply by (1 - annual reduction factor). +func (m Minter) NextInflation() sdk.Dec { + return m.Inflation.Mul(sdk.OneDec().Sub(m.AnnualReductionFactor)) } // NextAnnualProvisions returns the annual provisions based on current total @@ -75,7 +61,14 @@ func (m Minter) NextAnnualProvisions(_ Params, totalSupply math.Int) sdk.Dec { // BlockProvision returns the provisions for a block based on the annual // provisions rate. -func (m Minter) BlockProvision(params Params) sdk.Coin { +func (m Minter) BlockProvision(params Params, totalSupply math.Int) sdk.Coin { provisionAmt := m.AnnualProvisions.QuoInt(sdk.NewInt(int64(params.BlocksPerYear))) + + // Fixe rounding by limiting to the target supply at the end of the year block. + futureSupply := totalSupply.Add(provisionAmt.TruncateInt()) + if futureSupply.GT(m.TargetSupply) { + return sdk.NewCoin(params.MintDenom, m.TargetSupply.Sub(totalSupply)) + } + return sdk.NewCoin(params.MintDenom, provisionAmt.TruncateInt()) } diff --git a/x/mint/types/params.go b/x/mint/types/params.go index 69a909af..960a4040 100644 --- a/x/mint/types/params.go +++ b/x/mint/types/params.go @@ -27,27 +27,19 @@ func ParamKeyTable() paramtypes.KeyTable { } func NewParams( - mintDenom string, inflationRateChange, inflationMax, inflationMin, goalBonded sdk.Dec, blocksPerYear uint64, + mintDenom string, blocksPerYear uint64, ) Params { return Params{ - MintDenom: mintDenom, - InflationRateChange: inflationRateChange, - InflationMax: inflationMax, - InflationMin: inflationMin, - GoalBonded: goalBonded, - BlocksPerYear: blocksPerYear, + MintDenom: mintDenom, + BlocksPerYear: blocksPerYear, } } // default minting module parameters func DefaultParams() Params { return Params{ - MintDenom: sdk.DefaultBondDenom, - InflationRateChange: sdk.NewDecWithPrec(13, 2), - InflationMax: sdk.NewDecWithPrec(20, 2), - InflationMin: sdk.NewDecWithPrec(7, 2), - GoalBonded: sdk.NewDecWithPrec(67, 2), - BlocksPerYear: uint64(60 * 60 * 8766 / 5), // assuming 5 second block times + MintDenom: sdk.DefaultBondDenom, + BlocksPerYear: uint64(60 * 60 * 8766 / 5), // assuming 5 second block times } } @@ -56,27 +48,9 @@ func (p Params) Validate() error { if err := validateMintDenom(p.MintDenom); err != nil { return err } - if err := validateInflationRateChange(p.InflationRateChange); err != nil { - return err - } - if err := validateInflationMax(p.InflationMax); err != nil { - return err - } - if err := validateInflationMin(p.InflationMin); err != nil { - return err - } - if err := validateGoalBonded(p.GoalBonded); err != nil { - return err - } if err := validateBlocksPerYear(p.BlocksPerYear); err != nil { return err } - if p.InflationMax.LT(p.InflationMin) { - return fmt.Errorf( - "max inflation (%s) must be greater than or equal to min inflation (%s)", - p.InflationMax, p.InflationMin, - ) - } return nil } @@ -91,10 +65,6 @@ func (p Params) String() string { func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{ paramtypes.NewParamSetPair(KeyMintDenom, &p.MintDenom, validateMintDenom), - paramtypes.NewParamSetPair(KeyInflationRateChange, &p.InflationRateChange, validateInflationRateChange), - paramtypes.NewParamSetPair(KeyInflationMax, &p.InflationMax, validateInflationMax), - paramtypes.NewParamSetPair(KeyInflationMin, &p.InflationMin, validateInflationMin), - paramtypes.NewParamSetPair(KeyGoalBonded, &p.GoalBonded, validateGoalBonded), paramtypes.NewParamSetPair(KeyBlocksPerYear, &p.BlocksPerYear, validateBlocksPerYear), } }