diff --git a/Cargo.lock b/Cargo.lock index 808b6d33ae..a055895845 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8134,7 +8134,7 @@ dependencies = [ [[package]] name = "orml-traits" version = "0.10.0" -source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.11.0#b86bc7dda8cc59e57c8b5dcfa813ebdbf76bee0e" +source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.11.0#f653f239532bd72b6bc4a7290db10790a38b0b92" dependencies = [ "frame-support", "impl-trait-for-tuples", @@ -8154,7 +8154,7 @@ dependencies = [ [[package]] name = "orml-utilities" version = "0.10.0" -source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.11.0#b86bc7dda8cc59e57c8b5dcfa813ebdbf76bee0e" +source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.11.0#f653f239532bd72b6bc4a7290db10790a38b0b92" dependencies = [ "frame-support", "parity-scale-codec", @@ -8169,7 +8169,7 @@ dependencies = [ [[package]] name = "orml-xcm-support" version = "0.10.0" -source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.11.0#b86bc7dda8cc59e57c8b5dcfa813ebdbf76bee0e" +source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.11.0#f653f239532bd72b6bc4a7290db10790a38b0b92" dependencies = [ "frame-support", "orml-traits", @@ -8183,7 +8183,7 @@ dependencies = [ [[package]] name = "orml-xtokens" version = "0.10.0" -source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.11.0#b86bc7dda8cc59e57c8b5dcfa813ebdbf76bee0e" +source = "git+https://github.com/moonbeam-foundation/open-runtime-module-library?branch=moonbeam-polkadot-v1.11.0#f653f239532bd72b6bc4a7290db10790a38b0b92" dependencies = [ "frame-support", "frame-system", diff --git a/precompiles/xtokens/src/tests.rs b/precompiles/xtokens/src/tests.rs index 65f5eff676..ce2dff8a85 100644 --- a/precompiles/xtokens/src/tests.rs +++ b/precompiles/xtokens/src/tests.rs @@ -94,7 +94,7 @@ fn transfer_self_reserve_works() { weight: 4_000_000, }, ) - .expect_cost(2000) + .expect_cost(3000) .expect_no_logs() .execute_returns(()); @@ -139,7 +139,7 @@ fn transfer_to_reserve_works() { weight: 4_000_000, }, ) - .expect_cost(3000) + .expect_cost(4000) .expect_no_logs() .execute_returns(()); @@ -186,7 +186,7 @@ fn transfer_to_reserve_with_unlimited_weight_works() { weight: u64::MAX, }, ) - .expect_cost(3000) + .expect_cost(4000) .expect_no_logs() .execute_returns(()); @@ -235,7 +235,7 @@ fn transfer_to_reserve_with_fee_works() { weight: 4_000_000, }, ) - .expect_cost(3000) + .expect_cost(4000) .expect_no_logs() .execute_returns(()); @@ -290,7 +290,7 @@ fn transfer_non_reserve_to_non_reserve_works() { weight: 4_000_000, }, ) - .expect_cost(3000) + .expect_cost(4000) .expect_no_logs() .execute_returns(()); @@ -339,7 +339,7 @@ fn transfer_non_reserve_to_non_reserve_with_fee_works() { weight: 4_000_000, }, ) - .expect_cost(3000) + .expect_cost(4000) .expect_no_logs() .execute_returns(()); @@ -394,7 +394,7 @@ fn transfer_multi_asset_to_reserve_works() { weight: 4_000_000, }, ) - .expect_cost(3000) + .expect_cost(4000) .expect_no_logs() .execute_returns(()); @@ -442,7 +442,7 @@ fn transfer_multi_asset_self_reserve_works() { weight: 4_000_000, }, ) - .expect_cost(2000) + .expect_cost(3000) .expect_no_logs() .execute_returns(()); @@ -490,7 +490,7 @@ fn transfer_multi_asset_self_reserve_with_fee_works() { weight: 4_000_000, }, ) - .expect_cost(2000) + .expect_cost(3000) .expect_no_logs() .execute_returns(()); @@ -542,7 +542,7 @@ fn transfer_multi_asset_non_reserve_to_non_reserve() { weight: 4_000_000, }, ) - .expect_cost(3000) + .expect_cost(4000) .expect_no_logs() .execute_returns(()); @@ -591,7 +591,7 @@ fn transfer_multi_asset_non_reserve_to_non_reserve_with_fee() { weight: 4_000_000, }, ) - .expect_cost(3000) + .expect_cost(4000) .expect_no_logs() .execute_returns(()); @@ -645,7 +645,7 @@ fn transfer_multi_currencies() { weight: 4_000_000, }, ) - .expect_cost(3000) + .expect_cost(4000) .expect_no_logs() .execute_returns(()); @@ -718,7 +718,7 @@ fn transfer_multi_assets() { weight: 4_000_000, }, ) - .expect_cost(3000) + .expect_cost(4000) .expect_no_logs() .execute_returns(()); diff --git a/runtime/moonbase/tests/integration_test.rs b/runtime/moonbase/tests/integration_test.rs index c27f8085f9..841a55e0b7 100644 --- a/runtime/moonbase/tests/integration_test.rs +++ b/runtime/moonbase/tests/integration_test.rs @@ -1568,7 +1568,7 @@ fn xtokens_precompiles_transfer() { weight: 4_000_000, }, ) - .expect_cost(348090) + .expect_cost(348298) .expect_no_logs() // We expect an evm subcall ERC20.burnFrom .with_subcall_handle(move |subcall| { @@ -1659,7 +1659,7 @@ fn xtokens_precompiles_transfer_multiasset() { weight: 4_000_000, }, ) - .expect_cost(348090) + .expect_cost(348298) .expect_no_logs() // We expect an evm subcall ERC20.burnFrom .with_subcall_handle(move |subcall| { @@ -1743,7 +1743,7 @@ fn xtokens_precompiles_transfer_native() { weight: 4_000_000, }, ) - .expect_cost(16000) + .expect_cost(16208) .expect_no_logs() .execute_returns(()); }) diff --git a/runtime/moonbase/tests/xcm_mock/mod.rs b/runtime/moonbase/tests/xcm_mock/mod.rs index f556bc4710..6c23384729 100644 --- a/runtime/moonbase/tests/xcm_mock/mod.rs +++ b/runtime/moonbase/tests/xcm_mock/mod.rs @@ -106,7 +106,7 @@ decl_test_parachain! { Runtime = statemint_like::Runtime, XcmpMessageHandler = statemint_like::MsgQueue, DmpMessageHandler = statemint_like::MsgQueue, - new_ext = statemint_ext(4), + new_ext = statemint_ext(1000), } } @@ -118,7 +118,7 @@ decl_test_relay_chain! { XcmConfig = relay_chain::XcmConfig, MessageQueue = relay_chain::MessageQueue, System = relay_chain::System, - new_ext = relay_ext(vec![1, 2, 3, 4]), + new_ext = relay_ext(vec![1, 2, 3, 1000]), } } @@ -129,7 +129,7 @@ decl_test_network! { (1, ParaA), (2, ParaB), (3, ParaC), - (4, Statemint), + (1000, Statemint), ], } } diff --git a/runtime/moonbase/tests/xcm_mock/parachain.rs b/runtime/moonbase/tests/xcm_mock/parachain.rs index 4f39468545..0d7fd72050 100644 --- a/runtime/moonbase/tests/xcm_mock/parachain.rs +++ b/runtime/moonbase/tests/xcm_mock/parachain.rs @@ -50,11 +50,11 @@ use xcm::latest::{ }; use xcm_builder::{ AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, - FixedWeightBounds, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter, IsConcrete, - NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, - TakeWeightCredit, WithComputedOrigin, + AllowTopLevelPaidExecutionFrom, Case, ConvertedConcreteId, EnsureXcmOrigin, + FixedRateOfFungible, FixedWeightBounds, FungibleAdapter as XcmCurrencyAdapter, + FungiblesAdapter, IsConcrete, NoChecking, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountKey20AsNative, SovereignSignedViaLocation, TakeWeightCredit, WithComputedOrigin, }; use xcm_executor::{traits::JustTry, Config, XcmExecutor}; @@ -330,6 +330,18 @@ parameter_types! { ].into() }; pub const MaxAssetsIntoHolding: u32 = 64; + + pub AssetHubLocation: Location = Location::new(1, [Parachain(1000)]); + pub const RelayLocation: Location = Location::parent(); + pub RelayLocationFilter: AssetFilter = Wild(AllOf { + fun: WildFungible, + id: xcm::prelude::AssetId(RelayLocation::get()), + }); + + pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = ( + RelayLocationFilter::get(), + AssetHubLocation::get() + ); } use frame_system::RawOrigin; @@ -338,15 +350,22 @@ use sp_runtime::DispatchErrorWithPostInfo; use xcm_executor::traits::CallDispatcher; moonbeam_runtime_common::impl_moonbeam_xcm_call!(); +type Reserves = ( + // Relaychain (DOT) from Asset Hub + Case, + // Assets which the reserve is the same as the origin. + orml_xcm_support::MultiNativeAsset< + xcm_primitives::AbsoluteAndRelativeReserve, + >, +); + pub struct XcmConfig; impl Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = orml_xcm_support::MultiNativeAsset< - xcm_primitives::AbsoluteAndRelativeReserve, - >; + type IsReserve = Reserves; type IsTeleporter = (); type UniversalLocation = UniversalLocation; type Barrier = XcmBarrier; @@ -430,7 +449,7 @@ parameter_types! { parameter_type_with_key! { pub ParachainMinFee: |location: Location| -> Option { match (location.parents, location.first_interior()) { - (1, Some(Parachain(4u32))) => Some(50u128), + (1, Some(Parachain(1000u32))) => Some(50u128), _ => None, } }; diff --git a/runtime/moonbase/tests/xcm_mock/relay_chain.rs b/runtime/moonbase/tests/xcm_mock/relay_chain.rs index 15bb49b91e..f391b31d84 100644 --- a/runtime/moonbase/tests/xcm_mock/relay_chain.rs +++ b/runtime/moonbase/tests/xcm_mock/relay_chain.rs @@ -177,7 +177,7 @@ pub type XcmBarrier = ( parameter_types! { pub Kusama: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId(KsmLocation::get()) }); - pub Statemine: Location = Parachain(4).into(); + pub Statemine: Location = Parachain(1000).into(); pub KusamaForStatemine: (AssetFilter, Location) = (Kusama::get(), Statemine::get()); } diff --git a/runtime/moonbase/tests/xcm_mock/statemint_like.rs b/runtime/moonbase/tests/xcm_mock/statemint_like.rs index 48a358c0a5..594919836f 100644 --- a/runtime/moonbase/tests/xcm_mock/statemint_like.rs +++ b/runtime/moonbase/tests/xcm_mock/statemint_like.rs @@ -18,19 +18,17 @@ use frame_support::{ construct_runtime, parameter_types, - traits::{AsEnsureOriginWithArg, Contains, Everything, Nothing}, + traits::{AsEnsureOriginWithArg, Contains, ContainsPair, Everything, Get, Nothing}, weights::Weight, }; use frame_system::{EnsureRoot, EnsureSigned}; - +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; use sp_core::H256; use sp_runtime::{ traits::{ConstU32, Hash, IdentityLookup}, AccountId32, }; -use polkadot_core_primitives::BlockNumber as RelayBlockNumber; - use polkadot_parachain::primitives::Id as ParaId; use polkadot_parachain::primitives::Sibling; use sp_std::convert::TryFrom; @@ -290,8 +288,33 @@ pub type Barrier = ( parameter_types! { pub MatcherLocation: Location = Location::here(); pub const MaxAssetsIntoHolding: u32 = 64; + pub const RelayTokenLocation: Location = Location::parent(); } +// Copied from: +// +// https://github.com/paritytech/polkadot-sdk/blob/f4eb41773611008040c9d4d8a8e6b7323eccfca1/cumulus +// /parachains/common/src/xcm_config.rs#L118 +// +// The difference with the original "ConcreteAssetFromSystem" (which is used by AssetHub), +// is that in our tests we only need to check if the asset matches the relay one. +pub struct ConcreteAssetFromRelay(sp_std::marker::PhantomData); +impl> ContainsPair + for ConcreteAssetFromRelay +{ + fn contains(asset: &Asset, origin: &Location) -> bool { + let is_relay = match origin.unpack() { + // The Relay Chain + (1, []) => true, + // Others + _ => false, + }; + asset.id.0 == AssetLocation::get() && is_relay + } +} + +pub type TrustedTeleporters = (ConcreteAssetFromRelay,); + pub struct XcmConfig; impl Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -300,7 +323,7 @@ impl Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = orml_xcm_support::MultiNativeAsset; - type IsTeleporter = (); + type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; diff --git a/runtime/moonbase/tests/xcm_tests.rs b/runtime/moonbase/tests/xcm_tests.rs index 24ad722407..f894ce5329 100644 --- a/runtime/moonbase/tests/xcm_tests.rs +++ b/runtime/moonbase/tests/xcm_tests.rs @@ -30,9 +30,10 @@ use pallet_xcm_transactor::{ use sp_runtime::traits::MaybeEquivalence; use sp_std::boxed::Box; use xcm::latest::prelude::{ - AccountId32, AccountKey20, All, BuyExecution, ClearOrigin, DepositAsset, GeneralIndex, - Junction, Junctions, Limited, Location, OriginKind, PalletInstance, Parachain, QueryResponse, - Reanchorable, Response, WeightLimit, WithdrawAsset, Xcm, + AccountId32, AccountKey20, All, Asset as XcmAsset, AssetId as XcmAssetId, Assets as XcmAssets, + BuyExecution, ClearOrigin, DepositAsset, Fungible, GeneralIndex, Junction, Junctions, Limited, + Location, OriginKind, PalletInstance, Parachain, QueryResponse, Reanchorable, Response, + WeightLimit, WithdrawAsset, Xcm, }; use xcm::{VersionedLocation, WrapVersion}; use xcm_executor::traits::ConvertLocation; @@ -2626,7 +2627,7 @@ fn test_statemint_like() { let statemint_asset_a_balances = Location::new( 1, [ - Parachain(4), + Parachain(1000), PalletInstance(5), xcm::latest::prelude::GeneralIndex(0u128), ], @@ -2736,7 +2737,11 @@ fn send_statemint_asset_from_para_a_to_statemint_with_relay_fee() { // Statemint asset let statemint_asset = Location::new( 1, - [Parachain(4u32), PalletInstance(5u8), GeneralIndex(10u128)], + [ + Parachain(1000u32), + PalletInstance(5u8), + GeneralIndex(10u128), + ], ); let statemint_location_asset = parachain::AssetType::Xcm( xcm_builder::WithLatestLocationConverter::convert(&statemint_asset).expect("convert to v3"), @@ -2873,7 +2878,7 @@ fn send_statemint_asset_from_para_a_to_statemint_with_relay_fee() { let statemint_beneficiary = Location { parents: 1, interior: [ - Parachain(4), + Parachain(1000), AccountId32 { network: None, id: RELAYBOB.into(), @@ -2932,6 +2937,1006 @@ fn send_statemint_asset_from_para_a_to_statemint_with_relay_fee() { }); } +#[test] +fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemint_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemint_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs from AssetHub to ParaA (Moonbeam) + Statemint::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_relay_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemint::execute_with(|| { + // Check that Bob received the tokens back in AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 100 + ); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemint::execute_with(|| { + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!(StatemintBalances::free_balance(RELAYBOB), INITIAL_BALANCE); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_with_fee() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemint_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemint_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs from AssetHub to ParaA (Moonbeam) + Statemint::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_with_fee( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_relay_id), + 100, + 10, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 90); + }); + + Statemint::execute_with(|| { + // Free execution: check that Bob received the tokens back in AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 110 + ); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemint::execute_with(|| { + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 10 + ); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 190); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multiasset() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemint_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemint_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs from AssetHub to ParaA (Moonbeam) + Statemint::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multiasset( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new((Location::parent(), 100).into()), + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemint::execute_with(|| { + // Check that Bob received the tokens back in AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 100 + ); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemint::execute_with(|| { + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!(StatemintBalances::free_balance(RELAYBOB), INITIAL_BALANCE); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multicurrencies() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Statemint asset + let statemint_asset = Location::new( + 1, + [ + Parachain(1000u32), + PalletInstance(5u8), + GeneralIndex(10u128), + ], + ); + let statemint_location_asset = parachain::AssetType::Xcm( + xcm_builder::WithLatestLocationConverter::convert(&statemint_asset).expect("convert to v3"), + ); + let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); + + let asset_metadata_statemint_asset = parachain::AssetMetadata { + name: b"USDC".to_vec(), + symbol: b"USDC".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + statemint_location_asset.clone(), + asset_metadata_statemint_asset, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + statemint_location_asset, + 0u128, + 1 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemint_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemint_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) + Statemint::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + statemint_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + + assert_ok!(StatemintAssets::create( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 1 + )); + + assert_ok!(StatemintAssets::mint( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 300000000000000 + )); + + // Now send relay tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + + // Send USDC + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + GeneralIndex(10), + ], + 125 + ) + .into() + ), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + + // Alice has received 125 USDC + assert_eq!( + Assets::balance(source_statemint_asset_id, &PARAALICE.into()), + 125 + ); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multicurrencies( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + vec![ + ( + parachain::CurrencyId::ForeignAsset(source_statemint_asset_id), + 100 + ), + (parachain::CurrencyId::ForeignAsset(source_relay_id), 100) + ], + 1, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemint::execute_with(|| { + // Check that Bob received relay tokens back in AssetHub + // (100 - MinXcmFee) + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 50 + ); + + // Check that BOB received 100 USDC on AssetHub + assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemint::execute_with(|| { + let bob_previous_balance = StatemintBalances::free_balance(RELAYBOB); + + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + bob_previous_balance - 100 + ); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multiassets() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Statemint asset + let statemint_asset = Location::new( + 1, + [ + Parachain(1000u32), + PalletInstance(5u8), + GeneralIndex(10u128), + ], + ); + let statemint_location_asset = parachain::AssetType::Xcm( + xcm_builder::WithLatestLocationConverter::convert(&statemint_asset).expect("convert to v3"), + ); + let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); + + let asset_metadata_statemint_asset = parachain::AssetMetadata { + name: b"USDC".to_vec(), + symbol: b"USDC".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + statemint_location_asset.clone(), + asset_metadata_statemint_asset, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + statemint_location_asset, + 0u128, + 1 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemint_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemint_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) + Statemint::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + statemint_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + + assert_ok!(StatemintAssets::create( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 1 + )); + + assert_ok!(StatemintAssets::mint( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 300000000000000 + )); + + // Now send relay tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + + // Send USDC + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + GeneralIndex(10), + ], + 125 + ) + .into() + ), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + + // Alice has received 125 USDC + assert_eq!( + Assets::balance(source_statemint_asset_id, &PARAALICE.into()), + 125 + ); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + let statemint_asset_to_send = XcmAsset { + id: XcmAssetId(statemint_asset), + fun: Fungible(100), + }; + + let relay_asset_to_send = XcmAsset { + id: XcmAssetId(Location::parent()), + fun: Fungible(100), + }; + + let assets_to_send: XcmAssets = + XcmAssets::from(vec![statemint_asset_to_send, relay_asset_to_send.clone()]); + + // For some reason the order of the assets is inverted when creating the array above. + // We need to use relay asset for fees, so we pick index 0. + assert_eq!(assets_to_send.get(0).unwrap(), &relay_asset_to_send); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multiassets( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(assets_to_send.into()), + 0, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemint::execute_with(|| { + // Check that Bob received relay tokens back in AssetHub + // (100 - MinXcmFee) + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 50 + ); + + // Check that BOB received 100 USDC on AssetHub + assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemint::execute_with(|| { + let bob_previous_balance = StatemintBalances::free_balance(RELAYBOB); + + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + bob_previous_balance - 100 + ); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + #[test] fn transact_through_signed_multilocation() { MockNet::reset(); diff --git a/runtime/moonbeam/tests/integration_test.rs b/runtime/moonbeam/tests/integration_test.rs index 7035c7ef68..cc57f12aa7 100644 --- a/runtime/moonbeam/tests/integration_test.rs +++ b/runtime/moonbeam/tests/integration_test.rs @@ -1945,7 +1945,7 @@ fn xtokens_precompile_transfer() { weight: 4_000_000, }, ) - .expect_cost(196490) + .expect_cost(196698) .expect_no_logs() .execute_returns(()) }) @@ -1997,7 +1997,7 @@ fn xtokens_precompile_transfer_multiasset() { weight: 4_000_000, }, ) - .expect_cost(196490) + .expect_cost(196698) .expect_no_logs() .execute_returns(()); }) diff --git a/runtime/moonbeam/tests/xcm_mock/mod.rs b/runtime/moonbeam/tests/xcm_mock/mod.rs index d52700b810..96ba4b9ec4 100644 --- a/runtime/moonbeam/tests/xcm_mock/mod.rs +++ b/runtime/moonbeam/tests/xcm_mock/mod.rs @@ -106,7 +106,7 @@ decl_test_parachain! { Runtime = statemint_like::Runtime, XcmpMessageHandler = statemint_like::MsgQueue, DmpMessageHandler = statemint_like::MsgQueue, - new_ext = statemint_ext(4), + new_ext = statemint_ext(1000), } } @@ -118,7 +118,7 @@ decl_test_relay_chain! { XcmConfig = relay_chain::XcmConfig, MessageQueue = relay_chain::MessageQueue, System = relay_chain::System, - new_ext = relay_ext(vec![1, 2, 3, 4]), + new_ext = relay_ext(vec![1, 2, 3, 1000]), } } @@ -129,7 +129,7 @@ decl_test_network! { (1, ParaA), (2, ParaB), (3, ParaC), - (4, Statemint), + (1000, Statemint), ], } } diff --git a/runtime/moonbeam/tests/xcm_mock/parachain.rs b/runtime/moonbeam/tests/xcm_mock/parachain.rs index 2fe3e12459..5bffb0e38c 100644 --- a/runtime/moonbeam/tests/xcm_mock/parachain.rs +++ b/runtime/moonbeam/tests/xcm_mock/parachain.rs @@ -51,11 +51,11 @@ use xcm::latest::{ }; use xcm_builder::{ AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, - FixedWeightBounds, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter, IsConcrete, - NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, - TakeWeightCredit, WithComputedOrigin, + AllowTopLevelPaidExecutionFrom, Case, ConvertedConcreteId, EnsureXcmOrigin, + FixedRateOfFungible, FixedWeightBounds, FungibleAdapter as XcmCurrencyAdapter, + FungiblesAdapter, IsConcrete, NoChecking, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountKey20AsNative, SovereignSignedViaLocation, TakeWeightCredit, WithComputedOrigin, }; use xcm_executor::{traits::JustTry, Config, XcmExecutor}; @@ -321,6 +321,18 @@ parameter_types! { ].into() }; pub const MaxAssetsIntoHolding: u32 = 64; + + pub AssetHubLocation: Location = Location::new(1, [Parachain(1000)]); + pub const RelayLocation: Location = Location::parent(); + pub RelayLocationFilter: AssetFilter = Wild(AllOf { + fun: WildFungible, + id: xcm::prelude::AssetId(RelayLocation::get()), + }); + + pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = ( + RelayLocationFilter::get(), + AssetHubLocation::get() + ); } use frame_system::RawOrigin; @@ -329,15 +341,22 @@ use sp_runtime::DispatchErrorWithPostInfo; use xcm_executor::traits::CallDispatcher; moonbeam_runtime_common::impl_moonbeam_xcm_call!(); +type Reserves = ( + // Relaychain (DOT) from Asset Hub + Case, + // Assets which the reserve is the same as the origin. + orml_xcm_support::MultiNativeAsset< + xcm_primitives::AbsoluteAndRelativeReserve, + >, +); + pub struct XcmConfig; impl Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = orml_xcm_support::MultiNativeAsset< - xcm_primitives::AbsoluteAndRelativeReserve, - >; + type IsReserve = Reserves; type IsTeleporter = (); type UniversalLocation = UniversalLocation; type Barrier = XcmBarrier; @@ -415,7 +434,7 @@ parameter_types! { parameter_type_with_key! { pub ParachainMinFee: |location: Location| -> Option { match (location.parents, location.first_interior()) { - (1, Some(Parachain(4u32))) => Some(50u128), + (1, Some(Parachain(1000u32))) => Some(50u128), _ => None, } }; diff --git a/runtime/moonbeam/tests/xcm_mock/relay_chain.rs b/runtime/moonbeam/tests/xcm_mock/relay_chain.rs index 15bb49b91e..f391b31d84 100644 --- a/runtime/moonbeam/tests/xcm_mock/relay_chain.rs +++ b/runtime/moonbeam/tests/xcm_mock/relay_chain.rs @@ -177,7 +177,7 @@ pub type XcmBarrier = ( parameter_types! { pub Kusama: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId(KsmLocation::get()) }); - pub Statemine: Location = Parachain(4).into(); + pub Statemine: Location = Parachain(1000).into(); pub KusamaForStatemine: (AssetFilter, Location) = (Kusama::get(), Statemine::get()); } diff --git a/runtime/moonbeam/tests/xcm_mock/statemint_like.rs b/runtime/moonbeam/tests/xcm_mock/statemint_like.rs index 48a358c0a5..3b37a8e6c3 100644 --- a/runtime/moonbeam/tests/xcm_mock/statemint_like.rs +++ b/runtime/moonbeam/tests/xcm_mock/statemint_like.rs @@ -18,7 +18,7 @@ use frame_support::{ construct_runtime, parameter_types, - traits::{AsEnsureOriginWithArg, Contains, Everything, Nothing}, + traits::{AsEnsureOriginWithArg, Contains, ContainsPair, Everything, Get, Nothing}, weights::Weight, }; use frame_system::{EnsureRoot, EnsureSigned}; @@ -290,8 +290,33 @@ pub type Barrier = ( parameter_types! { pub MatcherLocation: Location = Location::here(); pub const MaxAssetsIntoHolding: u32 = 64; + pub const RelayTokenLocation: Location = Location::parent(); } +// Copied from: +// +// https://github.com/paritytech/polkadot-sdk/blob/f4eb41773611008040c9d4d8a8e6b7323eccfca1/cumulus +// /parachains/common/src/xcm_config.rs#L118 +// +// The difference with the original "ConcreteAssetFromSystem" (which is used by AssetHub), +// is that in our tests we only need to check if the asset matches the relay one. +pub struct ConcreteAssetFromRelay(sp_std::marker::PhantomData); +impl> ContainsPair + for ConcreteAssetFromRelay +{ + fn contains(asset: &Asset, origin: &Location) -> bool { + let is_relay = match origin.unpack() { + // The Relay Chain + (1, []) => true, + // Others + _ => false, + }; + asset.id.0 == AssetLocation::get() && is_relay + } +} + +pub type TrustedTeleporters = (ConcreteAssetFromRelay,); + pub struct XcmConfig; impl Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -300,7 +325,7 @@ impl Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = orml_xcm_support::MultiNativeAsset; - type IsTeleporter = (); + type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; diff --git a/runtime/moonbeam/tests/xcm_tests.rs b/runtime/moonbeam/tests/xcm_tests.rs index ff3409abc9..e711d0f10f 100644 --- a/runtime/moonbeam/tests/xcm_tests.rs +++ b/runtime/moonbeam/tests/xcm_tests.rs @@ -31,8 +31,9 @@ use pallet_xcm_transactor::{ use sp_core::ConstU32; use sp_runtime::traits::MaybeEquivalence; use xcm::latest::prelude::{ - AccountId32, AccountKey20, GeneralIndex, Junction, Junctions, Limited, Location, OriginKind, - PalletInstance, Parachain, QueryResponse, Reanchorable, Response, WeightLimit, Xcm, + AccountId32, AccountKey20, Asset as XcmAsset, AssetId as XcmAssetId, Assets as XcmAssets, + Fungible, GeneralIndex, Junction, Junctions, Limited, Location, OriginKind, PalletInstance, + Parachain, QueryResponse, Reanchorable, Response, WeightLimit, Xcm, }; use xcm::{VersionedLocation, WrapVersion}; use xcm_executor::traits::ConvertLocation; @@ -2350,7 +2351,7 @@ fn test_statemint_like() { let statemint_asset_a_balances = Location::new( 1, [ - Parachain(4), + Parachain(1000), PalletInstance(5), xcm::latest::prelude::GeneralIndex(0u128), ], @@ -2461,7 +2462,11 @@ fn send_statemint_asset_from_para_a_to_statemint_with_relay_fee() { // Statemint asset let statemint_asset = Location::new( 1, - [Parachain(4u32), PalletInstance(5u8), GeneralIndex(10u128)], + [ + Parachain(1000u32), + PalletInstance(5u8), + GeneralIndex(10u128), + ], ); let statemint_location_asset = parachain::AssetType::Xcm( xcm_builder::WithLatestLocationConverter::convert(&statemint_asset).expect("convert to v3"), @@ -2598,7 +2603,7 @@ fn send_statemint_asset_from_para_a_to_statemint_with_relay_fee() { let statemint_beneficiary = Location { parents: 1, interior: [ - Parachain(4), + Parachain(1000), AccountId32 { network: None, id: RELAYBOB.into(), @@ -2658,6 +2663,1006 @@ fn send_statemint_asset_from_para_a_to_statemint_with_relay_fee() { }); } +#[test] +fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemint_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemint_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs from AssetHub to ParaA (Moonbeam) + Statemint::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_relay_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemint::execute_with(|| { + // Check that Bob received the tokens back in AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 100 + ); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemint::execute_with(|| { + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!(StatemintBalances::free_balance(RELAYBOB), INITIAL_BALANCE); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_with_fee() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemint_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemint_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs from AssetHub to ParaA (Moonbeam) + Statemint::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_with_fee( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_relay_id), + 100, + 10, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 90); + }); + + Statemint::execute_with(|| { + // Free execution: check that Bob received the tokens back in AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 110 + ); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemint::execute_with(|| { + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 10 + ); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 190); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multiasset() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemint_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemint_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs from AssetHub to ParaA (Moonbeam) + Statemint::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multiasset( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new((Location::parent(), 100).into()), + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemint::execute_with(|| { + // Check that Bob received the tokens back in AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 100 + ); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemint::execute_with(|| { + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!(StatemintBalances::free_balance(RELAYBOB), INITIAL_BALANCE); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multicurrencies() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Statemint asset + let statemint_asset = Location::new( + 1, + [ + Parachain(1000u32), + PalletInstance(5u8), + GeneralIndex(10u128), + ], + ); + let statemint_location_asset = parachain::AssetType::Xcm( + xcm_builder::WithLatestLocationConverter::convert(&statemint_asset).expect("convert to v3"), + ); + let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); + + let asset_metadata_statemint_asset = parachain::AssetMetadata { + name: b"USDC".to_vec(), + symbol: b"USDC".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + statemint_location_asset.clone(), + asset_metadata_statemint_asset, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + statemint_location_asset, + 0u128, + 1 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemint_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemint_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) + Statemint::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + statemint_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + + assert_ok!(StatemintAssets::create( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 1 + )); + + assert_ok!(StatemintAssets::mint( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 300000000000000 + )); + + // Now send relay tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + + // Send USDC + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + GeneralIndex(10), + ], + 125 + ) + .into() + ), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + + // Alice has received 125 USDC + assert_eq!( + Assets::balance(source_statemint_asset_id, &PARAALICE.into()), + 125 + ); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multicurrencies( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + vec![ + ( + parachain::CurrencyId::ForeignAsset(source_statemint_asset_id), + 100 + ), + (parachain::CurrencyId::ForeignAsset(source_relay_id), 100) + ], + 1, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemint::execute_with(|| { + // Check that Bob received relay tokens back in AssetHub + // (100 - MinXcmFee) + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 50 + ); + + // Check that BOB received 100 USDC on AssetHub + assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemint::execute_with(|| { + let bob_previous_balance = StatemintBalances::free_balance(RELAYBOB); + + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + bob_previous_balance - 100 + ); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multiassets() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Statemint asset + let statemint_asset = Location::new( + 1, + [ + Parachain(1000u32), + PalletInstance(5u8), + GeneralIndex(10u128), + ], + ); + let statemint_location_asset = parachain::AssetType::Xcm( + xcm_builder::WithLatestLocationConverter::convert(&statemint_asset).expect("convert to v3"), + ); + let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); + + let asset_metadata_statemint_asset = parachain::AssetMetadata { + name: b"USDC".to_vec(), + symbol: b"USDC".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemint_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + statemint_location_asset.clone(), + asset_metadata_statemint_asset, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + statemint_location_asset, + 0u128, + 1 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemint_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemint_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) + Statemint::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemintBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemintBalances::transfer_allow_death( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + statemint_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + + assert_ok!(StatemintAssets::create( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 1 + )); + + assert_ok!(StatemintAssets::mint( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 300000000000000 + )); + + // Now send relay tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + + // Send USDC + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + GeneralIndex(10), + ], + 125 + ) + .into() + ), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + + // Alice has received 125 USDC + assert_eq!( + Assets::balance(source_statemint_asset_id, &PARAALICE.into()), + 125 + ); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + let statemint_asset_to_send = XcmAsset { + id: XcmAssetId(statemint_asset), + fun: Fungible(100), + }; + + let relay_asset_to_send = XcmAsset { + id: XcmAssetId(Location::parent()), + fun: Fungible(100), + }; + + let assets_to_send: XcmAssets = + XcmAssets::from(vec![statemint_asset_to_send, relay_asset_to_send.clone()]); + + // For some reason the order of the assets is inverted when creating the array above. + // We need to use relay asset for fees, so we pick index 0. + assert_eq!(assets_to_send.get(0).unwrap(), &relay_asset_to_send); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multiassets( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(assets_to_send.into()), + 0, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemint::execute_with(|| { + // Check that Bob received relay tokens back in AssetHub + // (100 - MinXcmFee) + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 50 + ); + + // Check that BOB received 100 USDC on AssetHub + assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemint::execute_with(|| { + let bob_previous_balance = StatemintBalances::free_balance(RELAYBOB); + + // Now send those tokens to ParaA + assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets( + statemint_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!( + StatemintBalances::free_balance(RELAYBOB), + bob_previous_balance - 100 + ); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + #[test] fn transact_through_signed_multilocation() { MockNet::reset(); diff --git a/runtime/moonriver/tests/integration_test.rs b/runtime/moonriver/tests/integration_test.rs index 27761b3224..6f01317e82 100644 --- a/runtime/moonriver/tests/integration_test.rs +++ b/runtime/moonriver/tests/integration_test.rs @@ -1925,7 +1925,7 @@ fn xtokens_precompiles_transfer() { weight: 4_000_000, }, ) - .expect_cost(196490) + .expect_cost(196698) .expect_no_logs() .execute_returns(()) }) @@ -1977,7 +1977,7 @@ fn xtokens_precompiles_transfer_multiasset() { weight: 4_000_000, }, ) - .expect_cost(196490) + .expect_cost(196698) .expect_no_logs() .execute_returns(()); }) diff --git a/runtime/moonriver/tests/xcm_mock/mod.rs b/runtime/moonriver/tests/xcm_mock/mod.rs index 8d5d6e8c6c..9b8f3ea50d 100644 --- a/runtime/moonriver/tests/xcm_mock/mod.rs +++ b/runtime/moonriver/tests/xcm_mock/mod.rs @@ -106,7 +106,7 @@ decl_test_parachain! { Runtime = statemine_like::Runtime, XcmpMessageHandler = statemine_like::MsgQueue, DmpMessageHandler = statemine_like::MsgQueue, - new_ext = statemine_ext(4), + new_ext = statemine_ext(1000), } } @@ -118,7 +118,7 @@ decl_test_relay_chain! { XcmConfig = relay_chain::XcmConfig, MessageQueue = relay_chain::MessageQueue, System = relay_chain::System, - new_ext = relay_ext(vec![1, 2, 3, 4]), + new_ext = relay_ext(vec![1, 2, 3, 1000]), } } @@ -129,7 +129,7 @@ decl_test_network! { (1, ParaA), (2, ParaB), (3, ParaC), - (4, Statemine), + (1000, Statemine), ], } } diff --git a/runtime/moonriver/tests/xcm_mock/parachain.rs b/runtime/moonriver/tests/xcm_mock/parachain.rs index 4a7f38e165..22cf8ec9fb 100644 --- a/runtime/moonriver/tests/xcm_mock/parachain.rs +++ b/runtime/moonriver/tests/xcm_mock/parachain.rs @@ -49,11 +49,11 @@ use xcm::latest::{ }; use xcm_builder::{ AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, - FixedWeightBounds, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter, IsConcrete, - NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, - TakeWeightCredit, WithComputedOrigin, + AllowTopLevelPaidExecutionFrom, Case, ConvertedConcreteId, EnsureXcmOrigin, + FixedRateOfFungible, FixedWeightBounds, FungibleAdapter as XcmCurrencyAdapter, + FungiblesAdapter, IsConcrete, NoChecking, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountKey20AsNative, SovereignSignedViaLocation, TakeWeightCredit, WithComputedOrigin, }; use xcm_executor::{traits::JustTry, Config, XcmExecutor}; @@ -321,6 +321,18 @@ parameter_types! { ].into() }; pub const MaxAssetsIntoHolding: u32 = 64; + + pub AssetHubLocation: Location = Location::new(1, [Parachain(1000)]); + pub const RelayLocation: Location = Location::parent(); + pub RelayLocationFilter: AssetFilter = Wild(AllOf { + fun: WildFungible, + id: xcm::prelude::AssetId(RelayLocation::get()), + }); + + pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = ( + RelayLocationFilter::get(), + AssetHubLocation::get() + ); } use frame_system::RawOrigin; @@ -329,15 +341,22 @@ use sp_runtime::DispatchErrorWithPostInfo; use xcm_executor::traits::CallDispatcher; moonbeam_runtime_common::impl_moonbeam_xcm_call!(); +type Reserves = ( + // Relaychain (DOT) from Asset Hub + Case, + // Assets which the reserve is the same as the origin. + orml_xcm_support::MultiNativeAsset< + xcm_primitives::AbsoluteAndRelativeReserve, + >, +); + pub struct XcmConfig; impl Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = orml_xcm_support::MultiNativeAsset< - xcm_primitives::AbsoluteAndRelativeReserve, - >; + type IsReserve = Reserves; type IsTeleporter = (); type UniversalLocation = UniversalLocation; type Barrier = XcmBarrier; @@ -420,7 +439,7 @@ parameter_types! { parameter_type_with_key! { pub ParachainMinFee: |location: Location| -> Option { match (location.parents, location.first_interior()) { - (1, Some(Parachain(4u32))) => Some(50u128), + (1, Some(Parachain(1000u32))) => Some(50u128), _ => None, } }; diff --git a/runtime/moonriver/tests/xcm_mock/relay_chain.rs b/runtime/moonriver/tests/xcm_mock/relay_chain.rs index 15bb49b91e..f391b31d84 100644 --- a/runtime/moonriver/tests/xcm_mock/relay_chain.rs +++ b/runtime/moonriver/tests/xcm_mock/relay_chain.rs @@ -177,7 +177,7 @@ pub type XcmBarrier = ( parameter_types! { pub Kusama: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId(KsmLocation::get()) }); - pub Statemine: Location = Parachain(4).into(); + pub Statemine: Location = Parachain(1000).into(); pub KusamaForStatemine: (AssetFilter, Location) = (Kusama::get(), Statemine::get()); } diff --git a/runtime/moonriver/tests/xcm_mock/statemine_like.rs b/runtime/moonriver/tests/xcm_mock/statemine_like.rs index 6168450597..f5d2b46cdf 100644 --- a/runtime/moonriver/tests/xcm_mock/statemine_like.rs +++ b/runtime/moonriver/tests/xcm_mock/statemine_like.rs @@ -18,7 +18,7 @@ use frame_support::{ construct_runtime, parameter_types, - traits::{AsEnsureOriginWithArg, Contains, Everything, Nothing}, + traits::{AsEnsureOriginWithArg, Contains, ContainsPair, Everything, Get, Nothing}, weights::Weight, }; use frame_system::{EnsureRoot, EnsureSigned}; @@ -290,8 +290,33 @@ pub type Barrier = ( parameter_types! { pub MatcherLocation: Location = Location::here(); pub const MaxAssetsIntoHolding: u32 = 64; + pub const RelayTokenLocation: Location = Location::parent(); } +// Copied from: +// +// https://github.com/paritytech/polkadot-sdk/blob/f4eb41773611008040c9d4d8a8e6b7323eccfca1/cumulus +// /parachains/common/src/xcm_config.rs#L118 +// +// The difference with the original "ConcreteAssetFromSystem" (which is used by AssetHub), +// is that in our tests we only need to check if the asset matches the relay one. +pub struct ConcreteAssetFromRelay(sp_std::marker::PhantomData); +impl> ContainsPair + for ConcreteAssetFromRelay +{ + fn contains(asset: &Asset, origin: &Location) -> bool { + let is_relay = match origin.unpack() { + // The Relay Chain + (1, []) => true, + // Others + _ => false, + }; + asset.id.0 == AssetLocation::get() && is_relay + } +} + +pub type TrustedTeleporters = (ConcreteAssetFromRelay,); + pub struct XcmConfig; impl Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -300,7 +325,7 @@ impl Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = orml_xcm_support::MultiNativeAsset; - type IsTeleporter = (); + type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; diff --git a/runtime/moonriver/tests/xcm_tests.rs b/runtime/moonriver/tests/xcm_tests.rs index 2ca25f5530..59e0cbc2b4 100644 --- a/runtime/moonriver/tests/xcm_tests.rs +++ b/runtime/moonriver/tests/xcm_tests.rs @@ -26,9 +26,10 @@ use frame_support::{ use sp_core::ConstU32; use sp_runtime::traits::MaybeEquivalence; use xcm::latest::prelude::{ - AccountId32, AccountKey20, All, BuyExecution, ClearOrigin, DepositAsset, GeneralIndex, - Junction, Junctions, Limited, Location, OriginKind, PalletInstance, Parachain, QueryResponse, - Reanchorable, Response, WeightLimit, WithdrawAsset, Xcm, + AccountId32, AccountKey20, All, Asset as XcmAsset, AssetId as XcmAssetId, Assets as XcmAssets, + BuyExecution, ClearOrigin, DepositAsset, Fungible, GeneralIndex, Junction, Junctions, Limited, + Location, OriginKind, PalletInstance, Parachain, QueryResponse, Reanchorable, Response, + WeightLimit, WithdrawAsset, Xcm, }; use xcm::{VersionedLocation, WrapVersion}; use xcm_executor::traits::ConvertLocation; @@ -2660,7 +2661,7 @@ fn test_statemine_like() { let statemine_asset_a_balances = Location::new( 1, [ - Parachain(4), + Parachain(1000), PalletInstance(5), xcm::latest::prelude::GeneralIndex(0u128), ], @@ -2754,7 +2755,7 @@ fn test_statemine_like() { } #[test] -fn send_statemint_asset_from_para_a_to_statemine_with_relay_fee() { +fn send_statemine_asset_from_para_a_to_statemine_with_relay_fee() { MockNet::reset(); // Relay asset @@ -2770,7 +2771,11 @@ fn send_statemint_asset_from_para_a_to_statemine_with_relay_fee() { // Statemine asset let statemine_asset = Location::new( 1, - [Parachain(4u32), PalletInstance(5u8), GeneralIndex(10u128)], + [ + Parachain(1000u32), + PalletInstance(5u8), + GeneralIndex(10u128), + ], ); let statemine_location_asset = parachain::AssetType::Xcm( xcm_builder::WithLatestLocationConverter::convert(&statemine_asset).expect("convert to v3"), @@ -2872,7 +2877,7 @@ fn send_statemint_asset_from_para_a_to_statemine_with_relay_fee() { )); // Send statemine USDC asset to Alice in Parachain A - let parachain_beneficiary_from_statemint: Location = AccountKey20 { + let parachain_beneficiary_from_statemine: Location = AccountKey20 { network: None, key: PARAALICE, } @@ -2883,7 +2888,7 @@ fn send_statemint_asset_from_para_a_to_statemine_with_relay_fee() { statemine_like::RuntimeOrigin::signed(RELAYALICE), Box::new(Location::new(1, [Parachain(1)]).into()), Box::new( - VersionedLocation::V4(parachain_beneficiary_from_statemint) + VersionedLocation::V4(parachain_beneficiary_from_statemine) .clone() .into() ), @@ -2907,7 +2912,7 @@ fn send_statemint_asset_from_para_a_to_statemine_with_relay_fee() { let statemine_beneficiary = Location { parents: 1, interior: [ - Parachain(4), + Parachain(1000), AccountId32 { network: None, id: RELAYBOB.into(), @@ -2966,6 +2971,1006 @@ fn send_statemint_asset_from_para_a_to_statemine_with_relay_fee() { }); } +#[test] +fn send_dot_from_moonbeam_to_statemine_via_xtokens_transfer() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemine_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemine_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemine_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs from AssetHub to ParaA (Moonbeam) + Statemine::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemineBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemineBalances::transfer_allow_death( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + // Now send those tokens to ParaA + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_relay_id), + 100, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemine::execute_with(|| { + // Check that Bob received the tokens back in AssetHub + assert_eq!( + StatemineBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 100 + ); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemine::execute_with(|| { + // Now send those tokens to ParaA + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!(StatemineBalances::free_balance(RELAYBOB), INITIAL_BALANCE); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemine_via_xtokens_transfer_with_fee() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemine_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemine_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemine_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs from AssetHub to ParaA (Moonbeam) + Statemine::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemineBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemineBalances::transfer_allow_death( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + // Now send those tokens to ParaA + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_with_fee( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + parachain::CurrencyId::ForeignAsset(source_relay_id), + 100, + 10, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 90); + }); + + Statemine::execute_with(|| { + // Free execution: check that Bob received the tokens back in AssetHub + assert_eq!( + StatemineBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 110 + ); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemine::execute_with(|| { + // Now send those tokens to ParaA + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!( + StatemineBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 10 + ); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 190); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemine_via_xtokens_transfer_multiasset() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemine_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemine_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemine_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs from AssetHub to ParaA (Moonbeam) + Statemine::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemineBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemineBalances::transfer_allow_death( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + // Now send those tokens to ParaA + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multiasset( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new((Location::parent(), 100).into()), + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemine::execute_with(|| { + // Check that Bob received the tokens back in AssetHub + assert_eq!( + StatemineBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 100 + ); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemine::execute_with(|| { + // Now send those tokens to ParaA + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!(StatemineBalances::free_balance(RELAYBOB), INITIAL_BALANCE); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemine_via_xtokens_transfer_multicurrencies() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Statemine asset + let statemine_asset = Location::new( + 1, + [ + Parachain(1000u32), + PalletInstance(5u8), + GeneralIndex(10u128), + ], + ); + let statemine_location_asset = parachain::AssetType::Xcm( + xcm_builder::WithLatestLocationConverter::convert(&statemine_asset).expect("convert to v3"), + ); + let source_statemine_asset_id: parachain::AssetId = statemine_location_asset.clone().into(); + + let asset_metadata_statemine_asset = parachain::AssetMetadata { + name: b"USDC".to_vec(), + symbol: b"USDC".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemine_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + statemine_location_asset.clone(), + asset_metadata_statemine_asset, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + statemine_location_asset, + 0u128, + 1 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemine_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemine_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) + Statemine::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemineBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemineBalances::transfer_allow_death( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + statemine_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + + assert_ok!(StatemineAssets::create( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 1 + )); + + assert_ok!(StatemineAssets::mint( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 300000000000000 + )); + + // Now send relay tokens to ParaA + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + + // Send USDC + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + GeneralIndex(10), + ], + 125 + ) + .into() + ), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + + // Alice has received 125 USDC + assert_eq!( + Assets::balance(source_statemine_asset_id, &PARAALICE.into()), + 125 + ); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multicurrencies( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + vec![ + ( + parachain::CurrencyId::ForeignAsset(source_statemine_asset_id), + 100 + ), + (parachain::CurrencyId::ForeignAsset(source_relay_id), 100) + ], + 1, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemine::execute_with(|| { + // Check that Bob received relay tokens back in AssetHub + // (100 - MinXcmFee) + assert_eq!( + StatemineBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 50 + ); + + // Check that BOB received 100 USDC on AssetHub + assert_eq!(StatemineAssets::account_balances(RELAYBOB), vec![(10, 100)]); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemine::execute_with(|| { + let bob_previous_balance = StatemineBalances::free_balance(RELAYBOB); + + // Now send those tokens to ParaA + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!( + StatemineBalances::free_balance(RELAYBOB), + bob_previous_balance - 100 + ); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + +#[test] +fn send_dot_from_moonbeam_to_statemine_via_xtokens_transfer_multiassets() { + MockNet::reset(); + + // Relay asset + let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); + let source_relay_id: parachain::AssetId = relay_location.clone().into(); + + let relay_asset_metadata = parachain::AssetMetadata { + name: b"RelayToken".to_vec(), + symbol: b"Relay".to_vec(), + decimals: 12, + }; + + // Statemine asset + let statemine_asset = Location::new( + 1, + [ + Parachain(1000u32), + PalletInstance(5u8), + GeneralIndex(10u128), + ], + ); + let statemine_location_asset = parachain::AssetType::Xcm( + xcm_builder::WithLatestLocationConverter::convert(&statemine_asset).expect("convert to v3"), + ); + let source_statemine_asset_id: parachain::AssetId = statemine_location_asset.clone().into(); + + let asset_metadata_statemine_asset = parachain::AssetMetadata { + name: b"USDC".to_vec(), + symbol: b"USDC".to_vec(), + decimals: 12, + }; + + let dest_para = Location::new(1, [Parachain(1)]); + + let sov = xcm_builder::SiblingParachainConvertsVia::< + polkadot_parachain::primitives::Sibling, + statemine_like::AccountId, + >::convert_location(&dest_para) + .unwrap(); + + ParaA::execute_with(|| { + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + relay_location.clone(), + relay_asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + relay_location, + 0u128, + 0 + )); + + assert_ok!(AssetManager::register_foreign_asset( + parachain::RuntimeOrigin::root(), + statemine_location_asset.clone(), + asset_metadata_statemine_asset, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::RuntimeOrigin::root(), + statemine_location_asset, + 0u128, + 1 + )); + }); + + let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { + network: None, + key: PARAALICE, + } + .into(); + + let statemine_beneficiary_absolute: Location = Junction::AccountId32 { + network: None, + id: RELAYALICE.into(), + } + .into(); + + // First we send relay chain asset to Alice in AssetHub (via teleport) + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_teleport_assets( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + Box::new(Parachain(1000).into()), + Box::new( + VersionedLocation::V4(statemine_beneficiary_absolute) + .clone() + .into() + ), + Box::new(([], 200).into()), + 0, + WeightLimit::Unlimited + )); + }); + + // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) + Statemine::execute_with(|| { + // Check Alice received 200 tokens on AssetHub + assert_eq!( + StatemineBalances::free_balance(RELAYALICE), + INITIAL_BALANCE + 200 + ); + + assert_ok!(StatemineBalances::transfer_allow_death( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + sov, + 110000000000000 + )); + + statemine_like::PrefixChanger::set_prefix( + PalletInstance(::index() as u8).into(), + ); + + assert_ok!(StatemineAssets::create( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 1 + )); + + assert_ok!(StatemineAssets::mint( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + 10, + RELAYALICE, + 300000000000000 + )); + + // Now send relay tokens to ParaA + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new((Location::parent(), 200).into()), + 0, + WeightLimit::Unlimited + )); + + // Send USDC + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYALICE), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute.clone()) + .clone() + .into() + ), + Box::new( + ( + [ + xcm::latest::prelude::PalletInstance( + ::index() as u8 + ), + GeneralIndex(10), + ], + 125 + ) + .into() + ), + 0, + WeightLimit::Unlimited + )); + }); + + ParaA::execute_with(|| { + // Alice should have received the DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + + // Alice has received 125 USDC + assert_eq!( + Assets::balance(source_statemine_asset_id, &PARAALICE.into()), + 125 + ); + }); + + let dest = Location::new( + 1, + [ + Parachain(1000), + AccountId32 { + network: None, + id: RELAYBOB.into(), + }, + ], + ); + + let statemine_asset_to_send = XcmAsset { + id: XcmAssetId(statemine_asset), + fun: Fungible(100), + }; + + let relay_asset_to_send = XcmAsset { + id: XcmAssetId(Location::parent()), + fun: Fungible(100), + }; + + let assets_to_send: XcmAssets = + XcmAssets::from(vec![statemine_asset_to_send, relay_asset_to_send.clone()]); + + // For some reason the order of the assets is inverted when creating the array above. + // We need to use relay asset for fees, so we pick index 0. + assert_eq!(assets_to_send.get(0).unwrap(), &relay_asset_to_send); + + // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_multiassets( + parachain::RuntimeOrigin::signed(PARAALICE.into()), + Box::new(assets_to_send.into()), + 0, + Box::new(VersionedLocation::V4(dest)), + WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) + )); + + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100); + }); + + Statemine::execute_with(|| { + // Check that Bob received relay tokens back in AssetHub + // (100 - MinXcmFee) + assert_eq!( + StatemineBalances::free_balance(RELAYBOB), + INITIAL_BALANCE + 50 + ); + + // Check that BOB received 100 USDC on AssetHub + assert_eq!(StatemineAssets::account_balances(RELAYBOB), vec![(10, 100)]); + }); + + // Send back tokens from AH to ParaA from Bob's account + Statemine::execute_with(|| { + let bob_previous_balance = StatemineBalances::free_balance(RELAYBOB); + + // Now send those tokens to ParaA + assert_ok!(StatemineChainPalletXcm::limited_reserve_transfer_assets( + statemine_like::RuntimeOrigin::signed(RELAYBOB), + Box::new(Location::new(1, [Parachain(1)]).into()), + Box::new( + VersionedLocation::V4(parachain_beneficiary_absolute) + .clone() + .into() + ), + Box::new((Location::parent(), 100).into()), + 0, + WeightLimit::Unlimited + )); + + // 100 DOTs were deducted from Bob's account + assert_eq!( + StatemineBalances::free_balance(RELAYBOB), + bob_previous_balance - 100 + ); + }); + + ParaA::execute_with(|| { + // Alice should have received 100 DOTs + assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200); + }); +} + #[test] fn transact_through_signed_multilocation() { MockNet::reset();