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 Serialize,83 Deserialize,84)]85pub enum NativeCurrency {86 Here = 0,87 Parent = 1,88}8990#[derive(91 Clone,92 Copy,93 Eq,94 PartialEq,95 PartialOrd,96 Ord,97 MaxEncodedLen,98 RuntimeDebug,99 Encode,100 Decode,101 TypeInfo,102 Serialize,103 Deserialize,104)]105pub enum AssetId {106 ForeignAssetId(ForeignAssetId),107 NativeAssetId(NativeCurrency),108}109110pub trait TryAsForeign<T, F> {111 fn try_as_foreign(asset: T) -> Option<F>;112}113114impl TryAsForeign<AssetId, ForeignAssetId> for AssetId {115 fn try_as_foreign(asset: AssetId) -> Option<ForeignAssetId> {116 match asset {117 Self::ForeignAssetId(id) => Some(id),118 _ => None,119 }120 }121}122123pub type ForeignAssetId = u32;124pub type CurrencyId = AssetId;125126mod impl_fungibles;127pub mod weights;128129#[cfg(feature = "runtime-benchmarks")]130mod benchmarking;131132pub use module::*;133pub use weights::WeightInfo;134135136pub type BalanceOf<T> =137 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;138139140pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {141 142 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;143 144 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;145 146 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;147}148149pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);150151impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>152 for XcmForeignAssetIdMapping<T>153{154 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {155 log::trace!(target: "fassets::asset_metadatas", "call");156 Pallet::<T>::asset_metadatas(AssetId::ForeignAssetId(foreign_asset_id))157 }158159 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {160 log::trace!(target: "fassets::get_multi_location", "call");161 Pallet::<T>::foreign_asset_locations(foreign_asset_id)162 }163164 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {165 log::trace!(target: "fassets::get_currency_id", "call");166 Pallet::<T>::location_to_currency_ids(multi_location).map(AssetId::ForeignAssetId)167 }168}169170#[frame_support::pallet]171pub mod module {172 use super::*;173174 #[pallet::config]175 pub trait Config:176 frame_system::Config177 + pallet_common::Config178 + pallet_fungible::Config179 + orml_tokens::Config180 + pallet_balances::Config181 {182 183 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;184185 186 type Currency: Currency<Self::AccountId>;187188 189 type RegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;190191 192 type WeightInfo: WeightInfo;193 }194195 pub type AssetName = BoundedVec<u8, ConstU32<32>>;196 pub type AssetSymbol = BoundedVec<u8, ConstU32<7>>;197198 #[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]199 pub struct AssetMetadata<Balance> {200 pub name: AssetName,201 pub symbol: AssetSymbol,202 pub decimals: u8,203 pub minimal_balance: Balance,204 }205206 #[pallet::error]207 pub enum Error<T> {208 209 210 BadLocation,211 212 MultiLocationExisted,213 214 AssetIdNotExists,215 216 AssetIdExisted,217 }218219 #[pallet::event]220 #[pallet::generate_deposit(fn deposit_event)]221 pub enum Event<T: Config> {222 223 ForeignAssetRegistered {224 asset_id: ForeignAssetId,225 asset_address: MultiLocation,226 metadata: AssetMetadata<BalanceOf<T>>,227 },228 229 ForeignAssetUpdated {230 asset_id: ForeignAssetId,231 asset_address: MultiLocation,232 metadata: AssetMetadata<BalanceOf<T>>,233 },234 235 AssetRegistered {236 asset_id: AssetId,237 metadata: AssetMetadata<BalanceOf<T>>,238 },239 240 AssetUpdated {241 asset_id: AssetId,242 metadata: AssetMetadata<BalanceOf<T>>,243 },244 }245246 247 248 249 #[pallet::storage]250 #[pallet::getter(fn next_foreign_asset_id)]251 pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;252 253 254 255 #[pallet::storage]256 #[pallet::getter(fn foreign_asset_locations)]257 pub type ForeignAssetLocations<T: Config> =258 StorageMap<_, Twox64Concat, ForeignAssetId, staging_xcm::v3::MultiLocation, OptionQuery>;259260 261 262 263 #[pallet::storage]264 #[pallet::getter(fn location_to_currency_ids)]265 pub type LocationToCurrencyIds<T: Config> =266 StorageMap<_, Twox64Concat, staging_xcm::v3::MultiLocation, ForeignAssetId, OptionQuery>;267268 269 270 271 #[pallet::storage]272 #[pallet::getter(fn asset_metadatas)]273 pub type AssetMetadatas<T: Config> =274 StorageMap<_, Twox64Concat, AssetId, AssetMetadata<BalanceOf<T>>, OptionQuery>;275276 277 278 #[pallet::storage]279 #[pallet::getter(fn asset_binding)]280 pub type AssetBinding<T: Config> =281 StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;282283 #[pallet::pallet]284 pub struct Pallet<T>(_);285286 #[pallet::call]287 impl<T: Config> Pallet<T> {288 #[pallet::call_index(0)]289 #[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]290 pub fn register_foreign_asset(291 origin: OriginFor<T>,292 owner: T::AccountId,293 location: Box<VersionedMultiLocation>,294 metadata: Box<AssetMetadata<BalanceOf<T>>>,295 ) -> DispatchResult {296 T::RegisterOrigin::ensure_origin(origin.clone())?;297298 let location: MultiLocation = (*location)299 .try_into()300 .map_err(|()| Error::<T>::BadLocation)?;301302 let md = metadata.clone();303 let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();304 let mut description: Vec<u16> = "Foreign assets collection for "305 .encode_utf16()306 .collect::<Vec<u16>>();307 description.append(&mut name.clone());308309 let data: CreateCollectionData<T::CrossAccountId> = CreateCollectionData {310 name: name.try_into().unwrap(),311 description: description.try_into().unwrap(),312 mode: CollectionMode::Fungible(md.decimals),313 ..Default::default()314 };315 let owner = T::CrossAccountId::from_sub(owner);316 let bounded_collection_id =317 <PalletFungible<T>>::init_foreign_collection(owner.clone(), owner, data)?;318 let foreign_asset_id =319 Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;320321 Self::deposit_event(Event::<T>::ForeignAssetRegistered {322 asset_id: foreign_asset_id,323 asset_address: location,324 metadata: *metadata,325 });326 Ok(())327 }328329 #[pallet::call_index(1)]330 #[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]331 pub fn update_foreign_asset(332 origin: OriginFor<T>,333 foreign_asset_id: ForeignAssetId,334 location: Box<VersionedMultiLocation>,335 metadata: Box<AssetMetadata<BalanceOf<T>>>,336 ) -> DispatchResult {337 T::RegisterOrigin::ensure_origin(origin)?;338339 let location: MultiLocation = (*location)340 .try_into()341 .map_err(|()| Error::<T>::BadLocation)?;342 Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;343344 Self::deposit_event(Event::<T>::ForeignAssetUpdated {345 asset_id: foreign_asset_id,346 asset_address: location,347 metadata: *metadata,348 });349 Ok(())350 }351 }352}353354impl<T: Config> Pallet<T> {355 fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {356 NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {357 let id = *current;358 *current = current359 .checked_add(One::one())360 .ok_or(ArithmeticError::Overflow)?;361 Ok(id)362 })363 }364365 fn do_register_foreign_asset(366 location: &MultiLocation,367 metadata: &AssetMetadata<BalanceOf<T>>,368 bounded_collection_id: CollectionId,369 ) -> Result<ForeignAssetId, DispatchError> {370 let foreign_asset_id = Self::get_next_foreign_asset_id()?;371 LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {372 ensure!(373 maybe_currency_ids.is_none(),374 Error::<T>::MultiLocationExisted375 );376 *maybe_currency_ids = Some(foreign_asset_id);377 378379 ForeignAssetLocations::<T>::try_mutate(380 foreign_asset_id,381 |maybe_location| -> DispatchResult {382 ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);383 *maybe_location = Some(*location);384385 AssetMetadatas::<T>::try_mutate(386 AssetId::ForeignAssetId(foreign_asset_id),387 |maybe_asset_metadatas| -> DispatchResult {388 ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);389 *maybe_asset_metadatas = Some(metadata.clone());390 Ok(())391 },392 )393 },394 )?;395396 AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {397 *collection_id = Some(bounded_collection_id);398 Ok(())399 })400 })?;401402 Ok(foreign_asset_id)403 }404405 fn do_update_foreign_asset(406 foreign_asset_id: ForeignAssetId,407 location: &MultiLocation,408 metadata: &AssetMetadata<BalanceOf<T>>,409 ) -> DispatchResult {410 ForeignAssetLocations::<T>::try_mutate(411 foreign_asset_id,412 |maybe_multi_locations| -> DispatchResult {413 let old_multi_locations = maybe_multi_locations414 .as_mut()415 .ok_or(Error::<T>::AssetIdNotExists)?;416417 AssetMetadatas::<T>::try_mutate(418 AssetId::ForeignAssetId(foreign_asset_id),419 |maybe_asset_metadatas| -> DispatchResult {420 ensure!(421 maybe_asset_metadatas.is_some(),422 Error::<T>::AssetIdNotExists423 );424425 426 if location != old_multi_locations {427 LocationToCurrencyIds::<T>::remove(*old_multi_locations);428 LocationToCurrencyIds::<T>::try_mutate(429 location,430 |maybe_currency_ids| -> DispatchResult {431 ensure!(432 maybe_currency_ids.is_none(),433 Error::<T>::MultiLocationExisted434 );435 436 *maybe_currency_ids = Some(foreign_asset_id);437 Ok(())438 },439 )?;440 }441 *maybe_asset_metadatas = Some(metadata.clone());442 *old_multi_locations = *location;443 Ok(())444 },445 )446 },447 )448 }449}450451pub use frame_support::{452 traits::{453 fungibles::Balanced, tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT,454 },455 weights::{WeightToFee, WeightToFeePolynomial},456};457458pub struct FreeForAll<459 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,460 AssetId: Get<MultiLocation>,461 AccountId,462 Currency: CurrencyT<AccountId>,463 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,464>(465 Weight,466 Currency::Balance,467 PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,468);469470impl<471 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,472 AssetId: Get<MultiLocation>,473 AccountId,474 Currency: CurrencyT<AccountId>,475 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,476 > WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>477{478 fn new() -> Self {479 Self(Weight::default(), Zero::zero(), PhantomData)480 }481482 fn buy_weight(483 &mut self,484 weight: Weight,485 payment: Assets,486 _xcm: &XcmContext,487 ) -> Result<Assets, XcmError> {488 log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);489 Ok(payment)490 }491}492impl<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> Drop493 for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>494where495 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,496 AssetId: Get<MultiLocation>,497 Currency: CurrencyT<AccountId>,498 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,499{500 fn drop(&mut self) {501 OnUnbalanced::on_unbalanced(Currency::issue(self.1));502 }503}