Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Checked Inflation documentation #222

Merged
merged 3 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions pallet-checked-inflation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Checked Inflation Pallet

## Overview

The Checked Inflation Pallet is designed to facilitate the inflationary aspect of a blockchain's economy.
It automatically mints new tokens at the start of every era, with the amount determined by a configurable inflation method.
This functionality is crucial for maintaining a controlled expansion of the token supply, aligning with economic models or rewarding network participants.

### Key Features

- **Configurable Inflation**: The amount and method of inflation can be tailored to suit the blockchain's economic model.
- **Automatic Token Minting**: New tokens are minted automatically at the beginning of each era.
- **Yearly and Era-Based Inflation**: Supports fixed yearly, fixed per era, or rate-based inflation calculations.

## Functionality

The pallet's core functionality revolves around the `on_initialize` hook, which triggers at the beginning of each block.
If conditions align (start of a new era or year), the pallet calculates the amount to mint based on the configured inflation method and mints the tokens.

## Inflation Methods

Inflation can be configured in one of three ways, as defined in the `InflationMethod` enum:

- **Rate**: A percentage of the current supply.
- **FixedYearly**: A fixed amount distributed evenly across all eras in a year.
- **FixedPerEra**: A fixed amount minted at the start of each era.

The choice of method allows for flexibility in how the token supply expands over time, catering to different economic strategies.

## Dispatchable Functions

### `set_first_year_supply`

Configures the initial token supply at the year's start, preparing the system for accurate inflation calculation.

- **Access Control**: Root

### `halt_unhalt_pallet`

Toggles the inflation process, allowing it to be halted or resumed based on network needs.

- **Parameters**:
- `halt`: A boolean indicating whether to halt (`true`) or resume (`false`) the inflation process.
- **Access Control**: Root


## Events

- **NewYear**: Marks the beginning of a new year, resetting era counts and updating the starting issuance for inflation calculations.
- **NewEra**: Signifies the start of a new era, triggering token minting according to the configured inflation rate.
- **InflationMinted**: Indicates that tokens have been minted due to inflation, detailing the amounts involved.
- **OverInflationDetected**: Warns of excess token minting beyond expected amounts, prompting corrective measures.
- **HaltChanged**: Reports changes in the inflation process's halt status.

## Errors

- **NoHaltChange**: Triggered when attempting to change the halt status to its current value, indicating no action is needed.

## Conclusion

The Checked Inflation Pallet offers a flexible and automated way to manage token supply expansion through inflation.
By configuring the inflation method to match your blockchain's economic model, you can ensure a controlled and predictable increase in token supply,
this pallet is an essential tool for managing network growth and stability through controlled inflation in response to evolving economic conditions,
ensuring long-term sustainability.

15 changes: 15 additions & 0 deletions pallet-checked-inflation/src/inflation.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
//! Available inflation methods and resulting inflation amount generated per era.
//!
//! ## Overview
//!
//! This module contains the available inflation methods and the resulting inflation amount generated per era.

use crate::{BalanceOf, Config};
use codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_arithmetic::per_things::Perbill;

/// Inflation methods.
///
/// The inflation methods are used to determine the amount of inflation generated per era.
#[derive(TypeInfo, Encode, Decode)]
pub enum InflationMethod<Balance> {
/// The inflation is calculated as a percentage (`Perbill`) of the current supply.
Rate(Perbill),
/// The inflation is a fixed amount per year.
FixedYearly(Balance),
/// The inflation is a fixed amount per era.
FixedPerEra(Balance),
}

/// Getter trait for the inflation amount to be minted in each era.
pub trait GetInflation<T: Config> {
/// Returns the inflation amount to be minted per era.
fn get_inflation_args(&self, eras_per_year: u32, current_supply: BalanceOf<T>) -> BalanceOf<T>;
}

impl<T: Config> GetInflation<T> for InflationMethod<BalanceOf<T>>
where
u32: Into<BalanceOf<T>>,
{
/// Returns the inflation amount to be minted per era based on the inflation method.
fn get_inflation_args(&self, eras_per_year: u32, current_supply: BalanceOf<T>) -> BalanceOf<T> {
match self {
Self::Rate(rate) => (*rate * current_supply) / eras_per_year.into(),
Expand Down
50 changes: 46 additions & 4 deletions pallet-checked-inflation/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
//! # Checked Inflation Pallet
//!
//! - [`Config`]
//! - [`Call`]
//! - [`Pallet`]
//!
//! ## Overview
//! This is a supporting pallet that provides the functionality for inflation. It is used to mint new tokens at the beginning of every era.
//!
//! The amount of tokens minted is determined by the inflation method and its amount, and is configurable in the runtime,
//! see the [`inflation`] module for the methods of inflation available and how their inflation amounts are calculated.
//!
//! Most of the logic is implemented in the `on_initialize` hook, which is called at the beginning of every block.
//!
//! ## Dispatchable Functions
//!
//! - `set_first_year_supply` - For configuring the pallet, sets the token's `YearStartIssuance` to its current total issuance.
//! - `halt_unhalt_pallet` - To start or stop the inflation process.

#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::traits::Get;
Expand Down Expand Up @@ -34,9 +53,11 @@ pub mod pallet {
};
use num_traits::CheckedSub;

/// The balance type of this pallet.
pub(crate) type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;

/// The opaque token type for an imbalance. This is returned by unbalanced operations and must be dealt with.
type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
<T as frame_system::Config>::AccountId,
>>::NegativeImbalance;
Expand All @@ -46,23 +67,30 @@ pub mod pallet {

#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

/// The currency (token) used in this pallet.
type Currency: LockableCurrency<Self::AccountId, Moment = Self::BlockNumber>
+ ReservableCurrency<Self::AccountId>
+ Currency<Self::AccountId>;

/// Number of blocks per era.
#[pallet::constant]
type BlocksPerEra: Get<BlockNumberFor<Self>>;

/// Number of eras per year.
#[pallet::constant]
type ErasPerYear: Get<u32>;

/// The inflation method and its amount.
#[pallet::constant]
type Inflation: Get<InflationMethod<BalanceOf<Self>>>;

/// The `NegativeImbalanceOf` the currency, i.e. the amount of inflation to be applied.
type DealWithInflation: OnUnbalanced<NegativeImbalanceOf<Self>>;

/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
}

Expand All @@ -86,44 +114,48 @@ pub mod pallet {
#[pallet::getter(fn inflation_per_era)]
pub type YearlyInflationPerEra<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;

/// Wheter or not the inflation process should be halted.
/// Whether the inflation process is halted.
#[pallet::storage]
#[pallet::getter(fn is_halted)]
pub type Halted<T: Config> = StorageValue<_, bool, ValueQuery>;

#[pallet::error]
pub enum Error<T> {
/// The pallet is already in the state that the user is trying to change it to.
NoHaltChange,
}

#[pallet::event]
#[pallet::generate_deposit(fn deposit_event)]
pub enum Event<T: Config> {
/// Beginning of a new year.
NewYear {
starting_issuance: BalanceOf<T>,
next_era_starting_block: BlockNumberFor<T>,
},

/// Beginning of a new era.
NewEra {
era: u32,
next_era_starting_block: BlockNumberFor<T>,
},

/// Tokens minted due to inflation.
InflationMinted {
year_start_issuance: BalanceOf<T>,
current_issuance: BalanceOf<T>,
expected_new_issuance: BalanceOf<T>,
minted: BalanceOf<T>,
},

/// Total supply of the token is higher than expected by Checked Inflation.
OverInflationDetected {
expected_issuance: BalanceOf<T>,
current_issuance: BalanceOf<T>,
},

HaltChanged {
is_halted: bool,
},
/// Halt status changed.
HaltChanged { is_halted: bool },
}

#[pallet::hooks]
Expand Down Expand Up @@ -257,6 +289,9 @@ pub mod pallet {

#[pallet::call]
impl<T: Config> Pallet<T> {
/// This call is used for configuring the inflation mechanism and sets the token's `YearStartIssuance` to its current total issuance.
///
/// The origin has to have `root` access.
#[pallet::call_index(0)]
#[pallet::weight(
<T as Config>::WeightInfo::set_first_year_supply()
Expand All @@ -271,6 +306,11 @@ pub mod pallet {
Ok(())
}

/// Halts or unhalts the inflation process.
///
/// The origin has to have `root` access.
///
/// - `halt`: `true` to halt the inflation process, `false` to unhalt it.
#[pallet::call_index(1)]
#[pallet::weight(
<T as Config>::WeightInfo::halt_unhalt_pallet()
Expand All @@ -291,11 +331,13 @@ pub mod pallet {
}

impl<T: Config> Pallet<T> {
/// Internal function for minting tokens to the currency due to inflation.
fn mint(amount: BalanceOf<T>) {
let inflation = T::Currency::issue(amount);
<T as Config>::DealWithInflation::on_unbalanced(inflation);
}

/// Internal function to set the halt status to storage.
pub fn internal_halt_unhalt(halt: bool) {
Halted::<T>::put(halt);
}
Expand Down
Loading