Newer
Older
// 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.
pub use codec::{Decode, Encode};
pub use paste;
pub use crate::{
Ignacio Palacios
committed
xcm_helpers::xcm_transact_unpaid_execution, PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD,
};
// Substrate
pub use frame_support::{
assert_ok,
Ignacio Palacios
committed
sp_runtime::AccountId32,
traits::fungibles::Inspect,
weights::{Weight, WeightMeter},
};
pub use pallet_assets;
pub use pallet_message_queue;
Ignacio Palacios
committed
pub use pallet_xcm;
use sp_core::Get;
Ignacio Palacios
committed
// Polkadot
pub use polkadot_runtime_parachains::{
dmp, hrmp,
inclusion::{AggregateMessageOrigin, UmpQueueId},
Ignacio Palacios
committed
pub use xcm::{
prelude::{Location, OriginKind, Outcome, VersionedXcm, XcmVersion},
v3,
v4::Error as XcmError,
Ignacio Palacios
committed
DoubleEncoded,
};
// Cumulus
pub use cumulus_pallet_parachain_system;
pub use cumulus_pallet_xcmp_queue;
pub use cumulus_primitives_core::{
Adrian Catangiu
committed
relay_chain::HrmpChannelId, DmpMessageHandler, Junction, Junctions, NetworkId, ParaId,
XcmpMessageHandler,
};
pub use parachains_common::{AccountId, Balance};
pub use xcm_emulator::{
assert_expected_events, bx, helpers::weight_within_threshold, BridgeMessage,
Ignacio Palacios
committed
BridgeMessageDispatchError, BridgeMessageHandler, Chain, Network, Parachain, RelayChain,
TestExt,
Ignacio Palacios
committed
// Bridges
use bp_messages::{
target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
LaneId, MessageKey, OutboundLaneData,
Ignacio Palacios
committed
use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult;
Adrian Catangiu
committed
use pallet_bridge_messages::{Config, OutboundLanes, Pallet};
pub use pallet_bridge_messages::{
Instance1 as BridgeMessagesInstance1, Instance2 as BridgeMessagesInstance2,
Instance3 as BridgeMessagesInstance3,
};
Adrian Catangiu
committed
pub struct BridgeHubMessageHandler<S, SI, T, TI> {
_marker: std::marker::PhantomData<(S, SI, T, TI)>,
}
struct LaneIdWrapper(LaneId);
impl From<LaneIdWrapper> for u32 {
fn from(lane_id: LaneIdWrapper) -> u32 {
u32::from_be_bytes(lane_id.0 .0)
}
}
impl From<u32> for LaneIdWrapper {
fn from(id: u32) -> LaneIdWrapper {
LaneIdWrapper(LaneId(id.to_be_bytes()))
}
}
Adrian Catangiu
committed
impl<S, SI, T, TI> BridgeMessageHandler for BridgeHubMessageHandler<S, SI, T, TI>
Adrian Catangiu
committed
S: Config<SI>,
SI: 'static,
T: Config<TI>,
TI: 'static,
<T as Config<TI>>::InboundPayload: From<Vec<u8>>,
<T as Config<TI>>::MessageDispatch:
MessageDispatch<DispatchLevelResult = XcmBlobMessageDispatchResult>,
{
fn get_source_outbound_messages() -> Vec<BridgeMessage> {
// get the source active outbound lanes
let active_lanes = S::ActiveOutboundLanes::get();
let mut messages: Vec<BridgeMessage> = Default::default();
// collect messages from `OutboundMessages` for each active outbound lane in the source
for lane in active_lanes {
Adrian Catangiu
committed
let latest_generated_nonce = OutboundLanes::<S, SI>::get(lane).latest_generated_nonce;
let latest_received_nonce = OutboundLanes::<S, SI>::get(lane).latest_received_nonce;
(latest_received_nonce + 1..=latest_generated_nonce).for_each(|nonce| {
Adrian Catangiu
committed
let encoded_payload: Vec<u8> = Pallet::<S, SI>::outbound_message_data(*lane, nonce)
.expect("Bridge message does not exist")
.into();
let payload = Vec::<u8>::decode(&mut &encoded_payload[..])
.expect("Decoding XCM message failed");
let id: u32 = LaneIdWrapper(*lane).into();
let message = BridgeMessage { id, nonce, payload };
messages.push(message);
});
}
messages
}
fn dispatch_target_inbound_message(
message: BridgeMessage,
) -> Result<(), BridgeMessageDispatchError> {
type TargetMessageDispatch<T, I> = <T as Config<I>>::MessageDispatch;
type InboundPayload<T, I> = <T as Config<I>>::InboundPayload;
let lane_id = LaneIdWrapper::from(message.id).0;
let nonce = message.nonce;
let payload = Ok(From::from(message.payload));
// Directly dispatch outbound messages assuming everything is correct
// and bypassing the `Relayers` and `InboundLane` logic
Adrian Catangiu
committed
let dispatch_result = TargetMessageDispatch::<T, TI>::dispatch(DispatchMessage {
key: MessageKey { lane_id, nonce },
Adrian Catangiu
committed
data: DispatchMessageData::<InboundPayload<T, TI>> { payload },
});
let result = match dispatch_result.dispatch_level_result {
XcmBlobMessageDispatchResult::Dispatched => Ok(()),
XcmBlobMessageDispatchResult::InvalidPayload => Err(BridgeMessageDispatchError(
Box::new(XcmBlobMessageDispatchResult::InvalidPayload),
)),
XcmBlobMessageDispatchResult::NotDispatched(e) => Err(BridgeMessageDispatchError(
Box::new(XcmBlobMessageDispatchResult::NotDispatched(e)),
)),
};
result
}
fn notify_source_message_delivery(lane_id: u32) {
Adrian Catangiu
committed
let data = OutboundLanes::<S, SI>::get(LaneIdWrapper::from(lane_id).0);
let new_data = OutboundLaneData {
oldest_unpruned_nonce: data.oldest_unpruned_nonce + 1,
latest_received_nonce: data.latest_received_nonce + 1,
..data
};
Adrian Catangiu
committed
OutboundLanes::<S, SI>::insert(LaneIdWrapper::from(lane_id).0, new_data);
#[macro_export]
macro_rules! impl_accounts_helpers_for_relay_chain {
( $chain:ident ) => {
$crate::impls::paste::paste! {
Ignacio Palacios
committed
impl<N: $crate::impls::Network> $chain<N> {
/// Fund a set of accounts with a balance
pub fn fund_accounts(accounts: Vec<($crate::impls::AccountId, $crate::impls::Balance)>) {
<Self as $crate::impls::TestExt>::execute_with(|| {
for account in accounts {
let who = account.0;
let actual = <Self as [<$chain RelayPallet>]>::Balances::free_balance(&who);
let actual = actual.saturating_add(<Self as [<$chain RelayPallet>]>::Balances::reserved_balance(&who));
Ignacio Palacios
committed
$crate::impls::assert_ok!(<Self as [<$chain RelayPallet>]>::Balances::force_set_balance(
<Self as $crate::impls::Chain>::RuntimeOrigin::root(),
who.into(),
actual.saturating_add(account.1),
));
}
});
}
/// Fund a sovereign account based on its Parachain Id
Ignacio Palacios
committed
pub fn fund_para_sovereign(amount: $crate::impls::Balance, para_id: $crate::impls::ParaId) -> $crate::impls::AccountId32 {
let sovereign_account = <Self as $crate::impls::RelayChain>::sovereign_account_id_of_child_para(para_id);
Self::fund_accounts(vec![(sovereign_account.clone(), amount)]);
sovereign_account
}
}
}
};
}
#[macro_export]
macro_rules! impl_assert_events_helpers_for_relay_chain {
( $chain:ident ) => {
$crate::impls::paste::paste! {
Ignacio Palacios
committed
type [<$chain RuntimeEvent>]<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
Ignacio Palacios
committed
impl<N: $crate::impls::Network> $chain<N> {
/// Asserts a dispatchable is completely executed and XCM sent
pub fn assert_xcm_pallet_attempted_complete(expected_weight: Option<$crate::impls::Weight>) {
$crate::impls::assert_expected_events!(
Self,
vec![
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::XcmPallet(
$crate::impls::pallet_xcm::Event::Attempted { outcome: $crate::impls::Outcome::Complete { used: weight } }
weight: $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
),
},
]
);
}
/// Asserts a dispatchable is incompletely executed and XCM sent
pub fn assert_xcm_pallet_attempted_incomplete(
expected_weight: Option<$crate::impls::Weight>,
$crate::impls::assert_expected_events!(
Self,
vec![
// Dispatchable is properly executed and XCM message sent
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::XcmPallet(
$crate::impls::pallet_xcm::Event::Attempted { outcome: $crate::impls::Outcome::Incomplete { used: weight, error } }
weight: $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
),
error: *error == expected_error.unwrap_or((*error).into()).into(),
},
]
);
}
/// Asserts an XCM program is sent.
pub fn assert_xcm_pallet_sent() {
$crate::impls::assert_expected_events!(
Self,
vec![
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::XcmPallet($crate::impls::pallet_xcm::Event::Sent { .. }) => {},
]
);
}
/// Asserts an XCM program from a System Parachain is successfully received and
/// processed within expectations.
pub fn assert_ump_queue_processed(
expected_success: bool,
expected_id: Option<$crate::impls::ParaId>,
expected_weight: Option<$crate::impls::Weight>,
$crate::impls::assert_expected_events!(
Self,
vec![
// XCM is successfully received and processed
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pallet_message_queue::Event::Processed {
origin: $crate::impls::AggregateMessageOrigin::Ump($crate::impls::UmpQueueId::Para(id)),
weight_used,
success,
..
}) => {
id: *id == expected_id.unwrap_or(*id),
weight_used: $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight_used),
*weight_used
),
success: *success == expected_success,
},
]
);
}
}
}
};
}
#[macro_export]
macro_rules! impl_hrmp_channels_helpers_for_relay_chain {
( $chain:ident ) => {
$crate::impls::paste::paste! {
Ignacio Palacios
committed
impl<N: $crate::impls::Network> $chain<N> {
/// Init open channel request with another Parachain
pub fn init_open_channel_call(
recipient_para_id: $crate::impls::ParaId,
max_capacity: u32,
max_message_size: u32,
) -> $crate::impls::DoubleEncoded<()> {
use $crate::impls::Encode;
<Self as $crate::impls::Chain>::RuntimeCall::Hrmp($crate::impls::hrmp::Call::<
<Self as $crate::impls::Chain>::Runtime,
>::hrmp_init_open_channel {
recipient: recipient_para_id,
proposed_max_capacity: max_capacity,
proposed_max_message_size: max_message_size,
})
.encode()
.into()
}
/// Recipient Parachain accept the open request from another Parachain
pub fn accept_open_channel_call(sender_para_id: $crate::impls::ParaId) -> $crate::impls::DoubleEncoded<()> {
use $crate::impls::Encode;
<Self as $crate::impls::Chain>::RuntimeCall::Hrmp($crate::impls::hrmp::Call::<
<Self as $crate::impls::Chain>::Runtime,
>::hrmp_accept_open_channel {
sender: sender_para_id,
})
.encode()
.into()
}
/// A root origin force to open a channel between two Parachains
pub fn force_process_hrmp_open(sender: $crate::impls::ParaId, recipient: $crate::impls::ParaId) {
use $crate::impls::Chain;
<Self as $crate::impls::TestExt>::execute_with(|| {
let relay_root_origin = <Self as Chain>::RuntimeOrigin::root();
// Force process HRMP open channel requests without waiting for the next session
Ignacio Palacios
committed
$crate::impls::assert_ok!(<Self as [<$chain RelayPallet>]>::Hrmp::force_process_hrmp_open(
relay_root_origin,
0
));
let channel_id = $crate::impls::HrmpChannelId { sender, recipient };
let hrmp_channel_exist = $crate::impls::hrmp::HrmpChannels::<
<Self as Chain>::Runtime,
>::contains_key(&channel_id);
// Check the HRMP channel has been successfully registered
assert!(hrmp_channel_exist)
});
}
}
}
};
}
#[macro_export]
macro_rules! impl_send_transact_helpers_for_relay_chain {
( $chain:ident ) => {
$crate::impls::paste::paste! {
Ignacio Palacios
committed
impl<N: $crate::impls::Network> $chain<N> {
/// A root origin (as governance) sends `xcm::Transact` with `UnpaidExecution` and encoded `call` to child parachain.
pub fn send_unpaid_transact_to_parachain_as_root(
recipient: $crate::impls::ParaId,
call: $crate::impls::DoubleEncoded<()>
) {
Francisco Aguirre
committed
use $crate::impls::{bx, Chain, RelayChain, Encode};
<Self as $crate::impls::TestExt>::execute_with(|| {
let root_origin = <Self as Chain>::RuntimeOrigin::root();
let destination: $crate::impls::Location = <Self as RelayChain>::child_location_of(recipient);
let xcm = $crate::impls::xcm_transact_unpaid_execution(call, $crate::impls::OriginKind::Superuser);
// Send XCM `Transact`
Francisco Aguirre
committed
$crate::impls::assert_ok!(<Self as [<$chain RelayPallet>]>::XcmPallet::send_blob(
Francisco Aguirre
committed
xcm.encode().try_into().unwrap(),
));
Self::assert_xcm_pallet_sent();
});
}
}
}
};
}
#[macro_export]
macro_rules! impl_accounts_helpers_for_parachain {
( $chain:ident ) => {
$crate::impls::paste::paste! {
Ignacio Palacios
committed
impl<N: $crate::impls::Network> $chain<N> {
/// Fund a set of accounts with a balance
pub fn fund_accounts(accounts: Vec<($crate::impls::AccountId, $crate::impls::Balance)>) {
<Self as $crate::impls::TestExt>::execute_with(|| {
for account in accounts {
let who = account.0;
let actual = <Self as [<$chain ParaPallet>]>::Balances::free_balance(&who);
let actual = actual.saturating_add(<Self as [<$chain ParaPallet>]>::Balances::reserved_balance(&who));
Ignacio Palacios
committed
$crate::impls::assert_ok!(<Self as [<$chain ParaPallet>]>::Balances::force_set_balance(
<Self as $crate::impls::Chain>::RuntimeOrigin::root(),
who.into(),
actual.saturating_add(account.1),
));
}
});
}
Adrian Catangiu
committed
/// Fund a sovereign account of sibling para.
pub fn fund_para_sovereign(sibling_para_id: $crate::impls::ParaId, balance: $crate::impls::Balance) {
let sibling_location = Self::sibling_location_of(sibling_para_id);
let sovereign_account = Self::sovereign_account_id_of(sibling_location);
Self::fund_accounts(vec![(sovereign_account.into(), balance)])
}
Adrian Catangiu
committed
/// Return local sovereign account of `para_id` on other `network_id`
pub fn sovereign_account_of_parachain_on_other_global_consensus(
network_id: $crate::impls::NetworkId,
para_id: $crate::impls::ParaId,
) -> $crate::impls::AccountId {
let remote_location = $crate::impls::Location::new(
2,
[
Adrian Catangiu
committed
$crate::impls::Junction::GlobalConsensus(network_id),
$crate::impls::Junction::Parachain(para_id.into()),
Adrian Catangiu
committed
<Self as $crate::impls::TestExt>::execute_with(|| {
Self::sovereign_account_id_of(remote_location)
})
}
}
}
};
}
#[macro_export]
macro_rules! impl_assert_events_helpers_for_parachain {
Adrian Catangiu
committed
( $chain:ident ) => {
$crate::impls::paste::paste! {
Ignacio Palacios
committed
type [<$chain RuntimeEvent>]<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
Ignacio Palacios
committed
impl<N: $crate::impls::Network> $chain<N> {
/// Asserts a dispatchable is completely executed and XCM sent
pub fn assert_xcm_pallet_attempted_complete(expected_weight: Option<$crate::impls::Weight>) {
$crate::impls::assert_expected_events!(
Self,
vec![
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::PolkadotXcm(
$crate::impls::pallet_xcm::Event::Attempted { outcome: $crate::impls::Outcome::Complete { used: weight } }
Adrian Catangiu
committed
weight: $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
),
},
]
);
}
/// Asserts a dispatchable is incompletely executed and XCM sent
pub fn assert_xcm_pallet_attempted_incomplete(
expected_weight: Option<$crate::impls::Weight>,
$crate::impls::assert_expected_events!(
Self,
vec![
// Dispatchable is properly executed and XCM message sent
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::PolkadotXcm(
$crate::impls::pallet_xcm::Event::Attempted { outcome: $crate::impls::Outcome::Incomplete { used: weight, error } }
Adrian Catangiu
committed
weight: $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
),
error: *error == expected_error.unwrap_or((*error).into()).into(),
},
]
);
}
/// Asserts a dispatchable throws and error when trying to be sent
pub fn assert_xcm_pallet_attempted_error(expected_error: Option<$crate::impls::XcmError>) {
$crate::impls::assert_expected_events!(
Self,
vec![
// Execution fails in the origin with `Barrier`
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::PolkadotXcm(
$crate::impls::pallet_xcm::Event::Attempted { outcome: $crate::impls::Outcome::Error { error } }
error: *error == expected_error.unwrap_or((*error).into()).into(),
},
]
);
}
/// Asserts a XCM message is sent
pub fn assert_xcm_pallet_sent() {
$crate::impls::assert_expected_events!(
Self,
vec![
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::PolkadotXcm($crate::impls::pallet_xcm::Event::Sent { .. }) => {},
]
);
}
/// Asserts a XCM message is sent to Relay Chain
pub fn assert_parachain_system_ump_sent() {
$crate::impls::assert_expected_events!(
Self,
vec![
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::ParachainSystem(
$crate::impls::cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }
) => {},
]
);
}
/// Asserts a XCM from Relay Chain is completely executed
pub fn assert_dmp_queue_complete(expected_weight: Option<$crate::impls::Weight>) {
$crate::impls::assert_expected_events!(
Self,
vec![
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pallet_message_queue::Event::Processed {
success: true, weight_used: weight, ..
Adrian Catangiu
committed
weight: $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
),
},
]
);
}
/// Asserts a XCM from Relay Chain is incompletely executed
pub fn assert_dmp_queue_incomplete(
expected_weight: Option<$crate::impls::Weight>,
$crate::impls::assert_expected_events!(
Self,
vec![
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pallet_message_queue::Event::Processed {
success: false, weight_used: weight, ..
Adrian Catangiu
committed
weight: $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
),
},
]
);
}
/// Asserts a XCM from Relay Chain is executed with error
pub fn assert_dmp_queue_error() {
$crate::impls::assert_expected_events!(
Self,
vec![
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pallet_message_queue::Event::ProcessingFailed {
},
]
);
}
/// Asserts a XCM from another Parachain is completely executed
pub fn assert_xcmp_queue_success(expected_weight: Option<$crate::impls::Weight>) {
$crate::impls::assert_expected_events!(
Self,
vec![
Ignacio Palacios
committed
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pallet_message_queue::Event::Processed { success: true, weight_used: weight, .. }
Adrian Catangiu
committed
weight: $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
),
},
]
);
}
}
}
};
}
#[macro_export]
macro_rules! impl_assets_helpers_for_system_parachain {
( $chain:ident, $relay_chain:ident ) => {
$crate::impls::paste::paste! {
Ignacio Palacios
committed
impl<N: $crate::impls::Network> $chain<N> {
/// Returns the encoded call for `force_create` from the assets pallet
pub fn force_create_asset_call(
asset_id: u32,
owner: $crate::impls::AccountId,
is_sufficient: bool,
min_balance: $crate::impls::Balance,
) -> $crate::impls::DoubleEncoded<()> {
use $crate::impls::{Chain, Encode};
<Self as Chain>::RuntimeCall::Assets($crate::impls::pallet_assets::Call::<
<Self as Chain>::Runtime,
$crate::impls::pallet_assets::Instance1,
>::force_create {
id: asset_id.into(),
owner: owner.into(),
is_sufficient,
min_balance,
})
.encode()
.into()
}
/// Returns a `VersionedXcm` for `force_create` from the assets pallet
pub fn force_create_asset_xcm(
origin_kind: $crate::impls::OriginKind,
asset_id: u32,
owner: $crate::impls::AccountId,
is_sufficient: bool,
min_balance: $crate::impls::Balance,
) -> $crate::impls::VersionedXcm<()> {
let call = Self::force_create_asset_call(asset_id, owner, is_sufficient, min_balance);
$crate::impls::xcm_transact_unpaid_execution(call, origin_kind)
}
/// Force create and mint assets making use of the assets pallet
pub fn force_create_and_mint_asset(
id: u32,
min_balance: u128,
is_sufficient: bool,
asset_owner: $crate::impls::AccountId,
dmp_weight_threshold: Option<$crate::impls::Weight>,
amount_to_mint: u128,
) {
use $crate::impls::Chain;
// Force create asset
Self::force_create_asset_from_relay_as_root(
id,
min_balance,
is_sufficient,
asset_owner.clone(),
dmp_weight_threshold
// Mint asset for System Parachain's sender
let signed_origin = <Self as Chain>::RuntimeOrigin::signed(asset_owner.clone());
Self::mint_asset(signed_origin, id, asset_owner, amount_to_mint);
}
/// Relay Chain sends `Transact` instruction with `force_create_asset` to Parachain with `Assets` instance of `pallet_assets` .
pub fn force_create_asset_from_relay_as_root(
id: u32,
min_balance: u128,
is_sufficient: bool,
asset_owner: $crate::impls::AccountId,
dmp_weight_threshold: Option<$crate::impls::Weight>,
) {
use $crate::impls::{Parachain, Inspect, TestExt};
Ignacio Palacios
committed
<$relay_chain<N>>::send_unpaid_transact_to_parachain_as_root(
Self::para_id(),
Self::force_create_asset_call(id, asset_owner.clone(), is_sufficient, min_balance),
);
// Receive XCM message in Assets Parachain
Self::execute_with(|| {
Ignacio Palacios
committed
type RuntimeEvent<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
Self::assert_dmp_queue_complete(dmp_weight_threshold);
$crate::impls::assert_expected_events!(
Self,
vec![
Ignacio Palacios
committed
RuntimeEvent::<N>::Assets($crate::impls::pallet_assets::Event::ForceCreated { asset_id, owner }) => {
asset_id: *asset_id == id,
},
]
);
assert!(<Self as [<$chain ParaPallet>]>::Assets::asset_exists(id.clone().into()));
});
}
}
}
};
}
Adrian Catangiu
committed
#[macro_export]
macro_rules! impl_assets_helpers_for_parachain {
( $chain:ident) => {
Adrian Catangiu
committed
$crate::impls::paste::paste! {
impl<N: $crate::impls::Network> $chain<N> {
/// Create foreign assets using sudo `ForeignAssets::force_create()`
pub fn force_create_foreign_asset(
Adrian Catangiu
committed
owner: $crate::impls::AccountId,
is_sufficient: bool,
min_balance: u128,
prefund_accounts: Vec<($crate::impls::AccountId, u128)>,
) {
use $crate::impls::Inspect;
let sudo_origin = <$chain<N> as $crate::impls::Chain>::RuntimeOrigin::root();
<Self as $crate::impls::TestExt>::execute_with(|| {
$crate::impls::assert_ok!(
<Self as [<$chain ParaPallet>]>::ForeignAssets::force_create(
sudo_origin,
Adrian Catangiu
committed
owner.clone().into(),
is_sufficient,
min_balance,
)
);
assert!(<Self as [<$chain ParaPallet>]>::ForeignAssets::asset_exists(id.clone()));
Adrian Catangiu
committed
type RuntimeEvent<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
$crate::impls::assert_expected_events!(
Self,
vec![
RuntimeEvent::<N>::ForeignAssets(
$crate::impls::pallet_assets::Event::ForceCreated {
asset_id,
..
}
) => { asset_id: *asset_id == id, },
]
);
});
for (beneficiary, amount) in prefund_accounts.into_iter() {
let signed_origin =
<$chain<N> as $crate::impls::Chain>::RuntimeOrigin::signed(owner.clone());
Self::mint_foreign_asset(signed_origin, id.clone(), beneficiary, amount);
Adrian Catangiu
committed
}
}
/// Mint assets making use of the ForeignAssets pallet-assets instance
pub fn mint_foreign_asset(
signed_origin: <Self as $crate::impls::Chain>::RuntimeOrigin,
Adrian Catangiu
committed
beneficiary: $crate::impls::AccountId,
amount_to_mint: u128,
) {
<Self as $crate::impls::TestExt>::execute_with(|| {
$crate::impls::assert_ok!(<Self as [<$chain ParaPallet>]>::ForeignAssets::mint(
signed_origin,
Adrian Catangiu
committed
beneficiary.clone().into(),
amount_to_mint
));
type RuntimeEvent<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
$crate::impls::assert_expected_events!(
Self,
vec![
RuntimeEvent::<N>::ForeignAssets(
$crate::impls::pallet_assets::Event::Issued { asset_id, owner, amount }
) => {
asset_id: *asset_id == id,
owner: *owner == beneficiary.clone().into(),
amount: *amount == amount_to_mint,
},
]
);
});
}
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
/// Create assets using sudo `Assets::force_create()`
pub fn force_create_asset(
id: u32,
owner: $crate::impls::AccountId,
is_sufficient: bool,
min_balance: u128,
prefund_accounts: Vec<($crate::impls::AccountId, u128)>,
) {
use $crate::impls::Inspect;
let sudo_origin = <$chain<N> as $crate::impls::Chain>::RuntimeOrigin::root();
<Self as $crate::impls::TestExt>::execute_with(|| {
$crate::impls::assert_ok!(
<Self as [<$chain ParaPallet>]>::Assets::force_create(
sudo_origin,
id.clone().into(),
owner.clone().into(),
is_sufficient,
min_balance,
)
);
assert!(<Self as [<$chain ParaPallet>]>::Assets::asset_exists(id.clone()));
type RuntimeEvent<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
$crate::impls::assert_expected_events!(
Self,
vec![
RuntimeEvent::<N>::Assets(
$crate::impls::pallet_assets::Event::ForceCreated {
asset_id,
..
}
) => { asset_id: *asset_id == id, },
]
);
});
for (beneficiary, amount) in prefund_accounts.into_iter() {
let signed_origin =
<$chain<N> as $crate::impls::Chain>::RuntimeOrigin::signed(owner.clone());
Self::mint_asset(signed_origin, id.clone(), beneficiary, amount);
}
}
/// Mint assets making use of the assets pallet
pub fn mint_asset(
signed_origin: <Self as $crate::impls::Chain>::RuntimeOrigin,
id: u32,
beneficiary: $crate::impls::AccountId,
amount_to_mint: u128,
) {
<Self as $crate::impls::TestExt>::execute_with(|| {
$crate::impls::assert_ok!(<Self as [<$chain ParaPallet>]>::Assets::mint(
signed_origin,
id.clone().into(),
beneficiary.clone().into(),
amount_to_mint
));
type RuntimeEvent<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
$crate::impls::assert_expected_events!(
Self,
vec![
RuntimeEvent::<N>::Assets(
$crate::impls::pallet_assets::Event::Issued { asset_id, owner, amount }
) => {
asset_id: *asset_id == id,
owner: *owner == beneficiary.clone().into(),
amount: *amount == amount_to_mint,
},
]
);
});
}
/// Returns the encoded call for `create` from the assets pallet
pub fn create_asset_call(
asset_id: u32,
min_balance: $crate::impls::Balance,
admin: $crate::impls::AccountId,
) -> $crate::impls::DoubleEncoded<()> {
use $crate::impls::{Chain, Encode};
<Self as Chain>::RuntimeCall::Assets($crate::impls::pallet_assets::Call::<
<Self as Chain>::Runtime,
$crate::impls::pallet_assets::Instance1,
>::create {
id: asset_id.into(),
min_balance,
admin: admin.into(),
})
.encode()
.into()
}
/// Returns the encoded call for `create` from the foreign assets pallet
pub fn create_foreign_asset_call(
asset_id: $crate::impls::v3::Location,
min_balance: $crate::impls::Balance,
admin: $crate::impls::AccountId,
) -> $crate::impls::DoubleEncoded<()> {
use $crate::impls::{Chain, Encode};
<Self as Chain>::RuntimeCall::ForeignAssets($crate::impls::pallet_assets::Call::<
<Self as Chain>::Runtime,
$crate::impls::pallet_assets::Instance2,
>::create {
id: asset_id.into(),
min_balance,
admin: admin.into(),
})
.encode()
.into()
}
Adrian Catangiu
committed
}
}
};
}
#[macro_export]
macro_rules! impl_xcm_helpers_for_parachain {
( $chain:ident ) => {
$crate::impls::paste::paste! {
impl<N: $crate::impls::Network> $chain<N> {
/// Set XCM version for destination.
pub fn force_xcm_version(dest: $crate::impls::Location, version: $crate::impls::XcmVersion) {
<Self as $crate::impls::TestExt>::execute_with(|| {
$crate::impls::assert_ok!(<Self as [<$chain ParaPallet>]>::PolkadotXcm::force_xcm_version(
<Self as $crate::impls::Chain>::RuntimeOrigin::root(),
$crate::impls::bx!(dest),
version,
));
});
}
/// Set default/safe XCM version for runtime.
pub fn force_default_xcm_version(version: Option<$crate::impls::XcmVersion>) {
<Self as $crate::impls::TestExt>::execute_with(|| {
$crate::impls::assert_ok!(<Self as [<$chain ParaPallet>]>::PolkadotXcm::force_default_xcm_version(
<Self as $crate::impls::Chain>::RuntimeOrigin::root(),
version,
));
});
}
}
}
}
}