--- a/runtime/common/construct_runtime/mod.rs +++ b/runtime/common/construct_runtime/mod.rs @@ -42,6 +42,7 @@ Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event} = 34, Sudo: pallet_sudo::{Pallet, Call, Storage, Config, Event} = 35, Vesting: orml_vesting::{Pallet, Storage, Call, Event, Config} = 37, + #[runtimes(opal)] XTokens: orml_xtokens = 38, Tokens: orml_tokens = 39, // Vesting: pallet_vesting::{Pallet, Call, Config, Storage, Event} = 37, --- a/runtime/quartz/src/xcm_config.rs +++ b/runtime/quartz/src/xcm_config.rs @@ -32,6 +32,7 @@ v1::{BodyId, Junction::*, Junctions::*, MultiLocation, NetworkId}, }; use xcm_builder::{ + AllowKnownQueryResponses, AllowSubscriptionsFrom, AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, LocationInverter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, @@ -47,7 +48,10 @@ constants::{MAXIMUM_BLOCK_WEIGHT, UNIQUE}, types::{AccountId, Balance}, }; - +use pallet_foreing_assets::{ + AssetIds, AssetIdMapping, XcmForeignAssetIdMapping, CurrencyId, NativeCurrency, + UsingAnyCurrencyComponents, TryAsForeing, ForeignAssetId, +}; use crate::{ Balances, Call, DmpQueue, Event, Origin, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, XcmpQueue, @@ -56,6 +60,7 @@ use crate::runtime_common::config::pallets::TreasuryAccountId; use crate::runtime_common::config::xcm::*; use crate::*; +use xcm::opaque::latest::prelude::{ DepositReserveAsset, DepositAsset, TransferAsset, TransferReserveAsset }; // Signed version of balance pub type Amount = i128; @@ -94,6 +99,55 @@ } } +pub fn get_allowed_locations() -> Vec { + vec![ + // Self location + MultiLocation { parents: 0, interior: Here }, + // Parent location + MultiLocation { parents: 1, interior: Here }, + // Karura/Acala location + MultiLocation { parents: 1, interior: X1(Parachain(2000)) }, + // Moonriver location + MultiLocation { parents: 1, interior: X1(Parachain(2023)) }, + // Self parachain address + MultiLocation { parents: 1, interior: X1(Parachain(ParachainInfo::get().into())) }, + ] +} + +// Allow xcm exchange only with locations in list +pub struct DenyExchangeWithUnknownLocation; +impl ShouldExecute for DenyExchangeWithUnknownLocation { + fn should_execute( + origin: &MultiLocation, + message: &mut Xcm, + _max_weight: Weight, + _weight_credit: &mut Weight, + ) -> Result<(), ()> { + + // Check if deposit or transfer belongs to allowed parachains + let mut allowed = get_allowed_locations().contains(origin); + + message.0.iter().for_each(|inst| { + match inst { + DepositReserveAsset { dest: dst, .. } => { allowed |= get_allowed_locations().contains(dst); } + TransferReserveAsset { dest: dst, .. } => { allowed |= get_allowed_locations().contains(dst); } + _ => {} + } + }); + + if allowed { + return Ok(()); + } + + log::warn!( + target: "xcm::barrier", + "Unexpected deposit or transfer location" + ); + // Deny + Err(()) + } +} + pub type Barrier = DenyThenTry< DenyExchangeWithUnknownLocation, ( @@ -106,4 +160,135 @@ // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), ->; \ No newline at end of file +>; + +impl orml_tokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type OnDust = orml_tokens::TransferDust; + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + // TODO: Add all module accounts + type DustRemovalWhitelist = DustRemovalWhitelist; + /// The id type for named reserves. + type ReserveIdentifier = (); + type OnNewTokenAccount = (); + type OnKilledTokenAccount = (); +} + +/* +impl orml_xtokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type CurrencyId = AssetIds; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountIdToMultiLocation = AccountIdToMultiLocation; + type SelfLocation = SelfLocation; + type XcmExecutor = XcmExecutor>; + type Weigher = FixedWeightBounds; + type BaseXcmWeight = BaseXcmWeight; + type LocationInverter = LocationInverter; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type MultiLocationsFilter = Everything; + type ReserveProvider = AbsoluteReserveProvider; +} + */ + +parameter_type_with_key! { + pub ExistentialDeposits: |currency_id: CurrencyId| -> Balance { + match currency_id { + CurrencyId::NativeAssetId(symbol) => match symbol { + NativeCurrency::Here => 0, + NativeCurrency::Parent=> 0, + }, + _ => 100_000 + } + }; +} + +pub struct DustRemovalWhitelist; +impl Contains for DustRemovalWhitelist { + fn contains(a: &AccountId) -> bool { + get_all_module_accounts().contains(a) + } +} + +/* +pub struct CurrencyIdConvert; +impl Convert> for CurrencyIdConvert { + fn convert(id: AssetIds) -> Option { + match id { + AssetIds::NativeAssetId(NativeCurrency::Here) => Some(MultiLocation::new( + 1, + X1(Parachain(ParachainInfo::get().into())), + )), + AssetIds::NativeAssetId(NativeCurrency::Parent) => Some(MultiLocation::parent()), + AssetIds::ForeignAssetId(foreign_asset_id) => { + XcmForeignAssetIdMapping::::get_multi_location(foreign_asset_id) + } + } + } +} +impl Convert> for CurrencyIdConvert { + fn convert(location: MultiLocation) -> Option { + if location == MultiLocation::here() + || location == MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))) + { + return Some(AssetIds::NativeAssetId(NativeCurrency::Here)); + } + + if location == MultiLocation::parent() { + return Some(AssetIds::NativeAssetId(NativeCurrency::Parent)); + } + + if let Some(currency_id) = + XcmForeignAssetIdMapping::::get_currency_id(location.clone()) + { + return Some(currency_id); + } + + None + } +} + + */ + + +parameter_types! { + pub const BaseXcmWeight: Weight = 100_000_000; // TODO: recheck this + pub const MaxAssetsForTransfer: usize = 2; +} + +parameter_types! { + pub const RelayLocation: MultiLocation = MultiLocation::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Polkadot; + pub RelayOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); + pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))); +} + +parameter_type_with_key! { + pub ParachainMinFee: |_location: MultiLocation| -> Option { + Some(100_000_000) + }; +} + +pub fn get_all_module_accounts() -> Vec { + vec![TreasuryModuleId::get().into_account_truncating()] +} + +pub struct AccountIdToMultiLocation; +impl Convert for AccountIdToMultiLocation { + fn convert(account: AccountId) -> MultiLocation { + X1(AccountId32 { + network: NetworkId::Any, + id: account.into(), + }) + .into() + } +} --- a/runtime/unique/src/lib.rs +++ b/runtime/unique/src/lib.rs @@ -35,6 +35,8 @@ #[path = "../../common/mod.rs"] mod runtime_common; +pub mod xcm_config; + pub use runtime_common::*; pub const RUNTIME_NAME: &str = "unique"; --- a/runtime/unique/src/xcm_config.rs +++ b/runtime/unique/src/xcm_config.rs @@ -0,0 +1,226 @@ +// 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 . + +use cumulus_pallet_xcm; +use frame_support::{ + {match_types, parameter_types, weights::Weight}, + pallet_prelude::Get, + traits::{Contains, Everything, fungibles}, +}; +use frame_system::EnsureRoot; +use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key}; +use pallet_xcm::XcmPassthrough; +use polkadot_parachain::primitives::Sibling; +use sp_runtime::traits::{AccountIdConversion, CheckedConversion, Convert, Zero}; +use sp_std::{borrow::Borrow, marker::PhantomData, vec, vec::Vec}; +use xcm::{ + latest::{MultiAsset, Xcm}, + prelude::{Concrete, Fungible as XcmFungible}, + v1::{BodyId, Junction::*, Junctions::*, MultiLocation, NetworkId}, +}; +use xcm_builder::{ + AllowKnownQueryResponses, AllowSubscriptionsFrom, + AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, + EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, LocationInverter, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + ConvertedConcreteAssetId, +}; +use xcm_executor::{ + {Config, XcmExecutor}, + traits::{Convert as ConvertXcm, FilterAssetLocation, JustTry, MatchesFungible, ShouldExecute}, +}; + +use up_common::{ + constants::{MAXIMUM_BLOCK_WEIGHT, UNIQUE}, + types::{AccountId, Balance}, +}; +use pallet_foreing_assets::{ + AssetIds, AssetIdMapping, XcmForeignAssetIdMapping, CurrencyId, NativeCurrency, + UsingAnyCurrencyComponents, TryAsForeing, ForeignAssetId, +}; +use crate::{ + Balances, Call, DmpQueue, Event, Origin, ParachainInfo, + ParachainSystem, PolkadotXcm, Runtime, XcmpQueue, +}; +use crate::runtime_common::config::substrate::{TreasuryModuleId, MaxLocks, MaxReserves}; +use crate::runtime_common::config::pallets::TreasuryAccountId; +use crate::runtime_common::config::xcm::*; +use crate::*; +use xcm::opaque::latest::prelude::{ DepositReserveAsset, DepositAsset, TransferAsset, TransferReserveAsset }; + +// Signed version of balance +pub type Amount = i128; + + +pub type Barrier = DenyThenTry< + DenyExchangeWithUnknownLocation, + ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + // Parent and its exec plurality get free execution + AllowUnpaidExecutionFrom, + // Expected responses are OK. + AllowKnownQueryResponses, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), +>; + +pub fn get_allowed_locations() -> Vec { + vec![ + // Self location + MultiLocation { parents: 0, interior: Here }, + // Parent location + MultiLocation { parents: 1, interior: Here }, + // Karura/Acala location + MultiLocation { parents: 1, interior: X1(Parachain(2000)) }, + // Self parachain address + MultiLocation { parents: 1, interior: X1(Parachain(ParachainInfo::get().into())) }, + ] +} + +pub struct DenyThenTry(PhantomData, PhantomData) + where + Deny: ShouldExecute, + Allow: ShouldExecute; + +impl ShouldExecute for DenyThenTry + where + Deny: ShouldExecute, + Allow: ShouldExecute, +{ + fn should_execute( + origin: &MultiLocation, + message: &mut Xcm, + max_weight: Weight, + weight_credit: &mut Weight, + ) -> Result<(), ()> { + Deny::should_execute(origin, message, max_weight, weight_credit)?; + Allow::should_execute(origin, message, max_weight, weight_credit) + } +} + +// Allow xcm exchange only with locations in list +pub struct DenyExchangeWithUnknownLocation; +impl ShouldExecute for DenyExchangeWithUnknownLocation { + fn should_execute( + origin: &MultiLocation, + message: &mut Xcm, + _max_weight: Weight, + _weight_credit: &mut Weight, + ) -> Result<(), ()> { + + // Check if deposit or transfer belongs to allowed parachains + let mut allowed = get_allowed_locations().contains(origin); + + message.0.iter().for_each(|inst| { + match inst { + DepositReserveAsset { dest: dst, .. } => { allowed |= get_allowed_locations().contains(dst); } + TransferReserveAsset { dest: dst, .. } => { allowed |= get_allowed_locations().contains(dst); } + _ => {} + } + }); + + if allowed { + return Ok(()); + } + + log::warn!( + target: "xcm::barrier", + "Unexpected deposit or transfer location" + ); + // Deny + Err(()) + } +} + + +match_types! { + pub type ParentOrParentsExecutivePlurality: impl Contains = { + MultiLocation { parents: 1, interior: Here } | + MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Executive, .. }) } + }; + pub type ParentOrSiblings: impl Contains = { + MultiLocation { parents: 1, interior: Here } | + MultiLocation { parents: 1, interior: X1(_) } + }; +} + +pub fn get_all_module_accounts() -> Vec { + vec![TreasuryModuleId::get().into_account_truncating()] +} + +pub struct DustRemovalWhitelist; +impl Contains for DustRemovalWhitelist { + fn contains(a: &AccountId) -> bool { + get_all_module_accounts().contains(a) + } +} + +parameter_type_with_key! { + pub ExistentialDeposits: |currency_id: CurrencyId| -> Balance { + match currency_id { + CurrencyId::NativeAssetId(symbol) => match symbol { + NativeCurrency::Here => 0, + NativeCurrency::Parent=> 0, + }, + _ => 100_000 + } + }; +} + +impl orml_tokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type OnDust = orml_tokens::TransferDust; + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + // TODO: Add all module accounts + type DustRemovalWhitelist = DustRemovalWhitelist; + /// The id type for named reserves. + type ReserveIdentifier = (); + type OnNewTokenAccount = (); + type OnKilledTokenAccount = (); +} + + +parameter_types! { + pub const BaseXcmWeight: Weight = 100_000_000; // TODO: recheck this + pub const MaxAssetsForTransfer: usize = 2; +} + +parameter_type_with_key! { + pub ParachainMinFee: |_location: MultiLocation| -> Option { + Some(100_000_000) + }; +} + + +pub struct AccountIdToMultiLocation; +impl Convert for AccountIdToMultiLocation { + fn convert(account: AccountId) -> MultiLocation { + X1(AccountId32 { + network: NetworkId::Any, + id: account.into(), + }) + .into() + } +} \ No newline at end of file