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
--- /dev/null
+++ b/pallets/foreing-assets/src/impl_fungibles.rs
@@ -0,0 +1,621 @@
+// 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/>.
+
+//! Implementations for fungibles trait.
+
+use super::*;
+use frame_system::Config as SystemConfig;
+
+use frame_support::traits::tokens::{DepositConsequence, WithdrawConsequence};
+use pallet_common::CollectionHandle;
+use pallet_fungible::FungibleHandle;
+use pallet_common::CommonCollectionOperations;
+use up_data_structs::budget::Unlimited;
+use sp_runtime::traits::{CheckedAdd, CheckedSub};
+
+impl<T: Config> fungibles::Inspect<<T as SystemConfig>::AccountId> for Pallet<T>
+where
+	T: orml_tokens::Config<CurrencyId = AssetIds>,
+{
+	type AssetId = AssetIds;
+	type Balance = BalanceOf<T>;
+
+	fn total_issuance(asset: Self::AssetId) -> Self::Balance {
+		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible total_issuance");
+
+		match asset {
+			AssetIds::NativeAssetId(NativeCurrency::Here) => {
+				let parent_amount = <pallet_balances::Pallet<T> as fungible::Inspect<
+					T::AccountId,
+				>>::total_issuance();
+
+				let value: u128 = match parent_amount.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				let ti: Self::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				ti
+			}
+			AssetIds::NativeAssetId(NativeCurrency::Parent) => {
+				let amount =
+					<orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::total_issuance(
+						AssetIds::NativeAssetId(NativeCurrency::Parent),
+					);
+
+				let value: u128 = match amount.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				let ti: Self::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				ti
+			}
+			AssetIds::ForeignAssetId(fid) => {
+				let target_collection_id = match <AssetBinding<T>>::get(fid) {
+					Some(v) => v,
+					None => return Zero::zero(),
+				};
+				let collection_handle = match <CollectionHandle<T>>::try_get(target_collection_id) {
+					Ok(v) => v,
+					Err(_) => return Zero::zero(),
+				};
+				let collection = FungibleHandle::cast(collection_handle);
+				Self::Balance::try_from(collection.total_supply()).unwrap_or(Zero::zero())
+			}
+		}
+	}
+
+	fn minimum_balance(asset: Self::AssetId) -> Self::Balance {
+		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible minimum_balance");
+		match asset {
+			AssetIds::NativeAssetId(NativeCurrency::Here) => {
+				let parent_amount = <pallet_balances::Pallet<T> as fungible::Inspect<
+					T::AccountId,
+				>>::minimum_balance();
+
+				let value: u128 = match parent_amount.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				let ti: Self::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				ti
+			}
+			AssetIds::NativeAssetId(NativeCurrency::Parent) => {
+				let amount =
+					<orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::minimum_balance(
+						AssetIds::NativeAssetId(NativeCurrency::Parent),
+					);
+
+				let value: u128 = match amount.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				let ti: Self::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				ti
+			}
+			AssetIds::ForeignAssetId(fid) => {
+				AssetMetadatas::<T>::get(AssetIds::ForeignAssetId(fid))
+					.map(|x| x.minimal_balance)
+					.unwrap_or_else(Zero::zero)
+			}
+		}
+	}
+
+	fn balance(asset: Self::AssetId, who: &<T as SystemConfig>::AccountId) -> Self::Balance {
+		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible balance");
+		match asset {
+			AssetIds::NativeAssetId(NativeCurrency::Here) => {
+				let parent_amount =
+					<pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::balance(who);
+
+				let value: u128 = match parent_amount.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				let ti: Self::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				ti
+			}
+			AssetIds::NativeAssetId(NativeCurrency::Parent) => {
+				let amount = <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::balance(
+					AssetIds::NativeAssetId(NativeCurrency::Parent),
+					who,
+				);
+
+				let value: u128 = match amount.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				let ti: Self::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				ti
+			}
+			AssetIds::ForeignAssetId(fid) => {
+				let target_collection_id = match <AssetBinding<T>>::get(fid) {
+					Some(v) => v,
+					None => return Zero::zero(),
+				};
+				let collection_handle = match <CollectionHandle<T>>::try_get(target_collection_id) {
+					Ok(v) => v,
+					Err(_) => return Zero::zero(),
+				};
+				let collection = FungibleHandle::cast(collection_handle);
+				Self::Balance::try_from(
+					collection.balance(T::CrossAccountId::from_sub(who.clone()), TokenId(0)),
+				)
+				.unwrap_or(Zero::zero())
+			}
+		}
+	}
+
+	fn reducible_balance(
+		asset: Self::AssetId,
+		who: &<T as SystemConfig>::AccountId,
+		keep_alive: bool,
+	) -> Self::Balance {
+		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible reducible_balance");
+
+		match asset {
+			AssetIds::NativeAssetId(NativeCurrency::Here) => {
+				let parent_amount = <pallet_balances::Pallet<T> as fungible::Inspect<
+					T::AccountId,
+				>>::reducible_balance(who, keep_alive);
+
+				let value: u128 = match parent_amount.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				let ti: Self::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				ti
+			}
+			AssetIds::NativeAssetId(NativeCurrency::Parent) => {
+				let amount =
+					<orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::reducible_balance(
+						AssetIds::NativeAssetId(NativeCurrency::Parent),
+						who,
+						keep_alive,
+					);
+
+				let value: u128 = match amount.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				let ti: Self::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => return Zero::zero(),
+				};
+
+				ti
+			}
+			_ => Self::balance(asset, who),
+		}
+	}
+
+	fn can_deposit(
+		asset: Self::AssetId,
+		who: &<T as SystemConfig>::AccountId,
+		amount: Self::Balance,
+		mint: bool,
+	) -> DepositConsequence {
+		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible can_deposit");
+
+		let value: u128 = match amount.try_into() {
+			Ok(val) => val,
+			Err(_) => return DepositConsequence::CannotCreate,
+		};
+
+		match asset {
+			AssetIds::NativeAssetId(NativeCurrency::Here) => {
+				let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => {
+						return DepositConsequence::CannotCreate;
+					}
+				};
+				<pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::can_deposit(
+					who,
+					this_amount,
+					mint,
+				)
+			}
+			AssetIds::NativeAssetId(NativeCurrency::Parent) => {
+				let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => {
+						return DepositConsequence::CannotCreate;
+					}
+				};
+				<orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::can_deposit(
+					AssetIds::NativeAssetId(NativeCurrency::Parent),
+					who,
+					parent_amount,
+					mint,
+				)
+			}
+			_ => {
+				if amount.is_zero() {
+					return DepositConsequence::Success;
+				}
+
+				let extential_deposit_value = T::ExistentialDeposit::get();
+				let ed_value: u128 = match extential_deposit_value.try_into() {
+					Ok(val) => val,
+					Err(_) => return DepositConsequence::CannotCreate,
+				};
+				let extential_deposit: Self::Balance = match ed_value.try_into() {
+					Ok(val) => val,
+					Err(_) => return DepositConsequence::CannotCreate,
+				};
+
+				let new_total_balance = match Self::balance(asset, who).checked_add(&amount) {
+					Some(x) => x,
+					None => return DepositConsequence::Overflow,
+				};
+
+				if new_total_balance < extential_deposit {
+					return DepositConsequence::BelowMinimum;
+				}
+
+				DepositConsequence::Success
+			}
+		}
+	}
+
+	fn can_withdraw(
+		asset: Self::AssetId,
+		who: &<T as SystemConfig>::AccountId,
+		amount: Self::Balance,
+	) -> WithdrawConsequence<Self::Balance> {
+		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible can_withdraw");
+		let value: u128 = match amount.try_into() {
+			Ok(val) => val,
+			Err(_) => return WithdrawConsequence::UnknownAsset,
+		};
+
+		match asset {
+			AssetIds::NativeAssetId(NativeCurrency::Here) => {
+				let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => {
+						return WithdrawConsequence::UnknownAsset;
+					}
+				};
+				match <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::can_withdraw(
+					who,
+					this_amount,
+				) {
+					WithdrawConsequence::NoFunds => WithdrawConsequence::NoFunds,
+					WithdrawConsequence::WouldDie => WithdrawConsequence::WouldDie,
+					WithdrawConsequence::UnknownAsset => WithdrawConsequence::UnknownAsset,
+					WithdrawConsequence::Underflow => WithdrawConsequence::Underflow,
+					WithdrawConsequence::Overflow => WithdrawConsequence::Overflow,
+					WithdrawConsequence::Frozen => WithdrawConsequence::Frozen,
+					WithdrawConsequence::Success => WithdrawConsequence::Success,
+					_ => WithdrawConsequence::NoFunds,
+				}
+			}
+			AssetIds::NativeAssetId(NativeCurrency::Parent) => {
+				let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => {
+						return WithdrawConsequence::UnknownAsset;
+					}
+				};
+				match <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::can_withdraw(
+					AssetIds::NativeAssetId(NativeCurrency::Parent),
+					who,
+					parent_amount,
+				) {
+					WithdrawConsequence::NoFunds => WithdrawConsequence::NoFunds,
+					WithdrawConsequence::WouldDie => WithdrawConsequence::WouldDie,
+					WithdrawConsequence::UnknownAsset => WithdrawConsequence::UnknownAsset,
+					WithdrawConsequence::Underflow => WithdrawConsequence::Underflow,
+					WithdrawConsequence::Overflow => WithdrawConsequence::Overflow,
+					WithdrawConsequence::Frozen => WithdrawConsequence::Frozen,
+					WithdrawConsequence::Success => WithdrawConsequence::Success,
+					_ => WithdrawConsequence::NoFunds,
+				}
+			}
+			_ => match Self::balance(asset, who).checked_sub(&amount) {
+				Some(_) => WithdrawConsequence::Success,
+				None => WithdrawConsequence::NoFunds,
+			},
+		}
+	}
+}
+
+impl<T: Config> fungibles::Mutate<<T as SystemConfig>::AccountId> for Pallet<T>
+where
+	T: orml_tokens::Config<CurrencyId = AssetIds>,
+{
+	fn mint_into(
+		asset: Self::AssetId,
+		who: &<T as SystemConfig>::AccountId,
+		amount: Self::Balance,
+	) -> DispatchResult {
+		//Self::do_mint(asset, who, amount, None)
+		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible mint_into {:?}", asset);
+
+		let value: u128 = match amount.try_into() {
+			Ok(val) => val,
+			Err(_) => return Err(DispatchError::Other("Bad amount to value conversion")),
+		};
+
+		match asset {
+			AssetIds::NativeAssetId(NativeCurrency::Here) => {
+				let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => {
+						return Err(DispatchError::Other(
+							"Bad amount to this parachain value conversion",
+						))
+					}
+				};
+
+				<pallet_balances::Pallet<T> as fungible::Mutate<T::AccountId>>::mint_into(
+					who,
+					this_amount,
+				)
+			}
+			AssetIds::NativeAssetId(NativeCurrency::Parent) => {
+				let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => {
+						return Err(DispatchError::Other(
+							"Bad amount to relay chain value conversion",
+						))
+					}
+				};
+
+				<orml_tokens::Pallet<T> as fungibles::Mutate<T::AccountId>>::mint_into(
+					AssetIds::NativeAssetId(NativeCurrency::Parent),
+					who,
+					parent_amount,
+				)
+			}
+			AssetIds::ForeignAssetId(fid) => {
+				let target_collection_id = match <AssetBinding<T>>::get(fid) {
+					Some(v) => v,
+					None => {
+						return Err(DispatchError::Other(
+							"Associated collection not found for asset",
+						))
+					}
+				};
+				let collection =
+					FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);
+				let account = T::CrossAccountId::from_sub(who.clone());
+
+				let amount_data: pallet_fungible::CreateItemData<T> = (account.clone(), value);
+
+				pallet_fungible::Pallet::<T>::create_item_foreign(
+					&collection,
+					&account,
+					amount_data,
+					&Unlimited,
+				)?;
+
+				Ok(())
+			}
+		}
+	}
+
+	fn burn_from(
+		asset: Self::AssetId,
+		who: &<T as SystemConfig>::AccountId,
+		amount: Self::Balance,
+	) -> Result<Self::Balance, DispatchError> {
+		// let f = DebitFlags { keep_alive: false, best_effort: false };
+		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible burn_from");
+
+		let value: u128 = match amount.try_into() {
+			Ok(val) => val,
+			Err(_) => return Err(DispatchError::Other("Bad amount to value conversion")),
+		};
+
+		match asset {
+			AssetIds::NativeAssetId(NativeCurrency::Here) => {
+				let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => {
+						return Err(DispatchError::Other(
+							"Bad amount to this parachain value conversion",
+						))
+					}
+				};
+
+				match <pallet_balances::Pallet<T> as fungible::Mutate<T::AccountId>>::burn_from(
+					who,
+					this_amount,
+				) {
+					Ok(_) => Ok(amount),
+					Err(e) => Err(e),
+				}
+			}
+			AssetIds::NativeAssetId(NativeCurrency::Parent) => {
+				let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => {
+						return Err(DispatchError::Other(
+							"Bad amount to relay chain value conversion",
+						))
+					}
+				};
+
+				match <orml_tokens::Pallet<T> as fungibles::Mutate<T::AccountId>>::burn_from(
+					AssetIds::NativeAssetId(NativeCurrency::Parent),
+					who,
+					parent_amount,
+				) {
+					Ok(_) => Ok(amount),
+					Err(e) => Err(e),
+				}
+			}
+			AssetIds::ForeignAssetId(fid) => {
+				let target_collection_id = match <AssetBinding<T>>::get(fid) {
+					Some(v) => v,
+					None => {
+						return Err(DispatchError::Other(
+							"Associated collection not found for asset",
+						))
+					}
+				};
+				let collection =
+					FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);
+				pallet_fungible::Pallet::<T>::burn_foreign(
+					&collection,
+					&T::CrossAccountId::from_sub(who.clone()),
+					value,
+				)?;
+
+				Ok(amount)
+			}
+		}
+	}
+
+	fn slash(
+		asset: Self::AssetId,
+		who: &<T as SystemConfig>::AccountId,
+		amount: Self::Balance,
+	) -> Result<Self::Balance, DispatchError> {
+		// let f = DebitFlags { keep_alive: false, best_effort: true };
+		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible slash");
+		Self::burn_from(asset, who, amount)?;
+		Ok(amount)
+	}
+}
+
+impl<T: Config> fungibles::Transfer<T::AccountId> for Pallet<T>
+where
+	T: orml_tokens::Config<CurrencyId = AssetIds>,
+{
+	fn transfer(
+		asset: Self::AssetId,
+		source: &<T as SystemConfig>::AccountId,
+		dest: &<T as SystemConfig>::AccountId,
+		amount: Self::Balance,
+		keep_alive: bool,
+	) -> Result<Self::Balance, DispatchError> {
+		// let f = TransferFlags { keep_alive, best_effort: false, burn_dust: false };
+		log::trace!(target: "fassets::impl_foreing_assets", "impl_fungible transfer");
+
+		let value: u128 = match amount.try_into() {
+			Ok(val) => val,
+			Err(_) => return Err(DispatchError::Other("Bad amount to value conversion")),
+		};
+
+		match asset {
+			AssetIds::NativeAssetId(NativeCurrency::Here) => {
+				let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => {
+						return Err(DispatchError::Other(
+							"Bad amount to this parachain value conversion",
+						))
+					}
+				};
+
+				match <pallet_balances::Pallet<T> as fungible::Transfer<T::AccountId>>::transfer(
+					source,
+					dest,
+					this_amount,
+					keep_alive,
+				) {
+					Ok(_) => Ok(amount),
+					Err(_) => Err(DispatchError::Other(
+						"Bad amount to relay chain value conversion",
+					)),
+				}
+			}
+			AssetIds::NativeAssetId(NativeCurrency::Parent) => {
+				let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {
+					Ok(val) => val,
+					Err(_) => {
+						return Err(DispatchError::Other(
+							"Bad amount to relay chain value conversion",
+						))
+					}
+				};
+
+				match <orml_tokens::Pallet<T> as fungibles::Transfer<T::AccountId>>::transfer(
+					AssetIds::NativeAssetId(NativeCurrency::Parent),
+					source,
+					dest,
+					parent_amount,
+					keep_alive,
+				) {
+					Ok(_) => Ok(amount),
+					Err(e) => Err(e),
+				}
+			}
+			AssetIds::ForeignAssetId(fid) => {
+				let target_collection_id = match <AssetBinding<T>>::get(fid) {
+					Some(v) => v,
+					None => {
+						return Err(DispatchError::Other(
+							"Associated collection not found for asset",
+						))
+					}
+				};
+				let collection =
+					FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);
+
+				pallet_fungible::Pallet::<T>::transfer(
+					&collection,
+					&T::CrossAccountId::from_sub(source.clone()),
+					&T::CrossAccountId::from_sub(dest.clone()),
+					value,
+					&Unlimited,
+				)?;
+
+				Ok(amount)
+			}
+		}
+	}
+}
addedpallets/foreing-assets/src/lib.rsdiffbeforeafterboth
after · pallets/foreing-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//! # Foreing assets18//!19//! - [`Config`]20//! - [`Call`]21//! - [`Pallet`]22//!23//! ## Overview24//!25//! The foreing 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	transactional, 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, MultiAsset};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 TryAsForeing<T, F> {109	fn try_as_foreing(asset: T) -> Option<F>;110}111112impl TryAsForeing<AssetIds, ForeignAssetId> for AssetIds {113	fn try_as_foreing(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		#[transactional]285		pub fn register_foreign_asset(286			origin: OriginFor<T>,287			owner: T::AccountId,288			location: Box<VersionedMultiLocation>,289			metadata: Box<AssetMetadata<BalanceOf<T>>>,290		) -> DispatchResult {291			T::RegisterOrigin::ensure_origin(origin.clone())?;292293			let location: MultiLocation = (*location)294				.try_into()295				.map_err(|()| Error::<T>::BadLocation)?;296297			let md = metadata.clone();298			let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();299			let mut description: Vec<u16> = "Foreing assets collection for "300				.encode_utf16()301				.collect::<Vec<u16>>();302			description.append(&mut name.clone());303304			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {305				name: name.try_into().unwrap(),306				description: description.try_into().unwrap(),307				mode: CollectionMode::Fungible(18),308				..Default::default()309			};310311			let bounded_collection_id = <PalletFungible<T>>::init_foreign_collection(312				CrossAccountId::from_sub(owner),313				data,314			)?;315			let foreign_asset_id =316				Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;317318			Self::deposit_event(Event::<T>::ForeignAssetRegistered {319				asset_id: foreign_asset_id,320				asset_address: location,321				metadata: *metadata,322			});323			Ok(())324		}325326		#[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]327		#[transactional]328		pub fn update_foreign_asset(329			origin: OriginFor<T>,330			foreign_asset_id: ForeignAssetId,331			location: Box<VersionedMultiLocation>,332			metadata: Box<AssetMetadata<BalanceOf<T>>>,333		) -> DispatchResult {334			T::RegisterOrigin::ensure_origin(origin)?;335336			let location: MultiLocation = (*location)337				.try_into()338				.map_err(|()| Error::<T>::BadLocation)?;339			Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;340341			Self::deposit_event(Event::<T>::ForeignAssetUpdated {342				asset_id: foreign_asset_id,343				asset_address: location,344				metadata: *metadata,345			});346			Ok(())347		}348	}349}350351impl<T: Config> Pallet<T> {352	fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {353		NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {354			let id = *current;355			*current = current356				.checked_add(One::one())357				.ok_or(ArithmeticError::Overflow)?;358			Ok(id)359		})360	}361362	fn do_register_foreign_asset(363		location: &MultiLocation,364		metadata: &AssetMetadata<BalanceOf<T>>,365		bounded_collection_id: CollectionId,366	) -> Result<ForeignAssetId, DispatchError> {367		let foreign_asset_id = Self::get_next_foreign_asset_id()?;368		LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {369			ensure!(370				maybe_currency_ids.is_none(),371				Error::<T>::MultiLocationExisted372			);373			*maybe_currency_ids = Some(foreign_asset_id);374			// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));375376			ForeignAssetLocations::<T>::try_mutate(377				foreign_asset_id,378				|maybe_location| -> DispatchResult {379					ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);380					*maybe_location = Some(location.clone());381382					AssetMetadatas::<T>::try_mutate(383						AssetIds::ForeignAssetId(foreign_asset_id),384						|maybe_asset_metadatas| -> DispatchResult {385							ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);386							*maybe_asset_metadatas = Some(metadata.clone());387							Ok(())388						},389					)390				},391			)?;392393			AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {394				*collection_id = Some(bounded_collection_id);395				Ok(())396			})397		})?;398399		Ok(foreign_asset_id)400	}401402	fn do_update_foreign_asset(403		foreign_asset_id: ForeignAssetId,404		location: &MultiLocation,405		metadata: &AssetMetadata<BalanceOf<T>>,406	) -> DispatchResult {407		ForeignAssetLocations::<T>::try_mutate(408			foreign_asset_id,409			|maybe_multi_locations| -> DispatchResult {410				let old_multi_locations = maybe_multi_locations411					.as_mut()412					.ok_or(Error::<T>::AssetIdNotExists)?;413414				AssetMetadatas::<T>::try_mutate(415					AssetIds::ForeignAssetId(foreign_asset_id),416					|maybe_asset_metadatas| -> DispatchResult {417						ensure!(418							maybe_asset_metadatas.is_some(),419							Error::<T>::AssetIdNotExists420						);421422						// modify location423						if location != old_multi_locations {424							LocationToCurrencyIds::<T>::remove(old_multi_locations.clone());425							LocationToCurrencyIds::<T>::try_mutate(426								location,427								|maybe_currency_ids| -> DispatchResult {428									ensure!(429										maybe_currency_ids.is_none(),430										Error::<T>::MultiLocationExisted431									);432									// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));433									*maybe_currency_ids = Some(foreign_asset_id);434									Ok(())435								},436							)?;437						}438						*maybe_asset_metadatas = Some(metadata.clone());439						*old_multi_locations = location.clone();440						Ok(())441					},442				)443			},444		)445	}446}447448use sp_runtime::SaturatedConversion;449use sp_runtime::traits::Saturating;450pub 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};458459use xcm::latest::{Fungibility::Fungible as XcmFungible};460461pub struct UsingAnyCurrencyComponents<462	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,463	AssetId: Get<MultiLocation>,464	AccountId,465	Currency: CurrencyT<AccountId>,466	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,467>(468	Weight,469	Currency::Balance,470	PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,471);472473impl<474		WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,475		AssetId: Get<MultiLocation>,476		AccountId,477		Currency: CurrencyT<AccountId>,478		OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,479	> WeightTrader480	for UsingAnyCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>481{482	fn new() -> Self {483		Self(0, Zero::zero(), PhantomData)484	}485486	fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {487		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);488489		let amount = WeightToFee::weight_to_fee(&weight);490		let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?;491492		let asset_id = payment493			.fungible494			.iter()495			.next()496			.map_or(Err(XcmError::TooExpensive), |v| Ok(v.0))?;497498		// First fungible pays fee499		let required = MultiAsset {500			id: asset_id.clone(),501			fun: XcmFungible(u128_amount),502		};503504		log::trace!(505			target: "fassets::weight", "buy_weight payment: {:?}, required: {:?}",506			payment, required,507		);508509		let unused = payment510			.checked_sub(required)511			.map_err(|_| XcmError::TooExpensive)?;512		self.0 = self.0.saturating_add(weight);513		self.1 = self.1.saturating_add(amount);514		Ok(unused)515	}516517	fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {518		let weight = weight.min(self.0);519		let amount = WeightToFee::weight_to_fee(&weight);520		self.0 -= weight;521		self.1 = self.1.saturating_sub(amount);522		let amount: u128 = amount.saturated_into();523		if amount > 0 {524			Some((AssetId::get(), amount).into())525		} else {526			None527		}528	}529}530impl<531		WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,532		AssetId: Get<MultiLocation>,533		AccountId,534		Currency: CurrencyT<AccountId>,535		OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,536	> Drop for UsingAnyCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>537{538	fn drop(&mut self) {539		OnUnbalanced::on_unbalanced(Currency::issue(self.1));540	}541}
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,