difftreelog
feat draft xcm deposit_asset
in: master
8 files changed
pallets/balances-adapter/src/common.rsdiffbeforeafterboth334use frame_support::{ensure, fail, weights::Weight};4use frame_support::{5 ensure, fail,6 traits::tokens::{fungible::Mutate, Fortitude, Precision},7 weights::Weight,8};5use pallet_balances::{weights::SubstrateWeight as BalancesWeight, WeightInfo};9use pallet_balances::{weights::SubstrateWeight as BalancesWeight, WeightInfo};6use pallet_common::{CommonCollectionOperations, CommonWeightInfo, Error as CommonError};10use pallet_common::{11 erc::CrossAccountId, CommonCollectionOperations, CommonWeightInfo, Error as CommonError,12};7use up_data_structs::TokenId;13use up_data_structs::{budget::Budget, TokenId};8149use crate::{Config, NativeFungibleHandle, Pallet};15use crate::{Config, NativeFungibleHandle, Pallet};1016332 0338 0333 }339 }340341 fn xcm_extensions(&self) -> Option<&dyn pallet_common::XcmExtensions<T>> {342 Some(self)343 }334344335 fn set_allowance_for_all(345 fn set_allowance_for_all(336 &self,346 &self,357 }367 }358}368}369370impl<T: Config> pallet_common::XcmExtensions<T> for NativeFungibleHandle<T> {371 fn is_foreign(&self) -> bool {372 false373 }374375 fn create_item_internal(376 &self,377 _depositor: &<T>::CrossAccountId,378 to: <T>::CrossAccountId,379 data: up_data_structs::CreateItemData,380 _nesting_budget: &dyn Budget,381 ) -> Result<TokenId, sp_runtime::DispatchError> {382 match &data {383 up_data_structs::CreateItemData::Fungible(fungible_data) => {384 T::Mutate::mint_into(385 to.as_sub(),386 fungible_data387 .value388 .try_into()389 .map_err(|_| sp_runtime::ArithmeticError::Overflow)?,390 )?;391392 Ok(TokenId::default())393 }394 _ => {395 fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken)396 }397 }398 }399400 fn transfer_item_internal(401 &self,402 _depositor: &<T>::CrossAccountId,403 from: &<T>::CrossAccountId,404 to: &<T>::CrossAccountId,405 token: TokenId,406 amount: u128,407 _nesting_budget: &dyn Budget,408 ) -> sp_runtime::DispatchResult {409 ensure!(410 token == TokenId::default(),411 <CommonError<T>>::FungibleItemsHaveNoId412 );413414 <Pallet<T>>::transfer(from, to, amount)415 .map(|_| ())416 .map_err(|post_info| post_info.error)417 }418419 fn burn_item_internal(420 &self,421 from: T::CrossAccountId,422 token: TokenId,423 amount: u128,424 ) -> sp_runtime::DispatchResult {425 ensure!(426 token == TokenId::default(),427 <CommonError<T>>::FungibleItemsHaveNoId428 );429430 T::Mutate::burn_from(431 from.as_sub(),432 amount433 .try_into()434 .map_err(|_| sp_runtime::ArithmeticError::Overflow)?,435 Precision::Exact,436 Fortitude::Polite,437 )?;438439 Ok(())440 }441}359442pallets/common/src/lib.rsdiffbeforeafterboth80use sp_std::vec::Vec;80use sp_std::vec::Vec;81use sp_weights::Weight;81use sp_weights::Weight;82use up_data_structs::{82use up_data_structs::{83 budget::Budget, AccessMode, Collection, CollectionId, CollectionLimits, CollectionMode,83 budget::Budget, mapping::TokenAddressMapping, AccessMode, Collection, CollectionId,84 CollectionPermissions, CollectionProperties as CollectionPropertiesT, CollectionStats,84 CollectionLimits, CollectionMode, CollectionPermissions,85 CreateCollectionData, CreateItemData, CreateItemExData, PhantomType, PropertiesError,85 CollectionProperties as CollectionPropertiesT, CollectionStats, CreateCollectionData,86 PropertiesPermissionMap, Property, PropertyKey, PropertyKeyPermission, PropertyPermission,86 CreateItemData, CreateItemExData, PhantomType, PropertiesError, PropertiesPermissionMap,787 /// Fungible tokens hold no ID, and the default value of TokenId for a fungible collection is 0.788 /// Fungible tokens hold no ID, and the default value of TokenId for a fungible collection is 0.788 FungibleItemsHaveNoId,789 FungibleItemsHaveNoId,790791 /// Not Fungible item data used to mint in Fungible collection.792 NotFungibleDataUsedToMintFungibleCollectionToken,789 }793 }790794791 /// Storage of the count of created collections. Essentially contains the last collection ID.795 /// Storage of the count of created collections. Essentially contains the last collection ID.2347 /// Is the collection a foreign one?2351 /// Is the collection a foreign one?2348 fn is_foreign(&self) -> bool;2352 fn is_foreign(&self) -> bool;234923532350 /// Create a collection's item.2354 /// Create a collection's item using a transaction.2355 ///2356 /// This function performs additional XCM-related checks before the actual creation.2357 #[transactional]2351 fn create_item(2358 fn create_item(2352 &self,2359 &self,2360 depositor: &T::CrossAccountId,2353 to: T::CrossAccountId,2361 to: T::CrossAccountId,2354 data: CreateItemData,2362 data: CreateItemData,2363 nesting_budget: &dyn Budget,2355 ) -> Result<TokenId, DispatchError>;2364 ) -> Result<TokenId, DispatchError> {2365 if T::CrossTokenAddressMapping::is_token_address(&to) {2366 return unsupported!(T);2367 }23682369 self.create_item_internal(depositor, to, data, nesting_budget)2370 }23712372 /// Create a collection's item.2373 fn create_item_internal(2374 &self,2375 depositor: &T::CrossAccountId,2376 to: T::CrossAccountId,2377 data: CreateItemData,2378 nesting_budget: &dyn Budget,2379 ) -> Result<TokenId, DispatchError>;23802381 /// Transfer an item from the `from` account to the `to` account using a transaction.2382 ///2383 /// This function performs additional XCM-related checks before the actual transfer.2384 #[transactional]2385 fn transfer_item(2386 &self,2387 depositor: &T::CrossAccountId,2388 from: &T::CrossAccountId,2389 to: &T::CrossAccountId,2390 token: TokenId,2391 amount: u128,2392 nesting_budget: &dyn Budget,2393 ) -> DispatchResult {2394 if T::CrossTokenAddressMapping::is_token_address(&to) {2395 return unsupported!(T);2396 }23972398 self.transfer_item_internal(depositor, from, to, token, amount, nesting_budget)2399 }235624002357 /// Transfer an item from the `from` account to the `to` account.2401 /// Transfer an item from the `from` account to the `to` account.2358 fn transfer(2402 fn transfer_item_internal(2359 &self,2403 &self,2404 depositor: &T::CrossAccountId,2360 from: T::CrossAccountId,2405 from: &T::CrossAccountId,2361 to: T::CrossAccountId,2406 to: &T::CrossAccountId,2362 token: TokenId,2407 token: TokenId,2363 amount: u128,2408 amount: u128,2409 nesting_budget: &dyn Budget,2364 ) -> DispatchResult;2410 ) -> DispatchResult;24112412 /// Burn a collection's item using a transaction.2413 #[transactional]2414 fn burn_item(&self, from: T::CrossAccountId, token: TokenId, amount: u128) -> DispatchResult {2415 self.burn_item_internal(from, token, amount)2416 }236524172366 /// Burn a collection's item.2418 /// Burn a collection's item.2367 fn burn(&self, from: T::CrossAccountId, token: TokenId, amount: u128) -> DispatchResult;2419 fn burn_item_internal(2420 &self,2421 from: T::CrossAccountId,2422 token: TokenId,pallets/foreign-assets/src/lib.rsdiffbeforeafterboth20//! - [`Call`]20//! - [`Call`]21//! - [`Pallet`]21//! - [`Pallet`]22//!22//!23//! ## Overview24//!25//! The foreign assests pallet provides functions for:26//!27//! - Local and foreign assets management. The foreign assets can be updated without runtime upgrade.28//! - Bounds between asset and target collection for cross chain transfer and inner transfers.29//!30//! ## Overview23//! ## Overview31//!24//!32//! Under construction25//! Under construction37use frame_support::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin, PalletId};30use frame_support::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin, PalletId};38use frame_system::pallet_prelude::*;31use frame_system::pallet_prelude::*;39use pallet_common::{32use pallet_common::{40 dispatch::CollectionDispatch, erc::CrossAccountId, NATIVE_FUNGIBLE_COLLECTION_ID,33 dispatch::CollectionDispatch, erc::CrossAccountId, XcmExtensions, NATIVE_FUNGIBLE_COLLECTION_ID,41};34};42use sp_runtime::traits::AccountIdConversion;35use sp_runtime::traits::AccountIdConversion;43use sp_std::{vec, vec::Vec};36use sp_std::{vec, vec::Vec};44// NOTE: MultiLocation is used in storages, we will need to do migration if upgrade the45// MultiLocation to the XCM v3.46use staging_xcm::{37use staging_xcm::{47 opaque::latest::{prelude::XcmError, Weight},38 opaque::latest::{prelude::XcmError, Weight},48 v3::{prelude::*, MultiAsset, XcmContext},39 v3::{prelude::*, MultiAsset, XcmContext},49};40};50use staging_xcm_executor::{41use staging_xcm_executor::{51 traits::{TransactAsset, WeightTrader},42 traits::{ConvertLocation, Error as XcmExecutorError, TransactAsset, WeightTrader},52 Assets,43 Assets,53};44};54use up_data_structs::{45use up_data_structs::{55 CollectionId, CollectionMode, CollectionName, CreateCollectionData, PropertyKey, TokenId,46 budget::ZeroBudget, CollectionId, CollectionMode, CollectionName, CreateCollectionData,47 CreateFungibleData, CreateItemData, CreateNftData, Property, PropertyKey, TokenId,56};48};574958pub mod weights;50pub mod weights;87 /// The ID of the foreign assets pallet.79 /// The ID of the foreign assets pallet.88 type PalletId: Get<PalletId>;80 type PalletId: Get<PalletId>;8182 /// Self-location of this parachain.83 type SelfLocation: Get<MultiLocation>;8485 /// The converter from a MultiLocation to a CrossAccountId.86 type LocationToAccountId: ConvertLocation<Self::CrossAccountId>;898790 /// Weight information for the extrinsics in this module.88 /// Weight information for the extrinsics in this module.91 type WeightInfo: WeightInfo;89 type WeightInfo: WeightInfo;113 pub type ForeignReserveLocationToCollection<T: Config> =111 pub type ForeignReserveLocationToCollection<T: Config> =114 StorageMap<_, Twox64Concat, staging_xcm::v3::MultiLocation, CollectionId, OptionQuery>;112 StorageMap<_, Twox64Concat, staging_xcm::v3::MultiLocation, CollectionId, OptionQuery>;113114 /// The corresponding reserve location of collections.115 #[pallet::storage]116 #[pallet::getter(fn collection_to_foreign_reserve_location)]117 pub type CollectionToForeignReserveLocation<T: Config> =118 StorageMap<_, Twox64Concat, CollectionId, staging_xcm::v3::MultiLocation, OptionQuery>;115119116 /// The correponding NFT token id of reserve NFTs120 /// The correponding NFT token id of reserve NFTs117 #[pallet::storage]121 #[pallet::storage]180 )?;184 )?;181185182 <ForeignReserveLocationToCollection<T>>::insert(reserve_location, collection_id);186 <ForeignReserveLocationToCollection<T>>::insert(reserve_location, collection_id);187 <CollectionToForeignReserveLocation<T>>::insert(collection_id, reserve_location);183188184 Self::deposit_event(Event::<T>::ForeignAssetRegistered {189 Self::deposit_event(Event::<T>::ForeignAssetRegistered {185 asset_id: collection_id,190 asset_id: collection_id,211 .expect("key length < max property key length; qed")216 .expect("key length < max property key length; qed")212 }217 }218219 fn native_asset_location_to_collection(220 asset_location: &MultiLocation,221 ) -> Result<Option<CollectionId>, XcmError> {222 let self_location = T::SelfLocation::get();223224 if *asset_location == Here.into() {225 Ok(Some(NATIVE_FUNGIBLE_COLLECTION_ID))226 } else if *asset_location == self_location {227 Ok(Some(NATIVE_FUNGIBLE_COLLECTION_ID))228 } else if asset_location.parents == self_location.parents {229 match asset_location230 .interior231 .match_and_split(&self_location.interior)232 {233 Some(GeneralIndex(collection_id)) => Ok(Some(CollectionId(234 (*collection_id)235 .try_into()236 .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?,237 ))),238 _ => Ok(None),239 }240 } else {241 Ok(None)242 }243 }244245 fn multiasset_to_collection(asset: &MultiAsset) -> Result<CollectionId, XcmError> {246 let AssetId::Concrete(asset_reserve_location) = asset.id else {247 return Err(XcmExecutorError::AssetNotHandled.into());248 };249250 Self::native_asset_location_to_collection(&asset_reserve_location)?251 .or_else(|| Self::foreign_reserve_location_to_collection(asset_reserve_location))252 .ok_or_else(|| XcmExecutorError::AssetIdConversionFailed.into())253 }254255 fn native_asset_instance_to_token_id(256 asset_instance: &AssetInstance,257 ) -> Result<TokenId, XcmError> {258 match asset_instance {259 AssetInstance::Index(token_id) => Ok(TokenId(260 (*token_id)261 .try_into()262 .map_err(|_| XcmError::AssetNotFound)?,263 )),264 _ => Err(XcmError::AssetNotFound),265 }266 }267268 /// Obtains the token id of the `asset_instance` in the collection.269 ///270 /// Returns `Ok(None)` only if the `asset_instance` points to a foreign item271 /// and it haven't been created on this blockchain yet.272 ///273 /// If the `asset_instance` points to a native item, it cannot return `Ok(None)`.274 fn asset_instance_to_token_id(275 xcm_ext: &dyn XcmExtensions<T>,276 collection_id: CollectionId,277 asset_instance: &AssetInstance,278 ) -> Result<Option<TokenId>, XcmError> {279 if xcm_ext.is_foreign() {280 Ok(Self::foreign_reserve_asset_instance_to_token_id(281 collection_id,282 asset_instance,283 ))284 } else {285 Self::native_asset_instance_to_token_id(asset_instance).map(Some)286 }287 }288289 fn create_foreign_asset_instance(290 xcm_ext: &dyn XcmExtensions<T>,291 collection_id: CollectionId,292 asset_instance: &AssetInstance,293 to: T::CrossAccountId,294 ) -> XcmResult {295 let asset_instance_encoded = asset_instance.encode();296297 let derivative_token_id = xcm_ext298 .create_item(299 &Self::pallet_account(),300 to,301 CreateItemData::NFT(CreateNftData {302 properties: vec![Property {303 key: Self::reserve_asset_instance_property_key(),304 value: asset_instance_encoded305 .try_into()306 .expect("asset instance length <= 32 bytes which is less than value length limit; qed"),307 }]308 .try_into()309 .expect("just one property can always be stored; qed"),310 }),311 &ZeroBudget,312 )313 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))?;314315 <ForeignReserveAssetInstanceToTokenId<T>>::insert(316 collection_id,317 asset_instance,318 derivative_token_id,319 );320321 Ok(())322 }323324 fn deposit_asset_instance(325 xcm_ext: &dyn XcmExtensions<T>,326 collection_id: CollectionId,327 to: T::CrossAccountId,328 asset_instance: &AssetInstance,329 ) -> XcmResult {330 if let Some(token_id) =331 Self::asset_instance_to_token_id(xcm_ext, collection_id, asset_instance)?332 {333 let depositor = &Self::pallet_account();334 let from = depositor;335 let amount = 1;336337 xcm_ext338 .transfer_item(depositor, from, &to, token_id, amount, &ZeroBudget)339 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))340 } else {341 Self::create_foreign_asset_instance(xcm_ext, collection_id, asset_instance, to)342 }343 }213}344}214345215impl<T: Config> TransactAsset for Pallet<T> {346impl<T: Config> TransactAsset for Pallet<T> {234 fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}365 fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}235366236 fn deposit_asset(what: &MultiAsset, to: &MultiLocation, context: &XcmContext) -> XcmResult {367 fn deposit_asset(what: &MultiAsset, to: &MultiLocation, context: &XcmContext) -> XcmResult {368 let collection_id = Self::multiasset_to_collection(what)?;369 let dispatch =370 T::CollectionDispatch::dispatch(collection_id).map_err(|_| XcmError::AssetNotFound)?;371372 let collection = dispatch.as_dyn();237 Err(XcmError::Unimplemented)373 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::Unimplemented)?;374375 let to = T::LocationToAccountId::convert_location(to)376 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;377378 match what.fun {379 Fungibility::Fungible(amount) => xcm_ext380 .create_item(381 &Self::pallet_account(),382 to,383 CreateItemData::Fungible(CreateFungibleData { value: amount }),384 &ZeroBudget,385 )386 .map(|_| ())387 .map_err(|_| XcmError::FailedToTransactAsset("fungible item deposit failed")),388389 Fungibility::NonFungible(asset_instance) => {390 Self::deposit_asset_instance(xcm_ext, collection_id, to, &asset_instance)391 }392 }238 }393 }239394240 fn withdraw_asset(395 fn withdraw_asset(263 if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {418 if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {264 Some(Here.into())419 Some(Here.into())265 } else {420 } else {266 // let dispatch = T::CollectionDispatch::dispatch(collection_id).ok()?;421 let dispatch = T::CollectionDispatch::dispatch(collection_id).ok()?;267 // let collection = dispatch.as_dyn();268 // let xcm_ext = collection.xcm_extensions()?;269270 // if xcm_ext.is_foreign() {271 // let encoded_location =272 // collection.property(&<Pallet<T>>::reserve_location_property_key())?;273 // MultiLocation::decode(&mut &encoded_location[..]).ok()274 // } else {275 // T::SelfLocation::get()276 // .pushed_with_interior(GeneralIndex(collection_id.0.into()))277 // .ok()278 // }279 todo!()422 let collection = dispatch.as_dyn();423 let xcm_ext = collection.xcm_extensions()?;424425 if xcm_ext.is_foreign() {426 <Pallet<T>>::collection_to_foreign_reserve_location(collection_id)427 } else {428 T::SelfLocation::get()429 .pushed_with_interior(GeneralIndex(collection_id.0.into()))430 .ok()431 }280 }432 }281 }433 }282}434}pallets/fungible/src/common.rsdiffbeforeafterboth19use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};19use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};20use pallet_common::{20use pallet_common::{21 weights::WeightInfo as _, with_weight, CommonCollectionOperations, CommonWeightInfo,21 weights::WeightInfo as _, with_weight, CommonCollectionOperations, CommonWeightInfo,22 Error as CommonError, RefungibleExtensions, SelfWeightOf as PalletCommonWeightOf,22 Error as CommonError, SelfWeightOf as PalletCommonWeightOf, XcmExtensions,23};23};24use sp_runtime::{ArithmeticError, DispatchError};24use sp_runtime::{ArithmeticError, DispatchError};25use sp_std::{vec, vec::Vec};25use sp_std::{vec, vec::Vec};114 <Pallet<T>>::create_item(self, &sender, (to, fungible_data.value), nesting_budget),114 <Pallet<T>>::create_item(self, &sender, (to, fungible_data.value), nesting_budget),115 <CommonWeights<T>>::create_item(&data),115 <CommonWeights<T>>::create_item(&data),116 ),116 ),117 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),117 _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),118 }118 }119 }119 }120120133 .checked_add(data.value)133 .checked_add(data.value)134 .ok_or(ArithmeticError::Overflow)?;134 .ok_or(ArithmeticError::Overflow)?;135 }135 }136 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),136 _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),137 }137 }138 }138 }139139152 let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);152 let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);153 let data = match data {153 let data = match data {154 up_data_structs::CreateItemExData::Fungible(f) => f,154 up_data_structs::CreateItemExData::Fungible(f) => f,155 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),155 _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),156 };156 };157157158 with_weight(158 with_weight(435 <TotalSupply<T>>::try_get(self.id).ok()435 <TotalSupply<T>>::try_get(self.id).ok()436 }436 }437438 fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {439 Some(self)440 }437441438 fn set_allowance_for_all(442 fn set_allowance_for_all(439 &self,443 &self,454 }458 }455}459}460461impl<T: Config> XcmExtensions<T> for FungibleHandle<T> {462 fn is_foreign(&self) -> bool {463 self.flags.foreign464 }465466 fn create_item_internal(467 &self,468 depositor: &<T>::CrossAccountId,469 to: <T>::CrossAccountId,470 data: CreateItemData,471 nesting_budget: &dyn Budget,472 ) -> Result<TokenId, sp_runtime::DispatchError> {473 match &data {474 up_data_structs::CreateItemData::Fungible(fungible_data) => {475 <Pallet<T>>::create_multiple_items(476 self,477 &depositor,478 [(to, fungible_data.value)].into_iter().collect(),479 nesting_budget,480 )?481 }482 _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),483 }484485 Ok(TokenId::default())486 }487488 fn transfer_item_internal(489 &self,490 depositor: &<T>::CrossAccountId,491 from: &<T>::CrossAccountId,492 to: &<T>::CrossAccountId,493 token: TokenId,494 amount: u128,495 nesting_budget: &dyn Budget,496 ) -> sp_runtime::DispatchResult {497 ensure!(498 token == TokenId::default(),499 <CommonError<T>>::FungibleItemsHaveNoId500 );501502 <Pallet<T>>::transfer_internal(self, &depositor, &from, &to, amount, nesting_budget)503 .map(|_| ())504 .map_err(|post_info| post_info.error)505 }506507 fn burn_item_internal(508 &self,509 from: <T>::CrossAccountId,510 token: TokenId,511 amount: u128,512 ) -> sp_runtime::DispatchResult {513 <Self as CommonCollectionOperations<T>>::burn_item(&self, from, token, amount)514 .map(|_| ())515 .map_err(|post_info| post_info.error)516 }517}456518pallets/fungible/src/lib.rsdiffbeforeafterboth95use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};95use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};96use sp_std::{collections::btree_map::BTreeMap, vec::Vec};96use sp_std::{collections::btree_map::BTreeMap, vec::Vec};97use up_data_structs::{97use up_data_structs::{98 budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId, CreateCollectionData,98 budget::{Budget, ZeroBudget},99 mapping::TokenAddressMapping,99 Property, PropertyKey, TokenId,100 AccessMode, CollectionId, CreateCollectionData, Property, PropertyKey, TokenId,100};101};121122122 #[pallet::error]123 #[pallet::error]123 pub enum Error<T> {124 pub enum Error<T> {124 /// Not Fungible item data used to mint in Fungible collection.125 NotFungibleDataUsedToMintFungibleCollectionToken,126 /// Tried to set data for fungible item.125 /// Tried to set data for fungible item.127 FungibleItemsDontHaveData,126 FungibleItemsDontHaveData,128 /// Fungible token does not support nesting.127 /// Fungible token does not support nesting.276 .checked_sub(amount)275 .checked_sub(amount)277 .ok_or(<CommonError<T>>::TokenValueTooLow)?;276 .ok_or(<CommonError<T>>::TokenValueTooLow)?;278279 // Foreign collection check280 ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);281277282 if collection.permissions.access() == AccessMode::AllowList {278 if collection.permissions.access() == AccessMode::AllowList {283 collection.check_allowlist(owner)?;279 collection.check_allowlist(owner)?;310 Ok(())306 Ok(())311 }307 }312313 /// Burns the specified amount of the token.314 pub fn burn_foreign(315 collection: &FungibleHandle<T>,316 owner: &T::CrossAccountId,317 amount: u128,318 ) -> DispatchResult {319 let total_supply = <TotalSupply<T>>::get(collection.id)320 .checked_sub(amount)321 .ok_or(<CommonError<T>>::TokenValueTooLow)?;322323 let balance = <Balance<T>>::get((collection.id, owner))324 .checked_sub(amount)325 .ok_or(<CommonError<T>>::TokenValueTooLow)?;326 // =========327328 if balance == 0 {329 <Balance<T>>::remove((collection.id, owner));330 <PalletStructure<T>>::unnest_if_nested(owner, collection.id, TokenId::default());331 } else {332 <Balance<T>>::insert((collection.id, owner), balance);333 }334 <TotalSupply<T>>::insert(collection.id, total_supply);335336 <PalletEvm<T>>::deposit_log(337 ERC20Events::Transfer {338 from: *owner.as_eth(),339 to: H160::default(),340 value: amount.into(),341 }342 .to_log(collection_id_to_address(collection.id)),343 );344 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(345 collection.id,346 TokenId::default(),347 owner.clone(),348 amount,349 ));350 Ok(())351 }352308353 /// Transfers the specified amount of tokens. Will check that309 /// Transfers the specified amount of tokens. Will check that354 /// the transfer is allowed for the token.310 /// the transfer is allowed for the token.450 }406 }451407452 /// Minting tokens for multiple IDs.408 /// Minting tokens for multiple IDs.453 /// It is a utility function used in [`create_multiple_items`][`Pallet::create_multiple_items`]454 /// and [`create_multiple_items_foreign`][`Pallet::create_multiple_items_foreign`]409 /// See [`create_item`][`Pallet::create_item`] for more details.455 pub fn create_multiple_items_common(410 pub fn create_multiple_items(456 collection: &FungibleHandle<T>,411 collection: &FungibleHandle<T>,457 sender: &T::CrossAccountId,412 depositor: &T::CrossAccountId,458 data: BTreeMap<T::CrossAccountId, u128>,413 data: BTreeMap<T::CrossAccountId, u128>,459 nesting_budget: &dyn Budget,414 nesting_budget: &dyn Budget,460 ) -> DispatchResult {415 ) -> DispatchResult {416 if !collection.is_owner_or_admin(depositor) {417 ensure!(418 collection.permissions.mint_mode(),419 <CommonError<T>>::PublicMintingNotAllowed420 );421 collection.check_allowlist(depositor)?;422423 for (owner, _) in data.iter() {424 collection.check_allowlist(owner)?;425 }426 }427461 let total_supply = data428 let total_supply = data462 .values()429 .values()468435469 for (to, _) in data.iter() {436 for (to, _) in data.iter() {470 <PalletStructure<T>>::check_nesting(437 <PalletStructure<T>>::check_nesting(471 sender,438 depositor,472 to,439 to,473 collection.id,440 collection.id,474 TokenId::default(),441 TokenId::default(),515 Ok(())482 Ok(())516 }483 }517518 /// Minting tokens for multiple IDs.519 /// See [`create_item`][`Pallet::create_item`] for more details.520 pub fn create_multiple_items(521 collection: &FungibleHandle<T>,522 sender: &T::CrossAccountId,523 data: BTreeMap<T::CrossAccountId, u128>,524 nesting_budget: &dyn Budget,525 ) -> DispatchResult {526 // Foreign collection check527 ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);528529 if !collection.is_owner_or_admin(sender) {530 ensure!(531 collection.permissions.mint_mode(),532 <CommonError<T>>::PublicMintingNotAllowed533 );534 collection.check_allowlist(sender)?;535536 for (owner, _) in data.iter() {537 collection.check_allowlist(owner)?;538 }539 }540541 Self::create_multiple_items_common(collection, sender, data, nesting_budget)542 }543544 /// Minting tokens for multiple IDs.545 /// See [`create_item_foreign`][`Pallet::create_item_foreign`] for more details.546 pub fn create_multiple_items_foreign(547 collection: &FungibleHandle<T>,548 sender: &T::CrossAccountId,549 data: BTreeMap<T::CrossAccountId, u128>,550 nesting_budget: &dyn Budget,551 ) -> DispatchResult {552 Self::create_multiple_items_common(collection, sender, data, nesting_budget)553 }554484555 fn set_allowance_unchecked(485 fn set_allowance_unchecked(556 collection: &FungibleHandle<T>,486 collection: &FungibleHandle<T>,784 )714 )785 }715 }786787 /// Creates fungible token.788 ///789 /// - `data`: Contains user who will become the owners of the tokens and amount790 /// of tokens he will receive.791 pub fn create_item_foreign(792 collection: &FungibleHandle<T>,793 sender: &T::CrossAccountId,794 data: CreateItemData<T>,795 nesting_budget: &dyn Budget,796 ) -> DispatchResult {797 Self::create_multiple_items_foreign(798 collection,799 sender,800 [(data.0, data.1)].into_iter().collect(),801 nesting_budget,802 )803 }804716805 /// Returns 10 tokens owners in no particular order717 /// Returns 10 tokens owners in no particular order806 ///718 ///pallets/nonfungible/src/common.rsdiffbeforeafterboth19use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};19use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};20use pallet_common::{20use pallet_common::{21 weights::WeightInfo as _, with_weight, write_token_properties_total_weight,21 weights::WeightInfo as _, with_weight, write_token_properties_total_weight,22 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions,22 CommonCollectionOperations, CommonWeightInfo, SelfWeightOf as PalletCommonWeightOf,23 SelfWeightOf as PalletCommonWeightOf,23 XcmExtensions,24};24};25use pallet_structure::Pallet as PalletStructure;25use pallet_structure::Pallet as PalletStructure;26use sp_runtime::DispatchError;26use sp_runtime::DispatchError;543 }543 }544 }544 }545545546 fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {547 Some(self)548 }549546 fn set_allowance_for_all(550 fn set_allowance_for_all(547 &self,551 &self,548 owner: T::CrossAccountId,552 owner: T::CrossAccountId,564 <Pallet<T>>::repair_item(self, token),568 <Pallet<T>>::repair_item(self, token),565 <CommonWeights<T>>::force_repair_item(),569 <CommonWeights<T>>::force_repair_item(),566 )570 )571 }572}573574impl<T: Config> XcmExtensions<T> for NonfungibleHandle<T> {575 fn is_foreign(&self) -> bool {576 self.flags.foreign577 }578579 fn create_item_internal(580 &self,581 depositor: &<T>::CrossAccountId,582 to: <T>::CrossAccountId,583 data: up_data_structs::CreateItemData,584 nesting_budget: &dyn Budget,585 ) -> Result<TokenId, sp_runtime::DispatchError> {586 <Pallet<T>>::create_multiple_items(587 self,588 &depositor,589 vec![map_create_data::<T>(data, &to)?],590 nesting_budget,591 )?;592593 Ok(self.last_token_id())594 }595596 fn transfer_item_internal(597 &self,598 depositor: &<T>::CrossAccountId,599 from: &<T>::CrossAccountId,600 to: &<T>::CrossAccountId,601 token: TokenId,602 amount: u128,603 nesting_budget: &dyn Budget,604 ) -> sp_runtime::DispatchResult {605 ensure!(amount == 1, <Error<T>>::NonfungibleItemsHaveNoAmount);606607 <Pallet<T>>::transfer_internal(self, &depositor, &from, &to, token, nesting_budget)608 .map(|_| ())609 .map_err(|post_info| post_info.error)610 }611612 fn burn_item_internal(613 &self,614 from: T::CrossAccountId,615 token: TokenId,616 amount: u128,617 ) -> sp_runtime::DispatchResult {618 ensure!(amount == 1, <Error<T>>::NonfungibleItemsHaveNoAmount);619620 <Pallet<T>>::burn(self, &from, token)567 }621 }568}622}569623primitives/data-structs/src/budget.rsdiffbeforeafterboth37 }37 }38}38}3940pub struct ZeroBudget;41impl Budget for ZeroBudget {42 fn consume_custom(&self, _calls: u32) -> bool {43 false44 }45}3946runtime/common/config/pallets/foreign_asset.rsdiffbeforeafterboth1use frame_support::{parameter_types, PalletId};1use frame_support::{parameter_types, PalletId};2use pallet_evm::account::CrossAccountId;3use sp_core::H160;4use staging_xcm::prelude::*;5use staging_xcm_builder::AccountKey20Aliases;263use crate::{runtime_common::config::governance, Runtime, RuntimeEvent};7use crate::{8 runtime_common::config::{9 ethereum::CrossAccountId as ConfigCrossAccountId,10 governance,11 xcm::{LocationToAccountId, SelfLocation},12 },13 RelayNetwork, Runtime, RuntimeEvent,14};4155parameter_types! {16parameter_types! {6 pub ForeignAssetPalletId: PalletId = PalletId(*b"frgnasts");17 pub ForeignAssetPalletId: PalletId = PalletId(*b"frgnasts");7}18}1920pub struct LocationToCrossAccountId;21impl staging_xcm_executor::traits::ConvertLocation<ConfigCrossAccountId>22 for LocationToCrossAccountId23{24 fn convert_location(location: &MultiLocation) -> Option<ConfigCrossAccountId> {25 LocationToAccountId::convert_location(location)26 .map(|sub| ConfigCrossAccountId::from_sub(sub))27 .or_else(|| {28 let eth_address =29 AccountKey20Aliases::<RelayNetwork, H160>::convert_location(location)?;3031 Some(ConfigCrossAccountId::from_eth(eth_address))32 })33 }34}8359impl pallet_foreign_assets::Config for Runtime {36impl pallet_foreign_assets::Config for Runtime {10 type RuntimeEvent = RuntimeEvent;37 type RuntimeEvent = RuntimeEvent;11 type ForceRegisterOrigin = governance::RootOrTechnicalCommitteeMember;38 type ForceRegisterOrigin = governance::RootOrTechnicalCommitteeMember;12 type PalletId = ForeignAssetPalletId;39 type PalletId = ForeignAssetPalletId;40 type SelfLocation = SelfLocation;41 type LocationToAccountId = LocationToCrossAccountId;13 type WeightInfo = pallet_foreign_assets::weights::SubstrateWeight<Self>;42 type WeightInfo = pallet_foreign_assets::weights::SubstrateWeight<Self>;14}43}1544