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::{MultiLocation, OriginKind, Outcome, VersionedXcm},
v3::Error,
DoubleEncoded,
};
// Cumulus
pub use cumulus_pallet_parachain_system;
pub use cumulus_pallet_xcmp_queue;
pub use cumulus_primitives_core::{
relay_chain::HrmpChannelId, DmpMessageHandler, 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;
pub use pallet_bridge_messages::Instance2 as BridgeMessagesInstance2;
use pallet_bridge_messages::{Config, Instance1, OutboundLanes, Pallet};
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
pub struct BridgeHubMessageHandler<S, T, I> {
_marker: std::marker::PhantomData<(S, T, I)>,
}
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()))
}
}
impl<S, T, I> BridgeMessageHandler for BridgeHubMessageHandler<S, T, I>
where
S: Config<Instance1>,
T: Config<I>,
I: 'static,
<T as Config<I>>::InboundPayload: From<Vec<u8>>,
<T as Config<I>>::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 {
let latest_generated_nonce =
OutboundLanes::<S, Instance1>::get(lane).latest_generated_nonce;
let latest_received_nonce =
OutboundLanes::<S, Instance1>::get(lane).latest_received_nonce;
(latest_received_nonce + 1..=latest_generated_nonce).for_each(|nonce| {
let encoded_payload: Vec<u8> =
Pallet::<S, Instance1>::outbound_message_data(*lane, nonce)
.expect("Bridge message does not exist")
.into();
let payload = Vec::<u8>::decode(&mut &encoded_payload[..])
.expect("Decodign 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
let dispatch_result = TargetMessageDispatch::<T, I>::dispatch(DispatchMessage {
key: MessageKey { lane_id, nonce },
data: DispatchMessageData::<InboundPayload<T, I>> { 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) {
let data = OutboundLanes::<S, Instance1>::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
};
OutboundLanes::<S, Instance1>::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 {
Ignacio Palacios
committed
$crate::impls::assert_ok!(<Self as [<$chain RelayPallet>]>::Balances::force_set_balance(
<Self as $crate::impls::Chain>::RuntimeOrigin::root(),
account.0.into(),
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(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>,
expected_error: Option<$crate::impls::Error>,
$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(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),
},
]
);
}
/// 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>::XcmPallet($crate::impls::pallet_xcm::Event::Sent { .. }) => {},
]
);
}
/// Asserts a XCM from System Parachain is succesfully received and proccessed
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 succesfully received and proccessed
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 registrered
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<()>
) {
use $crate::impls::{bx, Chain, RelayChain};
<Self as $crate::impls::TestExt>::execute_with(|| {
let root_origin = <Self as Chain>::RuntimeOrigin::root();
let destination: $crate::impls::MultiLocation = <Self as RelayChain>::child_location_of(recipient);
let xcm = $crate::impls::xcm_transact_unpaid_execution(call, $crate::impls::OriginKind::Superuser);
// Send XCM `Transact`
Ignacio Palacios
committed
$crate::impls::assert_ok!(<Self as [<$chain RelayPallet>]>::XcmPallet::send(
root_origin,
bx!(destination.into()),
bx!(xcm),
));
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 {
Ignacio Palacios
committed
$crate::impls::assert_ok!(<Self as [<$chain ParaPallet>]>::Balances::force_set_balance(
<Self as $crate::impls::Chain>::RuntimeOrigin::root(),
account.0.into(),
account.1,
));
}
});
}
}
}
};
}
#[macro_export]
macro_rules! impl_assert_events_helpers_for_parachain {
( $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(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>,
expected_error: Option<$crate::impls::Error>,
$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(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),
},
]
);
}
/// Asserts a dispatchable throws and error when trying to be sent
pub fn assert_xcm_pallet_attempted_error(expected_error: Option<$crate::impls::Error>) {
$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),
},
]
);
}
/// 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, ..
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, ..
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, .. }
weight: $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
),
},
]
);
}
}
}
};
}
#[macro_export]
Ignacio Palacios
committed
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)
}
/// Mint assets making use of the assets pallet
pub fn mint_asset(
signed_origin: <Self as $crate::impls::Chain>::RuntimeOrigin,
beneficiary: $crate::impls::AccountId,
amount_to_mint: u128,
) {
<Self as $crate::impls::TestExt>::execute_with(|| {
Ignacio Palacios
committed
$crate::impls::assert_ok!(<Self as [<$chain ParaPallet>]>::Assets::mint(
signed_origin,
id.into(),
beneficiary.clone().into(),
amount_to_mint
));
Ignacio Palacios
committed
type RuntimeEvent<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
$crate::impls::assert_expected_events!(
Self,
vec![
Ignacio Palacios
committed
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,
},
]
);
});
}
/// 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,
},
]
);
Ignacio Palacios
committed
assert!(<Self as [<$chain ParaPallet>]>::Assets::asset_exists(id.into()));
});
}
}
}
};
}