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

difftreelog

Add foreign assets pallet

Ilja Khabarov2022-08-19parent: #93a0bf6.patch.diff
in: master

5 files changed

addedpallets/foreing-assets/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/pallets/foreing-assets/Cargo.toml
@@ -0,0 +1,50 @@
+[package]
+name = "pallet-foreing-assets"
+version = "0.1.0"
+license = "GPLv3"
+edition = "2021"
+
+[dependencies]
+log = { version = "0.4.14", default-features = false }
+serde = { version = "1.0.136", optional = true }
+scale-info = { version = "2.0.1", default-features = false, features = ["derive"] }
+codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false }
+sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false }
+sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false  }
+frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false }
+frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false }
+up-data-structs = { default-features = false, path = '../../primitives/data-structs' }
+pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false }
+pallet-common = { default-features = false, path = '../common' }
+pallet-fungible = { default-features = false, path = '../fungible' }
+xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.24", default-features = false }
+xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.24", default-features = false }
+xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.24", 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 }
+
+[dev-dependencies]
+serde_json = "1.0.68"
+hex = { version = "0.4" }
+sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" }
+sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" }
+pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" }
+pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" }
+
+[features]
+default = ["std"]
+std = [
+	"serde",
+	"log/std",
+	"codec/std",
+	"scale-info/std",
+	"sp-runtime/std",
+	"sp-std/std",
+	"frame-support/std",
+	"frame-system/std",
+	"up-data-structs/std",
+	"pallet-common/std",
+	"pallet-balances/std",
+	"pallet-fungible/std",
+	"orml-tokens/std"
+]
+try-runtime = ["frame-support/try-runtime"]
addedpallets/foreing-assets/src/impl_fungibles.rsdiffbeforeafterboth
after · pallets/foreing-assets/src/impl_fungibles.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//! Implementations for fungibles trait.1819use super::*;20use frame_system::Config as SystemConfig;2122use frame_support::traits::tokens::{DepositConsequence, WithdrawConsequence};23use pallet_common::CollectionHandle;24use pallet_fungible::FungibleHandle;25use pallet_common::CommonCollectionOperations;26use up_data_structs::budget::Unlimited;27use sp_runtime::traits::{CheckedAdd, CheckedSub};2829impl<T: Config> fungibles::Inspect<<T as SystemConfig>::AccountId> for Pallet<T>30where31	T: orml_tokens::Config<CurrencyId = AssetIds>,32{33	type AssetId = AssetIds;34	type Balance = BalanceOf<T>;3536	fn total_issuance(asset: Self::AssetId) -> Self::Balance {37		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible total_issuance");3839		match asset {40			AssetIds::NativeAssetId(NativeCurrency::Here) => {41				let parent_amount = <pallet_balances::Pallet<T> as fungible::Inspect<42					T::AccountId,43				>>::total_issuance();4445				let value: u128 = match parent_amount.try_into() {46					Ok(val) => val,47					Err(_) => return Zero::zero(),48				};4950				let ti: Self::Balance = match value.try_into() {51					Ok(val) => val,52					Err(_) => return Zero::zero(),53				};5455				ti56			}57			AssetIds::NativeAssetId(NativeCurrency::Parent) => {58				let amount =59					<orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::total_issuance(60						AssetIds::NativeAssetId(NativeCurrency::Parent),61					);6263				let value: u128 = match amount.try_into() {64					Ok(val) => val,65					Err(_) => return Zero::zero(),66				};6768				let ti: Self::Balance = match value.try_into() {69					Ok(val) => val,70					Err(_) => return Zero::zero(),71				};7273				ti74			}75			AssetIds::ForeignAssetId(fid) => {76				let target_collection_id = match <AssetBinding<T>>::get(fid) {77					Some(v) => v,78					None => return Zero::zero(),79				};80				let collection_handle = match <CollectionHandle<T>>::try_get(target_collection_id) {81					Ok(v) => v,82					Err(_) => return Zero::zero(),83				};84				let collection = FungibleHandle::cast(collection_handle);85				Self::Balance::try_from(collection.total_supply()).unwrap_or(Zero::zero())86			}87		}88	}8990	fn minimum_balance(asset: Self::AssetId) -> Self::Balance {91		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible minimum_balance");92		match asset {93			AssetIds::NativeAssetId(NativeCurrency::Here) => {94				let parent_amount = <pallet_balances::Pallet<T> as fungible::Inspect<95					T::AccountId,96				>>::minimum_balance();9798				let value: u128 = match parent_amount.try_into() {99					Ok(val) => val,100					Err(_) => return Zero::zero(),101				};102103				let ti: Self::Balance = match value.try_into() {104					Ok(val) => val,105					Err(_) => return Zero::zero(),106				};107108				ti109			}110			AssetIds::NativeAssetId(NativeCurrency::Parent) => {111				let amount =112					<orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::minimum_balance(113						AssetIds::NativeAssetId(NativeCurrency::Parent),114					);115116				let value: u128 = match amount.try_into() {117					Ok(val) => val,118					Err(_) => return Zero::zero(),119				};120121				let ti: Self::Balance = match value.try_into() {122					Ok(val) => val,123					Err(_) => return Zero::zero(),124				};125126				ti127			}128			AssetIds::ForeignAssetId(fid) => {129				AssetMetadatas::<T>::get(AssetIds::ForeignAssetId(fid))130					.map(|x| x.minimal_balance)131					.unwrap_or_else(Zero::zero)132			}133		}134	}135136	fn balance(asset: Self::AssetId, who: &<T as SystemConfig>::AccountId) -> Self::Balance {137		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible balance");138		match asset {139			AssetIds::NativeAssetId(NativeCurrency::Here) => {140				let parent_amount =141					<pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::balance(who);142143				let value: u128 = match parent_amount.try_into() {144					Ok(val) => val,145					Err(_) => return Zero::zero(),146				};147148				let ti: Self::Balance = match value.try_into() {149					Ok(val) => val,150					Err(_) => return Zero::zero(),151				};152153				ti154			}155			AssetIds::NativeAssetId(NativeCurrency::Parent) => {156				let amount = <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::balance(157					AssetIds::NativeAssetId(NativeCurrency::Parent),158					who,159				);160161				let value: u128 = match amount.try_into() {162					Ok(val) => val,163					Err(_) => return Zero::zero(),164				};165166				let ti: Self::Balance = match value.try_into() {167					Ok(val) => val,168					Err(_) => return Zero::zero(),169				};170171				ti172			}173			AssetIds::ForeignAssetId(fid) => {174				let target_collection_id = match <AssetBinding<T>>::get(fid) {175					Some(v) => v,176					None => return Zero::zero(),177				};178				let collection_handle = match <CollectionHandle<T>>::try_get(target_collection_id) {179					Ok(v) => v,180					Err(_) => return Zero::zero(),181				};182				let collection = FungibleHandle::cast(collection_handle);183				Self::Balance::try_from(184					collection.balance(T::CrossAccountId::from_sub(who.clone()), TokenId(0)),185				)186				.unwrap_or(Zero::zero())187			}188		}189	}190191	fn reducible_balance(192		asset: Self::AssetId,193		who: &<T as SystemConfig>::AccountId,194		keep_alive: bool,195	) -> Self::Balance {196		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible reducible_balance");197198		match asset {199			AssetIds::NativeAssetId(NativeCurrency::Here) => {200				let parent_amount = <pallet_balances::Pallet<T> as fungible::Inspect<201					T::AccountId,202				>>::reducible_balance(who, keep_alive);203204				let value: u128 = match parent_amount.try_into() {205					Ok(val) => val,206					Err(_) => return Zero::zero(),207				};208209				let ti: Self::Balance = match value.try_into() {210					Ok(val) => val,211					Err(_) => return Zero::zero(),212				};213214				ti215			}216			AssetIds::NativeAssetId(NativeCurrency::Parent) => {217				let amount =218					<orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::reducible_balance(219						AssetIds::NativeAssetId(NativeCurrency::Parent),220						who,221						keep_alive,222					);223224				let value: u128 = match amount.try_into() {225					Ok(val) => val,226					Err(_) => return Zero::zero(),227				};228229				let ti: Self::Balance = match value.try_into() {230					Ok(val) => val,231					Err(_) => return Zero::zero(),232				};233234				ti235			}236			_ => Self::balance(asset, who),237		}238	}239240	fn can_deposit(241		asset: Self::AssetId,242		who: &<T as SystemConfig>::AccountId,243		amount: Self::Balance,244		mint: bool,245	) -> DepositConsequence {246		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible can_deposit");247248		let value: u128 = match amount.try_into() {249			Ok(val) => val,250			Err(_) => return DepositConsequence::CannotCreate,251		};252253		match asset {254			AssetIds::NativeAssetId(NativeCurrency::Here) => {255				let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {256					Ok(val) => val,257					Err(_) => {258						return DepositConsequence::CannotCreate;259					}260				};261				<pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::can_deposit(262					who,263					this_amount,264					mint,265				)266			}267			AssetIds::NativeAssetId(NativeCurrency::Parent) => {268				let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {269					Ok(val) => val,270					Err(_) => {271						return DepositConsequence::CannotCreate;272					}273				};274				<orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::can_deposit(275					AssetIds::NativeAssetId(NativeCurrency::Parent),276					who,277					parent_amount,278					mint,279				)280			}281			_ => {282				if amount.is_zero() {283					return DepositConsequence::Success;284				}285286				let extential_deposit_value = T::ExistentialDeposit::get();287				let ed_value: u128 = match extential_deposit_value.try_into() {288					Ok(val) => val,289					Err(_) => return DepositConsequence::CannotCreate,290				};291				let extential_deposit: Self::Balance = match ed_value.try_into() {292					Ok(val) => val,293					Err(_) => return DepositConsequence::CannotCreate,294				};295296				let new_total_balance = match Self::balance(asset, who).checked_add(&amount) {297					Some(x) => x,298					None => return DepositConsequence::Overflow,299				};300301				if new_total_balance < extential_deposit {302					return DepositConsequence::BelowMinimum;303				}304305				DepositConsequence::Success306			}307		}308	}309310	fn can_withdraw(311		asset: Self::AssetId,312		who: &<T as SystemConfig>::AccountId,313		amount: Self::Balance,314	) -> WithdrawConsequence<Self::Balance> {315		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible can_withdraw");316		let value: u128 = match amount.try_into() {317			Ok(val) => val,318			Err(_) => return WithdrawConsequence::UnknownAsset,319		};320321		match asset {322			AssetIds::NativeAssetId(NativeCurrency::Here) => {323				let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {324					Ok(val) => val,325					Err(_) => {326						return WithdrawConsequence::UnknownAsset;327					}328				};329				match <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::can_withdraw(330					who,331					this_amount,332				) {333					WithdrawConsequence::NoFunds => WithdrawConsequence::NoFunds,334					WithdrawConsequence::WouldDie => WithdrawConsequence::WouldDie,335					WithdrawConsequence::UnknownAsset => WithdrawConsequence::UnknownAsset,336					WithdrawConsequence::Underflow => WithdrawConsequence::Underflow,337					WithdrawConsequence::Overflow => WithdrawConsequence::Overflow,338					WithdrawConsequence::Frozen => WithdrawConsequence::Frozen,339					WithdrawConsequence::Success => WithdrawConsequence::Success,340					_ => WithdrawConsequence::NoFunds,341				}342			}343			AssetIds::NativeAssetId(NativeCurrency::Parent) => {344				let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {345					Ok(val) => val,346					Err(_) => {347						return WithdrawConsequence::UnknownAsset;348					}349				};350				match <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::can_withdraw(351					AssetIds::NativeAssetId(NativeCurrency::Parent),352					who,353					parent_amount,354				) {355					WithdrawConsequence::NoFunds => WithdrawConsequence::NoFunds,356					WithdrawConsequence::WouldDie => WithdrawConsequence::WouldDie,357					WithdrawConsequence::UnknownAsset => WithdrawConsequence::UnknownAsset,358					WithdrawConsequence::Underflow => WithdrawConsequence::Underflow,359					WithdrawConsequence::Overflow => WithdrawConsequence::Overflow,360					WithdrawConsequence::Frozen => WithdrawConsequence::Frozen,361					WithdrawConsequence::Success => WithdrawConsequence::Success,362					_ => WithdrawConsequence::NoFunds,363				}364			}365			_ => match Self::balance(asset, who).checked_sub(&amount) {366				Some(_) => WithdrawConsequence::Success,367				None => WithdrawConsequence::NoFunds,368			},369		}370	}371}372373impl<T: Config> fungibles::Mutate<<T as SystemConfig>::AccountId> for Pallet<T>374where375	T: orml_tokens::Config<CurrencyId = AssetIds>,376{377	fn mint_into(378		asset: Self::AssetId,379		who: &<T as SystemConfig>::AccountId,380		amount: Self::Balance,381	) -> DispatchResult {382		//Self::do_mint(asset, who, amount, None)383		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible mint_into {:?}", asset);384385		let value: u128 = match amount.try_into() {386			Ok(val) => val,387			Err(_) => return Err(DispatchError::Other("Bad amount to value conversion")),388		};389390		match asset {391			AssetIds::NativeAssetId(NativeCurrency::Here) => {392				let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {393					Ok(val) => val,394					Err(_) => {395						return Err(DispatchError::Other(396							"Bad amount to this parachain value conversion",397						))398					}399				};400401				<pallet_balances::Pallet<T> as fungible::Mutate<T::AccountId>>::mint_into(402					who,403					this_amount,404				)405			}406			AssetIds::NativeAssetId(NativeCurrency::Parent) => {407				let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {408					Ok(val) => val,409					Err(_) => {410						return Err(DispatchError::Other(411							"Bad amount to relay chain value conversion",412						))413					}414				};415416				<orml_tokens::Pallet<T> as fungibles::Mutate<T::AccountId>>::mint_into(417					AssetIds::NativeAssetId(NativeCurrency::Parent),418					who,419					parent_amount,420				)421			}422			AssetIds::ForeignAssetId(fid) => {423				let target_collection_id = match <AssetBinding<T>>::get(fid) {424					Some(v) => v,425					None => {426						return Err(DispatchError::Other(427							"Associated collection not found for asset",428						))429					}430				};431				let collection =432					FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);433				let account = T::CrossAccountId::from_sub(who.clone());434435				let amount_data: pallet_fungible::CreateItemData<T> = (account.clone(), value);436437				pallet_fungible::Pallet::<T>::create_item_foreign(438					&collection,439					&account,440					amount_data,441					&Unlimited,442				)?;443444				Ok(())445			}446		}447	}448449	fn burn_from(450		asset: Self::AssetId,451		who: &<T as SystemConfig>::AccountId,452		amount: Self::Balance,453	) -> Result<Self::Balance, DispatchError> {454		// let f = DebitFlags { keep_alive: false, best_effort: false };455		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible burn_from");456457		let value: u128 = match amount.try_into() {458			Ok(val) => val,459			Err(_) => return Err(DispatchError::Other("Bad amount to value conversion")),460		};461462		match asset {463			AssetIds::NativeAssetId(NativeCurrency::Here) => {464				let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {465					Ok(val) => val,466					Err(_) => {467						return Err(DispatchError::Other(468							"Bad amount to this parachain value conversion",469						))470					}471				};472473				match <pallet_balances::Pallet<T> as fungible::Mutate<T::AccountId>>::burn_from(474					who,475					this_amount,476				) {477					Ok(_) => Ok(amount),478					Err(e) => Err(e),479				}480			}481			AssetIds::NativeAssetId(NativeCurrency::Parent) => {482				let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {483					Ok(val) => val,484					Err(_) => {485						return Err(DispatchError::Other(486							"Bad amount to relay chain value conversion",487						))488					}489				};490491				match <orml_tokens::Pallet<T> as fungibles::Mutate<T::AccountId>>::burn_from(492					AssetIds::NativeAssetId(NativeCurrency::Parent),493					who,494					parent_amount,495				) {496					Ok(_) => Ok(amount),497					Err(e) => Err(e),498				}499			}500			AssetIds::ForeignAssetId(fid) => {501				let target_collection_id = match <AssetBinding<T>>::get(fid) {502					Some(v) => v,503					None => {504						return Err(DispatchError::Other(505							"Associated collection not found for asset",506						))507					}508				};509				let collection =510					FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);511				pallet_fungible::Pallet::<T>::burn_foreign(512					&collection,513					&T::CrossAccountId::from_sub(who.clone()),514					value,515				)?;516517				Ok(amount)518			}519		}520	}521522	fn slash(523		asset: Self::AssetId,524		who: &<T as SystemConfig>::AccountId,525		amount: Self::Balance,526	) -> Result<Self::Balance, DispatchError> {527		// let f = DebitFlags { keep_alive: false, best_effort: true };528		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible slash");529		Self::burn_from(asset, who, amount)?;530		Ok(amount)531	}532}533534impl<T: Config> fungibles::Transfer<T::AccountId> for Pallet<T>535where536	T: orml_tokens::Config<CurrencyId = AssetIds>,537{538	fn transfer(539		asset: Self::AssetId,540		source: &<T as SystemConfig>::AccountId,541		dest: &<T as SystemConfig>::AccountId,542		amount: Self::Balance,543		keep_alive: bool,544	) -> Result<Self::Balance, DispatchError> {545		// let f = TransferFlags { keep_alive, best_effort: false, burn_dust: false };546		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible transfer");547548		let value: u128 = match amount.try_into() {549			Ok(val) => val,550			Err(_) => return Err(DispatchError::Other("Bad amount to value conversion")),551		};552553		match asset {554			AssetIds::NativeAssetId(NativeCurrency::Here) => {555				let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {556					Ok(val) => val,557					Err(_) => {558						return Err(DispatchError::Other(559							"Bad amount to this parachain value conversion",560						))561					}562				};563564				match <pallet_balances::Pallet<T> as fungible::Transfer<T::AccountId>>::transfer(565					source,566					dest,567					this_amount,568					keep_alive,569				) {570					Ok(_) => Ok(amount),571					Err(_) => Err(DispatchError::Other(572						"Bad amount to relay chain value conversion",573					)),574				}575			}576			AssetIds::NativeAssetId(NativeCurrency::Parent) => {577				let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {578					Ok(val) => val,579					Err(_) => {580						return Err(DispatchError::Other(581							"Bad amount to relay chain value conversion",582						))583					}584				};585586				match <orml_tokens::Pallet<T> as fungibles::Transfer<T::AccountId>>::transfer(587					AssetIds::NativeAssetId(NativeCurrency::Parent),588					source,589					dest,590					parent_amount,591					keep_alive,592				) {593					Ok(_) => Ok(amount),594					Err(e) => Err(e),595				}596			}597			AssetIds::ForeignAssetId(fid) => {598				let target_collection_id = match <AssetBinding<T>>::get(fid) {599					Some(v) => v,600					None => {601						return Err(DispatchError::Other(602							"Associated collection not found for asset",603						))604					}605				};606				let collection =607					FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);608609				pallet_fungible::Pallet::<T>::transfer(610					&collection,611					&T::CrossAccountId::from_sub(source.clone()),612					&T::CrossAccountId::from_sub(dest.clone()),613					value,614					&Unlimited,615				)?;616617				Ok(amount)618			}619		}620	}621}
addedpallets/foreing-assets/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/foreing-assets/src/lib.rs
@@ -0,0 +1,541 @@
+// 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/>.
+
+//! # Foreing assets
+//!
+//! - [`Config`]
+//! - [`Call`]
+//! - [`Pallet`]
+//!
+//! ## Overview
+//!
+//! The foreing assests pallet provides functions for:
+//!
+//! - Local and foreign assets management. The foreign assets can be updated without runtime upgrade.
+//! - Bounds between asset and target collection for cross chain transfer and inner transfers.
+//!
+//! ## Overview
+//!
+//! Under construction
+
+#![cfg_attr(not(feature = "std"), no_std)]
+#![allow(clippy::unused_unit)]
+
+use frame_support::{
+	dispatch::DispatchResult,
+	ensure,
+	pallet_prelude::*,
+	traits::{fungible, fungibles, Currency, EnsureOrigin},
+	transactional, RuntimeDebug,
+};
+use frame_system::pallet_prelude::*;
+use up_data_structs::{CollectionMode};
+use pallet_fungible::{Pallet as PalletFungible};
+use scale_info::{TypeInfo};
+use sp_runtime::{
+	traits::{One, Zero},
+	ArithmeticError,
+};
+use sp_std::{boxed::Box, vec::Vec};
+use up_data_structs::{CollectionId, TokenId, CreateCollectionData};
+
+// NOTE:v1::MultiLocation is used in storages, we would need to do migration if upgrade the
+// MultiLocation in the future.
+use xcm::opaque::latest::{prelude::XcmError, MultiAsset};
+use xcm::{v1::MultiLocation, VersionedMultiLocation};
+use xcm_executor::{traits::WeightTrader, Assets};
+
+use pallet_common::erc::CrossAccountId;
+
+#[cfg(feature = "std")]
+use serde::{Deserialize, Serialize};
+
+// TODO: Move to primitives
+// Id of native currency.
+// 0 - QTZ\UNQ
+// 1 - KSM\DOT
+#[derive(
+	Clone,
+	Copy,
+	Eq,
+	PartialEq,
+	PartialOrd,
+	Ord,
+	MaxEncodedLen,
+	RuntimeDebug,
+	Encode,
+	Decode,
+	TypeInfo,
+)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum NativeCurrency {
+	Here = 0,
+	Parent = 1,
+}
+
+#[derive(
+	Clone,
+	Copy,
+	Eq,
+	PartialEq,
+	PartialOrd,
+	Ord,
+	MaxEncodedLen,
+	RuntimeDebug,
+	Encode,
+	Decode,
+	TypeInfo,
+)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum AssetIds {
+	ForeignAssetId(ForeignAssetId),
+	NativeAssetId(NativeCurrency),
+}
+
+pub trait TryAsForeing<T, F> {
+	fn try_as_foreing(asset: T) -> Option<F>;
+}
+
+impl TryAsForeing<AssetIds, ForeignAssetId> for AssetIds {
+	fn try_as_foreing(asset: AssetIds) -> Option<ForeignAssetId> {
+		match asset {
+			AssetIds::ForeignAssetId(id) => Some(id),
+			_ => None,
+		}
+	}
+}
+
+pub type ForeignAssetId = u32;
+pub type CurrencyId = AssetIds;
+
+mod impl_fungibles;
+mod weights;
+
+pub use module::*;
+pub use weights::WeightInfo;
+
+/// Type alias for currency balance.
+pub type BalanceOf<T> =
+	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
+
+/// A mapping between ForeignAssetId and AssetMetadata.
+pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {
+	/// Returns the AssetMetadata associated with a given ForeignAssetId.
+	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;
+	/// Returns the MultiLocation associated with a given ForeignAssetId.
+	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;
+	/// Returns the CurrencyId associated with a given MultiLocation.
+	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;
+}
+
+pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);
+
+impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>
+	for XcmForeignAssetIdMapping<T>
+{
+	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {
+		log::trace!(target: "fassets::asset_metadatas", "call");
+		Pallet::<T>::asset_metadatas(AssetIds::ForeignAssetId(foreign_asset_id))
+	}
+
+	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {
+		log::trace!(target: "fassets::get_multi_location", "call");
+		Pallet::<T>::foreign_asset_locations(foreign_asset_id)
+	}
+
+	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {
+		log::trace!(target: "fassets::get_currency_id", "call");
+		Some(AssetIds::ForeignAssetId(
+			Pallet::<T>::location_to_currency_ids(multi_location).unwrap_or(0),
+		))
+	}
+}
+
+#[frame_support::pallet]
+pub mod module {
+	use super::*;
+
+	#[pallet::config]
+	pub trait Config:
+		frame_system::Config
+		+ pallet_common::Config
+		+ pallet_fungible::Config
+		+ orml_tokens::Config
+		+ pallet_balances::Config
+	{
+		/// The overarching event type.
+		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
+
+		/// Currency type for withdraw and balance storage.
+		type Currency: Currency<Self::AccountId>;
+
+		/// Required origin for registering asset.
+		type RegisterOrigin: EnsureOrigin<Self::Origin>;
+
+		/// Weight information for the extrinsics in this module.
+		type WeightInfo: WeightInfo;
+	}
+
+	#[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo)]
+	pub struct AssetMetadata<Balance> {
+		pub name: Vec<u8>,
+		pub symbol: Vec<u8>,
+		pub decimals: u8,
+		pub minimal_balance: Balance,
+	}
+
+	#[pallet::error]
+	pub enum Error<T> {
+		/// The given location could not be used (e.g. because it cannot be expressed in the
+		/// desired version of XCM).
+		BadLocation,
+		/// MultiLocation existed
+		MultiLocationExisted,
+		/// AssetId not exists
+		AssetIdNotExists,
+		/// AssetId exists
+		AssetIdExisted,
+	}
+
+	#[pallet::event]
+	#[pallet::generate_deposit(fn deposit_event)]
+	pub enum Event<T: Config> {
+		/// The foreign asset registered.
+		ForeignAssetRegistered {
+			asset_id: ForeignAssetId,
+			asset_address: MultiLocation,
+			metadata: AssetMetadata<BalanceOf<T>>,
+		},
+		/// The foreign asset updated.
+		ForeignAssetUpdated {
+			asset_id: ForeignAssetId,
+			asset_address: MultiLocation,
+			metadata: AssetMetadata<BalanceOf<T>>,
+		},
+		/// The asset registered.
+		AssetRegistered {
+			asset_id: AssetIds,
+			metadata: AssetMetadata<BalanceOf<T>>,
+		},
+		/// The asset updated.
+		AssetUpdated {
+			asset_id: AssetIds,
+			metadata: AssetMetadata<BalanceOf<T>>,
+		},
+	}
+
+	/// Next available Foreign AssetId ID.
+	///
+	/// NextForeignAssetId: ForeignAssetId
+	#[pallet::storage]
+	#[pallet::getter(fn next_foreign_asset_id)]
+	pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;
+	/// The storages for MultiLocations.
+	///
+	/// ForeignAssetLocations: map ForeignAssetId => Option<MultiLocation>
+	#[pallet::storage]
+	#[pallet::getter(fn foreign_asset_locations)]
+	pub type ForeignAssetLocations<T: Config> =
+		StorageMap<_, Twox64Concat, ForeignAssetId, MultiLocation, OptionQuery>;
+
+	/// The storages for CurrencyIds.
+	///
+	/// LocationToCurrencyIds: map MultiLocation => Option<ForeignAssetId>
+	#[pallet::storage]
+	#[pallet::getter(fn location_to_currency_ids)]
+	pub type LocationToCurrencyIds<T: Config> =
+		StorageMap<_, Twox64Concat, MultiLocation, ForeignAssetId, OptionQuery>;
+
+	/// The storages for AssetMetadatas.
+	///
+	/// AssetMetadatas: map AssetIds => Option<AssetMetadata>
+	#[pallet::storage]
+	#[pallet::getter(fn asset_metadatas)]
+	pub type AssetMetadatas<T: Config> =
+		StorageMap<_, Twox64Concat, AssetIds, AssetMetadata<BalanceOf<T>>, OptionQuery>;
+
+	/// The storages for assets to fungible collection binding
+	///
+	#[pallet::storage]
+	#[pallet::getter(fn asset_binding)]
+	pub type AssetBinding<T: Config> =
+		StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;
+
+	#[pallet::pallet]
+	#[pallet::without_storage_info]
+	pub struct Pallet<T>(_);
+
+	#[pallet::call]
+	impl<T: Config> Pallet<T> {
+		#[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]
+		#[transactional]
+		pub fn register_foreign_asset(
+			origin: OriginFor<T>,
+			owner: T::AccountId,
+			location: Box<VersionedMultiLocation>,
+			metadata: Box<AssetMetadata<BalanceOf<T>>>,
+		) -> DispatchResult {
+			T::RegisterOrigin::ensure_origin(origin.clone())?;
+
+			let location: MultiLocation = (*location)
+				.try_into()
+				.map_err(|()| Error::<T>::BadLocation)?;
+
+			let md = metadata.clone();
+			let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();
+			let mut description: Vec<u16> = "Foreing assets collection for "
+				.encode_utf16()
+				.collect::<Vec<u16>>();
+			description.append(&mut name.clone());
+
+			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {
+				name: name.try_into().unwrap(),
+				description: description.try_into().unwrap(),
+				mode: CollectionMode::Fungible(18),
+				..Default::default()
+			};
+
+			let bounded_collection_id = <PalletFungible<T>>::init_foreign_collection(
+				CrossAccountId::from_sub(owner),
+				data,
+			)?;
+			let foreign_asset_id =
+				Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;
+
+			Self::deposit_event(Event::<T>::ForeignAssetRegistered {
+				asset_id: foreign_asset_id,
+				asset_address: location,
+				metadata: *metadata,
+			});
+			Ok(())
+		}
+
+		#[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]
+		#[transactional]
+		pub fn update_foreign_asset(
+			origin: OriginFor<T>,
+			foreign_asset_id: ForeignAssetId,
+			location: Box<VersionedMultiLocation>,
+			metadata: Box<AssetMetadata<BalanceOf<T>>>,
+		) -> DispatchResult {
+			T::RegisterOrigin::ensure_origin(origin)?;
+
+			let location: MultiLocation = (*location)
+				.try_into()
+				.map_err(|()| Error::<T>::BadLocation)?;
+			Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;
+
+			Self::deposit_event(Event::<T>::ForeignAssetUpdated {
+				asset_id: foreign_asset_id,
+				asset_address: location,
+				metadata: *metadata,
+			});
+			Ok(())
+		}
+	}
+}
+
+impl<T: Config> Pallet<T> {
+	fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {
+		NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {
+			let id = *current;
+			*current = current
+				.checked_add(One::one())
+				.ok_or(ArithmeticError::Overflow)?;
+			Ok(id)
+		})
+	}
+
+	fn do_register_foreign_asset(
+		location: &MultiLocation,
+		metadata: &AssetMetadata<BalanceOf<T>>,
+		bounded_collection_id: CollectionId,
+	) -> Result<ForeignAssetId, DispatchError> {
+		let foreign_asset_id = Self::get_next_foreign_asset_id()?;
+		LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {
+			ensure!(
+				maybe_currency_ids.is_none(),
+				Error::<T>::MultiLocationExisted
+			);
+			*maybe_currency_ids = Some(foreign_asset_id);
+			// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));
+
+			ForeignAssetLocations::<T>::try_mutate(
+				foreign_asset_id,
+				|maybe_location| -> DispatchResult {
+					ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);
+					*maybe_location = Some(location.clone());
+
+					AssetMetadatas::<T>::try_mutate(
+						AssetIds::ForeignAssetId(foreign_asset_id),
+						|maybe_asset_metadatas| -> DispatchResult {
+							ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);
+							*maybe_asset_metadatas = Some(metadata.clone());
+							Ok(())
+						},
+					)
+				},
+			)?;
+
+			AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {
+				*collection_id = Some(bounded_collection_id);
+				Ok(())
+			})
+		})?;
+
+		Ok(foreign_asset_id)
+	}
+
+	fn do_update_foreign_asset(
+		foreign_asset_id: ForeignAssetId,
+		location: &MultiLocation,
+		metadata: &AssetMetadata<BalanceOf<T>>,
+	) -> DispatchResult {
+		ForeignAssetLocations::<T>::try_mutate(
+			foreign_asset_id,
+			|maybe_multi_locations| -> DispatchResult {
+				let old_multi_locations = maybe_multi_locations
+					.as_mut()
+					.ok_or(Error::<T>::AssetIdNotExists)?;
+
+				AssetMetadatas::<T>::try_mutate(
+					AssetIds::ForeignAssetId(foreign_asset_id),
+					|maybe_asset_metadatas| -> DispatchResult {
+						ensure!(
+							maybe_asset_metadatas.is_some(),
+							Error::<T>::AssetIdNotExists
+						);
+
+						// modify location
+						if location != old_multi_locations {
+							LocationToCurrencyIds::<T>::remove(old_multi_locations.clone());
+							LocationToCurrencyIds::<T>::try_mutate(
+								location,
+								|maybe_currency_ids| -> DispatchResult {
+									ensure!(
+										maybe_currency_ids.is_none(),
+										Error::<T>::MultiLocationExisted
+									);
+									// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));
+									*maybe_currency_ids = Some(foreign_asset_id);
+									Ok(())
+								},
+							)?;
+						}
+						*maybe_asset_metadatas = Some(metadata.clone());
+						*old_multi_locations = location.clone();
+						Ok(())
+					},
+				)
+			},
+		)
+	}
+}
+
+use sp_runtime::SaturatedConversion;
+use sp_runtime::traits::Saturating;
+pub use frame_support::{
+	traits::{
+		fungibles::{Balanced, CreditOf},
+		tokens::currency::Currency as CurrencyT,
+		OnUnbalanced as OnUnbalancedT,
+	},
+	weights::{WeightToFeePolynomial, WeightToFee},
+};
+
+use xcm::latest::{Fungibility::Fungible as XcmFungible};
+
+pub struct UsingAnyCurrencyComponents<
+	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
+	AssetId: Get<MultiLocation>,
+	AccountId,
+	Currency: CurrencyT<AccountId>,
+	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
+>(
+	Weight,
+	Currency::Balance,
+	PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,
+);
+
+impl<
+		WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
+		AssetId: Get<MultiLocation>,
+		AccountId,
+		Currency: CurrencyT<AccountId>,
+		OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
+	> WeightTrader
+	for UsingAnyCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>
+{
+	fn new() -> Self {
+		Self(0, Zero::zero(), PhantomData)
+	}
+
+	fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {
+		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);
+
+		let amount = WeightToFee::weight_to_fee(&weight);
+		let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?;
+
+		let asset_id = payment
+			.fungible
+			.iter()
+			.next()
+			.map_or(Err(XcmError::TooExpensive), |v| Ok(v.0))?;
+
+		// First fungible pays fee
+		let required = MultiAsset {
+			id: asset_id.clone(),
+			fun: XcmFungible(u128_amount),
+		};
+
+		log::trace!(
+			target: "fassets::weight", "buy_weight payment: {:?}, required: {:?}",
+			payment, required,
+		);
+
+		let unused = payment
+			.checked_sub(required)
+			.map_err(|_| XcmError::TooExpensive)?;
+		self.0 = self.0.saturating_add(weight);
+		self.1 = self.1.saturating_add(amount);
+		Ok(unused)
+	}
+
+	fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {
+		let weight = weight.min(self.0);
+		let amount = WeightToFee::weight_to_fee(&weight);
+		self.0 -= weight;
+		self.1 = self.1.saturating_sub(amount);
+		let amount: u128 = amount.saturated_into();
+		if amount > 0 {
+			Some((AssetId::get(), amount).into())
+		} else {
+			None
+		}
+	}
+}
+impl<
+		WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
+		AssetId: Get<MultiLocation>,
+		AccountId,
+		Currency: CurrencyT<AccountId>,
+		OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
+	> Drop for UsingAnyCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>
+{
+	fn drop(&mut self) {
+		OnUnbalanced::on_unbalanced(Currency::issue(self.1));
+	}
+}
addedpallets/foreing-assets/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/foreing-assets/src/weights.rs
@@ -0,0 +1,43 @@
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(clippy::unnecessary_cast)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for module_asset_registry.
+pub trait WeightInfo {
+	fn register_foreign_asset() -> Weight;
+	fn update_foreign_asset() -> Weight;
+}
+
+/// Weights for module_asset_registry using the Acala node and recommended hardware.
+pub struct AcalaWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for AcalaWeight<T> {
+	fn register_foreign_asset() -> Weight {
+		(29_819_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(3 as Weight))
+	}
+	fn update_foreign_asset() -> Weight {
+		(25_119_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	fn register_foreign_asset() -> Weight {
+		(29_819_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(3 as Weight))
+	}
+	fn update_foreign_asset() -> Weight {
+		(25_119_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+}
\ No newline at end of file
modifiedruntime/common/construct_runtime/mod.rsdiffbeforeafterboth
--- a/runtime/common/construct_runtime/mod.rs
+++ b/runtime/common/construct_runtime/mod.rs
@@ -77,6 +77,9 @@
                 #[runtimes(opal)]
                 RmrkEquip: pallet_proxy_rmrk_equip::{Pallet, Call, Storage, Event<T>} = 72,
 
+                #[runtimes(opal)]
+                ForeingAssets: pallet_foreing_assets::{Pallet, Call, Storage, Event<T>} = 80,
+
                 // Frontier
                 EVM: pallet_evm::{Pallet, Config, Call, Storage, Event<T>} = 100,
                 Ethereum: pallet_ethereum::{Pallet, Config, Call, Storage, Event, Origin} = 101,