1use super::*;
21use crate::weights::WeightInfo;
22use frame_support::traits::{Time, UncheckedOnRuntimeUpgrade};
23
24pub type MigrateV1ToV2<T, P, B, M> = frame_support::migrations::VersionedMigration<
41 1,
42 2,
43 InnerMigrateV1ToV2<T, P, B, M>,
44 pallet::Pallet<T>,
45 <T as frame_system::Config>::DbWeight,
46>;
47
48pub struct InnerMigrateV1ToV2<T, P, B, M>(core::marker::PhantomData<(T, P, B, M)>);
50
51impl<T: Config, P: Get<u64>, B: Get<BudgetAllocationMap>, M: Get<u64>> UncheckedOnRuntimeUpgrade
52 for InnerMigrateV1ToV2<T, P, B, M>
53{
54 fn on_runtime_upgrade() -> frame_support::weights::Weight {
55 let mut weight = T::DbWeight::get().reads(3);
56
57 let current_budget = BudgetAllocation::<T>::get();
59 if current_budget.is_empty() {
60 BudgetAllocation::<T>::put(B::get());
61 weight = weight.saturating_add(T::DbWeight::get().writes(1));
62 log::info!(target: LOG_TARGET, "Initialized BudgetAllocation with default budget");
63 }
64
65 let now: u64 = T::Time::now().saturated_into();
66
67 if !LastIssuanceTimestamp::<T>::get().is_zero() {
69 log::warn!(
70 target: LOG_TARGET,
71 "DAP V1->V2: LastIssuanceTimestamp already set; skipping catch-up drip"
72 );
73 return weight;
74 }
75
76 let last_inflation = P::get();
77 let raw_elapsed = now.saturating_sub(last_inflation);
78 let elapsed = raw_elapsed.min(M::get());
79 if elapsed < raw_elapsed {
80 log::info!(
81 target: LOG_TARGET,
82 "DAP V1->V2: elapsed {raw_elapsed}ms clamped to bound {elapsed}ms"
83 );
84 }
85 let minted = pallet::Pallet::<T>::mint_and_distribute(elapsed);
86 weight = weight.saturating_add(<T as Config>::WeightInfo::drip_issuance());
87
88 LastIssuanceTimestamp::<T>::put(now);
90 weight = weight.saturating_add(T::DbWeight::get().writes(1));
91
92 log::info!(
93 target: LOG_TARGET,
94 "DAP V1->V2: elapsed={elapsed}ms, total_minted={minted:?}, \
95 seeded LastIssuanceTimestamp={now}"
96 );
97
98 weight
99 }
100
101 #[cfg(feature = "try-runtime")]
102 fn pre_upgrade() -> Result<alloc::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
103 use codec::Encode;
104
105 frame_support::ensure!(
106 LastIssuanceTimestamp::<T>::get() == 0 || BudgetAllocation::<T>::get().is_empty(),
107 "Migration not needed: LastIssuanceTimestamp and BudgetAllocation already set"
108 );
109
110 let last_inflation = P::get();
112 let now: u64 = T::Time::now().saturated_into();
113 let elapsed = now.saturating_sub(last_inflation).min(M::get());
114 let total_issuance_before = T::Currency::total_issuance();
115 let expected_mint = T::IssuanceCurve::issue(total_issuance_before, elapsed);
116
117 Ok((now, total_issuance_before, expected_mint).encode())
118 }
119
120 #[cfg(feature = "try-runtime")]
121 fn post_upgrade(state: alloc::vec::Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
122 use codec::Decode;
123
124 let (expected_now, total_issuance_before, expected_mint) =
125 <(u64, BalanceOf<T>, BalanceOf<T>)>::decode(&mut &state[..])
126 .map_err(|_| "pre_upgrade state decode failed")?;
127
128 frame_support::ensure!(
130 LastIssuanceTimestamp::<T>::get() == expected_now,
131 "LastIssuanceTimestamp should equal `now` after migration"
132 );
133
134 pallet::Pallet::<T>::do_try_state()?;
136
137 let actual_mint = T::Currency::total_issuance().saturating_sub(total_issuance_before);
142 let budget_len = BudgetAllocation::<T>::get().len();
143 let max_dust = BalanceOf::<T>::from(budget_len as u32);
144 frame_support::ensure!(
145 actual_mint <= expected_mint && actual_mint.saturating_add(max_dust) >= expected_mint,
146 "Catch-up mint outside expected [expected - dust, expected] window"
147 );
148
149 Ok(())
150 }
151}