pallet_assets_holder/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
46
47use frame_support::{
48 pallet_prelude::*,
49 traits::{tokens::IdAmount, VariantCount, VariantCountOf},
50 BoundedVec,
51};
52use frame_system::pallet_prelude::BlockNumberFor;
53
54pub use pallet::*;
55
56#[cfg(test)]
57mod mock;
58#[cfg(test)]
59mod tests;
60
61mod impl_fungibles;
62
63#[frame_support::pallet]
64pub mod pallet {
65 use super::*;
66
67 #[pallet::config(with_default)]
68 pub trait Config<I: 'static = ()>:
69 frame_system::Config + pallet_assets::Config<I, Holder = Pallet<Self, I>>
70 {
71 #[pallet::no_default_bounds]
73 type RuntimeHoldReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount;
74
75 #[pallet::no_default_bounds]
77 #[allow(deprecated)]
78 type RuntimeEvent: From<Event<Self, I>>
79 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
80 }
81
82 #[pallet::error]
83 pub enum Error<T, I = ()> {
84 TooManyHolds,
86 }
87
88 #[pallet::pallet]
89 pub struct Pallet<T, I = ()>(_);
90
91 #[pallet::event]
92 #[pallet::generate_deposit(pub(super) fn deposit_event)]
93 pub enum Event<T: Config<I>, I: 'static = ()> {
94 Held {
96 who: T::AccountId,
97 asset_id: T::AssetId,
98 reason: T::RuntimeHoldReason,
99 amount: T::Balance,
100 },
101 Released {
103 who: T::AccountId,
104 asset_id: T::AssetId,
105 reason: T::RuntimeHoldReason,
106 amount: T::Balance,
107 },
108 Burned {
110 who: T::AccountId,
111 asset_id: T::AssetId,
112 reason: T::RuntimeHoldReason,
113 amount: T::Balance,
114 },
115 }
116
117 #[pallet::storage]
119 pub(super) type Holds<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
120 _,
121 Blake2_128Concat,
122 T::AssetId,
123 Blake2_128Concat,
124 T::AccountId,
125 BoundedVec<
126 IdAmount<T::RuntimeHoldReason, T::Balance>,
127 VariantCountOf<T::RuntimeHoldReason>,
128 >,
129 ValueQuery,
130 >;
131
132 #[pallet::storage]
134 pub(super) type BalancesOnHold<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
135 _,
136 Blake2_128Concat,
137 T::AssetId,
138 Blake2_128Concat,
139 T::AccountId,
140 T::Balance,
141 >;
142
143 #[pallet::hooks]
144 impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
145 #[cfg(feature = "try-runtime")]
146 fn try_state(_: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
147 Self::do_try_state()
148 }
149 }
150}
151
152impl<T: Config<I>, I: 'static> Pallet<T, I> {
153 #[cfg(any(test, feature = "try-runtime"))]
154 fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
155 use sp_runtime::{
156 traits::{CheckedAdd, Zero},
157 ArithmeticError,
158 };
159
160 for (asset, who, balance_on_hold) in BalancesOnHold::<T, I>::iter() {
161 ensure!(balance_on_hold != Zero::zero(), "zero on hold must not be in state");
162
163 let mut amount_from_holds: T::Balance = Zero::zero();
164 for l in Holds::<T, I>::get(asset.clone(), who.clone()).iter() {
165 ensure!(l.amount != Zero::zero(), "zero amount is invalid");
166 amount_from_holds =
167 amount_from_holds.checked_add(&l.amount).ok_or(ArithmeticError::Overflow)?;
168 }
169
170 frame_support::ensure!(
171 balance_on_hold == amount_from_holds,
172 "The `BalancesOnHold` amount is not equal to the sum of `Holds` for (`asset`, `who`)"
173 );
174 }
175
176 Ok(())
177 }
178}