difftreelog
Add foreign assets pallet
in: master
5 files changed
pallets/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"]
pallets/foreing-assets/src/impl_fungibles.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! 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}pallets/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));
+ }
+}
pallets/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
runtime/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,