...@@ -70,7 +70,9 @@ mod imports { ...@@ -70,7 +70,9 @@ mod imports {
LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub, LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub,
LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, 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_ID: u32 = 3;
pub const ASSET_MIN_BALANCE: u128 = 1000; pub const ASSET_MIN_BALANCE: u128 = 1000;
...@@ -83,6 +85,7 @@ mod imports { ...@@ -83,6 +85,7 @@ mod imports {
pub type ParaToSystemParaTest = Test<PenpalA, AssetHubRococo>; pub type ParaToSystemParaTest = Test<PenpalA, AssetHubRococo>;
pub type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Rococo>; pub type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Rococo>;
pub type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubRococo>; pub type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubRococo>;
pub type RelayToParaThroughAHTest = Test<Rococo, PenpalA, AssetHubRococo>;
} }
#[cfg(test)] #[cfg(test)]
......
...@@ -54,14 +54,18 @@ fn para_to_para_assethub_hop_assertions(t: ParaToParaThroughAHTest) { ...@@ -54,14 +54,18 @@ fn para_to_para_assethub_hop_assertions(t: ParaToParaThroughAHTest) {
fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult { fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize; let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); 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, t.signed_origin,
bx!(t.args.dest.into()), bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()), bx!(t.args.assets.into()),
bx!(TransferType::LocalReserve), bx!(TransferType::LocalReserve),
bx!(fee.id.into()), bx!(fee.id.into()),
bx!(TransferType::LocalReserve), bx!(TransferType::LocalReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit, t.args.weight_limit,
) )
} }
...@@ -69,14 +73,18 @@ fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult { ...@@ -69,14 +73,18 @@ fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
fn para_to_ah_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult { fn para_to_ah_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize; let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); 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, t.signed_origin,
bx!(t.args.dest.into()), bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()), bx!(t.args.assets.into()),
bx!(TransferType::DestinationReserve), bx!(TransferType::DestinationReserve),
bx!(fee.id.into()), bx!(fee.id.into()),
bx!(TransferType::DestinationReserve), bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit, t.args.weight_limit,
) )
} }
...@@ -85,14 +93,18 @@ fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> Dispat ...@@ -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_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubRococo::para_id()); 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, t.signed_origin,
bx!(t.args.dest.into()), bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()), bx!(t.args.assets.into()),
bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())), bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())),
bx!(fee.id.into()), bx!(fee.id.into()),
bx!(TransferType::RemoteReserve(asset_hub_location.into())), bx!(TransferType::RemoteReserve(asset_hub_location.into())),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit, t.args.weight_limit,
) )
} }
...@@ -100,14 +112,18 @@ fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> Dispat ...@@ -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 { fn para_to_asset_hub_teleport_foreign_assets(t: ParaToSystemParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize; let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); 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, t.signed_origin,
bx!(t.args.dest.into()), bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()), bx!(t.args.assets.into()),
bx!(TransferType::Teleport), bx!(TransferType::Teleport),
bx!(fee.id.into()), bx!(fee.id.into()),
bx!(TransferType::DestinationReserve), bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit, t.args.weight_limit,
) )
} }
...@@ -115,14 +131,18 @@ fn para_to_asset_hub_teleport_foreign_assets(t: ParaToSystemParaTest) -> Dispatc ...@@ -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 { fn asset_hub_to_para_teleport_foreign_assets(t: SystemParaToParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize; let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); 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, t.signed_origin,
bx!(t.args.dest.into()), bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()), bx!(t.args.assets.into()),
bx!(TransferType::Teleport), bx!(TransferType::Teleport),
bx!(fee.id.into()), bx!(fee.id.into()),
bx!(TransferType::LocalReserve), bx!(TransferType::LocalReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit, t.args.weight_limit,
) )
} }
...@@ -626,3 +646,166 @@ fn bidirectional_teleport_foreign_asset_between_para_and_asset_hub_using_explici ...@@ -626,3 +646,166 @@ fn bidirectional_teleport_foreign_asset_between_para_and_asset_hub_using_explici
asset_hub_to_para_teleport_foreign_assets, 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 @@ ...@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
mod foreign_assets_transfers; mod hybrid_transfers;
mod reserve_transfer; mod reserve_transfer;
mod send; mod send;
mod set_xcm_versions; mod set_xcm_versions;
......
...@@ -574,7 +574,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() { ...@@ -574,7 +574,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() {
let sender = RococoSender::get(); let sender = RococoSender::get();
let amount_to_send: Balance = ROCOCO_ED * 1000; let amount_to_send: Balance = ROCOCO_ED * 1000;
// Init values fot Parachain // Init values for Parachain
let relay_native_asset_location = RelayLocation::get(); let relay_native_asset_location = RelayLocation::get();
let receiver = PenpalAReceiver::get(); let receiver = PenpalAReceiver::get();
......
...@@ -75,10 +75,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { ...@@ -75,10 +75,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() {
)]); )]);
PenpalA::execute_with(|| { PenpalA::execute_with(|| {
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob( assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
root_origin, root_origin,
bx!(system_para_destination), bx!(system_para_destination),
xcm.encode().try_into().unwrap(), bx!(xcm),
)); ));
PenpalA::assert_xcm_pallet_sent(); PenpalA::assert_xcm_pallet_sent();
...@@ -159,10 +159,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { ...@@ -159,10 +159,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() {
)]); )]);
PenpalA::execute_with(|| { PenpalA::execute_with(|| {
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob( assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
root_origin, root_origin,
bx!(system_para_destination), bx!(system_para_destination),
xcm.encode().try_into().unwrap(), bx!(xcm),
)); ));
PenpalA::assert_xcm_pallet_sent(); PenpalA::assert_xcm_pallet_sent();
......
...@@ -372,10 +372,10 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { ...@@ -372,10 +372,10 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() {
penpal.clone(), penpal.clone(),
); );
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob( assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
penpal_root, penpal_root,
bx!(asset_hub_location), bx!(asset_hub_location),
xcm.encode().try_into().unwrap(), bx!(xcm),
)); ));
PenpalA::assert_xcm_pallet_sent(); PenpalA::assert_xcm_pallet_sent();
......
...@@ -74,7 +74,9 @@ mod imports { ...@@ -74,7 +74,9 @@ mod imports {
LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub, LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub,
LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, 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_ID: u32 = 3;
pub const ASSET_MIN_BALANCE: u128 = 1000; pub const ASSET_MIN_BALANCE: u128 = 1000;
...@@ -87,6 +89,7 @@ mod imports { ...@@ -87,6 +89,7 @@ mod imports {
pub type ParaToSystemParaTest = Test<PenpalA, AssetHubWestend>; pub type ParaToSystemParaTest = Test<PenpalA, AssetHubWestend>;
pub type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Westend>; pub type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Westend>;
pub type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubWestend>; pub type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubWestend>;
pub type RelayToParaThroughAHTest = Test<Westend, PenpalA, AssetHubWestend>;
} }
#[cfg(test)] #[cfg(test)]
......
...@@ -54,14 +54,18 @@ fn para_to_para_assethub_hop_assertions(t: ParaToParaThroughAHTest) { ...@@ -54,14 +54,18 @@ fn para_to_para_assethub_hop_assertions(t: ParaToParaThroughAHTest) {
fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult { fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize; let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); 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, t.signed_origin,
bx!(t.args.dest.into()), bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()), bx!(t.args.assets.into()),
bx!(TransferType::LocalReserve), bx!(TransferType::LocalReserve),
bx!(fee.id.into()), bx!(fee.id.into()),
bx!(TransferType::LocalReserve), bx!(TransferType::LocalReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit, t.args.weight_limit,
) )
} }
...@@ -69,14 +73,18 @@ fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult { ...@@ -69,14 +73,18 @@ fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
fn para_to_ah_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult { fn para_to_ah_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize; let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); 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, t.signed_origin,
bx!(t.args.dest.into()), bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()), bx!(t.args.assets.into()),
bx!(TransferType::DestinationReserve), bx!(TransferType::DestinationReserve),
bx!(fee.id.into()), bx!(fee.id.into()),
bx!(TransferType::DestinationReserve), bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit, t.args.weight_limit,
) )
} }
...@@ -85,14 +93,18 @@ fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> Dispat ...@@ -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_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubWestend::para_id()); 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, t.signed_origin,
bx!(t.args.dest.into()), bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()), bx!(t.args.assets.into()),
bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())), bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())),
bx!(fee.id.into()), bx!(fee.id.into()),
bx!(TransferType::RemoteReserve(asset_hub_location.into())), bx!(TransferType::RemoteReserve(asset_hub_location.into())),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit, t.args.weight_limit,
) )
} }
...@@ -100,14 +112,18 @@ fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> Dispat ...@@ -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 { fn para_to_asset_hub_teleport_foreign_assets(t: ParaToSystemParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize; let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); 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, t.signed_origin,
bx!(t.args.dest.into()), bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()), bx!(t.args.assets.into()),
bx!(TransferType::Teleport), bx!(TransferType::Teleport),
bx!(fee.id.into()), bx!(fee.id.into()),
bx!(TransferType::DestinationReserve), bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit, t.args.weight_limit,
) )
} }
...@@ -115,14 +131,18 @@ fn para_to_asset_hub_teleport_foreign_assets(t: ParaToSystemParaTest) -> Dispatc ...@@ -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 { fn asset_hub_to_para_teleport_foreign_assets(t: SystemParaToParaTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize; let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); 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, t.signed_origin,
bx!(t.args.dest.into()), bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()), bx!(t.args.assets.into()),
bx!(TransferType::Teleport), bx!(TransferType::Teleport),
bx!(fee.id.into()), bx!(fee.id.into()),
bx!(TransferType::LocalReserve), bx!(TransferType::LocalReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit, t.args.weight_limit,
) )
} }
...@@ -627,3 +647,166 @@ fn bidirectional_teleport_foreign_asset_between_para_and_asset_hub_using_explici ...@@ -627,3 +647,166 @@ fn bidirectional_teleport_foreign_asset_between_para_and_asset_hub_using_explici
asset_hub_to_para_teleport_foreign_assets, 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 @@ ...@@ -14,7 +14,7 @@
// limitations under the License. // limitations under the License.
mod fellowship_treasury; mod fellowship_treasury;
mod foreign_assets_transfers; mod hybrid_transfers;
mod reserve_transfer; mod reserve_transfer;
mod send; mod send;
mod set_xcm_versions; mod set_xcm_versions;
......
...@@ -574,7 +574,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() { ...@@ -574,7 +574,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() {
let sender = WestendSender::get(); let sender = WestendSender::get();
let amount_to_send: Balance = WESTEND_ED * 1000; let amount_to_send: Balance = WESTEND_ED * 1000;
// Init values fot Parachain // Init values for Parachain
let relay_native_asset_location = RelayLocation::get(); let relay_native_asset_location = RelayLocation::get();
let receiver = PenpalAReceiver::get(); let receiver = PenpalAReceiver::get();
......
...@@ -75,10 +75,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { ...@@ -75,10 +75,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() {
)]); )]);
PenpalA::execute_with(|| { PenpalA::execute_with(|| {
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob( assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
root_origin, root_origin,
bx!(system_para_destination), bx!(system_para_destination),
xcm.encode().try_into().unwrap(), bx!(xcm),
)); ));
PenpalA::assert_xcm_pallet_sent(); PenpalA::assert_xcm_pallet_sent();
...@@ -159,10 +159,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { ...@@ -159,10 +159,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() {
)]); )]);
PenpalA::execute_with(|| { PenpalA::execute_with(|| {
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob( assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
root_origin, root_origin,
bx!(system_para_destination), bx!(system_para_destination),
xcm.encode().try_into().unwrap(), bx!(xcm),
)); ));
PenpalA::assert_xcm_pallet_sent(); PenpalA::assert_xcm_pallet_sent();
......
...@@ -371,10 +371,10 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { ...@@ -371,10 +371,10 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() {
penpal.clone(), penpal.clone(),
); );
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send_blob( assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
penpal_root, penpal_root,
bx!(asset_hub_location), bx!(asset_hub_location),
xcm.encode().try_into().unwrap(), bx!(xcm),
)); ));
PenpalA::assert_xcm_pallet_sent(); PenpalA::assert_xcm_pallet_sent();
......
...@@ -60,15 +60,19 @@ fn send_asset_from_penpal_rococo_through_local_asset_hub_to_westend_asset_hub( ...@@ -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(); AccountId32Junction { network: None, id: AssetHubWestendReceiver::get().into() }.into();
let assets: Assets = (id.clone(), transfer_amount).into(); let assets: Assets = (id.clone(), transfer_amount).into();
let fees_id: AssetId = id.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, signed_origin,
bx!(destination.into()), bx!(destination.into()),
bx!(beneficiary.into()),
bx!(assets.clone().into()), bx!(assets.clone().into()),
bx!(TransferType::RemoteReserve(local_asset_hub.clone().into())), bx!(TransferType::RemoteReserve(local_asset_hub.clone().into())),
bx!(fees_id.into()), bx!(fees_id.into()),
bx!(TransferType::RemoteReserve(local_asset_hub.into())), bx!(TransferType::RemoteReserve(local_asset_hub.into())),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
WeightLimit::Unlimited, WeightLimit::Unlimited,
) )
})); }));
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
// limitations under the License. // limitations under the License.
use crate::tests::*; use crate::tests::*;
use codec::Encode;
#[test] #[test]
fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable() { 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 ...@@ -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 remote_xcm = Xcm(vec![ClearOrigin]);
let xcm = VersionedXcm::from(Xcm::<()>(vec![ let xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit, check_origin }, UnpaidExecution { weight_limit, check_origin },
ExportMessage { ExportMessage {
network: WestendId.into(), network: WestendId.into(),
...@@ -39,10 +38,10 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable ...@@ -39,10 +38,10 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable
// Rococo Global Consensus // Rococo Global Consensus
// Send XCM message from Relay Chain to Bridge Hub source Parachain // Send XCM message from Relay Chain to Bridge Hub source Parachain
Rococo::execute_with(|| { Rococo::execute_with(|| {
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send_blob( assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
sudo_origin, sudo_origin,
bx!(destination), bx!(destination),
xcm.encode().try_into().unwrap(), bx!(xcm),
)); ));
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent; type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
......
...@@ -27,7 +27,7 @@ use snowbridge_pallet_inbound_queue_fixtures::{ ...@@ -27,7 +27,7 @@ use snowbridge_pallet_inbound_queue_fixtures::{
}; };
use snowbridge_pallet_system; use snowbridge_pallet_system;
use snowbridge_router_primitives::inbound::{ use snowbridge_router_primitives::inbound::{
Command, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage,
}; };
use sp_core::H256; use sp_core::H256;
use sp_runtime::{DispatchError::Token, TokenError::FundsUnavailable}; use sp_runtime::{DispatchError::Token, TokenError::FundsUnavailable};
...@@ -40,6 +40,7 @@ const TREASURY_ACCOUNT: [u8; 32] = ...@@ -40,6 +40,7 @@ const TREASURY_ACCOUNT: [u8; 32] =
const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d");
const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e");
const INSUFFICIENT_XCM_FEE: u128 = 1000; const INSUFFICIENT_XCM_FEE: u128 = 1000;
const XCM_FEE: u128 = 4_000_000_000;
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
pub enum ControlCall { pub enum ControlCall {
...@@ -82,7 +83,7 @@ fn create_agent() { ...@@ -82,7 +83,7 @@ fn create_agent() {
let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {}); let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {});
// Construct XCM to create an agent for para 1001 // 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 }, UnpaidExecution { weight_limit: Unlimited, check_origin: None },
DescendOrigin(Parachain(origin_para).into()), DescendOrigin(Parachain(origin_para).into()),
Transact { Transact {
...@@ -95,10 +96,10 @@ fn create_agent() { ...@@ -95,10 +96,10 @@ fn create_agent() {
// Rococo Global Consensus // Rococo Global Consensus
// Send XCM message from Relay Chain to Bridge Hub source Parachain // Send XCM message from Relay Chain to Bridge Hub source Parachain
Rococo::execute_with(|| { Rococo::execute_with(|| {
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send_blob( assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
sudo_origin, sudo_origin,
bx!(destination), bx!(destination),
remote_xcm.encode().try_into().unwrap(), bx!(remote_xcm),
)); ));
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent; type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
...@@ -140,7 +141,7 @@ fn create_channel() { ...@@ -140,7 +141,7 @@ fn create_channel() {
let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {}); let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {});
// Construct XCM to create an agent for para 1001 // 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 }, UnpaidExecution { weight_limit: Unlimited, check_origin: None },
DescendOrigin(Parachain(origin_para).into()), DescendOrigin(Parachain(origin_para).into()),
Transact { Transact {
...@@ -153,7 +154,7 @@ fn create_channel() { ...@@ -153,7 +154,7 @@ fn create_channel() {
let create_channel_call = let create_channel_call =
SnowbridgeControl::Control(ControlCall::CreateChannel { mode: OperatingMode::Normal }); SnowbridgeControl::Control(ControlCall::CreateChannel { mode: OperatingMode::Normal });
// Construct XCM to create a channel for para 1001 // 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 }, UnpaidExecution { weight_limit: Unlimited, check_origin: None },
DescendOrigin(Parachain(origin_para).into()), DescendOrigin(Parachain(origin_para).into()),
Transact { Transact {
...@@ -166,16 +167,16 @@ fn create_channel() { ...@@ -166,16 +167,16 @@ fn create_channel() {
// Rococo Global Consensus // Rococo Global Consensus
// Send XCM message from Relay Chain to Bridge Hub source Parachain // Send XCM message from Relay Chain to Bridge Hub source Parachain
Rococo::execute_with(|| { Rococo::execute_with(|| {
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send_blob( assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
sudo_origin.clone(), sudo_origin.clone(),
bx!(destination.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, sudo_origin,
bx!(destination), bx!(destination),
create_channel_xcm.encode().try_into().unwrap(), bx!(create_channel_xcm),
)); ));
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent; type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
...@@ -555,3 +556,133 @@ fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() { ...@@ -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 ...@@ -11,7 +11,6 @@ publish = false
workspace = true workspace = true
[dependencies] [dependencies]
codec = { package = "parity-scale-codec", version = "3.6.0" }
# Substrate # Substrate
frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } 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( ...@@ -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(); AccountId32Junction { network: None, id: AssetHubRococoReceiver::get().into() }.into();
let assets: Assets = (id.clone(), transfer_amount).into(); let assets: Assets = (id.clone(), transfer_amount).into();
let fees_id: AssetId = id.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, signed_origin,
bx!(destination.into()), bx!(destination.into()),
bx!(beneficiary.into()),
bx!(assets.into()), bx!(assets.into()),
bx!(TransferType::RemoteReserve(local_asset_hub.clone().into())), bx!(TransferType::RemoteReserve(local_asset_hub.clone().into())),
bx!(fees_id.into()), bx!(fees_id.into()),
bx!(TransferType::RemoteReserve(local_asset_hub.into())), bx!(TransferType::RemoteReserve(local_asset_hub.into())),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
WeightLimit::Unlimited, WeightLimit::Unlimited,
) )
})); }));
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
// limitations under the License. // limitations under the License.
use crate::tests::*; use crate::tests::*;
use codec::Encode;
#[test] #[test]
fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable() { 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 ...@@ -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 remote_xcm = Xcm(vec![ClearOrigin]);
let xcm = VersionedXcm::from(Xcm::<()>(vec![ let xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit, check_origin }, UnpaidExecution { weight_limit, check_origin },
ExportMessage { ExportMessage {
network: RococoId, network: RococoId,
...@@ -39,10 +38,10 @@ fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable ...@@ -39,10 +38,10 @@ fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable
// Westend Global Consensus // Westend Global Consensus
// Send XCM message from Relay Chain to Bridge Hub source Parachain // Send XCM message from Relay Chain to Bridge Hub source Parachain
Westend::execute_with(|| { Westend::execute_with(|| {
assert_ok!(<Westend as WestendPallet>::XcmPallet::send_blob( assert_ok!(<Westend as WestendPallet>::XcmPallet::send(
sudo_origin, sudo_origin,
bx!(destination), bx!(destination),
xcm.encode().try_into().unwrap(), bx!(xcm),
)); ));
type RuntimeEvent = <Westend as Chain>::RuntimeEvent; type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
......
...@@ -15,11 +15,7 @@ ...@@ -15,11 +15,7 @@
#[cfg(feature = "std")] #[cfg(feature = "std")]
fn main() { fn main() {
substrate_wasm_builder::WasmBuilder::new() substrate_wasm_builder::WasmBuilder::build_using_defaults();
.with_current_project()
.export_heap_base()
.import_memory()
.build()
} }
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
......
...@@ -973,10 +973,10 @@ pub type UncheckedExtrinsic = ...@@ -973,10 +973,10 @@ pub type UncheckedExtrinsic =
/// Migrations to apply on runtime upgrade. /// Migrations to apply on runtime upgrade.
#[allow(deprecated)] #[allow(deprecated)]
pub type Migrations = ( pub type Migrations = (
pallet_collator_selection::migration::v1::MigrateToV1<Runtime>,
InitStorageVersions, InitStorageVersions,
// unreleased // unreleased
cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4<Runtime>, cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4<Runtime>,
pallet_collator_selection::migration::v2::MigrationToV2<Runtime>,
// permanent // permanent
pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>, pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
); );
...@@ -1519,27 +1519,23 @@ impl_runtime_apis! { ...@@ -1519,27 +1519,23 @@ impl_runtime_apis! {
fn worst_case_holding(depositable_count: u32) -> xcm::v4::Assets { fn worst_case_holding(depositable_count: u32) -> xcm::v4::Assets {
// A mix of fungible, non-fungible, and concrete assets. // A mix of fungible, non-fungible, and concrete assets.
let holding_non_fungibles = MaxAssetsIntoHolding::get() / 2 - depositable_count; 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 fungibles_amount: u128 = 100;
let mut assets = (0..holding_fungibles) (0..holding_fungibles)
.map(|i| { .map(|i| {
Asset { Asset {
id: GeneralIndex(i as u128).into(), 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: 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 { .chain((0..holding_non_fungibles).map(|i| Asset {
id: GeneralIndex(i as u128).into(), id: GeneralIndex(i as u128).into(),
fun: NonFungible(asset_instance_from(i)), fun: NonFungible(asset_instance_from(i)),
})) }))
.collect::<Vec<_>>(); .collect::<Vec<_>>()
.into()
assets.push(Asset {
id: AssetId(TokenLocation::get()),
fun: Fungible(1_000_000 * UNITS),
});
assets.into()
} }
} }
......