Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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,
Expand Down Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,26 +113,17 @@ 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();
let roc_at_asset_hub_rococo = roc_at_ah_rococo();
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(),
Expand All @@ -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));
});
Expand Down Expand Up @@ -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(
<AssetHubRococo as Chain>::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(|| {
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
<AssetHubRococo as Chain>::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();
Expand All @@ -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,
Expand Down Expand Up @@ -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 = <AssetHubWestend as Chain>::account_data_of(receiver).free;
let receiver_wnds_after = <AssetHubWestend as Chain>::account_data_of(receiver.clone()).free;
let wnds_in_reserve_on_ahw_after =
<AssetHubWestend as Chain>::account_data_of(sov_ahr_on_ahw).free;

Expand All @@ -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(
<AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendAssetOwner::get()),
USDT_ID,
sov_ahr_on_ahw.clone(),
amount_to_send * 2,
);
AssetHubWestend::mint_foreign_asset(
<AssetHubWestend as Chain>::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 = <AssetHubWestend as AssetHubWestendPallet>::Assets;
<Assets as Inspect<_>>::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(|| {
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
<AssetHubRococo as Chain>::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 = <AssetHubWestend as AssetHubWestendPallet>::Assets;
<Assets as Inspect<_>>::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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::imports::*;

mod asset_transfers;
mod claim_assets;
mod register_bridged_assets;
mod send_xcm;
mod snowbridge;
mod teleport;
Expand Down Expand Up @@ -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()),
],
Expand Down Expand Up @@ -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 = <AssetHubWestend as Chain>::RuntimeEvent;
let owner = AssetHubWestendSender::get();
let signed_owner = <AssetHubWestend as Chain>::RuntimeOrigin::signed(owner.clone());

assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::ForeignAssets::mint(
signed_owner.clone(),
foreign_asset.clone().into(),
owner.clone().into(),
3_000_000_000_000,
));
if is_foreign {
assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::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!(<AssetHubWestend as AssetHubWestendPallet>::Assets::mint(
signed_owner.clone(),
asset_id.into(),
owner.clone().into(),
3_000_000_000_000,
));
}
assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::AssetConversion::create_pool(
signed_owner.clone(),
Box::new(wnd.clone()),
Box::new(foreign_asset.clone()),
Box::new(asset.clone()),
));
assert_expected_events!(
AssetHubWestend,
Expand All @@ -127,7 +141,7 @@ pub(crate) fn set_up_pool_with_wnd_on_ah_westend(foreign_asset: v4::Location) {
assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::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,
Expand All @@ -149,7 +163,7 @@ pub(crate) fn send_assets_from_asset_hub_rococo(
fee_idx: u32,
) -> DispatchResult {
let signed_origin =
<AssetHubRococo as Chain>::RuntimeOrigin::signed(AssetHubRococoSender::get().into());
<AssetHubRococo as Chain>::RuntimeOrigin::signed(AssetHubRococoSender::get());
let beneficiary: Location =
AccountId32Junction { network: None, id: AssetHubWestendReceiver::get().into() }.into();

Expand Down
Loading