difftreelog
fix(foreign-assets) put storage version in genesis config
in: master
1 file changed
pallets/foreign-assets/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Foreign Assets18//!19//! ## Overview20//!21//! The Foreign Assets is a proxy that maps XCM operations to the Unique Network's pallets logic.2223#![cfg_attr(not(feature = "std"), no_std)]24#![allow(clippy::unused_unit)]2526use core::ops::Deref;2728use frame_support::{29 dispatch::DispatchResult, pallet_prelude::*, storage_alias, traits::EnsureOrigin, PalletId,30};31use frame_system::pallet_prelude::*;32use pallet_common::{33 dispatch::CollectionDispatch, erc::CrossAccountId, XcmExtensions, NATIVE_FUNGIBLE_COLLECTION_ID,34};35use sp_runtime::traits::AccountIdConversion;36use sp_std::{boxed::Box, vec, vec::Vec};37use staging_xcm::{v4::prelude::*, VersionedAssetId};38use staging_xcm_executor::{39 traits::{ConvertLocation, Error as XcmExecutorError, TransactAsset, WeightTrader},40 AssetsInHolding,41};42use up_data_structs::{43 budget::ZeroBudget, CollectionFlags, CollectionId, CollectionMode, CollectionName,44 CollectionTokenPrefix, CreateCollectionData, CreateFungibleData, CreateItemData, TokenId,45};4647pub mod weights;4849#[cfg(feature = "runtime-benchmarks")]50mod benchmarking;5152pub use module::*;53pub use weights::WeightInfo;5455/// Status of storage migration from an old XCM version to a new one.56#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]57pub enum MigrationStatus {58 V3ToV4(MigrationStatusV3ToV4),59}6061/// Status of storage migration from XCMv3 to XCMv4.62#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]63pub enum MigrationStatusV3ToV4 {64 /// The migration is completed.65 Done,6667 /// An asset is skipped during the migration68 /// due to its inconsistent state.69 SkippedInconsistentAssetData(staging_xcm::v3::AssetId),7071 /// An asset instance is skipped during the migration72 /// due to its inconsistent state.73 SkippedInconsistentAssetInstanceData {74 asset_id: staging_xcm::v3::AssetId,75 asset_instance: staging_xcm::v3::AssetInstance,76 },7778 /// An asset is skipped during the migration79 /// because it couldn't be converted to the new XCM version.80 SkippedNotConvertibleAssetId(staging_xcm::v3::AssetId),8182 /// An asset instance is skipped during the migration83 /// because it couldn't be converted to the new XCM version.84 SkippedNotConvertibleAssetInstance {85 asset_id: staging_xcm::v3::AssetId,86 asset_instance: staging_xcm::v3::AssetInstance,87 },88}8990#[frame_support::pallet]91pub mod module {92 use frame_support::traits::BuildGenesisConfig;93 use pallet_common::CollectionIssuer;94 use up_data_structs::CollectionDescription;9596 use super::*;9798 #[pallet::config]99 pub trait Config:100 frame_system::Config101 + pallet_common::Config102 + pallet_fungible::Config103 + pallet_balances::Config104 {105 /// The overarching event type.106 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;107108 /// Origin for force registering of a foreign asset.109 type ForceRegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;110111 /// The ID of the foreign assets pallet.112 type PalletId: Get<PalletId>;113114 /// Self-location of this parachain.115 type SelfLocation: Get<Location>;116117 /// The converter from a Location to a CrossAccountId.118 type LocationToAccountId: ConvertLocation<Self::CrossAccountId>;119120 /// Weight information for the extrinsics in this module.121 type WeightInfo: WeightInfo;122 }123124 #[pallet::error]125 pub enum Error<T> {126 /// The foreign asset is already registered.127 ForeignAssetAlreadyRegistered,128129 /// The given asset ID could not be converted into the current XCM version.130 BadForeignAssetId,131 }132133 #[pallet::event]134 #[pallet::generate_deposit(pub(crate) fn deposit_event)]135 pub enum Event<T: Config> {136 /// The foreign asset registered.137 ForeignAssetRegistered {138 collection_id: CollectionId,139 asset_id: Box<VersionedAssetId>,140 },141142 /// The migration status.143 MigrationStatus(MigrationStatus),144 }145146 /// The corresponding collections of foreign assets.147 #[pallet::storage]148 #[pallet::getter(fn foreign_asset_to_collection)]149 pub type ForeignAssetToCollection<T: Config> =150 StorageMap<_, Blake2_128Concat, staging_xcm::v4::AssetId, CollectionId, OptionQuery>;151152 /// The corresponding foreign assets of collections.153 #[pallet::storage]154 #[pallet::getter(fn collection_to_foreign_asset)]155 pub type CollectionToForeignAsset<T: Config> =156 StorageMap<_, Blake2_128Concat, CollectionId, staging_xcm::v4::AssetId, OptionQuery>;157158 /// The correponding NFT token id of reserve NFTs159 #[pallet::storage]160 #[pallet::getter(fn foreign_reserve_asset_instance_to_token_id)]161 pub type ForeignReserveAssetInstanceToTokenId<T: Config> = StorageDoubleMap<162 Hasher1 = Blake2_128Concat,163 Key1 = CollectionId,164 Hasher2 = Blake2_128Concat,165 Key2 = staging_xcm::v4::AssetInstance,166 Value = TokenId,167 QueryKind = OptionQuery,168 >;169170 /// The correponding reserve NFT of a token ID171 #[pallet::storage]172 #[pallet::getter(fn token_id_to_foreign_reserve_asset_instance)]173 pub type TokenIdToForeignReserveAssetInstance<T: Config> = StorageDoubleMap<174 Hasher1 = Blake2_128Concat,175 Key1 = CollectionId,176 Hasher2 = Blake2_128Concat,177 Key2 = TokenId,178 Value = staging_xcm::v4::AssetInstance,179 QueryKind = OptionQuery,180 >;181182 const STORAGE_VERSION: StorageVersion = StorageVersion::new(staging_xcm::v4::VERSION as u16);183184 #[pallet::pallet]185 #[pallet::storage_version(STORAGE_VERSION)]186 pub struct Pallet<T>(_);187188 #[pallet::call]189 impl<T: Config> Pallet<T> {190 #[pallet::call_index(0)]191 #[pallet::weight(<T as Config>::WeightInfo::force_register_foreign_asset())]192 pub fn force_register_foreign_asset(193 origin: OriginFor<T>,194 versioned_asset_id: Box<VersionedAssetId>,195 name: CollectionName,196 token_prefix: CollectionTokenPrefix,197 mode: ForeignCollectionMode,198 ) -> DispatchResult {199 T::ForceRegisterOrigin::ensure_origin(origin.clone())?;200201 let asset_id: AssetId = versioned_asset_id202 .as_ref()203 .clone()204 .try_into()205 .map_err(|()| Error::<T>::BadForeignAssetId)?;206207 ensure!(208 !<ForeignAssetToCollection<T>>::contains_key(&asset_id),209 <Error<T>>::ForeignAssetAlreadyRegistered,210 );211212 let foreign_collection_owner = Self::pallet_account();213214 let description: CollectionDescription = "Foreign Assets Collection"215 .encode_utf16()216 .collect::<Vec<_>>()217 .try_into()218 .expect("description length < max description length; qed");219220 let collection_id = T::CollectionDispatch::create(221 foreign_collection_owner,222 CollectionIssuer::Internals,223 CreateCollectionData {224 name,225 token_prefix,226 description,227 mode: mode.into(),228 flags: CollectionFlags {229 foreign: true,230 ..Default::default()231 },232 ..Default::default()233 },234 )?;235236 <ForeignAssetToCollection<T>>::insert(&asset_id, collection_id);237 <CollectionToForeignAsset<T>>::insert(collection_id, asset_id);238239 Self::deposit_event(Event::<T>::ForeignAssetRegistered {240 collection_id,241 asset_id: versioned_asset_id,242 });243244 Ok(())245 }246 }247248 #[pallet::genesis_config]249 #[derive(Default)]250 pub struct GenesisConfig<T: Config>(PhantomData<T>);251252 #[pallet::genesis_build]253 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {254 fn build(&self) {255 <Pallet<T>>::in_code_storage_version().put::<Pallet<T>>();256 }257 }258259 #[pallet::hooks]260 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {261 fn on_runtime_upgrade() -> Weight {262 if Self::on_chain_storage_version() < staging_xcm::v4::VERSION as u16 {263 let put_version_weight = T::DbWeight::get().writes(1);264 let fix_foreign_flag_weight = Self::fix_foreign_flag();265 let weight_v3_to_v4 = Self::migrate_v3_to_v4();266267 Self::in_code_storage_version().put::<Self>();268269 put_version_weight270 .saturating_add(fix_foreign_flag_weight)271 .saturating_add(weight_v3_to_v4)272 } else {273 Weight::zero()274 }275 }276 }277}278279mod v3_storage {280 use super::*;281282 #[storage_alias]283 pub type ForeignAssetToCollection<T: Config> =284 StorageMap<Pallet<T>, Twox64Concat, staging_xcm::v3::AssetId, CollectionId, OptionQuery>;285286 #[storage_alias]287 pub type CollectionToForeignAsset<T: Config> =288 StorageMap<Pallet<T>, Twox64Concat, CollectionId, staging_xcm::v3::AssetId, OptionQuery>;289290 #[storage_alias]291 pub type ForeignReserveAssetInstanceToTokenId<T: Config> = StorageDoubleMap<292 Pallet<T>,293 Twox64Concat,294 CollectionId,295 Blake2_128Concat,296 staging_xcm::v3::AssetInstance,297 TokenId,298 OptionQuery,299 >;300301 #[storage_alias]302 pub type TokenIdToForeignReserveAssetInstance<T: Config> = StorageDoubleMap<303 Pallet<T>,304 Twox64Concat,305 CollectionId,306 Blake2_128Concat,307 TokenId,308 staging_xcm::v3::AssetInstance,309 OptionQuery,310 >;311}312313impl<T: Config> Pallet<T> {314 fn fix_foreign_flag() -> Weight {315 let mut weight = Weight::zero();316317 for (_, collection_id) in v3_storage::ForeignAssetToCollection::<T>::iter() {318 pallet_common::CollectionById::<T>::mutate(collection_id, |collection| {319 if let Some(collection) = collection {320 collection.flags.foreign = true;321 }322 });323324 weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 1));325 }326327 weight328 }329330 fn migrate_v3_to_v4() -> Weight {331 let event_weight = T::DbWeight::get().writes(1);332 let collection_migration_weight = Self::migrate_collections();333334 Self::deposit_event(Event::<T>::MigrationStatus(MigrationStatus::V3ToV4(335 MigrationStatusV3ToV4::Done,336 )));337338 collection_migration_weight.saturating_add(event_weight)339 }340341 fn migrate_collections() -> Weight {342 use MigrationStatus::*;343 use MigrationStatusV3ToV4::*;344345 let mut weight = Weight::zero();346347 for (fwd_asset_id, collection_id) in v3_storage::ForeignAssetToCollection::<T>::drain() {348 let bwd_asset_id = v3_storage::CollectionToForeignAsset::<T>::take(collection_id);349 weight = weight.saturating_add(T::DbWeight::get().reads(2));350351 let Some(bwd_asset_id) = bwd_asset_id else {352 Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(353 SkippedInconsistentAssetData(fwd_asset_id),354 )));355356 weight = weight.saturating_add(T::DbWeight::get().writes(1));357 continue;358 };359360 if fwd_asset_id != bwd_asset_id {361 Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(362 SkippedInconsistentAssetData(fwd_asset_id),363 )));364365 weight = weight.saturating_add(T::DbWeight::get().writes(1));366 continue;367 }368369 let Ok(asset_id) = staging_xcm::v4::AssetId::try_from(fwd_asset_id) else {370 Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(371 SkippedNotConvertibleAssetId(fwd_asset_id),372 )));373374 weight = weight.saturating_add(T::DbWeight::get().writes(1));375 continue;376 };377378 <ForeignAssetToCollection<T>>::insert(&asset_id, collection_id);379 <CollectionToForeignAsset<T>>::insert(collection_id, asset_id);380 weight = weight.saturating_add(T::DbWeight::get().writes(2));381382 let migrate_tokens_weight = Self::migrate_tokens(&fwd_asset_id, collection_id);383 weight = weight.saturating_add(migrate_tokens_weight);384 }385386 weight387 }388389 fn migrate_tokens(asset_id: &staging_xcm::v3::AssetId, collection_id: CollectionId) -> Weight {390 use MigrationStatus::*;391 use MigrationStatusV3ToV4::*;392393 let mut weight = Weight::zero();394395 for (fwd_asset_instance, token_id) in396 v3_storage::ForeignReserveAssetInstanceToTokenId::<T>::drain_prefix(collection_id)397 {398 let bwd_asset_instance = v3_storage::TokenIdToForeignReserveAssetInstance::<T>::take(399 collection_id,400 token_id,401 );402 weight = weight.saturating_add(T::DbWeight::get().reads(2));403404 let Some(bwd_asset_instance) = bwd_asset_instance else {405 Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(406 SkippedInconsistentAssetInstanceData {407 asset_id: *asset_id,408 asset_instance: fwd_asset_instance,409 },410 )));411412 weight = weight.saturating_add(T::DbWeight::get().writes(1));413 continue;414 };415416 if fwd_asset_instance != bwd_asset_instance {417 Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(418 SkippedInconsistentAssetInstanceData {419 asset_id: *asset_id,420 asset_instance: fwd_asset_instance,421 },422 )));423424 weight = weight.saturating_add(T::DbWeight::get().writes(1));425 continue;426 }427428 let Ok(asset_instance) = staging_xcm::v4::AssetInstance::try_from(fwd_asset_instance)429 else {430 Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(431 SkippedNotConvertibleAssetInstance {432 asset_id: *asset_id,433 asset_instance: fwd_asset_instance,434 },435 )));436437 weight = weight.saturating_add(T::DbWeight::get().writes(1));438 continue;439 };440441 <ForeignReserveAssetInstanceToTokenId<T>>::insert(442 collection_id,443 &asset_instance,444 token_id,445 );446 <TokenIdToForeignReserveAssetInstance<T>>::insert(447 collection_id,448 token_id,449 asset_instance,450 );451 weight = weight.saturating_add(T::DbWeight::get().writes(2));452 }453454 weight455 }456457 fn pallet_account() -> T::CrossAccountId {458 let owner: T::AccountId = T::PalletId::get().into_account_truncating();459 T::CrossAccountId::from_sub(owner)460 }461462 /// Converts a concrete asset ID (the asset multilocation) to a local collection on Unique Network.463 ///464 /// The multilocation corresponds to a local collection if:465 /// * It is `Here` location that corresponds to the native token of this parachain.466 /// * It is `../Parachain(<Unique Network Para ID>)` that also corresponds to the native token of this parachain.467 /// * It is `../Parachain(<Unique Network Para ID>)/GeneralIndex(<Collection ID>)` that corresponds468 /// to the collection with the ID equal to `<Collection ID>`. The `<Collection ID>` must be in the valid range,469 /// otherwise `None` is returned.470 /// * It is `GeneralIndex(<Collection ID>)`. Same as the last one above.471 ///472 /// If the multilocation doesn't match the patterns listed above,473 /// or the `<Collection ID>` points to a foreign collection,474 /// `None` is returned, identifying that the given multilocation doesn't correspond to a local collection.475 fn local_asset_id_to_collection(476 AssetId(asset_location): &AssetId,477 ) -> Option<CollectionLocality> {478 let self_location = T::SelfLocation::get();479480 if *asset_location == Here.into() || *asset_location == self_location {481 return Some(CollectionLocality::Local(NATIVE_FUNGIBLE_COLLECTION_ID));482 }483484 let prefix = if asset_location.parents == 0 {485 &Here486 } else if asset_location.parents == self_location.parents {487 &self_location.interior488 } else {489 return None;490 };491492 let GeneralIndex(collection_id) = asset_location.interior.match_and_split(prefix)? else {493 return None;494 };495496 let collection_id = CollectionId((*collection_id).try_into().ok()?);497498 Self::collection_to_foreign_asset(collection_id)499 .is_none()500 .then_some(CollectionLocality::Local(collection_id))501 }502503 /// Converts an asset ID to a Unique Network's collection locality (either foreign or a local one).504 ///505 /// The function will check if the asset's reserve location has the corresponding506 /// foreign collection on Unique Network,507 /// and will return the "foreign" locality containing the collection ID if found.508 ///509 /// If no corresponding foreign collection is found, the function will check510 /// if the asset's reserve location corresponds to a local collection.511 /// If the local collection is found, the "local" locality with the collection ID is returned.512 ///513 /// If all of the above have failed, the `AssetIdConversionFailed` error will be returned.514 fn asset_to_collection(asset_id: &AssetId) -> Result<CollectionLocality, XcmError> {515 Self::foreign_asset_to_collection(asset_id)516 .map(CollectionLocality::Foreign)517 .or_else(|| Self::local_asset_id_to_collection(asset_id))518 .ok_or_else(|| XcmExecutorError::AssetIdConversionFailed.into())519 }520521 /// Converts an XCM asset instance of local collection to the Unique Network's token ID.522 ///523 /// The asset instance corresponds to the Unique Network's token ID if it is in the following format:524 /// `AssetInstance::Index(<token ID>)`.525 ///526 /// If the asset instance is not in the valid format or the `<token ID>` can't fit into the valid token ID,527 /// `None` will be returned.528 ///529 /// Note: this function can return `Some` containing the token ID of a non-existing NFT.530 /// It returns `None` when it failed to convert the `asset_instance` to a local ID.531 fn local_asset_instance_to_token_id(asset_instance: &AssetInstance) -> Option<TokenId> {532 match asset_instance {533 AssetInstance::Index(token_id) => Some(TokenId((*token_id).try_into().ok()?)),534 _ => None,535 }536 }537538 /// Obtains the token ID of the `asset_instance` in the collection.539 ///540 /// Note: this function can return `Some` containing the token ID of a non-existing NFT.541 /// It returns `None` when it failed to convert the `asset_instance` to a local ID.542 fn asset_instance_to_token_id(543 collection_locality: CollectionLocality,544 asset_instance: &AssetInstance,545 ) -> Option<TokenId> {546 match collection_locality {547 CollectionLocality::Local(_) => Self::local_asset_instance_to_token_id(asset_instance),548 CollectionLocality::Foreign(collection_id) => {549 Self::foreign_reserve_asset_instance_to_token_id(collection_id, asset_instance)550 }551 }552 }553554 /// Creates a foreign item in the the collection.555 fn create_foreign_asset_instance(556 xcm_ext: &dyn XcmExtensions<T>,557 collection_id: CollectionId,558 asset_instance: &AssetInstance,559 to: T::CrossAccountId,560 ) -> DispatchResult {561 let derivative_token_id = xcm_ext.create_item(562 &Self::pallet_account(),563 to,564 CreateItemData::NFT(Default::default()),565 &ZeroBudget,566 )?;567568 <ForeignReserveAssetInstanceToTokenId<T>>::insert(569 collection_id,570 asset_instance,571 derivative_token_id,572 );573574 <TokenIdToForeignReserveAssetInstance<T>>::insert(575 collection_id,576 derivative_token_id,577 asset_instance,578 );579580 Ok(())581 }582583 /// Deposits an asset instance to the `to` account.584 ///585 /// Either transfers an existing item from the pallet's account586 /// or creates a foreign item.587 fn deposit_asset_instance(588 xcm_ext: &dyn XcmExtensions<T>,589 collection_locality: CollectionLocality,590 asset_instance: &AssetInstance,591 to: T::CrossAccountId,592 ) -> XcmResult {593 let token_id = Self::asset_instance_to_token_id(collection_locality, asset_instance);594595 let deposit_result = match (collection_locality, token_id) {596 (_, Some(token_id)) => {597 let depositor = &Self::pallet_account();598 let from = depositor;599 let amount = 1;600601 xcm_ext.transfer_item(depositor, from, &to, token_id, amount, &ZeroBudget)602 }603 (CollectionLocality::Foreign(collection_id), None) => {604 Self::create_foreign_asset_instance(xcm_ext, collection_id, asset_instance, to)605 }606 (CollectionLocality::Local(_), None) => {607 return Err(XcmExecutorError::InstanceConversionFailed.into());608 }609 };610611 deposit_result612 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))613 }614615 /// Withdraws an asset instance from the `from` account.616 ///617 /// Transfers the asset instance to the pallet's account.618 fn withdraw_asset_instance(619 xcm_ext: &dyn XcmExtensions<T>,620 collection_locality: CollectionLocality,621 asset_instance: &AssetInstance,622 from: T::CrossAccountId,623 ) -> XcmResult {624 let token_id = Self::asset_instance_to_token_id(collection_locality, asset_instance)625 .ok_or(XcmExecutorError::InstanceConversionFailed)?;626627 let depositor = &from;628 let to = Self::pallet_account();629 let amount = 1;630 xcm_ext631 .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)632 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item withdraw failed"))?;633634 Ok(())635 }636}637638// #[derive()]639// pub enum Migration {640641// }642643impl<T: Config> TransactAsset for Pallet<T> {644 fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {645 Err(XcmError::Unimplemented)646 }647648 fn check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) {}649650 fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {651 Err(XcmError::Unimplemented)652 }653654 fn check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) {}655656 fn deposit_asset(what: &Asset, to: &Location, _context: Option<&XcmContext>) -> XcmResult {657 let to = T::LocationToAccountId::convert_location(to)658 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;659660 let collection_locality = Self::asset_to_collection(&what.id)?;661 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)662 .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?;663664 let collection = dispatch.as_dyn();665 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::Unimplemented)?;666667 match what.fun {668 Fungibility::Fungible(amount) => xcm_ext669 .create_item(670 &Self::pallet_account(),671 to,672 CreateItemData::Fungible(CreateFungibleData { value: amount }),673 &ZeroBudget,674 )675 .map(|_| ())676 .map_err(|_| XcmError::FailedToTransactAsset("fungible item deposit failed")),677678 Fungibility::NonFungible(asset_instance) => {679 Self::deposit_asset_instance(xcm_ext, collection_locality, &asset_instance, to)680 }681 }682 }683684 fn withdraw_asset(685 what: &Asset,686 from: &Location,687 _maybe_context: Option<&XcmContext>,688 ) -> Result<AssetsInHolding, XcmError> {689 let from = T::LocationToAccountId::convert_location(from)690 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;691692 let collection_locality = Self::asset_to_collection(&what.id)?;693 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)694 .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?;695696 let collection = dispatch.as_dyn();697 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;698699 match what.fun {700 Fungibility::Fungible(amount) => xcm_ext701 .burn_item(from, TokenId::default(), amount)702 .map_err(|_| XcmError::FailedToTransactAsset("fungible item withdraw failed"))?,703704 Fungibility::NonFungible(asset_instance) => {705 Self::withdraw_asset_instance(xcm_ext, collection_locality, &asset_instance, from)?;706 }707 }708709 Ok(what.clone().into())710 }711712 fn internal_transfer_asset(713 what: &Asset,714 from: &Location,715 to: &Location,716 _context: &XcmContext,717 ) -> Result<AssetsInHolding, XcmError> {718 let from = T::LocationToAccountId::convert_location(from)719 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;720721 let to = T::LocationToAccountId::convert_location(to)722 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;723724 let collection_locality = Self::asset_to_collection(&what.id)?;725726 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)727 .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?;728 let collection = dispatch.as_dyn();729 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;730731 let depositor = &from;732733 let token_id;734 let amount;735 let map_error: fn(DispatchError) -> XcmError;736737 match what.fun {738 Fungibility::Fungible(fungible_amount) => {739 token_id = TokenId::default();740 amount = fungible_amount;741 map_error = |_| XcmError::FailedToTransactAsset("fungible item transfer failed");742 }743744 Fungibility::NonFungible(asset_instance) => {745 token_id = Self::asset_instance_to_token_id(collection_locality, &asset_instance)746 .ok_or(XcmExecutorError::InstanceConversionFailed)?;747748 amount = 1;749 map_error = |_| XcmError::FailedToTransactAsset("non-fungible item transfer failed")750 }751 }752753 xcm_ext754 .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)755 .map_err(map_error)?;756757 Ok(what.clone().into())758 }759}760761#[derive(Clone, Copy)]762pub enum CollectionLocality {763 Local(CollectionId),764 Foreign(CollectionId),765}766767impl Deref for CollectionLocality {768 type Target = CollectionId;769770 fn deref(&self) -> &Self::Target {771 match self {772 Self::Local(id) => id,773 Self::Foreign(id) => id,774 }775 }776}777778pub struct CurrencyIdConvert<T: Config>(PhantomData<T>);779impl<T: Config> sp_runtime::traits::Convert<CollectionId, Option<Location>>780 for CurrencyIdConvert<T>781{782 fn convert(collection_id: CollectionId) -> Option<Location> {783 if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {784 Some(T::SelfLocation::get())785 } else {786 <Pallet<T>>::collection_to_foreign_asset(collection_id)787 .map(|AssetId(location)| location)788 .or_else(|| {789 T::SelfLocation::get()790 .pushed_with_interior(GeneralIndex(collection_id.0.into()))791 .ok()792 })793 }794 }795}796797#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]798pub enum ForeignCollectionMode {799 NFT,800 Fungible(u8),801}802803impl From<ForeignCollectionMode> for CollectionMode {804 fn from(value: ForeignCollectionMode) -> Self {805 match value {806 ForeignCollectionMode::NFT => Self::NFT,807 ForeignCollectionMode::Fungible(decimals) => Self::Fungible(decimals),808 }809 }810}811812pub struct FreeForAll;813814impl WeightTrader for FreeForAll {815 fn new() -> Self {816 Self817 }818819 fn buy_weight(820 &mut self,821 weight: Weight,822 payment: AssetsInHolding,823 _xcm: &XcmContext,824 ) -> Result<AssetsInHolding, XcmError> {825 log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);826 Ok(payment)827 }828}