feat(x/mint)!: Replace InflationCalculationFn with MintFn + simple epoch minting (#20363)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Marko <marko@baricevic.me>
This commit is contained in:
Facundo Medica
2024-06-11 12:27:20 +00:00
committed by GitHub
co-authored by coderabbitai[bot] Marko
parent fbd3b75c5a
commit 150ca963f2
37 changed files with 949 additions and 269 deletions
+1 -1
View File
@@ -171,7 +171,7 @@ type SimApp struct {
ConsensusParamsKeeper consensusparamkeeper.Keeper
CircuitKeeper circuitkeeper.Keeper
PoolKeeper poolkeeper.Keeper
EpochsKeeper epochskeeper.Keeper
EpochsKeeper *epochskeeper.Keeper
// managers
ModuleManager *module.Manager
+4 -4
View File
@@ -93,7 +93,7 @@ type SimApp struct {
ConsensusParamsKeeper consensuskeeper.Keeper
CircuitBreakerKeeper circuitkeeper.Keeper
PoolKeeper poolkeeper.Keeper
EpochsKeeper epochskeeper.Keeper
EpochsKeeper *epochskeeper.Keeper
// simulation manager
sm *module.SimulationManager
@@ -111,7 +111,8 @@ func init() {
// AppConfig returns the default app config.
func AppConfig() depinject.Config {
return depinject.Configs(
appConfig, // Alternatively use appconfig.LoadYAML(AppConfigYAML)
appConfig, // Alternatively use appconfig.LoadYAML(AppConfigYAML)
depinject.Provide(ProvideExampleMintFn), // optional: override the mint module's mint function with epoched minting
)
}
@@ -174,8 +175,7 @@ func NewSimApp(
//
// For providing a custom inflation function for x/mint add here your
// custom function that implements the minttypes.InflationCalculationFn
// interface.
// custom function that implements the minttypes.MintFn interface.
),
)
)
+1 -1
View File
@@ -19,7 +19,7 @@ require (
cosmossdk.io/x/bank v0.0.0-20240226161501-23359a0b6d91
cosmossdk.io/x/circuit v0.0.0-20230613133644-0a778132a60f
cosmossdk.io/x/distribution v0.0.0-20240227221813-a248d05f70f4
cosmossdk.io/x/epochs v0.0.0-00010101000000-000000000000
cosmossdk.io/x/epochs v0.0.0-20240522060652-a1ae4c3e0337
cosmossdk.io/x/evidence v0.0.0-20230613133644-0a778132a60f
cosmossdk.io/x/feegrant v0.0.0-20230613133644-0a778132a60f
cosmossdk.io/x/gov v0.0.0-20231113122742-912390d5fc4a
+125
View File
@@ -0,0 +1,125 @@
package simapp
import (
"context"
"encoding/binary"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/core/event"
"cosmossdk.io/math"
authtypes "cosmossdk.io/x/auth/types"
banktypes "cosmossdk.io/x/bank/types"
minttypes "cosmossdk.io/x/mint/types"
stakingtypes "cosmossdk.io/x/staking/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
type MintBankKeeper interface {
MintCoins(ctx context.Context, moduleName string, coins sdk.Coins) error
SendCoinsFromModuleToModule(ctx context.Context, senderModule string, recipientModule string, amt sdk.Coins) error
}
// ProvideExampleMintFn returns the function used in x/mint's endblocker to mint new tokens.
// Note that this function can not have the mint keeper as a parameter because it would create a cyclic dependency.
func ProvideExampleMintFn(bankKeeper MintBankKeeper) minttypes.MintFn {
return func(ctx context.Context, env appmodule.Environment, minter *minttypes.Minter, epochID string, epochNumber int64) error {
// in this example we ignore epochNumber as we don't care what epoch we are in, we just assume we are being called every minute.
if epochID != "minute" {
return nil
}
var stakingParams stakingtypes.QueryParamsResponse
err := env.QueryRouterService.InvokeTyped(ctx, &stakingtypes.QueryParamsRequest{}, &stakingParams)
if err != nil {
return err
}
var bankSupply banktypes.QuerySupplyOfResponse
err = env.QueryRouterService.InvokeTyped(ctx, &banktypes.QuerySupplyOfRequest{Denom: stakingParams.Params.BondDenom}, &bankSupply)
if err != nil {
return err
}
stakingTokenSupply := bankSupply.Amount
var mintParams minttypes.QueryParamsResponse
err = env.QueryRouterService.InvokeTyped(ctx, &minttypes.QueryParamsRequest{}, &mintParams)
if err != nil {
return err
}
var stakingPool stakingtypes.QueryPoolResponse
err = env.QueryRouterService.InvokeTyped(ctx, &stakingtypes.QueryPoolRequest{}, &stakingPool)
if err != nil {
return err
}
// bondedRatio
bondedRatio := math.LegacyNewDecFromInt(stakingPool.Pool.BondedTokens).QuoInt(stakingTokenSupply.Amount)
minter.Inflation = minter.NextInflationRate(mintParams.Params, bondedRatio)
minter.AnnualProvisions = minter.NextAnnualProvisions(mintParams.Params, stakingTokenSupply.Amount)
// to get a more accurate amount of tokens minted, we get, and later store, last minting time.
// if this is the first time minting, we initialize the minter.Data with the current time - 60s
// to mint tokens at the beginning. Note: this is a custom behavior to avoid breaking tests.
if minter.Data == nil {
minter.Data = make([]byte, 8)
binary.BigEndian.PutUint64(minter.Data, (uint64)(env.HeaderService.HeaderInfo(ctx).Time.Unix()-60))
}
lastMint := binary.BigEndian.Uint64(minter.Data)
binary.BigEndian.PutUint64(minter.Data, (uint64)(env.HeaderService.HeaderInfo(ctx).Time.Unix()))
// calculate the amount of tokens to mint, based on the time since the last mint
secsSinceLastMint := env.HeaderService.HeaderInfo(ctx).Time.Unix() - (int64)(lastMint)
provisionAmt := minter.AnnualProvisions.QuoInt64(31536000).MulInt64(secsSinceLastMint) // 31536000 = seconds in a year
mintedCoin := sdk.NewCoin(mintParams.Params.MintDenom, provisionAmt.TruncateInt())
maxSupply := mintParams.Params.MaxSupply
totalSupply := stakingTokenSupply.Amount
if !maxSupply.IsZero() {
// supply is not infinite, check the amount to mint
remainingSupply := maxSupply.Sub(totalSupply)
if remainingSupply.LTE(math.ZeroInt()) {
// max supply reached, no new tokens will be minted
// also handles the case where totalSupply > maxSupply
return nil
}
// if the amount to mint is greater than the remaining supply, mint the remaining supply
if mintedCoin.Amount.GT(remainingSupply) {
mintedCoin.Amount = remainingSupply
}
}
if mintedCoin.Amount.IsZero() {
// skip as no coins need to be minted
return nil
}
mintedCoins := sdk.NewCoins(mintedCoin)
if err := bankKeeper.MintCoins(ctx, minttypes.ModuleName, mintedCoins); err != nil {
return err
}
// Example of custom send while minting
// Send some tokens to a "team account"
// if err = bankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, ... ); err != nil {
// return err
// }
if err = bankKeeper.SendCoinsFromModuleToModule(ctx, minttypes.ModuleName, authtypes.FeeCollectorName, mintedCoins); err != nil {
return err
}
return env.EventService.EventManager(ctx).EmitKV(
minttypes.EventTypeMint,
event.NewAttribute(minttypes.AttributeKeyBondedRatio, bondedRatio.String()),
event.NewAttribute(minttypes.AttributeKeyInflation, minter.Inflation.String()),
event.NewAttribute(minttypes.AttributeKeyAnnualProvisions, minter.AnnualProvisions.String()),
event.NewAttribute(sdk.AttributeKeyAmount, mintedCoin.Amount.String()),
)
}
}