pallet_assets_freezer/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
49
50use frame::{
51 prelude::*,
52 traits::{
53 fungibles::{Inspect, InspectFreeze, MutateFreeze},
54 tokens::{
55 DepositConsequence, Fortitude, IdAmount, Preservation, Provenance, WithdrawConsequence,
56 },
57 },
58};
59
60pub use pallet::*;
61
62#[cfg(feature = "try-runtime")]
63use frame::try_runtime::TryRuntimeError;
64
65#[cfg(test)]
66mod mock;
67#[cfg(test)]
68mod tests;
69
70mod impls;
71
72#[frame::pallet]
73pub mod pallet {
74 use super::*;
75
76 #[pallet::config(with_default)]
77 pub trait Config<I: 'static = ()>: frame_system::Config + pallet_assets::Config<I> {
78 #[pallet::no_default_bounds]
80 type RuntimeFreezeReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount;
81
82 #[pallet::no_default_bounds]
84 #[allow(deprecated)]
85 type RuntimeEvent: From<Event<Self, I>>
86 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
87 }
88
89 #[pallet::error]
90 pub enum Error<T, I = ()> {
91 TooManyFreezes,
93 }
94
95 #[pallet::pallet]
96 pub struct Pallet<T, I = ()>(_);
97
98 #[pallet::event]
99 #[pallet::generate_deposit(pub(super) fn deposit_event)]
100 pub enum Event<T: Config<I>, I: 'static = ()> {
101 Frozen { who: T::AccountId, asset_id: T::AssetId, amount: T::Balance },
103 Thawed { who: T::AccountId, asset_id: T::AssetId, amount: T::Balance },
105 }
106
107 #[pallet::storage]
109 pub type Freezes<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
110 _,
111 Blake2_128Concat,
112 T::AssetId,
113 Blake2_128Concat,
114 T::AccountId,
115 BoundedVec<
116 IdAmount<T::RuntimeFreezeReason, T::Balance>,
117 VariantCountOf<T::RuntimeFreezeReason>,
118 >,
119 ValueQuery,
120 >;
121
122 #[pallet::storage]
124 pub type FrozenBalances<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
125 _,
126 Blake2_128Concat,
127 T::AssetId,
128 Blake2_128Concat,
129 T::AccountId,
130 T::Balance,
131 >;
132
133 #[pallet::hooks]
134 impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
135 #[cfg(feature = "try-runtime")]
136 fn try_state(_: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
137 Self::do_try_state()
138 }
139 }
140}
141
142impl<T: Config<I>, I: 'static> Pallet<T, I> {
143 fn update_freezes(
144 asset: T::AssetId,
145 who: &T::AccountId,
146 freezes: BoundedSlice<
147 IdAmount<T::RuntimeFreezeReason, T::Balance>,
148 VariantCountOf<T::RuntimeFreezeReason>,
149 >,
150 ) -> DispatchResult {
151 let prev_frozen = FrozenBalances::<T, I>::get(asset.clone(), who).unwrap_or_default();
152 let after_frozen = freezes.into_iter().map(|f| f.amount).max().unwrap_or_else(Zero::zero);
153 FrozenBalances::<T, I>::set(asset.clone(), who, Some(after_frozen));
154 if freezes.is_empty() {
155 Freezes::<T, I>::remove(asset.clone(), who);
156 FrozenBalances::<T, I>::remove(asset.clone(), who);
157 } else {
158 Freezes::<T, I>::insert(asset.clone(), who, freezes);
159 }
160 if prev_frozen > after_frozen {
161 let amount = prev_frozen.saturating_sub(after_frozen);
162 Self::deposit_event(Event::Thawed { asset_id: asset, who: who.clone(), amount });
163 } else if after_frozen > prev_frozen {
164 let amount = after_frozen.saturating_sub(prev_frozen);
165 Self::deposit_event(Event::Frozen { asset_id: asset, who: who.clone(), amount });
166 }
167 Ok(())
168 }
169
170 #[cfg(feature = "try-runtime")]
171 fn do_try_state() -> Result<(), TryRuntimeError> {
172 for (asset, who, _) in FrozenBalances::<T, I>::iter() {
173 let max_frozen_amount =
174 Freezes::<T, I>::get(asset.clone(), who.clone()).iter().map(|l| l.amount).max();
175
176 ensure!(
177 FrozenBalances::<T, I>::get(asset, who) == max_frozen_amount,
178 "The `FrozenAmount` is not equal to the maximum amount in `Freezes` for (`asset`, `who`)"
179 );
180 }
181
182 Ok(())
183 }
184}