1use crate::runtime_api::FungiblesAccessError;
19use alloc::vec::Vec;
20use core::borrow::Borrow;
21use frame_support::traits::Contains;
22use sp_runtime::traits::MaybeEquivalence;
23use xcm::latest::{Asset, Location};
24use xcm_builder::{ConvertedConcreteId, MatchedConvertedConcreteId};
25use xcm_executor::traits::MatchesFungibles;
26
27pub trait AssetConverter<AssetId, Balance, ConvertAssetId, ConvertBalance>:
29 MatchesFungibles<AssetId, Balance>
30where
31 AssetId: Clone,
32 Balance: Clone,
33 ConvertAssetId: MaybeEquivalence<Location, AssetId>,
34 ConvertBalance: MaybeEquivalence<u128, Balance>,
35{
36 fn convert_ref(value: impl Borrow<(AssetId, Balance)>) -> Result<Asset, FungiblesAccessError>;
37}
38
39pub trait MatchesLocation<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertBalance>:
41 MatchesFungibles<AssetId, Balance>
42where
43 AssetId: Clone,
44 Balance: Clone,
45 MatchAssetId: Contains<Location>,
46 ConvertAssetId: MaybeEquivalence<Location, AssetId>,
47 ConvertBalance: MaybeEquivalence<u128, Balance>,
48{
49 fn contains(location: &Location) -> bool;
50}
51
52impl<
53 AssetId: Clone,
54 Balance: Clone,
55 ConvertAssetId: MaybeEquivalence<Location, AssetId>,
56 ConvertBalance: MaybeEquivalence<u128, Balance>,
57 > AssetConverter<AssetId, Balance, ConvertAssetId, ConvertBalance>
58 for ConvertedConcreteId<AssetId, Balance, ConvertAssetId, ConvertBalance>
59{
60 fn convert_ref(value: impl Borrow<(AssetId, Balance)>) -> Result<Asset, FungiblesAccessError> {
61 let (asset_id, balance) = value.borrow();
62 match ConvertAssetId::convert_back(asset_id) {
63 Some(asset_id_as_location) => match ConvertBalance::convert_back(balance) {
64 Some(amount) => Ok((asset_id_as_location, amount).into()),
65 None => Err(FungiblesAccessError::AmountToBalanceConversionFailed),
66 },
67 None => Err(FungiblesAccessError::AssetIdConversionFailed),
68 }
69 }
70}
71
72impl<
73 AssetId: Clone,
74 Balance: Clone,
75 MatchAssetId: Contains<Location>,
76 ConvertAssetId: MaybeEquivalence<Location, AssetId>,
77 ConvertBalance: MaybeEquivalence<u128, Balance>,
78 > AssetConverter<AssetId, Balance, ConvertAssetId, ConvertBalance>
79 for MatchedConvertedConcreteId<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertBalance>
80{
81 fn convert_ref(value: impl Borrow<(AssetId, Balance)>) -> Result<Asset, FungiblesAccessError> {
82 let (asset_id, balance) = value.borrow();
83 match ConvertAssetId::convert_back(asset_id) {
84 Some(asset_id_as_location) => match ConvertBalance::convert_back(balance) {
85 Some(amount) => Ok((asset_id_as_location, amount).into()),
86 None => Err(FungiblesAccessError::AmountToBalanceConversionFailed),
87 },
88 None => Err(FungiblesAccessError::AssetIdConversionFailed),
89 }
90 }
91}
92
93impl<
94 AssetId: Clone,
95 Balance: Clone,
96 MatchAssetId: Contains<Location>,
97 ConvertAssetId: MaybeEquivalence<Location, AssetId>,
98 ConvertBalance: MaybeEquivalence<u128, Balance>,
99 > MatchesLocation<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertBalance>
100 for MatchedConvertedConcreteId<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertBalance>
101{
102 fn contains(location: &Location) -> bool {
103 MatchAssetId::contains(location)
104 }
105}
106
107#[impl_trait_for_tuples::impl_for_tuples(30)]
108impl<
109 AssetId: Clone,
110 Balance: Clone,
111 MatchAssetId: Contains<Location>,
112 ConvertAssetId: MaybeEquivalence<Location, AssetId>,
113 ConvertBalance: MaybeEquivalence<u128, Balance>,
114 > MatchesLocation<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertBalance> for Tuple
115{
116 fn contains(location: &Location) -> bool {
117 for_tuples!( #(
118 match Tuple::contains(location) { o @ true => return o, _ => () }
119 )* );
120 tracing::trace!(target: "xcm::contains", ?location, "MatchesLocation: no match");
121 false
122 }
123}
124
125pub fn convert<'a, AssetId, Balance, ConvertAssetId, ConvertBalance, Converter>(
127 items: impl Iterator<Item = &'a (AssetId, Balance)>,
128) -> Result<Vec<Asset>, FungiblesAccessError>
129where
130 AssetId: Clone + 'a,
131 Balance: Clone + 'a,
132 ConvertAssetId: MaybeEquivalence<Location, AssetId>,
133 ConvertBalance: MaybeEquivalence<u128, Balance>,
134 Converter: AssetConverter<AssetId, Balance, ConvertAssetId, ConvertBalance>,
135{
136 items.map(Converter::convert_ref).collect()
137}
138
139pub fn convert_balance<T: frame_support::pallet_prelude::Get<Location>, Balance: TryInto<u128>>(
141 balance: Balance,
142) -> Result<Asset, FungiblesAccessError> {
143 match balance.try_into() {
144 Ok(balance) => Ok((T::get(), balance).into()),
145 Err(_) => Err(FungiblesAccessError::AmountToBalanceConversionFailed),
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use frame_support::traits::Everything;
153
154 use xcm::latest::prelude::*;
155 use xcm_executor::traits::{Identity, JustTry};
156
157 type Converter = MatchedConvertedConcreteId<Location, u64, Everything, Identity, JustTry>;
158
159 #[test]
160 fn converted_concrete_id_fungible_multi_asset_conversion_roundtrip_works() {
161 let location = Location::new(0, [GlobalConsensus(ByGenesis([0; 32]))]);
162 let amount = 123456_u64;
163 let expected_multi_asset = Asset {
164 id: AssetId(Location::new(0, [GlobalConsensus(ByGenesis([0; 32]))])),
165 fun: Fungible(123456_u128),
166 };
167
168 assert_eq!(
169 Converter::matches_fungibles(&expected_multi_asset).map_err(|_| ()),
170 Ok((location.clone(), amount))
171 );
172
173 assert_eq!(Converter::convert_ref((location, amount)), Ok(expected_multi_asset));
174 }
175
176 #[test]
177 fn converted_concrete_id_fungible_multi_asset_conversion_collection_works() {
178 let data = vec![
179 (Location::new(0, [GlobalConsensus(ByGenesis([0; 32]))]), 123456_u64),
180 (Location::new(1, [GlobalConsensus(ByGenesis([1; 32]))]), 654321_u64),
181 ];
182
183 let expected_data = vec![
184 Asset {
185 id: AssetId(Location::new(0, [GlobalConsensus(ByGenesis([0; 32]))])),
186 fun: Fungible(123456_u128),
187 },
188 Asset {
189 id: AssetId(Location::new(1, [GlobalConsensus(ByGenesis([1; 32]))])),
190 fun: Fungible(654321_u128),
191 },
192 ];
193
194 assert_eq!(convert::<_, _, _, _, Converter>(data.iter()), Ok(expected_data));
195 }
196}