Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AHs] Support registering assets on Asset Hubs over bridge #5435

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading