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
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}