difftreelog
fix use CollectionIssuer enum for collection creation
in: master
6 files changed
pallets/common/src/dispatch.rsdiffbeforeafterboth--- a/pallets/common/src/dispatch.rs
+++ b/pallets/common/src/dispatch.rs
@@ -11,7 +11,7 @@
use sp_weights::Weight;
use up_data_structs::{CollectionId, CreateCollectionData};
-use crate::{pallet::Config, CommonCollectionOperations};
+use crate::{pallet::Config, CollectionIssuer, CommonCollectionOperations};
// TODO: move to benchmarking
/// Price of [`dispatch_tx`] call with noop `call` argument
@@ -72,26 +72,11 @@
/// Create a regular collection. The collection will be created according to the value of [`data.mode`](CreateCollectionData::mode).
///
/// * `sender` - The user who will become the owner of the collection.
- /// * `payer` - If set, the user who pays the collection creation deposit.
+ /// * `issuer` - An entity that creates the collection.
/// * `data` - Description of the created collection.
fn create(
sender: T::CrossAccountId,
- payer: Option<T::CrossAccountId>,
- data: CreateCollectionData<T::CrossAccountId>,
- ) -> Result<CollectionId, DispatchError> {
- Self::create_raw(sender, payer, false, data)
- }
-
- /// Function for creating regular and special collections.
- ///
- /// * `sender` - The user who will become the owner of the collection.
- /// * `payer` - If set, the user who pays the collection creation deposit.
- /// * `data` - Description of the created collection.
- /// * `is_special_collection` -- Whether this collection is a special one, i.e. can have special flags set.
- fn create_raw(
- sender: T::CrossAccountId,
- payer: Option<T::CrossAccountId>,
- is_special_collection: bool,
+ issuer: CollectionIssuer<T::CrossAccountId>,
data: CreateCollectionData<T::CrossAccountId>,
) -> Result<CollectionId, DispatchError>;
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -944,6 +944,15 @@
}
}
+/// An issuer of a collection.
+pub enum CollectionIssuer<CrossAccountId> {
+ /// A user who creates the collection.
+ User(CrossAccountId),
+
+ /// The internal mechanisms are creating the collection.
+ Internals,
+}
+
fn check_token_permissions<T: Config>(
collection_admin_permitted: bool,
token_owner_permitted: bool,
@@ -1128,32 +1137,34 @@
/// Create new collection.
///
/// * `owner` - The owner of the collection.
- /// * `payer` - If set, the user that will pay a deposit for the collection creation.
- /// * `data` - Description of the created collection.
+ /// * `issuer` - An entity that creates the collection.
/// * `is_special_collection` -- Whether this collection is a special one, i.e. can have special flags set.
pub fn init_collection(
owner: T::CrossAccountId,
- payer: Option<T::CrossAccountId>,
- is_special_collection: bool,
+ issuer: CollectionIssuer<T::CrossAccountId>,
data: CreateCollectionData<T::CrossAccountId>,
) -> Result<CollectionId, DispatchError> {
- if !is_special_collection {
- ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);
- }
+ match issuer {
+ CollectionIssuer::User(payer) => {
+ ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);
- // Take a (non-refundable) deposit of collection creation
- if let Some(payer) = payer {
- let mut imbalance = <Debt<T::AccountId, <T as Config>::Currency>>::zero();
- imbalance.subsume(<T as Config>::Currency::deposit(
- &T::TreasuryAccountId::get(),
- T::CollectionCreationPrice::get(),
- Precision::Exact,
- )?);
- let credit =
- <T as Config>::Currency::settle(payer.as_sub(), imbalance, Preservation::Preserve)
- .map_err(|_| Error::<T>::NotSufficientFounds)?;
+ // Take a (non-refundable) deposit of collection creation
+ let mut imbalance = <Debt<T::AccountId, <T as Config>::Currency>>::zero();
+ imbalance.subsume(<T as Config>::Currency::deposit(
+ &T::TreasuryAccountId::get(),
+ T::CollectionCreationPrice::get(),
+ Precision::Exact,
+ )?);
+ let credit = <T as Config>::Currency::settle(
+ payer.as_sub(),
+ imbalance,
+ Preservation::Preserve,
+ )
+ .map_err(|_| Error::<T>::NotSufficientFounds)?;
- debug_assert!(credit.peek().is_zero())
+ debug_assert!(credit.peek().is_zero());
+ }
+ CollectionIssuer::Internals => {}
}
{
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::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin, PalletId};29use frame_system::pallet_prelude::*;30use pallet_common::{31 dispatch::CollectionDispatch, erc::CrossAccountId, XcmExtensions, NATIVE_FUNGIBLE_COLLECTION_ID,32};33use sp_runtime::traits::AccountIdConversion;34use sp_std::{boxed::Box, vec, vec::Vec};35use staging_xcm::{36 opaque::latest::{prelude::XcmError, Weight},37 v3::{prelude::*, MultiAsset, XcmContext},38};39use staging_xcm_executor::{40 traits::{ConvertLocation, Error as XcmExecutorError, TransactAsset, WeightTrader},41 Assets,42};43use up_data_structs::{44 budget::ZeroBudget, CollectionId, CollectionMode, CollectionName, CollectionTokenPrefix,45 CreateCollectionData, CreateFungibleData, CreateItemData, CreateNftData, Property, PropertyKey,46 TokenId,47};4849pub mod weights;5051#[cfg(feature = "runtime-benchmarks")]52mod benchmarking;5354pub use module::*;55pub use weights::WeightInfo;5657#[frame_support::pallet]58pub mod module {59 use up_data_structs::{60 CollectionDescription, Property, PropertyKeyPermission, PropertyPermission,61 };6263 use super::*;6465 #[pallet::config]66 pub trait Config:67 frame_system::Config68 + pallet_common::Config69 + pallet_fungible::Config70 + pallet_balances::Config71 {72 /// The overarching event type.73 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;7475 /// Origin for force registering of a foreign asset.76 type ForceRegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;7778 /// The ID of the foreign assets pallet.79 type PalletId: Get<PalletId>;8081 /// Self-location of this parachain.82 type SelfLocation: Get<MultiLocation>;8384 /// The converter from a MultiLocation to a CrossAccountId.85 type LocationToAccountId: ConvertLocation<Self::CrossAccountId>;8687 /// Weight information for the extrinsics in this module.88 type WeightInfo: WeightInfo;89 }9091 #[pallet::error]92 pub enum Error<T> {93 /// The foreign asset is already registered.94 ForeignAssetAlreadyRegistered,95 }9697 #[pallet::event]98 #[pallet::generate_deposit(fn deposit_event)]99 pub enum Event<T: Config> {100 /// The foreign asset registered.101 ForeignAssetRegistered {102 collection_id: CollectionId,103 asset_id: Box<AssetId>,104 },105 }106107 /// The corresponding collections of foreign assets.108 #[pallet::storage]109 #[pallet::getter(fn foreign_asset_to_collection)]110 pub type ForeignAssetToCollection<T: Config> =111 StorageMap<_, Twox64Concat, staging_xcm::v3::AssetId, CollectionId, OptionQuery>;112113 /// The corresponding foreign assets of collections.114 #[pallet::storage]115 #[pallet::getter(fn collection_to_foreign_asset)]116 pub type CollectionToForeignAsset<T: Config> =117 StorageMap<_, Twox64Concat, CollectionId, staging_xcm::v3::AssetId, OptionQuery>;118119 /// The correponding NFT token id of reserve NFTs120 #[pallet::storage]121 #[pallet::getter(fn foreign_reserve_asset_instance_to_token_id)]122 pub type ForeignReserveAssetInstanceToTokenId<T: Config> = StorageDoubleMap<123 Hasher1 = Twox64Concat,124 Key1 = CollectionId,125 Hasher2 = Blake2_128Concat,126 Key2 = staging_xcm::v3::AssetInstance,127 Value = TokenId,128 QueryKind = OptionQuery,129 >;130131 /// The correponding reserve NFT of a token ID132 #[pallet::storage]133 #[pallet::getter(fn token_id_to_foreign_reserve_asset_instance)]134 pub type TokenIdToForeignReserveAssetInstance<T: Config> = StorageDoubleMap<135 Hasher1 = Twox64Concat,136 Key1 = CollectionId,137 Hasher2 = Blake2_128Concat,138 Key2 = TokenId,139 Value = staging_xcm::v3::AssetInstance,140 QueryKind = OptionQuery,141 >;142143 #[pallet::pallet]144 pub struct Pallet<T>(_);145146 #[pallet::call]147 impl<T: Config> Pallet<T> {148 #[pallet::call_index(0)]149 #[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]150 pub fn force_register_foreign_asset(151 origin: OriginFor<T>,152 asset_id: Box<AssetId>,153 name: CollectionName,154 token_prefix: CollectionTokenPrefix,155 mode: ForeignCollectionMode,156 ) -> DispatchResult {157 T::ForceRegisterOrigin::ensure_origin(origin.clone())?;158159 ensure!(160 !<ForeignAssetToCollection<T>>::contains_key(*asset_id),161 <Error<T>>::ForeignAssetAlreadyRegistered,162 );163164 let foreign_collection_owner = Self::pallet_account();165166 let description: CollectionDescription = "Foreign Assets Collection"167 .encode_utf16()168 .collect::<Vec<_>>()169 .try_into()170 .expect("description length < max description length; qed");171172 let payer = None;173 let is_special_collection = true;174 let collection_id = T::CollectionDispatch::create_raw(175 foreign_collection_owner,176 payer,177 is_special_collection,178 CreateCollectionData {179 name,180 token_prefix,181 description,182 mode: mode.into(),183184 properties: vec![Property {185 key: Self::reserve_location_property_key(),186 value: asset_id187 .encode()188 .try_into()189 .expect("multilocation is less than 32k; qed"),190 }]191 .try_into()192 .expect("just one property can always be stored; qed"),193194 token_property_permissions: vec![PropertyKeyPermission {195 key: Self::reserve_asset_instance_property_key(),196 permission: PropertyPermission {197 mutable: false,198 collection_admin: true,199 token_owner: false,200 },201 }]202 .try_into()203 .expect("just one property permission can always be stored; qed"),204 ..Default::default()205 },206 )?;207208 <ForeignAssetToCollection<T>>::insert(*asset_id, collection_id);209 <CollectionToForeignAsset<T>>::insert(collection_id, *asset_id);210211 Self::deposit_event(Event::<T>::ForeignAssetRegistered {212 collection_id,213 asset_id,214 });215216 Ok(())217 }218 }219}220221impl<T: Config> Pallet<T> {222 fn pallet_account() -> T::CrossAccountId {223 let owner: T::AccountId = T::PalletId::get().into_account_truncating();224 T::CrossAccountId::from_sub(owner)225 }226227 fn reserve_location_property_key() -> PropertyKey {228 b"reserve-location"229 .to_vec()230 .try_into()231 .expect("key length < max property key length; qed")232 }233234 fn reserve_asset_instance_property_key() -> PropertyKey {235 b"reserve-asset-instance"236 .to_vec()237 .try_into()238 .expect("key length < max property key length; qed")239 }240241 /// Converts a concrete asset ID (the asset multilocation) to a local collection on Unique Network.242 ///243 /// The multilocation corresponds to a local collection if:244 /// * It is `Here` location that corresponds to the native token of this parachain.245 /// * It is `../Parachain(<Unique Network Para ID>)` that also corresponds to the native token of this parachain.246 /// * It is `../Parachain(<Unique Network Para ID>)/GeneralIndex(<Collection ID>)` that corresponds247 /// to the collection with the ID equal to `<Collection ID>`. The `<Collection ID>` must be in the valid range,248 /// otherwise `None` is returned.249 /// * It is `GeneralIndex(<Collection ID>)`. Same as the last one above.250 ///251 /// If the multilocation doesn't match the patterns listed above,252 /// or the `<Collection ID>` points to a foreign collection,253 /// `None` is returned, identifying that the given multilocation doesn't correspond to a local collection.254 fn local_asset_id_to_collection(asset_id: &AssetId) -> Option<CollectionLocality> {255 let AssetId::Concrete(asset_location) = asset_id else {256 return None;257 };258259 let self_location = T::SelfLocation::get();260261 if *asset_location == Here.into() || *asset_location == self_location {262 return Some(CollectionLocality::Local(NATIVE_FUNGIBLE_COLLECTION_ID));263 }264265 let prefix = if asset_location.parents == 0 {266 &Here267 } else if asset_location.parents == self_location.parents {268 &self_location.interior269 } else {270 return None;271 };272273 let GeneralIndex(collection_id) = asset_location.interior.match_and_split(prefix)? else {274 return None;275 };276277 let collection_id = CollectionId((*collection_id).try_into().ok()?);278279 Self::collection_to_foreign_asset(collection_id)280 .is_none()281 .then_some(CollectionLocality::Local(collection_id))282 }283284 /// Converts an asset ID to a Unique Network's collection locality (either foreign or a local one).285 ///286 /// The function will check if the asset's reserve location has the corresponding287 /// foreign collection on Unique Network,288 /// and will return the "foreign" locality containing the collection ID if found.289 ///290 /// If no corresponding foreign collection is found, the function will check291 /// if the asset's reserve location corresponds to a local collection.292 /// If the local collection is found, the "local" locality with the collection ID is returned.293 ///294 /// If all of the above have failed, the `AssetIdConversionFailed` error will be returned.295 fn asset_to_collection(asset_id: &AssetId) -> Result<CollectionLocality, XcmError> {296 Self::foreign_asset_to_collection(asset_id)297 .map(CollectionLocality::Foreign)298 .or_else(|| Self::local_asset_id_to_collection(asset_id))299 .ok_or_else(|| XcmExecutorError::AssetIdConversionFailed.into())300 }301302 /// Converts an XCM asset instance of local collection to the Unique Network's token ID.303 ///304 /// The asset instance corresponds to the Unique Network's token ID if it is in the following format:305 /// `AssetInstance::Index(<token ID>)`.306 ///307 /// If the asset instance is not in the valid format or the `<token ID>` can't fit into the valid token ID,308 /// `None` will be returned.309 fn local_asset_instance_to_token_id(asset_instance: &AssetInstance) -> Option<TokenId> {310 match asset_instance {311 AssetInstance::Index(token_id) => Some(TokenId((*token_id).try_into().ok()?)),312 _ => None,313 }314 }315316 /// Obtains the token ID of the `asset_instance` in the collection.317 fn asset_instance_to_token_id(318 collection_locality: CollectionLocality,319 asset_instance: &AssetInstance,320 ) -> Option<TokenId> {321 match collection_locality {322 CollectionLocality::Local(_) => Self::local_asset_instance_to_token_id(asset_instance),323 CollectionLocality::Foreign(collection_id) => {324 Self::foreign_reserve_asset_instance_to_token_id(collection_id, asset_instance)325 }326 }327 }328329 /// Creates a foreign item in the the collection.330 fn create_foreign_asset_instance(331 xcm_ext: &dyn XcmExtensions<T>,332 collection_id: CollectionId,333 asset_instance: &AssetInstance,334 to: T::CrossAccountId,335 ) -> DispatchResult {336 let asset_instance_encoded = asset_instance.encode();337338 let derivative_token_id = xcm_ext.create_item(339 &Self::pallet_account(),340 to,341 CreateItemData::NFT(CreateNftData {342 properties: vec![Property {343 key: Self::reserve_asset_instance_property_key(),344 value: asset_instance_encoded345 .try_into()346 .expect("asset instance length <= 32 bytes which is less than value length limit; qed"),347 }]348 .try_into()349 .expect("just one property can always be stored; qed"),350 }),351 &ZeroBudget,352 )?;353354 <ForeignReserveAssetInstanceToTokenId<T>>::insert(355 collection_id,356 asset_instance,357 derivative_token_id,358 );359360 <TokenIdToForeignReserveAssetInstance<T>>::insert(361 collection_id,362 derivative_token_id,363 asset_instance,364 );365366 Ok(())367 }368369 /// Deposits an asset instance to the `to` account.370 ///371 /// Either transfers an existing item from the pallet's account372 /// or creates a foreign item.373 fn deposit_asset_instance(374 xcm_ext: &dyn XcmExtensions<T>,375 collection_locality: CollectionLocality,376 asset_instance: &AssetInstance,377 to: T::CrossAccountId,378 ) -> XcmResult {379 let token_id = Self::asset_instance_to_token_id(collection_locality, asset_instance);380381 let deposit_result = match (collection_locality, token_id) {382 (_, Some(token_id)) => {383 let depositor = &Self::pallet_account();384 let from = depositor;385 let amount = 1;386387 xcm_ext.transfer_item(depositor, from, &to, token_id, amount, &ZeroBudget)388 }389 (CollectionLocality::Foreign(collection_id), None) => {390 Self::create_foreign_asset_instance(xcm_ext, collection_id, asset_instance, to)391 }392 (CollectionLocality::Local(_), None) => {393 return Err(XcmError::AssetNotFound);394 }395 };396397 deposit_result398 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))399 }400401 /// Withdraws an asset instance from the `from` account.402 ///403 /// Transfers the asset instance to the pallet's account.404 fn withdraw_asset_instance(405 xcm_ext: &dyn XcmExtensions<T>,406 collection_locality: CollectionLocality,407 asset_instance: &AssetInstance,408 from: T::CrossAccountId,409 ) -> XcmResult {410 let token_id = Self::asset_instance_to_token_id(collection_locality, asset_instance)411 .ok_or(XcmError::AssetNotFound)?;412413 let depositor = &from;414 let to = Self::pallet_account();415 let amount = 1;416 xcm_ext417 .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)418 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item withdraw failed"))?;419420 Ok(())421 }422}423424impl<T: Config> TransactAsset for Pallet<T> {425 fn can_check_in(426 _origin: &MultiLocation,427 _what: &MultiAsset,428 _context: &XcmContext,429 ) -> XcmResult {430 Err(XcmError::Unimplemented)431 }432433 fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}434435 fn can_check_out(436 _dest: &MultiLocation,437 _what: &MultiAsset,438 _context: &XcmContext,439 ) -> XcmResult {440 Err(XcmError::Unimplemented)441 }442443 fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}444445 fn deposit_asset(446 what: &MultiAsset,447 to: &MultiLocation,448 _context: Option<&XcmContext>,449 ) -> XcmResult {450 let to = T::LocationToAccountId::convert_location(to)451 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;452453 let collection_locality = Self::asset_to_collection(&what.id)?;454 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)455 .map_err(|_| XcmError::AssetNotFound)?;456457 let collection = dispatch.as_dyn();458 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::Unimplemented)?;459460 match what.fun {461 Fungibility::Fungible(amount) => xcm_ext462 .create_item(463 &Self::pallet_account(),464 to,465 CreateItemData::Fungible(CreateFungibleData { value: amount }),466 &ZeroBudget,467 )468 .map(|_| ())469 .map_err(|_| XcmError::FailedToTransactAsset("fungible item deposit failed")),470471 Fungibility::NonFungible(asset_instance) => {472 Self::deposit_asset_instance(xcm_ext, collection_locality, &asset_instance, to)473 }474 }475 }476477 fn withdraw_asset(478 what: &MultiAsset,479 from: &MultiLocation,480 _maybe_context: Option<&XcmContext>,481 ) -> Result<staging_xcm_executor::Assets, XcmError> {482 let from = T::LocationToAccountId::convert_location(from)483 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;484485 let collection_locality = Self::asset_to_collection(&what.id)?;486 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)487 .map_err(|_| XcmError::AssetNotFound)?;488489 let collection = dispatch.as_dyn();490 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;491492 match what.fun {493 Fungibility::Fungible(amount) => xcm_ext494 .burn_item(from, TokenId::default(), amount)495 .map_err(|_| XcmError::FailedToTransactAsset("fungible item withdraw failed"))?,496497 Fungibility::NonFungible(asset_instance) => {498 Self::withdraw_asset_instance(xcm_ext, collection_locality, &asset_instance, from)?;499 }500 }501502 Ok(what.clone().into())503 }504505 fn internal_transfer_asset(506 what: &MultiAsset,507 from: &MultiLocation,508 to: &MultiLocation,509 _context: &XcmContext,510 ) -> Result<staging_xcm_executor::Assets, XcmError> {511 let from = T::LocationToAccountId::convert_location(from)512 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;513514 let to = T::LocationToAccountId::convert_location(to)515 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;516517 let collection_locality = Self::asset_to_collection(&what.id)?;518519 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)520 .map_err(|_| XcmError::AssetNotFound)?;521 let collection = dispatch.as_dyn();522 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;523524 let depositor = &from;525526 let token_id;527 let amount;528 let map_error: fn(DispatchError) -> XcmError;529530 match what.fun {531 Fungibility::Fungible(fungible_amount) => {532 token_id = TokenId::default();533 amount = fungible_amount;534 map_error = |_| XcmError::FailedToTransactAsset("fungible item transfer failed");535 }536537 Fungibility::NonFungible(asset_instance) => {538 token_id = Self::asset_instance_to_token_id(collection_locality, &asset_instance)539 .ok_or(XcmError::AssetNotFound)?;540541 amount = 1;542 map_error = |_| XcmError::FailedToTransactAsset("non-fungible item transfer failed")543 }544 }545546 xcm_ext547 .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)548 .map_err(map_error)?;549550 Ok(what.clone().into())551 }552}553554#[derive(Clone, Copy)]555pub enum CollectionLocality {556 Local(CollectionId),557 Foreign(CollectionId),558}559560impl Deref for CollectionLocality {561 type Target = CollectionId;562563 fn deref(&self) -> &Self::Target {564 match self {565 Self::Local(id) => id,566 Self::Foreign(id) => id,567 }568 }569}570571pub struct CurrencyIdConvert<T: Config>(PhantomData<T>);572impl<T: Config> sp_runtime::traits::Convert<CollectionId, Option<MultiLocation>>573 for CurrencyIdConvert<T>574{575 fn convert(collection_id: CollectionId) -> Option<MultiLocation> {576 if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {577 Some(T::SelfLocation::get())578 } else {579 <Pallet<T>>::collection_to_foreign_asset(collection_id)580 .and_then(|asset_id| match asset_id {581 AssetId::Concrete(location) => Some(location),582 _ => None,583 })584 .or_else(|| {585 T::SelfLocation::get()586 .pushed_with_interior(GeneralIndex(collection_id.0.into()))587 .ok()588 })589 }590 }591}592593#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]594pub enum ForeignCollectionMode {595 NFT,596 Fungible(u8),597}598599impl From<ForeignCollectionMode> for CollectionMode {600 fn from(value: ForeignCollectionMode) -> Self {601 match value {602 ForeignCollectionMode::NFT => Self::NFT,603 ForeignCollectionMode::Fungible(decimals) => Self::Fungible(decimals),604 }605 }606}607608pub struct FreeForAll;609610impl WeightTrader for FreeForAll {611 fn new() -> Self {612 Self613 }614615 fn buy_weight(616 &mut self,617 weight: Weight,618 payment: Assets,619 _xcm: &XcmContext,620 ) -> Result<Assets, XcmError> {621 log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);622 Ok(payment)623 }624}1// 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::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin, PalletId};29use frame_system::pallet_prelude::*;30use pallet_common::{31 dispatch::CollectionDispatch, erc::CrossAccountId, XcmExtensions, NATIVE_FUNGIBLE_COLLECTION_ID,32};33use sp_runtime::traits::AccountIdConversion;34use sp_std::{boxed::Box, vec, vec::Vec};35use staging_xcm::{36 opaque::latest::{prelude::XcmError, Weight},37 v3::{prelude::*, MultiAsset, XcmContext},38};39use staging_xcm_executor::{40 traits::{ConvertLocation, Error as XcmExecutorError, TransactAsset, WeightTrader},41 Assets,42};43use up_data_structs::{44 budget::ZeroBudget, CollectionId, CollectionMode, CollectionName, CollectionTokenPrefix,45 CreateCollectionData, CreateFungibleData, CreateItemData, CreateNftData, Property, PropertyKey,46 TokenId,47};4849pub mod weights;5051#[cfg(feature = "runtime-benchmarks")]52mod benchmarking;5354pub use module::*;55pub use weights::WeightInfo;5657#[frame_support::pallet]58pub mod module {59 use pallet_common::CollectionIssuer;60 use up_data_structs::{61 CollectionDescription, Property, PropertyKeyPermission, PropertyPermission,62 };6364 use super::*;6566 #[pallet::config]67 pub trait Config:68 frame_system::Config69 + pallet_common::Config70 + pallet_fungible::Config71 + pallet_balances::Config72 {73 /// The overarching event type.74 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;7576 /// Origin for force registering of a foreign asset.77 type ForceRegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;7879 /// The ID of the foreign assets pallet.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>;8788 /// Weight information for the extrinsics in this module.89 type WeightInfo: WeightInfo;90 }9192 #[pallet::error]93 pub enum Error<T> {94 /// The foreign asset is already registered.95 ForeignAssetAlreadyRegistered,96 }9798 #[pallet::event]99 #[pallet::generate_deposit(fn deposit_event)]100 pub enum Event<T: Config> {101 /// The foreign asset registered.102 ForeignAssetRegistered {103 collection_id: CollectionId,104 asset_id: Box<AssetId>,105 },106 }107108 /// The corresponding collections of foreign assets.109 #[pallet::storage]110 #[pallet::getter(fn foreign_asset_to_collection)]111 pub type ForeignAssetToCollection<T: Config> =112 StorageMap<_, Twox64Concat, staging_xcm::v3::AssetId, CollectionId, OptionQuery>;113114 /// The corresponding foreign assets of collections.115 #[pallet::storage]116 #[pallet::getter(fn collection_to_foreign_asset)]117 pub type CollectionToForeignAsset<T: Config> =118 StorageMap<_, Twox64Concat, CollectionId, staging_xcm::v3::AssetId, OptionQuery>;119120 /// The correponding NFT token id of reserve NFTs121 #[pallet::storage]122 #[pallet::getter(fn foreign_reserve_asset_instance_to_token_id)]123 pub type ForeignReserveAssetInstanceToTokenId<T: Config> = StorageDoubleMap<124 Hasher1 = Twox64Concat,125 Key1 = CollectionId,126 Hasher2 = Blake2_128Concat,127 Key2 = staging_xcm::v3::AssetInstance,128 Value = TokenId,129 QueryKind = OptionQuery,130 >;131132 /// The correponding reserve NFT of a token ID133 #[pallet::storage]134 #[pallet::getter(fn token_id_to_foreign_reserve_asset_instance)]135 pub type TokenIdToForeignReserveAssetInstance<T: Config> = StorageDoubleMap<136 Hasher1 = Twox64Concat,137 Key1 = CollectionId,138 Hasher2 = Blake2_128Concat,139 Key2 = TokenId,140 Value = staging_xcm::v3::AssetInstance,141 QueryKind = OptionQuery,142 >;143144 #[pallet::pallet]145 pub struct Pallet<T>(_);146147 #[pallet::call]148 impl<T: Config> Pallet<T> {149 #[pallet::call_index(0)]150 #[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]151 pub fn force_register_foreign_asset(152 origin: OriginFor<T>,153 asset_id: Box<AssetId>,154 name: CollectionName,155 token_prefix: CollectionTokenPrefix,156 mode: ForeignCollectionMode,157 ) -> DispatchResult {158 T::ForceRegisterOrigin::ensure_origin(origin.clone())?;159160 ensure!(161 !<ForeignAssetToCollection<T>>::contains_key(*asset_id),162 <Error<T>>::ForeignAssetAlreadyRegistered,163 );164165 let foreign_collection_owner = Self::pallet_account();166167 let description: CollectionDescription = "Foreign Assets Collection"168 .encode_utf16()169 .collect::<Vec<_>>()170 .try_into()171 .expect("description length < max description length; qed");172173 let collection_id = T::CollectionDispatch::create(174 foreign_collection_owner,175 CollectionIssuer::Internals,176 CreateCollectionData {177 name,178 token_prefix,179 description,180 mode: mode.into(),181182 properties: vec![Property {183 key: Self::reserve_location_property_key(),184 value: asset_id185 .encode()186 .try_into()187 .expect("multilocation is less than 32k; qed"),188 }]189 .try_into()190 .expect("just one property can always be stored; qed"),191192 token_property_permissions: vec![PropertyKeyPermission {193 key: Self::reserve_asset_instance_property_key(),194 permission: PropertyPermission {195 mutable: false,196 collection_admin: true,197 token_owner: false,198 },199 }]200 .try_into()201 .expect("just one property permission can always be stored; qed"),202 ..Default::default()203 },204 )?;205206 <ForeignAssetToCollection<T>>::insert(*asset_id, collection_id);207 <CollectionToForeignAsset<T>>::insert(collection_id, *asset_id);208209 Self::deposit_event(Event::<T>::ForeignAssetRegistered {210 collection_id,211 asset_id,212 });213214 Ok(())215 }216 }217}218219impl<T: Config> Pallet<T> {220 fn pallet_account() -> T::CrossAccountId {221 let owner: T::AccountId = T::PalletId::get().into_account_truncating();222 T::CrossAccountId::from_sub(owner)223 }224225 fn reserve_location_property_key() -> PropertyKey {226 b"reserve-location"227 .to_vec()228 .try_into()229 .expect("key length < max property key length; qed")230 }231232 fn reserve_asset_instance_property_key() -> PropertyKey {233 b"reserve-asset-instance"234 .to_vec()235 .try_into()236 .expect("key length < max property key length; qed")237 }238239 /// Converts a concrete asset ID (the asset multilocation) to a local collection on Unique Network.240 ///241 /// The multilocation corresponds to a local collection if:242 /// * It is `Here` location that corresponds to the native token of this parachain.243 /// * It is `../Parachain(<Unique Network Para ID>)` that also corresponds to the native token of this parachain.244 /// * It is `../Parachain(<Unique Network Para ID>)/GeneralIndex(<Collection ID>)` that corresponds245 /// to the collection with the ID equal to `<Collection ID>`. The `<Collection ID>` must be in the valid range,246 /// otherwise `None` is returned.247 /// * It is `GeneralIndex(<Collection ID>)`. Same as the last one above.248 ///249 /// If the multilocation doesn't match the patterns listed above,250 /// or the `<Collection ID>` points to a foreign collection,251 /// `None` is returned, identifying that the given multilocation doesn't correspond to a local collection.252 fn local_asset_id_to_collection(asset_id: &AssetId) -> Option<CollectionLocality> {253 let AssetId::Concrete(asset_location) = asset_id else {254 return None;255 };256257 let self_location = T::SelfLocation::get();258259 if *asset_location == Here.into() || *asset_location == self_location {260 return Some(CollectionLocality::Local(NATIVE_FUNGIBLE_COLLECTION_ID));261 }262263 let prefix = if asset_location.parents == 0 {264 &Here265 } else if asset_location.parents == self_location.parents {266 &self_location.interior267 } else {268 return None;269 };270271 let GeneralIndex(collection_id) = asset_location.interior.match_and_split(prefix)? else {272 return None;273 };274275 let collection_id = CollectionId((*collection_id).try_into().ok()?);276277 Self::collection_to_foreign_asset(collection_id)278 .is_none()279 .then_some(CollectionLocality::Local(collection_id))280 }281282 /// Converts an asset ID to a Unique Network's collection locality (either foreign or a local one).283 ///284 /// The function will check if the asset's reserve location has the corresponding285 /// foreign collection on Unique Network,286 /// and will return the "foreign" locality containing the collection ID if found.287 ///288 /// If no corresponding foreign collection is found, the function will check289 /// if the asset's reserve location corresponds to a local collection.290 /// If the local collection is found, the "local" locality with the collection ID is returned.291 ///292 /// If all of the above have failed, the `AssetIdConversionFailed` error will be returned.293 fn asset_to_collection(asset_id: &AssetId) -> Result<CollectionLocality, XcmError> {294 Self::foreign_asset_to_collection(asset_id)295 .map(CollectionLocality::Foreign)296 .or_else(|| Self::local_asset_id_to_collection(asset_id))297 .ok_or_else(|| XcmExecutorError::AssetIdConversionFailed.into())298 }299300 /// Converts an XCM asset instance of local collection to the Unique Network's token ID.301 ///302 /// The asset instance corresponds to the Unique Network's token ID if it is in the following format:303 /// `AssetInstance::Index(<token ID>)`.304 ///305 /// If the asset instance is not in the valid format or the `<token ID>` can't fit into the valid token ID,306 /// `None` will be returned.307 fn local_asset_instance_to_token_id(asset_instance: &AssetInstance) -> Option<TokenId> {308 match asset_instance {309 AssetInstance::Index(token_id) => Some(TokenId((*token_id).try_into().ok()?)),310 _ => None,311 }312 }313314 /// Obtains the token ID of the `asset_instance` in the collection.315 fn asset_instance_to_token_id(316 collection_locality: CollectionLocality,317 asset_instance: &AssetInstance,318 ) -> Option<TokenId> {319 match collection_locality {320 CollectionLocality::Local(_) => Self::local_asset_instance_to_token_id(asset_instance),321 CollectionLocality::Foreign(collection_id) => {322 Self::foreign_reserve_asset_instance_to_token_id(collection_id, asset_instance)323 }324 }325 }326327 /// Creates a foreign item in the the collection.328 fn create_foreign_asset_instance(329 xcm_ext: &dyn XcmExtensions<T>,330 collection_id: CollectionId,331 asset_instance: &AssetInstance,332 to: T::CrossAccountId,333 ) -> DispatchResult {334 let asset_instance_encoded = asset_instance.encode();335336 let derivative_token_id = xcm_ext.create_item(337 &Self::pallet_account(),338 to,339 CreateItemData::NFT(CreateNftData {340 properties: vec![Property {341 key: Self::reserve_asset_instance_property_key(),342 value: asset_instance_encoded343 .try_into()344 .expect("asset instance length <= 32 bytes which is less than value length limit; qed"),345 }]346 .try_into()347 .expect("just one property can always be stored; qed"),348 }),349 &ZeroBudget,350 )?;351352 <ForeignReserveAssetInstanceToTokenId<T>>::insert(353 collection_id,354 asset_instance,355 derivative_token_id,356 );357358 <TokenIdToForeignReserveAssetInstance<T>>::insert(359 collection_id,360 derivative_token_id,361 asset_instance,362 );363364 Ok(())365 }366367 /// Deposits an asset instance to the `to` account.368 ///369 /// Either transfers an existing item from the pallet's account370 /// or creates a foreign item.371 fn deposit_asset_instance(372 xcm_ext: &dyn XcmExtensions<T>,373 collection_locality: CollectionLocality,374 asset_instance: &AssetInstance,375 to: T::CrossAccountId,376 ) -> XcmResult {377 let token_id = Self::asset_instance_to_token_id(collection_locality, asset_instance);378379 let deposit_result = match (collection_locality, token_id) {380 (_, Some(token_id)) => {381 let depositor = &Self::pallet_account();382 let from = depositor;383 let amount = 1;384385 xcm_ext.transfer_item(depositor, from, &to, token_id, amount, &ZeroBudget)386 }387 (CollectionLocality::Foreign(collection_id), None) => {388 Self::create_foreign_asset_instance(xcm_ext, collection_id, asset_instance, to)389 }390 (CollectionLocality::Local(_), None) => {391 return Err(XcmError::AssetNotFound);392 }393 };394395 deposit_result396 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))397 }398399 /// Withdraws an asset instance from the `from` account.400 ///401 /// Transfers the asset instance to the pallet's account.402 fn withdraw_asset_instance(403 xcm_ext: &dyn XcmExtensions<T>,404 collection_locality: CollectionLocality,405 asset_instance: &AssetInstance,406 from: T::CrossAccountId,407 ) -> XcmResult {408 let token_id = Self::asset_instance_to_token_id(collection_locality, asset_instance)409 .ok_or(XcmError::AssetNotFound)?;410411 let depositor = &from;412 let to = Self::pallet_account();413 let amount = 1;414 xcm_ext415 .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)416 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item withdraw failed"))?;417418 Ok(())419 }420}421422impl<T: Config> TransactAsset for Pallet<T> {423 fn can_check_in(424 _origin: &MultiLocation,425 _what: &MultiAsset,426 _context: &XcmContext,427 ) -> XcmResult {428 Err(XcmError::Unimplemented)429 }430431 fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}432433 fn can_check_out(434 _dest: &MultiLocation,435 _what: &MultiAsset,436 _context: &XcmContext,437 ) -> XcmResult {438 Err(XcmError::Unimplemented)439 }440441 fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}442443 fn deposit_asset(444 what: &MultiAsset,445 to: &MultiLocation,446 _context: Option<&XcmContext>,447 ) -> XcmResult {448 let to = T::LocationToAccountId::convert_location(to)449 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;450451 let collection_locality = Self::asset_to_collection(&what.id)?;452 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)453 .map_err(|_| XcmError::AssetNotFound)?;454455 let collection = dispatch.as_dyn();456 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::Unimplemented)?;457458 match what.fun {459 Fungibility::Fungible(amount) => xcm_ext460 .create_item(461 &Self::pallet_account(),462 to,463 CreateItemData::Fungible(CreateFungibleData { value: amount }),464 &ZeroBudget,465 )466 .map(|_| ())467 .map_err(|_| XcmError::FailedToTransactAsset("fungible item deposit failed")),468469 Fungibility::NonFungible(asset_instance) => {470 Self::deposit_asset_instance(xcm_ext, collection_locality, &asset_instance, to)471 }472 }473 }474475 fn withdraw_asset(476 what: &MultiAsset,477 from: &MultiLocation,478 _maybe_context: Option<&XcmContext>,479 ) -> Result<staging_xcm_executor::Assets, XcmError> {480 let from = T::LocationToAccountId::convert_location(from)481 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;482483 let collection_locality = Self::asset_to_collection(&what.id)?;484 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)485 .map_err(|_| XcmError::AssetNotFound)?;486487 let collection = dispatch.as_dyn();488 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;489490 match what.fun {491 Fungibility::Fungible(amount) => xcm_ext492 .burn_item(from, TokenId::default(), amount)493 .map_err(|_| XcmError::FailedToTransactAsset("fungible item withdraw failed"))?,494495 Fungibility::NonFungible(asset_instance) => {496 Self::withdraw_asset_instance(xcm_ext, collection_locality, &asset_instance, from)?;497 }498 }499500 Ok(what.clone().into())501 }502503 fn internal_transfer_asset(504 what: &MultiAsset,505 from: &MultiLocation,506 to: &MultiLocation,507 _context: &XcmContext,508 ) -> Result<staging_xcm_executor::Assets, XcmError> {509 let from = T::LocationToAccountId::convert_location(from)510 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;511512 let to = T::LocationToAccountId::convert_location(to)513 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;514515 let collection_locality = Self::asset_to_collection(&what.id)?;516517 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)518 .map_err(|_| XcmError::AssetNotFound)?;519 let collection = dispatch.as_dyn();520 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;521522 let depositor = &from;523524 let token_id;525 let amount;526 let map_error: fn(DispatchError) -> XcmError;527528 match what.fun {529 Fungibility::Fungible(fungible_amount) => {530 token_id = TokenId::default();531 amount = fungible_amount;532 map_error = |_| XcmError::FailedToTransactAsset("fungible item transfer failed");533 }534535 Fungibility::NonFungible(asset_instance) => {536 token_id = Self::asset_instance_to_token_id(collection_locality, &asset_instance)537 .ok_or(XcmError::AssetNotFound)?;538539 amount = 1;540 map_error = |_| XcmError::FailedToTransactAsset("non-fungible item transfer failed")541 }542 }543544 xcm_ext545 .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)546 .map_err(map_error)?;547548 Ok(what.clone().into())549 }550}551552#[derive(Clone, Copy)]553pub enum CollectionLocality {554 Local(CollectionId),555 Foreign(CollectionId),556}557558impl Deref for CollectionLocality {559 type Target = CollectionId;560561 fn deref(&self) -> &Self::Target {562 match self {563 Self::Local(id) => id,564 Self::Foreign(id) => id,565 }566 }567}568569pub struct CurrencyIdConvert<T: Config>(PhantomData<T>);570impl<T: Config> sp_runtime::traits::Convert<CollectionId, Option<MultiLocation>>571 for CurrencyIdConvert<T>572{573 fn convert(collection_id: CollectionId) -> Option<MultiLocation> {574 if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {575 Some(T::SelfLocation::get())576 } else {577 <Pallet<T>>::collection_to_foreign_asset(collection_id)578 .and_then(|asset_id| match asset_id {579 AssetId::Concrete(location) => Some(location),580 _ => None,581 })582 .or_else(|| {583 T::SelfLocation::get()584 .pushed_with_interior(GeneralIndex(collection_id.0.into()))585 .ok()586 })587 }588 }589}590591#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]592pub enum ForeignCollectionMode {593 NFT,594 Fungible(u8),595}596597impl From<ForeignCollectionMode> for CollectionMode {598 fn from(value: ForeignCollectionMode) -> Self {599 match value {600 ForeignCollectionMode::NFT => Self::NFT,601 ForeignCollectionMode::Fungible(decimals) => Self::Fungible(decimals),602 }603 }604}605606pub struct FreeForAll;607608impl WeightTrader for FreeForAll {609 fn new() -> Self {610 Self611 }612613 fn buy_weight(614 &mut self,615 weight: Weight,616 payment: Assets,617 _xcm: &XcmContext,618 ) -> Result<Assets, XcmError> {619 log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);620 Ok(payment)621 }622}pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -26,7 +26,7 @@
dispatch::CollectionDispatch,
erc::{static_property::key, CollectionHelpersEvents},
eth::{self, collection_id_to_address, map_eth_to_id},
- CollectionById, CollectionHandle, Pallet as PalletCommon,
+ CollectionById, CollectionHandle, CollectionIssuer, Pallet as PalletCommon,
};
use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};
use pallet_evm_coder_substrate::{
@@ -110,9 +110,12 @@
let collection_helpers_address =
T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
- let collection_id =
- T::CollectionDispatch::create(caller, Some(collection_helpers_address), data)
- .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+ let collection_id = T::CollectionDispatch::create(
+ caller,
+ CollectionIssuer::User(collection_helpers_address),
+ data,
+ )
+ .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
}
@@ -241,9 +244,12 @@
let collection_helpers_address =
T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
- let collection_id =
- T::CollectionDispatch::create(caller, Some(collection_helpers_address), data)
- .map_err(dispatch_to_evm::<T>)?;
+ let collection_id = T::CollectionDispatch::create(
+ caller,
+ CollectionIssuer::User(collection_helpers_address),
+ data,
+ )
+ .map_err(dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
@@ -276,9 +282,12 @@
check_sent_amount_equals_collection_creation_price::<T>(value)?;
let collection_helpers_address =
T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
- let collection_id =
- T::CollectionDispatch::create(caller, Some(collection_helpers_address), data)
- .map_err(dispatch_to_evm::<T>)?;
+ let collection_id = T::CollectionDispatch::create(
+ caller,
+ CollectionIssuer::User(collection_helpers_address),
+ data,
+ )
+ .map_err(dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -93,7 +93,8 @@
use frame_system::{ensure_root, ensure_signed};
use pallet_common::{
dispatch::{dispatch_tx, CollectionDispatch},
- CollectionHandle, CommonWeightInfo, Pallet as PalletCommon, RefungibleExtensionsWeightInfo,
+ CollectionHandle, CollectionIssuer, CommonWeightInfo, Pallet as PalletCommon,
+ RefungibleExtensionsWeightInfo,
};
use pallet_evm::account::CrossAccountId;
use pallet_structure::weights::WeightInfo as StructureWeightInfo;
@@ -401,7 +402,11 @@
// =========
let sender = T::CrossAccountId::from_sub(sender);
- let _id = T::CollectionDispatch::create(sender.clone(), Some(sender), data)?;
+ let _id = T::CollectionDispatch::create(
+ sender.clone(),
+ CollectionIssuer::User(sender),
+ data,
+ )?;
Ok(())
}
runtime/common/dispatch.rsdiffbeforeafterboth--- a/runtime/common/dispatch.rs
+++ b/runtime/common/dispatch.rs
@@ -18,7 +18,7 @@
use pallet_balances_adapter::NativeFungibleHandle;
pub use pallet_common::dispatch::CollectionDispatch;
use pallet_common::{
- erc::CommonEvmHandler, eth::map_eth_to_id, CollectionById, CollectionHandle,
+ erc::CommonEvmHandler, eth::map_eth_to_id, CollectionById, CollectionHandle, CollectionIssuer,
CommonCollectionOperations, Pallet as PalletCommon,
};
use pallet_evm::{PrecompileHandle, PrecompileResult};
@@ -66,10 +66,9 @@
}
}
- fn create_raw(
+ fn create(
sender: T::CrossAccountId,
- payer: Option<T::CrossAccountId>,
- is_special_collection: bool,
+ issuer: CollectionIssuer<T::CrossAccountId>,
data: CreateCollectionData<T::CrossAccountId>,
) -> Result<CollectionId, DispatchError> {
match data.mode {
@@ -87,7 +86,7 @@
_ => {}
};
- <PalletCommon<T>>::init_collection(sender, payer, is_special_collection, data)
+ <PalletCommon<T>>::init_collection(sender, issuer, data)
}
fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {