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:
co-authored by
coderabbitai[bot]
Marko
parent
fbd3b75c5a
commit
150ca963f2
+1
-1
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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()),
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user