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}