......@@ -70,7 +70,9 @@ mod imports {
LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub,
LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub,
};
pub use rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig;
pub use rococo_runtime::xcm_config::{
UniversalLocation as RococoUniversalLocation, XcmConfig as RococoXcmConfig,
};
pub const ASSET_ID: u32 = 3;
pub const ASSET_MIN_BALANCE: u128 = 1000;
......@@ -83,6 +85,7 @@ mod imports {
pub type ParaToSystemParaTest = Test<PenpalA, AssetHubRococo>;
pub type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Rococo>;
pub type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubRococo>;
pub type RelayToParaThroughAHTest = Test<Rococo, PenpalA, AssetHubRococo>;
}
#[cfg(test)]
......
......@@ -54,14 +54,18 @@ fn para_to_para_assethub_hop_assertions(t: ParaToParaThroughAHTest) {
fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::transfer_assets_using_type(
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
bx!(TransferType::LocalReserve),
bx!(fee.id.into()),
bx!(TransferType::LocalReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
......@@ -69,14 +73,18 @@ fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
fn para_to_ah_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type(
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
bx!(TransferType::DestinationReserve),
bx!(fee.id.into()),
bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
......@@ -85,14 +93,18 @@ fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> Dispat
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubRococo::para_id());
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type(
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())),
bx!(fee.id.into()),
bx!(TransferType::RemoteReserve(asset_hub_location.into())),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
......@@ -100,14 +112,18 @@ fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> Dispat
fn para_to_asset_hub_teleport_foreign_assets(t: ParaToSystemParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type(
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
bx!(TransferType::Teleport),
bx!(fee.id.into()),
bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
......@@ -115,14 +131,18 @@ fn para_to_asset_hub_teleport_foreign_assets(t: ParaToSystemParaTest) -> Dispatc
fn asset_hub_to_para_teleport_foreign_assets(t: SystemParaToParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::transfer_assets_using_type(
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
bx!(TransferType::Teleport),
bx!(fee.id.into()),
bx!(TransferType::LocalReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
......@@ -626,3 +646,166 @@ fn bidirectional_teleport_foreign_asset_between_para_and_asset_hub_using_explici
asset_hub_to_para_teleport_foreign_assets,
);
}
// ===============================================================
// ===== Transfer - Native Asset - Relay->AssetHub->Parachain ====
// ===============================================================
/// Transfers of native asset Relay to Parachain (using AssetHub reserve). Parachains want to avoid
/// managing SAs on all system chains, thus want all their DOT-in-reserve to be held in their
/// Sovereign Account on Asset Hub.
#[test]
fn transfer_native_asset_from_relay_to_para_through_asset_hub() {
// Init values for Relay
let destination = Rococo::child_location_of(PenpalA::para_id());
let sender = RococoSender::get();
let amount_to_send: Balance = ROCOCO_ED * 1000;
// Init values for Parachain
let relay_native_asset_location = RelayLocation::get();
let receiver = PenpalAReceiver::get();
// Init Test
let test_args = TestContext {
sender,
receiver: receiver.clone(),
args: TestArgs::new_relay(destination.clone(), receiver.clone(), amount_to_send),
};
let mut test = RelayToParaThroughAHTest::new(test_args);
let sov_penpal_on_ah = AssetHubRococo::sovereign_account_id_of(
AssetHubRococo::sibling_location_of(PenpalA::para_id()),
);
// Query initial balances
let sender_balance_before = test.sender.balance;
let sov_penpal_on_ah_before = AssetHubRococo::execute_with(|| {
<AssetHubRococo as AssetHubRococoPallet>::Balances::free_balance(sov_penpal_on_ah.clone())
});
let receiver_assets_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &receiver)
});
fn relay_assertions(t: RelayToParaThroughAHTest) {
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
Rococo::assert_xcm_pallet_attempted_complete(None);
assert_expected_events!(
Rococo,
vec![
// Amount to teleport is withdrawn from Sender
RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => {
who: *who == t.sender.account_id,
amount: *amount == t.args.amount,
},
// Amount to teleport is deposited in Relay's `CheckAccount`
RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => {
who: *who == <Rococo as RococoPallet>::XcmPallet::check_account(),
amount: *amount == t.args.amount,
},
]
);
}
fn asset_hub_assertions(_: RelayToParaThroughAHTest) {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
let sov_penpal_on_ah = AssetHubRococo::sovereign_account_id_of(
AssetHubRococo::sibling_location_of(PenpalA::para_id()),
);
assert_expected_events!(
AssetHubRococo,
vec![
// Deposited to receiver parachain SA
RuntimeEvent::Balances(
pallet_balances::Event::Minted { who, .. }
) => {
who: *who == sov_penpal_on_ah,
},
RuntimeEvent::MessageQueue(
pallet_message_queue::Event::Processed { success: true, .. }
) => {},
]
);
}
fn penpal_assertions(t: RelayToParaThroughAHTest) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
let expected_id =
t.args.assets.into_inner().first().unwrap().id.0.clone().try_into().unwrap();
assert_expected_events!(
PenpalA,
vec![
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => {
asset_id: *asset_id == expected_id,
owner: *owner == t.receiver.account_id,
},
]
);
}
fn transfer_assets_dispatchable(t: RelayToParaThroughAHTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
let asset_hub_location = Rococo::child_location_of(AssetHubRococo::para_id());
let context = RococoUniversalLocation::get();
// reanchor fees to the view of destination (Penpal)
let mut remote_fees = fee.clone().reanchored(&t.args.dest, &context).unwrap();
if let Fungible(ref mut amount) = remote_fees.fun {
// we already spent some fees along the way, just use half of what we started with
*amount = *amount / 2;
}
let xcm_on_final_dest = Xcm::<()>(vec![
BuyExecution { fees: remote_fees, weight_limit: t.args.weight_limit.clone() },
DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
},
]);
// reanchor final dest (Penpal) to the view of hop (Asset Hub)
let mut dest = t.args.dest.clone();
dest.reanchor(&asset_hub_location, &context).unwrap();
// on Asset Hub, forward assets to Penpal
let xcm_on_hop = Xcm::<()>(vec![DepositReserveAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
dest,
xcm: xcm_on_final_dest,
}]);
// First leg is a teleport, from there a local-reserve-transfer to final dest
<Rococo as RococoPallet>::XcmPallet::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(asset_hub_location.into()),
bx!(t.args.assets.into()),
bx!(TransferType::Teleport),
bx!(fee.id.into()),
bx!(TransferType::Teleport),
bx!(VersionedXcm::from(xcm_on_hop)),
t.args.weight_limit,
)
}
// Set assertions and dispatchables
test.set_assertion::<Rococo>(relay_assertions);
test.set_assertion::<AssetHubRococo>(asset_hub_assertions);
test.set_assertion::<PenpalA>(penpal_assertions);
test.set_dispatchable::<Rococo>(transfer_assets_dispatchable);
test.assert();
// Query final balances
let sender_balance_after = test.sender.balance;
let sov_penpal_on_ah_after = AssetHubRococo::execute_with(|| {
<AssetHubRococo as AssetHubRococoPallet>::Balances::free_balance(sov_penpal_on_ah)
});
let receiver_assets_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &receiver)
});
// Sender's balance is reduced by amount sent plus delivery fees
assert!(sender_balance_after < sender_balance_before - amount_to_send);
// SA on AH balance is increased
assert!(sov_penpal_on_ah_after > sov_penpal_on_ah_before);
// Receiver's asset balance is increased
assert!(receiver_assets_after > receiver_assets_before);
// Receiver's asset balance increased by `amount_to_send - delivery_fees - bought_execution`;
// `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but
// should be non-zero
assert!(receiver_assets_after < receiver_assets_before + amount_to_send);
}
......@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
mod foreign_assets_transfers;
mod hybrid_transfers;
mod reserve_transfer;
mod send;
mod set_xcm_versions;
......
......@@ -574,7 +574,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() {
let sender = RococoSender::get();
let amount_to_send: Balance = ROCOCO_ED * 1000;
// Init values fot Parachain
// Init values for Parachain
let relay_native_asset_location = RelayLocation::get();
let receiver = PenpalAReceiver::get();
......
......@@ -75,10 +75,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() {
)]);
PenpalA::execute_with(|| {
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob(
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
root_origin,
bx!(system_para_destination),
xcm.encode().try_into().unwrap(),
bx!(xcm),
));
PenpalA::assert_xcm_pallet_sent();
......@@ -159,10 +159,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() {
)]);
PenpalA::execute_with(|| {
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob(
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
root_origin,
bx!(system_para_destination),
xcm.encode().try_into().unwrap(),
bx!(xcm),
));
PenpalA::assert_xcm_pallet_sent();
......
......@@ -372,10 +372,10 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() {
penpal.clone(),
);
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob(
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
penpal_root,
bx!(asset_hub_location),
xcm.encode().try_into().unwrap(),
bx!(xcm),
));
PenpalA::assert_xcm_pallet_sent();
......
......@@ -74,7 +74,9 @@ mod imports {
LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub,
LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub,
};
pub use westend_runtime::xcm_config::XcmConfig as WestendXcmConfig;
pub use westend_runtime::xcm_config::{
UniversalLocation as WestendUniversalLocation, XcmConfig as WestendXcmConfig,
};
pub const ASSET_ID: u32 = 3;
pub const ASSET_MIN_BALANCE: u128 = 1000;
......@@ -87,6 +89,7 @@ mod imports {
pub type ParaToSystemParaTest = Test<PenpalA, AssetHubWestend>;
pub type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Westend>;
pub type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubWestend>;
pub type RelayToParaThroughAHTest = Test<Westend, PenpalA, AssetHubWestend>;
}
#[cfg(test)]
......
......@@ -54,14 +54,18 @@ fn para_to_para_assethub_hop_assertions(t: ParaToParaThroughAHTest) {
fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::transfer_assets_using_type(
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
bx!(TransferType::LocalReserve),
bx!(fee.id.into()),
bx!(TransferType::LocalReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
......@@ -69,14 +73,18 @@ fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
fn para_to_ah_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type(
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
bx!(TransferType::DestinationReserve),
bx!(fee.id.into()),
bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
......@@ -85,14 +93,18 @@ fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> Dispat
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubWestend::para_id());
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type(
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())),
bx!(fee.id.into()),
bx!(TransferType::RemoteReserve(asset_hub_location.into())),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
......@@ -100,14 +112,18 @@ fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> Dispat
fn para_to_asset_hub_teleport_foreign_assets(t: ParaToSystemParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type(
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
bx!(TransferType::Teleport),
bx!(fee.id.into()),
bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
......@@ -115,14 +131,18 @@ fn para_to_asset_hub_teleport_foreign_assets(t: ParaToSystemParaTest) -> Dispatc
fn asset_hub_to_para_teleport_foreign_assets(t: SystemParaToParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::transfer_assets_using_type(
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
bx!(TransferType::Teleport),
bx!(fee.id.into()),
bx!(TransferType::LocalReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
......@@ -627,3 +647,166 @@ fn bidirectional_teleport_foreign_asset_between_para_and_asset_hub_using_explici
asset_hub_to_para_teleport_foreign_assets,
);
}
// ===============================================================
// ===== Transfer - Native Asset - Relay->AssetHub->Parachain ====
// ===============================================================
/// Transfers of native asset Relay to Parachain (using AssetHub reserve). Parachains want to avoid
/// managing SAs on all system chains, thus want all their DOT-in-reserve to be held in their
/// Sovereign Account on Asset Hub.
#[test]
fn transfer_native_asset_from_relay_to_para_through_asset_hub() {
// Init values for Relay
let destination = Westend::child_location_of(PenpalA::para_id());
let sender = WestendSender::get();
let amount_to_send: Balance = WESTEND_ED * 1000;
// Init values for Parachain
let relay_native_asset_location = RelayLocation::get();
let receiver = PenpalAReceiver::get();
// Init Test
let test_args = TestContext {
sender,
receiver: receiver.clone(),
args: TestArgs::new_relay(destination.clone(), receiver.clone(), amount_to_send),
};
let mut test = RelayToParaThroughAHTest::new(test_args);
let sov_penpal_on_ah = AssetHubWestend::sovereign_account_id_of(
AssetHubWestend::sibling_location_of(PenpalA::para_id()),
);
// Query initial balances
let sender_balance_before = test.sender.balance;
let sov_penpal_on_ah_before = AssetHubWestend::execute_with(|| {
<AssetHubWestend as AssetHubWestendPallet>::Balances::free_balance(sov_penpal_on_ah.clone())
});
let receiver_assets_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &receiver)
});
fn relay_assertions(t: RelayToParaThroughAHTest) {
type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
Westend::assert_xcm_pallet_attempted_complete(None);
assert_expected_events!(
Westend,
vec![
// Amount to teleport is withdrawn from Sender
RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => {
who: *who == t.sender.account_id,
amount: *amount == t.args.amount,
},
// Amount to teleport is deposited in Relay's `CheckAccount`
RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => {
who: *who == <Westend as WestendPallet>::XcmPallet::check_account(),
amount: *amount == t.args.amount,
},
]
);
}
fn asset_hub_assertions(_: RelayToParaThroughAHTest) {
type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
let sov_penpal_on_ah = AssetHubWestend::sovereign_account_id_of(
AssetHubWestend::sibling_location_of(PenpalA::para_id()),
);
assert_expected_events!(
AssetHubWestend,
vec![
// Deposited to receiver parachain SA
RuntimeEvent::Balances(
pallet_balances::Event::Minted { who, .. }
) => {
who: *who == sov_penpal_on_ah,
},
RuntimeEvent::MessageQueue(
pallet_message_queue::Event::Processed { success: true, .. }
) => {},
]
);
}
fn penpal_assertions(t: RelayToParaThroughAHTest) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
let expected_id =
t.args.assets.into_inner().first().unwrap().id.0.clone().try_into().unwrap();
assert_expected_events!(
PenpalA,
vec![
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => {
asset_id: *asset_id == expected_id,
owner: *owner == t.receiver.account_id,
},
]
);
}
fn transfer_assets_dispatchable(t: RelayToParaThroughAHTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
let asset_hub_location = Westend::child_location_of(AssetHubWestend::para_id());
let context = WestendUniversalLocation::get();
// reanchor fees to the view of destination (Penpal)
let mut remote_fees = fee.clone().reanchored(&t.args.dest, &context).unwrap();
if let Fungible(ref mut amount) = remote_fees.fun {
// we already spent some fees along the way, just use half of what we started with
*amount = *amount / 2;
}
let xcm_on_final_dest = Xcm::<()>(vec![
BuyExecution { fees: remote_fees, weight_limit: t.args.weight_limit.clone() },
DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
},
]);
// reanchor final dest (Penpal) to the view of hop (Asset Hub)
let mut dest = t.args.dest.clone();
dest.reanchor(&asset_hub_location, &context).unwrap();
// on Asset Hub, forward assets to Penpal
let xcm_on_hop = Xcm::<()>(vec![DepositReserveAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
dest,
xcm: xcm_on_final_dest,
}]);
// First leg is a teleport, from there a local-reserve-transfer to final dest
<Westend as WestendPallet>::XcmPallet::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(asset_hub_location.into()),
bx!(t.args.assets.into()),
bx!(TransferType::Teleport),
bx!(fee.id.into()),
bx!(TransferType::Teleport),
bx!(VersionedXcm::from(xcm_on_hop)),
t.args.weight_limit,
)
}
// Set assertions and dispatchables
test.set_assertion::<Westend>(relay_assertions);
test.set_assertion::<AssetHubWestend>(asset_hub_assertions);
test.set_assertion::<PenpalA>(penpal_assertions);
test.set_dispatchable::<Westend>(transfer_assets_dispatchable);
test.assert();
// Query final balances
let sender_balance_after = test.sender.balance;
let sov_penpal_on_ah_after = AssetHubWestend::execute_with(|| {
<AssetHubWestend as AssetHubWestendPallet>::Balances::free_balance(sov_penpal_on_ah)
});
let receiver_assets_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &receiver)
});
// Sender's balance is reduced by amount sent plus delivery fees
assert!(sender_balance_after < sender_balance_before - amount_to_send);
// SA on AH balance is increased
assert!(sov_penpal_on_ah_after > sov_penpal_on_ah_before);
// Receiver's asset balance is increased
assert!(receiver_assets_after > receiver_assets_before);
// Receiver's asset balance increased by `amount_to_send - delivery_fees - bought_execution`;
// `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but
// should be non-zero
assert!(receiver_assets_after < receiver_assets_before + amount_to_send);
}
......@@ -14,7 +14,7 @@
// limitations under the License.
mod fellowship_treasury;
mod foreign_assets_transfers;
mod hybrid_transfers;
mod reserve_transfer;
mod send;
mod set_xcm_versions;
......
......@@ -574,7 +574,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() {
let sender = WestendSender::get();
let amount_to_send: Balance = WESTEND_ED * 1000;
// Init values fot Parachain
// Init values for Parachain
let relay_native_asset_location = RelayLocation::get();
let receiver = PenpalAReceiver::get();
......
......@@ -75,10 +75,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() {
)]);
PenpalA::execute_with(|| {
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob(
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
root_origin,
bx!(system_para_destination),
xcm.encode().try_into().unwrap(),
bx!(xcm),
));
PenpalA::assert_xcm_pallet_sent();
......@@ -159,10 +159,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() {
)]);
PenpalA::execute_with(|| {
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob(
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
root_origin,
bx!(system_para_destination),
xcm.encode().try_into().unwrap(),
bx!(xcm),
));
PenpalA::assert_xcm_pallet_sent();
......
......@@ -371,10 +371,10 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() {
penpal.clone(),
);
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob(
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
penpal_root,
bx!(asset_hub_location),
xcm.encode().try_into().unwrap(),
bx!(xcm),
));
PenpalA::assert_xcm_pallet_sent();
......
......@@ -60,15 +60,19 @@ fn send_asset_from_penpal_rococo_through_local_asset_hub_to_westend_asset_hub(
AccountId32Junction { network: None, id: AssetHubWestendReceiver::get().into() }.into();
let assets: Assets = (id.clone(), transfer_amount).into();
let fees_id: AssetId = id.into();
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(assets.len() as u32)),
beneficiary,
}]);
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type(
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
signed_origin,
bx!(destination.into()),
bx!(beneficiary.into()),
bx!(assets.clone().into()),
bx!(TransferType::RemoteReserve(local_asset_hub.clone().into())),
bx!(fees_id.into()),
bx!(TransferType::RemoteReserve(local_asset_hub.into())),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
WeightLimit::Unlimited,
)
}));
......
......@@ -14,7 +14,6 @@
// limitations under the License.
use crate::tests::*;
use codec::Encode;
#[test]
fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable() {
......@@ -27,7 +26,7 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable
let remote_xcm = Xcm(vec![ClearOrigin]);
let xcm = VersionedXcm::from(Xcm::<()>(vec![
let xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit, check_origin },
ExportMessage {
network: WestendId.into(),
......@@ -39,10 +38,10 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable
// Rococo Global Consensus
// Send XCM message from Relay Chain to Bridge Hub source Parachain
Rococo::execute_with(|| {
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send_blob(
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
sudo_origin,
bx!(destination),
xcm.encode().try_into().unwrap(),
bx!(xcm),
));
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
......
......@@ -27,7 +27,7 @@ use snowbridge_pallet_inbound_queue_fixtures::{
};
use snowbridge_pallet_system;
use snowbridge_router_primitives::inbound::{
Command, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage,
Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage,
};
use sp_core::H256;
use sp_runtime::{DispatchError::Token, TokenError::FundsUnavailable};
......@@ -40,6 +40,7 @@ const TREASURY_ACCOUNT: [u8; 32] =
const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d");
const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e");
const INSUFFICIENT_XCM_FEE: u128 = 1000;
const XCM_FEE: u128 = 4_000_000_000;
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
pub enum ControlCall {
......@@ -82,7 +83,7 @@ fn create_agent() {
let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {});
// Construct XCM to create an agent for para 1001
let remote_xcm = VersionedXcm::from(Xcm::<()>(vec![
let remote_xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
DescendOrigin(Parachain(origin_para).into()),
Transact {
......@@ -95,10 +96,10 @@ fn create_agent() {
// Rococo Global Consensus
// Send XCM message from Relay Chain to Bridge Hub source Parachain
Rococo::execute_with(|| {
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send_blob(
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
sudo_origin,
bx!(destination),
remote_xcm.encode().try_into().unwrap(),
bx!(remote_xcm),
));
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
......@@ -140,7 +141,7 @@ fn create_channel() {
let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {});
// Construct XCM to create an agent for para 1001
let create_agent_xcm = VersionedXcm::from(Xcm::<()>(vec![
let create_agent_xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
DescendOrigin(Parachain(origin_para).into()),
Transact {
......@@ -153,7 +154,7 @@ fn create_channel() {
let create_channel_call =
SnowbridgeControl::Control(ControlCall::CreateChannel { mode: OperatingMode::Normal });
// Construct XCM to create a channel for para 1001
let create_channel_xcm = VersionedXcm::from(Xcm::<()>(vec![
let create_channel_xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
DescendOrigin(Parachain(origin_para).into()),
Transact {
......@@ -166,16 +167,16 @@ fn create_channel() {
// Rococo Global Consensus
// Send XCM message from Relay Chain to Bridge Hub source Parachain
Rococo::execute_with(|| {
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send_blob(
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
sudo_origin.clone(),
bx!(destination.clone()),
create_agent_xcm.encode().try_into().unwrap(),
bx!(create_agent_xcm),
));
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send_blob(
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
sudo_origin,
bx!(destination),
create_channel_xcm.encode().try_into().unwrap(),
bx!(create_channel_xcm),
));
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
......@@ -555,3 +556,133 @@ fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() {
);
});
}
fn send_token_from_ethereum_to_asset_hub_with_fee(account_id: [u8; 32], fee: u128) {
let weth_asset_location: Location = Location::new(
2,
[EthereumNetwork::get().into(), AccountKey20 { network: None, key: WETH }],
);
// (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH })
// Fund asset hub sovereign on bridge hub
let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new(
1,
[Parachain(AssetHubRococo::para_id().into())],
));
BridgeHubRococo::fund_accounts(vec![(asset_hub_sovereign.clone(), INITIAL_FUND)]);
// Register WETH
AssetHubRococo::execute_with(|| {
type RuntimeOrigin = <AssetHubRococo as Chain>::RuntimeOrigin;
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::ForeignAssets::force_create(
RuntimeOrigin::root(),
weth_asset_location.clone().try_into().unwrap(),
asset_hub_sovereign.into(),
false,
1,
));
assert!(<AssetHubRococo as AssetHubRococoPallet>::ForeignAssets::asset_exists(
weth_asset_location.clone().try_into().unwrap(),
));
});
// Send WETH to an existent account on asset hub
BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
type EthereumInboundQueue =
<BridgeHubRococo as BridgeHubRococoPallet>::EthereumInboundQueue;
let message_id: H256 = [0; 32].into();
let message = VersionedMessage::V1(MessageV1 {
chain_id: CHAIN_ID,
command: Command::SendToken {
token: WETH.into(),
destination: Destination::AccountId32 { id: account_id },
amount: 1_000_000,
fee,
},
});
let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap();
assert_ok!(EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()));
// Check that the message was sent
assert_expected_events!(
BridgeHubRococo,
vec![
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
]
);
});
}
#[test]
fn send_token_from_ethereum_to_existent_account_on_asset_hub() {
send_token_from_ethereum_to_asset_hub_with_fee(AssetHubRococoSender::get().into(), XCM_FEE);
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
// Check that the token was received and issued as a foreign asset on AssetHub
assert_expected_events!(
AssetHubRococo,
vec![
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},
]
);
});
}
#[test]
fn send_token_from_ethereum_to_non_existent_account_on_asset_hub() {
send_token_from_ethereum_to_asset_hub_with_fee([1; 32], XCM_FEE);
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
// Check that the token was received and issued as a foreign asset on AssetHub
assert_expected_events!(
AssetHubRococo,
vec![
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},
]
);
});
}
#[test]
fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_insufficient_fee() {
send_token_from_ethereum_to_asset_hub_with_fee([1; 32], INSUFFICIENT_XCM_FEE);
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
// Check that the message was not processed successfully due to insufficient fee
assert_expected_events!(
AssetHubRococo,
vec![
RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {},
]
);
});
}
#[test]
fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_sufficient_fee_but_do_not_satisfy_ed(
) {
// On AH the xcm fee is 33_873_024 and the ED is 3_300_000
send_token_from_ethereum_to_asset_hub_with_fee([1; 32], 36_000_000);
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
// Check that the message was not processed successfully due to insufficient ED
assert_expected_events!(
AssetHubRococo,
vec![
RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {},
]
);
});
}
......@@ -11,7 +11,6 @@ publish = false
workspace = true
[dependencies]
codec = { package = "parity-scale-codec", version = "3.6.0" }
# Substrate
frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false }
......
......@@ -59,15 +59,19 @@ fn send_asset_from_penpal_westend_through_local_asset_hub_to_rococo_asset_hub(
AccountId32Junction { network: None, id: AssetHubRococoReceiver::get().into() }.into();
let assets: Assets = (id.clone(), transfer_amount).into();
let fees_id: AssetId = id.into();
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(assets.len() as u32)),
beneficiary,
}]);
<PenpalB as PenpalBPallet>::PolkadotXcm::transfer_assets_using_type(
<PenpalB as PenpalBPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
signed_origin,
bx!(destination.into()),
bx!(beneficiary.into()),
bx!(assets.into()),
bx!(TransferType::RemoteReserve(local_asset_hub.clone().into())),
bx!(fees_id.into()),
bx!(TransferType::RemoteReserve(local_asset_hub.into())),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
WeightLimit::Unlimited,
)
}));
......
......@@ -14,7 +14,6 @@
// limitations under the License.
use crate::tests::*;
use codec::Encode;
#[test]
fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable() {
......@@ -27,7 +26,7 @@ fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable
let remote_xcm = Xcm(vec![ClearOrigin]);
let xcm = VersionedXcm::from(Xcm::<()>(vec![
let xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit, check_origin },
ExportMessage {
network: RococoId,
......@@ -39,10 +38,10 @@ fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable
// Westend Global Consensus
// Send XCM message from Relay Chain to Bridge Hub source Parachain
Westend::execute_with(|| {
assert_ok!(<Westend as WestendPallet>::XcmPallet::send_blob(
assert_ok!(<Westend as WestendPallet>::XcmPallet::send(
sudo_origin,
bx!(destination),
xcm.encode().try_into().unwrap(),
bx!(xcm),
));
type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
......
......@@ -15,11 +15,7 @@
#[cfg(feature = "std")]
fn main() {
substrate_wasm_builder::WasmBuilder::new()
.with_current_project()
.export_heap_base()
.import_memory()
.build()
substrate_wasm_builder::WasmBuilder::build_using_defaults();
}
#[cfg(not(feature = "std"))]
......
......@@ -973,10 +973,10 @@ pub type UncheckedExtrinsic =
/// Migrations to apply on runtime upgrade.
#[allow(deprecated)]
pub type Migrations = (
pallet_collator_selection::migration::v1::MigrateToV1<Runtime>,
InitStorageVersions,
// unreleased
cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4<Runtime>,
pallet_collator_selection::migration::v2::MigrationToV2<Runtime>,
// permanent
pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
);
......@@ -1519,27 +1519,23 @@ impl_runtime_apis! {
fn worst_case_holding(depositable_count: u32) -> xcm::v4::Assets {
// A mix of fungible, non-fungible, and concrete assets.
let holding_non_fungibles = MaxAssetsIntoHolding::get() / 2 - depositable_count;
let holding_fungibles = holding_non_fungibles.saturating_sub(1);
let holding_fungibles = holding_non_fungibles.saturating_sub(2); // -2 for two `iter::once` bellow
let fungibles_amount: u128 = 100;
let mut assets = (0..holding_fungibles)
(0..holding_fungibles)
.map(|i| {
Asset {
id: GeneralIndex(i as u128).into(),
fun: Fungible(fungibles_amount * i as u128),
fun: Fungible(fungibles_amount * (i + 1) as u128), // non-zero amount
}
})
.chain(core::iter::once(Asset { id: Here.into(), fun: Fungible(u128::MAX) }))
.chain(core::iter::once(Asset { id: AssetId(TokenLocation::get()), fun: Fungible(1_000_000 * UNITS) }))
.chain((0..holding_non_fungibles).map(|i| Asset {
id: GeneralIndex(i as u128).into(),
fun: NonFungible(asset_instance_from(i)),
}))
.collect::<Vec<_>>();
assets.push(Asset {
id: AssetId(TokenLocation::get()),
fun: Fungible(1_000_000 * UNITS),
});
assets.into()
.collect::<Vec<_>>()
.into()
}
}
......