pallet_xc_asset_config/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
55
56use frame_support::pallet;
57pub use pallet::*;
58
59#[cfg(any(test, feature = "runtime-benchmarks"))]
60mod benchmarking;
61
62#[cfg(test)]
63pub mod mock;
64#[cfg(test)]
65pub mod tests;
66
67pub mod migrations;
68
69pub mod weights;
70pub use weights::WeightInfo;
71
72#[pallet]
73pub mod pallet {
74 use crate::weights::WeightInfo;
75 use frame_support::{
76 pallet_prelude::*, traits::EnsureOrigin, weights::constants::WEIGHT_REF_TIME_PER_SECOND,
77 };
78 use frame_system::pallet_prelude::*;
79 use parity_scale_codec::HasCompact;
80 use sp_std::boxed::Box;
81 use xcm::{v5::Location, VersionedLocation};
82
83 const STORAGE_VERSION: StorageVersion = StorageVersion::new(5);
84
85 #[pallet::pallet]
86 #[pallet::storage_version(STORAGE_VERSION)]
87 #[pallet::without_storage_info]
88 pub struct Pallet<T>(PhantomData<T>);
89
90 pub trait XcAssetLocation<AssetId> {
92 fn get_xc_asset_location(asset_id: AssetId) -> Option<Location>;
94
95 fn get_asset_id(xc_asset_location: Location) -> Option<AssetId>;
97 }
98
99 pub trait ExecutionPaymentRate {
101 fn get_units_per_second(asset_location: Location) -> Option<u128>;
103 }
104
105 impl<T: Config> XcAssetLocation<T::AssetId> for Pallet<T> {
106 fn get_xc_asset_location(asset_id: T::AssetId) -> Option<Location> {
107 AssetIdToLocation::<T>::get(asset_id).and_then(|x| x.try_into().ok())
108 }
109
110 fn get_asset_id(asset_location: Location) -> Option<T::AssetId> {
111 AssetLocationToId::<T>::get(asset_location.into_versioned())
112 }
113 }
114
115 impl<T: Config> ExecutionPaymentRate for Pallet<T> {
116 fn get_units_per_second(asset_location: Location) -> Option<u128> {
117 AssetLocationUnitsPerSecond::<T>::get(asset_location.into_versioned())
118 }
119 }
120
121 impl<T: Config> Pallet<T> {
122 pub fn weight_to_fee(weight: Weight, units_per_second: u128) -> u128 {
124 units_per_second.saturating_mul(weight.ref_time() as u128)
125 / (WEIGHT_REF_TIME_PER_SECOND as u128)
126 }
127 }
128
129 #[pallet::config]
130 pub trait Config: frame_system::Config {
131 type AssetId: Member + Parameter + Default + Copy + HasCompact + MaxEncodedLen;
134
135 type ManagerOrigin: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin>;
139
140 type WeightInfo: WeightInfo;
141 }
142
143 #[pallet::error]
144 pub enum Error<T> {
145 AssetAlreadyRegistered,
147 AssetDoesNotExist,
149 MultiLocationNotSupported,
151 }
152
153 #[allow(clippy::large_enum_variant)]
154 #[pallet::event]
155 #[pallet::generate_deposit(pub(crate) fn deposit_event)]
156 pub enum Event<T: Config> {
157 AssetRegistered {
159 asset_location: VersionedLocation,
160 asset_id: T::AssetId,
161 },
162 UnitsPerSecondChanged {
164 asset_location: VersionedLocation,
165 units_per_second: u128,
166 },
167 AssetLocationChanged {
169 previous_asset_location: VersionedLocation,
170 asset_id: T::AssetId,
171 new_asset_location: VersionedLocation,
172 },
173 SupportedAssetRemoved { asset_location: VersionedLocation },
175 AssetRemoved {
177 asset_location: VersionedLocation,
178 asset_id: T::AssetId,
179 },
180 }
181
182 #[pallet::storage]
186 pub type AssetIdToLocation<T: Config> =
187 StorageMap<_, Twox64Concat, T::AssetId, VersionedLocation>;
188
189 #[pallet::storage]
193 pub type AssetLocationToId<T: Config> =
194 StorageMap<_, Twox64Concat, VersionedLocation, T::AssetId>;
195
196 #[pallet::storage]
201 pub type AssetLocationUnitsPerSecond<T: Config> =
202 StorageMap<_, Twox64Concat, VersionedLocation, u128>;
203
204 #[pallet::call]
205 impl<T: Config> Pallet<T> {
206 #[pallet::call_index(0)]
210 #[pallet::weight(T::WeightInfo::register_asset_location())]
211 pub fn register_asset_location(
212 origin: OriginFor<T>,
213 asset_location: Box<VersionedLocation>,
214 #[pallet::compact] asset_id: T::AssetId,
215 ) -> DispatchResult {
216 T::ManagerOrigin::ensure_origin(origin)?;
217
218 ensure!(
220 !AssetIdToLocation::<T>::contains_key(asset_id),
221 Error::<T>::AssetAlreadyRegistered
222 );
223
224 let v5_asset_loc = Location::try_from(*asset_location)
225 .map_err(|_| Error::<T>::MultiLocationNotSupported)?;
226 let asset_location = VersionedLocation::V5(v5_asset_loc);
227
228 AssetIdToLocation::<T>::insert(asset_id, asset_location.clone());
229 AssetLocationToId::<T>::insert(&asset_location, asset_id);
230
231 Self::deposit_event(Event::AssetRegistered {
232 asset_location,
233 asset_id,
234 });
235 Ok(())
236 }
237
238 #[pallet::call_index(1)]
241 #[pallet::weight(T::WeightInfo::set_asset_units_per_second())]
242 pub fn set_asset_units_per_second(
243 origin: OriginFor<T>,
244 asset_location: Box<VersionedLocation>,
245 #[pallet::compact] units_per_second: u128,
246 ) -> DispatchResult {
247 T::ManagerOrigin::ensure_origin(origin)?;
248
249 let v5_asset_loc = Location::try_from(*asset_location)
250 .map_err(|_| Error::<T>::MultiLocationNotSupported)?;
251 let asset_location = VersionedLocation::V5(v5_asset_loc);
252
253 ensure!(
254 AssetLocationToId::<T>::contains_key(&asset_location),
255 Error::<T>::AssetDoesNotExist
256 );
257
258 AssetLocationUnitsPerSecond::<T>::insert(&asset_location, units_per_second);
259
260 Self::deposit_event(Event::UnitsPerSecondChanged {
261 asset_location,
262 units_per_second,
263 });
264 Ok(())
265 }
266
267 #[pallet::call_index(2)]
270 #[pallet::weight(T::WeightInfo::change_existing_asset_location())]
271 pub fn change_existing_asset_location(
272 origin: OriginFor<T>,
273 new_asset_location: Box<VersionedLocation>,
274 #[pallet::compact] asset_id: T::AssetId,
275 ) -> DispatchResult {
276 T::ManagerOrigin::ensure_origin(origin)?;
277
278 let v5_asset_loc = Location::try_from(*new_asset_location)
279 .map_err(|_| Error::<T>::MultiLocationNotSupported)?;
280 let new_asset_location = VersionedLocation::V5(v5_asset_loc);
281
282 let previous_asset_location =
283 AssetIdToLocation::<T>::get(asset_id).ok_or(Error::<T>::AssetDoesNotExist)?;
284
285 AssetIdToLocation::<T>::insert(asset_id, new_asset_location.clone());
287 AssetLocationToId::<T>::insert(&new_asset_location, asset_id);
288
289 AssetLocationToId::<T>::remove(&previous_asset_location);
291
292 if let Some(units) = AssetLocationUnitsPerSecond::<T>::take(&previous_asset_location) {
294 AssetLocationUnitsPerSecond::<T>::insert(&new_asset_location, units);
295 }
296
297 Self::deposit_event(Event::AssetLocationChanged {
298 previous_asset_location,
299 asset_id,
300 new_asset_location,
301 });
302 Ok(())
303 }
304
305 #[pallet::call_index(3)]
309 #[pallet::weight(T::WeightInfo::remove_payment_asset())]
310 pub fn remove_payment_asset(
311 origin: OriginFor<T>,
312 asset_location: Box<VersionedLocation>,
313 ) -> DispatchResult {
314 T::ManagerOrigin::ensure_origin(origin)?;
315
316 let v5_asset_loc = Location::try_from(*asset_location)
317 .map_err(|_| Error::<T>::MultiLocationNotSupported)?;
318 let asset_location = VersionedLocation::V5(v5_asset_loc);
319
320 AssetLocationUnitsPerSecond::<T>::remove(&asset_location);
321
322 Self::deposit_event(Event::SupportedAssetRemoved { asset_location });
323 Ok(())
324 }
325
326 #[pallet::call_index(4)]
328 #[pallet::weight(T::WeightInfo::remove_asset())]
329 pub fn remove_asset(
330 origin: OriginFor<T>,
331 #[pallet::compact] asset_id: T::AssetId,
332 ) -> DispatchResult {
333 T::ManagerOrigin::ensure_origin(origin)?;
334
335 let asset_location =
336 AssetIdToLocation::<T>::get(asset_id).ok_or(Error::<T>::AssetDoesNotExist)?;
337
338 AssetIdToLocation::<T>::remove(asset_id);
339 AssetLocationToId::<T>::remove(&asset_location);
340 AssetLocationUnitsPerSecond::<T>::remove(&asset_location);
341
342 Self::deposit_event(Event::AssetRemoved {
343 asset_id,
344 asset_location,
345 });
346 Ok(())
347 }
348 }
349}