1use super::*;
19
20use frame_support::traits::{
21 fungibles::{Dust, Inspect, InspectHold, MutateHold, Unbalanced, UnbalancedHold},
22 tokens::{
23 DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence,
24 },
25};
26use pallet_assets::BalanceOnHold;
27use sp_runtime::{
28 traits::{CheckedAdd, CheckedSub, Zero},
29 ArithmeticError,
30};
31use storage::StorageDoubleMap;
32
33impl<T: Config<I>, I: 'static> BalanceOnHold<T::AssetId, T::AccountId, T::Balance>
37 for Pallet<T, I>
38{
39 fn balance_on_hold(asset: T::AssetId, who: &T::AccountId) -> Option<T::Balance> {
40 BalancesOnHold::<T, I>::get(asset, who)
41 }
42
43 fn died(asset: T::AssetId, who: &T::AccountId) {
44 defensive_assert!(
45 Holds::<T, I>::get(asset.clone(), who).is_empty(),
46 "The list of Holds should be empty before allowing an account to die"
47 );
48 defensive_assert!(
49 BalancesOnHold::<T, I>::get(asset.clone(), who).is_none(),
50 "The should not be a balance on hold before allowing to die"
51 );
52
53 Holds::<T, I>::remove(asset.clone(), who);
54 BalancesOnHold::<T, I>::remove(asset, who);
55 }
56
57 fn contains_holds(asset: T::AssetId) -> bool {
58 Holds::<T, I>::contains_prefix(asset)
59 }
60}
61
62impl<T: Config<I>, I: 'static> Inspect<T::AccountId> for Pallet<T, I> {
67 type AssetId = T::AssetId;
68 type Balance = T::Balance;
69
70 fn total_issuance(asset: Self::AssetId) -> Self::Balance {
71 pallet_assets::Pallet::<T, I>::total_issuance(asset)
72 }
73
74 fn minimum_balance(asset: Self::AssetId) -> Self::Balance {
75 pallet_assets::Pallet::<T, I>::minimum_balance(asset)
76 }
77
78 fn total_balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance {
79 pallet_assets::Pallet::<T, I>::total_balance(asset, who)
80 }
81
82 fn balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance {
83 pallet_assets::Pallet::<T, I>::balance(asset, who)
84 }
85
86 fn reducible_balance(
87 asset: Self::AssetId,
88 who: &T::AccountId,
89 preservation: Preservation,
90 force: Fortitude,
91 ) -> Self::Balance {
92 pallet_assets::Pallet::<T, I>::reducible_balance(asset, who, preservation, force)
93 }
94
95 fn can_deposit(
96 asset: Self::AssetId,
97 who: &T::AccountId,
98 amount: Self::Balance,
99 provenance: Provenance,
100 ) -> DepositConsequence {
101 pallet_assets::Pallet::<T, I>::can_deposit(asset, who, amount, provenance)
102 }
103
104 fn can_withdraw(
105 asset: Self::AssetId,
106 who: &T::AccountId,
107 amount: Self::Balance,
108 ) -> WithdrawConsequence<Self::Balance> {
109 pallet_assets::Pallet::<T, I>::can_withdraw(asset, who, amount)
110 }
111
112 fn asset_exists(asset: Self::AssetId) -> bool {
113 pallet_assets::Pallet::<T, I>::asset_exists(asset)
114 }
115
116 fn is_sufficient(asset: Self::AssetId) -> bool {
117 pallet_assets::Pallet::<T, I>::is_sufficient(asset)
118 }
119}
120
121impl<T: Config<I>, I: 'static> InspectHold<T::AccountId> for Pallet<T, I> {
122 type Reason = T::RuntimeHoldReason;
123
124 fn total_balance_on_hold(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance {
125 BalancesOnHold::<T, I>::get(asset, who).unwrap_or_else(Zero::zero)
126 }
127
128 fn balance_on_hold(
129 asset: Self::AssetId,
130 reason: &Self::Reason,
131 who: &T::AccountId,
132 ) -> Self::Balance {
133 Holds::<T, I>::get(asset, who)
134 .iter()
135 .find(|x| &x.id == reason)
136 .map(|x| x.amount)
137 .unwrap_or_else(Zero::zero)
138 }
139}
140
141impl<T: Config<I>, I: 'static> Unbalanced<T::AccountId> for Pallet<T, I> {
142 fn handle_dust(dust: Dust<T::AccountId, Self>) {
143 let Dust(id, balance) = dust;
144 pallet_assets::Pallet::<T, I>::handle_dust(Dust(id, balance));
145 }
146
147 fn write_balance(
148 asset: Self::AssetId,
149 who: &T::AccountId,
150 amount: Self::Balance,
151 ) -> Result<Option<Self::Balance>, DispatchError> {
152 pallet_assets::Pallet::<T, I>::write_balance(asset, who, amount)
153 }
154
155 fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) {
156 pallet_assets::Pallet::<T, I>::set_total_issuance(asset, amount)
157 }
158
159 fn decrease_balance(
160 asset: Self::AssetId,
161 who: &T::AccountId,
162 amount: Self::Balance,
163 precision: Precision,
164 preservation: Preservation,
165 force: Fortitude,
166 ) -> Result<Self::Balance, DispatchError> {
167 pallet_assets::Pallet::<T, I>::decrease_balance(
168 asset,
169 who,
170 amount,
171 precision,
172 preservation,
173 force,
174 )
175 }
176
177 fn increase_balance(
178 asset: Self::AssetId,
179 who: &T::AccountId,
180 amount: Self::Balance,
181 precision: Precision,
182 ) -> Result<Self::Balance, DispatchError> {
183 pallet_assets::Pallet::<T, I>::increase_balance(asset, who, amount, precision)
184 }
185}
186
187impl<T: Config<I>, I: 'static> UnbalancedHold<T::AccountId> for Pallet<T, I> {
188 fn set_balance_on_hold(
189 asset: Self::AssetId,
190 reason: &Self::Reason,
191 who: &T::AccountId,
192 amount: Self::Balance,
193 ) -> DispatchResult {
194 let mut holds = Holds::<T, I>::get(asset.clone(), who);
195 let amount_on_hold =
196 BalancesOnHold::<T, I>::get(asset.clone(), who).unwrap_or_else(Zero::zero);
197
198 let amount_on_hold = if amount.is_zero() {
199 if let Some(pos) = holds.iter().position(|x| &x.id == reason) {
200 let item = &mut holds[pos];
201 let amount = item.amount;
202
203 holds.swap_remove(pos);
204 amount_on_hold.checked_sub(&amount).ok_or(ArithmeticError::Underflow)?
205 } else {
206 amount_on_hold
207 }
208 } else {
209 let (increase, delta) = if let Some(pos) = holds.iter().position(|x| &x.id == reason) {
210 let item = &mut holds[pos];
211 let (increase, delta) =
212 (amount > item.amount, item.amount.max(amount) - item.amount.min(amount));
213
214 item.amount = amount;
215 if item.amount.is_zero() {
216 holds.swap_remove(pos);
217 }
218
219 (increase, delta)
220 } else {
221 holds
222 .try_push(IdAmount { id: *reason, amount })
223 .map_err(|_| Error::<T, I>::TooManyHolds)?;
224 (true, amount)
225 };
226
227 let amount_on_hold = if increase {
228 amount_on_hold.checked_add(&delta).ok_or(ArithmeticError::Overflow)?
229 } else {
230 amount_on_hold.checked_sub(&delta).ok_or(ArithmeticError::Underflow)?
231 };
232
233 amount_on_hold
234 };
235
236 if !holds.is_empty() {
237 Holds::<T, I>::insert(asset.clone(), who, holds);
238 } else {
239 Holds::<T, I>::remove(asset.clone(), who);
240 }
241
242 if amount_on_hold.is_zero() {
243 BalancesOnHold::<T, I>::remove(asset.clone(), who);
244 } else {
245 BalancesOnHold::<T, I>::insert(asset.clone(), who, amount_on_hold);
246 }
247
248 Ok(())
249 }
250}
251
252impl<T: Config<I>, I: 'static> MutateHold<T::AccountId> for Pallet<T, I> {
253 fn done_hold(
254 asset_id: Self::AssetId,
255 reason: &Self::Reason,
256 who: &T::AccountId,
257 amount: Self::Balance,
258 ) {
259 Self::deposit_event(Event::<T, I>::Held {
260 asset_id,
261 who: who.clone(),
262 reason: *reason,
263 amount,
264 });
265 }
266
267 fn done_release(
268 asset_id: Self::AssetId,
269 reason: &Self::Reason,
270 who: &T::AccountId,
271 amount: Self::Balance,
272 ) {
273 Self::deposit_event(Event::<T, I>::Released {
274 asset_id,
275 who: who.clone(),
276 reason: *reason,
277 amount,
278 });
279 }
280
281 fn done_burn_held(
282 asset_id: Self::AssetId,
283 reason: &Self::Reason,
284 who: &T::AccountId,
285 amount: Self::Balance,
286 ) {
287 Self::deposit_event(Event::<T, I>::Burned {
288 asset_id,
289 who: who.clone(),
290 reason: *reason,
291 amount,
292 });
293 }
294}