mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 08:41:02 +00:00
pallet-xcm: add new extrinsic for asset transfers using explicit XCM transfer types (#3695)
# Description Add `transfer_assets_using()` for transferring assets from local chain to destination chain using explicit XCM transfer types such as: - `TransferType::LocalReserve`: transfer assets to sovereign account of destination chain and forward a notification XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. - `TransferType::DestinationReserve`: burn local assets and forward a notification to `dest` chain to withdraw the reserve assets from this chain's sovereign account and deposit them to `beneficiary`. - `TransferType::RemoteReserve(reserve)`: burn local assets, forward XCM to `reserve` chain to move reserves from this chain's SA to `dest` chain's SA, and forward another XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. Typically the remote `reserve` is Asset Hub. - `TransferType::Teleport`: burn local assets and forward XCM to `dest` chain to mint/teleport assets and deposit them to `beneficiary`. By default, an asset's reserve is its origin chain. But sometimes we may want to explicitly use another chain as reserve (as long as allowed by runtime `IsReserve` filter). This is very helpful for transferring assets with multiple configured reserves (such as Asset Hub ForeignAssets), when the transfer strictly depends on the used reserve. E.g. For transferring Foreign Assets over a bridge, Asset Hub must be used as the reserve location. # Example usage scenarios ## Transfer bridged ethereum ERC20-tokenX between ecosystem parachains. ERC20-tokenX is registered on AssetHub as a ForeignAsset by the Polkadot<>Ethereum bridge (Snowbridge). Its asset_id is something like `(parents:2, (GlobalConsensus(Ethereum), Address(tokenX_contract)))`. Its _original_ reserve is Ethereum (only we can't use Ethereum as a reserve in local transfers); but, since tokenX is also registered on AssetHub as a ForeignAsset, we can use AssetHub as a reserve. With this PR we can transfer tokenX from ParaA to ParaB while using AssetHub as a reserve. ## Transfer AssetHub ForeignAssets between parachains AssetA created on ParaA but also registered as foreign asset on Asset Hub. Can use AssetHub as a reserve. And all of the above can be done while still controlling transfer type for `fees` so mixing assets in same transfer is supported. # Tests Added integration tests for showcasing: - transferring local (not bridged) assets from parachain over bridge using local Asset Hub reserve, - transferring foreign assets from parachain to Asset Hub, - transferring foreign assets from Asset Hub to parachain, - transferring foreign assets from parachain to parachain using local Asset Hub reserve. --------- Co-authored-by: Branislav Kontur <bkontur@gmail.com> Co-authored-by: command-bot <>
This commit is contained in:
@@ -30,6 +30,7 @@ mod imports {
|
||||
prelude::{AccountId32 as AccountId32Junction, *},
|
||||
v3,
|
||||
};
|
||||
pub use xcm_executor::traits::TransferType;
|
||||
|
||||
// Cumulus
|
||||
pub use asset_test_utils::xcm_helpers;
|
||||
@@ -81,6 +82,7 @@ mod imports {
|
||||
pub type SystemParaToParaTest = Test<AssetHubRococo, PenpalA>;
|
||||
pub type ParaToSystemParaTest = Test<PenpalA, AssetHubRococo>;
|
||||
pub type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Rococo>;
|
||||
pub type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubRococo>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+608
@@ -0,0 +1,608 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::reserve_transfer::*;
|
||||
use crate::{
|
||||
imports::*,
|
||||
tests::teleport::do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt,
|
||||
};
|
||||
|
||||
fn para_to_para_assethub_hop_assertions(t: ParaToParaThroughAHTest) {
|
||||
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
|
||||
let sov_penpal_a_on_ah = AssetHubRococo::sovereign_account_id_of(
|
||||
AssetHubRococo::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
let sov_penpal_b_on_ah = AssetHubRococo::sovereign_account_id_of(
|
||||
AssetHubRococo::sibling_location_of(PenpalB::para_id()),
|
||||
);
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubRococo,
|
||||
vec![
|
||||
// Withdrawn from sender parachain SA
|
||||
RuntimeEvent::Balances(
|
||||
pallet_balances::Event::Burned { who, amount }
|
||||
) => {
|
||||
who: *who == sov_penpal_a_on_ah,
|
||||
amount: *amount == t.args.amount,
|
||||
},
|
||||
// Deposited to receiver parachain SA
|
||||
RuntimeEvent::Balances(
|
||||
pallet_balances::Event::Minted { who, .. }
|
||||
) => {
|
||||
who: *who == sov_penpal_b_on_ah,
|
||||
},
|
||||
RuntimeEvent::MessageQueue(
|
||||
pallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
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),
|
||||
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(
|
||||
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),
|
||||
t.args.weight_limit,
|
||||
)
|
||||
}
|
||||
|
||||
fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> 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: Location = PenpalA::sibling_location_of(AssetHubRococo::para_id());
|
||||
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type(
|
||||
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())),
|
||||
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(
|
||||
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),
|
||||
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(
|
||||
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),
|
||||
t.args.weight_limit,
|
||||
)
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// ======= Transfer - Native + Bridged Assets - AssetHub->Parachain ==========
|
||||
// ===========================================================================
|
||||
/// Transfers of native asset plus bridged asset from AssetHub to some Parachain
|
||||
/// while paying fees using native asset.
|
||||
#[test]
|
||||
fn transfer_foreign_assets_from_asset_hub_to_para() {
|
||||
let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id());
|
||||
let sender = AssetHubRococoSender::get();
|
||||
let native_amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000;
|
||||
let native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let receiver = PenpalAReceiver::get();
|
||||
let assets_owner = PenpalAssetOwner::get();
|
||||
// Foreign asset used: bridged WND
|
||||
let foreign_amount_to_send = ASSET_HUB_ROCOCO_ED * 10_000_000;
|
||||
let wnd_at_rococo_parachains =
|
||||
v3::Location::new(2, [v3::Junction::GlobalConsensus(v3::NetworkId::Westend)]);
|
||||
let wnd_at_rococo_parachains_latest: Location = wnd_at_rococo_parachains.try_into().unwrap();
|
||||
|
||||
// Configure destination chain to trust AH as reserve of WND
|
||||
PenpalA::execute_with(|| {
|
||||
assert_ok!(<PenpalA as Chain>::System::set_storage(
|
||||
<PenpalA as Chain>::RuntimeOrigin::root(),
|
||||
vec![(
|
||||
penpal_runtime::xcm_config::CustomizableAssetFromSystemAssetHub::key().to_vec(),
|
||||
Location::new(2, [GlobalConsensus(Westend)]).encode(),
|
||||
)],
|
||||
));
|
||||
});
|
||||
PenpalA::force_create_foreign_asset(
|
||||
wnd_at_rococo_parachains,
|
||||
assets_owner.clone(),
|
||||
false,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
AssetHubRococo::force_create_foreign_asset(
|
||||
wnd_at_rococo_parachains,
|
||||
assets_owner.clone(),
|
||||
false,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
AssetHubRococo::mint_foreign_asset(
|
||||
<AssetHubRococo as Chain>::RuntimeOrigin::signed(assets_owner),
|
||||
wnd_at_rococo_parachains,
|
||||
sender.clone(),
|
||||
foreign_amount_to_send * 2,
|
||||
);
|
||||
|
||||
// Assets to send
|
||||
let assets: Vec<Asset> = vec![
|
||||
(Parent, native_amount_to_send).into(),
|
||||
(wnd_at_rococo_parachains_latest, foreign_amount_to_send).into(),
|
||||
];
|
||||
let fee_asset_id = AssetId(Parent.into());
|
||||
let fee_asset_item = assets.iter().position(|a| a.id == fee_asset_id).unwrap() as u32;
|
||||
|
||||
// Init Test
|
||||
let test_args = TestContext {
|
||||
sender: sender.clone(),
|
||||
receiver: receiver.clone(),
|
||||
args: TestArgs::new_para(
|
||||
destination.clone(),
|
||||
receiver.clone(),
|
||||
native_amount_to_send,
|
||||
assets.into(),
|
||||
None,
|
||||
fee_asset_item,
|
||||
),
|
||||
};
|
||||
let mut test = SystemParaToParaTest::new(test_args);
|
||||
|
||||
// Query initial balances
|
||||
let sender_balance_before = test.sender.balance;
|
||||
let sender_wnds_before = AssetHubRococo::execute_with(|| {
|
||||
type ForeignAssets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &sender)
|
||||
});
|
||||
let receiver_assets_before = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(native_asset_location.into(), &receiver)
|
||||
});
|
||||
let receiver_wnds_before = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &receiver)
|
||||
});
|
||||
|
||||
// Set assertions and dispatchables
|
||||
test.set_assertion::<AssetHubRococo>(system_para_to_para_sender_assertions);
|
||||
test.set_assertion::<PenpalA>(system_para_to_para_receiver_assertions);
|
||||
test.set_dispatchable::<AssetHubRococo>(ah_to_para_transfer_assets);
|
||||
test.assert();
|
||||
|
||||
// Query final balances
|
||||
let sender_balance_after = test.sender.balance;
|
||||
let sender_wnds_after = AssetHubRococo::execute_with(|| {
|
||||
type ForeignAssets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &sender)
|
||||
});
|
||||
let receiver_assets_after = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(native_asset_location, &receiver)
|
||||
});
|
||||
let receiver_wnds_after = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &receiver)
|
||||
});
|
||||
|
||||
// Sender's balance is reduced by amount sent plus delivery fees
|
||||
assert!(sender_balance_after < sender_balance_before - native_amount_to_send);
|
||||
// Sender's balance is reduced by foreign amount sent
|
||||
assert_eq!(sender_wnds_after, sender_wnds_before - foreign_amount_to_send);
|
||||
// Receiver's assets is increased
|
||||
assert!(receiver_assets_after > receiver_assets_before);
|
||||
// Receiver's assets 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 + native_amount_to_send);
|
||||
// Receiver's balance is increased by foreign amount sent
|
||||
assert_eq!(receiver_wnds_after, receiver_wnds_before + foreign_amount_to_send);
|
||||
}
|
||||
|
||||
/// Reserve Transfers of native asset from Parachain to System Parachain should work
|
||||
// ===========================================================================
|
||||
// ======= Transfer - Native + Bridged Assets - Parachain->AssetHub ==========
|
||||
// ===========================================================================
|
||||
/// Transfers of native asset plus bridged asset from some Parachain to AssetHub
|
||||
/// while paying fees using native asset.
|
||||
#[test]
|
||||
fn transfer_foreign_assets_from_para_to_asset_hub() {
|
||||
// Init values for Parachain
|
||||
let destination = PenpalA::sibling_location_of(AssetHubRococo::para_id());
|
||||
let sender = PenpalASender::get();
|
||||
let native_amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000;
|
||||
let native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let assets_owner = PenpalAssetOwner::get();
|
||||
|
||||
// Foreign asset used: bridged WND
|
||||
let foreign_amount_to_send = ASSET_HUB_ROCOCO_ED * 10_000_000;
|
||||
let wnd_at_rococo_parachains =
|
||||
v3::Location::new(2, [v3::Junction::GlobalConsensus(v3::NetworkId::Westend)]);
|
||||
let wnd_at_rococo_parachains_latest: Location = wnd_at_rococo_parachains.try_into().unwrap();
|
||||
|
||||
// Configure destination chain to trust AH as reserve of WND
|
||||
PenpalA::execute_with(|| {
|
||||
assert_ok!(<PenpalA as Chain>::System::set_storage(
|
||||
<PenpalA as Chain>::RuntimeOrigin::root(),
|
||||
vec![(
|
||||
penpal_runtime::xcm_config::CustomizableAssetFromSystemAssetHub::key().to_vec(),
|
||||
Location::new(2, [GlobalConsensus(Westend)]).encode(),
|
||||
)],
|
||||
));
|
||||
});
|
||||
PenpalA::force_create_foreign_asset(
|
||||
wnd_at_rococo_parachains,
|
||||
assets_owner.clone(),
|
||||
false,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
AssetHubRococo::force_create_foreign_asset(
|
||||
wnd_at_rococo_parachains,
|
||||
assets_owner.clone(),
|
||||
false,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
|
||||
// fund Parachain's sender account
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()),
|
||||
native_asset_location,
|
||||
sender.clone(),
|
||||
native_amount_to_send * 2,
|
||||
);
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()),
|
||||
wnd_at_rococo_parachains,
|
||||
sender.clone(),
|
||||
foreign_amount_to_send * 2,
|
||||
);
|
||||
|
||||
// Init values for System Parachain
|
||||
let receiver = AssetHubRococoReceiver::get();
|
||||
let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id());
|
||||
let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr);
|
||||
|
||||
// fund Parachain's SA on AssetHub with the assets held in reserve
|
||||
AssetHubRococo::fund_accounts(vec![(
|
||||
sov_penpal_on_ahr.clone().into(),
|
||||
native_amount_to_send * 2,
|
||||
)]);
|
||||
AssetHubRococo::mint_foreign_asset(
|
||||
<AssetHubRococo as Chain>::RuntimeOrigin::signed(assets_owner),
|
||||
wnd_at_rococo_parachains,
|
||||
sov_penpal_on_ahr,
|
||||
foreign_amount_to_send * 2,
|
||||
);
|
||||
|
||||
// Assets to send
|
||||
let assets: Vec<Asset> = vec![
|
||||
(Parent, native_amount_to_send).into(),
|
||||
(wnd_at_rococo_parachains_latest, foreign_amount_to_send).into(),
|
||||
];
|
||||
let fee_asset_id = AssetId(Parent.into());
|
||||
let fee_asset_item = assets.iter().position(|a| a.id == fee_asset_id).unwrap() as u32;
|
||||
|
||||
// Init Test
|
||||
let test_args = TestContext {
|
||||
sender: sender.clone(),
|
||||
receiver: receiver.clone(),
|
||||
args: TestArgs::new_para(
|
||||
destination.clone(),
|
||||
receiver.clone(),
|
||||
native_amount_to_send,
|
||||
assets.into(),
|
||||
None,
|
||||
fee_asset_item,
|
||||
),
|
||||
};
|
||||
let mut test = ParaToSystemParaTest::new(test_args);
|
||||
|
||||
// Query initial balances
|
||||
let sender_native_before = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(native_asset_location, &sender)
|
||||
});
|
||||
let sender_wnds_before = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &sender)
|
||||
});
|
||||
let receiver_native_before = test.receiver.balance;
|
||||
let receiver_wnds_before = AssetHubRococo::execute_with(|| {
|
||||
type ForeignAssets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &receiver)
|
||||
});
|
||||
|
||||
// Set assertions and dispatchables
|
||||
test.set_assertion::<PenpalA>(para_to_system_para_sender_assertions);
|
||||
test.set_assertion::<AssetHubRococo>(para_to_system_para_receiver_assertions);
|
||||
test.set_dispatchable::<PenpalA>(para_to_ah_transfer_assets);
|
||||
test.assert();
|
||||
|
||||
// Query final balances
|
||||
let sender_native_after = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(native_asset_location, &sender)
|
||||
});
|
||||
let sender_wnds_after = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &sender)
|
||||
});
|
||||
let receiver_native_after = test.receiver.balance;
|
||||
let receiver_wnds_after = AssetHubRococo::execute_with(|| {
|
||||
type ForeignAssets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &receiver)
|
||||
});
|
||||
|
||||
// Sender's balance is reduced by amount sent plus delivery fees
|
||||
assert!(sender_native_after < sender_native_before - native_amount_to_send);
|
||||
// Sender's balance is reduced by foreign amount sent
|
||||
assert_eq!(sender_wnds_after, sender_wnds_before - foreign_amount_to_send);
|
||||
// Receiver's balance is increased
|
||||
assert!(receiver_native_after > receiver_native_before);
|
||||
// Receiver's 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_native_after < receiver_native_before + native_amount_to_send);
|
||||
// Receiver's balance is increased by foreign amount sent
|
||||
assert_eq!(receiver_wnds_after, receiver_wnds_before + foreign_amount_to_send);
|
||||
}
|
||||
|
||||
// ==============================================================================
|
||||
// ===== Transfer - Native + Bridged Assets - Parachain->AssetHub->Parachain ====
|
||||
// ==============================================================================
|
||||
/// Transfers of native asset plus bridged asset from Parachain to Parachain
|
||||
/// (through AssetHub reserve) with fees paid using native asset.
|
||||
#[test]
|
||||
fn transfer_foreign_assets_from_para_to_para_through_asset_hub() {
|
||||
// Init values for Parachain Origin
|
||||
let destination = PenpalA::sibling_location_of(PenpalB::para_id());
|
||||
let sender = PenpalASender::get();
|
||||
let roc_to_send: Balance = ROCOCO_ED * 10000;
|
||||
let assets_owner = PenpalAssetOwner::get();
|
||||
let roc_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let roc_location_latest: Location = roc_location.try_into().unwrap();
|
||||
let sender_as_seen_by_ah = AssetHubRococo::sibling_location_of(PenpalA::para_id());
|
||||
let sov_of_sender_on_ah = AssetHubRococo::sovereign_account_id_of(sender_as_seen_by_ah);
|
||||
let receiver_as_seen_by_ah = AssetHubRococo::sibling_location_of(PenpalB::para_id());
|
||||
let sov_of_receiver_on_ah = AssetHubRococo::sovereign_account_id_of(receiver_as_seen_by_ah);
|
||||
let wnd_to_send = ASSET_HUB_ROCOCO_ED * 10_000_000;
|
||||
|
||||
// Configure destination chain to trust AH as reserve of WND
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as Chain>::System::set_storage(
|
||||
<PenpalB as Chain>::RuntimeOrigin::root(),
|
||||
vec![(
|
||||
penpal_runtime::xcm_config::CustomizableAssetFromSystemAssetHub::key().to_vec(),
|
||||
Location::new(2, [GlobalConsensus(Westend)]).encode(),
|
||||
)],
|
||||
));
|
||||
});
|
||||
|
||||
// Register WND as foreign asset and transfer it around the Rococo ecosystem
|
||||
let wnd_at_rococo_parachains =
|
||||
v3::Location::new(2, [v3::Junction::GlobalConsensus(v3::NetworkId::Westend)]);
|
||||
let wnd_at_rococo_parachains_latest: Location = wnd_at_rococo_parachains.try_into().unwrap();
|
||||
AssetHubRococo::force_create_foreign_asset(
|
||||
wnd_at_rococo_parachains,
|
||||
assets_owner.clone(),
|
||||
false,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
PenpalA::force_create_foreign_asset(
|
||||
wnd_at_rococo_parachains,
|
||||
assets_owner.clone(),
|
||||
false,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
PenpalB::force_create_foreign_asset(
|
||||
wnd_at_rococo_parachains,
|
||||
assets_owner.clone(),
|
||||
false,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
|
||||
// fund Parachain's sender account
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()),
|
||||
roc_location,
|
||||
sender.clone(),
|
||||
roc_to_send * 2,
|
||||
);
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()),
|
||||
wnd_at_rococo_parachains,
|
||||
sender.clone(),
|
||||
wnd_to_send * 2,
|
||||
);
|
||||
// fund the Parachain Origin's SA on Asset Hub with the assets held in reserve
|
||||
AssetHubRococo::fund_accounts(vec![(sov_of_sender_on_ah.clone().into(), roc_to_send * 2)]);
|
||||
AssetHubRococo::mint_foreign_asset(
|
||||
<AssetHubRococo as Chain>::RuntimeOrigin::signed(assets_owner),
|
||||
wnd_at_rococo_parachains,
|
||||
sov_of_sender_on_ah.clone(),
|
||||
wnd_to_send * 2,
|
||||
);
|
||||
|
||||
// Init values for Parachain Destination
|
||||
let receiver = PenpalBReceiver::get();
|
||||
|
||||
// Assets to send
|
||||
let assets: Vec<Asset> = vec![
|
||||
(roc_location_latest.clone(), roc_to_send).into(),
|
||||
(wnd_at_rococo_parachains_latest, wnd_to_send).into(),
|
||||
];
|
||||
let fee_asset_id: AssetId = roc_location_latest.into();
|
||||
let fee_asset_item = assets.iter().position(|a| a.id == fee_asset_id).unwrap() as u32;
|
||||
|
||||
// Init Test
|
||||
let test_args = TestContext {
|
||||
sender: sender.clone(),
|
||||
receiver: receiver.clone(),
|
||||
args: TestArgs::new_para(
|
||||
destination,
|
||||
receiver.clone(),
|
||||
roc_to_send,
|
||||
assets.into(),
|
||||
None,
|
||||
fee_asset_item,
|
||||
),
|
||||
};
|
||||
let mut test = ParaToParaThroughAHTest::new(test_args);
|
||||
|
||||
// Query initial balances
|
||||
let sender_rocs_before = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(roc_location, &sender)
|
||||
});
|
||||
let sender_wnds_before = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &sender)
|
||||
});
|
||||
let rocs_in_sender_reserve_on_ahr_before =
|
||||
<AssetHubRococo as Chain>::account_data_of(sov_of_sender_on_ah.clone()).free;
|
||||
let wnds_in_sender_reserve_on_ahr_before = AssetHubRococo::execute_with(|| {
|
||||
type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(wnd_at_rococo_parachains, &sov_of_sender_on_ah)
|
||||
});
|
||||
let rocs_in_receiver_reserve_on_ahr_before =
|
||||
<AssetHubRococo as Chain>::account_data_of(sov_of_receiver_on_ah.clone()).free;
|
||||
let wnds_in_receiver_reserve_on_ahr_before = AssetHubRococo::execute_with(|| {
|
||||
type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(wnd_at_rococo_parachains, &sov_of_receiver_on_ah)
|
||||
});
|
||||
let receiver_rocs_before = PenpalB::execute_with(|| {
|
||||
type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(roc_location, &receiver)
|
||||
});
|
||||
let receiver_wnds_before = PenpalB::execute_with(|| {
|
||||
type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &receiver)
|
||||
});
|
||||
|
||||
// Set assertions and dispatchables
|
||||
test.set_assertion::<PenpalA>(para_to_para_through_hop_sender_assertions);
|
||||
test.set_assertion::<AssetHubRococo>(para_to_para_assethub_hop_assertions);
|
||||
test.set_assertion::<PenpalB>(para_to_para_through_hop_receiver_assertions);
|
||||
test.set_dispatchable::<PenpalA>(para_to_para_transfer_assets_through_ah);
|
||||
test.assert();
|
||||
|
||||
// Query final balances
|
||||
let sender_rocs_after = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(roc_location, &sender)
|
||||
});
|
||||
let sender_wnds_after = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &sender)
|
||||
});
|
||||
let wnds_in_sender_reserve_on_ahr_after = AssetHubRococo::execute_with(|| {
|
||||
type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(wnd_at_rococo_parachains, &sov_of_sender_on_ah)
|
||||
});
|
||||
let rocs_in_sender_reserve_on_ahr_after =
|
||||
<AssetHubRococo as Chain>::account_data_of(sov_of_sender_on_ah).free;
|
||||
let wnds_in_receiver_reserve_on_ahr_after = AssetHubRococo::execute_with(|| {
|
||||
type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(wnd_at_rococo_parachains, &sov_of_receiver_on_ah)
|
||||
});
|
||||
let rocs_in_receiver_reserve_on_ahr_after =
|
||||
<AssetHubRococo as Chain>::account_data_of(sov_of_receiver_on_ah).free;
|
||||
let receiver_rocs_after = PenpalB::execute_with(|| {
|
||||
type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(roc_location, &receiver)
|
||||
});
|
||||
let receiver_wnds_after = PenpalB::execute_with(|| {
|
||||
type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &receiver)
|
||||
});
|
||||
|
||||
// Sender's balance is reduced by amount sent plus delivery fees
|
||||
assert!(sender_rocs_after < sender_rocs_before - roc_to_send);
|
||||
assert_eq!(sender_wnds_after, sender_wnds_before - wnd_to_send);
|
||||
// Sovereign accounts on reserve are changed accordingly
|
||||
assert_eq!(
|
||||
rocs_in_sender_reserve_on_ahr_after,
|
||||
rocs_in_sender_reserve_on_ahr_before - roc_to_send
|
||||
);
|
||||
assert_eq!(
|
||||
wnds_in_sender_reserve_on_ahr_after,
|
||||
wnds_in_sender_reserve_on_ahr_before - wnd_to_send
|
||||
);
|
||||
assert!(rocs_in_receiver_reserve_on_ahr_after > rocs_in_receiver_reserve_on_ahr_before);
|
||||
assert_eq!(
|
||||
wnds_in_receiver_reserve_on_ahr_after,
|
||||
wnds_in_receiver_reserve_on_ahr_before + wnd_to_send
|
||||
);
|
||||
// Receiver's balance is increased
|
||||
assert!(receiver_rocs_after > receiver_rocs_before);
|
||||
assert_eq!(receiver_wnds_after, receiver_wnds_before + wnd_to_send);
|
||||
}
|
||||
|
||||
// ==============================================================================================
|
||||
// ==== Bidirectional Transfer - Native + Teleportable Foreign Assets - Parachain<->AssetHub ====
|
||||
// ==============================================================================================
|
||||
/// Transfers of native asset plus teleportable foreign asset from Parachain to AssetHub and back
|
||||
/// with fees paid using native asset.
|
||||
#[test]
|
||||
fn bidirectional_teleport_foreign_asset_between_para_and_asset_hub_using_explicit_transfer_types() {
|
||||
do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt(
|
||||
para_to_asset_hub_teleport_foreign_assets,
|
||||
asset_hub_to_para_teleport_foreign_assets,
|
||||
);
|
||||
}
|
||||
+1
@@ -13,6 +13,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
mod foreign_assets_transfers;
|
||||
mod reserve_transfer;
|
||||
mod send;
|
||||
mod set_xcm_versions;
|
||||
|
||||
+168
-126
@@ -47,7 +47,7 @@ fn para_to_relay_sender_assertions(t: ParaToRelayTest) {
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pallet_assets::Event::Burned { asset_id, owner, balance, .. }
|
||||
) => {
|
||||
asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"),
|
||||
asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).unwrap(),
|
||||
owner: *owner == t.sender.account_id,
|
||||
balance: *balance == t.args.amount,
|
||||
},
|
||||
@@ -55,70 +55,91 @@ fn para_to_relay_sender_assertions(t: ParaToRelayTest) {
|
||||
);
|
||||
}
|
||||
|
||||
fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) {
|
||||
pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) {
|
||||
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
|
||||
AssetHubRococo::assert_xcm_pallet_attempted_complete(None);
|
||||
|
||||
AssetHubRococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
|
||||
864_610_000,
|
||||
8_799,
|
||||
)));
|
||||
|
||||
let sov_acc_of_dest = AssetHubRococo::sovereign_account_id_of(t.args.dest.clone());
|
||||
for (idx, asset) in t.args.assets.into_inner().into_iter().enumerate() {
|
||||
let expected_id = asset.id.0.clone().try_into().unwrap();
|
||||
let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap();
|
||||
if idx == t.args.fee_asset_item as usize {
|
||||
assert_expected_events!(
|
||||
AssetHubRococo,
|
||||
vec![
|
||||
// Amount of native asset is transferred to Parachain's Sovereign account
|
||||
RuntimeEvent::Balances(
|
||||
pallet_balances::Event::Transfer { from, to, amount }
|
||||
) => {
|
||||
from: *from == t.sender.account_id,
|
||||
to: *to == sov_acc_of_dest,
|
||||
amount: *amount == asset_amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
} else {
|
||||
assert_expected_events!(
|
||||
AssetHubRococo,
|
||||
vec![
|
||||
// Amount of foreign asset is transferred to Parachain's Sovereign account
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pallet_assets::Event::Transferred { asset_id, from, to, amount },
|
||||
) => {
|
||||
asset_id: *asset_id == expected_id,
|
||||
from: *from == t.sender.account_id,
|
||||
to: *to == sov_acc_of_dest,
|
||||
amount: *amount == asset_amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
assert_expected_events!(
|
||||
AssetHubRococo,
|
||||
vec![
|
||||
// Amount to reserve transfer is transferred to Parachain's Sovereign account
|
||||
RuntimeEvent::Balances(
|
||||
pallet_balances::Event::Transfer { from, to, amount }
|
||||
) => {
|
||||
from: *from == t.sender.account_id,
|
||||
to: *to == AssetHubRococo::sovereign_account_id_of(
|
||||
t.args.dest.clone()
|
||||
),
|
||||
amount: *amount == t.args.amount,
|
||||
},
|
||||
// Transport fees are paid
|
||||
RuntimeEvent::PolkadotXcm(
|
||||
pallet_xcm::Event::FeesPaid { .. }
|
||||
) => {},
|
||||
RuntimeEvent::PolkadotXcm(pallet_xcm::Event::FeesPaid { .. }) => {},
|
||||
]
|
||||
);
|
||||
AssetHubRococo::assert_xcm_pallet_sent();
|
||||
}
|
||||
|
||||
fn system_para_to_para_receiver_assertions(t: SystemParaToParaTest) {
|
||||
pub fn system_para_to_para_receiver_assertions(t: SystemParaToParaTest) {
|
||||
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
|
||||
let system_para_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
|
||||
PenpalA::assert_xcmp_queue_success(None);
|
||||
|
||||
assert_expected_events!(
|
||||
PenpalA,
|
||||
vec![
|
||||
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == system_para_native_asset_location,
|
||||
owner: *owner == t.receiver.account_id,
|
||||
},
|
||||
]
|
||||
);
|
||||
for asset in t.args.assets.into_inner().into_iter() {
|
||||
let expected_id = asset.id.0.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 para_to_system_para_sender_assertions(t: ParaToSystemParaTest) {
|
||||
pub fn para_to_system_para_sender_assertions(t: ParaToSystemParaTest) {
|
||||
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
|
||||
PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8_799)));
|
||||
assert_expected_events!(
|
||||
PenpalA,
|
||||
vec![
|
||||
// Amount to reserve transfer is transferred to Parachain's Sovereign account
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pallet_assets::Event::Burned { asset_id, owner, balance, .. }
|
||||
) => {
|
||||
asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"),
|
||||
owner: *owner == t.sender.account_id,
|
||||
balance: *balance == t.args.amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
PenpalA::assert_xcm_pallet_attempted_complete(None);
|
||||
for asset in t.args.assets.into_inner().into_iter() {
|
||||
let expected_id = asset.id.0.try_into().unwrap();
|
||||
let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap();
|
||||
assert_expected_events!(
|
||||
PenpalA,
|
||||
vec![
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pallet_assets::Event::Burned { asset_id, owner, balance }
|
||||
) => {
|
||||
asset_id: *asset_id == expected_id,
|
||||
owner: *owner == t.sender.account_id,
|
||||
balance: *balance == asset_amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn para_to_relay_receiver_assertions(t: ParaToRelayTest) {
|
||||
@@ -150,25 +171,57 @@ fn para_to_relay_receiver_assertions(t: ParaToRelayTest) {
|
||||
);
|
||||
}
|
||||
|
||||
fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) {
|
||||
pub fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) {
|
||||
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
|
||||
let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(
|
||||
AssetHubRococo::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
|
||||
AssetHubRococo::assert_xcmp_queue_success(None);
|
||||
|
||||
let sov_acc_of_penpal = AssetHubRococo::sovereign_account_id_of(t.args.dest.clone());
|
||||
for (idx, asset) in t.args.assets.into_inner().into_iter().enumerate() {
|
||||
let expected_id = asset.id.0.clone().try_into().unwrap();
|
||||
let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap();
|
||||
if idx == t.args.fee_asset_item as usize {
|
||||
assert_expected_events!(
|
||||
AssetHubRococo,
|
||||
vec![
|
||||
// Amount of native is withdrawn from Parachain's Sovereign account
|
||||
RuntimeEvent::Balances(
|
||||
pallet_balances::Event::Burned { who, amount }
|
||||
) => {
|
||||
who: *who == sov_acc_of_penpal.clone().into(),
|
||||
amount: *amount == asset_amount,
|
||||
},
|
||||
RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => {
|
||||
who: *who == t.receiver.account_id,
|
||||
},
|
||||
]
|
||||
);
|
||||
} else {
|
||||
assert_expected_events!(
|
||||
AssetHubRococo,
|
||||
vec![
|
||||
// Amount of foreign asset is transferred from Parachain's Sovereign account
|
||||
// to Receiver's account
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pallet_assets::Event::Burned { asset_id, owner, balance },
|
||||
) => {
|
||||
asset_id: *asset_id == expected_id,
|
||||
owner: *owner == sov_acc_of_penpal,
|
||||
balance: *balance == asset_amount,
|
||||
},
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pallet_assets::Event::Issued { asset_id, owner, amount },
|
||||
) => {
|
||||
asset_id: *asset_id == expected_id,
|
||||
owner: *owner == t.receiver.account_id,
|
||||
amount: *amount == asset_amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
assert_expected_events!(
|
||||
AssetHubRococo,
|
||||
vec![
|
||||
// Amount to reserve transfer is withdrawn from Parachain's Sovereign account
|
||||
RuntimeEvent::Balances(
|
||||
pallet_balances::Event::Burned { who, amount }
|
||||
) => {
|
||||
who: *who == sov_penpal_on_ahr.clone().into(),
|
||||
amount: *amount == t.args.amount,
|
||||
},
|
||||
RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {},
|
||||
RuntimeEvent::MessageQueue(
|
||||
pallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
@@ -212,10 +265,9 @@ fn system_para_to_para_assets_sender_assertions(t: SystemParaToParaTest) {
|
||||
|
||||
fn para_to_system_para_assets_sender_assertions(t: ParaToSystemParaTest) {
|
||||
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
|
||||
let system_para_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let system_para_native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let reservable_asset_location =
|
||||
v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works");
|
||||
v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).unwrap();
|
||||
PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8799)));
|
||||
assert_expected_events!(
|
||||
PenpalA,
|
||||
@@ -246,13 +298,13 @@ fn para_to_system_para_assets_sender_assertions(t: ParaToSystemParaTest) {
|
||||
fn system_para_to_para_assets_receiver_assertions(t: SystemParaToParaTest) {
|
||||
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
|
||||
let system_para_asset_location =
|
||||
v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works");
|
||||
v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).unwrap();
|
||||
PenpalA::assert_xcmp_queue_success(None);
|
||||
assert_expected_events!(
|
||||
PenpalA,
|
||||
vec![
|
||||
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"),
|
||||
asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).unwrap(),
|
||||
owner: *owner == t.receiver.account_id,
|
||||
},
|
||||
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => {
|
||||
@@ -304,7 +356,7 @@ fn relay_to_para_assets_receiver_assertions(t: RelayToParaTest) {
|
||||
PenpalA,
|
||||
vec![
|
||||
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"),
|
||||
asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).unwrap(),
|
||||
owner: *owner == t.receiver.account_id,
|
||||
},
|
||||
RuntimeEvent::MessageQueue(
|
||||
@@ -314,29 +366,27 @@ fn relay_to_para_assets_receiver_assertions(t: RelayToParaTest) {
|
||||
);
|
||||
}
|
||||
|
||||
fn para_to_para_through_relay_sender_assertions(t: ParaToParaThroughRelayTest) {
|
||||
pub fn para_to_para_through_hop_sender_assertions<Hop: Clone>(t: Test<PenpalA, PenpalB, Hop>) {
|
||||
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
|
||||
|
||||
let relay_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
|
||||
PenpalA::assert_xcm_pallet_attempted_complete(None);
|
||||
// XCM sent to relay reserve
|
||||
PenpalA::assert_parachain_system_ump_sent();
|
||||
|
||||
assert_expected_events!(
|
||||
PenpalA,
|
||||
vec![
|
||||
// Amount to reserve transfer is transferred to Parachain's Sovereign account
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pallet_assets::Event::Burned { asset_id, owner, balance },
|
||||
) => {
|
||||
asset_id: *asset_id == relay_asset_location,
|
||||
owner: *owner == t.sender.account_id,
|
||||
balance: *balance == t.args.amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
for asset in t.args.assets.into_inner() {
|
||||
let expected_id = asset.id.0.clone().try_into().unwrap();
|
||||
let amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap();
|
||||
assert_expected_events!(
|
||||
PenpalA,
|
||||
vec![
|
||||
// Amount to reserve transfer is transferred to Parachain's Sovereign account
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pallet_assets::Event::Burned { asset_id, owner, balance },
|
||||
) => {
|
||||
asset_id: *asset_id == expected_id,
|
||||
owner: *owner == t.sender.account_id,
|
||||
balance: *balance == amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) {
|
||||
@@ -369,22 +419,22 @@ fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) {
|
||||
);
|
||||
}
|
||||
|
||||
fn para_to_para_through_relay_receiver_assertions(t: ParaToParaThroughRelayTest) {
|
||||
pub fn para_to_para_through_hop_receiver_assertions<Hop: Clone>(t: Test<PenpalA, PenpalB, Hop>) {
|
||||
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
|
||||
let relay_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
|
||||
PenpalB::assert_xcmp_queue_success(None);
|
||||
|
||||
assert_expected_events!(
|
||||
PenpalB,
|
||||
vec![
|
||||
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == relay_asset_location,
|
||||
owner: *owner == t.receiver.account_id,
|
||||
},
|
||||
]
|
||||
);
|
||||
for asset in t.args.assets.into_inner().into_iter() {
|
||||
let expected_id = asset.id.0.try_into().unwrap();
|
||||
assert_expected_events!(
|
||||
PenpalB,
|
||||
vec![
|
||||
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == expected_id,
|
||||
owner: *owner == t.receiver.account_id,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn relay_to_para_reserve_transfer_assets(t: RelayToParaTest) -> DispatchResult {
|
||||
@@ -526,8 +576,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() {
|
||||
let amount_to_send: Balance = ROCOCO_ED * 1000;
|
||||
|
||||
// Init values fot Parachain
|
||||
let relay_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let relay_native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let receiver = PenpalAReceiver::get();
|
||||
|
||||
// Init Test
|
||||
@@ -577,8 +626,7 @@ fn reserve_transfer_native_asset_from_para_to_relay() {
|
||||
let amount_to_send: Balance = ROCOCO_ED * 1000;
|
||||
let assets: Assets = (Parent, amount_to_send).into();
|
||||
let asset_owner = PenpalAssetOwner::get();
|
||||
let relay_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let relay_native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
|
||||
// fund Parachain's sender account
|
||||
PenpalA::mint_foreign_asset(
|
||||
@@ -654,8 +702,7 @@ fn reserve_transfer_native_asset_from_system_para_to_para() {
|
||||
let assets: Assets = (Parent, amount_to_send).into();
|
||||
|
||||
// Init values for Parachain
|
||||
let system_para_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let system_para_native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let receiver = PenpalAReceiver::get();
|
||||
|
||||
// Init Test
|
||||
@@ -711,8 +758,7 @@ fn reserve_transfer_native_asset_from_para_to_system_para() {
|
||||
let sender = PenpalASender::get();
|
||||
let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000;
|
||||
let assets: Assets = (Parent, amount_to_send).into();
|
||||
let system_para_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let system_para_native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let asset_owner = PenpalAssetOwner::get();
|
||||
|
||||
// fund Parachain's sender account
|
||||
@@ -776,9 +822,9 @@ fn reserve_transfer_native_asset_from_para_to_system_para() {
|
||||
assert!(receiver_balance_after < receiver_balance_before + amount_to_send);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// ======= Reserve Transfers - Non-system Asset - AssetHub<>Parachain ======
|
||||
// =========================================================================
|
||||
// ==================================================================================
|
||||
// ======= Reserve Transfers - Native + Non-system Asset - AssetHub<>Parachain ======
|
||||
// ==================================================================================
|
||||
/// Reserve Transfers of a local asset and native asset from System Parachain to Parachain should
|
||||
/// work
|
||||
#[test]
|
||||
@@ -817,10 +863,9 @@ fn reserve_transfer_assets_from_system_para_to_para() {
|
||||
|
||||
// Init values for Parachain
|
||||
let receiver = PenpalAReceiver::get();
|
||||
let system_para_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let system_para_native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let system_para_foreign_asset_location =
|
||||
v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works");
|
||||
v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).unwrap();
|
||||
|
||||
// Init Test
|
||||
let para_test_args = TestContext {
|
||||
@@ -905,10 +950,9 @@ fn reserve_transfer_assets_from_para_to_system_para() {
|
||||
let penpal_asset_owner = PenpalAssetOwner::get();
|
||||
let penpal_asset_owner_signer = <PenpalA as Chain>::RuntimeOrigin::signed(penpal_asset_owner);
|
||||
let asset_location_on_penpal =
|
||||
v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works");
|
||||
v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).unwrap();
|
||||
let asset_location_on_penpal_latest: Location = asset_location_on_penpal.try_into().unwrap();
|
||||
let system_asset_location_on_penpal =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let system_asset_location_on_penpal = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let assets: Assets = vec![
|
||||
(Parent, fee_amount_to_send).into(),
|
||||
(asset_location_on_penpal_latest, asset_amount_to_send).into(),
|
||||
@@ -938,10 +982,9 @@ fn reserve_transfer_assets_from_para_to_system_para() {
|
||||
let receiver = AssetHubRococoReceiver::get();
|
||||
let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id());
|
||||
let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr);
|
||||
let system_para_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let system_para_native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let system_para_foreign_asset_location =
|
||||
v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works");
|
||||
v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).unwrap();
|
||||
let ah_asset_owner = AssetHubRococoAssetOwner::get();
|
||||
let ah_asset_owner_signer = <AssetHubRococo as Chain>::RuntimeOrigin::signed(ah_asset_owner);
|
||||
|
||||
@@ -1029,15 +1072,14 @@ fn reserve_transfer_assets_from_para_to_system_para() {
|
||||
/// Reserve Transfers of native asset from Parachain to Parachain (through Relay reserve) should
|
||||
/// work
|
||||
#[test]
|
||||
fn reserve_transfer_native_asset_from_para_to_para_trough_relay() {
|
||||
fn reserve_transfer_native_asset_from_para_to_para_through_relay() {
|
||||
// Init values for Parachain Origin
|
||||
let destination = PenpalA::sibling_location_of(PenpalB::para_id());
|
||||
let sender = PenpalASender::get();
|
||||
let amount_to_send: Balance = ROCOCO_ED * 10000;
|
||||
let asset_owner = PenpalAssetOwner::get();
|
||||
let assets = (Parent, amount_to_send).into();
|
||||
let relay_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let relay_native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let sender_as_seen_by_relay = Rococo::child_location_of(PenpalA::para_id());
|
||||
let sov_of_sender_on_relay = Rococo::sovereign_account_id_of(sender_as_seen_by_relay);
|
||||
|
||||
@@ -1074,9 +1116,9 @@ fn reserve_transfer_native_asset_from_para_to_para_trough_relay() {
|
||||
});
|
||||
|
||||
// Set assertions and dispatchables
|
||||
test.set_assertion::<PenpalA>(para_to_para_through_relay_sender_assertions);
|
||||
test.set_assertion::<PenpalA>(para_to_para_through_hop_sender_assertions);
|
||||
test.set_assertion::<Rococo>(para_to_para_relay_hop_assertions);
|
||||
test.set_assertion::<PenpalB>(para_to_para_through_relay_receiver_assertions);
|
||||
test.set_assertion::<PenpalB>(para_to_para_through_hop_receiver_assertions);
|
||||
test.set_dispatchable::<PenpalA>(para_to_para_through_relay_limited_reserve_transfer_assets);
|
||||
test.assert();
|
||||
|
||||
|
||||
+2
-3
@@ -112,10 +112,9 @@ fn swap_locally_on_chain_using_local_assets() {
|
||||
|
||||
#[test]
|
||||
fn swap_locally_on_chain_using_foreign_assets() {
|
||||
let asset_native =
|
||||
Box::new(v3::Location::try_from(RelayLocation::get()).expect("conversion works"));
|
||||
let asset_native = Box::new(v3::Location::try_from(RelayLocation::get()).unwrap());
|
||||
let asset_location_on_penpal =
|
||||
v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion works");
|
||||
v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).unwrap();
|
||||
let foreign_asset_at_asset_hub_rococo =
|
||||
v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())])
|
||||
.appended_with(asset_location_on_penpal)
|
||||
|
||||
+22
-13
@@ -110,8 +110,7 @@ fn para_dest_assertions(t: RelayToSystemParaTest) {
|
||||
|
||||
fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) {
|
||||
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
|
||||
let system_para_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let system_para_native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let expected_asset_id = t.args.asset_id.unwrap();
|
||||
let (_, expected_asset_amount) =
|
||||
non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap();
|
||||
@@ -204,8 +203,7 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) {
|
||||
let (_, expected_asset_amount) =
|
||||
non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap();
|
||||
let checking_account = <PenpalA as PenpalAPallet>::PolkadotXcm::check_account();
|
||||
let system_para_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let system_para_native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
|
||||
PenpalA::assert_xcmp_queue_success(None);
|
||||
|
||||
@@ -414,22 +412,23 @@ fn teleport_to_other_system_parachains_works() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets should work
|
||||
/// (using native reserve-based transfer for fees)
|
||||
#[test]
|
||||
fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() {
|
||||
/// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets while paying
|
||||
/// fees using (reserve transferred) native asset.
|
||||
pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt(
|
||||
para_to_ah_dispatchable: fn(ParaToSystemParaTest) -> DispatchResult,
|
||||
ah_to_para_dispatchable: fn(SystemParaToParaTest) -> DispatchResult,
|
||||
) {
|
||||
// Init values for Parachain
|
||||
let fee_amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000;
|
||||
let asset_location_on_penpal =
|
||||
v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion works");
|
||||
v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).unwrap();
|
||||
let asset_id_on_penpal = match asset_location_on_penpal.last() {
|
||||
Some(v3::Junction::GeneralIndex(id)) => *id as u32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let asset_amount_to_send = ASSET_HUB_ROCOCO_ED * 1000;
|
||||
let asset_owner = PenpalAssetOwner::get();
|
||||
let system_para_native_asset_location =
|
||||
v3::Location::try_from(RelayLocation::get()).expect("conversion works");
|
||||
let system_para_native_asset_location = v3::Location::try_from(RelayLocation::get()).unwrap();
|
||||
let sender = PenpalASender::get();
|
||||
let penpal_check_account = <PenpalA as PenpalAPallet>::PolkadotXcm::check_account();
|
||||
let ah_as_seen_by_penpal = PenpalA::sibling_location_of(AssetHubRococo::para_id());
|
||||
@@ -515,7 +514,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() {
|
||||
|
||||
penpal_to_ah.set_assertion::<PenpalA>(penpal_to_ah_foreign_assets_sender_assertions);
|
||||
penpal_to_ah.set_assertion::<AssetHubRococo>(penpal_to_ah_foreign_assets_receiver_assertions);
|
||||
penpal_to_ah.set_dispatchable::<PenpalA>(para_to_system_para_transfer_assets);
|
||||
penpal_to_ah.set_dispatchable::<PenpalA>(para_to_ah_dispatchable);
|
||||
penpal_to_ah.assert();
|
||||
|
||||
let penpal_sender_balance_after = PenpalA::execute_with(|| {
|
||||
@@ -622,7 +621,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() {
|
||||
|
||||
ah_to_penpal.set_assertion::<AssetHubRococo>(ah_to_penpal_foreign_assets_sender_assertions);
|
||||
ah_to_penpal.set_assertion::<PenpalA>(ah_to_penpal_foreign_assets_receiver_assertions);
|
||||
ah_to_penpal.set_dispatchable::<AssetHubRococo>(system_para_to_para_transfer_assets);
|
||||
ah_to_penpal.set_dispatchable::<AssetHubRococo>(ah_to_para_dispatchable);
|
||||
ah_to_penpal.assert();
|
||||
|
||||
let ah_sender_balance_after = ah_to_penpal.sender.balance;
|
||||
@@ -660,3 +659,13 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() {
|
||||
// Receiver's balance is increased by exact amount
|
||||
assert_eq!(penpal_receiver_assets_after, penpal_receiver_assets_before + asset_amount_to_send);
|
||||
}
|
||||
|
||||
/// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets should work
|
||||
/// (using native reserve-based transfer for fees)
|
||||
#[test]
|
||||
fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() {
|
||||
do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt(
|
||||
para_to_system_para_transfer_assets,
|
||||
system_para_to_para_transfer_assets,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user