12345678910111213141516171819202122232425262728293031323334#![cfg_attr(not(feature = "std"), no_std)]35#![allow(clippy::unused_unit)]3637use frame_support::{38 dispatch::DispatchResult,39 ensure,40 pallet_prelude::*,41 traits::{fungible, fungibles, Currency, EnsureOrigin},42 RuntimeDebug,43};44use frame_system::pallet_prelude::*;45use up_data_structs::CollectionMode;46use pallet_fungible::Pallet as PalletFungible;47use scale_info::TypeInfo;48use sp_runtime::{49 traits::{One, Zero},50 ArithmeticError,51};52use sp_std::{boxed::Box, vec::Vec};53use up_data_structs::{CollectionId, TokenId, CreateCollectionData};54555657use xcm::opaque::latest::{prelude::XcmError, Weight};58use xcm::{latest::MultiLocation, VersionedMultiLocation};59use xcm_executor::{traits::WeightTrader, Assets};6061use pallet_common::erc::CrossAccountId;6263#[cfg(feature = "std")]64use serde::{Deserialize, Serialize};656667686970#[derive(71 Clone,72 Copy,73 Eq,74 PartialEq,75 PartialOrd,76 Ord,77 MaxEncodedLen,78 RuntimeDebug,79 Encode,80 Decode,81 TypeInfo,82)]83#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]84pub enum NativeCurrency {85 Here = 0,86 Parent = 1,87}8889#[derive(90 Clone,91 Copy,92 Eq,93 PartialEq,94 PartialOrd,95 Ord,96 MaxEncodedLen,97 RuntimeDebug,98 Encode,99 Decode,100 TypeInfo,101)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub enum AssetIds {104 ForeignAssetId(ForeignAssetId),105 NativeAssetId(NativeCurrency),106}107108pub trait TryAsForeign<T, F> {109 fn try_as_foreign(asset: T) -> Option<F>;110}111112impl TryAsForeign<AssetIds, ForeignAssetId> for AssetIds {113 fn try_as_foreign(asset: AssetIds) -> Option<ForeignAssetId> {114 match asset {115 AssetIds::ForeignAssetId(id) => Some(id),116 _ => None,117 }118 }119}120121pub type ForeignAssetId = u32;122pub type CurrencyId = AssetIds;123124mod impl_fungibles;125pub mod weights;126127#[cfg(feature = "runtime-benchmarks")]128mod benchmarking;129130pub use module::*;131pub use weights::WeightInfo;132133134pub type BalanceOf<T> =135 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;136137138pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {139 140 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;141 142 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;143 144 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;145}146147pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);148149impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>150 for XcmForeignAssetIdMapping<T>151{152 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {153 log::trace!(target: "fassets::asset_metadatas", "call");154 Pallet::<T>::asset_metadatas(AssetIds::ForeignAssetId(foreign_asset_id))155 }156157 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {158 log::trace!(target: "fassets::get_multi_location", "call");159 Pallet::<T>::foreign_asset_locations(foreign_asset_id)160 }161162 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {163 log::trace!(target: "fassets::get_currency_id", "call");164 Pallet::<T>::location_to_currency_ids(multi_location).map(AssetIds::ForeignAssetId)165 }166}167168#[frame_support::pallet]169pub mod module {170 use super::*;171172 #[pallet::config]173 pub trait Config:174 frame_system::Config175 + pallet_common::Config176 + pallet_fungible::Config177 + orml_tokens::Config178 + pallet_balances::Config179 {180 181 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;182183 184 type Currency: Currency<Self::AccountId>;185186 187 type RegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;188189 190 type WeightInfo: WeightInfo;191 }192193 pub type AssetName = BoundedVec<u8, ConstU32<32>>;194 pub type AssetSymbol = BoundedVec<u8, ConstU32<7>>;195196 #[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]197 pub struct AssetMetadata<Balance> {198 pub name: AssetName,199 pub symbol: AssetSymbol,200 pub decimals: u8,201 pub minimal_balance: Balance,202 }203204 #[pallet::error]205 pub enum Error<T> {206 207 208 BadLocation,209 210 MultiLocationExisted,211 212 AssetIdNotExists,213 214 AssetIdExisted,215 }216217 #[pallet::event]218 #[pallet::generate_deposit(fn deposit_event)]219 pub enum Event<T: Config> {220 221 ForeignAssetRegistered {222 asset_id: ForeignAssetId,223 asset_address: MultiLocation,224 metadata: AssetMetadata<BalanceOf<T>>,225 },226 227 ForeignAssetUpdated {228 asset_id: ForeignAssetId,229 asset_address: MultiLocation,230 metadata: AssetMetadata<BalanceOf<T>>,231 },232 233 AssetRegistered {234 asset_id: AssetIds,235 metadata: AssetMetadata<BalanceOf<T>>,236 },237 238 AssetUpdated {239 asset_id: AssetIds,240 metadata: AssetMetadata<BalanceOf<T>>,241 },242 }243244 245 246 247 #[pallet::storage]248 #[pallet::getter(fn next_foreign_asset_id)]249 pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;250 251 252 253 #[pallet::storage]254 #[pallet::getter(fn foreign_asset_locations)]255 pub type ForeignAssetLocations<T: Config> =256 StorageMap<_, Twox64Concat, ForeignAssetId, xcm::v3::MultiLocation, OptionQuery>;257258 259 260 261 #[pallet::storage]262 #[pallet::getter(fn location_to_currency_ids)]263 pub type LocationToCurrencyIds<T: Config> =264 StorageMap<_, Twox64Concat, xcm::v3::MultiLocation, ForeignAssetId, OptionQuery>;265266 267 268 269 #[pallet::storage]270 #[pallet::getter(fn asset_metadatas)]271 pub type AssetMetadatas<T: Config> =272 StorageMap<_, Twox64Concat, AssetIds, AssetMetadata<BalanceOf<T>>, OptionQuery>;273274 275 276 #[pallet::storage]277 #[pallet::getter(fn asset_binding)]278 pub type AssetBinding<T: Config> =279 StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;280281 #[pallet::pallet]282 pub struct Pallet<T>(_);283284 #[pallet::call]285 impl<T: Config> Pallet<T> {286 #[pallet::call_index(0)]287 #[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]288 pub fn register_foreign_asset(289 origin: OriginFor<T>,290 owner: T::AccountId,291 location: Box<VersionedMultiLocation>,292 metadata: Box<AssetMetadata<BalanceOf<T>>>,293 ) -> DispatchResult {294 T::RegisterOrigin::ensure_origin(origin.clone())?;295296 let location: MultiLocation = (*location)297 .try_into()298 .map_err(|()| Error::<T>::BadLocation)?;299300 let md = metadata.clone();301 let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();302 let mut description: Vec<u16> = "Foreign assets collection for "303 .encode_utf16()304 .collect::<Vec<u16>>();305 description.append(&mut name.clone());306307 let data: CreateCollectionData<T::CrossAccountId> = CreateCollectionData {308 name: name.try_into().unwrap(),309 description: description.try_into().unwrap(),310 mode: CollectionMode::Fungible(md.decimals),311 ..Default::default()312 };313 let owner = T::CrossAccountId::from_sub(owner);314 let bounded_collection_id =315 <PalletFungible<T>>::init_foreign_collection(owner.clone(), owner, data)?;316 let foreign_asset_id =317 Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;318319 Self::deposit_event(Event::<T>::ForeignAssetRegistered {320 asset_id: foreign_asset_id,321 asset_address: location,322 metadata: *metadata,323 });324 Ok(())325 }326327 #[pallet::call_index(1)]328 #[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]329 pub fn update_foreign_asset(330 origin: OriginFor<T>,331 foreign_asset_id: ForeignAssetId,332 location: Box<VersionedMultiLocation>,333 metadata: Box<AssetMetadata<BalanceOf<T>>>,334 ) -> DispatchResult {335 T::RegisterOrigin::ensure_origin(origin)?;336337 let location: MultiLocation = (*location)338 .try_into()339 .map_err(|()| Error::<T>::BadLocation)?;340 Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;341342 Self::deposit_event(Event::<T>::ForeignAssetUpdated {343 asset_id: foreign_asset_id,344 asset_address: location,345 metadata: *metadata,346 });347 Ok(())348 }349 }350}351352impl<T: Config> Pallet<T> {353 fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {354 NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {355 let id = *current;356 *current = current357 .checked_add(One::one())358 .ok_or(ArithmeticError::Overflow)?;359 Ok(id)360 })361 }362363 fn do_register_foreign_asset(364 location: &MultiLocation,365 metadata: &AssetMetadata<BalanceOf<T>>,366 bounded_collection_id: CollectionId,367 ) -> Result<ForeignAssetId, DispatchError> {368 let foreign_asset_id = Self::get_next_foreign_asset_id()?;369 LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {370 ensure!(371 maybe_currency_ids.is_none(),372 Error::<T>::MultiLocationExisted373 );374 *maybe_currency_ids = Some(foreign_asset_id);375 376377 ForeignAssetLocations::<T>::try_mutate(378 foreign_asset_id,379 |maybe_location| -> DispatchResult {380 ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);381 *maybe_location = Some(*location);382383 AssetMetadatas::<T>::try_mutate(384 AssetIds::ForeignAssetId(foreign_asset_id),385 |maybe_asset_metadatas| -> DispatchResult {386 ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);387 *maybe_asset_metadatas = Some(metadata.clone());388 Ok(())389 },390 )391 },392 )?;393394 AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {395 *collection_id = Some(bounded_collection_id);396 Ok(())397 })398 })?;399400 Ok(foreign_asset_id)401 }402403 fn do_update_foreign_asset(404 foreign_asset_id: ForeignAssetId,405 location: &MultiLocation,406 metadata: &AssetMetadata<BalanceOf<T>>,407 ) -> DispatchResult {408 ForeignAssetLocations::<T>::try_mutate(409 foreign_asset_id,410 |maybe_multi_locations| -> DispatchResult {411 let old_multi_locations = maybe_multi_locations412 .as_mut()413 .ok_or(Error::<T>::AssetIdNotExists)?;414415 AssetMetadatas::<T>::try_mutate(416 AssetIds::ForeignAssetId(foreign_asset_id),417 |maybe_asset_metadatas| -> DispatchResult {418 ensure!(419 maybe_asset_metadatas.is_some(),420 Error::<T>::AssetIdNotExists421 );422423 424 if location != old_multi_locations {425 LocationToCurrencyIds::<T>::remove(*old_multi_locations);426 LocationToCurrencyIds::<T>::try_mutate(427 location,428 |maybe_currency_ids| -> DispatchResult {429 ensure!(430 maybe_currency_ids.is_none(),431 Error::<T>::MultiLocationExisted432 );433 434 *maybe_currency_ids = Some(foreign_asset_id);435 Ok(())436 },437 )?;438 }439 *maybe_asset_metadatas = Some(metadata.clone());440 *old_multi_locations = *location;441 Ok(())442 },443 )444 },445 )446 }447}448449pub use frame_support::{450 traits::{451 fungibles::Balanced, tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT,452 },453 weights::{WeightToFeePolynomial, WeightToFee},454};455456pub struct FreeForAll<457 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,458 AssetId: Get<MultiLocation>,459 AccountId,460 Currency: CurrencyT<AccountId>,461 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,462>(463 Weight,464 Currency::Balance,465 PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,466);467468impl<469 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,470 AssetId: Get<MultiLocation>,471 AccountId,472 Currency: CurrencyT<AccountId>,473 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,474 > WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>475{476 fn new() -> Self {477 Self(Weight::default(), Zero::zero(), PhantomData)478 }479480 fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {481 log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);482 Ok(payment)483 }484}485impl<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> Drop486 for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>487where488 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,489 AssetId: Get<MultiLocation>,490 Currency: CurrencyT<AccountId>,491 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,492{493 fn drop(&mut self) {494 OnUnbalanced::on_unbalanced(Currency::issue(self.1));495 }496}