Skip to content

Commit

Permalink
feat(x/mint)!: Replace InflationCalculationFn with MintFn + simple ep…
Browse files Browse the repository at this point in the history
…och minting (#20363)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Marko <marko@baricevic.me>
  • Loading branch information
3 people committed Jun 11, 2024
1 parent fbd3b75 commit 150ca96
Show file tree
Hide file tree
Showing 37 changed files with 949 additions and 269 deletions.
196 changes: 138 additions & 58 deletions api/cosmos/mint/v1beta1/mint.pulsar.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions simapp/app_di.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
)
}

Expand Down Expand Up @@ -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.
),
)
)
Expand Down
2 changes: 1 addition & 1 deletion simapp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
125 changes: 125 additions & 0 deletions simapp/mint_fn.go
Original file line number Diff line number Diff line change
@@ -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()),
)
}
}
8 changes: 4 additions & 4 deletions tests/e2e/bank/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (s *E2ETestSuite) TestTotalSupplyGRPCHandler() {
&types.QueryTotalSupplyResponse{
Supply: sdk.NewCoins(
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), s.cfg.AccountTokens),
sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(3))),
sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(47))),
),
Pagination: &query.PageResponse{
Total: 2,
Expand All @@ -50,7 +50,7 @@ func (s *E2ETestSuite) TestTotalSupplyGRPCHandler() {
},
&types.QuerySupplyOfResponse{},
&types.QuerySupplyOfResponse{
Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(3))),
Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(47))),
},
},
{
Expand All @@ -61,7 +61,7 @@ func (s *E2ETestSuite) TestTotalSupplyGRPCHandler() {
},
&types.QuerySupplyOfResponse{},
&types.QuerySupplyOfResponse{
Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(6))),
Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(47))),
},
},
{
Expand All @@ -72,7 +72,7 @@ func (s *E2ETestSuite) TestTotalSupplyGRPCHandler() {
},
&types.QuerySupplyOfResponse{},
&types.QuerySupplyOfResponse{
Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(3))),
Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(47))),
},
},
{
Expand Down
6 changes: 3 additions & 3 deletions tests/e2e/distribution/grpc_query_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (s *GRPCQueryTestSuite) TestQueryOutstandingRewardsGRPC() {
val := s.network.GetValidators()[0]
baseURL := val.GetAPIAddress()

rewards, err := sdk.ParseDecCoins("5.88stake")
rewards, err := sdk.ParseDecCoins("46.06stake")
s.Require().NoError(err)

testCases := []struct {
Expand Down Expand Up @@ -170,7 +170,7 @@ func (s *GRPCQueryTestSuite) TestQueryValidatorCommissionGRPC() {
val := s.network.GetValidators()[0]
baseURL := val.GetAPIAddress()

commission, err := sdk.ParseDecCoins("2.94stake")
commission, err := sdk.ParseDecCoins("23.03stake")
s.Require().NoError(err)

testCases := []struct {
Expand Down Expand Up @@ -283,7 +283,7 @@ func (s *GRPCQueryTestSuite) TestQueryDelegatorRewardsGRPC() {
val := s.network.GetValidators()[0]
baseURL := val.GetAPIAddress()

rewards, err := sdk.ParseDecCoins("2.94stake")
rewards, err := sdk.ParseDecCoins("23.03stake")
s.Require().NoError(err)

testCases := []struct {
Expand Down
2 changes: 1 addition & 1 deletion tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ require (
cosmossdk.io/client/v2 v2.0.0-20230630094428-02b760776860 // indirect
cosmossdk.io/core/testing v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/x/circuit v0.0.0-20230613133644-0a778132a60f // indirect
cosmossdk.io/x/epochs v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/x/epochs v0.0.0-20240522060652-a1ae4c3e0337 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.2 // indirect
Expand Down
4 changes: 2 additions & 2 deletions x/epochs/depinject.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type ModuleInputs struct {
type ModuleOutputs struct {
depinject.Out

EpochKeeper keeper.Keeper
EpochKeeper *keeper.Keeper
Module appmodule.AppModule
}

Expand All @@ -49,7 +49,7 @@ func ProvideModule(in ModuleInputs) ModuleOutputs {
return ModuleOutputs{EpochKeeper: k, Module: m}
}

func InvokeSetHooks(keeper keeper.Keeper, hooks map[string]types.EpochHooksWrapper) error {
func InvokeSetHooks(keeper *keeper.Keeper, hooks map[string]types.EpochHooksWrapper) error {
if hooks == nil {
return nil
}
Expand Down
7 changes: 4 additions & 3 deletions x/epochs/keeper/epoch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ func (s *KeeperTestSuite) TestEpochLifeCycle() {

allEpochs, err := s.EpochsKeeper.AllEpochInfos(s.Ctx)
s.Require().NoError(err)
s.Require().Len(allEpochs, 4)
s.Require().Len(allEpochs, 5)
s.Require().Equal(allEpochs[0].Identifier, "day") // alphabetical order
s.Require().Equal(allEpochs[1].Identifier, "hour")
s.Require().Equal(allEpochs[2].Identifier, "monthly")
s.Require().Equal(allEpochs[3].Identifier, "week")
s.Require().Equal(allEpochs[2].Identifier, "minute")
s.Require().Equal(allEpochs[3].Identifier, "monthly")
s.Require().Equal(allEpochs[4].Identifier, "week")
}
2 changes: 1 addition & 1 deletion x/epochs/keeper/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func TestEpochsExportGenesis(t *testing.T) {

genesis, err := epochsKeeper.ExportGenesis(ctx)
require.NoError(t, err)
require.Len(t, genesis.Epochs, 3)
require.Len(t, genesis.Epochs, 4)

expectedEpochs := types.DefaultGenesis().Epochs
for i := 0; i < len(expectedEpochs); i++ {
Expand Down
2 changes: 1 addition & 1 deletion x/epochs/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func (s *KeeperTestSuite) TestQueryEpochInfos() {
// Check that querying epoch infos on default genesis returns the default genesis epoch infos
epochInfosResponse, err := queryClient.EpochInfos(s.Ctx, &types.QueryEpochsInfoRequest{})
s.Require().NoError(err)
s.Require().Len(epochInfosResponse.Epochs, 3)
s.Require().Len(epochInfosResponse.Epochs, 4)
expectedEpochs := types.DefaultGenesis().Epochs
for id := range expectedEpochs {
expectedEpochs[id].StartTime = s.Ctx.BlockTime()
Expand Down
6 changes: 3 additions & 3 deletions x/epochs/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type Keeper struct {
}

// NewKeeper returns a new keeper by codec and storeKey inputs.
func NewKeeper(env appmodule.Environment, cdc codec.BinaryCodec) Keeper {
func NewKeeper(env appmodule.Environment, cdc codec.BinaryCodec) *Keeper {
sb := collections.NewSchemaBuilder(env.KVStoreService)
k := Keeper{
Environment: env,
Expand All @@ -32,11 +32,11 @@ func NewKeeper(env appmodule.Environment, cdc codec.BinaryCodec) Keeper {
panic(err)
}
k.Schema = schema
return k
return &k
}

// Set the gamm hooks.
func (k Keeper) SetHooks(eh types.EpochHooks) Keeper {
func (k *Keeper) SetHooks(eh types.EpochHooks) *Keeper {
if k.hooks != nil {
panic("cannot set epochs hooks twice")
}
Expand Down
8 changes: 4 additions & 4 deletions x/epochs/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type KeeperTestSuite struct {
suite.Suite
Ctx sdk.Context
environment appmodule.Environment
EpochsKeeper epochskeeper.Keeper
EpochsKeeper *epochskeeper.Keeper
queryClient types.QueryClient
}

Expand All @@ -39,7 +39,7 @@ func (s *KeeperTestSuite) SetupTest() {
s.environment = environment
queryRouter := baseapp.NewGRPCQueryRouter()
cfg := module.NewConfigurator(nil, nil, queryRouter)
types.RegisterQueryServer(cfg.QueryServer(), epochskeeper.NewQuerier(s.EpochsKeeper))
types.RegisterQueryServer(cfg.QueryServer(), epochskeeper.NewQuerier(*s.EpochsKeeper))
grpcQueryService := &baseapp.QueryServiceTestHelper{
GRPCQueryRouter: queryRouter,
Ctx: s.Ctx,
Expand All @@ -49,7 +49,7 @@ func (s *KeeperTestSuite) SetupTest() {
s.queryClient = types.NewQueryClient(grpcQueryService)
}

func Setup(t *testing.T) (sdk.Context, epochskeeper.Keeper, appmodule.Environment) {
func Setup(t *testing.T) (sdk.Context, *epochskeeper.Keeper, appmodule.Environment) {
t.Helper()

key := storetypes.NewKVStoreKey(types.StoreKey)
Expand All @@ -67,7 +67,7 @@ func Setup(t *testing.T) (sdk.Context, epochskeeper.Keeper, appmodule.Environmen
ctx.WithHeaderInfo(header.Info{Height: 1, Time: time.Now().UTC(), ChainID: "epochs"})
err := epochsKeeper.InitGenesis(ctx, *types.DefaultGenesis())
require.NoError(t, err)
SetEpochStartTime(ctx, epochsKeeper)
SetEpochStartTime(ctx, *epochsKeeper)

return ctx, epochsKeeper, environment
}
Expand Down
6 changes: 3 additions & 3 deletions x/epochs/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ const ConsensusVersion = 1
// AppModule implements the AppModule interface for the epochs module.
type AppModule struct {
cdc codec.Codec
keeper keeper.Keeper
keeper *keeper.Keeper
}

// NewAppModule creates a new AppModule object.
func NewAppModule(cdc codec.Codec, keeper keeper.Keeper) AppModule {
func NewAppModule(cdc codec.Codec, keeper *keeper.Keeper) AppModule {
return AppModule{
cdc: cdc,
keeper: keeper,
Expand All @@ -67,7 +67,7 @@ func (AppModule) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *gwrunt

// RegisterServices registers module services.
func (am AppModule) RegisterServices(registrar grpc.ServiceRegistrar) error {
types.RegisterQueryServer(registrar, keeper.NewQuerier(am.keeper))
types.RegisterQueryServer(registrar, keeper.NewQuerier(*am.keeper))
return nil
}

Expand Down
1 change: 1 addition & 0 deletions x/epochs/types/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func DefaultGenesis() *GenesisState {
epochs := []EpochInfo{
NewGenesisEpochInfo("day", time.Hour*24), // alphabetical order
NewGenesisEpochInfo("hour", time.Hour),
NewGenesisEpochInfo("minute", time.Minute),
NewGenesisEpochInfo("week", time.Hour*24*7),
}
return NewGenesisState(epochs)
Expand Down
1 change: 1 addition & 0 deletions x/group/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ require (
cosmossdk.io/collections v0.4.0 // indirect
cosmossdk.io/core/testing v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/x/accounts/defaults/lockup v0.0.0-20240417181816-5e7aae0db1f5 // indirect
cosmossdk.io/x/epochs v0.0.0-20240522060652-a1ae4c3e0337 // indirect
cosmossdk.io/x/tx v0.13.3 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
Expand Down
Loading

0 comments on commit 150ca96

Please sign in to comment.