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

difftreelog

Foreign assets benchmark

Dev2022-09-12parent: #838ab9f.patch.diff
in: master

8 files changed

modifiedMakefilediffbeforeafterboth
--- a/Makefile
+++ b/Makefile
@@ -128,5 +128,9 @@
 bench-rmrk-equip:
 	make _bench PALLET=proxy-rmrk-equip
 
+.PHONY: bench-foreign-assets
+bench-foreign-assets:
+	make _bench PALLET=foreign-assets	
+
 .PHONY: bench
-bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible bench-scheduler bench-rmrk-core bench-rmrk-equip
+bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible bench-scheduler bench-rmrk-core bench-rmrk-equip bench-foreign-assets
modifiedpallets/foreign-assets/Cargo.tomldiffbeforeafterboth
--- a/pallets/foreign-assets/Cargo.toml
+++ b/pallets/foreign-assets/Cargo.toml
@@ -22,8 +22,7 @@
 xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.27", default-features = false }
 #orml-tokens = { git = 'https://github.com/UniqueNetwork/open-runtime-module-library', branch = 'unique-polkadot-v0.9.24', version = "0.4.1-dev", default-features = false }
 orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.27", version = "0.4.1-dev", default-features = false }
-#git = "https://github.com/open-web3-stack/open-runtime-module-library"
-#branch = "polkadot-v0.9.27"
+frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" }
 
 [dev-dependencies]
 serde_json = "1.0.68"
@@ -51,3 +50,4 @@
 	"orml-tokens/std"
 ]
 try-runtime = ["frame-support/try-runtime"]
+runtime-benchmarks = ['frame-benchmarking', 'pallet-common/runtime-benchmarks']
\ No newline at end of file
addedpallets/foreign-assets/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/foreign-assets/src/benchmarking.rs
@@ -0,0 +1,76 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+#![allow(missing_docs)]
+
+use super::{Config, Pallet};
+use frame_benchmarking::{benchmarks, account};
+use frame_system::RawOrigin;
+use crate::AssetMetadata;
+use crate::Pallet as ForeignAssets;
+use xcm::opaque::latest::Junction::Parachain;
+use xcm::VersionedMultiLocation;
+use frame_support::{
+	traits::{Currency},
+};
+use sp_std::boxed::Box;
+
+benchmarks! {
+	register_foreign_asset {
+		let origin: RawOrigin<T::AccountId> = frame_system::RawOrigin::Root;
+		let owner: T::AccountId = account("user", 0, 1);
+		let location: VersionedMultiLocation = VersionedMultiLocation::from(Parachain(1000).into());
+		let metadata: AssetMetadata<<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance> = AssetMetadata{
+			name: "name".into(),
+			symbol: "symbol".into(),
+			decimals: 18,
+			minimal_balance: 1u32.into()
+		};
+		let mut balance: <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance =
+		   4_000_000_000u32.into();
+		balance = balance * balance;
+		<T as Config>::Currency::make_free_balance_be(&owner,
+			balance);
+	}: {
+		ForeignAssets::<T>::register_foreign_asset(origin.into(), owner, Box::new(location), Box::new(metadata))?
+	}
+
+	update_foreign_asset {
+		let origin: RawOrigin<T::AccountId> = frame_system::RawOrigin::Root;
+		let owner: T::AccountId = account("user", 0, 1);
+		let location: VersionedMultiLocation = VersionedMultiLocation::from(Parachain(2000).into());
+		let metadata: AssetMetadata<<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance> = AssetMetadata{
+			name: "name".into(),
+			symbol: "symbol".into(),
+			decimals: 18,
+			minimal_balance: 1u32.into()
+		};
+		let metadata2: AssetMetadata<<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance> = AssetMetadata{
+			name: "name2".into(),
+			symbol: "symbol2".into(),
+			decimals: 18,
+			minimal_balance: 1u32.into()
+		};
+		let mut balance: <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance =
+		   4_000_000_000u32.into();
+		balance = balance * balance;
+		<T as Config>::Currency::make_free_balance_be(&owner, balance);
+		ForeignAssets::<T>::register_foreign_asset(origin.clone().into(), owner, Box::new(location.clone()), Box::new(metadata))?;
+
+	}: {
+		ForeignAssets::<T>::update_foreign_asset(origin.into(), 0, Box::new(location), Box::new(metadata2))?
+	}
+}
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;125mod weights;126127pub use module::*;128pub use weights::WeightInfo;129130/// Type alias for currency balance.131pub type BalanceOf<T> =132	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;133134/// A mapping between ForeignAssetId and AssetMetadata.135pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {136	/// Returns the AssetMetadata associated with a given ForeignAssetId.137	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;138	/// Returns the MultiLocation associated with a given ForeignAssetId.139	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;140	/// Returns the CurrencyId associated with a given MultiLocation.141	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;142}143144pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);145146impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>147	for XcmForeignAssetIdMapping<T>148{149	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {150		log::trace!(target: "fassets::asset_metadatas", "call");151		Pallet::<T>::asset_metadatas(AssetIds::ForeignAssetId(foreign_asset_id))152	}153154	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {155		log::trace!(target: "fassets::get_multi_location", "call");156		Pallet::<T>::foreign_asset_locations(foreign_asset_id)157	}158159	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {160		log::trace!(target: "fassets::get_currency_id", "call");161		Some(AssetIds::ForeignAssetId(162			Pallet::<T>::location_to_currency_ids(multi_location).unwrap_or(0),163		))164	}165}166167#[frame_support::pallet]168pub mod module {169	use super::*;170171	#[pallet::config]172	pub trait Config:173		frame_system::Config174		+ pallet_common::Config175		+ pallet_fungible::Config176		+ orml_tokens::Config177		+ pallet_balances::Config178	{179		/// The overarching event type.180		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;181182		/// Currency type for withdraw and balance storage.183		type Currency: Currency<Self::AccountId>;184185		/// Required origin for registering asset.186		type RegisterOrigin: EnsureOrigin<Self::Origin>;187188		/// Weight information for the extrinsics in this module.189		type WeightInfo: WeightInfo;190	}191192	#[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo)]193	pub struct AssetMetadata<Balance> {194		pub name: Vec<u8>,195		pub symbol: Vec<u8>,196		pub decimals: u8,197		pub minimal_balance: Balance,198	}199200	#[pallet::error]201	pub enum Error<T> {202		/// The given location could not be used (e.g. because it cannot be expressed in the203		/// desired version of XCM).204		BadLocation,205		/// MultiLocation existed206		MultiLocationExisted,207		/// AssetId not exists208		AssetIdNotExists,209		/// AssetId exists210		AssetIdExisted,211	}212213	#[pallet::event]214	#[pallet::generate_deposit(fn deposit_event)]215	pub enum Event<T: Config> {216		/// The foreign asset registered.217		ForeignAssetRegistered {218			asset_id: ForeignAssetId,219			asset_address: MultiLocation,220			metadata: AssetMetadata<BalanceOf<T>>,221		},222		/// The foreign asset updated.223		ForeignAssetUpdated {224			asset_id: ForeignAssetId,225			asset_address: MultiLocation,226			metadata: AssetMetadata<BalanceOf<T>>,227		},228		/// The asset registered.229		AssetRegistered {230			asset_id: AssetIds,231			metadata: AssetMetadata<BalanceOf<T>>,232		},233		/// The asset updated.234		AssetUpdated {235			asset_id: AssetIds,236			metadata: AssetMetadata<BalanceOf<T>>,237		},238	}239240	/// Next available Foreign AssetId ID.241	///242	/// NextForeignAssetId: ForeignAssetId243	#[pallet::storage]244	#[pallet::getter(fn next_foreign_asset_id)]245	pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;246	/// The storages for MultiLocations.247	///248	/// ForeignAssetLocations: map ForeignAssetId => Option<MultiLocation>249	#[pallet::storage]250	#[pallet::getter(fn foreign_asset_locations)]251	pub type ForeignAssetLocations<T: Config> =252		StorageMap<_, Twox64Concat, ForeignAssetId, MultiLocation, OptionQuery>;253254	/// The storages for CurrencyIds.255	///256	/// LocationToCurrencyIds: map MultiLocation => Option<ForeignAssetId>257	#[pallet::storage]258	#[pallet::getter(fn location_to_currency_ids)]259	pub type LocationToCurrencyIds<T: Config> =260		StorageMap<_, Twox64Concat, MultiLocation, ForeignAssetId, OptionQuery>;261262	/// The storages for AssetMetadatas.263	///264	/// AssetMetadatas: map AssetIds => Option<AssetMetadata>265	#[pallet::storage]266	#[pallet::getter(fn asset_metadatas)]267	pub type AssetMetadatas<T: Config> =268		StorageMap<_, Twox64Concat, AssetIds, AssetMetadata<BalanceOf<T>>, OptionQuery>;269270	/// The storages for assets to fungible collection binding271	///272	#[pallet::storage]273	#[pallet::getter(fn asset_binding)]274	pub type AssetBinding<T: Config> =275		StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;276277	#[pallet::pallet]278	#[pallet::without_storage_info]279	pub struct Pallet<T>(_);280281	#[pallet::call]282	impl<T: Config> Pallet<T> {283		#[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]284		pub fn register_foreign_asset(285			origin: OriginFor<T>,286			owner: T::AccountId,287			location: Box<VersionedMultiLocation>,288			metadata: Box<AssetMetadata<BalanceOf<T>>>,289		) -> DispatchResult {290			T::RegisterOrigin::ensure_origin(origin.clone())?;291292			let location: MultiLocation = (*location)293				.try_into()294				.map_err(|()| Error::<T>::BadLocation)?;295296			let md = metadata.clone();297			let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();298			let mut description: Vec<u16> = "Foreign assets collection for "299				.encode_utf16()300				.collect::<Vec<u16>>();301			description.append(&mut name.clone());302303			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {304				name: name.try_into().unwrap(),305				description: description.try_into().unwrap(),306				mode: CollectionMode::Fungible(18),307				..Default::default()308			};309310			let bounded_collection_id = <PalletFungible<T>>::init_foreign_collection(311				CrossAccountId::from_sub(owner),312				data,313			)?;314			let foreign_asset_id =315				Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;316317			Self::deposit_event(Event::<T>::ForeignAssetRegistered {318				asset_id: foreign_asset_id,319				asset_address: location,320				metadata: *metadata,321			});322			Ok(())323		}324325		#[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]326		pub fn update_foreign_asset(327			origin: OriginFor<T>,328			foreign_asset_id: ForeignAssetId,329			location: Box<VersionedMultiLocation>,330			metadata: Box<AssetMetadata<BalanceOf<T>>>,331		) -> DispatchResult {332			T::RegisterOrigin::ensure_origin(origin)?;333334			let location: MultiLocation = (*location)335				.try_into()336				.map_err(|()| Error::<T>::BadLocation)?;337			Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;338339			Self::deposit_event(Event::<T>::ForeignAssetUpdated {340				asset_id: foreign_asset_id,341				asset_address: location,342				metadata: *metadata,343			});344			Ok(())345		}346	}347}348349impl<T: Config> Pallet<T> {350	fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {351		NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {352			let id = *current;353			*current = current354				.checked_add(One::one())355				.ok_or(ArithmeticError::Overflow)?;356			Ok(id)357		})358	}359360	fn do_register_foreign_asset(361		location: &MultiLocation,362		metadata: &AssetMetadata<BalanceOf<T>>,363		bounded_collection_id: CollectionId,364	) -> Result<ForeignAssetId, DispatchError> {365		let foreign_asset_id = Self::get_next_foreign_asset_id()?;366		LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {367			ensure!(368				maybe_currency_ids.is_none(),369				Error::<T>::MultiLocationExisted370			);371			*maybe_currency_ids = Some(foreign_asset_id);372			// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));373374			ForeignAssetLocations::<T>::try_mutate(375				foreign_asset_id,376				|maybe_location| -> DispatchResult {377					ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);378					*maybe_location = Some(location.clone());379380					AssetMetadatas::<T>::try_mutate(381						AssetIds::ForeignAssetId(foreign_asset_id),382						|maybe_asset_metadatas| -> DispatchResult {383							ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);384							*maybe_asset_metadatas = Some(metadata.clone());385							Ok(())386						},387					)388				},389			)?;390391			AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {392				*collection_id = Some(bounded_collection_id);393				Ok(())394			})395		})?;396397		Ok(foreign_asset_id)398	}399400	fn do_update_foreign_asset(401		foreign_asset_id: ForeignAssetId,402		location: &MultiLocation,403		metadata: &AssetMetadata<BalanceOf<T>>,404	) -> DispatchResult {405		ForeignAssetLocations::<T>::try_mutate(406			foreign_asset_id,407			|maybe_multi_locations| -> DispatchResult {408				let old_multi_locations = maybe_multi_locations409					.as_mut()410					.ok_or(Error::<T>::AssetIdNotExists)?;411412				AssetMetadatas::<T>::try_mutate(413					AssetIds::ForeignAssetId(foreign_asset_id),414					|maybe_asset_metadatas| -> DispatchResult {415						ensure!(416							maybe_asset_metadatas.is_some(),417							Error::<T>::AssetIdNotExists418						);419420						// modify location421						if location != old_multi_locations {422							LocationToCurrencyIds::<T>::remove(old_multi_locations.clone());423							LocationToCurrencyIds::<T>::try_mutate(424								location,425								|maybe_currency_ids| -> DispatchResult {426									ensure!(427										maybe_currency_ids.is_none(),428										Error::<T>::MultiLocationExisted429									);430									// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));431									*maybe_currency_ids = Some(foreign_asset_id);432									Ok(())433								},434							)?;435						}436						*maybe_asset_metadatas = Some(metadata.clone());437						*old_multi_locations = location.clone();438						Ok(())439					},440				)441			},442		)443	}444}445446pub use frame_support::{447	traits::{448		fungibles::{Balanced, CreditOf},449		tokens::currency::Currency as CurrencyT,450		OnUnbalanced as OnUnbalancedT,451	},452	weights::{WeightToFeePolynomial, WeightToFee},453};454455pub struct FreeForAll<456	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,457	AssetId: Get<MultiLocation>,458	AccountId,459	Currency: CurrencyT<AccountId>,460	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,461>(462	Weight,463	Currency::Balance,464	PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,465);466467impl<468		WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,469		AssetId: Get<MultiLocation>,470		AccountId,471		Currency: CurrencyT<AccountId>,472		OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,473	> WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>474{475	fn new() -> Self {476		Self(0, Zero::zero(), PhantomData)477	}478479	fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {480		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);481		Ok(payment)482	}483}484impl<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> Drop485	for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>486where487	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,488	AssetId: Get<MultiLocation>,489	Currency: CurrencyT<AccountId>,490	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,491{492	fn drop(&mut self) {493		OnUnbalanced::on_unbalanced(Currency::issue(self.1));494	}495}
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;125mod 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}
modifiedruntime/common/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/runtime_apis.rs
+++ b/runtime/common/runtime_apis.rs
@@ -653,6 +653,10 @@
                     #[cfg(not(any(feature = "unique-runtime", feature = "quartz-runtime")))]
                     list_benchmark!(list, extra, pallet_proxy_rmrk_equip, RmrkEquip);
 
+                    #[cfg(not(any(feature = "unique-runtime", feature = "quartz-runtime")))]
+                    list_benchmark!(list, extra, pallet_foreign_assets, ForeignAssets);
+
+
                     // list_benchmark!(list, extra, pallet_evm_coder_substrate, EvmCoderSubstrate);
 
                     let storage_info = AllPalletsReversedWithSystemFirst::storage_info();
@@ -708,6 +712,9 @@
                     #[cfg(not(any(feature = "unique-runtime", feature = "quartz-runtime")))]
                     add_benchmark!(params, batches, pallet_proxy_rmrk_equip, RmrkEquip);
 
+                    #[cfg(not(any(feature = "unique-runtime", feature = "quartz-runtime")))]
+                    add_benchmark!(params, batches, pallet_foreign_assets, ForeignAssets);
+
                     // add_benchmark!(params, batches, pallet_evm_coder_substrate, EvmCoderSubstrate);
 
                     if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }
modifiedruntime/opal/Cargo.tomldiffbeforeafterboth
--- a/runtime/opal/Cargo.toml
+++ b/runtime/opal/Cargo.toml
@@ -35,6 +35,7 @@
     'pallet-nonfungible/runtime-benchmarks',
     'pallet-proxy-rmrk-core/runtime-benchmarks',
     'pallet-proxy-rmrk-equip/runtime-benchmarks',
+    'pallet-foreign-assets/runtime-benchmarks',
     'pallet-unique/runtime-benchmarks',
     'pallet-inflation/runtime-benchmarks',
     'pallet-unique-scheduler/runtime-benchmarks',
modifiedruntime/quartz/Cargo.tomldiffbeforeafterboth
--- a/runtime/quartz/Cargo.toml
+++ b/runtime/quartz/Cargo.toml
@@ -36,6 +36,7 @@
     'pallet-proxy-rmrk-core/runtime-benchmarks',
     'pallet-proxy-rmrk-equip/runtime-benchmarks',
     'pallet-unique/runtime-benchmarks',
+    'pallet-foreign-assets/runtime-benchmarks',
     'pallet-inflation/runtime-benchmarks',
     'pallet-unique-scheduler/runtime-benchmarks',
     'pallet-xcm/runtime-benchmarks',
modifiedruntime/unique/Cargo.tomldiffbeforeafterboth
--- a/runtime/unique/Cargo.toml
+++ b/runtime/unique/Cargo.toml
@@ -36,6 +36,7 @@
     'pallet-proxy-rmrk-core/runtime-benchmarks',
     'pallet-proxy-rmrk-equip/runtime-benchmarks',
     'pallet-unique/runtime-benchmarks',
+    'pallet-foreign-assets/runtime-benchmarks',
     'pallet-inflation/runtime-benchmarks',
     'pallet-unique-scheduler/runtime-benchmarks',
     'pallet-xcm/runtime-benchmarks',