Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

pallet-xcm::transfer_assets_using_type_and_then() supports custom actions on destination #4260

Merged
merged 4 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,29 +54,37 @@ 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,
)
}

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,
)
}
Expand All @@ -85,44 +93,56 @@ 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,
)
}

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,
)
}

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,
)
}
Expand Down Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)]
Expand Down
Loading
Loading