orml_tokens/
impls.rs

1use frame_support::traits::tokens::{Fortitude, Precision, Preservation, Provenance};
2use frame_support::traits::{
3	fungible, fungibles,
4	tokens::{Balance as BalanceT, DepositConsequence, WithdrawConsequence},
5	Contains, Get,
6};
7use sp_arithmetic::{traits::Bounded, ArithmeticError};
8use sp_runtime::DispatchError;
9
10pub struct Combiner<AccountId, TestKey, A, B>(sp_std::marker::PhantomData<(AccountId, TestKey, A, B)>);
11
12impl<AccountId, TestKey, A, B> fungibles::Inspect<AccountId> for Combiner<AccountId, TestKey, A, B>
13where
14	TestKey: Contains<<B as fungibles::Inspect<AccountId>>::AssetId>,
15	A: fungible::Inspect<AccountId, Balance = <B as fungibles::Inspect<AccountId>>::Balance>,
16	B: fungibles::Inspect<AccountId>,
17{
18	type AssetId = <B as fungibles::Inspect<AccountId>>::AssetId;
19	type Balance = <B as fungibles::Inspect<AccountId>>::Balance;
20
21	fn total_issuance(asset: Self::AssetId) -> Self::Balance {
22		if TestKey::contains(&asset) {
23			A::total_issuance()
24		} else {
25			B::total_issuance(asset)
26		}
27	}
28
29	fn minimum_balance(asset: Self::AssetId) -> Self::Balance {
30		if TestKey::contains(&asset) {
31			A::minimum_balance()
32		} else {
33			B::minimum_balance(asset)
34		}
35	}
36
37	fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
38		if TestKey::contains(&asset) {
39			A::balance(who)
40		} else {
41			B::balance(asset, who)
42		}
43	}
44
45	fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
46		if TestKey::contains(&asset) {
47			A::total_balance(who)
48		} else {
49			B::total_balance(asset, who)
50		}
51	}
52
53	fn reducible_balance(
54		asset: Self::AssetId,
55		who: &AccountId,
56		preservation: Preservation,
57		fortitude: Fortitude,
58	) -> Self::Balance {
59		if TestKey::contains(&asset) {
60			A::reducible_balance(who, preservation, fortitude)
61		} else {
62			B::reducible_balance(asset, who, preservation, fortitude)
63		}
64	}
65
66	fn can_deposit(
67		asset: Self::AssetId,
68		who: &AccountId,
69		amount: Self::Balance,
70		provenance: Provenance,
71	) -> DepositConsequence {
72		if TestKey::contains(&asset) {
73			A::can_deposit(who, amount, provenance)
74		} else {
75			B::can_deposit(asset, who, amount, provenance)
76		}
77	}
78
79	fn can_withdraw(
80		asset: Self::AssetId,
81		who: &AccountId,
82		amount: Self::Balance,
83	) -> WithdrawConsequence<Self::Balance> {
84		if TestKey::contains(&asset) {
85			A::can_withdraw(who, amount)
86		} else {
87			B::can_withdraw(asset, who, amount)
88		}
89	}
90
91	fn asset_exists(asset: Self::AssetId) -> bool {
92		if TestKey::contains(&asset) {
93			true
94		} else {
95			B::asset_exists(asset)
96		}
97	}
98}
99
100impl<AccountId, TestKey, A, B> fungibles::Mutate<AccountId> for Combiner<AccountId, TestKey, A, B>
101where
102	TestKey: Contains<<B as fungibles::Inspect<AccountId>>::AssetId>,
103	A: fungible::Mutate<AccountId, Balance = <B as fungibles::Inspect<AccountId>>::Balance>,
104	B: fungibles::Mutate<AccountId>,
105	AccountId: Eq,
106{
107	fn mint_into(
108		asset: Self::AssetId,
109		dest: &AccountId,
110		amount: Self::Balance,
111	) -> Result<Self::Balance, DispatchError> {
112		if TestKey::contains(&asset) {
113			A::mint_into(dest, amount)
114		} else {
115			B::mint_into(asset, dest, amount)
116		}
117	}
118
119	fn burn_from(
120		asset: Self::AssetId,
121		dest: &AccountId,
122		amount: Self::Balance,
123		preservation: Preservation,
124		precision: Precision,
125		fortitude: Fortitude,
126	) -> Result<Self::Balance, DispatchError> {
127		if TestKey::contains(&asset) {
128			A::burn_from(dest, amount, preservation, precision, fortitude)
129		} else {
130			B::burn_from(asset, dest, amount, preservation, precision, fortitude)
131		}
132	}
133
134	fn transfer(
135		asset: Self::AssetId,
136		source: &AccountId,
137		dest: &AccountId,
138		amount: Self::Balance,
139		preservation: Preservation,
140	) -> Result<Self::Balance, DispatchError> {
141		if TestKey::contains(&asset) {
142			A::transfer(source, dest, amount, preservation)
143		} else {
144			B::transfer(asset, source, dest, amount, preservation)
145		}
146	}
147}
148
149impl<AccountId, TestKey, A, B> fungibles::Unbalanced<AccountId> for Combiner<AccountId, TestKey, A, B>
150where
151	TestKey: Contains<<B as fungibles::Inspect<AccountId>>::AssetId>,
152	A: fungible::Mutate<AccountId, Balance = <B as fungibles::Inspect<AccountId>>::Balance>,
153	B: fungibles::Mutate<AccountId>,
154	AccountId: Eq,
155{
156	fn handle_dust(_dust: fungibles::Dust<AccountId, Self>) {
157		// FIXME: only way to access internals of Dust is into_credit, but T is
158		// not balanced
159	}
160
161	fn write_balance(
162		asset: Self::AssetId,
163		who: &AccountId,
164		amount: Self::Balance,
165	) -> Result<Option<Self::Balance>, DispatchError> {
166		if TestKey::contains(&asset) {
167			A::write_balance(who, amount)
168		} else {
169			B::write_balance(asset, who, amount)
170		}
171	}
172
173	fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) {
174		if TestKey::contains(&asset) {
175			A::set_total_issuance(amount)
176		} else {
177			B::set_total_issuance(asset, amount)
178		}
179	}
180}
181
182pub trait ConvertBalance<A: Bounded, B: Bounded> {
183	type AssetId;
184	fn convert_balance(amount: A, asset_id: Self::AssetId) -> Result<B, ArithmeticError>;
185	fn convert_balance_back(amount: B, asset_id: Self::AssetId) -> Result<A, ArithmeticError>;
186
187	fn convert_balance_saturated(amount: A, asset_id: Self::AssetId) -> B {
188		Self::convert_balance(amount, asset_id).unwrap_or_else(|e| match e {
189			ArithmeticError::Overflow => B::max_value(),
190			ArithmeticError::Underflow => B::min_value(),
191			ArithmeticError::DivisionByZero => B::max_value(),
192		})
193	}
194	fn convert_balance_back_saturated(amount: B, asset_id: Self::AssetId) -> A {
195		Self::convert_balance_back(amount, asset_id).unwrap_or_else(|e| match e {
196			ArithmeticError::Overflow => A::max_value(),
197			ArithmeticError::Underflow => A::min_value(),
198			ArithmeticError::DivisionByZero => A::max_value(),
199		})
200	}
201}
202
203pub struct Mapper<AccountId, T, C, B, GetCurrencyId>(sp_std::marker::PhantomData<(AccountId, T, C, B, GetCurrencyId)>);
204impl<AccountId, T, C, B, GetCurrencyId> fungible::Inspect<AccountId> for Mapper<AccountId, T, C, B, GetCurrencyId>
205where
206	T: fungibles::Inspect<AccountId>,
207	C: ConvertBalance<
208		<T as fungibles::Inspect<AccountId>>::Balance,
209		B,
210		AssetId = <T as fungibles::Inspect<AccountId>>::AssetId,
211	>,
212	B: BalanceT,
213	GetCurrencyId: Get<<T as fungibles::Inspect<AccountId>>::AssetId>,
214{
215	type Balance = B;
216
217	fn total_issuance() -> Self::Balance {
218		C::convert_balance_saturated(T::total_issuance(GetCurrencyId::get()), GetCurrencyId::get())
219	}
220
221	fn minimum_balance() -> Self::Balance {
222		C::convert_balance_saturated(T::minimum_balance(GetCurrencyId::get()), GetCurrencyId::get())
223	}
224
225	fn balance(who: &AccountId) -> Self::Balance {
226		C::convert_balance_saturated(T::balance(GetCurrencyId::get(), who), GetCurrencyId::get())
227	}
228
229	fn total_balance(who: &AccountId) -> Self::Balance {
230		C::convert_balance_saturated(T::total_balance(GetCurrencyId::get(), who), GetCurrencyId::get())
231	}
232
233	fn reducible_balance(who: &AccountId, preservation: Preservation, fortitude: Fortitude) -> Self::Balance {
234		C::convert_balance_saturated(
235			T::reducible_balance(GetCurrencyId::get(), who, preservation, fortitude),
236			GetCurrencyId::get(),
237		)
238	}
239
240	fn can_deposit(who: &AccountId, amount: Self::Balance, provenance: Provenance) -> DepositConsequence {
241		let amount = C::convert_balance_back(amount, GetCurrencyId::get());
242		let amount = match amount {
243			Ok(amount) => amount,
244			Err(_) => return DepositConsequence::Overflow,
245		};
246		T::can_deposit(GetCurrencyId::get(), who, amount, provenance)
247	}
248
249	fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence<Self::Balance> {
250		use WithdrawConsequence::*;
251
252		let amount = C::convert_balance_back(amount, GetCurrencyId::get());
253		let amount = match amount {
254			Ok(amount) => amount,
255			Err(ArithmeticError::Overflow) => return Overflow,
256			Err(ArithmeticError::Underflow) => return Underflow,
257			Err(ArithmeticError::DivisionByZero) => return Overflow,
258		};
259
260		let res = T::can_withdraw(GetCurrencyId::get(), who, amount);
261		match res {
262			WithdrawConsequence::ReducedToZero(b) => {
263				WithdrawConsequence::ReducedToZero(C::convert_balance_saturated(b, GetCurrencyId::get()))
264			}
265			BalanceLow => BalanceLow,
266			WouldDie => WouldDie,
267			UnknownAsset => UnknownAsset,
268			Underflow => Underflow,
269			Overflow => Overflow,
270			Frozen => Frozen,
271			Success => Success,
272		}
273	}
274}
275
276impl<AccountId, T, C, B, GetCurrencyId> fungible::Mutate<AccountId> for Mapper<AccountId, T, C, B, GetCurrencyId>
277where
278	T: fungibles::Mutate<AccountId, Balance = B>,
279	C: ConvertBalance<
280		<T as fungibles::Inspect<AccountId>>::Balance,
281		B,
282		AssetId = <T as fungibles::Inspect<AccountId>>::AssetId,
283	>,
284	B: BalanceT,
285	GetCurrencyId: Get<<T as fungibles::Inspect<AccountId>>::AssetId>,
286	AccountId: Eq,
287{
288	fn mint_into(dest: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
289		T::mint_into(
290			GetCurrencyId::get(),
291			dest,
292			C::convert_balance_back(amount, GetCurrencyId::get())?,
293		)
294	}
295
296	fn burn_from(
297		dest: &AccountId,
298		amount: Self::Balance,
299		preservation: Preservation,
300		precision: Precision,
301		fortitude: Fortitude,
302	) -> Result<Self::Balance, DispatchError> {
303		T::burn_from(
304			GetCurrencyId::get(),
305			dest,
306			C::convert_balance_back(amount, GetCurrencyId::get())?,
307			preservation,
308			precision,
309			fortitude,
310		)
311	}
312
313	fn transfer(
314		source: &AccountId,
315		dest: &AccountId,
316		amount: B,
317		preservation: Preservation,
318	) -> Result<B, DispatchError> {
319		T::transfer(
320			GetCurrencyId::get(),
321			source,
322			dest,
323			C::convert_balance_back(amount, GetCurrencyId::get())?,
324			preservation,
325		)
326	}
327}
328
329impl<AccountId, T, C, B, GetCurrencyId> fungible::Unbalanced<AccountId> for Mapper<AccountId, T, C, B, GetCurrencyId>
330where
331	T: fungibles::Unbalanced<AccountId, Balance = B>,
332	C: ConvertBalance<
333		<T as fungibles::Inspect<AccountId>>::Balance,
334		B,
335		AssetId = <T as fungibles::Inspect<AccountId>>::AssetId,
336	>,
337	B: BalanceT,
338	GetCurrencyId: Get<<T as fungibles::Inspect<AccountId>>::AssetId>,
339{
340	fn handle_dust(_dust: fungible::Dust<AccountId, Self>) {
341		// FIXME: only way to access internals of Dust is into_credit, but T is
342		// not balanced
343	}
344
345	fn write_balance(who: &AccountId, amount: Self::Balance) -> Result<Option<Self::Balance>, DispatchError> {
346		T::write_balance(GetCurrencyId::get(), who, amount)
347	}
348
349	fn set_total_issuance(amount: Self::Balance) {
350		T::set_total_issuance(GetCurrencyId::get(), amount)
351	}
352}