referrerpolicy=no-referrer-when-downgrade

pallet_assets_holder/
impl_fungibles.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use 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
33// Implements [`BalanceOnHold`] from [`pallet-assets`], so it can understand whether there's some
34// balance on hold for an asset account, and is able to signal to this pallet when to clear the
35// state of an account.
36impl<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
62// Implement [`fungibles::Inspect`](frame_support::traits::fungibles::Inspect) as it is bound by
63// [`fungibles::InspectHold`](frame_support::traits::fungibles::InspectHold) and
64// [`fungibles::MutateHold`](frame_support::traits::fungibles::MutateHold). To do so, we'll
65// re-export all of `pallet-assets` implementation of the same trait.
66impl<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}