diff --git a/Cargo.lock b/Cargo.lock index 3dd708eec..a992270b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6185,6 +6185,20 @@ dependencies = [ "sp-std", ] +[[package]] +name = "orml-xcm" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library.git?branch=polkadot-v0.9.40#19afb58a300faac6ceb0e6e4e341859282897c53" +dependencies = [ + "frame-support", + "frame-system", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-std", + "xcm", +] + [[package]] name = "orml-xcm-support" version = "0.4.1-dev" @@ -9947,11 +9961,17 @@ version = "0.1.0" dependencies = [ "amplitude-runtime", "cumulus-pallet-dmp-queue", + "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-utility", "frame-support", "frame-system", "kusama-runtime", "orml-tokens", + "orml-traits", + "orml-xcm", + "orml-xcm-support", "orml-xtokens", "pallet-assets", "pallet-balances", @@ -9965,11 +9985,16 @@ dependencies = [ "polkadot-runtime", "polkadot-runtime-common", "polkadot-runtime-parachains", + "runtime-common", "scale-info", + "serde", "sp-core", + "sp-debug-derive", "sp-io", "sp-runtime", "sp-std", + "sp-tracing", + "spacewalk-primitives", "statemine-runtime", "statemint-runtime", "xcm", diff --git a/runtime/amplitude/src/xcm_config.rs b/runtime/amplitude/src/xcm_config.rs index d4a5ea3d0..473b3ca5b 100644 --- a/runtime/amplitude/src/xcm_config.rs +++ b/runtime/amplitude/src/xcm_config.rs @@ -15,6 +15,7 @@ use orml_traits::{ use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; +use runtime_common::parachains::kusama::asset_hub; use sp_runtime::traits::Convert; use xcm::latest::{prelude::*, Weight as XCMWeight}; use xcm_builder::{ @@ -27,7 +28,6 @@ use xcm_executor::{ traits::{JustTry, ShouldExecute}, XcmExecutor, }; -use runtime_common::parachains::kusama::asset_hub; const XCM_ASSET_RELAY_KSM: u8 = 0; const XCM_ASSET_ASSETHUB_USDT: u8 = 1; @@ -75,6 +75,10 @@ impl Convert> for CurrencyIdConvert { )), _ => None, }, + CurrencyId::Native => Some(MultiLocation::new( + 1, + X2(Parachain(ParachainInfo::parachain_id().into()), PalletInstance(10)), + )), _ => None, } } @@ -83,16 +87,25 @@ impl Convert> for CurrencyIdConvert { impl Convert> for CurrencyIdConvert { fn convert(location: MultiLocation) -> Option { match location { - MultiLocation { parents: 1, interior: Here } => Some(CurrencyId::XCM(XCM_ASSET_RELAY_KSM)), + MultiLocation { parents: 1, interior: Here } => + Some(CurrencyId::XCM(XCM_ASSET_RELAY_KSM)), MultiLocation { parents: 1, interior: X3( Parachain(asset_hub::PARA_ID), PalletInstance(asset_hub::ASSET_PALLET_ID), - GeneralIndex(asset_hub::USDT_ASSET_ID) - ) + GeneralIndex(asset_hub::USDT_ASSET_ID), + ), } => Some(CurrencyId::XCM(XCM_ASSET_ASSETHUB_USDT)), + // Our native currency location without re-anchoring + MultiLocation { parents: 1, interior: X2(Parachain(id), PalletInstance(10)) } + if id == u32::from(ParachainInfo::parachain_id()) => + Some(CurrencyId::Native), + // Our native currency location with re-anchoring + // The XCM pallet will try to re-anchor the location before it reaches here + MultiLocation { parents: 0, interior: X1(PalletInstance(10)) } => + Some(CurrencyId::Native), _ => None, } } diff --git a/runtime/foucoco/src/xcm_config.rs b/runtime/foucoco/src/xcm_config.rs index 6f16a76fb..0486e76dc 100644 --- a/runtime/foucoco/src/xcm_config.rs +++ b/runtime/foucoco/src/xcm_config.rs @@ -63,6 +63,10 @@ impl Convert> for CurrencyIdConvert { 0 => Some(MultiLocation::parent()), _ => None, }, + CurrencyId::Native => Some(MultiLocation::new( + 1, + X2(Parachain(ParachainInfo::parachain_id().into()), PalletInstance(10)), + )), _ => None, } } @@ -72,6 +76,14 @@ impl Convert> for CurrencyIdConvert { fn convert(location: MultiLocation) -> Option { match location { MultiLocation { parents: 1, interior: Here } => Some(CurrencyId::XCM(0)), + // Our native currency location without re-anchoring + MultiLocation { parents: 1, interior: X2(Parachain(id), PalletInstance(10)) } + if id == u32::from(ParachainInfo::parachain_id()) => + Some(CurrencyId::Native), + // Our native currency location with re-anchoring + // The XCM pallet will try to re-anchor the location before it reaches here + MultiLocation { parents: 0, interior: X1(PalletInstance(10)) } => + Some(CurrencyId::Native), _ => None, } } diff --git a/runtime/integration-tests/Cargo.toml b/runtime/integration-tests/Cargo.toml index 1ccea787d..96eae61ec 100644 --- a/runtime/integration-tests/Cargo.toml +++ b/runtime/integration-tests/Cargo.toml @@ -8,6 +8,10 @@ version = "0.1.0" [dev-dependencies] codec = { package = "parity-scale-codec", version = "3.0.0" } scale-info = { version = "2.1.2", features = ["derive"] } +serde = { version = "1.0.144", features = ["derive"] } + +# Spacewalk libraries +spacewalk-primitives = { git = "https://github.com/pendulum-chain/spacewalk", default-features = false, rev = "58983d2695c309665c9c017a022436aaee088f3d"} frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } @@ -17,6 +21,8 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-tracing = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-debug-derive = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.40" } xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.40" } @@ -34,13 +40,28 @@ xcm-emulator = { git = "https://github.com/shaunxw/xcm-simulator", rev = "bea35c cumulus-pallet-dmp-queue = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40"} cumulus-pallet-xcmp-queue = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40"} +cumulus-pallet-xcm = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40"} +cumulus-primitives-core = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40"} +cumulus-primitives-utility = {git = "https://github.com/paritytech/cumulus", default-features = false, branch = "polkadot-v0.9.40"} parachain-info = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40" } statemint-runtime = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40" } statemine-runtime = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40" } +orml-xcm = {git = "https://github.com/open-web3-stack/open-runtime-module-library.git", branch = "polkadot-v0.9.40" } +orml-xcm-support = {git = "https://github.com/open-web3-stack/open-runtime-module-library.git", branch = "polkadot-v0.9.40" } +orml-traits = {git = "https://github.com/open-web3-stack/open-runtime-module-library.git", branch = "polkadot-v0.9.40" } orml-tokens = {git = "https://github.com/open-web3-stack/open-runtime-module-library.git", branch = "polkadot-v0.9.40" } orml-xtokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.40" } +# Local +runtime-common = {path = "../common", default-features = false} + pendulum-runtime = { path = "../pendulum" } amplitude-runtime = {path = "../amplitude" } + +[features] +default = ["std"] +std = [ + "codec/std", +] diff --git a/runtime/integration-tests/src/amplitude_tests.rs b/runtime/integration-tests/src/amplitude_tests.rs index 2ad863dc3..25aad2601 100644 --- a/runtime/integration-tests/src/amplitude_tests.rs +++ b/runtime/integration-tests/src/amplitude_tests.rs @@ -1,12 +1,14 @@ use crate::{ mock::{kusama_relay_ext, para_ext, ParachainType, USDT_ASSET_ID}, + sibling, test_macros::{ parachain1_transfer_asset_to_parachain2, parachain1_transfer_asset_to_parachain2_and_back, parachain1_transfer_incorrect_asset_to_parachain2_should_fail, transfer_10_relay_token_from_parachain_to_relay_chain, transfer_20_relay_token_from_relay_chain_to_parachain, + transfer_native_token_from_parachain1_to_parachain2_and_back, }, - AMPLITUDE_ID, KUSAMA_ASSETHUB_ID, + AMPLITUDE_ID, ASSETHUB_ID, SIBLING_ID, }; use frame_support::assert_ok; @@ -34,6 +36,16 @@ decl_test_parachain! { } } +decl_test_parachain! { + pub struct SiblingParachain { + Runtime = sibling::Runtime, + RuntimeOrigin = sibling::RuntimeOrigin, + XcmpMessageHandler = sibling::XcmpQueue, + DmpMessageHandler = sibling::DmpQueue, + new_ext = para_ext(ParachainType::Sibling), + } +} + decl_test_parachain! { pub struct AssetHubParachain { Runtime = kusama_asset_hub_runtime::Runtime, @@ -50,6 +62,7 @@ decl_test_network! { parachains = vec![ (1000, AssetHubParachain), (2124, AmplitudeParachain), + (9999, SiblingParachain), ], } } @@ -108,7 +121,7 @@ fn assethub_transfer_asset_to_amplitude_and_back() { parachain1_transfer_asset_to_parachain2_and_back!( kusama_asset_hub_runtime, AssetHubParachain, - KUSAMA_ASSETHUB_ID, + ASSETHUB_ID, USDT_ASSET_ID, amplitude_runtime, AmplitudeParachain, @@ -116,3 +129,16 @@ fn assethub_transfer_asset_to_amplitude_and_back() { network_id ); } + +#[test] +fn transfer_native_token_from_amplitude_to_sibling_parachain_and_back() { + transfer_native_token_from_parachain1_to_parachain2_and_back!( + KusamaMockNet, + amplitude_runtime, + AmplitudeParachain, + sibling, + SiblingParachain, + AMPLITUDE_ID, + SIBLING_ID + ); +} diff --git a/runtime/integration-tests/src/lib.rs b/runtime/integration-tests/src/lib.rs index 3dd931dec..9c48c76e8 100644 --- a/runtime/integration-tests/src/lib.rs +++ b/runtime/integration-tests/src/lib.rs @@ -10,8 +10,10 @@ mod amplitude_tests; #[cfg(test)] mod test_macros; -pub const PENDULUM_ID: u32 = 2094; -pub const POLKADOT_ASSETHUB_ID: u32 = 1000; +#[cfg(test)] +mod sibling; +pub const PENDULUM_ID: u32 = 2094; pub const AMPLITUDE_ID: u32 = 2124; -pub const KUSAMA_ASSETHUB_ID: u32 = 1000; +pub const ASSETHUB_ID: u32 = 1000; +pub const SIBLING_ID: u32 = 9999; diff --git a/runtime/integration-tests/src/mock.rs b/runtime/integration-tests/src/mock.rs index cfb316560..75e1a39a5 100644 --- a/runtime/integration-tests/src/mock.rs +++ b/runtime/integration-tests/src/mock.rs @@ -1,16 +1,17 @@ -use crate::{AMPLITUDE_ID, PENDULUM_ID, KUSAMA_ASSETHUB_ID, POLKADOT_ASSETHUB_ID}; +use crate::{sibling, AMPLITUDE_ID, ASSETHUB_ID, PENDULUM_ID, SIBLING_ID}; use frame_support::traits::GenesisBuild; -use pendulum_runtime::CurrencyId; +use pendulum_runtime::CurrencyId as PendulumCurrencyId; use polkadot_core_primitives::{AccountId, Balance, BlockNumber}; use polkadot_parachain::primitives::Id as ParaId; use polkadot_primitives::v2::{MAX_CODE_SIZE, MAX_POV_SIZE}; use polkadot_runtime_parachains::configuration::HostConfiguration; +use sibling::CurrencyId as SiblingCurrencyId; use sp_io::TestExternalities; use sp_runtime::traits::AccountIdConversion; use xcm_emulator::Weight; -use statemint_runtime as polkadot_asset_hub_runtime; use statemine_runtime as kusama_asset_hub_runtime; +use statemint_runtime as polkadot_asset_hub_runtime; pub const ALICE: [u8; 32] = [4u8; 32]; pub const BOB: [u8; 32] = [5u8; 32]; @@ -60,7 +61,7 @@ macro_rules! build_relaychain { } macro_rules! build_parachain_with_orml { - ($self:ident, $runtime:ty, $system:tt, $balance:tt, $orml_balance:tt) => {{ + ($self:ident, $runtime:ty, $system:tt, $balance:tt, $orml_balance:tt, $currency_id_type:ty) => {{ let mut t = frame_system::GenesisConfig::default().build_storage::<$runtime>().unwrap(); pallet_balances::GenesisConfig::<$runtime> { balances: vec![(AccountId::from(ALICE), $balance), (AccountId::from(BOB), $balance)], @@ -68,8 +69,14 @@ macro_rules! build_parachain_with_orml { .assimilate_storage(&mut t) .unwrap(); + type CurrencyId = $currency_id_type; orml_tokens::GenesisConfig::<$runtime> { - balances: vec![(AccountId::from(BOB), CurrencyId::XCM(0), units($orml_balance))], + balances: vec![ + (AccountId::from(BOB), CurrencyId::XCM(0), units($orml_balance)), + (AccountId::from(ALICE), CurrencyId::XCM(0), units($orml_balance)), + (AccountId::from(BOB), CurrencyId::Native, units($orml_balance)), + (AccountId::from(ALICE), CurrencyId::Native, units($orml_balance)), + ], } .assimilate_storage(&mut t) .unwrap(); @@ -110,6 +117,7 @@ pub enum ParachainType { KusamaAssetHub, Pendulum, Amplitude, + Sibling, } pub struct ExtBuilderParachain { @@ -182,22 +190,24 @@ pub fn para_ext(chain: ParachainType) -> sp_io::TestExternalities { ParachainType::Pendulum => ExtBuilderParachain::pendulum_default().balances(vec![]).build(), ParachainType::Amplitude => ExtBuilderParachain::amplitude_default().balances(vec![]).build(), + ParachainType::Sibling => ExtBuilderParachain::sibling_default().balances(vec![]).build(), } } impl ExtBuilderParachain { fn get_parachain_id(&self) -> u32 { match self.chain { - ParachainType::PolkadotAssetHub => POLKADOT_ASSETHUB_ID, - ParachainType::KusamaAssetHub => KUSAMA_ASSETHUB_ID, + ParachainType::PolkadotAssetHub => ASSETHUB_ID, + ParachainType::KusamaAssetHub => ASSETHUB_ID, ParachainType::Pendulum => PENDULUM_ID, + ParachainType::Sibling => SIBLING_ID, ParachainType::Amplitude => AMPLITUDE_ID, } } } // ------------------- for Pendulum and Amplitude ------------------- -impl ExtBuilderParachain { +impl ExtBuilderParachain { pub fn pendulum_default() -> Self { Self { balances: vec![], chain: ParachainType::Pendulum } } @@ -207,8 +217,8 @@ impl ExtBuilderParachain { } } -impl Builder for ExtBuilderParachain { - fn balances(mut self, balances: Vec<(AccountId, CurrencyId, Balance)>) -> Self { +impl Builder for ExtBuilderParachain { + fn balances(mut self, balances: Vec<(AccountId, PendulumCurrencyId, Balance)>) -> Self { self.balances = balances; self } @@ -222,7 +232,8 @@ impl Builder for ExtBuilderParachain { Runtime, System, INITIAL_BALANCE, - ORML_INITIAL_BALANCE + ORML_INITIAL_BALANCE, + PendulumCurrencyId ) }, ParachainType::Amplitude => { @@ -232,7 +243,39 @@ impl Builder for ExtBuilderParachain { Runtime, System, INITIAL_BALANCE, - ORML_INITIAL_BALANCE + ORML_INITIAL_BALANCE, + PendulumCurrencyId + ) + }, + _ => panic!("cannot use this chain to build"), + } + } +} + +// ------------------- for Sibling ------------------- +impl ExtBuilderParachain { + pub fn sibling_default() -> Self { + Self { balances: vec![], chain: ParachainType::Sibling } + } +} + +impl Builder for ExtBuilderParachain { + fn balances(mut self, balances: Vec<(AccountId, SiblingCurrencyId, Balance)>) -> Self { + self.balances = balances; + self + } + + fn build(self) -> TestExternalities { + match self.chain { + ParachainType::Sibling => { + use sibling::{Runtime, System}; + build_parachain_with_orml!( + self, + Runtime, + System, + INITIAL_BALANCE, + ORML_INITIAL_BALANCE, + SiblingCurrencyId ) }, _ => panic!("cannot use this chain to build"), diff --git a/runtime/integration-tests/src/pendulum_tests.rs b/runtime/integration-tests/src/pendulum_tests.rs index 4476245c4..79caa871c 100644 --- a/runtime/integration-tests/src/pendulum_tests.rs +++ b/runtime/integration-tests/src/pendulum_tests.rs @@ -1,12 +1,14 @@ use crate::{ mock::{para_ext, polkadot_relay_ext, ParachainType, USDT_ASSET_ID}, + sibling, test_macros::{ parachain1_transfer_asset_to_parachain2, parachain1_transfer_asset_to_parachain2_and_back, parachain1_transfer_incorrect_asset_to_parachain2_should_fail, transfer_10_relay_token_from_parachain_to_relay_chain, transfer_20_relay_token_from_relay_chain_to_parachain, + transfer_native_token_from_parachain1_to_parachain2_and_back, }, - PENDULUM_ID, POLKADOT_ASSETHUB_ID, + ASSETHUB_ID, PENDULUM_ID, SIBLING_ID, }; use frame_support::assert_ok; @@ -34,6 +36,16 @@ decl_test_parachain! { } } +decl_test_parachain! { + pub struct SiblingParachain { + Runtime = sibling::Runtime, + RuntimeOrigin = sibling::RuntimeOrigin, + XcmpMessageHandler = sibling::XcmpQueue, + DmpMessageHandler = sibling::DmpQueue, + new_ext = para_ext(ParachainType::Sibling), + } +} + decl_test_parachain! { pub struct AssetHubParachain { Runtime = polkadot_asset_hub_runtime::Runtime, @@ -50,6 +62,7 @@ decl_test_network! { parachains = vec![ (1000, AssetHubParachain), (2094, PendulumParachain), + (9999, SiblingParachain), ], } } @@ -108,7 +121,7 @@ fn assethub_transfer_asset_to_pendulum_and_back() { parachain1_transfer_asset_to_parachain2_and_back!( polkadot_asset_hub_runtime, AssetHubParachain, - POLKADOT_ASSETHUB_ID, + ASSETHUB_ID, USDT_ASSET_ID, pendulum_runtime, PendulumParachain, @@ -116,3 +129,16 @@ fn assethub_transfer_asset_to_pendulum_and_back() { network_id ); } + +#[test] +fn transfer_native_token_from_pendulum_to_sibling_parachain_and_back() { + transfer_native_token_from_parachain1_to_parachain2_and_back!( + PolkadotMockNet, + pendulum_runtime, + PendulumParachain, + sibling, + SiblingParachain, + PENDULUM_ID, + SIBLING_ID + ); +} diff --git a/runtime/integration-tests/src/sibling.rs b/runtime/integration-tests/src/sibling.rs new file mode 100644 index 000000000..90c565489 --- /dev/null +++ b/runtime/integration-tests/src/sibling.rs @@ -0,0 +1,635 @@ +// Based on https://github.com/open-web3-stack/open-runtime-module-library/blob/83a76c2bf66c0b1236c31077e6fb24bb760a3535/xtokens/src/mock/para.rs +#![cfg(test)] + +use codec::{Decode, Encode, MaxEncodedLen}; +use core::marker::PhantomData; +use frame_support::{ + log, match_types, parameter_types, + traits::{ConstU32, ContainsPair, Everything, Nothing}, +}; +use frame_system::EnsureRoot; +use orml_traits::{ + location::{RelativeReserveProvider, Reserve}, + parameter_type_with_key, +}; +use pallet_xcm::XcmPassthrough; +use polkadot_parachain::primitives::Sibling; +use polkadot_runtime_common::MAXIMUM_BLOCK_WEIGHT; +use runtime_common::parachains::polkadot::asset_hub; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_core::H256; +use sp_debug_derive::RuntimeDebug; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, Convert, IdentityLookup, Zero}, + AccountId32, +}; +use xcm::v3::prelude::*; +use xcm_emulator::{ + cumulus_pallet_parachain_system::{self, RelayNumberStrictlyIncreases}, + Weight, +}; +use xcm_executor::{ + traits::{JustTry, ShouldExecute, WeightTrader}, + Assets, XcmExecutor, +}; + +use xcm::latest::Weight as XCMWeight; +use xcm_builder::{ + AccountId32Aliases, AllowUnpaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, + FixedWeightBounds, FungiblesAdapter, NoChecking, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, +}; + +use crate::{AMPLITUDE_ID, ASSETHUB_ID, PENDULUM_ID}; + +const XCM_ASSET_RELAY_DOT: u8 = 0; +const XCM_ASSET_ASSETHUB_USDT: u8 = 1; + +pub type AccountId = AccountId32; + +parameter_types! { + pub const RelayLocation: MultiLocation = MultiLocation::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Polkadot; + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub UniversalLocation: InteriorMultiLocation = + X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); + +} + +/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the parent `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +#[derive( + Encode, + Decode, + Eq, + PartialEq, + Copy, + Clone, + RuntimeDebug, + PartialOrd, + Ord, + MaxEncodedLen, + TypeInfo, +)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum CurrencyId { + Pendulum, + Amplitude, + Native, + XCM(u8), +} + +// Convert from u32 parachain id to CurrencyId +// Needed for the test macro so it works regardless of the XCM sender parachain +impl From for CurrencyId { + fn from(id: u32) -> Self { + match id { + PENDULUM_ID => CurrencyId::Pendulum, + AMPLITUDE_ID => CurrencyId::Amplitude, + ASSETHUB_ID => CurrencyId::XCM(XCM_ASSET_ASSETHUB_USDT), + id if id == u32::from(ParachainInfo::parachain_id()) => CurrencyId::Native, + // Relay + _ => CurrencyId::XCM(XCM_ASSET_RELAY_DOT), + } + } +} + +/// CurrencyIdConvert +/// This type implements conversions from our `CurrencyId` type into `MultiLocation` and vice-versa. +/// A currency locally is identified with a `CurrencyId` variant but in the network it is identified +/// in the form of a `MultiLocation`, in this case a pCfg (Para-Id, Currency-Id). +pub struct CurrencyIdConvert; + +// Only supports native currency for now +impl Convert> for CurrencyIdConvert { + fn convert(id: CurrencyId) -> Option { + match id { + CurrencyId::Native => Some(MultiLocation::new( + 1, + X2(Parachain(ParachainInfo::parachain_id().into()), PalletInstance(10)), + )), + CurrencyId::Pendulum => + Some(MultiLocation::new(1, X2(Parachain(PENDULUM_ID), PalletInstance(10)))), + CurrencyId::Amplitude => + Some(MultiLocation::new(1, X2(Parachain(AMPLITUDE_ID), PalletInstance(10)))), + CurrencyId::XCM(f) => match f { + XCM_ASSET_RELAY_DOT => Some(MultiLocation::parent()), + // Handles both Kusama and Polkadot asset hub + XCM_ASSET_ASSETHUB_USDT => Some(MultiLocation::new( + 1, + X3( + Parachain(asset_hub::PARA_ID), + PalletInstance(asset_hub::ASSET_PALLET_ID), + GeneralIndex(asset_hub::USDT_ASSET_ID), + ), + )), + _ => None, + }, + } + } +} + +impl Convert> for CurrencyIdConvert { + fn convert(location: MultiLocation) -> Option { + match location { + MultiLocation { + parents: 1, + interior: X2(Parachain(PENDULUM_ID), PalletInstance(10)), + } => Some(CurrencyId::Pendulum), + MultiLocation { + parents: 1, + interior: X2(Parachain(AMPLITUDE_ID), PalletInstance(10)), + } => Some(CurrencyId::Amplitude), + MultiLocation { parents: 0, interior: X1(PalletInstance(10)) } => + Some(CurrencyId::Native), + // Handles both Kusama and Polkadot asset hub + MultiLocation { + parents: 1, + interior: + X3( + Parachain(asset_hub::PARA_ID), + PalletInstance(asset_hub::ASSET_PALLET_ID), + GeneralIndex(asset_hub::USDT_ASSET_ID), + ), + } => Some(CurrencyId::XCM(XCM_ASSET_ASSETHUB_USDT)), + MultiLocation { parents: 1, interior: Here } => + Some(CurrencyId::XCM(XCM_ASSET_RELAY_DOT)), + _ => None, + } + } +} + +impl Convert> for CurrencyIdConvert { + fn convert(a: MultiAsset) -> Option { + if let MultiAsset { id: AssetId::Concrete(id), fun: _ } = a { + Self::convert(id) + } else { + None + } + } +} + +/// Convert an incoming `MultiLocation` into a `CurrencyId` if possible. +/// Here we need to know the canonical representation of all the tokens we handle in order to +/// correctly convert their `MultiLocation` representation into our internal `CurrencyId` type. +impl xcm_executor::traits::Convert for CurrencyIdConvert { + fn convert(location: MultiLocation) -> Result { + >>::convert(location.clone()) + .ok_or(location) + } +} + +/// A `FilterAssetLocation` implementation. Filters multi native assets whose +/// reserve is same with `origin`. +pub struct MultiNativeAsset(PhantomData); +impl ContainsPair for MultiNativeAsset +where + ReserveProvider: Reserve, +{ + fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool { + if let Some(ref reserve) = ReserveProvider::reserve(asset) { + if reserve == origin { + return true + } + } + false + } +} + +/// Means for transacting the fungibles assets of ths parachain. +pub type FungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation + Tokens, + // This means that this adapter should handle any token that `CurrencyIdConvert` can convert + // to `CurrencyId`, the `CurrencyId` type of `Tokens`, the fungibles implementation it uses. + ConvertedConcreteId, + // Convert an XCM MultiLocation into a local account id + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly) + AccountId, + // We dont allow teleports. + NoChecking, + // The account to use for tracking teleports. + CheckingAccount, +>; + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognized. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognized. + SiblingParachainAsNative, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `Origin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +parameter_types! { + // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. + pub UnitWeightCost: XCMWeight = XCMWeight::from_parts(1_000_000_000, 0); + pub const MaxInstructions: u32 = 100; + pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::parachain_id().into()))); + pub const BaseXcmWeight: XCMWeight = XCMWeight::from_parts(150_000_000, 0); + pub const MaxAssetsForTransfer: usize = 2; +} + +match_types! { + pub type ParentOrParentsExecutivePlurality: impl Contains = { + MultiLocation { parents: 1, interior: Here } | + MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Executive, .. }) } + }; +} + +//TODO: move DenyThenTry to polkadot's xcm module. +/// Deny executing the xcm message if it matches any of the Deny filter regardless of anything else. +/// If it passes the Deny, and matches one of the Allow cases then it is let through. +pub struct DenyThenTry(PhantomData, PhantomData) +where + Deny: ShouldExecute, + Allow: ShouldExecute; + +impl ShouldExecute for DenyThenTry +where + Deny: ShouldExecute, + Allow: ShouldExecute, +{ + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + max_weight: XCMWeight, + weight_credit: &mut XCMWeight, + ) -> Result<(), ()> { + Deny::should_execute(origin, instructions, max_weight, weight_credit)?; + Allow::should_execute(origin, instructions, max_weight, weight_credit) + } +} + +// See issue #5233 +pub struct DenyReserveTransferToRelayChain; +impl ShouldExecute for DenyReserveTransferToRelayChain { + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + _max_weight: XCMWeight, + _weight_credit: &mut XCMWeight, + ) -> Result<(), ()> { + if instructions.iter().any(|inst| { + matches!( + inst, + InitiateReserveWithdraw { + reserve: MultiLocation { parents: 1, interior: Here }, + .. + } | DepositReserveAsset { dest: MultiLocation { parents: 1, interior: Here }, .. } | + TransferReserveAsset { + dest: MultiLocation { parents: 1, interior: Here }, + .. + } + ) + }) { + return Err(()) // Deny + } + + // allow reserve transfers to arrive from relay chain + if matches!(origin, MultiLocation { parents: 1, interior: Here }) && + instructions.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) + { + log::warn!( + target: "xcm::barriers", + "Unexpected ReserveAssetDeposited from the relay chain", + ); + } + // Permit everything else + Ok(()) + } +} + +pub type Barrier = AllowUnpaidExecutionFrom; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = FungiblesTransactor; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = MultiNativeAsset; + // Teleporting is disabled. + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = AllTokensAreCreatedEqualToWeight; + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = ConstU32<8>; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = SignedToAccountId32; + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type CurrencyMatcher = (); + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + // ^ Disable dispatchable execute on the XCM pallet. + // Needs to be `Everything` for local testing. + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + // ^ Override for AdvertisedXcmVersion default + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; +} + +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + +parameter_type_with_key! { + pub ParachainMinFee: |_location: MultiLocation| -> Option { + None + }; +} + +impl orml_xtokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountIdToMultiLocation = AccountIdToMultiLocation; + type SelfLocation = SelfLocation; + type XcmExecutor = XcmExecutor; + type Weigher = FixedWeightBounds; + type BaseXcmWeight = BaseXcmWeight; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; //TODO to support hrmp transfer beetween parachain adjust this parameter + type MultiLocationsFilter = Everything; + type ReserveProvider = RelativeReserveProvider; + type UniversalLocation = UniversalLocation; +} + +pub struct AccountIdToMultiLocation; +impl Convert for AccountIdToMultiLocation { + fn convert(account: AccountId) -> MultiLocation { + MultiLocation { + parents: 0, + interior: X1(xcm::v3::Junction::AccountId32 { network: None, id: account.into() }), + } + } +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Tokens: orml_tokens::{Pallet, Storage, Config, Event}, + XTokens: orml_xtokens::{Pallet, Storage, Call, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Event}, + PolkadotXcm: pallet_xcm, + ParachainSystem: cumulus_pallet_parachain_system::{ + Pallet, Call, Config, Storage, Inherent, Event, ValidateUnsigned, + }, + ParachainInfo: parachain_info::{Pallet, Storage, Config}, + XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event}, + DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event}, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin}, + } +); + +pub type Balance = u128; +pub type BlockNumber = u64; +pub type Index = u64; +pub type Amount = i64; + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} +impl frame_system::Config for Runtime { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = Index; + type BlockNumber = BlockNumber; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const MaxLocks: u32 = 50; +} + +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: CurrencyId| -> Balance { + 0 + }; +} + +pub struct CurrencyHooks(sp_std::marker::PhantomData); +impl + orml_traits::currency::MutationHooks for CurrencyHooks +{ + type OnDust = orml_tokens::BurnDust; + type OnSlash = (); + type PreDeposit = (); + type PostDeposit = (); + type PreTransfer = (); + type PostTransfer = (); + type OnNewTokenAccount = (); + type OnKilledTokenAccount = (); +} + +impl orml_tokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type CurrencyHooks = CurrencyHooks; + type MaxLocks = MaxLocks; + type MaxReserves = ConstU32<0>; + type ReserveIdentifier = (); + type DustRemovalWhitelist = Everything; +} + +parameter_types! { + pub const ExistentialDeposit: Balance = 1000; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = pallet_balances::weights::SubstrateWeight; + type MaxReserves = MaxReserves; + type ReserveIdentifier = (); +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = (); + type SelfParaId = parachain_info::Pallet; + type DmpMessageHandler = DmpQueue; + type ReservedDmpWeight = ReservedDmpWeight; + type OutboundXcmpMessageSource = XcmpQueue; + type XcmpMessageHandler = XcmpQueue; + type ReservedXcmpWeight = ReservedXcmpWeight; + type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; +} + +impl parachain_info::Config for Runtime {} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; + type ChannelInfo = ParachainSystem; + type VersionWrapper = (); + type ExecuteOverweightOrigin = EnsureRoot; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type PriceForSiblingDelivery = (); + type WeightInfo = cumulus_pallet_xcmp_queue::weights::SubstrateWeight; +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; + type ExecuteOverweightOrigin = EnsureRoot; +} + +/// A trader who believes all tokens are created equal to "weight" of any chain, +/// which is not true, but good enough to mock the fee payment of XCM execution. +/// +/// This mock will always trade `n` amount of weight to `n` amount of tokens. +pub struct AllTokensAreCreatedEqualToWeight(MultiLocation); +impl WeightTrader for AllTokensAreCreatedEqualToWeight { + fn new() -> Self { + Self(MultiLocation::parent()) + } + + fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { + let asset_id = payment.fungible.iter().next().expect("Payment must be something; qed").0; + let required = + MultiAsset { id: asset_id.clone(), fun: Fungible(weight.ref_time() as u128) }; + + if let MultiAsset { fun: _, id: Concrete(ref id) } = &required { + self.0 = id.clone(); + } + + let unused = payment.checked_sub(required).map_err(|_| XcmError::TooExpensive)?; + Ok(unused) + } + + fn refund_weight(&mut self, weight: Weight) -> Option { + if weight.is_zero() { + None + } else { + Some((self.0.clone(), weight.ref_time() as u128).into()) + } + } +} diff --git a/runtime/integration-tests/src/test_macros.rs b/runtime/integration-tests/src/test_macros.rs index 8b68195e1..09c23780b 100644 --- a/runtime/integration-tests/src/test_macros.rs +++ b/runtime/integration-tests/src/test_macros.rs @@ -461,9 +461,147 @@ macro_rules! parachain1_transfer_asset_to_parachain2_and_back { }}; } +macro_rules! transfer_native_token_from_parachain1_to_parachain2_and_back { + ( + $mocknet:ident, + $parachain1_runtime:ident, + $parachain1:ident, + $parachain2_runtime:ident, + $parachain2:ident, + $parachain1_id:ident, + $parachain2_id:ident + ) => {{ + use crate::mock::{units, ALICE, BOB}; + use frame_support::traits::fungibles::Inspect; + use polkadot_core_primitives::Balance; + use xcm::latest::{ + Junction, Junction::AccountId32, Junctions::X2, MultiLocation, WeightLimit, + }; + use $parachain1_runtime::CurrencyId as Parachain1CurrencyId; + use $parachain2_runtime::CurrencyId as Parachain2CurrencyId; + + + $mocknet::reset(); + + let transfer_amount: Balance = units(10); + let asset_location = MultiLocation::new( + 1, + X2(Junction::Parachain($parachain1_id), Junction::PalletInstance(10)), + ); + // This is needed in order to have the correct mapping regardless of the XCM sender parachain provided + // Used for checking BOB's balance + let para1_native_currency_on_para2 = Parachain2CurrencyId::from($parachain1_id); + + // Get ALICE's balance on parachain1 before the transfer + let native_tokens_before: Balance = units(100); + $parachain1::execute_with(|| { + assert_eq!( + $parachain1_runtime::Tokens::balance(Parachain1CurrencyId::Native, &ALICE.into()), + native_tokens_before + ); + }); + $parachain2::execute_with(|| { + assert_eq!( + $parachain2_runtime::Tokens::balance(para1_native_currency_on_para2, &BOB.into()), + 0 + ); + }); + + // Execute the transfer from parachain1 to parachain2 + $parachain1::execute_with(|| { + use $parachain1_runtime::{RuntimeEvent, System, XTokens}; + + // Transfer using multilocation + assert_ok!(XTokens::transfer_multiasset( + $parachain1_runtime::RuntimeOrigin::signed(ALICE.into()), + Box::new((asset_location.clone(), transfer_amount).into()), + Box::new( + MultiLocation { + parents: 1, + interior: X2( + Junction::Parachain($parachain2_id), + AccountId32 { network: None, id: BOB } + ) + } + .into() + ), + WeightLimit::Unlimited + )); + + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::XTokens(orml_xtokens::Event::TransferredMultiAssets { .. }) + ))); + }); + + // Verify BOB's balance on parachain2 after receiving + // Should increase by the transfer amount + $parachain2::execute_with(|| { + assert_eq!( + $parachain2_runtime::Tokens::balance(para1_native_currency_on_para2, &BOB.into()), + transfer_amount + ); + }); + + // Verify ALICE's balance on parachain1 after transfer + $parachain1::execute_with(|| { + assert_eq!( + $parachain1_runtime::Tokens::balance(Parachain1CurrencyId::Native, &ALICE.into()), + native_tokens_before - transfer_amount + ); + }); + + // Send same amount back to ALICE on parachain1 + $parachain2::execute_with(|| { + use $parachain2_runtime::{RuntimeEvent, System, XTokens}; + + // Transfer using the same multilocation + assert_ok!(XTokens::transfer_multiasset( + $parachain2_runtime::RuntimeOrigin::signed(BOB.into()), + Box::new((asset_location.clone(), transfer_amount).into()), + Box::new( + MultiLocation { + parents: 1, + interior: X2( + Junction::Parachain($parachain1_id), + AccountId32 { network: None, id: ALICE } + ) + } + .into() + ), + WeightLimit::Unlimited + )); + + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::XTokens(orml_xtokens::Event::TransferredMultiAssets { .. }) + ))); + }); + + // Verify BOB's balance on parachain2 after transfer + // Should become the same amount as initial balance before both transfers + $parachain2::execute_with(|| { + assert_eq!( + $parachain2_runtime::Tokens::balance(para1_native_currency_on_para2, &BOB.into()), + 0 + ); + }); + + // Verify ALICE's balance on parachain1 after receiving + // Should become the same amount as initial balance before both transfers + $parachain1::execute_with(|| { + assert_eq!( + $parachain1_runtime::Tokens::balance(Parachain1CurrencyId::Native, &ALICE.into()), + native_tokens_before + ); + }); + }}; +} + // macros defined at the bottom of this file to prevent unresolved imports pub(super) use parachain1_transfer_asset_to_parachain2; pub(super) use parachain1_transfer_asset_to_parachain2_and_back; pub(super) use parachain1_transfer_incorrect_asset_to_parachain2_should_fail; pub(super) use transfer_10_relay_token_from_parachain_to_relay_chain; pub(super) use transfer_20_relay_token_from_relay_chain_to_parachain; +pub(super) use transfer_native_token_from_parachain1_to_parachain2_and_back; diff --git a/runtime/pendulum/src/xcm_config.rs b/runtime/pendulum/src/xcm_config.rs index a6d68269b..dd16d4f26 100644 --- a/runtime/pendulum/src/xcm_config.rs +++ b/runtime/pendulum/src/xcm_config.rs @@ -76,6 +76,10 @@ impl Convert> for CurrencyIdConvert { )), _ => None, }, + CurrencyId::Native => Some(MultiLocation::new( + 1, + X2(Parachain(ParachainInfo::parachain_id().into()), PalletInstance(10)), + )), _ => None, } } @@ -95,6 +99,14 @@ impl Convert> for CurrencyIdConvert { GeneralIndex(asset_hub::USDT_ASSET_ID), ), } => Some(CurrencyId::XCM(XCM_ASSET_ASSETHUB_USDT)), + // Our native currency location without re-anchoring + MultiLocation { parents: 1, interior: X2(Parachain(id), PalletInstance(10)) } + if id == u32::from(ParachainInfo::parachain_id()) => + Some(CurrencyId::Native), + // Our native currency location with re-anchoring + // The XCM pallet will try to re-anchor the location before it reaches here + MultiLocation { parents: 0, interior: X1(PalletInstance(10)) } => + Some(CurrencyId::Native), _ => None, } }