Mint Module
Overview
The Mint module is responsible for the flexible creation of tokens. These tokens serve various purposes such as rewarding validators, incentivizing liquidity provision in pools, motivating DApp developers on Pryzm, financing feeders who contribute to the oracle process, rewarding developers for maintaining and enhancing Pryzm, and funding community and treasury fee pools.
Concepts
The Mint module facilitates the periodic creation of new tokens within a chain. The approach adopted by Pryzm includes:
- Minting new tokens once per epoch (default is one week), using time-based epochs supported by the epochs module.
- Allowing for a flexible inflation rate, determined by market demand, targeting a specific bonded-stake ratio.
- Achieving a balance between market liquidity and staked supply.
The Mint module uses a moving change rate mechanism to determine the suitable market rate for inflation rewards. This mechanism ensures that if the percentage of bonded tokens is either above or below the target percentage, the inflation rate will adjust to either encourage or discourage bonding.
Setting the target percentage of bonded tokens at less than 100% prompts the network to maintain some non-staked tokens, aiding liquidity. The mechanism operates in three parts:
- If the inflation rate is below the target percentage, the rate will increase until it reaches a maximum value.
- If the target percentage (default is 67%) is maintained, the inflation rate will remain constant.
- If the inflation rate is above the target percentage, the rate will decrease until it reaches a minimum value.
Transitions
AfterEpochEnd
This function recalculates the minting parameters and disburses inflation at the end of each epoch, signaled by the epochs module. The function is represented below in pseudocode:
func (k Keeper) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epochNumber int64) error {
params := k.GetParams(ctx)
if epochIdentifier != params.EpochIdentifier {
return nil
}
// not distribute rewards if it's not time yet for rewards distribution
if epochNumber < params.MintingRewardsDistributionStartEpoch {
return nil
}
epochsPerYear := int64(yearSeconds / k.epochsKeeper.GetEpochInfo(ctx, epochIdentifier).Duration.Seconds())
bondedRatio := k.stakingKeeper.BondedRatio(ctx)
// recalculate minter properties
minter, err := k.updateMinter(ctx, params, bondedRatio, epochsPerYear)
if err != nil {
panic(err)
}
// mint coins
mintedCoin, err := k.mintCoin(ctx, minter, params.MintDenom, epochsPerYear)
if err != nil {
panic(err)
}
// distribute minted coins
distributedAmounts, err := k.distributeMintedCoin(ctx, *mintedCoin)
if err != nil {
panic(err)
}
return nil
}
Functions
InflationCalculationFn
The inflation rate is calculated using an "inflation calculation function" that is passed to the NewAppModule
function. If no function is provided, the default inflation function (NextInflationRate
) is used. For custom inflation
calculation logic, define and pass a function that matches the InflationCalculationFn
signature.
type InflationCalculationFn func(ctx sdk.Context, minter Minter, params Params, bondedRatio sdk.Dec, epochsPerYear int64) sdk.Dec
NextInflationRate
The target annual inflation rate is recalculated every epoch. It can increase or decrease based on its deviation from the target ratio of 67%. The maximum allowable rate change is 13% per year, yet the annual inflation rate is confined between 7% and 20%.
func (m Minter) NextInflationRate(params Params, bondedRatio sdk.Dec, epochsPerYear int64) sdk.Dec {
// (1 - bondedRatio/GoalBonded) * InflationRateChange
inflationRateChangePerYear := sdk.OneDec().
Sub(bondedRatio.Quo(params.GoalBonded)).
Mul(params.InflationRateChange)
inflationRateChange := inflationRateChangePerYear.Quo(sdk.NewDec(epochsPerYear))
// 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
}
NextAnnualProvisions
The annual provisions are calculated once per epoch using the current total supply and inflation rate.
func (m Minter) NextAnnualProvisions(totalSupply math.Int) sdk.Dec {
return m.Inflation.MulInt(totalSupply)
}
EpochProvision
This function calculates the provisions for each epoch using the current annual provisions. The mint
module's ModuleMinterAccount
then mints the provisions and distributes them to specified accounts with
the distributeMintedCoin
function.
func (m Minter) EpochProvisions(mintDenom string, epochsPerYear int64) sdk.Coin {
provisionAmt := m.AnnualProvisions.QuoInt(sdk.NewInt(epochsPerYear))
return sdk.NewCoin(mintDenom, provisionAmt.TruncateInt())
}
updateMinter
This function calculates the inflation and annual provisions, and then updates the minter state.
func (k Keeper) updateMinter(ctx sdk.Context, params types.Params, bondedRatio sdk.Dec, epochsPerYear int64) (*types.Minter, error) {
totalStakingSupply := k.stakingKeeper.StakingTokenSupply(ctx)
minter := k.GetMinter(ctx)
minter.Inflation = k.inflationCalculator(ctx, minter, params, bondedRatio, epochsPerYear)
minter.AnnualProvisions = minter.NextAnnualProvisions(totalStakingSupply)
err := k.setMinter(ctx, minter)
if err != nil {
return nil, err
}
return &minter, nil
}
distributeMintedCoin
This function computes the amount to transfer to each account based on the DistributionProportions
parameters.
Registered hooks are invoked after each transfer.
func (k Keeper) distributeMintedCoin(ctx sdk.Context, mintedCoin sdk.Coin) (_ *types.DistributionProportions, err error)
Hooks
The module supports numerous hooks that notify relevant modules about the distributed quantities of minted coins:
type MintHooks interface {
AfterDistributeMintedCoin(ctx sdk.Context, amount sdk.Coin)
OnStakingPortionDistributed(ctx sdk.Context, amount sdk.Coin)
OnPoolIncentivesPortionDistributed(ctx sdk.Context, amount sdk.Coin)
OnOperationsAndDevelopmentPortionDistributed(ctx sdk.Context, amount sdk.Coin)
OnDappPortionDistributed(ctx sdk.Context, amount sdk.Coin)
OnOraclePortionDistributed(ctx sdk.Context, amount sdk.Coin)
OnTreasuryPortionDistributed(ctx sdk.Context, amount sdk.Coin)
OnCommunityPoolPortionDistributed(ctx sdk.Context, amount sdk.Coin)
}
Modules keen on these notifications should implement the above interface and register the implementation in app.go
:
app.MintKeeper.SetHooks(
mintmoduletypes.NewMultiMintHooks(&MintHooksImpl{}),
)
Message
MsgUpdateParams
MsgUpdateParams
is a governance operation for updating the module's parameters.
message MsgUpdateParams {
string authority = 1;
Params params = 2;
}
The structure and definitions of the module parameters are detailed in the Parameters section.
MsgDappAccountSpend
MsgDappAccountSpend
allows the transfer of tokens from the DApp account in the mint module to another account. It
includes fields for the authority
, title
and description
of the message, the recipient
account's address, and
the amount
of tokens to be transferred.
message MsgDappAccountSpend {
string authority = 1;
string title = 2;
string description = 3;
string recipient = 4;
repeated cosmos.base.v1beta1.Coin amount = 5 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}
Query
Params
A query to retrieve the current module parameters.
message QueryParamsRequest {}
message QueryParamsResponse {
Params params = 1 [(gogoproto.nullable) = false];
}
The CLI command is also supported:
pryzmd query mint params
Minter
A query to fetch the minter's current state.
message QueryMinterRequest {}
message QueryMinterResponse {
Minter minter = 1 [(gogoproto.nullable) = false];
}
The CLI command is also supported:
pryzmd query mint minter
Events
The EventMint
is triggered when a mint epoch has successfully completed. It indicates that total_minted
tokens of
type MintDenom
have been minted and distributed at the end of epoch number epoch_number
, with the distributed
amounts detailed in distributed_amounts
.
message EventMint {
Minter minter = 1;
string bonded_ratio = 2;
string total_minted = 3;
DistributionProportions distributed_amounts = 4;
int64 epoch_number = 5;
}
message DistributionProportions {
string staking = 1;
string pool_incentives = 2;
string operations_and_development = 3;
string dapp = 4;
string oracle = 5;
string treasury = 6;
string community = 7;
}
State
Parameters
Parameters are stored with this store key:
(
[]byte("v1/params/")
) -> ProtoBuf(Params)
Minter
The Minter
data structure stores the current inflation information.
message Minter {
string inflation = 1; // sdk.Dec
// current annual expected provisions
string annual_provisions = 2; // sdk.Dec
}
The minter is stored with a key:
[]byte("v1/minter/")
Parameters
type Params struct {
MintDenom string
InflationRateChange sdk.Dec
InflationMax sdk.Dec
InflationMin sdk.Dec
GoalBonded sdk.Dec
EpochIdentifier string
MintingRewardsDistributionStartEpoch int64
DistributionProportions DistributionProportions
GenesisEpochProvisions sdk.Dec
OperationsAndDevelopmentAccountAddress string
}
type DistributionProportions struct {
Staking sdk.Dec
PoolIncentives sdk.Dec
OperationsAndDevelopment sdk.Dec
Dapp sdk.Dec
Oracle sdk.Dec
Treasury sdk.Dec
Community sdk.Dec
}
MintDenom
Defines the type of coin to mint.
- type:
string
- default:
upryzm
InflationRateChange
Sets the maximum annual change in the inflation rate.
- type:
sdk.Dec
- range: [0, 1)
- default:
0.13
InflationMax
Sets the maximum inflation rate.
- type:
sdk.Dec
- range: [0, 1)
- default:
0.20
InflationMin
Sets the minimum inflation rate.
- type:
sdk.Dec
- range: [0, 1)
- default:
0.07
GoalBonded
Sets the goal for the percentage of bonded upryzms.
- type:
sdk.Dec
- range: (0, 1)
- default:
0.67
EpochIdentifier
Sets the mint epoch identifier.
- type:
string
- range:
week
,day
, orhour
- default:
week
MintingRewardsDistributionStartEpoch
Sets the start epoch for distributing minting rewards.
- type:
int64
- range: [0, inf)
- default:
0
GenesisEpochProvisions
Sets the epoch provisions from the first epoch.
- type:
sdk.Dec
- range: [0, inf)
- default:
5000000
OperationsAndDevelopmentAccountAddress
Identifies the account address that receives the minted operations and development rewards.
- type:
string
- default: empty
DistributionProportions
Defines the distribution ratios of the minted denom, determining the recipients of the minted denoms and their proportions.
Staking
Sets the proportion of the minted MintDenom
allocated as staking rewards.
- type:
sdk.Dec
- range: [0, inf)
- default:
0
PoolIncentives
This denotes the fraction of minted MintDenom
allocated as pool incentives.
- Type:
sdk.Dec
- Range: [0, inf)
- Default value:
0
OperationsAndDevelopment
This denotes the fraction of minted MintDenom
allocated to the operations and development account address.
- Type:
sdk.Dec
- Range: [0, inf)
- Default value:
0
Dapp
This denotes the fraction of minted MintDenom
allocated to DApp developers.
- Type:
sdk.Dec
- Range: [0, inf)
- Default value:
0
Oracle
This denotes the fraction of minted MintDenom
allocated to oracle feeders.
- Type:
sdk.Dec
- Range: [0, inf)
- Default value:
0
Treasury
This denotes the fraction of minted MintDenom
allocated to treasury.
- Type:
sdk.Dec
- Range: [0, inf)
- Default value:
0.5
Community
This denotes the fraction of minted MintDenom
allocated to community pool.
- Type:
sdk.Dec
- Range: [0, inf)
- Default value:
0.5
Genesis
The genesis state for the mint module may contain values for the Parameters of the module and the Minter. The genesis state's full structure is as follows:
message GenesisState {
Params params = 1;
Minter minter = 2;
}