vesting_mbm/
lib.rs

1// This file is part of Astar.
2
3// Copyright (C) Stake Technologies Pte.Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6// Astar is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// Astar is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with Astar. If not, see <http://www.gnu.org/licenses/>.
18
19#![cfg_attr(not(feature = "std"), no_std)]
20
21use frame_support::{
22    migrations::{MigrationId, SteppedMigration, SteppedMigrationError},
23    weights::WeightMeter,
24};
25use pallet_vesting::{Vesting, VestingInfo};
26use sp_arithmetic::traits::{SaturatedConversion, Saturating};
27use sp_runtime::{traits::BlockNumberProvider, Percent};
28
29pub use pallet::*;
30
31#[cfg(feature = "runtime-benchmarks")]
32mod benchmarks;
33#[cfg(test)]
34mod mock;
35#[cfg(test)]
36mod tests;
37
38pub mod weights;
39
40const LOG_TARGET: &str = "mbm::vesting";
41const PALLET_MIGRATIONS_ID: &[u8; 18] = b"pallet-vesting-mbm";
42
43pub struct LazyMigration<T, W: weights::WeightInfo>(core::marker::PhantomData<(T, W)>);
44
45impl<T: pallet_vesting::Config, W: weights::WeightInfo> SteppedMigration for LazyMigration<T, W> {
46    type Cursor = <T as frame_system::Config>::AccountId;
47    // Without the explicit length here the construction of the ID would not be infallible.
48    type Identifier = MigrationId<18>;
49
50    /// The identifier of this migration. Which should be globally unique.
51    fn id() -> Self::Identifier {
52        MigrationId {
53            pallet_id: *PALLET_MIGRATIONS_ID,
54            version_from: 0,
55            version_to: 1,
56        }
57    }
58
59    fn step(
60        mut cursor: Option<Self::Cursor>,
61        meter: &mut WeightMeter,
62    ) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
63        let required = W::step(T::MAX_VESTING_SCHEDULES);
64        // If there is not enough weight for a single step, return an error. This case can be
65        // problematic if it is the first migration that ran in this block. But there is nothing
66        // that we can do about it here.
67        if meter.remaining().any_lt(required) {
68            return Err(SteppedMigrationError::InsufficientWeight { required });
69        }
70
71        let mut count = 0u32;
72        let para_block_number = frame_system::Pallet::<T>::block_number();
73        let current_block_number = T::BlockNumberProvider::current_block_number();
74
75        // We loop here to do as much progress as possible per step.
76        loop {
77            // stop when remaining weight is lower than step max weight
78            if meter.remaining().any_lt(required) {
79                break;
80            }
81
82            let mut iter = if let Some(last_key) = cursor {
83                // If a cursor is provided, start iterating from the stored value
84                // corresponding to the last key processed in the previous step.
85                // Note that this only works if the old and the new map use the same way to hash
86                // storage keys.
87                Vesting::<T>::iter_from(Vesting::<T>::hashed_key_for(last_key))
88            } else {
89                // If no cursor is provided, start iterating from the beginning.
90                Vesting::<T>::iter()
91            };
92
93            // If there's a next item in the iterator, perform the migration.
94            if let Some((ref last_key, mut schedules)) = iter.next() {
95                for schedule in schedules.iter_mut() {
96                    // remaining locked balance
97                    let locked = schedule.locked_at::<T::BlockNumberToBalance>(para_block_number);
98                    // reduce unlock `per_block` into half
99                    let per_block = Percent::from_percent(50) * schedule.per_block();
100                    // remaining blocks to start vesting if vesting hasn't started yet
101                    // remaining blocks will be doubled
102                    let remaining_blocks = schedule
103                        .starting_block()
104                        .saturating_sub(para_block_number)
105                        .saturating_mul(2u32.into());
106                    let start_block = current_block_number.saturating_add(remaining_blocks);
107
108                    *schedule = VestingInfo::new(locked, per_block, start_block);
109                }
110
111                // consume the exact weight
112                meter.consume(W::step(schedules.len().saturated_into()));
113
114                // Override vesting schedules
115                Vesting::<T>::insert(last_key, schedules);
116
117                // inc counter
118                count.saturating_inc();
119
120                // Return the processed key as the new cursor.
121                cursor = Some(last_key.clone())
122            } else {
123                // Signal that the migration is complete (no more items to process).
124                cursor = None;
125                break;
126            }
127        }
128        log::debug!(target: LOG_TARGET, "migrated {count:?} entries");
129        Ok(cursor)
130    }
131}
132
133#[frame_support::pallet]
134pub mod pallet {
135    use super::*;
136
137    #[pallet::pallet]
138    #[pallet::without_storage_info]
139    pub struct Pallet<T>(_);
140
141    #[pallet::config]
142    pub trait Config: frame_system::Config + pallet_vesting::Config {}
143}