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
117impl<T: Config<I>, I: 'static> InspectHold<T::AccountId> for Pallet<T, I> {
118 type Reason = T::RuntimeHoldReason;
119
120 fn total_balance_on_hold(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance {
121 BalancesOnHold::<T, I>::get(asset, who).unwrap_or_else(Zero::zero)
122 }
123
124 fn balance_on_hold(
125 asset: Self::AssetId,
126 reason: &Self::Reason,
127 who: &T::AccountId,
128 ) -> Self::Balance {
129 Holds::<T, I>::get(asset, who)
130 .iter()
131 .find(|x| &x.id == reason)
132 .map(|x| x.amount)
133 .unwrap_or_else(Zero::zero)
134 }
135}
136
137impl<T: Config<I>, I: 'static> Unbalanced<T::AccountId> for Pallet<T, I> {
138 fn handle_dust(dust: Dust<T::AccountId, Self>) {
139 let Dust(id, balance) = dust;
140 pallet_assets::Pallet::<T, I>::handle_dust(Dust(id, balance));
141 }
142
143 fn write_balance(
144 asset: Self::AssetId,
145 who: &T::AccountId,
146 amount: Self::Balance,
147 ) -> Result<Option<Self::Balance>, DispatchError> {
148 pallet_assets::Pallet::<T, I>::write_balance(asset, who, amount)
149 }
150
151 fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) {
152 pallet_assets::Pallet::<T, I>::set_total_issuance(asset, amount)
153 }
154
155 fn decrease_balance(
156 asset: Self::AssetId,
157 who: &T::AccountId,
158 amount: Self::Balance,
159 precision: Precision,
160 preservation: Preservation,
161 force: Fortitude,
162 ) -> Result<Self::Balance, DispatchError> {
163 pallet_assets::Pallet::<T, I>::decrease_balance(
164 asset,
165 who,
166 amount,
167 precision,
168 preservation,
169 force,
170 )
171 }
172
173 fn increase_balance(
174 asset: Self::AssetId,
175 who: &T::AccountId,
176 amount: Self::Balance,
177 precision: Precision,
178 ) -> Result<Self::Balance, DispatchError> {
179 pallet_assets::Pallet::<T, I>::increase_balance(asset, who, amount, precision)
180 }
181}
182
183impl<T: Config<I>, I: 'static> UnbalancedHold<T::AccountId> for Pallet<T, I> {
184 fn set_balance_on_hold(
185 asset: Self::AssetId,
186 reason: &Self::Reason,
187 who: &T::AccountId,
188 amount: Self::Balance,
189 ) -> DispatchResult {
190 let mut holds = Holds::<T, I>::get(asset.clone(), who);
191 let amount_on_hold =
192 BalancesOnHold::<T, I>::get(asset.clone(), who).unwrap_or_else(Zero::zero);
193
194 let amount_on_hold = if amount.is_zero() {
195 if let Some(pos) = holds.iter().position(|x| &x.id == reason) {
196 let item = &mut holds[pos];
197 let amount = item.amount;
198
199 holds.swap_remove(pos);
200 amount_on_hold.checked_sub(&amount).ok_or(ArithmeticError::Underflow)?
201 } else {
202 amount_on_hold
203 }
204 } else {
205 let (increase, delta) = if let Some(pos) = holds.iter().position(|x| &x.id == reason) {
206 let item = &mut holds[pos];
207 let (increase, delta) =
208 (amount > item.amount, item.amount.max(amount) - item.amount.min(amount));
209
210 item.amount = amount;
211 if item.amount.is_zero() {
212 holds.swap_remove(pos);
213 }
214
215 (increase, delta)
216 } else {
217 holds
218 .try_push(IdAmount { id: *reason, amount })
219 .map_err(|_| Error::<T, I>::TooManyHolds)?;
220 (true, amount)
221 };
222
223 let amount_on_hold = if increase {
224 amount_on_hold.checked_add(&delta).ok_or(ArithmeticError::Overflow)?
225 } else {
226 amount_on_hold.checked_sub(&delta).ok_or(ArithmeticError::Underflow)?
227 };
228
229 amount_on_hold
230 };
231
232 if !holds.is_empty() {
233 Holds::<T, I>::insert(asset.clone(), who, holds);
234 } else {
235 Holds::<T, I>::remove(asset.clone(), who);
236 }
237
238 if amount_on_hold.is_zero() {
239 BalancesOnHold::<T, I>::remove(asset.clone(), who);
240 } else {
241 BalancesOnHold::<T, I>::insert(asset.clone(), who, amount_on_hold);
242 }
243
244 Ok(())
245 }
246}
247
248impl<T: Config<I>, I: 'static> MutateHold<T::AccountId> for Pallet<T, I> {
249 fn done_hold(
250 asset_id: Self::AssetId,
251 reason: &Self::Reason,
252 who: &T::AccountId,
253 amount: Self::Balance,
254 ) {
255 Self::deposit_event(Event::<T, I>::Held {
256 asset_id,
257 who: who.clone(),
258 reason: *reason,
259 amount,
260 });
261 }
262
263 fn done_release(
264 asset_id: Self::AssetId,
265 reason: &Self::Reason,
266 who: &T::AccountId,
267 amount: Self::Balance,
268 ) {
269 Self::deposit_event(Event::<T, I>::Released {
270 asset_id,
271 who: who.clone(),
272 reason: *reason,
273 amount,
274 });
275 }
276
277 fn done_burn_held(
278 asset_id: Self::AssetId,
279 reason: &Self::Reason,
280 who: &T::AccountId,
281 amount: Self::Balance,
282 ) {
283 Self::deposit_event(Event::<T, I>::Burned {
284 asset_id,
285 who: who.clone(),
286 reason: *reason,
287 amount,
288 });
289 }
290}