diff --git a/OCIF/README.md b/OCIF/README.md deleted file mode 100644 index bee15c0f..00000000 --- a/OCIF/README.md +++ /dev/null @@ -1,3 +0,0 @@ -[![Compatible with Substrate v3.0.0](https://img.shields.io/badge/Substrate-v3.0.0-E6007A)](https://github.com/paritytech/substrate/releases/tag/v3.0.0) - -# On-Chain Innovation Funding (OCIF) Substrate FRAME Pallets diff --git a/OCIF/staking/README.md b/OCIF/staking/README.md index 59f6cb12..6b214267 100644 --- a/OCIF/staking/README.md +++ b/OCIF/staking/README.md @@ -1,55 +1,47 @@ -# pallet-ocif-staking +# OCIF Staking Pallet -## OCIF Staking pallet -A pallet for for allowing INV-Cores to be staked towards. +## Overview +The OCIF Staking Pallet is a pallet designed to facilitate staking towards INV-Cores within a blockchain network. This pallet introduces a staking mechanism that allows two distinct sets of entities, namely Cores and Stakers, to participate in the distribution of tokens from a predefined pot. The allocation of rewards is determined based on the amount staked by each entity and the total stake towards each Core. -### Overview +### Cores -This pallet provides functionality to allow 2 sets of entities to participate in distribution of tokens -available in a predefined pot account. -The tokens provided to the pot account are to be handled by the Runtime, -either directly or with the assistance of another pallet. +Cores represent virtual accounts identified by unique IDs, which are responsible for registering themselves within the staking ecosystem. The primary role of Cores is to attract Stakers to lock tokens in their favor. The rewards allocated to Cores are proportional to the total amount staked towards them by Stakers. However, for a Core to be eligible for rewards, it must have a total stake above a predefined threshold, thereby becoming `active`. -The 2 entity sets will be referred to in code as Cores and Stakers: +### Stakers -#### Cores -Cores are virtual accounts that have an ID used to derive their own account address, -their task in the process is to register themselves and have Stakers lock tokens in favor of a specifc Core. -Cores receive their fraction of the pot rewards based on the total amount staked towards them by Stakers, -however, a Core must have total stake above the defined threshold (making it `active`), otherwise they won't be entitled to rewards. +Stakers are individual accounts that engage in locking tokens in favor of a Core. Unlike Cores, Stakers receive a fraction of the rewards based on their own stake. -#### Stakers -Stakers are any account existing on the chain, their task is to lock tokens in favor of a Core. -Unlike Cores, Stakers get their fraction of the rewards based on their own stake and regardless of -the `active` state of the Core they staked towards. +## Runtime Configuration Parameters +- `BlocksPerEra`: Defines the duration of an era in terms of block numbers. +- `RegisterDeposit`: Specifies the deposit amount required for Core registration. +- `MaxStakersPerCore`: Limits the maximum number of Stakers that can simultaneously stake towards a single Core. +- `MinimumStakingAmount`: Sets the minimum amount required for a Staker to participate in staking. +- `UnbondingPeriod`: Determines the period, in eras, required for unbonding staked tokens. +- `RewardRatio`: Establishes the distribution ratio of rewards between Cores and Stakers. +- `StakeThresholdForActiveCore`: Sets the stake threshold required for a Core to become `active`. -### Relevant runtime configs +## Dispatchable Functions -* `BlocksPerEra` - Defines how many blocks constitute an era. -* `RegisterDeposit` - Defines the deposit amount for a Core to register in the system. -* `MaxStakersPerCore` - Defines the maximum amount of Stakers allowed to stake simultaneously towards the same Core. -* `MinimumStakingAmount` - Defines the minimum amount a Staker has to stake to participate. -* `UnbondingPeriod` - Defines the period, in blocks, that it takes to unbond a stake. -* `RewardRatio` - Defines the ratio of balance from the pot to distribute to Cores and Stakers, respectively. -* `StakeThresholdForActiveCore` - Defines the threshold of stake a Core needs to surpass to become active. +- `register_core`: Allows Cores to register themselves in the system. +- `unregister_core`: Enables Cores to unregister from the system, initiating the unbonding period for Stakers. +- `change_core_metadata`: Changes the metadata associated to a Core. +- `stake`: Allows Stakers to lock tokens in favor of a Core. +- `unstake`: Unstakes tokens previously staked to a Core, starting the unbonding period. +- `withdraw_unstaked`: Allows Stakers to withdraw tokens that have completed the unbonding period. +- `staker_claim_rewards`: Allows Stakers to claim available rewards. +- `core_claim_rewards`: Allows rewards to be claimed for Cores. +- `halt_unhalt_pallet`: Allows Root to trigger a halt of the system, eras will stop counting and rewards won't be distributed. -**Example Runtime implementation can be found in [src/testing/mock.rs](./src/testing/mock.rs)** +## Events -### Dispatchable Functions +The pallet emits events such as `Staked`, `Unstaked`, `CoreRegistered`, `CoreUnregistered`, and others to signal various operations and state changes within the staking ecosystem. -* `register_core` - Registers a Core in the system. -* `unregister_core` - Unregisters a Core from the system, starting the unbonding period for the Stakers. -* `change_core_metadata` - Changes the metadata tied to a Core. -* `stake` - Stakes tokens towards a Core. -* `untake` - Unstakes tokens from a core and starts the unbonding period for those tokens. -* `withdraw_unstaked` - Withdraws tokens that have already been through the unbonding period. -* `staker_claim_rewards` - Claims rewards available for a Staker. -* `core_claim_rewards` - Claims rewards available for a Core. -* `halt_unhalt_pallet` - Allows Root to trigger a halt of the system, eras will stop counting and rewards won't be distributed. +## Errors -[`Call`]: ./enum.Call.html -[`Config`]: ./trait.Config.html +Errors such as `StakingNothing`, `InsufficientBalance`, `MaxStakersReached`, and others are defined to handle exceptional scenarios encountered during pallet operations. -License: GPLv3 +## Example Runtime Implementation + +For an example runtime implementation that integrates this pallet, refer to [src/testing/mock.rs](./src/testing/mock.rs). diff --git a/OCIF/staking/src/lib.rs b/OCIF/staking/src/lib.rs index 4d72590b..1d471c9c 100644 --- a/OCIF/staking/src/lib.rs +++ b/OCIF/staking/src/lib.rs @@ -1,6 +1,5 @@ //! # OCIF Staking pallet -//! A pallet for for allowing INV-Cores to be staked towards. -//! +//! A pallet for allowing INV-Cores to be staked towards. //! //! ## Overview //! @@ -22,14 +21,13 @@ //! Unlike Cores, Stakers get their fraction of the rewards based on their own stake and regardless of //! the `active` state of the Core they staked towards. //! -//! //! ## Relevant runtime configs //! //! * `BlocksPerEra` - Defines how many blocks constitute an era. //! * `RegisterDeposit` - Defines the deposit amount for a Core to register in the system. -//! * `MaxStakersPerCore` - Defines the maximum amount of Stakers allowed to stake simultaneously towards the same Core. +//! * `MaxStakersPerCore` - Defines the maximum amount of Stakers allowed staking simultaneously towards the same Core. //! * `MinimumStakingAmount` - Defines the minimum amount a Staker has to stake to participate. -//! * `UnbondingPeriod` - Defines the period, in blocks, that it takes to unbond a stake. +//! * `UnbondingPeriod` - Defines the period, in eras, that it takes to unbond a stake. //! * `RewardRatio` - Defines the ratio of balance from the pot to distribute to Cores and Stakers, respectively. //! * `StakeThresholdForActiveCore` - Defines the threshold of stake a Core needs to surpass to become active. //! @@ -41,7 +39,7 @@ //! * `unregister_core` - Unregisters a Core from the system, starting the unbonding period for the Stakers. //! * `change_core_metadata` - Changes the metadata tied to a Core. //! * `stake` - Stakes tokens towards a Core. -//! * `untake` - Unstakes tokens from a core and starts the unbonding period for those tokens. +//! * `unstake` - Unstakes tokens from a core and starts the unbonding period for those tokens. //! * `withdraw_unstaked` - Withdraws tokens that have already been through the unbonding period. //! * `staker_claim_rewards` - Claims rewards available for a Staker. //! * `core_claim_rewards` - Claims rewards available for a Core. @@ -85,6 +83,7 @@ pub mod weights; pub use weights::WeightInfo; +/// Staking lock identifier. const LOCK_ID: LockIdentifier = *b"ocif-stk"; pub use pallet::*; @@ -98,30 +97,37 @@ pub mod pallet { use super::*; + /// The balance type of this pallet. pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; #[pallet::pallet] pub struct Pallet(PhantomData); + /// The opaque token type for an imbalance. This is returned by unbalanced operations and must be dealt with. type NegativeImbalanceOf = <::Currency as Currency< ::AccountId, >>::NegativeImbalance; + /// The core metadata type of this pallet. pub type CoreMetadataOf = CoreMetadata< BoundedVec::MaxNameLength>, BoundedVec::MaxDescriptionLength>, BoundedVec::MaxImageUrlLength>, >; + /// The core information type, containing a core's AccountId and CoreMetadataOf. pub type CoreInfoOf = CoreInfo<::AccountId, CoreMetadataOf>; + /// Alias type for the era identifier type. pub type Era = u32; #[pallet::config] pub trait Config: frame_system::Config + pallet_inv4::Config { + /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The currency used in staking. type Currency: LockableCurrency + ReservableCurrency; @@ -135,77 +141,108 @@ pub mod pallet { // + Clone // + From<::CoreId>; + /// Number of blocks per era. #[pallet::constant] type BlocksPerEra: Get>; + /// Deposit amount that will be reserved as part of new core registration. #[pallet::constant] type RegisterDeposit: Get>; + /// Maximum number of unique stakers per core. #[pallet::constant] type MaxStakersPerCore: Get; + /// Minimum amount user must have staked on a core. + /// User can stake less if they already have the minimum staking amount staked. #[pallet::constant] type MinimumStakingAmount: Get>; + /// Account Identifier from which the internal Pot is generated. #[pallet::constant] type PotId: Get; + /// The minimum amount required to keep an account open. #[pallet::constant] type ExistentialDeposit: Get>; + /// Max number of unlocking chunks per account Id <-> core Id pairing. + /// If value is zero, unlocking becomes impossible. #[pallet::constant] type MaxUnlocking: Get; + /// Number of eras that need to pass until unstaked value can be withdrawn. + /// When set to `0`, it's equal to having no unbonding period. #[pallet::constant] type UnbondingPeriod: Get; + /// Max number of unique `EraStake` values that can exist for a `(staker, core)` pairing. + /// + /// When stakers claims rewards, they will either keep the number of `EraStake` values the same or they will reduce them by one. + /// Stakers cannot add an additional `EraStake` value by calling `bond&stake` or `unbond&unstake` if they've reached the max number of values. + /// + /// This ensures that history doesn't grow indefinitely - if there are too many chunks, stakers should first claim their former rewards + /// before adding additional `EraStake` values. #[pallet::constant] type MaxEraStakeValues: Get; + /// Reward ratio of the pot to be distributed between the core and stakers, respectively. #[pallet::constant] type RewardRatio: Get<(u32, u32)>; + /// Threshold of staked tokens necessary for a core to become active. #[pallet::constant] type StakeThresholdForActiveCore: Get>; + /// Maximum length of a core's name. #[pallet::constant] type MaxNameLength: Get; + /// Maximum length of a core's description. #[pallet::constant] type MaxDescriptionLength: Get; + /// Maximum length of a core's image URL. #[pallet::constant] type MaxImageUrlLength: Get; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } + /// General information about the staker. #[pallet::storage] #[pallet::getter(fn ledger)] pub type Ledger = StorageMap<_, Blake2_128Concat, T::AccountId, AccountLedger>, ValueQuery>; + /// The current era index. #[pallet::storage] #[pallet::getter(fn current_era)] pub type CurrentEra = StorageValue<_, Era, ValueQuery>; + /// Accumulator for block rewards during an era. It is reset at every new era. #[pallet::storage] #[pallet::getter(fn reward_accumulator)] pub type RewardAccumulator = StorageValue<_, RewardInfo>, ValueQuery>; + /// Stores the block number of when the next era starts. #[pallet::storage] #[pallet::getter(fn next_era_starting_block)] pub type NextEraStartingBlock = StorageValue<_, T::BlockNumber, ValueQuery>; + /// Simple map where CoreId points to the respective core information. #[pallet::storage] #[pallet::getter(fn core_info)] pub(crate) type RegisteredCore = StorageMap<_, Blake2_128Concat, T::CoreId, CoreInfoOf>; + /// General information about an era. #[pallet::storage] #[pallet::getter(fn general_era_info)] pub type GeneralEraInfo = StorageMap<_, Twox64Concat, Era, EraInfo>>; + /// Staking information about a core in a particular era. #[pallet::storage] #[pallet::getter(fn core_stake_info)] pub type CoreEraStake = StorageDoubleMap< @@ -217,6 +254,7 @@ pub mod pallet { CoreStakeInfo>, >; + /// Info about staker's stakes on a particular core. #[pallet::storage] #[pallet::getter(fn staker_info)] pub type GeneralStakerInfo = StorageDoubleMap< @@ -229,6 +267,7 @@ pub mod pallet { ValueQuery, >; + /// Denotes whether the pallet is halted (disabled). #[pallet::storage] #[pallet::getter(fn is_halted)] pub type Halted = StorageValue<_, bool, ValueQuery>; @@ -236,49 +275,62 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event { + /// Account has staked funds to a core. Staked { staker: T::AccountId, core: T::CoreId, amount: BalanceOf, }, + + /// Account has unstaked funds from a core. Unstaked { staker: T::AccountId, core: T::CoreId, amount: BalanceOf, }, + + /// Account has withdrawn unbonded funds. Withdrawn { staker: T::AccountId, amount: BalanceOf, }, - CoreRegistered { - core: T::CoreId, - }, - CoreUnregistered { - core: T::CoreId, - }, - NewEra { - era: u32, - }, + + /// New core registered for staking. + CoreRegistered { core: T::CoreId }, + + /// Core unregistered. + CoreUnregistered { core: T::CoreId }, + + /// Beginning of a new era. + NewEra { era: u32 }, + + /// Staker claimed rewards. StakerClaimed { staker: T::AccountId, core: T::CoreId, era: u32, amount: BalanceOf, }, + + /// Rewards claimed for core. CoreClaimed { core: T::CoreId, destination_account: T::AccountId, era: u32, amount: BalanceOf, }, - HaltChanged { - is_halted: bool, - }, + + /// Halt status changed. + HaltChanged { is_halted: bool }, + + /// Core metadata changed. MetadataChanged { core: T::CoreId, old_metadata: CoreMetadata, Vec, Vec>, new_metadata: CoreMetadata, Vec, Vec>, }, + + /// Staker moved an amount of stake to another core. StakeMoved { staker: T::AccountId, from_core: T::CoreId, @@ -289,36 +341,62 @@ pub mod pallet { #[pallet::error] pub enum Error { + /// Staking nothing. StakingNothing, + /// Attempted to stake less than the minimum amount. InsufficientBalance, + /// Maximum number of stakers reached. MaxStakersReached, + /// Core not found. CoreNotFound, + /// No stake available for withdrawal. NoStakeAvailable, + /// Core is not unregistered. NotUnregisteredCore, + /// Unclaimed rewards available. UnclaimedRewardsAvailable, + /// Unstaking nothing. UnstakingNothing, + /// Nothing available for withdrawal. NothingToWithdraw, + /// Core already registered. CoreAlreadyRegistered, + /// Unknown rewards for era. UnknownEraReward, + /// Unexpected stake info for era. UnexpectedStakeInfoEra, + /// Too many unlocking chunks. TooManyUnlockingChunks, + /// Reward already claimed. RewardAlreadyClaimed, + /// Incorrect era. IncorrectEra, + /// Too many era stake values. TooManyEraStakeValues, + /// Not a staker. NotAStaker, + /// No permission. NoPermission, + /// Name exceeds maximum length. MaxNameExceeded, + /// Description exceeds maximum length. MaxDescriptionExceeded, + /// Image URL exceeds maximum length. MaxImageExceeded, + /// Core not registered. NotRegistered, + /// Halted. Halted, + /// No halt change. NoHaltChange, + /// Attempted to move stake to the same core. MoveStakeToSameCore, } #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(now: BlockNumberFor) -> Weight { + // If the pallet is halted we don't process a new era. if Self::is_halted() { return T::DbWeight::get().reads(1); } @@ -326,6 +404,7 @@ pub mod pallet { let previous_era = Self::current_era(); let next_era_starting_block = Self::next_era_starting_block(); + // Process a new era if past the start block of next era or if this is the first ever era. if now >= next_era_starting_block || previous_era.is_zero() { let blocks_per_era = T::BlocksPerEra::get(); let next_era = previous_era + 1; @@ -353,6 +432,15 @@ pub mod pallet { From<::RuntimeOrigin>, T::AccountId: From<[u8; 32]>, { + /// Used to register core for staking. + /// + /// The origin has to be the core origin. + /// + /// As part of this call, `RegisterDeposit` will be reserved from the core account. + /// + /// - `name`: Name of the core. + /// - `description`: Description of the core. + /// - `image`: Image URL of the core. #[pallet::call_index(0)] #[pallet::weight( ::WeightInfo::register_core( @@ -402,6 +490,14 @@ pub mod pallet { }) } + /// Unregister existing core for staking, making it ineligible for rewards from current era onwards and + /// starts the unbonding period for the stakers. + /// + /// The origin has to be the core origin. + /// + /// Deposit is returned to the core account. + /// + /// - `core_id`: Id of the core to be unregistered. #[pallet::call_index(1)] #[pallet::weight( ::WeightInfo::unregister_core() + @@ -482,6 +578,13 @@ pub mod pallet { ) } + /// Used to change the metadata of a core. + /// + /// The origin has to be the core origin. + /// + /// - `name`: Name of the core. + /// - `description`: Description of the core. + /// - `image`: Image URL of the core. #[pallet::call_index(2)] #[pallet::weight( ::WeightInfo::change_core_metadata( @@ -534,6 +637,15 @@ pub mod pallet { }) } + /// Lock up and stake balance of the origin account. + /// + /// `value` must be more than the `minimum_stake` specified by `MinimumStakingAmount` + /// unless account already has bonded value equal or more than 'minimum_stake'. + /// + /// The dispatch origin for this call must be _Signed_ by the staker's account. + /// + /// - `core_id`: Id of the core to stake towards. + /// - `value`: Amount to stake. #[pallet::call_index(3)] #[pallet::weight(::WeightInfo::stake())] pub fn stake( @@ -588,6 +700,16 @@ pub mod pallet { Ok(().into()) } + /// Start unbonding process and unstake balance from the core. + /// + /// The unstaked amount will no longer be eligible for rewards but still won't be unlocked. + /// User needs to wait for the unbonding period to finish before being able to withdraw + /// the funds via `withdraw_unstaked` call. + /// + /// In case remaining staked balance is below minimum staking amount, + /// entire stake will be unstaked. + /// + /// - `core_id`: Id of the core to unstake from. #[pallet::call_index(4)] #[pallet::weight(::WeightInfo::unstake())] pub fn unstake( @@ -643,6 +765,12 @@ pub mod pallet { Ok(().into()) } + /// Withdraw all funds that have completed the unbonding process. + /// + /// If there are unbonding chunks which will be fully unbonded in future eras, + /// they will remain and can be withdrawn later. + /// + /// The dispatch origin for this call must be _Signed_ by the staker's account. #[pallet::call_index(5)] #[pallet::weight(::WeightInfo::withdraw_unstaked())] pub fn withdraw_unstaked(origin: OriginFor) -> DispatchResultWithPostInfo { @@ -676,6 +804,13 @@ pub mod pallet { Ok(().into()) } + /// Claim the staker's rewards. + /// + /// In case reward cannot be claimed or was already claimed, an error is raised. + /// + /// The dispatch origin for this call must be _Signed_ by the staker's account. + /// + /// - `core_id`: Id of the core to claim rewards from. #[pallet::call_index(6)] #[pallet::weight(::WeightInfo::staker_claim_rewards())] pub fn staker_claim_rewards( @@ -721,6 +856,12 @@ pub mod pallet { Ok(().into()) } + /// Claim core reward for the specified era. + /// + /// In case reward cannot be claimed or was already claimed, an error is raised. + /// + /// - `core_id`: Id of the core to claim rewards from. + /// - `era`: Era for which rewards are to be claimed. #[pallet::call_index(7)] #[pallet::weight(::WeightInfo::core_claim_rewards())] pub fn core_claim_rewards( @@ -774,6 +915,11 @@ pub mod pallet { Ok(().into()) } + /// Halt or unhalt the pallet. + /// + /// The dispatch origin for this call must be _Root_. + /// + /// - `halt`: `true` to halt, `false` to unhalt. #[pallet::call_index(8)] #[pallet::weight((::WeightInfo::halt_unhalt_pallet(), Pays::No))] pub fn halt_unhalt_pallet(origin: OriginFor, halt: bool) -> DispatchResultWithPostInfo { @@ -790,6 +936,13 @@ pub mod pallet { Ok(().into()) } + /// Move stake from one core to another. + /// + /// The dispatch origin for this call must be _Signed_ by the staker's account. + /// + /// - `from_core`: Id of the core to move stake from. + /// - `amount`: Amount to move. + /// - `to_core`: Id of the core to move stake to. #[pallet::call_index(9)] #[pallet::weight(::WeightInfo::move_stake())] pub fn move_stake( @@ -848,6 +1001,8 @@ pub mod pallet { } impl Pallet { + /// Internal function responsible for validating a stake and updating in-place + /// both the staker's and core staking info. fn internal_stake( staker_info: &mut StakerInfo>, staking_info: &mut CoreStakeInfo>, @@ -882,6 +1037,8 @@ pub mod pallet { Ok(()) } + /// Internal function responsible for validating an unstake and updating in-place + /// both the staker's and core staking info. fn internal_unstake( staker_info: &mut StakerInfo>, core_stake_info: &mut CoreStakeInfo>, @@ -924,6 +1081,8 @@ pub mod pallet { T::PotId::get().into_account_truncating() } + /// Update the ledger for a staker. This will also update the stash lock. + /// This lock will lock the entire funds except paying for further transactions. fn update_ledger(staker: &T::AccountId, ledger: AccountLedger>) { if ledger.is_empty() { Ledger::::remove(staker); @@ -939,6 +1098,11 @@ pub mod pallet { } } + /// The block rewards are accumulated on the pallet's account during an era. + /// This function takes a snapshot of the pallet's balance accrued during current era + /// and stores it for future distribution + /// + /// This is called just at the beginning of an era. fn reward_balance_snapshot( era: Era, rewards: RewardInfo>, @@ -962,6 +1126,9 @@ pub mod pallet { GeneralEraInfo::::insert(era, era_info); } + /// Adds `stakers` and `cores` rewards to the reward pool. + /// + /// - `inflation`: Total inflation for the era. pub fn rewards(inflation: NegativeImbalanceOf) { let (core_part, stakers_part) = ::RewardRatio::get(); @@ -979,6 +1146,7 @@ pub mod pallet { ); } + /// Updates staker info for a core. fn update_staker_info( staker: &T::AccountId, core_id: T::CoreId, @@ -991,6 +1159,7 @@ pub mod pallet { } } + /// Returns available staking balance for the potential staker. fn available_staking_balance( staker: &T::AccountId, ledger: &AccountLedger>, @@ -1001,6 +1170,9 @@ pub mod pallet { free_balance.saturating_sub(ledger.locked) } + /// Returns total value locked by staking. + /// + /// Note that this can differ from _total staked value_ since some funds might be undergoing the unbonding period. pub fn tvl() -> BalanceOf { let current_era = Self::current_era(); if let Some(era_info) = Self::general_era_info(current_era) { @@ -1010,6 +1182,9 @@ pub mod pallet { } } + /// Calculate reward split between core and stakers. + /// + /// Returns (core reward, joint stakers reward) pub(crate) fn core_stakers_split( core_info: &CoreStakeInfo>, era_info: &EraInfo>, @@ -1027,6 +1202,7 @@ pub mod pallet { (core_reward_part, stakers_joint_reward) } + /// Used to copy all `CoreStakeInfo` from the ending era over to the next era. fn rotate_staking_info(current_era: Era) -> (Weight, BalanceOf) { let next_era = current_era + 1; @@ -1058,10 +1234,12 @@ pub mod pallet { (consumed_weight, new_active_stake) } + /// Sets the halt state of the pallet. pub fn internal_halt_unhalt(halt: bool) { Halted::::put(halt); } + /// Ensure the pallet is not halted. pub fn ensure_not_halted() -> Result<(), Error> { if Self::is_halted() { Err(Error::::Halted) diff --git a/OCIF/staking/src/primitives.rs b/OCIF/staking/src/primitives.rs index 05058faf..ca9cfc67 100644 --- a/OCIF/staking/src/primitives.rs +++ b/OCIF/staking/src/primitives.rs @@ -1,3 +1,24 @@ +//! Provides supporting types and traits for the staking pallet. +//! +//! ## Overview +//! +//! Primitives provides the foundational types and traits for a staking pallet. +//! +//! ## Types overview: +//! +//! - `BalanceOf` - A type alias for the balance of a currency in the system. +//! - `CoreMetadata` - A struct that holds metadata for a core entity in the system. +//! - `CoreInfo` - A struct that holds information about a core entity, including its account ID and metadata. +//! - `RewardInfo` - A struct that holds information about rewards, including the balance for stakers and the core. +//! - `EraInfo` - A struct that holds information about a specific era, including rewards, staked balance, active stake, and locked balance. +//! - `CoreStakeInfo` - A struct that holds information about a core's stake, including the total balance, +//! number of stakers, and whether a reward has been claimed. +//! - `EraStake` - A struct that holds information about the stake for a specific era. +//! - `StakerInfo` - A struct that holds information about a staker's stakes across different eras. +//! - `UnlockingChunk` - A struct that holds information about an unlocking chunk of balance. +//! - `UnbondingInfo` - A struct that holds information about unbonding chunks of balance. +//! - `AccountLedger` - A struct that holds information about an account's locked balance and unbonding information. + use codec::{Decode, Encode, HasCompact, MaxEncodedLen}; use frame_support::traits::Currency; use scale_info::TypeInfo; @@ -9,11 +30,13 @@ use sp_std::{ops::Add, prelude::*}; pub use crate::pallet::*; +/// The balance type of this pallet. pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; const MAX_ASSUMED_VEC_LEN: u32 = 10; +/// Metadata for a core entity in the system. #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct CoreMetadata { pub name: Name, @@ -21,12 +44,14 @@ pub struct CoreMetadata { pub image: Image, } +/// Information about a core entity, including its account ID and metadata. #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct CoreInfo { pub account: AccountId, pub metadata: Metadata, } +/// Information about rewards, including the balance for stakers and the core. #[derive(PartialEq, Eq, Clone, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct RewardInfo { #[codec(compact)] @@ -35,6 +60,7 @@ pub struct RewardInfo { pub(crate) core: Balance, } +/// Information about a specific era, including rewards, staked balance, active stake, and locked balance. #[derive(PartialEq, Eq, Clone, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct EraInfo { pub(crate) rewards: RewardInfo, @@ -46,6 +72,7 @@ pub struct EraInfo { pub(crate) locked: Balance, } +/// Information about a core's stake, including the total balance, number of stakers, and whether a reward has been claimed. #[derive(Clone, PartialEq, Eq, Encode, Decode, Default, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct CoreStakeInfo { #[codec(compact)] @@ -56,6 +83,7 @@ pub struct CoreStakeInfo { pub(crate) active: bool, } +/// Information about the stake for a specific era. #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub(crate) struct EraStake { #[codec(compact)] @@ -64,6 +92,7 @@ pub(crate) struct EraStake pub(crate) era: Era, } +/// Information about a staker's stakes across different eras. #[derive(Encode, Decode, Clone, Default, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct StakerInfo { pub(crate) stakes: Vec>, @@ -89,6 +118,7 @@ impl StakerInfo { self.stakes.len() as u32 } + /// Stakes the given value in the current era, mutates StakerInfo in-place. pub(crate) fn stake(&mut self, current_era: Era, value: Balance) -> Result<(), &str> { if let Some(era_stake) = self.stakes.last_mut() { if era_stake.era > current_era { @@ -118,6 +148,7 @@ impl StakerInfo { Ok(()) } + /// Unstakes the given value in the current era, mutates StakerInfo in-place. pub(crate) fn unstake(&mut self, current_era: Era, value: Balance) -> Result<(), &str> { if let Some(era_stake) = self.stakes.last_mut() { if era_stake.era > current_era { @@ -145,6 +176,8 @@ impl StakerInfo { Ok(()) } + /// Claims the stake for the current era, mutates StakerInfo in-place. + /// Returns the era and the staked balance. pub(crate) fn claim(&mut self) -> (Era, Balance) { if let Some(era_stake) = self.stakes.first() { let era_stake = *era_stake; @@ -168,11 +201,13 @@ impl StakerInfo { } } + /// Returns the latest staked balance. pub(crate) fn latest_staked_value(&self) -> Balance { self.stakes.last().map_or(Zero::zero(), |x| x.staked) } } +/// A chunk of balance that is unlocking until a specific era. #[derive( Clone, PartialEq, Eq, Copy, Encode, Decode, Default, RuntimeDebug, TypeInfo, MaxEncodedLen, )] @@ -187,11 +222,13 @@ impl UnlockingChunk where Balance: Add + Copy + MaxEncodedLen, { + /// Adds the given amount to the chunk's amount. pub(crate) fn add_amount(&mut self, amount: Balance) { self.amount = self.amount + amount } } +/// Information about unbonding chunks of balance. #[derive(Clone, PartialEq, Eq, Encode, Decode, Default, RuntimeDebug, TypeInfo)] pub(crate) struct UnbondingInfo { pub(crate) unlocking_chunks: Vec>, @@ -222,6 +259,7 @@ where self.unlocking_chunks.is_empty() } + /// Returns the total amount of the unlocking chunks. pub(crate) fn sum(&self) -> Balance { self.unlocking_chunks .iter() @@ -230,6 +268,7 @@ where .unwrap_or_default() } + /// Adds the given chunk to the unbonding info. pub(crate) fn add(&mut self, chunk: UnlockingChunk) { match self .unlocking_chunks @@ -240,6 +279,7 @@ where } } + /// returns the chucks before and after a given era. pub(crate) fn partition(self, era: Era) -> (Self, Self) { let (matching_chunks, other_chunks): ( Vec>, @@ -260,6 +300,7 @@ where } } +/// Information about an account's locked balance and unbonding information. #[derive(Clone, PartialEq, Eq, Encode, Decode, Default, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct AccountLedger { #[codec(compact)]