use super::xcm_helpers;
use crate::{assert_matches_reserve_asset_deposited_instructions, get_fungible_delivery_fees};
use codec::Encode;
use cumulus_primitives_core::XcmpMessageSource;
use frame_support::{
assert_noop, assert_ok,
traits::{
fungible::Mutate, fungibles::InspectEnumerable, Currency, Get, OnFinalize, OnInitialize,
OriginTrait,
},
weights::Weight,
};
use frame_system::pallet_prelude::BlockNumberFor;
use parachains_common::{AccountId, Balance};
use parachains_runtimes_test_utils::{
assert_metadata, assert_total, mock_open_hrmp_channel, AccountIdOf, BalanceOf,
CollatorSessionKeys, ExtBuilder, SlotDurations, ValidatorIdOf, XcmReceivedFrom,
};
use sp_runtime::{
traits::{Block as BlockT, MaybeEquivalence, StaticLookup, Zero},
DispatchError, Saturating,
};
use xcm::{latest::prelude::*, VersionedAssets};
use xcm_executor::{traits::ConvertLocation, XcmExecutor};
use xcm_runtime_apis::fees::{
runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, Error as XcmPaymentApiError,
};
type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
parachains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
pub fn teleports_for_native_asset_works<
Runtime,
AllPalletsWithoutSystem,
XcmConfig,
CheckingAccount,
WeightToFee,
HrmpChannelOpener,
>(
collator_session_keys: CollatorSessionKeys<Runtime>,
slot_durations: SlotDurations,
existential_deposit: BalanceOf<Runtime>,
target_account: AccountIdOf<Runtime>,
unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
runtime_para_id: u32,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config
+ cumulus_pallet_xcmp_queue::Config
+ pallet_timestamp::Config,
AllPalletsWithoutSystem:
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
AccountIdOf<Runtime>: Into<[u8; 32]>,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
BalanceOf<Runtime>: From<Balance> + Into<u128>,
WeightToFee: frame_support::weights::WeightToFee<Balance = Balance>,
<WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
<Runtime as frame_system::Config>::AccountId:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
From<<Runtime as frame_system::Config>::AccountId>,
<Runtime as frame_system::Config>::AccountId: From<AccountId>,
XcmConfig: xcm_executor::Config,
CheckingAccount: Get<AccountIdOf<Runtime>>,
HrmpChannelOpener: frame_support::inherent::ProvideInherent<
Call = cumulus_pallet_parachain_system::Call<Runtime>,
>,
{
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_keys.collators())
.with_session_keys(collator_session_keys.session_keys())
.with_safe_xcm_version(XCM_VERSION)
.with_para_id(runtime_para_id.into())
.with_tracing()
.build()
.execute_with(|| {
let mut alice = [0u8; 32];
alice[0] = 1;
let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
2,
AccountId::from(alice).into(),
);
assert_eq!(<pallet_balances::Pallet<Runtime>>::free_balance(&target_account), 0.into());
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
0.into()
);
let native_asset_id = Location::parent();
let buy_execution_fee_amount_eta =
WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 1024));
let native_asset_amount_unit = existential_deposit;
let native_asset_amount_received =
native_asset_amount_unit * 10.into() + buy_execution_fee_amount_eta.into();
let xcm = Xcm(vec![
ReceiveTeleportedAsset(Assets::from(vec![Asset {
id: AssetId(native_asset_id.clone()),
fun: Fungible(native_asset_amount_received.into()),
}])),
ClearOrigin,
BuyExecution {
fees: Asset {
id: AssetId(native_asset_id.clone()),
fun: Fungible(buy_execution_fee_amount_eta),
},
weight_limit: Limited(Weight::from_parts(3035310000, 65536)),
},
DepositAsset {
assets: Wild(AllCounted(1)),
beneficiary: Location {
parents: 0,
interior: [AccountId32 {
network: None,
id: target_account.clone().into(),
}]
.into(),
},
},
ExpectTransactStatus(MaybeErrorCode::Success),
]);
let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
Parent,
xcm,
&mut hash,
RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Parent),
Weight::zero(),
);
assert_ok!(outcome.ensure_complete());
assert_ne!(<pallet_balances::Pallet<Runtime>>::free_balance(&target_account), 0.into());
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
0.into()
);
{
let dest = Location::parent();
let mut dest_beneficiary = Location::parent()
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
let target_account_balance_before_teleport =
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
let native_asset_to_teleport_away = native_asset_amount_unit * 3.into();
assert!(
native_asset_to_teleport_away <
target_account_balance_before_teleport - existential_deposit
);
let delivery_fees =
xcm_helpers::teleport_assets_delivery_fees::<XcmConfig::XcmSender>(
(native_asset_id.clone(), native_asset_to_teleport_away.into()).into(),
0,
Unlimited,
dest_beneficiary.clone(),
dest.clone(),
);
<pallet_balances::Pallet<Runtime>>::mint_into(
&target_account,
delivery_fees.into(),
)
.unwrap();
assert_ok!(RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
dest,
dest_beneficiary,
(native_asset_id.clone(), native_asset_to_teleport_away.into()),
None,
included_head.clone(),
&alice,
&slot_durations,
));
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
target_account_balance_before_teleport - native_asset_to_teleport_away
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
0.into()
);
RuntimeHelper::<Runtime>::assert_pallet_xcm_event_outcome(
&unwrap_pallet_xcm_event,
|outcome| {
assert_ok!(outcome.ensure_complete());
},
);
}
{
let other_para_id = 2345;
let dest = Location::new(1, [Parachain(other_para_id)]);
let mut dest_beneficiary = Location::new(1, [Parachain(other_para_id)])
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
let target_account_balance_before_teleport =
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
let native_asset_to_teleport_away = native_asset_amount_unit * 3.into();
assert!(
native_asset_to_teleport_away <
target_account_balance_before_teleport - existential_deposit
);
assert_eq!(
RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
dest,
dest_beneficiary,
(native_asset_id, native_asset_to_teleport_away.into()),
Some((runtime_para_id, other_para_id)),
included_head,
&alice,
&slot_durations,
),
Err(DispatchError::Module(sp_runtime::ModuleError {
index: 31,
error: [2, 0, 0, 0,],
message: Some("Filtered",),
},),)
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
target_account_balance_before_teleport
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
0.into()
);
}
})
}
#[macro_export]
macro_rules! include_teleports_for_native_asset_works(
(
$runtime:path,
$all_pallets_without_system:path,
$xcm_config:path,
$checking_account:path,
$weight_to_fee:path,
$hrmp_channel_opener:path,
$collator_session_key:expr,
$slot_durations:expr,
$existential_deposit:expr,
$unwrap_pallet_xcm_event:expr,
$runtime_para_id:expr
) => {
#[test]
fn teleports_for_native_asset_works() {
const BOB: [u8; 32] = [2u8; 32];
let target_account = parachains_common::AccountId::from(BOB);
$crate::test_cases::teleports_for_native_asset_works::<
$runtime,
$all_pallets_without_system,
$xcm_config,
$checking_account,
$weight_to_fee,
$hrmp_channel_opener
>(
$collator_session_key,
$slot_durations,
$existential_deposit,
target_account,
$unwrap_pallet_xcm_event,
$runtime_para_id
)
}
}
);
pub fn teleports_for_foreign_assets_works<
Runtime,
AllPalletsWithoutSystem,
XcmConfig,
CheckingAccount,
WeightToFee,
HrmpChannelOpener,
SovereignAccountOf,
ForeignAssetsPalletInstance,
>(
collator_session_keys: CollatorSessionKeys<Runtime>,
slot_durations: SlotDurations,
target_account: AccountIdOf<Runtime>,
existential_deposit: BalanceOf<Runtime>,
asset_owner: AccountIdOf<Runtime>,
unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
unwrap_xcmp_queue_event: Box<
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
>,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config
+ cumulus_pallet_xcmp_queue::Config
+ pallet_assets::Config<ForeignAssetsPalletInstance>
+ pallet_timestamp::Config,
AllPalletsWithoutSystem:
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
AccountIdOf<Runtime>: Into<[u8; 32]>,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
BalanceOf<Runtime>: From<Balance>,
XcmConfig: xcm_executor::Config,
CheckingAccount: Get<AccountIdOf<Runtime>>,
HrmpChannelOpener: frame_support::inherent::ProvideInherent<
Call = cumulus_pallet_parachain_system::Call<Runtime>,
>,
WeightToFee: frame_support::weights::WeightToFee<Balance = Balance>,
<WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
SovereignAccountOf: ConvertLocation<AccountIdOf<Runtime>>,
<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetId:
From<xcm::v5::Location> + Into<xcm::v5::Location>,
<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetIdParameter:
From<xcm::v5::Location> + Into<xcm::v5::Location>,
<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::Balance:
From<Balance> + Into<u128>,
<Runtime as frame_system::Config>::AccountId:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
From<<Runtime as frame_system::Config>::AccountId>,
<Runtime as frame_system::Config>::AccountId: From<AccountId>,
ForeignAssetsPalletInstance: 'static,
{
let foreign_para_id = 2222;
let foreign_asset_id_location = xcm::v5::Location {
parents: 1,
interior: [
xcm::v5::Junction::Parachain(foreign_para_id),
xcm::v5::Junction::GeneralIndex(1234567),
]
.into(),
};
let foreign_creator = Location { parents: 1, interior: [Parachain(foreign_para_id)].into() };
let foreign_creator_as_account_id =
SovereignAccountOf::convert_location(&foreign_creator).expect("");
let buy_execution_fee_amount =
WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 0));
let buy_execution_fee =
Asset { id: AssetId(Location::parent()), fun: Fungible(buy_execution_fee_amount) };
let teleported_foreign_asset_amount = 10_000_000_000_000;
let runtime_para_id = 1000;
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_keys.collators())
.with_session_keys(collator_session_keys.session_keys())
.with_balances(vec![
(
foreign_creator_as_account_id,
existential_deposit + (buy_execution_fee_amount * 2).into(),
),
(target_account.clone(), existential_deposit),
(CheckingAccount::get(), existential_deposit),
])
.with_safe_xcm_version(XCM_VERSION)
.with_para_id(runtime_para_id.into())
.with_tracing()
.build()
.execute_with(|| {
let mut alice = [0u8; 32];
alice[0] = 1;
let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
2,
AccountId::from(alice).into(),
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
existential_deposit
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
existential_deposit
);
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
foreign_asset_id_location.clone().into(),
&target_account
),
0.into()
);
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
foreign_asset_id_location.clone().into(),
&CheckingAccount::get()
),
0.into()
);
assert_total::<
pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
AccountIdOf<Runtime>,
>(foreign_asset_id_location.clone(), 0, 0);
let asset_minimum_asset_balance = 3333333_u128;
assert_ok!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::force_create(
RuntimeHelper::<Runtime>::root_origin(),
foreign_asset_id_location.clone().into(),
asset_owner.into(),
false,
asset_minimum_asset_balance.into()
)
);
assert_total::<
pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
AccountIdOf<Runtime>,
>(foreign_asset_id_location.clone(), 0, 0);
assert!(teleported_foreign_asset_amount > asset_minimum_asset_balance);
let xcm = Xcm(vec![
WithdrawAsset(buy_execution_fee.clone().into()),
BuyExecution {
fees: Asset {
id: AssetId(Location::parent()),
fun: Fungible(buy_execution_fee_amount),
},
weight_limit: Limited(Weight::from_parts(403531000, 65536)),
},
ReceiveTeleportedAsset(Assets::from(vec![Asset {
id: AssetId(foreign_asset_id_location.clone()),
fun: Fungible(teleported_foreign_asset_amount),
}])),
DepositAsset {
assets: Wild(AllOf {
id: AssetId(foreign_asset_id_location.clone()),
fun: WildFungibility::Fungible,
}),
beneficiary: Location {
parents: 0,
interior: [AccountId32 {
network: None,
id: target_account.clone().into(),
}]
.into(),
},
},
ExpectTransactStatus(MaybeErrorCode::Success),
]);
let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
foreign_creator,
xcm,
&mut hash,
RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
Weight::zero(),
);
assert_ok!(outcome.ensure_complete());
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
existential_deposit
);
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
foreign_asset_id_location.clone().into(),
&target_account
),
teleported_foreign_asset_amount.into()
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
existential_deposit
);
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
foreign_asset_id_location.clone().into(),
&CheckingAccount::get()
),
0.into()
);
assert_total::<
pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
AccountIdOf<Runtime>,
>(
foreign_asset_id_location.clone(),
teleported_foreign_asset_amount,
teleported_foreign_asset_amount,
);
{
let dest = Location::new(1, [Parachain(foreign_para_id)]);
let mut dest_beneficiary = Location::new(1, [Parachain(foreign_para_id)])
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
let target_account_balance_before_teleport =
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
foreign_asset_id_location.clone().into(),
&target_account,
);
let asset_to_teleport_away = asset_minimum_asset_balance * 3;
assert!(
asset_to_teleport_away <
(target_account_balance_before_teleport -
asset_minimum_asset_balance.into())
.into()
);
let delivery_fees =
xcm_helpers::teleport_assets_delivery_fees::<XcmConfig::XcmSender>(
(foreign_asset_id_location.clone(), asset_to_teleport_away).into(),
0,
Unlimited,
dest_beneficiary.clone(),
dest.clone(),
);
<pallet_balances::Pallet<Runtime>>::mint_into(
&target_account,
delivery_fees.into(),
)
.unwrap();
assert_ok!(RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
dest,
dest_beneficiary,
(foreign_asset_id_location.clone(), asset_to_teleport_away),
Some((runtime_para_id, foreign_para_id)),
included_head,
&alice,
&slot_durations,
));
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
foreign_asset_id_location.clone().into(),
&target_account
),
(target_account_balance_before_teleport - asset_to_teleport_away.into())
);
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
foreign_asset_id_location.clone().into(),
&CheckingAccount::get()
),
0.into()
);
assert_total::<
pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
AccountIdOf<Runtime>,
>(
foreign_asset_id_location.clone(),
teleported_foreign_asset_amount - asset_to_teleport_away,
teleported_foreign_asset_amount - asset_to_teleport_away,
);
RuntimeHelper::<Runtime>::assert_pallet_xcm_event_outcome(
&unwrap_pallet_xcm_event,
|outcome| {
assert_ok!(outcome.ensure_complete());
},
);
assert!(RuntimeHelper::<Runtime>::xcmp_queue_message_sent(unwrap_xcmp_queue_event)
.is_some());
}
})
}
#[macro_export]
macro_rules! include_teleports_for_foreign_assets_works(
(
$runtime:path,
$all_pallets_without_system:path,
$xcm_config:path,
$checking_account:path,
$weight_to_fee:path,
$hrmp_channel_opener:path,
$sovereign_account_of:path,
$assets_pallet_instance:path,
$collator_session_key:expr,
$slot_durations:expr,
$existential_deposit:expr,
$unwrap_pallet_xcm_event:expr,
$unwrap_xcmp_queue_event:expr
) => {
#[test]
fn teleports_for_foreign_assets_works() {
const BOB: [u8; 32] = [2u8; 32];
let target_account = parachains_common::AccountId::from(BOB);
const SOME_ASSET_OWNER: [u8; 32] = [5u8; 32];
let asset_owner = parachains_common::AccountId::from(SOME_ASSET_OWNER);
$crate::test_cases::teleports_for_foreign_assets_works::<
$runtime,
$all_pallets_without_system,
$xcm_config,
$checking_account,
$weight_to_fee,
$hrmp_channel_opener,
$sovereign_account_of,
$assets_pallet_instance
>(
$collator_session_key,
$slot_durations,
target_account,
$existential_deposit,
asset_owner,
$unwrap_pallet_xcm_event,
$unwrap_xcmp_queue_event
)
}
}
);
pub fn asset_transactor_transfer_with_local_consensus_currency_works<Runtime, XcmConfig>(
collator_session_keys: CollatorSessionKeys<Runtime>,
source_account: AccountIdOf<Runtime>,
target_account: AccountIdOf<Runtime>,
existential_deposit: BalanceOf<Runtime>,
additional_checks_before: Box<dyn Fn()>,
additional_checks_after: Box<dyn Fn()>,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config
+ pallet_timestamp::Config,
AccountIdOf<Runtime>: Into<[u8; 32]>,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
BalanceOf<Runtime>: From<Balance>,
XcmConfig: xcm_executor::Config,
<Runtime as pallet_balances::Config>::Balance: From<Balance> + Into<u128>,
<Runtime as frame_system::Config>::AccountId:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
From<<Runtime as frame_system::Config>::AccountId>,
{
let unit = existential_deposit;
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_keys.collators())
.with_session_keys(collator_session_keys.session_keys())
.with_balances(vec![(source_account.clone(), (BalanceOf::<Runtime>::from(10_u128) * unit))])
.with_tracing()
.build()
.execute_with(|| {
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&source_account),
(BalanceOf::<Runtime>::from(10_u128) * unit)
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
(BalanceOf::<Runtime>::zero() * unit)
);
additional_checks_before();
let _ = RuntimeHelper::<XcmConfig>::do_transfer(
Location {
parents: 0,
interior: [AccountId32 { network: None, id: source_account.clone().into() }]
.into(),
},
Location {
parents: 0,
interior: [AccountId32 { network: None, id: target_account.clone().into() }]
.into(),
},
(
Location { parents: 1, interior: Here },
(BalanceOf::<Runtime>::from(1_u128) * unit).into(),
),
)
.expect("no error");
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(source_account),
(BalanceOf::<Runtime>::from(9_u128) * unit)
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(target_account),
(BalanceOf::<Runtime>::from(1_u128) * unit)
);
additional_checks_after();
})
}
#[macro_export]
macro_rules! include_asset_transactor_transfer_with_local_consensus_currency_works(
(
$runtime:path,
$xcm_config:path,
$collator_session_key:expr,
$existential_deposit:expr,
$additional_checks_before:expr,
$additional_checks_after:expr
) => {
#[test]
fn asset_transactor_transfer_with_local_consensus_currency_works() {
const ALICE: [u8; 32] = [1u8; 32];
let source_account = parachains_common::AccountId::from(ALICE);
const BOB: [u8; 32] = [2u8; 32];
let target_account = parachains_common::AccountId::from(BOB);
$crate::test_cases::asset_transactor_transfer_with_local_consensus_currency_works::<
$runtime,
$xcm_config
>(
$collator_session_key,
source_account,
target_account,
$existential_deposit,
$additional_checks_before,
$additional_checks_after
)
}
}
);
pub fn asset_transactor_transfer_with_pallet_assets_instance_works<
Runtime,
XcmConfig,
AssetsPalletInstance,
AssetId,
AssetIdConverter,
>(
collator_session_keys: CollatorSessionKeys<Runtime>,
existential_deposit: BalanceOf<Runtime>,
asset_id: AssetId,
asset_owner: AccountIdOf<Runtime>,
alice_account: AccountIdOf<Runtime>,
bob_account: AccountIdOf<Runtime>,
charlie_account: AccountIdOf<Runtime>,
additional_checks_before: Box<dyn Fn()>,
additional_checks_after: Box<dyn Fn()>,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config
+ pallet_assets::Config<AssetsPalletInstance>
+ pallet_timestamp::Config,
AccountIdOf<Runtime>: Into<[u8; 32]>,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
BalanceOf<Runtime>: From<Balance>,
XcmConfig: xcm_executor::Config,
<Runtime as pallet_assets::Config<AssetsPalletInstance>>::AssetId:
From<AssetId> + Into<AssetId>,
<Runtime as pallet_assets::Config<AssetsPalletInstance>>::AssetIdParameter:
From<AssetId> + Into<AssetId>,
<Runtime as pallet_assets::Config<AssetsPalletInstance>>::Balance: From<Balance> + Into<u128>,
<Runtime as frame_system::Config>::AccountId:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
From<<Runtime as frame_system::Config>::AccountId>,
AssetsPalletInstance: 'static,
AssetId: Clone,
AssetIdConverter: MaybeEquivalence<Location, AssetId>,
{
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_keys.collators())
.with_session_keys(collator_session_keys.session_keys())
.with_balances(vec![
(asset_owner.clone(), existential_deposit),
(alice_account.clone(), existential_deposit),
(bob_account.clone(), existential_deposit),
])
.with_tracing()
.build()
.execute_with(|| {
let asset_minimum_asset_balance = 3333333_u128;
let asset_id_as_location = AssetIdConverter::convert_back(&asset_id).unwrap();
assert_ok!(<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::force_create(
RuntimeHelper::<Runtime>::root_origin(),
asset_id.clone().into(),
asset_owner.clone().into(),
false,
asset_minimum_asset_balance.into()
));
assert_ok!(<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::mint(
RuntimeHelper::<Runtime>::origin_of(asset_owner.clone()),
asset_id.clone().into(),
alice_account.clone().into(),
(6 * asset_minimum_asset_balance).into()
));
assert_eq!(
<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
asset_id.clone().into(),
&alice_account
),
(6 * asset_minimum_asset_balance).into()
);
assert_eq!(
<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
asset_id.clone().into(),
&bob_account
),
0.into()
);
assert_eq!(
<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
asset_id.clone().into(),
&charlie_account
),
0.into()
);
assert_eq!(
<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
asset_id.clone().into(),
&asset_owner
),
0.into()
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
existential_deposit
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&bob_account),
existential_deposit
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&charlie_account),
0.into()
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&asset_owner),
existential_deposit
);
additional_checks_before();
assert_noop!(
RuntimeHelper::<XcmConfig>::do_transfer(
Location {
parents: 0,
interior: [AccountId32 { network: None, id: alice_account.clone().into() }]
.into(),
},
Location {
parents: 0,
interior: [AccountId32 {
network: None,
id: charlie_account.clone().into()
}]
.into(),
},
(asset_id_as_location.clone(), asset_minimum_asset_balance),
),
XcmError::FailedToTransactAsset(Into::<&str>::into(
sp_runtime::TokenError::CannotCreate
))
);
assert!(matches!(
RuntimeHelper::<XcmConfig>::do_transfer(
Location {
parents: 0,
interior: [AccountId32 { network: None, id: alice_account.clone().into() }]
.into(),
},
Location {
parents: 0,
interior: [AccountId32 { network: None, id: bob_account.clone().into() }]
.into(),
},
(asset_id_as_location, asset_minimum_asset_balance),
),
Ok(_)
));
assert_eq!(
<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
asset_id.clone().into(),
&alice_account
),
(5 * asset_minimum_asset_balance).into()
);
assert_eq!(
<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
asset_id.clone().into(),
&bob_account
),
asset_minimum_asset_balance.into()
);
assert_eq!(
<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
asset_id.clone().into(),
&charlie_account
),
0.into()
);
assert_eq!(
<pallet_assets::Pallet<Runtime, AssetsPalletInstance>>::balance(
asset_id.into(),
&asset_owner
),
0.into()
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
existential_deposit
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&bob_account),
existential_deposit
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&charlie_account),
0.into()
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&asset_owner),
existential_deposit
);
additional_checks_after();
})
}
#[macro_export]
macro_rules! include_asset_transactor_transfer_with_pallet_assets_instance_works(
(
$test_name:tt,
$runtime:path,
$xcm_config:path,
$assets_pallet_instance:path,
$asset_id:path,
$asset_id_converter:path,
$collator_session_key:expr,
$existential_deposit:expr,
$tested_asset_id:expr,
$additional_checks_before:expr,
$additional_checks_after:expr
) => {
#[test]
fn $test_name() {
const SOME_ASSET_OWNER: [u8; 32] = [5u8; 32];
let asset_owner = parachains_common::AccountId::from(SOME_ASSET_OWNER);
const ALICE: [u8; 32] = [1u8; 32];
let alice_account = parachains_common::AccountId::from(ALICE);
const BOB: [u8; 32] = [2u8; 32];
let bob_account = parachains_common::AccountId::from(BOB);
const CHARLIE: [u8; 32] = [3u8; 32];
let charlie_account = parachains_common::AccountId::from(CHARLIE);
$crate::test_cases::asset_transactor_transfer_with_pallet_assets_instance_works::<
$runtime,
$xcm_config,
$assets_pallet_instance,
$asset_id,
$asset_id_converter
>(
$collator_session_key,
$existential_deposit,
$tested_asset_id,
asset_owner,
alice_account,
bob_account,
charlie_account,
$additional_checks_before,
$additional_checks_after
)
}
}
);
pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works<
Runtime,
XcmConfig,
WeightToFee,
SovereignAccountOf,
ForeignAssetsPalletInstance,
AssetId,
AssetIdConverter,
>(
collator_session_keys: CollatorSessionKeys<Runtime>,
existential_deposit: BalanceOf<Runtime>,
asset_deposit: BalanceOf<Runtime>,
metadata_deposit_base: BalanceOf<Runtime>,
metadata_deposit_per_byte: BalanceOf<Runtime>,
alice_account: AccountIdOf<Runtime>,
bob_account: AccountIdOf<Runtime>,
runtime_call_encode: Box<
dyn Fn(pallet_assets::Call<Runtime, ForeignAssetsPalletInstance>) -> Vec<u8>,
>,
unwrap_pallet_assets_event: Box<
dyn Fn(Vec<u8>) -> Option<pallet_assets::Event<Runtime, ForeignAssetsPalletInstance>>,
>,
additional_checks_before: Box<dyn Fn()>,
additional_checks_after: Box<dyn Fn()>,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config
+ pallet_assets::Config<ForeignAssetsPalletInstance>
+ pallet_timestamp::Config,
AccountIdOf<Runtime>: Into<[u8; 32]>,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
BalanceOf<Runtime>: From<Balance>,
XcmConfig: xcm_executor::Config,
WeightToFee: frame_support::weights::WeightToFee<Balance = Balance>,
<WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
SovereignAccountOf: ConvertLocation<AccountIdOf<Runtime>>,
<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetId:
From<AssetId> + Into<AssetId>,
<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetIdParameter:
From<AssetId> + Into<AssetId>,
<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::Balance:
From<Balance> + Into<u128>,
<Runtime as frame_system::Config>::AccountId:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
From<<Runtime as frame_system::Config>::AccountId>,
ForeignAssetsPalletInstance: 'static,
AssetId: Clone,
AssetIdConverter: MaybeEquivalence<Location, AssetId>,
{
let foreign_asset_id_location = Location::new(1, [Parachain(2222), GeneralIndex(1234567)]);
let asset_id = AssetIdConverter::convert(&foreign_asset_id_location).unwrap();
let foreign_creator = Location { parents: 1, interior: [Parachain(2222)].into() };
let foreign_creator_as_account_id =
SovereignAccountOf::convert_location(&foreign_creator).expect("");
let buy_execution_fee_amount =
WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 0));
let buy_execution_fee =
Asset { id: AssetId(Location::parent()), fun: Fungible(buy_execution_fee_amount) };
const ASSET_NAME: &str = "My super coin";
const ASSET_SYMBOL: &str = "MY_S_COIN";
let metadata_deposit_per_byte_eta = metadata_deposit_per_byte
.saturating_mul(((ASSET_NAME.len() + ASSET_SYMBOL.len()) as u128).into());
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_keys.collators())
.with_session_keys(collator_session_keys.session_keys())
.with_balances(vec![(
foreign_creator_as_account_id.clone(),
existential_deposit +
asset_deposit +
metadata_deposit_base +
metadata_deposit_per_byte_eta +
buy_execution_fee_amount.into() +
buy_execution_fee_amount.into(),
)])
.with_tracing()
.build()
.execute_with(|| {
assert!(<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::asset_ids()
.collect::<Vec<_>>()
.is_empty());
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&foreign_creator_as_account_id),
existential_deposit +
asset_deposit + metadata_deposit_base +
metadata_deposit_per_byte_eta +
buy_execution_fee_amount.into() +
buy_execution_fee_amount.into()
);
additional_checks_before();
let foreign_asset_create = runtime_call_encode(pallet_assets::Call::<
Runtime,
ForeignAssetsPalletInstance,
>::create {
id: asset_id.clone().into(),
admin: foreign_creator_as_account_id.clone().into(),
min_balance: 1.into(),
});
let foreign_asset_set_metadata = runtime_call_encode(pallet_assets::Call::<
Runtime,
ForeignAssetsPalletInstance,
>::set_metadata {
id: asset_id.clone().into(),
name: Vec::from(ASSET_NAME),
symbol: Vec::from(ASSET_SYMBOL),
decimals: 12,
});
let foreign_asset_set_team = runtime_call_encode(pallet_assets::Call::<
Runtime,
ForeignAssetsPalletInstance,
>::set_team {
id: asset_id.clone().into(),
issuer: foreign_creator_as_account_id.clone().into(),
admin: foreign_creator_as_account_id.clone().into(),
freezer: bob_account.clone().into(),
});
let xcm = Xcm(vec![
WithdrawAsset(buy_execution_fee.clone().into()),
BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited },
Transact {
origin_kind: OriginKind::Xcm,
call: foreign_asset_create.into(),
fallback_max_weight: None,
},
Transact {
origin_kind: OriginKind::SovereignAccount,
call: foreign_asset_set_metadata.into(),
fallback_max_weight: None,
},
Transact {
origin_kind: OriginKind::SovereignAccount,
call: foreign_asset_set_team.into(),
fallback_max_weight: None,
},
ExpectTransactStatus(MaybeErrorCode::Success),
]);
let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
foreign_creator.clone(),
xcm,
&mut hash,
RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
Weight::zero(),
);
assert_ok!(outcome.ensure_complete());
let mut events = <frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_pallet_assets_event(e.event.encode()));
assert!(events.any(|e| matches!(e, pallet_assets::Event::Created { .. })));
assert!(events.any(|e| matches!(e, pallet_assets::Event::MetadataSet { .. })));
assert!(events.any(|e| matches!(e, pallet_assets::Event::TeamChanged { .. })));
assert!(!<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::asset_ids()
.collect::<Vec<_>>()
.is_empty());
use frame_support::traits::fungibles::roles::Inspect as InspectRoles;
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::owner(
asset_id.clone().into()
),
Some(foreign_creator_as_account_id.clone())
);
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::admin(
asset_id.clone().into()
),
Some(foreign_creator_as_account_id.clone())
);
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::issuer(
asset_id.clone().into()
),
Some(foreign_creator_as_account_id.clone())
);
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::freezer(
asset_id.clone().into()
),
Some(bob_account.clone())
);
assert!(
<pallet_balances::Pallet<Runtime>>::free_balance(&foreign_creator_as_account_id) >=
existential_deposit + buy_execution_fee_amount.into(),
"Free balance: {:?} should be ge {:?}",
<pallet_balances::Pallet<Runtime>>::free_balance(&foreign_creator_as_account_id),
existential_deposit + buy_execution_fee_amount.into()
);
assert_metadata::<
pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>,
AccountIdOf<Runtime>,
>(asset_id.clone(), ASSET_NAME, ASSET_SYMBOL, 12);
assert_noop!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::freeze(
RuntimeHelper::<Runtime>::origin_of(bob_account),
asset_id.clone().into(),
alice_account.clone().into()
),
pallet_assets::Error::<Runtime, ForeignAssetsPalletInstance>::NoAccount
);
assert_noop!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::freeze(
RuntimeHelper::<Runtime>::origin_of(foreign_creator_as_account_id.clone()),
asset_id.into(),
alice_account.into()
),
pallet_assets::Error::<Runtime, ForeignAssetsPalletInstance>::NoPermission
);
let foreign_asset_id_location =
Location { parents: 1, interior: [Parachain(3333), GeneralIndex(1234567)].into() };
let asset_id = AssetIdConverter::convert(&foreign_asset_id_location).unwrap();
let foreign_asset_create = runtime_call_encode(pallet_assets::Call::<
Runtime,
ForeignAssetsPalletInstance,
>::create {
id: asset_id.into(),
admin: foreign_creator_as_account_id.clone().into(),
min_balance: 1.into(),
});
let xcm = Xcm(vec![
WithdrawAsset(buy_execution_fee.clone().into()),
BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited },
Transact {
origin_kind: OriginKind::Xcm,
call: foreign_asset_create.into(),
fallback_max_weight: None,
},
ExpectTransactStatus(MaybeErrorCode::from(DispatchError::BadOrigin.encode())),
]);
let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
foreign_creator,
xcm,
&mut hash,
RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
Weight::zero(),
);
assert_ok!(outcome.ensure_complete());
additional_checks_after();
})
}
#[macro_export]
macro_rules! include_create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works(
(
$runtime:path,
$xcm_config:path,
$weight_to_fee:path,
$sovereign_account_of:path,
$assets_pallet_instance:path,
$asset_id:path,
$asset_id_converter:path,
$collator_session_key:expr,
$existential_deposit:expr,
$asset_deposit:expr,
$metadata_deposit_base:expr,
$metadata_deposit_per_byte:expr,
$runtime_call_encode:expr,
$unwrap_pallet_assets_event:expr,
$additional_checks_before:expr,
$additional_checks_after:expr
) => {
#[test]
fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works() {
const ALICE: [u8; 32] = [1u8; 32];
let alice_account = parachains_common::AccountId::from(ALICE);
const BOB: [u8; 32] = [2u8; 32];
let bob_account = parachains_common::AccountId::from(BOB);
$crate::test_cases::create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works::<
$runtime,
$xcm_config,
$weight_to_fee,
$sovereign_account_of,
$assets_pallet_instance,
$asset_id,
$asset_id_converter
>(
$collator_session_key,
$existential_deposit,
$asset_deposit,
$metadata_deposit_base,
$metadata_deposit_per_byte,
alice_account,
bob_account,
$runtime_call_encode,
$unwrap_pallet_assets_event,
$additional_checks_before,
$additional_checks_after
)
}
}
);
pub fn reserve_transfer_native_asset_to_non_teleport_para_works<
Runtime,
AllPalletsWithoutSystem,
XcmConfig,
HrmpChannelOpener,
HrmpChannelSource,
LocationToAccountId,
>(
collator_session_keys: CollatorSessionKeys<Runtime>,
slot_durations: SlotDurations,
existential_deposit: BalanceOf<Runtime>,
alice_account: AccountIdOf<Runtime>,
unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
unwrap_xcmp_queue_event: Box<
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
>,
weight_limit: WeightLimit,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config
+ cumulus_pallet_xcmp_queue::Config
+ pallet_timestamp::Config,
AllPalletsWithoutSystem:
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
AccountIdOf<Runtime>: Into<[u8; 32]>,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
BalanceOf<Runtime>: From<Balance>,
<Runtime as pallet_balances::Config>::Balance: From<Balance> + Into<u128>,
XcmConfig: xcm_executor::Config,
LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
<Runtime as frame_system::Config>::AccountId:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
From<<Runtime as frame_system::Config>::AccountId>,
<Runtime as frame_system::Config>::AccountId: From<AccountId>,
HrmpChannelOpener: frame_support::inherent::ProvideInherent<
Call = cumulus_pallet_parachain_system::Call<Runtime>,
>,
HrmpChannelSource: XcmpMessageSource,
{
let runtime_para_id = 1000;
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_keys.collators())
.with_session_keys(collator_session_keys.session_keys())
.with_tracing()
.with_safe_xcm_version(3)
.with_para_id(runtime_para_id.into())
.build()
.execute_with(|| {
let mut alice = [0u8; 32];
alice[0] = 1;
let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
2,
AccountId::from(alice).into(),
);
let other_para_id = 2345;
let native_asset = Location::parent();
let dest = Location::new(1, [Parachain(other_para_id)]);
let mut dest_beneficiary = Location::new(1, [Parachain(other_para_id)])
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, &XcmConfig::UniversalLocation::get()).unwrap();
let reserve_account = LocationToAccountId::convert_location(&dest)
.expect("Sovereign account for reserves");
let balance_to_transfer = 1_000_000_000_000_u128;
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
runtime_para_id.into(),
other_para_id.into(),
included_head,
&alice,
&slot_durations,
);
let delivery_fees_buffer = 40_000_000_000u128;
let alice_account_init_balance = existential_deposit.saturating_mul(2.into()) +
balance_to_transfer.into() +
delivery_fees_buffer.into();
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
&alice_account,
alice_account_init_balance,
);
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
&reserve_account,
existential_deposit,
);
assert!(
(<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account) -
balance_to_transfer.into()) >=
existential_deposit
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
existential_deposit
);
let asset_to_transfer =
Asset { fun: Fungible(balance_to_transfer.into()), id: AssetId(native_asset) };
assert_ok!(<pallet_xcm::Pallet<Runtime>>::limited_reserve_transfer_assets(
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(alice_account.clone()),
Box::new(dest.clone().into_versioned()),
Box::new(dest_beneficiary.clone().into_versioned()),
Box::new(VersionedAssets::from(Assets::from(asset_to_transfer))),
0,
weight_limit,
));
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::assert_pallet_xcm_event_outcome(
&unwrap_pallet_xcm_event,
|outcome| {
assert_ok!(outcome.ensure_complete());
},
);
let xcm_sent_message_hash = <frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
.find_map(|e| match e {
cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } =>
Some(message_hash),
_ => None,
});
let xcm_sent = RuntimeHelper::<HrmpChannelSource, AllPalletsWithoutSystem>::take_xcm(
other_para_id.into(),
)
.unwrap();
let delivery_fees = get_fungible_delivery_fees::<
<XcmConfig as xcm_executor::Config>::XcmSender,
>(dest.clone(), Xcm::try_from(xcm_sent.clone()).unwrap());
assert_eq!(
xcm_sent_message_hash,
Some(xcm_sent.using_encoded(sp_io::hashing::blake2_256))
);
let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm");
println!("reserve_transfer_native_asset_works sent xcm: {:?}", xcm_sent);
let reserve_assets_deposited = Assets::from(vec![Asset {
id: AssetId(Location { parents: 1, interior: Here }),
fun: Fungible(1000000000000),
}]);
assert_matches_reserve_asset_deposited_instructions(
&mut xcm_sent,
&reserve_assets_deposited,
&dest_beneficiary,
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
alice_account_init_balance - balance_to_transfer.into() - delivery_fees.into()
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
existential_deposit + balance_to_transfer.into()
);
})
}
pub fn xcm_payment_api_with_pools_works<Runtime, RuntimeCall, RuntimeOrigin, Block>()
where
Runtime: XcmPaymentApiV1<Block>
+ frame_system::Config<RuntimeOrigin = RuntimeOrigin, AccountId = AccountId>
+ pallet_balances::Config<Balance = u128>
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config
+ cumulus_pallet_xcmp_queue::Config
+ pallet_timestamp::Config
+ pallet_assets::Config<
pallet_assets::Instance1,
AssetId = u32,
Balance = <Runtime as pallet_balances::Config>::Balance,
> + pallet_asset_conversion::Config<
AssetKind = xcm::v5::Location,
Balance = <Runtime as pallet_balances::Config>::Balance,
>,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
RuntimeOrigin: OriginTrait<AccountId = <Runtime as frame_system::Config>::AccountId>,
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
From<<Runtime as frame_system::Config>::AccountId>,
Block: BlockT,
{
use xcm::prelude::*;
ExtBuilder::<Runtime>::default().build().execute_with(|| {
let test_account = AccountId::from([0u8; 32]);
let transfer_amount = 100u128;
let xcm_to_weigh = Xcm::<RuntimeCall>::builder_unsafe()
.withdraw_asset((Here, transfer_amount))
.buy_execution((Here, transfer_amount), Unlimited)
.deposit_asset(AllCounted(1), [1u8; 32])
.build();
let versioned_xcm_to_weigh = VersionedXcm::from(xcm_to_weigh.clone().into());
let xcm_weight = Runtime::query_xcm_weight(versioned_xcm_to_weigh);
assert!(xcm_weight.is_ok());
let native_token: Location = Parent.into();
let native_token_versioned = VersionedAssetId::from(AssetId(native_token.clone()));
let execution_fees =
Runtime::query_weight_to_asset_fee(xcm_weight.unwrap(), native_token_versioned);
assert!(execution_fees.is_ok());
assert_ok!(
pallet_balances::Pallet::<Runtime>::mint_into(&test_account, 3_000_000_000_000,)
);
let asset_id = 1984u32; let asset_not_in_pool: Location =
(PalletInstance(50), GeneralIndex(asset_id.into())).into();
assert_ok!(pallet_assets::Pallet::<Runtime, pallet_assets::Instance1>::create(
RuntimeOrigin::signed(test_account.clone()),
asset_id.into(),
test_account.clone().into(),
1000
));
let execution_fees = Runtime::query_weight_to_asset_fee(
xcm_weight.unwrap(),
asset_not_in_pool.clone().into(),
);
assert_eq!(execution_fees, Err(XcmPaymentApiError::AssetNotFound));
assert_ok!(pallet_asset_conversion::Pallet::<Runtime>::create_pool(
RuntimeOrigin::signed(test_account.clone()),
native_token.clone().try_into().unwrap(),
asset_not_in_pool.clone().try_into().unwrap()
));
let execution_fees = Runtime::query_weight_to_asset_fee(
xcm_weight.unwrap(),
asset_not_in_pool.clone().into(),
);
assert_eq!(execution_fees, Err(XcmPaymentApiError::AssetNotFound));
assert_ok!(pallet_assets::Pallet::<Runtime, pallet_assets::Instance1>::mint(
RuntimeOrigin::signed(test_account.clone()),
asset_id.into(),
test_account.clone().into(),
3_000_000_000_000,
));
assert_ok!(pallet_asset_conversion::Pallet::<Runtime>::add_liquidity(
RuntimeOrigin::signed(test_account.clone()),
native_token.try_into().unwrap(),
asset_not_in_pool.clone().try_into().unwrap(),
1_000_000_000_000,
2_000_000_000_000,
0,
0,
test_account
));
let execution_fees =
Runtime::query_weight_to_asset_fee(xcm_weight.unwrap(), asset_not_in_pool.into());
assert_ok!(execution_fees);
});
}