diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs index a542de16de5f..77e4c8183e65 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs @@ -24,8 +24,7 @@ mod imports { pub use xcm::{ latest::ParentThen, prelude::{AccountId32 as AccountId32Junction, *}, - v4, - v4::NetworkId::Westend as WestendId, + v4::{self, NetworkId::Westend as WestendId}, }; pub use xcm_executor::traits::TransferType; @@ -38,17 +37,18 @@ mod imports { xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, TestExt, }, + xcm_helpers::xcm_transact_paid_execution, ASSETS_PALLET_ID, USDT_ID, }; pub use parachains_common::AccountId; pub use rococo_westend_system_emulated_network::{ asset_hub_rococo_emulated_chain::{ asset_hub_rococo_runtime::xcm_config as ahr_xcm_config, - genesis::{AssetHubRococoAssetOwner, ED as ASSET_HUB_ROCOCO_ED}, - AssetHubRococoParaPallet as AssetHubRococoPallet, + genesis::ED as ASSET_HUB_ROCOCO_ED, AssetHubRococoParaPallet as AssetHubRococoPallet, }, asset_hub_westend_emulated_chain::{ - genesis::ED as ASSET_HUB_WESTEND_ED, AssetHubWestendParaPallet as AssetHubWestendPallet, + genesis::{AssetHubWestendAssetOwner, ED as ASSET_HUB_WESTEND_ED}, + AssetHubWestendParaPallet as AssetHubWestendPallet, }, bridge_hub_rococo_emulated_chain::{ genesis::ED as BRIDGE_HUB_ROCOCO_ED, BridgeHubRococoExistentialDeposit, @@ -80,6 +80,7 @@ mod imports { RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender, }; + pub const ASSET_ID: u32 = 1; pub const ASSET_MIN_BALANCE: u128 = 1000; } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs index f1e2a6dcca61..0e1cfdd82aaf 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs @@ -113,14 +113,8 @@ fn send_assets_from_penpal_rococo_through_rococo_ah_to_westend_ah( } #[test] -/// Test transfer of ROC, USDT and wETH from AssetHub Rococo to AssetHub Westend. -/// -/// This mix of assets should cover the whole range: -/// - native assets: ROC, -/// - trust-based assets: USDT (exists only on Rococo, Westend gets it from Rococo over bridge), -/// - foreign asset / bridged asset (other bridge / Snowfork): wETH (bridged from Ethereum to Rococo -/// over Snowbridge, then bridged over to Westend through this bridge). -fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { +/// Test transfer of ROC from AssetHub Rococo to AssetHub Westend. +fn send_roc_from_asset_hub_rococo_to_asset_hub_westend() { let amount = ASSET_HUB_ROCOCO_ED * 1_000_000; let sender = AssetHubRococoSender::get(); let receiver = AssetHubWestendReceiver::get(); @@ -128,11 +122,8 @@ fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { let bridged_roc_at_asset_hub_westend = bridged_roc_at_ah_westend(); create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), true); - set_up_pool_with_wnd_on_ah_westend(bridged_roc_at_asset_hub_westend.clone()); + set_up_pool_with_wnd_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), true); - //////////////////////////////////////////////////////////// - // Let's first send over just some ROCs as a simple example - //////////////////////////////////////////////////////////// let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( Westend, AssetHubWestend::para_id(), @@ -146,8 +137,7 @@ fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { // send ROCs, use them for fees send_assets_over_bridge(|| { let destination = asset_hub_westend_location(); - let assets: Assets = - (Location::try_from(roc_at_asset_hub_rococo.clone()).unwrap(), amount).into(); + let assets: Assets = (roc_at_asset_hub_rococo.clone(), amount).into(); let fee_idx = 0; assert_ok!(send_assets_from_asset_hub_rococo(destination, assets, fee_idx)); }); @@ -183,84 +173,18 @@ fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { assert!(receiver_rocs_after > receiver_rocs_before); // Reserve ROC balance is increased by sent amount assert_eq!(rocs_in_reserve_on_ahr_after, rocs_in_reserve_on_ahr_before + amount); - - ///////////////////////////////////////////////////////////// - // Now let's send over USDTs + wETH (and pay fees with USDT) - ///////////////////////////////////////////////////////////// - - let usdt_at_asset_hub_rococo = usdt_at_ah_rococo(); - let bridged_usdt_at_asset_hub_westend = bridged_usdt_at_ah_westend(); - // wETH has same relative location on both Rococo and Westend AssetHubs - let bridged_weth_at_ah = weth_at_asset_hubs(); - - // mint USDT in sender's account (USDT already created in genesis) - AssetHubRococo::mint_asset( - ::RuntimeOrigin::signed(AssetHubRococoAssetOwner::get()), - USDT_ID, - sender.clone(), - amount * 2, - ); - // create wETH at src and dest and prefund sender's account - create_foreign_on_ah_rococo( - bridged_weth_at_ah.clone(), - true, - vec![(sender.clone(), amount * 2)], - ); - create_foreign_on_ah_westend(bridged_weth_at_ah.clone(), true); - create_foreign_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone(), true); - set_up_pool_with_wnd_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone()); - - let receiver_usdts_before = - foreign_balance_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone(), &receiver); - let receiver_weth_before = foreign_balance_on_ah_westend(bridged_weth_at_ah.clone(), &receiver); - - // send USDTs and wETHs - let assets: Assets = vec![ - (usdt_at_asset_hub_rococo.clone(), amount).into(), - (Location::try_from(bridged_weth_at_ah.clone()).unwrap(), amount).into(), - ] - .into(); - // use USDT for fees - let fee: AssetId = usdt_at_asset_hub_rococo.into(); - - // use the more involved transfer extrinsic - let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(AllCounted(assets.len() as u32)), - beneficiary: AccountId32Junction { network: None, id: receiver.clone().into() }.into(), - }]); - assert_ok!(AssetHubRococo::execute_with(|| { - ::PolkadotXcm::transfer_assets_using_type_and_then( - ::RuntimeOrigin::signed(sender.into()), - bx!(asset_hub_westend_location().into()), - bx!(assets.into()), - bx!(TransferType::LocalReserve), - bx!(fee.into()), - bx!(TransferType::LocalReserve), - bx!(VersionedXcm::from(custom_xcm_on_dest)), - WeightLimit::Unlimited, - ) - })); - // verify hops (also advances the message through the hops) - assert_bridge_hub_rococo_message_accepted(true); - assert_bridge_hub_westend_message_received(); - AssetHubWestend::execute_with(|| { - AssetHubWestend::assert_xcmp_queue_success(None); - }); - - let receiver_usdts_after = - foreign_balance_on_ah_westend(bridged_usdt_at_asset_hub_westend, &receiver); - let receiver_weth_after = foreign_balance_on_ah_westend(bridged_weth_at_ah, &receiver); - - // Receiver's USDT balance is increased by almost `amount` (minus fees) - assert!(receiver_usdts_after > receiver_usdts_before); - assert!(receiver_usdts_after < receiver_usdts_before + amount); - // Receiver's wETH balance is increased by sent amount - assert_eq!(receiver_weth_after, receiver_weth_before + amount); } #[test] -/// Send bridged WNDs "back" from AssetHub Rococo to AssetHub Westend. -fn send_back_wnds_from_asset_hub_rococo_to_asset_hub_westend() { +/// Send bridged assets "back" from AssetHub Rococo to AssetHub Westend. +/// +/// This mix of assets should cover the whole range: +/// - bridged native assets: ROC, +/// - bridged trust-based assets: USDT (exists only on Westend, Rococo gets it from Westend over +/// bridge), +/// - bridged foreign asset / double-bridged asset (other bridge / Snowfork): wETH (bridged from +/// Ethereum to Westend over Snowbridge, then bridged over to Rococo through this bridge). +fn send_back_wnds_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { let prefund_amount = 10_000_000_000_000u128; let amount_to_send = ASSET_HUB_WESTEND_ED * 1_000; let sender = AssetHubRococoSender::get(); @@ -269,6 +193,10 @@ fn send_back_wnds_from_asset_hub_rococo_to_asset_hub_westend() { let prefund_accounts = vec![(sender.clone(), prefund_amount)]; create_foreign_on_ah_rococo(wnd_at_asset_hub_rococo.clone(), true, prefund_accounts); + //////////////////////////////////////////////////////////// + // Let's first send back just some WNDs as a simple example + //////////////////////////////////////////////////////////// + // fund the AHR's SA on AHW with the WND tokens held in reserve let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( Rococo, @@ -317,7 +245,7 @@ fn send_back_wnds_from_asset_hub_rococo_to_asset_hub_westend() { }); let sender_wnds_after = foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo, &sender); - let receiver_wnds_after = ::account_data_of(receiver).free; + let receiver_wnds_after = ::account_data_of(receiver.clone()).free; let wnds_in_reserve_on_ahw_after = ::account_data_of(sov_ahr_on_ahw).free; @@ -327,6 +255,96 @@ fn send_back_wnds_from_asset_hub_rococo_to_asset_hub_westend() { assert!(receiver_wnds_after > receiver_wnds_before); // Reserve balance is reduced by sent amount assert_eq!(wnds_in_reserve_on_ahw_after, wnds_in_reserve_on_ahw_before - amount_to_send); + + ////////////////////////////////////////////////////////////////// + // Now let's send back over USDTs + wETH (and pay fees with USDT) + ////////////////////////////////////////////////////////////////// + + // wETH has same relative location on both Westend and Rococo AssetHubs + let bridged_weth_at_ah = weth_at_asset_hubs(); + let bridged_usdt_at_asset_hub_rococo = bridged_usdt_at_ah_rococo(); + + // set up destination chain AH Westend: + // create a WND/USDT pool to be able to pay fees with USDT (USDT created in genesis) + set_up_pool_with_wnd_on_ah_westend(usdt_at_ah_westend(), false); + // create wETH on Westend (IRL it's already created by Snowbridge) + create_foreign_on_ah_westend(bridged_weth_at_ah.clone(), true); + // prefund AHR's sovereign account on AHW to be able to withdraw USDT and wETH from reserves + let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( + Rococo, + AssetHubRococo::para_id(), + ); + AssetHubWestend::mint_asset( + ::RuntimeOrigin::signed(AssetHubWestendAssetOwner::get()), + USDT_ID, + sov_ahr_on_ahw.clone(), + amount_to_send * 2, + ); + AssetHubWestend::mint_foreign_asset( + ::RuntimeOrigin::signed(AssetHubWestend::account_id_of(ALICE)), + bridged_weth_at_ah.clone(), + sov_ahr_on_ahw, + amount_to_send * 2, + ); + + // set up source chain AH Rococo: + // create wETH and USDT foreign assets on Rococo and prefund sender's account + let prefund_accounts = vec![(sender.clone(), amount_to_send * 2)]; + create_foreign_on_ah_rococo(bridged_weth_at_ah.clone(), true, prefund_accounts.clone()); + create_foreign_on_ah_rococo(bridged_usdt_at_asset_hub_rococo.clone(), true, prefund_accounts); + + // check balances before + let receiver_usdts_before = AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + >::balance(USDT_ID, &receiver) + }); + let receiver_weth_before = foreign_balance_on_ah_westend(bridged_weth_at_ah.clone(), &receiver); + + let usdt_id: AssetId = Location::try_from(bridged_usdt_at_asset_hub_rococo).unwrap().into(); + // send USDTs and wETHs + let assets: Assets = vec![ + (usdt_id.clone(), amount_to_send).into(), + (Location::try_from(bridged_weth_at_ah.clone()).unwrap(), amount_to_send).into(), + ] + .into(); + // use USDT for fees + let fee = usdt_id; + + // use the more involved transfer extrinsic + let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(AllCounted(assets.len() as u32)), + beneficiary: AccountId32Junction { network: None, id: receiver.clone().into() }.into(), + }]); + assert_ok!(AssetHubRococo::execute_with(|| { + ::PolkadotXcm::transfer_assets_using_type_and_then( + ::RuntimeOrigin::signed(sender.into()), + bx!(asset_hub_westend_location().into()), + bx!(assets.into()), + bx!(TransferType::DestinationReserve), + bx!(fee.into()), + bx!(TransferType::DestinationReserve), + bx!(VersionedXcm::from(custom_xcm_on_dest)), + WeightLimit::Unlimited, + ) + })); + // verify hops (also advances the message through the hops) + assert_bridge_hub_rococo_message_accepted(true); + assert_bridge_hub_westend_message_received(); + AssetHubWestend::execute_with(|| { + AssetHubWestend::assert_xcmp_queue_success(None); + }); + + let receiver_usdts_after = AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + >::balance(USDT_ID, &receiver) + }); + let receiver_weth_after = foreign_balance_on_ah_westend(bridged_weth_at_ah, &receiver); + + // Receiver's USDT balance is increased by almost `amount_to_send` (minus fees) + assert!(receiver_usdts_after > receiver_usdts_before); + assert!(receiver_usdts_after < receiver_usdts_before + amount_to_send); + // Receiver's wETH balance is increased by `amount_to_send` + assert_eq!(receiver_weth_after, receiver_weth_before + amount_to_send); } #[test] diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs index a989881fef09..767f74f6ad7f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs @@ -17,6 +17,7 @@ use crate::imports::*; mod asset_transfers; mod claim_assets; +mod register_bridged_assets; mod send_xcm; mod snowbridge; mod teleport; @@ -45,15 +46,15 @@ pub(crate) fn bridged_wnd_at_ah_rococo() -> Location { } // USDT and wUSDT -pub(crate) fn usdt_at_ah_rococo() -> Location { +pub(crate) fn usdt_at_ah_westend() -> Location { Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]) } -pub(crate) fn bridged_usdt_at_ah_westend() -> Location { +pub(crate) fn bridged_usdt_at_ah_rococo() -> Location { Location::new( 2, [ - GlobalConsensus(Rococo), - Parachain(AssetHubRococo::para_id().into()), + GlobalConsensus(Westend), + Parachain(AssetHubWestend::para_id().into()), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into()), ], @@ -100,23 +101,36 @@ pub(crate) fn foreign_balance_on_ah_westend(id: v4::Location, who: &AccountId) - } // set up pool -pub(crate) fn set_up_pool_with_wnd_on_ah_westend(foreign_asset: v4::Location) { +pub(crate) fn set_up_pool_with_wnd_on_ah_westend(asset: v4::Location, is_foreign: bool) { let wnd: v4::Location = v4::Parent.into(); AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; let owner = AssetHubWestendSender::get(); let signed_owner = ::RuntimeOrigin::signed(owner.clone()); - assert_ok!(::ForeignAssets::mint( - signed_owner.clone(), - foreign_asset.clone().into(), - owner.clone().into(), - 3_000_000_000_000, - )); + if is_foreign { + assert_ok!(::ForeignAssets::mint( + signed_owner.clone(), + asset.clone().into(), + owner.clone().into(), + 3_000_000_000_000, + )); + } else { + let asset_id = match asset.interior.last() { + Some(v4::Junction::GeneralIndex(id)) => *id as u32, + _ => unreachable!(), + }; + assert_ok!(::Assets::mint( + signed_owner.clone(), + asset_id.into(), + owner.clone().into(), + 3_000_000_000_000, + )); + } assert_ok!(::AssetConversion::create_pool( signed_owner.clone(), Box::new(wnd.clone()), - Box::new(foreign_asset.clone()), + Box::new(asset.clone()), )); assert_expected_events!( AssetHubWestend, @@ -127,7 +141,7 @@ pub(crate) fn set_up_pool_with_wnd_on_ah_westend(foreign_asset: v4::Location) { assert_ok!(::AssetConversion::add_liquidity( signed_owner.clone(), Box::new(wnd), - Box::new(foreign_asset), + Box::new(asset), 1_000_000_000_000, 2_000_000_000_000, 1, @@ -149,7 +163,7 @@ pub(crate) fn send_assets_from_asset_hub_rococo( fee_idx: u32, ) -> DispatchResult { let signed_origin = - ::RuntimeOrigin::signed(AssetHubRococoSender::get().into()); + ::RuntimeOrigin::signed(AssetHubRococoSender::get()); let beneficiary: Location = AccountId32Junction { network: None, id: AssetHubWestendReceiver::get().into() }.into(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/register_bridged_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/register_bridged_assets.rs new file mode 100644 index 000000000000..44637670112b --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/register_bridged_assets.rs @@ -0,0 +1,107 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{imports::*, tests::*}; + +const XCM_FEE: u128 = 4_000_000_000_000; + +/// Tests the registering of a Rococo Asset as a bridged asset on Westend Asset Hub. +#[test] +fn register_rococo_asset_on_wah_from_rah() { + let sa_of_rah_on_wah = + AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( + Rococo, + AssetHubRococo::para_id(), + ); + + // Rococo Asset Hub asset when bridged to Westend Asset Hub. + let bridged_asset_at_wah = Location::new( + 2, + [ + GlobalConsensus(Rococo), + Parachain(AssetHubRococo::para_id().into()), + PalletInstance(ASSETS_PALLET_ID), + GeneralIndex(ASSET_ID.into()), + ], + ); + + // Encoded `create_asset` call to be executed in Westend Asset Hub ForeignAssets pallet. + let call = AssetHubWestend::create_foreign_asset_call( + bridged_asset_at_wah.clone(), + ASSET_MIN_BALANCE, + sa_of_rah_on_wah.clone(), + ); + + let origin_kind = OriginKind::Xcm; + let fee_amount = XCM_FEE; + let fees = (Parent, fee_amount).into(); + + let xcm = xcm_transact_paid_execution(call, origin_kind, fees, sa_of_rah_on_wah.clone()); + + // SA-of-RAH-on-WAH needs to have balance to pay for fees and asset creation deposit + AssetHubWestend::fund_accounts(vec![( + sa_of_rah_on_wah.clone(), + ASSET_HUB_WESTEND_ED * 10000000000, + )]); + + let destination = asset_hub_westend_location(); + + // fund the RAH's SA on RBH for paying bridge transport fees + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), 10_000_000_000_000u128); + + // set XCM versions + AssetHubRococo::force_xcm_version(destination.clone(), XCM_VERSION); + BridgeHubRococo::force_xcm_version(bridge_hub_westend_location(), XCM_VERSION); + + let root_origin = ::RuntimeOrigin::root(); + AssetHubRococo::execute_with(|| { + assert_ok!(::PolkadotXcm::send( + root_origin, + bx!(destination.into()), + bx!(xcm), + )); + + AssetHubRococo::assert_xcm_pallet_sent(); + }); + + assert_bridge_hub_rococo_message_accepted(true); + assert_bridge_hub_westend_message_received(); + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + AssetHubWestend::assert_xcmp_queue_success(None); + assert_expected_events!( + AssetHubWestend, + vec![ + // Burned the fee + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { + who: *who == sa_of_rah_on_wah.clone(), + amount: *amount == fee_amount, + }, + // Foreign Asset created + RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { asset_id, creator, owner }) => { + asset_id: asset_id == &bridged_asset_at_wah, + creator: *creator == sa_of_rah_on_wah.clone(), + owner: *owner == sa_of_rah_on_wah, + }, + // Unspent fee minted to origin + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { + who: *who == sa_of_rah_on_wah.clone(), + }, + ] + ); + type ForeignAssets = ::ForeignAssets; + assert!(ForeignAssets::asset_exists(bridged_asset_at_wah)); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 9228700c0198..76e8312921de 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -37,16 +37,17 @@ mod imports { xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, TestExt, }, + xcm_helpers::xcm_transact_paid_execution, ASSETS_PALLET_ID, USDT_ID, }; pub use parachains_common::AccountId; pub use rococo_westend_system_emulated_network::{ asset_hub_rococo_emulated_chain::{ - genesis::{AssetHubRococoAssetOwner, ED as ASSET_HUB_ROCOCO_ED}, - AssetHubRococoParaPallet as AssetHubRococoPallet, + genesis::ED as ASSET_HUB_ROCOCO_ED, AssetHubRococoParaPallet as AssetHubRococoPallet, }, asset_hub_westend_emulated_chain::{ - genesis::ED as ASSET_HUB_WESTEND_ED, AssetHubWestendParaPallet as AssetHubWestendPallet, + genesis::{AssetHubWestendAssetOwner, ED as ASSET_HUB_WESTEND_ED}, + AssetHubWestendParaPallet as AssetHubWestendPallet, }, bridge_hub_westend_emulated_chain::{ genesis::ED as BRIDGE_HUB_WESTEND_ED, BridgeHubWestendExistentialDeposit, @@ -74,6 +75,7 @@ mod imports { WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, }; + pub const ASSET_ID: u32 = 1; pub const ASSET_MIN_BALANCE: u128 = 1000; } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs index 7def637a66e4..0856c9526009 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs @@ -113,17 +113,26 @@ fn send_assets_from_penpal_westend_through_westend_ah_to_rococo_ah( } #[test] -/// Test transfer of WND from AssetHub Westend to AssetHub Rococo. -fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() { +/// Test transfer of WND, USDT and wETH from AssetHub Westend to AssetHub Rococo. +/// +/// This mix of assets should cover the whole range: +/// - native assets: WND, +/// - trust-based assets: USDT (exists only on Westend, Rococo gets it from Westend over bridge), +/// - foreign asset / bridged asset (other bridge / Snowfork): wETH (bridged from Ethereum to +/// Westend over Snowbridge, then bridged over to Rococo through this bridge). +fn send_wnds_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { let amount = ASSET_HUB_WESTEND_ED * 1_000; let sender = AssetHubWestendSender::get(); let receiver = AssetHubRococoReceiver::get(); let wnd_at_asset_hub_westend = wnd_at_ah_westend(); let bridged_wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo(); - create_foreign_on_ah_rococo(bridged_wnd_at_asset_hub_rococo.clone(), true); + create_foreign_on_ah_rococo(bridged_wnd_at_asset_hub_rococo.clone(), true); set_up_pool_with_roc_on_ah_rococo(bridged_wnd_at_asset_hub_rococo.clone(), true); + //////////////////////////////////////////////////////////// + // Let's first send over just some WNDs as a simple example + //////////////////////////////////////////////////////////// let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( Rococo, AssetHubRococo::para_id(), @@ -161,7 +170,7 @@ fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() { ); }); - let sender_wnds_after = ::account_data_of(sender).free; + let sender_wnds_after = ::account_data_of(sender.clone()).free; let receiver_wnds_after = foreign_balance_on_ah_rococo(bridged_wnd_at_asset_hub_rococo, &receiver); let wnds_in_reserve_on_ahw_after = @@ -173,18 +182,83 @@ fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() { assert!(receiver_wnds_after > receiver_wnds_before); // Reserve balance is increased by sent amount assert_eq!(wnds_in_reserve_on_ahw_after, wnds_in_reserve_on_ahw_before + amount); + + ///////////////////////////////////////////////////////////// + // Now let's send over USDTs + wETH (and pay fees with USDT) + ///////////////////////////////////////////////////////////// + let usdt_at_asset_hub_westend = usdt_at_ah_westend(); + let bridged_usdt_at_asset_hub_rococo = bridged_usdt_at_ah_rococo(); + // wETH has same relative location on both Westend and Rococo AssetHubs + let bridged_weth_at_ah = weth_at_asset_hubs(); + + // mint USDT in sender's account (USDT already created in genesis) + AssetHubWestend::mint_asset( + ::RuntimeOrigin::signed(AssetHubWestendAssetOwner::get()), + USDT_ID, + sender.clone(), + amount * 2, + ); + // create wETH at src and dest and prefund sender's account + create_foreign_on_ah_westend( + bridged_weth_at_ah.clone(), + true, + vec![(sender.clone(), amount * 2)], + ); + create_foreign_on_ah_rococo(bridged_weth_at_ah.clone(), true); + create_foreign_on_ah_rococo(bridged_usdt_at_asset_hub_rococo.clone(), true); + set_up_pool_with_roc_on_ah_rococo(bridged_usdt_at_asset_hub_rococo.clone(), true); + + let receiver_usdts_before = + foreign_balance_on_ah_rococo(bridged_usdt_at_asset_hub_rococo.clone(), &receiver); + let receiver_weth_before = foreign_balance_on_ah_rococo(bridged_weth_at_ah.clone(), &receiver); + + // send USDTs and wETHs + let assets: Assets = vec![ + (usdt_at_asset_hub_westend.clone(), amount).into(), + (Location::try_from(bridged_weth_at_ah.clone()).unwrap(), amount).into(), + ] + .into(); + // use USDT for fees + let fee: AssetId = usdt_at_asset_hub_westend.into(); + + // use the more involved transfer extrinsic + let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(AllCounted(assets.len() as u32)), + beneficiary: AccountId32Junction { network: None, id: receiver.clone().into() }.into(), + }]); + assert_ok!(AssetHubWestend::execute_with(|| { + ::PolkadotXcm::transfer_assets_using_type_and_then( + ::RuntimeOrigin::signed(sender.into()), + bx!(asset_hub_rococo_location().into()), + bx!(assets.into()), + bx!(TransferType::LocalReserve), + bx!(fee.into()), + bx!(TransferType::LocalReserve), + bx!(VersionedXcm::from(custom_xcm_on_dest)), + WeightLimit::Unlimited, + ) + })); + // verify hops (also advances the message through the hops) + assert_bridge_hub_westend_message_accepted(true); + assert_bridge_hub_rococo_message_received(); + AssetHubRococo::execute_with(|| { + AssetHubRococo::assert_xcmp_queue_success(None); + }); + + let receiver_usdts_after = + foreign_balance_on_ah_rococo(bridged_usdt_at_asset_hub_rococo, &receiver); + let receiver_weth_after = foreign_balance_on_ah_rococo(bridged_weth_at_ah, &receiver); + + // Receiver's USDT balance is increased by almost `amount` (minus fees) + assert!(receiver_usdts_after > receiver_usdts_before); + assert!(receiver_usdts_after < receiver_usdts_before + amount); + // Receiver's wETH balance is increased by sent amount + assert_eq!(receiver_weth_after, receiver_weth_before + amount); } #[test] -/// Send bridged assets "back" from AssetHub Rococo to AssetHub Westend. -/// -/// This mix of assets should cover the whole range: -/// - bridged native assets: ROC, -/// - bridged trust-based assets: USDT (exists only on Rococo, Westend gets it from Rococo over -/// bridge), -/// - bridged foreign asset / double-bridged asset (other bridge / Snowfork): wETH (bridged from -/// Ethereum to Rococo over Snowbridge, then bridged over to Westend through this bridge). -fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { +/// Send bridged ROCs "back" from AssetHub Westend to AssetHub Rococo. +fn send_back_rocs_from_asset_hub_westend_to_asset_hub_rococo() { let prefund_amount = 10_000_000_000_000u128; let amount_to_send = ASSET_HUB_ROCOCO_ED * 1_000; let sender = AssetHubWestendSender::get(); @@ -193,10 +267,6 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { let prefund_accounts = vec![(sender.clone(), prefund_amount)]; create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), true, prefund_accounts); - //////////////////////////////////////////////////////////// - // Let's first send back just some ROCs as a simple example - //////////////////////////////////////////////////////////// - // fund the AHW's SA on AHR with the ROC tokens held in reserve let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( Westend, @@ -257,96 +327,6 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { assert!(receiver_rocs_after > receiver_rocs_before); // Reserve balance is reduced by sent amount assert_eq!(rocs_in_reserve_on_ahr_after, rocs_in_reserve_on_ahr_before - amount_to_send); - - ////////////////////////////////////////////////////////////////// - // Now let's send back over USDTs + wETH (and pay fees with USDT) - ////////////////////////////////////////////////////////////////// - - // wETH has same relative location on both Rococo and Westend AssetHubs - let bridged_weth_at_ah = weth_at_asset_hubs(); - let bridged_usdt_at_asset_hub_westend = bridged_usdt_at_ah_westend(); - - // set up destination chain AH Rococo: - // create a ROC/USDT pool to be able to pay fees with USDT (USDT created in genesis) - set_up_pool_with_roc_on_ah_rococo(usdt_at_ah_rococo(), false); - // create wETH on Rococo (IRL it's already created by Snowbridge) - create_foreign_on_ah_rococo(bridged_weth_at_ah.clone(), true); - // prefund AHW's sovereign account on AHR to be able to withdraw USDT and wETH from reserves - let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( - Westend, - AssetHubWestend::para_id(), - ); - AssetHubRococo::mint_asset( - ::RuntimeOrigin::signed(AssetHubRococoAssetOwner::get()), - USDT_ID, - sov_ahw_on_ahr.clone(), - amount_to_send * 2, - ); - AssetHubRococo::mint_foreign_asset( - ::RuntimeOrigin::signed(AssetHubRococo::account_id_of(ALICE)), - bridged_weth_at_ah.clone(), - sov_ahw_on_ahr, - amount_to_send * 2, - ); - - // set up source chain AH Westend: - // create wETH and USDT foreign assets on Westend and prefund sender's account - let prefund_accounts = vec![(sender.clone(), amount_to_send * 2)]; - create_foreign_on_ah_westend(bridged_weth_at_ah.clone(), true, prefund_accounts.clone()); - create_foreign_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone(), true, prefund_accounts); - - // check balances before - let receiver_usdts_before = AssetHubRococo::execute_with(|| { - type Assets = ::Assets; - >::balance(USDT_ID, &receiver) - }); - let receiver_weth_before = foreign_balance_on_ah_rococo(bridged_weth_at_ah.clone(), &receiver); - - let usdt_id: AssetId = Location::try_from(bridged_usdt_at_asset_hub_westend).unwrap().into(); - // send USDTs and wETHs - let assets: Assets = vec![ - (usdt_id.clone(), amount_to_send).into(), - (Location::try_from(bridged_weth_at_ah.clone()).unwrap(), amount_to_send).into(), - ] - .into(); - // use USDT for fees - let fee = usdt_id; - - // use the more involved transfer extrinsic - let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(AllCounted(assets.len() as u32)), - beneficiary: AccountId32Junction { network: None, id: receiver.clone().into() }.into(), - }]); - assert_ok!(AssetHubWestend::execute_with(|| { - ::PolkadotXcm::transfer_assets_using_type_and_then( - ::RuntimeOrigin::signed(sender.into()), - bx!(asset_hub_rococo_location().into()), - bx!(assets.into()), - bx!(TransferType::DestinationReserve), - bx!(fee.into()), - bx!(TransferType::DestinationReserve), - bx!(VersionedXcm::from(custom_xcm_on_dest)), - WeightLimit::Unlimited, - ) - })); - // verify hops (also advances the message through the hops) - assert_bridge_hub_westend_message_accepted(true); - assert_bridge_hub_rococo_message_received(); - AssetHubRococo::execute_with(|| { - AssetHubRococo::assert_xcmp_queue_success(None); - }); - - let receiver_usdts_after = AssetHubRococo::execute_with(|| { - type Assets = ::Assets; - >::balance(USDT_ID, &receiver) - }); - let receiver_weth_after = foreign_balance_on_ah_rococo(bridged_weth_at_ah, &receiver); - - // Receiver's USDT balance is increased by almost `amount_to_send` (minus fees) - assert!(receiver_usdts_after > receiver_usdts_before); - assert!(receiver_usdts_after < receiver_usdts_before + amount_to_send); - // Receiver's wETH balance is increased by `amount_to_send` - assert_eq!(receiver_weth_after, receiver_weth_before + amount_to_send); } #[test] diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs index f037a05a8276..af11f0f7ba72 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs @@ -17,6 +17,7 @@ use crate::imports::*; mod asset_transfers; mod claim_assets; +mod register_bridged_assets; mod send_xcm; mod teleport; @@ -47,15 +48,15 @@ pub(crate) fn bridged_roc_at_ah_westend() -> Location { } // USDT and wUSDT -pub(crate) fn usdt_at_ah_rococo() -> Location { +pub(crate) fn usdt_at_ah_westend() -> Location { Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]) } -pub(crate) fn bridged_usdt_at_ah_westend() -> Location { +pub(crate) fn bridged_usdt_at_ah_rococo() -> Location { Location::new( 2, [ - GlobalConsensus(Rococo), - Parachain(AssetHubRococo::para_id().into()), + GlobalConsensus(Westend), + Parachain(AssetHubWestend::para_id().into()), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into()), ], diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/register_bridged_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/register_bridged_assets.rs new file mode 100644 index 000000000000..7a7ad6da2d55 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/register_bridged_assets.rs @@ -0,0 +1,131 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + imports::*, + tests::{ + snowbridge::{CHAIN_ID, WETH}, + *, + }, +}; + +const XCM_FEE: u128 = 40_000_000_000; + +/// Tests the registering of a Westend Asset as a bridged asset on Rococo Asset Hub. +#[test] +fn register_westend_asset_on_rah_from_wah() { + // Westend Asset Hub asset when bridged to Rococo Asset Hub. + let bridged_asset_at_rah = Location::new( + 2, + [ + GlobalConsensus(Westend), + Parachain(AssetHubWestend::para_id().into()), + PalletInstance(ASSETS_PALLET_ID), + GeneralIndex(ASSET_ID.into()), + ], + ); + // Register above asset on Rococo AH from Westend AH. + register_asset_on_rah_from_wah(bridged_asset_at_rah); +} + +/// Tests the registering of an Ethereum Asset as a bridged asset on Rococo Asset Hub. +#[test] +fn register_ethereum_asset_on_rah_from_wah() { + // Ethereum asset when bridged to Rococo Asset Hub. + let bridged_asset_at_rah = Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: CHAIN_ID }), + AccountKey20 { network: None, key: WETH }, + ], + ); + // Register above asset on Rococo AH from Westend AH. + register_asset_on_rah_from_wah(bridged_asset_at_rah); +} + +fn register_asset_on_rah_from_wah(bridged_asset_at_rah: Location) { + let sa_of_wah_on_rah = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( + Westend, + AssetHubWestend::para_id(), + ); + + // Encoded `create_asset` call to be executed in Rococo Asset Hub ForeignAssets pallet. + let call = AssetHubRococo::create_foreign_asset_call( + bridged_asset_at_rah.clone(), + ASSET_MIN_BALANCE, + sa_of_wah_on_rah.clone(), + ); + + let origin_kind = OriginKind::Xcm; + let fee_amount = XCM_FEE; + let fees = (Parent, fee_amount).into(); + + let xcm = xcm_transact_paid_execution(call, origin_kind, fees, sa_of_wah_on_rah.clone()); + + // SA-of-WAH-on-RAH needs to have balance to pay for fees and asset creation deposit + AssetHubRococo::fund_accounts(vec![( + sa_of_wah_on_rah.clone(), + ASSET_HUB_ROCOCO_ED * 10000000000, + )]); + + let destination = asset_hub_rococo_location(); + + // fund the WAH's SA on WBH for paying bridge transport fees + BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), 10_000_000_000_000u128); + + // set XCM versions + AssetHubWestend::force_xcm_version(destination.clone(), XCM_VERSION); + BridgeHubWestend::force_xcm_version(bridge_hub_rococo_location(), XCM_VERSION); + + let root_origin = ::RuntimeOrigin::root(); + AssetHubWestend::execute_with(|| { + assert_ok!(::PolkadotXcm::send( + root_origin, + bx!(destination.into()), + bx!(xcm), + )); + + AssetHubWestend::assert_xcm_pallet_sent(); + }); + + assert_bridge_hub_westend_message_accepted(true); + assert_bridge_hub_rococo_message_received(); + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + AssetHubRococo::assert_xcmp_queue_success(None); + assert_expected_events!( + AssetHubRococo, + vec![ + // Burned the fee + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { + who: *who == sa_of_wah_on_rah.clone(), + amount: *amount == fee_amount, + }, + // Foreign Asset created + RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { asset_id, creator, owner }) => { + asset_id: asset_id == &bridged_asset_at_rah, + creator: *creator == sa_of_wah_on_rah.clone(), + owner: *owner == sa_of_wah_on_rah, + }, + // Unspent fee minted to origin + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { + who: *who == sa_of_wah_on_rah.clone(), + }, + ] + ); + type ForeignAssets = ::ForeignAssets; + assert!(ForeignAssets::asset_exists(bridged_asset_at_rah)); + }); +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 96dbc9c0a186..701f6a7959af 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -419,6 +419,7 @@ impl pallet_assets::Config for Runtime { ( FromSiblingParachain, xcm::v4::Location>, FromNetwork, + xcm_config::bridging::to_westend::WestendOrEthereumAssetFromAssetHubWestend, ), ForeignCreatorsSovereignAccountOf, AccountId, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index fab0141e6d41..32fbfb6d0199 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -361,8 +361,8 @@ impl xcm_executor::Config for XcmConfig { // to the Westend ecosystem. We also allow Ethereum contracts to act as reserves for the foreign // assets identified by the same respective contracts locations. type IsReserve = ( - bridging::to_westend::WestendAssetFromAssetHubWestend, - bridging::to_ethereum::IsTrustedBridgedReserveLocationForForeignAsset, + bridging::to_westend::WestendOrEthereumAssetFromAssetHubWestend, + bridging::to_ethereum::EthereumAssetFromEthereum, ); type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; @@ -517,6 +517,7 @@ pub type ForeignCreatorsSovereignAccountOf = ( AccountId32Aliases, ParentIsPreset, GlobalConsensusEthereumConvertsFor, + GlobalConsensusParachainConvertsFor, ); /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. @@ -589,7 +590,9 @@ pub mod bridging { ); pub const WestendNetwork: NetworkId = NetworkId::Westend; + pub const EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; pub WestendEcosystem: Location = Location::new(2, [GlobalConsensus(WestendNetwork::get())]); + pub EthereumEcosystem: Location = Location::new(2, [GlobalConsensus(EthereumNetwork::get())]); pub WndLocation: Location = Location::new(2, [GlobalConsensus(WestendNetwork::get())]); pub AssetHubWestend: Location = Location::new(2, [ GlobalConsensus(WestendNetwork::get()), @@ -627,9 +630,12 @@ pub mod bridging { } } - /// Allow any asset native to the Westend ecosystem if it comes from Westend Asset Hub. - pub type WestendAssetFromAssetHubWestend = - matching::RemoteAssetFromLocation, AssetHubWestend>; + /// Allow any asset native to the Westend or Ethereum ecosystems if it comes from Westend + /// Asset Hub. + pub type WestendOrEthereumAssetFromAssetHubWestend = matching::RemoteAssetFromLocation< + (StartsWith, StartsWith), + AssetHubWestend, + >; } pub mod to_ethereum { @@ -672,7 +678,7 @@ pub mod bridging { ); } - pub type IsTrustedBridgedReserveLocationForForeignAsset = + pub type EthereumAssetFromEthereum = IsForeignConcreteAsset>; impl Contains<(Location, Junction)> for UniversalAliases { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 19baa921ff23..5616c670294f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -417,6 +417,7 @@ impl pallet_assets::Config for Runtime { ( FromSiblingParachain, xcm::v4::Location>, FromNetwork, + xcm_config::bridging::to_rococo::RococoAssetFromAssetHubRococo, ), ForeignCreatorsSovereignAccountOf, AccountId, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index cc96bd59cb48..858ac7f5c639 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -384,8 +384,8 @@ impl xcm_executor::Config for XcmConfig { // held). On Westend Asset Hub, we allow Rococo Asset Hub to act as reserve for any asset native // to the Rococo or Ethereum ecosystems. type IsReserve = ( - bridging::to_rococo::RococoOrEthereumAssetFromAssetHubRococo, - bridging::to_ethereum::IsTrustedBridgedReserveLocationForForeignAsset, + bridging::to_rococo::RococoAssetFromAssetHubRococo, + bridging::to_ethereum::EthereumAssetFromEthereum, ); type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; @@ -541,6 +541,7 @@ pub type ForeignCreatorsSovereignAccountOf = ( AccountId32Aliases, ParentIsPreset, GlobalConsensusEthereumConvertsFor, + GlobalConsensusParachainConvertsFor, ); /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. @@ -605,10 +606,8 @@ pub mod bridging { ); pub const RococoNetwork: NetworkId = NetworkId::Rococo; - pub const EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; pub RococoEcosystem: Location = Location::new(2, [GlobalConsensus(RococoNetwork::get())]); pub RocLocation: Location = Location::new(2, [GlobalConsensus(RococoNetwork::get())]); - pub EthereumEcosystem: Location = Location::new(2, [GlobalConsensus(EthereumNetwork::get())]); pub AssetHubRococo: Location = Location::new(2, [ GlobalConsensus(RococoNetwork::get()), Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID) @@ -645,12 +644,9 @@ pub mod bridging { } } - /// Allow any asset native to the Rococo or Ethereum ecosystems if it comes from Rococo - /// Asset Hub. - pub type RococoOrEthereumAssetFromAssetHubRococo = matching::RemoteAssetFromLocation< - (StartsWith, StartsWith), - AssetHubRococo, - >; + /// Allow any asset native to the Rococo ecosystem if it comes from Rococo Asset Hub. + pub type RococoAssetFromAssetHubRococo = + matching::RemoteAssetFromLocation, AssetHubRococo>; } pub mod to_ethereum { @@ -704,7 +700,7 @@ pub mod bridging { pub type EthereumNetworkExportTable = xcm_builder::NetworkExportTable; - pub type IsTrustedBridgedReserveLocationForForeignAsset = + pub type EthereumAssetFromEthereum = IsForeignConcreteAsset>; impl Contains<(Location, Junction)> for UniversalAliases { diff --git a/cumulus/parachains/runtimes/assets/common/src/matching.rs b/cumulus/parachains/runtimes/assets/common/src/matching.rs index 9bb35d0c5328..9ac2056a67f4 100644 --- a/cumulus/parachains/runtimes/assets/common/src/matching.rs +++ b/cumulus/parachains/runtimes/assets/common/src/matching.rs @@ -102,19 +102,27 @@ impl< pub struct RemoteAssetFromLocation( core::marker::PhantomData<(AssetsAllowedNetworks, OriginLocation)>, ); -impl, OriginLocation: Get> - ContainsPair for RemoteAssetFromLocation +impl< + L: TryInto + Clone, + AssetsAllowedNetworks: Contains, + OriginLocation: Get, + > ContainsPair for RemoteAssetFromLocation { - fn contains(asset: &Asset, origin: &Location) -> bool { + fn contains(asset: &L, origin: &L) -> bool { + let Ok(asset) = asset.clone().try_into() else { + return false; + }; + let Ok(origin) = origin.clone().try_into() else { + return false; + }; let expected_origin = OriginLocation::get(); // ensure `origin` is expected `OriginLocation` - if !expected_origin.eq(origin) { + if !expected_origin.eq(&origin) { log::trace!( target: "xcm::contains", - "RemoteAssetFromLocation asset: {:?}, origin: {:?} is not from expected {:?}", - asset, origin, expected_origin, + "RemoteAssetFromLocation asset: {asset:?}, origin: {origin:?} is not from expected {expected_origin:?}" ); - return false + return false; } else { log::trace!( target: "xcm::contains", @@ -123,7 +131,14 @@ impl, OriginLocation: Get> } // ensure `asset` is from remote consensus listed in `AssetsAllowedNetworks` - AssetsAllowedNetworks::contains(&asset.id.0) + AssetsAllowedNetworks::contains(&asset) + } +} +impl, OriginLocation: Get> + ContainsPair for RemoteAssetFromLocation +{ + fn contains(asset: &Asset, origin: &Location) -> bool { + >::contains(&asset.id.0, origin) } } diff --git a/prdoc/pr_5435.prdoc b/prdoc/pr_5435.prdoc new file mode 100644 index 000000000000..d3621e385bcd --- /dev/null +++ b/prdoc/pr_5435.prdoc @@ -0,0 +1,16 @@ +title: "Support registering assets on Asset Hubs over bridge" + +doc: + - audience: Runtime User + description: | + Allows one Asset Hub on one side, to register assets on the other Asset Hub over the bridge. + Rococo <> Ethereum test bridge will be dropped in favor of Westend <> Ethereum test bridge. + This PR also changes emulated tests to simulate double bridging from Ethereum<>Westend<>Rococo. + +crates: + - name: assets-common + bump: patch + - name: asset-hub-rococo-runtime + bump: patch + - name: asset-hub-westend-runtime + bump: patch