referrerpolicy=no-referrer-when-downgrade

pallet_vesting/
vesting_info.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Module to enforce private fields on `VestingInfo`.
19
20use super::*;
21
22/// Struct to encode the vesting schedule of an individual account.
23#[derive(
24	Encode,
25	Decode,
26	DecodeWithMemTracking,
27	Copy,
28	Clone,
29	PartialEq,
30	Eq,
31	RuntimeDebug,
32	MaxEncodedLen,
33	TypeInfo,
34)]
35pub struct VestingInfo<Balance, BlockNumber> {
36	/// Locked amount at genesis.
37	locked: Balance,
38	/// Amount that gets unlocked every block after `starting_block`.
39	per_block: Balance,
40	/// Starting block for unlocking(vesting).
41	starting_block: BlockNumber,
42}
43
44impl<Balance, BlockNumber> VestingInfo<Balance, BlockNumber>
45where
46	Balance: AtLeast32BitUnsigned + Copy,
47	BlockNumber: AtLeast32BitUnsigned + Copy + Bounded,
48{
49	/// Instantiate a new `VestingInfo`.
50	pub fn new(
51		locked: Balance,
52		per_block: Balance,
53		starting_block: BlockNumber,
54	) -> VestingInfo<Balance, BlockNumber> {
55		VestingInfo { locked, per_block, starting_block }
56	}
57
58	/// Validate parameters for `VestingInfo`. Note that this does not check
59	/// against `MinVestedTransfer`.
60	pub fn is_valid(&self) -> bool {
61		!self.locked.is_zero() && !self.raw_per_block().is_zero()
62	}
63
64	/// Locked amount at schedule creation.
65	pub fn locked(&self) -> Balance {
66		self.locked
67	}
68
69	/// Amount that gets unlocked every block after `starting_block`. Corrects for `per_block` of 0.
70	/// We don't let `per_block` be less than 1, or else the vesting will never end.
71	/// This should be used whenever accessing `per_block` unless explicitly checking for 0 values.
72	pub fn per_block(&self) -> Balance {
73		self.per_block.max(One::one())
74	}
75
76	/// Get the unmodified `per_block`. Generally should not be used, but is useful for
77	/// validating `per_block`.
78	pub(crate) fn raw_per_block(&self) -> Balance {
79		self.per_block
80	}
81
82	/// Starting block for unlocking(vesting).
83	pub fn starting_block(&self) -> BlockNumber {
84		self.starting_block
85	}
86
87	/// Amount locked at block `n`.
88	pub fn locked_at<BlockNumberToBalance: Convert<BlockNumber, Balance>>(
89		&self,
90		n: BlockNumber,
91	) -> Balance {
92		// Number of blocks that count toward vesting;
93		// saturating to 0 when n < starting_block.
94		let vested_block_count = n.saturating_sub(self.starting_block);
95		let vested_block_count = BlockNumberToBalance::convert(vested_block_count);
96		// Return amount that is still locked in vesting.
97		vested_block_count
98			.checked_mul(&self.per_block()) // `per_block` accessor guarantees at least 1.
99			.map(|to_unlock| self.locked.saturating_sub(to_unlock))
100			.unwrap_or(Zero::zero())
101	}
102
103	/// Block number at which the schedule ends (as type `Balance`).
104	pub fn ending_block_as_balance<BlockNumberToBalance: Convert<BlockNumber, Balance>>(
105		&self,
106	) -> Balance {
107		let starting_block = BlockNumberToBalance::convert(self.starting_block);
108		let duration = if self.per_block() >= self.locked {
109			// If `per_block` is bigger than `locked`, the schedule will end
110			// the block after starting.
111			One::one()
112		} else {
113			self.locked / self.per_block() +
114				if (self.locked % self.per_block()).is_zero() {
115					Zero::zero()
116				} else {
117					// `per_block` does not perfectly divide `locked`, so we need an extra block to
118					// unlock some amount less than `per_block`.
119					One::one()
120				}
121		};
122
123		starting_block.saturating_add(duration)
124	}
125}