Skip to main content
Version: Next

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, or hour
  • 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;
}