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;58use xcm::{v1::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;125mod weights;126127pub use module::*;128pub use weights::WeightInfo;129130131pub type BalanceOf<T> =132 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;133134135pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {136 137 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;138 139 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;140 141 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;142}143144pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);145146impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>147 for XcmForeignAssetIdMapping<T>148{149 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {150 log::trace!(target: "fassets::asset_metadatas", "call");151 Pallet::<T>::asset_metadatas(AssetIds::ForeignAssetId(foreign_asset_id))152 }153154 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {155 log::trace!(target: "fassets::get_multi_location", "call");156 Pallet::<T>::foreign_asset_locations(foreign_asset_id)157 }158159 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {160 log::trace!(target: "fassets::get_currency_id", "call");161 Some(AssetIds::ForeignAssetId(162 Pallet::<T>::location_to_currency_ids(multi_location).unwrap_or(0),163 ))164 }165}166167#[frame_support::pallet]168pub mod module {169 use super::*;170171 #[pallet::config]172 pub trait Config:173 frame_system::Config174 + pallet_common::Config175 + pallet_fungible::Config176 + orml_tokens::Config177 + pallet_balances::Config178 {179 180 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;181182 183 type Currency: Currency<Self::AccountId>;184185 186 type RegisterOrigin: EnsureOrigin<Self::Origin>;187188 189 type WeightInfo: WeightInfo;190 }191192 #[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo)]193 pub struct AssetMetadata<Balance> {194 pub name: Vec<u8>,195 pub symbol: Vec<u8>,196 pub decimals: u8,197 pub minimal_balance: Balance,198 }199200 #[pallet::error]201 pub enum Error<T> {202 203 204 BadLocation,205 206 MultiLocationExisted,207 208 AssetIdNotExists,209 210 AssetIdExisted,211 }212213 #[pallet::event]214 #[pallet::generate_deposit(fn deposit_event)]215 pub enum Event<T: Config> {216 217 ForeignAssetRegistered {218 asset_id: ForeignAssetId,219 asset_address: MultiLocation,220 metadata: AssetMetadata<BalanceOf<T>>,221 },222 223 ForeignAssetUpdated {224 asset_id: ForeignAssetId,225 asset_address: MultiLocation,226 metadata: AssetMetadata<BalanceOf<T>>,227 },228 229 AssetRegistered {230 asset_id: AssetIds,231 metadata: AssetMetadata<BalanceOf<T>>,232 },233 234 AssetUpdated {235 asset_id: AssetIds,236 metadata: AssetMetadata<BalanceOf<T>>,237 },238 }239240 241 242 243 #[pallet::storage]244 #[pallet::getter(fn next_foreign_asset_id)]245 pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;246 247 248 249 #[pallet::storage]250 #[pallet::getter(fn foreign_asset_locations)]251 pub type ForeignAssetLocations<T: Config> =252 StorageMap<_, Twox64Concat, ForeignAssetId, MultiLocation, OptionQuery>;253254 255 256 257 #[pallet::storage]258 #[pallet::getter(fn location_to_currency_ids)]259 pub type LocationToCurrencyIds<T: Config> =260 StorageMap<_, Twox64Concat, MultiLocation, ForeignAssetId, OptionQuery>;261262 263 264 265 #[pallet::storage]266 #[pallet::getter(fn asset_metadatas)]267 pub type AssetMetadatas<T: Config> =268 StorageMap<_, Twox64Concat, AssetIds, AssetMetadata<BalanceOf<T>>, OptionQuery>;269270 271 272 #[pallet::storage]273 #[pallet::getter(fn asset_binding)]274 pub type AssetBinding<T: Config> =275 StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;276277 #[pallet::pallet]278 #[pallet::without_storage_info]279 pub struct Pallet<T>(_);280281 #[pallet::call]282 impl<T: Config> Pallet<T> {283 #[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]284 pub fn register_foreign_asset(285 origin: OriginFor<T>,286 owner: T::AccountId,287 location: Box<VersionedMultiLocation>,288 metadata: Box<AssetMetadata<BalanceOf<T>>>,289 ) -> DispatchResult {290 T::RegisterOrigin::ensure_origin(origin.clone())?;291292 let location: MultiLocation = (*location)293 .try_into()294 .map_err(|()| Error::<T>::BadLocation)?;295296 let md = metadata.clone();297 let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();298 let mut description: Vec<u16> = "Foreign assets collection for "299 .encode_utf16()300 .collect::<Vec<u16>>();301 description.append(&mut name.clone());302303 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {304 name: name.try_into().unwrap(),305 description: description.try_into().unwrap(),306 mode: CollectionMode::Fungible(18),307 ..Default::default()308 };309310 let bounded_collection_id = <PalletFungible<T>>::init_foreign_collection(311 CrossAccountId::from_sub(owner),312 data,313 )?;314 let foreign_asset_id =315 Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;316317 Self::deposit_event(Event::<T>::ForeignAssetRegistered {318 asset_id: foreign_asset_id,319 asset_address: location,320 metadata: *metadata,321 });322 Ok(())323 }324325 #[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]326 pub fn update_foreign_asset(327 origin: OriginFor<T>,328 foreign_asset_id: ForeignAssetId,329 location: Box<VersionedMultiLocation>,330 metadata: Box<AssetMetadata<BalanceOf<T>>>,331 ) -> DispatchResult {332 T::RegisterOrigin::ensure_origin(origin)?;333334 let location: MultiLocation = (*location)335 .try_into()336 .map_err(|()| Error::<T>::BadLocation)?;337 Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;338339 Self::deposit_event(Event::<T>::ForeignAssetUpdated {340 asset_id: foreign_asset_id,341 asset_address: location,342 metadata: *metadata,343 });344 Ok(())345 }346 }347}348349impl<T: Config> Pallet<T> {350 fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {351 NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {352 let id = *current;353 *current = current354 .checked_add(One::one())355 .ok_or(ArithmeticError::Overflow)?;356 Ok(id)357 })358 }359360 fn do_register_foreign_asset(361 location: &MultiLocation,362 metadata: &AssetMetadata<BalanceOf<T>>,363 bounded_collection_id: CollectionId,364 ) -> Result<ForeignAssetId, DispatchError> {365 let foreign_asset_id = Self::get_next_foreign_asset_id()?;366 LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {367 ensure!(368 maybe_currency_ids.is_none(),369 Error::<T>::MultiLocationExisted370 );371 *maybe_currency_ids = Some(foreign_asset_id);372 373374 ForeignAssetLocations::<T>::try_mutate(375 foreign_asset_id,376 |maybe_location| -> DispatchResult {377 ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);378 *maybe_location = Some(location.clone());379380 AssetMetadatas::<T>::try_mutate(381 AssetIds::ForeignAssetId(foreign_asset_id),382 |maybe_asset_metadatas| -> DispatchResult {383 ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);384 *maybe_asset_metadatas = Some(metadata.clone());385 Ok(())386 },387 )388 },389 )?;390391 AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {392 *collection_id = Some(bounded_collection_id);393 Ok(())394 })395 })?;396397 Ok(foreign_asset_id)398 }399400 fn do_update_foreign_asset(401 foreign_asset_id: ForeignAssetId,402 location: &MultiLocation,403 metadata: &AssetMetadata<BalanceOf<T>>,404 ) -> DispatchResult {405 ForeignAssetLocations::<T>::try_mutate(406 foreign_asset_id,407 |maybe_multi_locations| -> DispatchResult {408 let old_multi_locations = maybe_multi_locations409 .as_mut()410 .ok_or(Error::<T>::AssetIdNotExists)?;411412 AssetMetadatas::<T>::try_mutate(413 AssetIds::ForeignAssetId(foreign_asset_id),414 |maybe_asset_metadatas| -> DispatchResult {415 ensure!(416 maybe_asset_metadatas.is_some(),417 Error::<T>::AssetIdNotExists418 );419420 421 if location != old_multi_locations {422 LocationToCurrencyIds::<T>::remove(old_multi_locations.clone());423 LocationToCurrencyIds::<T>::try_mutate(424 location,425 |maybe_currency_ids| -> DispatchResult {426 ensure!(427 maybe_currency_ids.is_none(),428 Error::<T>::MultiLocationExisted429 );430 431 *maybe_currency_ids = Some(foreign_asset_id);432 Ok(())433 },434 )?;435 }436 *maybe_asset_metadatas = Some(metadata.clone());437 *old_multi_locations = location.clone();438 Ok(())439 },440 )441 },442 )443 }444}445446pub use frame_support::{447 traits::{448 fungibles::{Balanced, CreditOf},449 tokens::currency::Currency as CurrencyT,450 OnUnbalanced as OnUnbalancedT,451 },452 weights::{WeightToFeePolynomial, WeightToFee},453};454455pub struct FreeForAll<456 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,457 AssetId: Get<MultiLocation>,458 AccountId,459 Currency: CurrencyT<AccountId>,460 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,461>(462 Weight,463 Currency::Balance,464 PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,465);466467impl<468 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,469 AssetId: Get<MultiLocation>,470 AccountId,471 Currency: CurrencyT<AccountId>,472 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,473 > WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>474{475 fn new() -> Self {476 Self(0, Zero::zero(), PhantomData)477 }478479 fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {480 log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);481 Ok(payment)482 }483}484impl<485 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,486 AssetId: Get<MultiLocation>,487 AccountId,488 Currency: CurrencyT<AccountId>,489 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,490> Drop for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>491{492 fn drop(&mut self) {493 OnUnbalanced::on_unbalanced(Currency::issue(self.1));494 }495}