git.delta.rocks / unique-network / refs/commits / c9c7549767f0

difftreelog

feature: make collection creation methods `payable`

Grigoriy Simonov2022-09-30parent: #fea73f4.patch.diff
in: master

19 files changed

modifiedcrates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth
--- a/crates/evm-coder/procedural/src/solidity_interface.rs
+++ b/crates/evm-coder/procedural/src/solidity_interface.rs
@@ -560,6 +560,7 @@
 	selector: u32,
 	args: Vec<MethodArg>,
 	has_normal_args: bool,
+	has_value_args: bool,
 	mutability: Mutability,
 	result: Type,
 	weight: Option<Expr>,
@@ -647,14 +648,20 @@
 			.unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));
 		let mut selector_str = camel_name.clone();
 		selector_str.push('(');
-		let mut has_normal_args = false;
-		for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {
-			if i != 0 {
-				selector_str.push(',');
+		let mut normal_args_count = 0u32;
+		let mut has_value_args = false;
+		for arg in args.iter() {
+			if arg.is_value() {
+				has_value_args = true;
+			} else if !arg.is_special() {
+				if normal_args_count != 0 {
+					selector_str.push(',');
+				}
+				write!(selector_str, "{}", arg.selector_ty()).unwrap();
+				normal_args_count = normal_args_count.saturating_add(1);
 			}
-			write!(selector_str, "{}", arg.selector_ty()).unwrap();
-			has_normal_args = true;
 		}
+		let has_normal_args = normal_args_count > 0;
 		selector_str.push(')');
 		let selector = fn_selector_str(&selector_str);
 
@@ -667,6 +674,7 @@
 			selector,
 			args,
 			has_normal_args,
+			has_value_args,
 			mutability,
 			result: result.clone(),
 			weight,
@@ -823,7 +831,7 @@
 		let docs = &self.docs;
 		let selector_str = &self.selector_str;
 		let selector = self.selector;
-
+		let is_payable = self.has_value_args;
 		quote! {
 			SolidityFunction {
 				docs: &[#(#docs),*],
@@ -831,6 +839,7 @@
 				selector: #selector,
 				name: #camel_name,
 				mutability: #mutability,
+				is_payable: #is_payable,
 				args: (
 					#(
 						#args,
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -422,6 +422,7 @@
 	pub args: A,
 	pub result: R,
 	pub mutability: SolidityMutability,
+	pub is_payable: bool,
 }
 impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {
 	fn solidity_name(
@@ -452,6 +453,9 @@
 			SolidityMutability::View => write!(writer, " view")?,
 			SolidityMutability::Mutable => {}
 		}
+		if self.is_payable {
+			write!(writer, " payable")?;
+		}
 		if !self.result.is_empty() {
 			write!(writer, " returns (")?;
 			self.result.solidity_name(writer, tc)?;
modifiedpallets/common/src/dispatch.rsdiffbeforeafterboth
--- a/pallets/common/src/dispatch.rs
+++ b/pallets/common/src/dispatch.rs
@@ -78,6 +78,7 @@
 	/// * `data` - Description of the created collection.
 	fn create(
 		sender: T::CrossAccountId,
+		payer: T::CrossAccountId,
 		data: CreateCollectionData<T::AccountId>,
 	) -> Result<CollectionId, DispatchError>;
 
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -866,6 +866,7 @@
 	/// * `flags` - Extra flags to store.
 	pub fn init_collection(
 		owner: T::CrossAccountId,
+		payer: T::CrossAccountId,
 		data: CreateCollectionData<T::AccountId>,
 		flags: CollectionFlags,
 	) -> Result<CollectionId, DispatchError> {
@@ -939,7 +940,7 @@
 				),
 			);
 			<T as Config>::Currency::settle(
-				owner.as_sub(),
+				payer.as_sub(),
 				imbalance,
 				WithdrawReasons::TRANSFER,
 				ExistenceRequirement::KeepAlive,
modifiedpallets/foreign-assets/src/lib.rsdiffbeforeafterboth
before · pallets/foreign-assets/src/lib.rs
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//! - [`Config`]20//! - [`Call`]21//! - [`Pallet`]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//! ## Overview31//!32//! Under construction3334#![cfg_attr(not(feature = "std"), no_std)]35#![allow(clippy::unused_unit)]3637use frame_support::{38	dispatch::DispatchResult,39	ensure,40	pallet_prelude::*,41	traits::{fungible, fungibles, Currency, EnsureOrigin},42	RuntimeDebug,43};44use frame_system::pallet_prelude::*;45use up_data_structs::{CollectionMode};46use pallet_fungible::{Pallet as PalletFungible};47use scale_info::{TypeInfo};48use sp_runtime::{49	traits::{One, Zero},50	ArithmeticError,51};52use sp_std::{boxed::Box, vec::Vec};53use up_data_structs::{CollectionId, TokenId, CreateCollectionData};5455// NOTE:v1::MultiLocation is used in storages, we would need to do migration if upgrade the56// MultiLocation in the future.57use xcm::opaque::latest::prelude::XcmError;58use xcm::{v1::MultiLocation, VersionedMultiLocation};59use xcm_executor::{traits::WeightTrader, Assets};6061use pallet_common::erc::CrossAccountId;6263#[cfg(feature = "std")]64use serde::{Deserialize, Serialize};6566// TODO: Move to primitives67// Id of native currency.68// 0 - QTZ\UNQ69// 1 - KSM\DOT70#[derive(71	Clone,72	Copy,73	Eq,74	PartialEq,75	PartialOrd,76	Ord,77	MaxEncodedLen,78	RuntimeDebug,79	Encode,80	Decode,81	TypeInfo,82)]83#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]84pub enum NativeCurrency {85	Here = 0,86	Parent = 1,87}8889#[derive(90	Clone,91	Copy,92	Eq,93	PartialEq,94	PartialOrd,95	Ord,96	MaxEncodedLen,97	RuntimeDebug,98	Encode,99	Decode,100	TypeInfo,101)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub enum AssetIds {104	ForeignAssetId(ForeignAssetId),105	NativeAssetId(NativeCurrency),106}107108pub trait TryAsForeign<T, F> {109	fn try_as_foreign(asset: T) -> Option<F>;110}111112impl TryAsForeign<AssetIds, ForeignAssetId> for AssetIds {113	fn try_as_foreign(asset: AssetIds) -> Option<ForeignAssetId> {114		match asset {115			AssetIds::ForeignAssetId(id) => Some(id),116			_ => None,117		}118	}119}120121pub type ForeignAssetId = u32;122pub type CurrencyId = AssetIds;123124mod impl_fungibles;125pub mod weights;126127#[cfg(feature = "runtime-benchmarks")]128mod benchmarking;129130pub use module::*;131pub use weights::WeightInfo;132133/// Type alias for currency balance.134pub type BalanceOf<T> =135	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;136137/// A mapping between ForeignAssetId and AssetMetadata.138pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {139	/// Returns the AssetMetadata associated with a given ForeignAssetId.140	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;141	/// Returns the MultiLocation associated with a given ForeignAssetId.142	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;143	/// Returns the CurrencyId associated with a given MultiLocation.144	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;145}146147pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);148149impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>150	for XcmForeignAssetIdMapping<T>151{152	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {153		log::trace!(target: "fassets::asset_metadatas", "call");154		Pallet::<T>::asset_metadatas(AssetIds::ForeignAssetId(foreign_asset_id))155	}156157	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {158		log::trace!(target: "fassets::get_multi_location", "call");159		Pallet::<T>::foreign_asset_locations(foreign_asset_id)160	}161162	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {163		log::trace!(target: "fassets::get_currency_id", "call");164		Some(AssetIds::ForeignAssetId(165			Pallet::<T>::location_to_currency_ids(multi_location).unwrap_or(0),166		))167	}168}169170#[frame_support::pallet]171pub mod module {172	use super::*;173174	#[pallet::config]175	pub trait Config:176		frame_system::Config177		+ pallet_common::Config178		+ pallet_fungible::Config179		+ orml_tokens::Config180		+ pallet_balances::Config181	{182		/// The overarching event type.183		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;184185		/// Currency type for withdraw and balance storage.186		type Currency: Currency<Self::AccountId>;187188		/// Required origin for registering asset.189		type RegisterOrigin: EnsureOrigin<Self::Origin>;190191		/// Weight information for the extrinsics in this module.192		type WeightInfo: WeightInfo;193	}194195	#[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo)]196	pub struct AssetMetadata<Balance> {197		pub name: Vec<u8>,198		pub symbol: Vec<u8>,199		pub decimals: u8,200		pub minimal_balance: Balance,201	}202203	#[pallet::error]204	pub enum Error<T> {205		/// The given location could not be used (e.g. because it cannot be expressed in the206		/// desired version of XCM).207		BadLocation,208		/// MultiLocation existed209		MultiLocationExisted,210		/// AssetId not exists211		AssetIdNotExists,212		/// AssetId exists213		AssetIdExisted,214	}215216	#[pallet::event]217	#[pallet::generate_deposit(fn deposit_event)]218	pub enum Event<T: Config> {219		/// The foreign asset registered.220		ForeignAssetRegistered {221			asset_id: ForeignAssetId,222			asset_address: MultiLocation,223			metadata: AssetMetadata<BalanceOf<T>>,224		},225		/// The foreign asset updated.226		ForeignAssetUpdated {227			asset_id: ForeignAssetId,228			asset_address: MultiLocation,229			metadata: AssetMetadata<BalanceOf<T>>,230		},231		/// The asset registered.232		AssetRegistered {233			asset_id: AssetIds,234			metadata: AssetMetadata<BalanceOf<T>>,235		},236		/// The asset updated.237		AssetUpdated {238			asset_id: AssetIds,239			metadata: AssetMetadata<BalanceOf<T>>,240		},241	}242243	/// Next available Foreign AssetId ID.244	///245	/// NextForeignAssetId: ForeignAssetId246	#[pallet::storage]247	#[pallet::getter(fn next_foreign_asset_id)]248	pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;249	/// The storages for MultiLocations.250	///251	/// ForeignAssetLocations: map ForeignAssetId => Option<MultiLocation>252	#[pallet::storage]253	#[pallet::getter(fn foreign_asset_locations)]254	pub type ForeignAssetLocations<T: Config> =255		StorageMap<_, Twox64Concat, ForeignAssetId, MultiLocation, OptionQuery>;256257	/// The storages for CurrencyIds.258	///259	/// LocationToCurrencyIds: map MultiLocation => Option<ForeignAssetId>260	#[pallet::storage]261	#[pallet::getter(fn location_to_currency_ids)]262	pub type LocationToCurrencyIds<T: Config> =263		StorageMap<_, Twox64Concat, MultiLocation, ForeignAssetId, OptionQuery>;264265	/// The storages for AssetMetadatas.266	///267	/// AssetMetadatas: map AssetIds => Option<AssetMetadata>268	#[pallet::storage]269	#[pallet::getter(fn asset_metadatas)]270	pub type AssetMetadatas<T: Config> =271		StorageMap<_, Twox64Concat, AssetIds, AssetMetadata<BalanceOf<T>>, OptionQuery>;272273	/// The storages for assets to fungible collection binding274	///275	#[pallet::storage]276	#[pallet::getter(fn asset_binding)]277	pub type AssetBinding<T: Config> =278		StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;279280	#[pallet::pallet]281	#[pallet::without_storage_info]282	pub struct Pallet<T>(_);283284	#[pallet::call]285	impl<T: Config> Pallet<T> {286		#[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]287		pub fn register_foreign_asset(288			origin: OriginFor<T>,289			owner: T::AccountId,290			location: Box<VersionedMultiLocation>,291			metadata: Box<AssetMetadata<BalanceOf<T>>>,292		) -> DispatchResult {293			T::RegisterOrigin::ensure_origin(origin.clone())?;294295			let location: MultiLocation = (*location)296				.try_into()297				.map_err(|()| Error::<T>::BadLocation)?;298299			let md = metadata.clone();300			let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();301			let mut description: Vec<u16> = "Foreign assets collection for "302				.encode_utf16()303				.collect::<Vec<u16>>();304			description.append(&mut name.clone());305306			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {307				name: name.try_into().unwrap(),308				description: description.try_into().unwrap(),309				mode: CollectionMode::Fungible(md.decimals),310				..Default::default()311			};312313			let bounded_collection_id = <PalletFungible<T>>::init_foreign_collection(314				CrossAccountId::from_sub(owner),315				data,316			)?;317			let foreign_asset_id =318				Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;319320			Self::deposit_event(Event::<T>::ForeignAssetRegistered {321				asset_id: foreign_asset_id,322				asset_address: location,323				metadata: *metadata,324			});325			Ok(())326		}327328		#[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]329		pub fn update_foreign_asset(330			origin: OriginFor<T>,331			foreign_asset_id: ForeignAssetId,332			location: Box<VersionedMultiLocation>,333			metadata: Box<AssetMetadata<BalanceOf<T>>>,334		) -> DispatchResult {335			T::RegisterOrigin::ensure_origin(origin)?;336337			let location: MultiLocation = (*location)338				.try_into()339				.map_err(|()| Error::<T>::BadLocation)?;340			Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;341342			Self::deposit_event(Event::<T>::ForeignAssetUpdated {343				asset_id: foreign_asset_id,344				asset_address: location,345				metadata: *metadata,346			});347			Ok(())348		}349	}350}351352impl<T: Config> Pallet<T> {353	fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {354		NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {355			let id = *current;356			*current = current357				.checked_add(One::one())358				.ok_or(ArithmeticError::Overflow)?;359			Ok(id)360		})361	}362363	fn do_register_foreign_asset(364		location: &MultiLocation,365		metadata: &AssetMetadata<BalanceOf<T>>,366		bounded_collection_id: CollectionId,367	) -> Result<ForeignAssetId, DispatchError> {368		let foreign_asset_id = Self::get_next_foreign_asset_id()?;369		LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {370			ensure!(371				maybe_currency_ids.is_none(),372				Error::<T>::MultiLocationExisted373			);374			*maybe_currency_ids = Some(foreign_asset_id);375			// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));376377			ForeignAssetLocations::<T>::try_mutate(378				foreign_asset_id,379				|maybe_location| -> DispatchResult {380					ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);381					*maybe_location = Some(location.clone());382383					AssetMetadatas::<T>::try_mutate(384						AssetIds::ForeignAssetId(foreign_asset_id),385						|maybe_asset_metadatas| -> DispatchResult {386							ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);387							*maybe_asset_metadatas = Some(metadata.clone());388							Ok(())389						},390					)391				},392			)?;393394			AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {395				*collection_id = Some(bounded_collection_id);396				Ok(())397			})398		})?;399400		Ok(foreign_asset_id)401	}402403	fn do_update_foreign_asset(404		foreign_asset_id: ForeignAssetId,405		location: &MultiLocation,406		metadata: &AssetMetadata<BalanceOf<T>>,407	) -> DispatchResult {408		ForeignAssetLocations::<T>::try_mutate(409			foreign_asset_id,410			|maybe_multi_locations| -> DispatchResult {411				let old_multi_locations = maybe_multi_locations412					.as_mut()413					.ok_or(Error::<T>::AssetIdNotExists)?;414415				AssetMetadatas::<T>::try_mutate(416					AssetIds::ForeignAssetId(foreign_asset_id),417					|maybe_asset_metadatas| -> DispatchResult {418						ensure!(419							maybe_asset_metadatas.is_some(),420							Error::<T>::AssetIdNotExists421						);422423						// modify location424						if location != old_multi_locations {425							LocationToCurrencyIds::<T>::remove(old_multi_locations.clone());426							LocationToCurrencyIds::<T>::try_mutate(427								location,428								|maybe_currency_ids| -> DispatchResult {429									ensure!(430										maybe_currency_ids.is_none(),431										Error::<T>::MultiLocationExisted432									);433									// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));434									*maybe_currency_ids = Some(foreign_asset_id);435									Ok(())436								},437							)?;438						}439						*maybe_asset_metadatas = Some(metadata.clone());440						*old_multi_locations = location.clone();441						Ok(())442					},443				)444			},445		)446	}447}448449pub use frame_support::{450	traits::{451		fungibles::{Balanced, CreditOf},452		tokens::currency::Currency as CurrencyT,453		OnUnbalanced as OnUnbalancedT,454	},455	weights::{WeightToFeePolynomial, WeightToFee},456};457458pub struct FreeForAll<459	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,460	AssetId: Get<MultiLocation>,461	AccountId,462	Currency: CurrencyT<AccountId>,463	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,464>(465	Weight,466	Currency::Balance,467	PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,468);469470impl<471		WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,472		AssetId: Get<MultiLocation>,473		AccountId,474		Currency: CurrencyT<AccountId>,475		OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,476	> WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>477{478	fn new() -> Self {479		Self(0, Zero::zero(), PhantomData)480	}481482	fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {483		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);484		Ok(payment)485	}486}487impl<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> Drop488	for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>489where490	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,491	AssetId: Get<MultiLocation>,492	Currency: CurrencyT<AccountId>,493	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,494{495	fn drop(&mut self) {496		OnUnbalanced::on_unbalanced(Currency::issue(self.1));497	}498}
after · pallets/foreign-assets/src/lib.rs
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//! - [`Config`]20//! - [`Call`]21//! - [`Pallet`]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//! ## Overview31//!32//! Under construction3334#![cfg_attr(not(feature = "std"), no_std)]35#![allow(clippy::unused_unit)]3637use frame_support::{38	dispatch::DispatchResult,39	ensure,40	pallet_prelude::*,41	traits::{fungible, fungibles, Currency, EnsureOrigin},42	RuntimeDebug,43};44use frame_system::pallet_prelude::*;45use up_data_structs::{CollectionMode};46use pallet_fungible::{Pallet as PalletFungible};47use scale_info::{TypeInfo};48use sp_runtime::{49	traits::{One, Zero},50	ArithmeticError,51};52use sp_std::{boxed::Box, vec::Vec};53use up_data_structs::{CollectionId, TokenId, CreateCollectionData};5455// NOTE:v1::MultiLocation is used in storages, we would need to do migration if upgrade the56// MultiLocation in the future.57use xcm::opaque::latest::prelude::XcmError;58use xcm::{v1::MultiLocation, VersionedMultiLocation};59use xcm_executor::{traits::WeightTrader, Assets};6061use pallet_common::erc::CrossAccountId;6263#[cfg(feature = "std")]64use serde::{Deserialize, Serialize};6566// TODO: Move to primitives67// Id of native currency.68// 0 - QTZ\UNQ69// 1 - KSM\DOT70#[derive(71	Clone,72	Copy,73	Eq,74	PartialEq,75	PartialOrd,76	Ord,77	MaxEncodedLen,78	RuntimeDebug,79	Encode,80	Decode,81	TypeInfo,82)]83#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]84pub enum NativeCurrency {85	Here = 0,86	Parent = 1,87}8889#[derive(90	Clone,91	Copy,92	Eq,93	PartialEq,94	PartialOrd,95	Ord,96	MaxEncodedLen,97	RuntimeDebug,98	Encode,99	Decode,100	TypeInfo,101)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub enum AssetIds {104	ForeignAssetId(ForeignAssetId),105	NativeAssetId(NativeCurrency),106}107108pub trait TryAsForeign<T, F> {109	fn try_as_foreign(asset: T) -> Option<F>;110}111112impl TryAsForeign<AssetIds, ForeignAssetId> for AssetIds {113	fn try_as_foreign(asset: AssetIds) -> Option<ForeignAssetId> {114		match asset {115			AssetIds::ForeignAssetId(id) => Some(id),116			_ => None,117		}118	}119}120121pub type ForeignAssetId = u32;122pub type CurrencyId = AssetIds;123124mod impl_fungibles;125pub mod weights;126127#[cfg(feature = "runtime-benchmarks")]128mod benchmarking;129130pub use module::*;131pub use weights::WeightInfo;132133/// Type alias for currency balance.134pub type BalanceOf<T> =135	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;136137/// A mapping between ForeignAssetId and AssetMetadata.138pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {139	/// Returns the AssetMetadata associated with a given ForeignAssetId.140	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;141	/// Returns the MultiLocation associated with a given ForeignAssetId.142	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;143	/// Returns the CurrencyId associated with a given MultiLocation.144	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;145}146147pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);148149impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>150	for XcmForeignAssetIdMapping<T>151{152	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {153		log::trace!(target: "fassets::asset_metadatas", "call");154		Pallet::<T>::asset_metadatas(AssetIds::ForeignAssetId(foreign_asset_id))155	}156157	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {158		log::trace!(target: "fassets::get_multi_location", "call");159		Pallet::<T>::foreign_asset_locations(foreign_asset_id)160	}161162	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {163		log::trace!(target: "fassets::get_currency_id", "call");164		Some(AssetIds::ForeignAssetId(165			Pallet::<T>::location_to_currency_ids(multi_location).unwrap_or(0),166		))167	}168}169170#[frame_support::pallet]171pub mod module {172	use super::*;173174	#[pallet::config]175	pub trait Config:176		frame_system::Config177		+ pallet_common::Config178		+ pallet_fungible::Config179		+ orml_tokens::Config180		+ pallet_balances::Config181	{182		/// The overarching event type.183		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;184185		/// Currency type for withdraw and balance storage.186		type Currency: Currency<Self::AccountId>;187188		/// Required origin for registering asset.189		type RegisterOrigin: EnsureOrigin<Self::Origin>;190191		/// Weight information for the extrinsics in this module.192		type WeightInfo: WeightInfo;193	}194195	#[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo)]196	pub struct AssetMetadata<Balance> {197		pub name: Vec<u8>,198		pub symbol: Vec<u8>,199		pub decimals: u8,200		pub minimal_balance: Balance,201	}202203	#[pallet::error]204	pub enum Error<T> {205		/// The given location could not be used (e.g. because it cannot be expressed in the206		/// desired version of XCM).207		BadLocation,208		/// MultiLocation existed209		MultiLocationExisted,210		/// AssetId not exists211		AssetIdNotExists,212		/// AssetId exists213		AssetIdExisted,214	}215216	#[pallet::event]217	#[pallet::generate_deposit(fn deposit_event)]218	pub enum Event<T: Config> {219		/// The foreign asset registered.220		ForeignAssetRegistered {221			asset_id: ForeignAssetId,222			asset_address: MultiLocation,223			metadata: AssetMetadata<BalanceOf<T>>,224		},225		/// The foreign asset updated.226		ForeignAssetUpdated {227			asset_id: ForeignAssetId,228			asset_address: MultiLocation,229			metadata: AssetMetadata<BalanceOf<T>>,230		},231		/// The asset registered.232		AssetRegistered {233			asset_id: AssetIds,234			metadata: AssetMetadata<BalanceOf<T>>,235		},236		/// The asset updated.237		AssetUpdated {238			asset_id: AssetIds,239			metadata: AssetMetadata<BalanceOf<T>>,240		},241	}242243	/// Next available Foreign AssetId ID.244	///245	/// NextForeignAssetId: ForeignAssetId246	#[pallet::storage]247	#[pallet::getter(fn next_foreign_asset_id)]248	pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;249	/// The storages for MultiLocations.250	///251	/// ForeignAssetLocations: map ForeignAssetId => Option<MultiLocation>252	#[pallet::storage]253	#[pallet::getter(fn foreign_asset_locations)]254	pub type ForeignAssetLocations<T: Config> =255		StorageMap<_, Twox64Concat, ForeignAssetId, MultiLocation, OptionQuery>;256257	/// The storages for CurrencyIds.258	///259	/// LocationToCurrencyIds: map MultiLocation => Option<ForeignAssetId>260	#[pallet::storage]261	#[pallet::getter(fn location_to_currency_ids)]262	pub type LocationToCurrencyIds<T: Config> =263		StorageMap<_, Twox64Concat, MultiLocation, ForeignAssetId, OptionQuery>;264265	/// The storages for AssetMetadatas.266	///267	/// AssetMetadatas: map AssetIds => Option<AssetMetadata>268	#[pallet::storage]269	#[pallet::getter(fn asset_metadatas)]270	pub type AssetMetadatas<T: Config> =271		StorageMap<_, Twox64Concat, AssetIds, AssetMetadata<BalanceOf<T>>, OptionQuery>;272273	/// The storages for assets to fungible collection binding274	///275	#[pallet::storage]276	#[pallet::getter(fn asset_binding)]277	pub type AssetBinding<T: Config> =278		StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;279280	#[pallet::pallet]281	#[pallet::without_storage_info]282	pub struct Pallet<T>(_);283284	#[pallet::call]285	impl<T: Config> Pallet<T> {286		#[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]287		pub fn register_foreign_asset(288			origin: OriginFor<T>,289			owner: T::AccountId,290			location: Box<VersionedMultiLocation>,291			metadata: Box<AssetMetadata<BalanceOf<T>>>,292		) -> DispatchResult {293			T::RegisterOrigin::ensure_origin(origin.clone())?;294295			let location: MultiLocation = (*location)296				.try_into()297				.map_err(|()| Error::<T>::BadLocation)?;298299			let md = metadata.clone();300			let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();301			let mut description: Vec<u16> = "Foreign assets collection for "302				.encode_utf16()303				.collect::<Vec<u16>>();304			description.append(&mut name.clone());305306			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {307				name: name.try_into().unwrap(),308				description: description.try_into().unwrap(),309				mode: CollectionMode::Fungible(md.decimals),310				..Default::default()311			};312			let owner = T::CrossAccountId::from_sub(owner);313			let bounded_collection_id = <PalletFungible<T>>::init_foreign_collection(314				owner.clone(),315				owner,316				data,317			)?;318			let foreign_asset_id =319				Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;320321			Self::deposit_event(Event::<T>::ForeignAssetRegistered {322				asset_id: foreign_asset_id,323				asset_address: location,324				metadata: *metadata,325			});326			Ok(())327		}328329		#[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]330		pub fn update_foreign_asset(331			origin: OriginFor<T>,332			foreign_asset_id: ForeignAssetId,333			location: Box<VersionedMultiLocation>,334			metadata: Box<AssetMetadata<BalanceOf<T>>>,335		) -> DispatchResult {336			T::RegisterOrigin::ensure_origin(origin)?;337338			let location: MultiLocation = (*location)339				.try_into()340				.map_err(|()| Error::<T>::BadLocation)?;341			Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;342343			Self::deposit_event(Event::<T>::ForeignAssetUpdated {344				asset_id: foreign_asset_id,345				asset_address: location,346				metadata: *metadata,347			});348			Ok(())349		}350	}351}352353impl<T: Config> Pallet<T> {354	fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {355		NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {356			let id = *current;357			*current = current358				.checked_add(One::one())359				.ok_or(ArithmeticError::Overflow)?;360			Ok(id)361		})362	}363364	fn do_register_foreign_asset(365		location: &MultiLocation,366		metadata: &AssetMetadata<BalanceOf<T>>,367		bounded_collection_id: CollectionId,368	) -> Result<ForeignAssetId, DispatchError> {369		let foreign_asset_id = Self::get_next_foreign_asset_id()?;370		LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {371			ensure!(372				maybe_currency_ids.is_none(),373				Error::<T>::MultiLocationExisted374			);375			*maybe_currency_ids = Some(foreign_asset_id);376			// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));377378			ForeignAssetLocations::<T>::try_mutate(379				foreign_asset_id,380				|maybe_location| -> DispatchResult {381					ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);382					*maybe_location = Some(location.clone());383384					AssetMetadatas::<T>::try_mutate(385						AssetIds::ForeignAssetId(foreign_asset_id),386						|maybe_asset_metadatas| -> DispatchResult {387							ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);388							*maybe_asset_metadatas = Some(metadata.clone());389							Ok(())390						},391					)392				},393			)?;394395			AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {396				*collection_id = Some(bounded_collection_id);397				Ok(())398			})399		})?;400401		Ok(foreign_asset_id)402	}403404	fn do_update_foreign_asset(405		foreign_asset_id: ForeignAssetId,406		location: &MultiLocation,407		metadata: &AssetMetadata<BalanceOf<T>>,408	) -> DispatchResult {409		ForeignAssetLocations::<T>::try_mutate(410			foreign_asset_id,411			|maybe_multi_locations| -> DispatchResult {412				let old_multi_locations = maybe_multi_locations413					.as_mut()414					.ok_or(Error::<T>::AssetIdNotExists)?;415416				AssetMetadatas::<T>::try_mutate(417					AssetIds::ForeignAssetId(foreign_asset_id),418					|maybe_asset_metadatas| -> DispatchResult {419						ensure!(420							maybe_asset_metadatas.is_some(),421							Error::<T>::AssetIdNotExists422						);423424						// modify location425						if location != old_multi_locations {426							LocationToCurrencyIds::<T>::remove(old_multi_locations.clone());427							LocationToCurrencyIds::<T>::try_mutate(428								location,429								|maybe_currency_ids| -> DispatchResult {430									ensure!(431										maybe_currency_ids.is_none(),432										Error::<T>::MultiLocationExisted433									);434									// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));435									*maybe_currency_ids = Some(foreign_asset_id);436									Ok(())437								},438							)?;439						}440						*maybe_asset_metadatas = Some(metadata.clone());441						*old_multi_locations = location.clone();442						Ok(())443					},444				)445			},446		)447	}448}449450pub use frame_support::{451	traits::{452		fungibles::{Balanced, CreditOf},453		tokens::currency::Currency as CurrencyT,454		OnUnbalanced as OnUnbalancedT,455	},456	weights::{WeightToFeePolynomial, WeightToFee},457};458459pub struct FreeForAll<460	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,461	AssetId: Get<MultiLocation>,462	AccountId,463	Currency: CurrencyT<AccountId>,464	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,465>(466	Weight,467	Currency::Balance,468	PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,469);470471impl<472		WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,473		AssetId: Get<MultiLocation>,474		AccountId,475		Currency: CurrencyT<AccountId>,476		OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,477	> WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>478{479	fn new() -> Self {480		Self(0, Zero::zero(), PhantomData)481	}482483	fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {484		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);485		Ok(payment)486	}487}488impl<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> Drop489	for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>490where491	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,492	AssetId: Get<MultiLocation>,493	Currency: CurrencyT<AccountId>,494	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,495{496	fn drop(&mut self) {497		OnUnbalanced::on_unbalanced(Currency::issue(self.1));498	}499}
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -210,18 +210,21 @@
 	/// Initializes the collection. Returns [CollectionId] on success, [DispatchError] otherwise.
 	pub fn init_collection(
 		owner: T::CrossAccountId,
+		payer: T::CrossAccountId,
 		data: CreateCollectionData<T::AccountId>,
 	) -> Result<CollectionId, DispatchError> {
-		<PalletCommon<T>>::init_collection(owner, data, CollectionFlags::default())
+		<PalletCommon<T>>::init_collection(owner, payer, data, CollectionFlags::default())
 	}
 
 	/// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise.
 	pub fn init_foreign_collection(
 		owner: T::CrossAccountId,
+		payer: T::CrossAccountId,
 		data: CreateCollectionData<T::AccountId>,
 	) -> Result<CollectionId, DispatchError> {
 		let id = <PalletCommon<T>>::init_collection(
 			owner,
+			payer,
 			data,
 			CollectionFlags {
 				foreign: true,
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -405,11 +405,13 @@
 	/// - `data`: Contains settings for collection limits and permissions.
 	pub fn init_collection(
 		owner: T::CrossAccountId,
+		payer: T::CrossAccountId,
 		data: CreateCollectionData<T::AccountId>,
 		is_external: bool,
 	) -> Result<CollectionId, DispatchError> {
 		<PalletCommon<T>>::init_collection(
 			owner,
+			payer,
 			data,
 			CollectionFlags {
 				external: is_external,
modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -1448,7 +1448,7 @@
 		data: CreateCollectionData<T::AccountId>,
 		properties: impl Iterator<Item = Property>,
 	) -> Result<CollectionId, DispatchError> {
-		let collection_id = <PalletNft<T>>::init_collection(sender, data, true);
+		let collection_id = <PalletNft<T>>::init_collection(sender.clone(), sender, data, true);
 
 		if let Err(DispatchError::Arithmetic(_)) = &collection_id {
 			return Err(<Error<T>>::NoAvailableCollectionId.into());
modifiedpallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-equip/src/lib.rs
+++ b/pallets/proxy-rmrk-equip/src/lib.rs
@@ -251,7 +251,7 @@
 			};
 
 			let collection_id_res =
-				<PalletNft<T>>::init_collection(cross_sender.clone(), data, true);
+				<PalletNft<T>>::init_collection(cross_sender.clone(), cross_sender.clone(), data, true);
 
 			if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {
 				return Err(<Error<T>>::NoAvailableBaseId.into());
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -373,9 +373,10 @@
 	/// - `data`: Contains settings for collection limits and permissions.
 	pub fn init_collection(
 		owner: T::CrossAccountId,
+		payer: T::CrossAccountId,
 		data: CreateCollectionData<T::AccountId>,
 	) -> Result<CollectionId, DispatchError> {
-		<PalletCommon<T>>::init_collection(owner, data, CollectionFlags::default())
+		<PalletCommon<T>>::init_collection(owner, payer, data, CollectionFlags::default())
 	}
 
 	/// Destroy RFT collection
modifiedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -28,7 +28,7 @@
 		static_property::{key, value as property_value},
 	},
 };
-use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
+use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder};
 use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};
 use up_data_structs::{
 	CollectionName, CollectionDescription, CollectionTokenPrefix, CreateCollectionData,
@@ -156,6 +156,7 @@
 	T: Config + pallet_nonfungible::Config + pallet_refungible::Config,
 >(
 	caller: caller,
+	value: value,
 	name: string,
 	description: string,
 	token_prefix: string,
@@ -172,8 +173,16 @@
 		base_uri_value,
 		add_properties,
 	)?;
+	let value = value.as_u128();
+	let creation_price: Result<u128> = T::CollectionCreationPrice::get()
+		.try_into()
+		.map_err(|_| "collection creation price should be convertible to u128".into());
+	if value != creation_price? {
+		return Err("Sent amount not equals to collection creation price".into());
+	}
+	let collection_helpers_address =  T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
 
-	let collection_id = T::CollectionDispatch::create(caller.clone(), data)
+	let collection_id = T::CollectionDispatch::create(caller.clone(), 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)
@@ -183,7 +192,7 @@
 #[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents))]
 impl<T> EvmCollectionHelpers<T>
 where
-	T: Config + pallet_nonfungible::Config + pallet_refungible::Config,
+	T: Config + pallet_common::Config + pallet_nonfungible::Config + pallet_refungible::Config,
 {
 	/// Create an NFT collection
 	/// @param name Name of the collection
@@ -209,8 +218,17 @@
 			Default::default(),
 			false,
 		)?;
-		let collection_id = T::CollectionDispatch::create(caller, data)
-			.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+		let value = value.as_u128();
+		let creation_price: Result<u128> = T::CollectionCreationPrice::get()
+			.try_into()
+			.map_err(|_| "collection creation price should be convertible to u128".into());
+		let creation_price = creation_price?;
+		if value != creation_price {
+			return Err(format!("Sent amount not equals to collection creation price ({0})", creation_price).into());
+		}
+		let collection_helpers_address =  T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
+		let collection_id =
+			T::CollectionDispatch::create(caller, collection_helpers_address, data).map_err(dispatch_to_evm::<T>)?;
 
 		let address = pallet_common::eth::collection_id_to_address(collection_id);
 		Ok(address)
@@ -221,6 +239,7 @@
 	fn create_nonfungible_collection_with_properties(
 		&mut self,
 		caller: caller,
+		value: value,
 		name: string,
 		description: string,
 		token_prefix: string,
@@ -236,7 +255,15 @@
 			base_uri_value,
 			true,
 		)?;
-		let collection_id = T::CollectionDispatch::create(caller, data)
+		let value = value.as_u128();
+		let creation_price: Result<u128> = T::CollectionCreationPrice::get()
+			.try_into()
+			.map_err(|_| "collection creation price should be convertible to u128".into());
+		if value != creation_price? {
+			return Err("Sent amount not equals to collection creation price".into());
+		}
+		let collection_helpers_address =  T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
+		let collection_id = T::CollectionDispatch::create(caller, 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);
@@ -248,12 +275,14 @@
 	fn create_refungible_collection(
 		&mut self,
 		caller: caller,
+		value: value,
 		name: string,
 		description: string,
 		token_prefix: string,
 	) -> Result<address> {
 		create_refungible_collection_internal::<T>(
 			caller,
+			value,
 			name,
 			description,
 			token_prefix,
@@ -267,6 +296,7 @@
 	fn create_refungible_collection_with_properties(
 		&mut self,
 		caller: caller,
+		value: value,
 		name: string,
 		description: string,
 		token_prefix: string,
@@ -274,6 +304,7 @@
 	) -> Result<address> {
 		create_refungible_collection_internal::<T>(
 			caller,
+			value,
 			name,
 			description,
 			token_prefix,
modifiedpallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth
--- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol
+++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol
@@ -36,7 +36,7 @@
 		string memory name,
 		string memory description,
 		string memory tokenPrefix
-	) public returns (address) {
+	) public payable returns (address) {
 		require(false, stub_error);
 		name;
 		description;
@@ -52,7 +52,7 @@
 		string memory description,
 		string memory tokenPrefix,
 		string memory baseUri
-	) public returns (address) {
+	) public payable returns (address) {
 		require(false, stub_error);
 		name;
 		description;
@@ -68,7 +68,7 @@
 		string memory name,
 		string memory description,
 		string memory tokenPrefix
-	) public returns (address) {
+	) public payable returns (address) {
 		require(false, stub_error);
 		name;
 		description;
@@ -84,7 +84,7 @@
 		string memory description,
 		string memory tokenPrefix,
 		string memory baseUri
-	) public returns (address) {
+	) public payable returns (address) {
 		require(false, stub_error);
 		name;
 		description;
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -344,8 +344,8 @@
 			let sender = ensure_signed(origin)?;
 
 			// =========
-
-			let _id = T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;
+			let sender = T::CrossAccountId::from_sub(sender);
+			let _id = T::CollectionDispatch::create(sender.clone(), sender, data)?;
 
 			Ok(())
 		}
modifiedruntime/common/dispatch.rsdiffbeforeafterboth
--- a/runtime/common/dispatch.rs
+++ b/runtime/common/dispatch.rs
@@ -55,21 +55,22 @@
 {
 	fn create(
 		sender: T::CrossAccountId,
+		payer: T::CrossAccountId,
 		data: CreateCollectionData<T::AccountId>,
 	) -> Result<CollectionId, DispatchError> {
 		let id = match data.mode {
-			CollectionMode::NFT => <PalletNonfungible<T>>::init_collection(sender, data, false)?,
+			CollectionMode::NFT => <PalletNonfungible<T>>::init_collection(sender, payer, data, false)?,
 			CollectionMode::Fungible(decimal_points) => {
 				// check params
 				ensure!(
 					decimal_points <= MAX_DECIMAL_POINTS,
 					pallet_unique::Error::<T>::CollectionDecimalPointLimitExceeded
 				);
-				<PalletFungible<T>>::init_collection(sender, data)?
+				<PalletFungible<T>>::init_collection(sender, payer, data)?
 			}
 
 			#[cfg(feature = "refungible")]
-			CollectionMode::ReFungible => <PalletRefungible<T>>::init_collection(sender, data)?,
+			CollectionMode::ReFungible => <PalletRefungible<T>>::init_collection(sender, payer, data)?,
 
 			#[cfg(not(feature = "refungible"))]
 			CollectionMode::ReFungible => return unsupported!(T),
modifiedtests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth
--- a/tests/src/eth/api/CollectionHelpers.sol
+++ b/tests/src/eth/api/CollectionHelpers.sol
@@ -31,7 +31,7 @@
 		string memory name,
 		string memory description,
 		string memory tokenPrefix
-	) external returns (address);
+	) external payable returns (address);
 
 	/// @dev EVM selector for this function is: 0xa634a5f9,
 	///  or in textual repr: createERC721MetadataCompatibleCollection(string,string,string,string)
@@ -40,7 +40,7 @@
 		string memory description,
 		string memory tokenPrefix,
 		string memory baseUri
-	) external returns (address);
+	) external payable returns (address);
 
 	/// @dev EVM selector for this function is: 0xab173450,
 	///  or in textual repr: createRFTCollection(string,string,string)
@@ -48,7 +48,7 @@
 		string memory name,
 		string memory description,
 		string memory tokenPrefix
-	) external returns (address);
+	) external payable returns (address);
 
 	/// @dev EVM selector for this function is: 0xa5596388,
 	///  or in textual repr: createERC721MetadataCompatibleRFTCollection(string,string,string,string)
@@ -57,7 +57,7 @@
 		string memory description,
 		string memory tokenPrefix,
 		string memory baseUri
-	) external returns (address);
+	) external payable returns (address);
 
 	/// Check if a collection exists
 	/// @param collectionAddress Address of the collection in question
modifiedtests/src/eth/collectionHelpersAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/collectionHelpersAbi.json
+++ b/tests/src/eth/collectionHelpersAbi.json
@@ -27,7 +27,7 @@
     ],
     "name": "createERC721MetadataCompatibleCollection",
     "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "nonpayable",
+    "stateMutability": "payable",
     "type": "function"
   },
   {
@@ -39,7 +39,7 @@
     ],
     "name": "createERC721MetadataCompatibleRFTCollection",
     "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "nonpayable",
+    "stateMutability": "payable",
     "type": "function"
   },
   {
@@ -50,7 +50,7 @@
     ],
     "name": "createNonfungibleCollection",
     "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "nonpayable",
+    "stateMutability": "payable",
     "type": "function"
   },
   {
@@ -61,7 +61,7 @@
     ],
     "name": "createRFTCollection",
     "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "nonpayable",
+    "stateMutability": "payable",
     "type": "function"
   },
   {
modifiedtests/src/eth/fractionalizer/Fractionalizer.soldiffbeforeafterboth
--- a/tests/src/eth/fractionalizer/Fractionalizer.sol
+++ b/tests/src/eth/fractionalizer/Fractionalizer.sol
@@ -18,7 +18,8 @@
 	mapping(address => bool) nftCollectionAllowList;
 	mapping(address => mapping(uint256 => uint256)) public nft2rftMapping;
 	mapping(address => Token) public rft2nftMapping;
-	bytes32 refungibleCollectionType = keccak256(bytes("ReFungible"));
+	//use constant to reduce gas cost
+	bytes32 constant refungibleCollectionType = keccak256(bytes("ReFungible"));
 
 	receive() external payable onlyOwner {}
 
@@ -51,11 +52,12 @@
 	///  Throws if `msg.sender` is not owner or admin of provided RFT collection.
 	///  Can only be called by contract owner.
 	/// @param _collection address of RFT collection.
-	function setRFTCollection(address _collection) public onlyOwner {
+	function setRFTCollection(address _collection) external onlyOwner {
 		require(rftCollection == address(0), "RFT collection is already set");
 		UniqueRefungible refungibleContract = UniqueRefungible(_collection);
 		string memory collectionType = refungibleContract.uniqueCollectionType();
 
+		// compare hashed to reduce gas cost
 		require(
 			keccak256(bytes(collectionType)) == refungibleCollectionType,
 			"Wrong collection type. Collection is not refungible."
@@ -79,7 +81,7 @@
 		string calldata _name,
 		string calldata _description,
 		string calldata _tokenPrefix
-	) public onlyOwner {
+	) external onlyOwner {
 		require(rftCollection == address(0), "RFT collection is already set");
 		address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;
 		rftCollection = CollectionHelpers(collectionHelpers).createRFTCollection(_name, _description, _tokenPrefix);
@@ -90,7 +92,7 @@
 	/// @dev Can only be called by contract owner.
 	/// @param collection NFT token address.
 	/// @param status `true` to allow and `false` to disallow NFT token.
-	function setNftCollectionIsAllowed(address collection, bool status) public onlyOwner {
+	function setNftCollectionIsAllowed(address collection, bool status) external onlyOwner {
 		nftCollectionAllowList[collection] = status;
 		emit AllowListSet(collection, status);
 	}
@@ -109,7 +111,7 @@
 		address _collection,
 		uint256 _token,
 		uint128 _pieces
-	) public {
+	) external {
 		require(rftCollection != address(0), "RFT collection is not set");
 		UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection);
 		require(
@@ -148,7 +150,7 @@
 	///  Throws if `msg.sender` isn't owner of all RFT token pieces.
 	/// @param _collection RFT collection address
 	/// @param _token id of RFT token
-	function rft2nft(address _collection, uint256 _token) public {
+	function rft2nft(address _collection, uint256 _token) external {
 		require(rftCollection != address(0), "RFT collection is not set");
 		require(rftCollection == _collection, "Wrong RFT collection");
 		UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection);
modifiedtests/src/eth/payable.test.tsdiffbeforeafterboth
--- a/tests/src/eth/payable.test.ts
+++ b/tests/src/eth/payable.test.ts
@@ -140,16 +140,13 @@
   });
   
   itEth('Fee for nested calls to native methods is withdrawn from the user', async({helper}) => {
-    const CONTRACT_BALANCE = 3n * helper.balance.getOneTokenNominal();
+    const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();
 
     const deployer = await helper.eth.createAccountWithBalance(donor);
     const caller = await helper.eth.createAccountWithBalance(donor);
     const contract = await deployProxyContract(helper, deployer);
-    
-    const web3 = helper.getWeb3();
-    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS});
 
-    const collectionAddress = (await contract.methods.createNonfungibleCollection().send({from: caller})).events.CollectionCreated.returnValues.collection;
+    const collectionAddress = (await contract.methods.createNonfungibleCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection;
     const initialCallerBalance = await helper.balance.getEthereum(caller);
     const initialContractBalance = await helper.balance.getEthereum(contract.options.address);
     await contract.methods.mintNftToken(collectionAddress).send({from: caller});
@@ -160,22 +157,18 @@
   });
   
   itEth('Fee for nested calls to create*Collection methods is withdrawn from the user and from the contract', async({helper}) => {
-    const CONTRACT_BALANCE = 3n * helper.balance.getOneTokenNominal();
-
+    const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();
     const deployer = await helper.eth.createAccountWithBalance(donor);
     const caller = await helper.eth.createAccountWithBalance(donor);
     const contract = await deployProxyContract(helper, deployer);
-    
-    const web3 = helper.getWeb3();
-    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS});
 
     const initialCallerBalance = await helper.balance.getEthereum(caller);
     const initialContractBalance = await helper.balance.getEthereum(contract.options.address);
-    await contract.methods.createNonfungibleCollection().send({from: caller});
+    await contract.methods.createNonfungibleCollection().send({from: caller, value: Number(CONTRACT_BALANCE)});
     const finalCallerBalance = await helper.balance.getEthereum(caller);
     const finalContractBalance = await helper.balance.getEthereum(contract.options.address);
     expect(finalCallerBalance < initialCallerBalance).to.be.true;
-    expect(finalContractBalance < initialContractBalance).to.be.true;
+    expect(finalContractBalance == initialContractBalance).to.be.true;
   });
 
   async function deployProxyContract(helper: EthUniqueHelper, deployer: string) {
@@ -189,6 +182,8 @@
       import {CollectionHelpers} from "../api/CollectionHelpers.sol";
       import {UniqueNFT} from "../api/UniqueNFT.sol";
 
+      error Value(uint256 value);
+
       contract ProxyContract {
         bool value = false;
         address flipper;
@@ -207,30 +202,30 @@
           Flipper(flipper).flip();
         }
 
-        function createNonfungibleCollection() public {
+        function createNonfungibleCollection() external payable {
           address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;
-		      address nftCollection = CollectionHelpers(collectionHelpers).createNonfungibleCollection("A", "B", "C");
+		      address nftCollection = CollectionHelpers(collectionHelpers).createNonfungibleCollection{value: msg.value}("A", "B", "C");
           emit CollectionCreated(nftCollection);
         }
 
-        function mintNftToken(address collectionAddress) public {
+        function mintNftToken(address collectionAddress) external  {
           UniqueNFT collection = UniqueNFT(collectionAddress);
           uint256 tokenId = collection.nextTokenId();
           collection.mint(msg.sender, tokenId);
           emit TokenMinted(tokenId);
         }
 
-        function getValue() public view returns (bool) {
+        function getValue() external view returns (bool) {
           return Flipper(flipper).getValue();
         }
       }
 
       contract Flipper {
         bool value = false;
-        function flip() public {
+        function flip() external {
           value = !value;
         }
-        function getValue() public view returns (bool) {
+        function getValue() external view returns (bool) {
           return value;
         }
       }