feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
[package]
|
||||
name = "emulated-integration-tests-common"
|
||||
version = "3.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "Common resources for integration testing with xcm-emulator"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true }
|
||||
hex-literal = { workspace = true }
|
||||
paste = { workspace = true, default-features = true }
|
||||
|
||||
# Bizinikiwi
|
||||
pezframe-support = { workspace = true, default-features = true }
|
||||
pezframe-system = { workspace = true, default-features = true }
|
||||
pezpallet-asset-conversion = { workspace = true, default-features = true }
|
||||
pezpallet-assets = { workspace = true, default-features = true }
|
||||
pezpallet-balances = { workspace = true, default-features = true }
|
||||
pezpallet-message-queue = { workspace = true, default-features = true }
|
||||
pezpallet-whitelist = { workspace = true, default-features = true }
|
||||
pezsc-consensus-grandpa = { workspace = true, default-features = true }
|
||||
pezsp-authority-discovery = { workspace = true, default-features = true }
|
||||
pezsp-consensus-babe = { workspace = true, default-features = true }
|
||||
pezsp-consensus-beefy = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true, default-features = true }
|
||||
pezsp-keyring = { workspace = true, default-features = true }
|
||||
pezsp-runtime = { workspace = true, default-features = true }
|
||||
|
||||
# Pezkuwi
|
||||
pezpallet-xcm = { features = [
|
||||
"test-utils",
|
||||
], workspace = true, default-features = true }
|
||||
pezkuwi-primitives = { workspace = true, default-features = true }
|
||||
pezkuwi-runtime-teyrchains = { workspace = true, default-features = true }
|
||||
pezkuwi-teyrchain-primitives = { workspace = true, default-features = true }
|
||||
xcm = { workspace = true, default-features = true }
|
||||
xcm-builder = { workspace = true, default-features = true }
|
||||
xcm-executor = { workspace = true, default-features = true }
|
||||
xcm-runtime-apis = { workspace = true, default-features = true }
|
||||
xcm-simulator = { workspace = true, default-features = true }
|
||||
|
||||
# Pezcumulus
|
||||
asset-test-utils = { workspace = true, default-features = true }
|
||||
pezcumulus-pezpallet-teyrchain-system = { workspace = true, default-features = true }
|
||||
pezcumulus-pezpallet-xcmp-queue = { workspace = true, default-features = true }
|
||||
pezcumulus-primitives-core = { workspace = true, default-features = true }
|
||||
teyrchains-common = { workspace = true, default-features = true }
|
||||
xcm-emulator = { workspace = true, default-features = true }
|
||||
|
||||
# Bridges
|
||||
bp-messages = { workspace = true, default-features = true }
|
||||
bp-xcm-bridge-hub = { workspace = true, default-features = true }
|
||||
pezpallet-bridge-messages = { workspace = true, default-features = true }
|
||||
pezpallet-xcm-bridge-hub = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"asset-test-utils/runtime-benchmarks",
|
||||
"bp-messages/runtime-benchmarks",
|
||||
"bp-xcm-bridge-hub/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-xcmp-queue/runtime-benchmarks",
|
||||
"pezcumulus-primitives-core/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezframe-system/runtime-benchmarks",
|
||||
"pezpallet-asset-conversion/runtime-benchmarks",
|
||||
"pezpallet-assets/runtime-benchmarks",
|
||||
"pezpallet-balances/runtime-benchmarks",
|
||||
"pezpallet-bridge-messages/runtime-benchmarks",
|
||||
"pezpallet-message-queue/runtime-benchmarks",
|
||||
"pezpallet-whitelist/runtime-benchmarks",
|
||||
"pezpallet-xcm-bridge-hub/runtime-benchmarks",
|
||||
"pezpallet-xcm/runtime-benchmarks",
|
||||
"pezkuwi-primitives/runtime-benchmarks",
|
||||
"pezkuwi-runtime-teyrchains/runtime-benchmarks",
|
||||
"pezkuwi-teyrchain-primitives/runtime-benchmarks",
|
||||
"pezsc-consensus-grandpa/runtime-benchmarks",
|
||||
"pezsp-authority-discovery/runtime-benchmarks",
|
||||
"pezsp-consensus-babe/runtime-benchmarks",
|
||||
"pezsp-consensus-beefy/runtime-benchmarks",
|
||||
"pezsp-keyring/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"teyrchains-common/runtime-benchmarks",
|
||||
"xcm-builder/runtime-benchmarks",
|
||||
"xcm-emulator/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
"xcm-runtime-apis/runtime-benchmarks",
|
||||
"xcm-simulator/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,999 @@
|
||||
// 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::{
|
||||
xcm_helpers::{xcm_transact_paid_execution, xcm_transact_unpaid_execution},
|
||||
PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD,
|
||||
};
|
||||
|
||||
// Bizinikiwi
|
||||
pub use pezframe_support::{
|
||||
assert_ok,
|
||||
pezsp_runtime::AccountId32,
|
||||
traits::fungibles::Inspect,
|
||||
weights::{Weight, WeightMeter},
|
||||
};
|
||||
pub use pezpallet_assets;
|
||||
pub use pezpallet_message_queue;
|
||||
pub use pezpallet_xcm;
|
||||
pub use xcm;
|
||||
|
||||
// Pezkuwi
|
||||
pub use pezkuwi_runtime_teyrchains::{
|
||||
dmp, hrmp,
|
||||
inclusion::{AggregateMessageOrigin, UmpQueueId},
|
||||
};
|
||||
pub use xcm::{
|
||||
prelude::{
|
||||
Asset, InteriorLocation, Location, OriginKind, Outcome, VersionedXcm, XcmError, XcmVersion,
|
||||
},
|
||||
DoubleEncoded,
|
||||
};
|
||||
|
||||
// Pezcumulus
|
||||
pub use cumulus_pallet_teyrchain_system;
|
||||
pub use cumulus_pallet_xcmp_queue;
|
||||
pub use cumulus_primitives_core::{
|
||||
relay_chain::HrmpChannelId, DmpMessageHandler, Junction, Junctions, NetworkId, ParaId,
|
||||
XcmpMessageHandler,
|
||||
};
|
||||
pub use teyrchains_common::{AccountId, Balance};
|
||||
pub use xcm_emulator::{
|
||||
assert_expected_events, bx, helpers::weight_within_threshold, BridgeLaneId, BridgeMessage,
|
||||
BridgeMessageDispatchError, BridgeMessageHandler, Chain, Network, RelayChain, TestExt,
|
||||
Teyrchain,
|
||||
};
|
||||
|
||||
// Bridges
|
||||
use bp_messages::{
|
||||
target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
|
||||
MessageKey, OutboundLaneData,
|
||||
};
|
||||
pub use bp_xcm_bridge_hub::XcmBridgeHubCall;
|
||||
use pezpallet_bridge_messages::{Config as BridgeMessagesConfig, LaneIdOf, OutboundLanes, Pallet};
|
||||
pub use pezpallet_bridge_messages::{
|
||||
Instance1 as BridgeMessagesInstance1, Instance2 as BridgeMessagesInstance2,
|
||||
Instance3 as BridgeMessagesInstance3,
|
||||
};
|
||||
use pezpallet_xcm_bridge_hub::XcmBlobMessageDispatchResult;
|
||||
|
||||
pub struct BridgeHubMessageHandler<S, SI, T, TI> {
|
||||
_marker: std::marker::PhantomData<(S, SI, T, TI)>,
|
||||
}
|
||||
|
||||
struct LaneIdWrapper<LaneId>(LaneId);
|
||||
impl<LaneId: Encode> From<LaneIdWrapper<LaneId>> for BridgeLaneId {
|
||||
fn from(lane_id: LaneIdWrapper<LaneId>) -> BridgeLaneId {
|
||||
lane_id.0.encode()
|
||||
}
|
||||
}
|
||||
impl<LaneId: Decode> From<BridgeLaneId> for LaneIdWrapper<LaneId> {
|
||||
fn from(id: BridgeLaneId) -> LaneIdWrapper<LaneId> {
|
||||
LaneIdWrapper(LaneId::decode(&mut &id[..]).expect("decodable"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, SI, T, TI> BridgeMessageHandler for BridgeHubMessageHandler<S, SI, T, TI>
|
||||
where
|
||||
S: BridgeMessagesConfig<SI>,
|
||||
SI: 'static,
|
||||
T: BridgeMessagesConfig<TI>,
|
||||
TI: 'static,
|
||||
<T as BridgeMessagesConfig<TI>>::InboundPayload: From<Vec<u8>>,
|
||||
<T as BridgeMessagesConfig<TI>>::MessageDispatch:
|
||||
MessageDispatch<DispatchLevelResult = XcmBlobMessageDispatchResult>,
|
||||
{
|
||||
fn get_source_outbound_messages() -> Vec<BridgeMessage> {
|
||||
// get the source active outbound lanes
|
||||
let active_outbound_lanes = OutboundLanes::<S, SI>::iter_keys();
|
||||
|
||||
let mut messages: Vec<BridgeMessage> = Default::default();
|
||||
|
||||
// collect messages from `OutboundMessages` for each active outbound lane in the source
|
||||
for lane in active_outbound_lanes {
|
||||
let latest_generated_nonce =
|
||||
OutboundLanes::<S, SI>::get(lane).unwrap().latest_generated_nonce;
|
||||
let latest_received_nonce =
|
||||
OutboundLanes::<S, SI>::get(lane).unwrap().latest_received_nonce;
|
||||
|
||||
(latest_received_nonce + 1..=latest_generated_nonce).for_each(|nonce| {
|
||||
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 message = BridgeMessage { lane_id: LaneIdWrapper(lane).into(), nonce, payload };
|
||||
|
||||
messages.push(message);
|
||||
});
|
||||
}
|
||||
messages
|
||||
}
|
||||
|
||||
fn dispatch_target_inbound_message(
|
||||
message: BridgeMessage,
|
||||
) -> Result<(), BridgeMessageDispatchError> {
|
||||
type TargetMessageDispatch<T, I> = <T as BridgeMessagesConfig<I>>::MessageDispatch;
|
||||
type InboundPayload<T, I> = <T as BridgeMessagesConfig<I>>::InboundPayload;
|
||||
|
||||
let lane_id = LaneIdWrapper::from(message.lane_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, TI>::dispatch(DispatchMessage {
|
||||
key: MessageKey { lane_id, nonce },
|
||||
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: BridgeLaneId) {
|
||||
let lane_id: LaneIdOf<S, SI> = LaneIdWrapper::from(lane_id).0;
|
||||
let data = OutboundLanes::<S, SI>::get(lane_id).unwrap();
|
||||
let new_data = OutboundLaneData {
|
||||
oldest_unpruned_nonce: data.oldest_unpruned_nonce + 1,
|
||||
latest_received_nonce: data.latest_received_nonce + 1,
|
||||
..data
|
||||
};
|
||||
|
||||
OutboundLanes::<S, SI>::insert(lane_id, new_data);
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_accounts_helpers_for_relay_chain {
|
||||
( $chain:ident ) => {
|
||||
$crate::impls::paste::paste! {
|
||||
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));
|
||||
|
||||
$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 Teyrchain Id
|
||||
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! {
|
||||
type [<$chain RuntimeEvent>]<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
|
||||
|
||||
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![
|
||||
[<$chain RuntimeEvent>]::<N>::XcmPallet(
|
||||
$crate::impls::pezpallet_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>,
|
||||
expected_error: Option<$crate::impls::XcmError>,
|
||||
) {
|
||||
$crate::impls::assert_expected_events!(
|
||||
Self,
|
||||
vec![
|
||||
// Dispatchable is properly executed and XCM message sent
|
||||
[<$chain RuntimeEvent>]::<N>::XcmPallet(
|
||||
$crate::impls::pezpallet_xcm::Event::Attempted { outcome: $crate::impls::Outcome::Incomplete { used: weight, error: $crate::impls::xcm::prelude::InstructionError { 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![
|
||||
[<$chain RuntimeEvent>]::<N>::XcmPallet($crate::impls::pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// Asserts an XCM program from a System Teyrchain 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
|
||||
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pezpallet_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! {
|
||||
impl<N: $crate::impls::Network> $chain<N> {
|
||||
/// Init open channel request with another Teyrchain
|
||||
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 Teyrchain accept the open request from another Teyrchain
|
||||
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 Teyrchains
|
||||
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
|
||||
$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! {
|
||||
impl<N: $crate::impls::Network> $chain<N> {
|
||||
/// A root origin (as governance) sends `xcm::Transact` with `UnpaidExecution` and encoded `call` to child teyrchain.
|
||||
pub fn send_unpaid_transact_to_teyrchain_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::Location = <Self as RelayChain>::child_location_of(recipient);
|
||||
let xcm = $crate::impls::xcm_transact_unpaid_execution(call, $crate::impls::OriginKind::Superuser);
|
||||
|
||||
$crate::impls::dmp::Pallet::<<Self as $crate::impls::Chain>::Runtime>::make_teyrchain_reachable(recipient);
|
||||
|
||||
// Send XCM `Transact`
|
||||
$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_teyrchain {
|
||||
( $chain:ident ) => {
|
||||
$crate::impls::paste::paste! {
|
||||
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));
|
||||
|
||||
$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),
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 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)])
|
||||
}
|
||||
|
||||
/// Return local sovereign account of `para_id` on other `network_id`
|
||||
pub fn sovereign_account_of_teyrchain_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,
|
||||
[
|
||||
$crate::impls::Junction::GlobalConsensus(network_id),
|
||||
$crate::impls::Junction::Teyrchain(para_id.into()),
|
||||
],
|
||||
);
|
||||
<Self as $crate::impls::TestExt>::execute_with(|| {
|
||||
Self::sovereign_account_id_of(remote_location)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_assert_events_helpers_for_teyrchain {
|
||||
( $chain:ident ) => {
|
||||
$crate::impls::paste::paste! {
|
||||
type [<$chain RuntimeEvent>]<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
|
||||
|
||||
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![
|
||||
[<$chain RuntimeEvent>]::<N>::PezkuwiXcm(
|
||||
$crate::impls::pezpallet_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>,
|
||||
expected_error: Option<$crate::impls::XcmError>,
|
||||
) {
|
||||
$crate::impls::assert_expected_events!(
|
||||
Self,
|
||||
vec![
|
||||
// Dispatchable is properly executed and XCM message sent
|
||||
[<$chain RuntimeEvent>]::<N>::PezkuwiXcm(
|
||||
$crate::impls::pezpallet_xcm::Event::Attempted { outcome: $crate::impls::Outcome::Incomplete { used: weight, error: $crate::impls::xcm::prelude::InstructionError { 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 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`
|
||||
[<$chain RuntimeEvent>]::<N>::PezkuwiXcm(
|
||||
$crate::impls::pezpallet_xcm::Event::Attempted { outcome: $crate::impls::Outcome::Error($crate::impls::xcm::prelude::InstructionError { 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![
|
||||
[<$chain RuntimeEvent>]::<N>::PezkuwiXcm($crate::impls::pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// Asserts a XCM message is sent to Relay Chain
|
||||
pub fn assert_teyrchain_system_ump_sent() {
|
||||
$crate::impls::assert_expected_events!(
|
||||
Self,
|
||||
vec![
|
||||
[<$chain RuntimeEvent>]::<N>::TeyrchainSystem(
|
||||
$crate::impls::cumulus_pallet_teyrchain_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![
|
||||
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pezpallet_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![
|
||||
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pezpallet_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![
|
||||
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pezpallet_message_queue::Event::ProcessingFailed {
|
||||
..
|
||||
}) => {
|
||||
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// Asserts a XCM from another Teyrchain is completely executed
|
||||
pub fn assert_xcmp_queue_success(expected_weight: Option<$crate::impls::Weight>) {
|
||||
$crate::impls::assert_expected_events!(
|
||||
Self,
|
||||
vec![
|
||||
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pezpallet_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]
|
||||
macro_rules! impl_assets_helpers_for_system_teyrchain {
|
||||
( $chain:ident, $relay_chain:ident ) => {
|
||||
$crate::impls::paste::paste! {
|
||||
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::pezpallet_assets::Call::<
|
||||
<Self as Chain>::Runtime,
|
||||
$crate::impls::pezpallet_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 Teyrchain'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 Teyrchain with `Assets` instance of `pezpallet_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::{Teyrchain, Inspect, TestExt};
|
||||
|
||||
<$relay_chain<N>>::send_unpaid_transact_to_teyrchain_as_root(
|
||||
Self::para_id(),
|
||||
Self::force_create_asset_call(id, asset_owner.clone(), is_sufficient, min_balance),
|
||||
);
|
||||
|
||||
// Receive XCM message in Assets Teyrchain
|
||||
Self::execute_with(|| {
|
||||
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![
|
||||
RuntimeEvent::<N>::Assets($crate::impls::pezpallet_assets::Event::ForceCreated { asset_id, owner }) => {
|
||||
asset_id: *asset_id == id,
|
||||
owner: *owner == asset_owner,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
assert!(<Self as [<$chain ParaPallet>]>::Assets::asset_exists(id.clone().into()));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_assets_helpers_for_teyrchain {
|
||||
($chain:ident) => {
|
||||
$crate::impls::paste::paste! {
|
||||
impl<N: $crate::impls::Network> $chain<N> {
|
||||
/// 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::pezpallet_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::pezpallet_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::pezpallet_assets::Call::<
|
||||
<Self as Chain>::Runtime,
|
||||
$crate::impls::pezpallet_assets::Instance1,
|
||||
>::create {
|
||||
id: asset_id.into(),
|
||||
min_balance,
|
||||
admin: admin.into(),
|
||||
})
|
||||
.encode()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_foreign_assets_helpers_for_teyrchain {
|
||||
($chain:ident, $asset_id_type:ty, $reserve_data_type:ty) => {
|
||||
$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(
|
||||
id: $asset_id_type,
|
||||
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,
|
||||
id.clone(),
|
||||
owner.clone().into(),
|
||||
is_sufficient,
|
||||
min_balance,
|
||||
)
|
||||
);
|
||||
assert!(<Self as [<$chain ParaPallet>]>::ForeignAssets::asset_exists(id.clone()));
|
||||
type RuntimeEvent<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
|
||||
$crate::impls::assert_expected_events!(
|
||||
Self,
|
||||
vec![
|
||||
RuntimeEvent::<N>::ForeignAssets(
|
||||
$crate::impls::pezpallet_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);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set reserves for foreign asset using the asset's `owner` account.
|
||||
pub fn set_foreign_asset_reserves(
|
||||
id: $asset_id_type,
|
||||
owner: $crate::impls::AccountId,
|
||||
reserves: Vec<$reserve_data_type>,
|
||||
) {
|
||||
use $crate::impls::Inspect;
|
||||
let owner_origin =
|
||||
<$chain<N> as $crate::impls::Chain>::RuntimeOrigin::signed(owner.clone());
|
||||
<Self as $crate::impls::TestExt>::execute_with(|| {
|
||||
$crate::impls::assert_ok!(
|
||||
<Self as [<$chain ParaPallet>]>::ForeignAssets::set_reserves(
|
||||
owner_origin,
|
||||
id.clone(),
|
||||
reserves,
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Mint assets making use of the ForeignAssets pezpallet-assets instance
|
||||
pub fn mint_foreign_asset(
|
||||
signed_origin: <Self as $crate::impls::Chain>::RuntimeOrigin,
|
||||
id: $asset_id_type,
|
||||
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,
|
||||
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>::ForeignAssets(
|
||||
$crate::impls::pezpallet_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 foreign assets pallet
|
||||
pub fn create_foreign_asset_call(
|
||||
asset_id: $asset_id_type,
|
||||
min_balance: $crate::impls::Balance,
|
||||
admin: $crate::impls::AccountId,
|
||||
) -> $crate::impls::DoubleEncoded<()> {
|
||||
use $crate::impls::{Chain, Encode};
|
||||
|
||||
<Self as Chain>::RuntimeCall::ForeignAssets($crate::impls::pezpallet_assets::Call::<
|
||||
<Self as Chain>::Runtime,
|
||||
$crate::impls::pezpallet_assets::Instance2,
|
||||
>::create {
|
||||
id: asset_id.into(),
|
||||
min_balance,
|
||||
admin: admin.into(),
|
||||
})
|
||||
.encode()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_xcm_helpers_for_teyrchain {
|
||||
( $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>]>::PezkuwiXcm::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>]>::PezkuwiXcm::force_default_xcm_version(
|
||||
<Self as $crate::impls::Chain>::RuntimeOrigin::root(),
|
||||
version,
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_bridge_helpers_for_chain {
|
||||
( $chain:ident, $pallet:ident, $pezpallet_xcm:ident, $runtime_call_wrapper:path ) => {
|
||||
$crate::impls::paste::paste! {
|
||||
impl<N: $crate::impls::Network> $chain<N> {
|
||||
/// Open bridge with `dest`.
|
||||
pub fn open_bridge(
|
||||
bridge_location: $crate::impls::Location,
|
||||
bridge_destination_universal_location: $crate::impls::InteriorLocation,
|
||||
maybe_paid: Option<($crate::impls::Asset, $crate::impls::AccountId)>
|
||||
) {
|
||||
<Self as $crate::impls::TestExt>::execute_with(|| {
|
||||
use $crate::impls::{bx, Chain};
|
||||
use $crate::impls::XcmBridgeHubCall;
|
||||
use $crate::impls::Encode;
|
||||
|
||||
// important to use `root` and `OriginKind::Xcm`
|
||||
let root_origin = <Self as Chain>::RuntimeOrigin::root();
|
||||
|
||||
// construct call
|
||||
let call: $crate::impls::DoubleEncoded<()> = $runtime_call_wrapper(XcmBridgeHubCall::open_bridge {
|
||||
bridge_destination_universal_location: bx!(
|
||||
bridge_destination_universal_location.clone().into()
|
||||
)
|
||||
}).encode().into();
|
||||
|
||||
let xcm = if let Some((fee_asset, beneficiary)) = maybe_paid {
|
||||
$crate::impls::xcm_transact_paid_execution(call, $crate::impls::OriginKind::Xcm, fee_asset, beneficiary)
|
||||
} else {
|
||||
$crate::impls::xcm_transact_unpaid_execution(call, $crate::impls::OriginKind::Xcm)
|
||||
};
|
||||
|
||||
// Send XCM `Transact` with `open_bridge` call
|
||||
$crate::impls::assert_ok!(<Self as [<$chain $pallet>]>::$pezpallet_xcm::send(
|
||||
root_origin,
|
||||
bx!(bridge_location.into()),
|
||||
bx!(xcm),
|
||||
));
|
||||
Self::assert_xcm_pallet_sent();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
// 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 mod impls;
|
||||
pub mod macros;
|
||||
pub mod xcm_helpers;
|
||||
|
||||
use codec::Encode;
|
||||
use cumulus_primitives_core::relay_chain::Slot;
|
||||
pub use xcm_emulator;
|
||||
pub use xcm_simulator;
|
||||
|
||||
// Bizinikiwi
|
||||
use pezframe_support::parameter_types;
|
||||
use pezsc_consensus_grandpa::AuthorityId as GrandpaId;
|
||||
use pezsp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
|
||||
use pezsp_consensus_babe::AuthorityId as BabeId;
|
||||
use pezsp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId;
|
||||
use pezsp_core::storage::Storage;
|
||||
use pezsp_keyring::{Ed25519Keyring, Sr25519Keyring};
|
||||
use pezsp_runtime::{traits::AccountIdConversion, BuildStorage, Digest, DigestItem};
|
||||
|
||||
// Pezkuwi
|
||||
use pezkuwi_runtime_teyrchains::configuration::HostConfiguration;
|
||||
use pezkuwi_teyrchain_primitives::primitives::Sibling;
|
||||
use teyrchains_common::BlockNumber;
|
||||
|
||||
// Pezcumulus
|
||||
use pezkuwi_primitives::{AssignmentId, ValidatorId};
|
||||
use pezsp_runtime::traits::Convert;
|
||||
use teyrchains_common::{AccountId, AuraId};
|
||||
use xcm_emulator::{RelayBlockNumber, AURA_ENGINE_ID};
|
||||
|
||||
pub const XCM_V2: u32 = 2;
|
||||
pub const XCM_V3: u32 = 3;
|
||||
pub const XCM_V4: u32 = 4;
|
||||
pub const XCM_V5: u32 = 5;
|
||||
pub const REF_TIME_THRESHOLD: u64 = 33;
|
||||
pub const PROOF_SIZE_THRESHOLD: u64 = 33;
|
||||
|
||||
/// The default XCM version to set in genesis config.
|
||||
pub const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION;
|
||||
|
||||
// (trust-backed) Asset registered on AH and reserve-transferred between Teyrchain and AH
|
||||
pub const RESERVABLE_ASSET_ID: u32 = 1;
|
||||
// ForeignAsset registered on AH and teleported between Penpal and AH
|
||||
pub const TELEPORTABLE_ASSET_ID: u32 = 2;
|
||||
|
||||
// USDT registered on AH as (trust-backed) Asset and reserve-transferred between Teyrchain and AH
|
||||
pub const USDT_ID: u32 = 1984;
|
||||
|
||||
pub const PENPAL_A_ID: u32 = 2000;
|
||||
pub const PENPAL_B_ID: u32 = 2001;
|
||||
pub const ASSET_HUB_PEZKUWICHAIN_ID: u32 = 1000;
|
||||
pub const ASSET_HUB_ZAGROS_ID: u32 = 1000;
|
||||
pub const ASSETS_PALLET_ID: u8 = 50;
|
||||
|
||||
pub struct AuraDigestProvider {}
|
||||
|
||||
impl Convert<(BlockNumber, RelayBlockNumber), Digest> for AuraDigestProvider {
|
||||
fn convert((_, relay_block_number): (BlockNumber, RelayBlockNumber)) -> Digest {
|
||||
let slot: Slot = (relay_block_number as u64).into();
|
||||
let mut digest = Digest::default();
|
||||
digest.logs.push(DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode()));
|
||||
digest
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub PenpalALocation: xcm::v5::Location
|
||||
= xcm::v5::Location::new(1, [xcm::v5::Junction::Teyrchain(PENPAL_A_ID)]);
|
||||
pub PenpalBLocation: xcm::v5::Location
|
||||
= xcm::v5::Location::new(1, [xcm::v5::Junction::Teyrchain(PENPAL_B_ID)]);
|
||||
pub PenpalATeleportableAssetLocation: xcm::v5::Location
|
||||
= xcm::v5::Location::new(1, [
|
||||
xcm::v5::Junction::Teyrchain(PENPAL_A_ID),
|
||||
xcm::v5::Junction::PalletInstance(ASSETS_PALLET_ID),
|
||||
xcm::v5::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()),
|
||||
]
|
||||
);
|
||||
pub PenpalBTeleportableAssetLocation: xcm::v5::Location
|
||||
= xcm::v5::Location::new(1, [
|
||||
xcm::v5::Junction::Teyrchain(PENPAL_B_ID),
|
||||
xcm::v5::Junction::PalletInstance(ASSETS_PALLET_ID),
|
||||
xcm::v5::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()),
|
||||
]
|
||||
);
|
||||
pub PenpalASiblingSovereignAccount: AccountId = Sibling::from(PENPAL_A_ID).into_account_truncating();
|
||||
pub PenpalBSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_B_ID).into_account_truncating();
|
||||
}
|
||||
|
||||
pub fn get_host_config() -> HostConfiguration<BlockNumber> {
|
||||
HostConfiguration {
|
||||
max_upward_queue_count: 10,
|
||||
max_upward_queue_size: 51200,
|
||||
max_upward_message_size: 51200,
|
||||
max_upward_message_num_per_candidate: 10,
|
||||
max_downward_message_size: 51200,
|
||||
hrmp_sender_deposit: 0,
|
||||
hrmp_recipient_deposit: 0,
|
||||
hrmp_channel_max_capacity: 1000,
|
||||
hrmp_channel_max_message_size: 102400,
|
||||
hrmp_channel_max_total_size: 102400,
|
||||
hrmp_max_teyrchain_outbound_channels: 30,
|
||||
hrmp_max_teyrchain_inbound_channels: 30,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function used in tests to build the genesis storage using given RuntimeGenesisConfig and
|
||||
/// code Used in `legacy_vs_json_check` submods to verify storage building with JSON patch against
|
||||
/// building with RuntimeGenesisConfig struct.
|
||||
pub fn build_genesis_storage(builder: &dyn BuildStorage, code: &[u8]) -> Storage {
|
||||
let mut storage = builder.build_storage().unwrap();
|
||||
storage
|
||||
.top
|
||||
.insert(pezsp_core::storage::well_known_keys::CODE.to_vec(), code.into());
|
||||
storage
|
||||
}
|
||||
|
||||
pub mod accounts {
|
||||
use super::*;
|
||||
pub const ALICE: &str = "Alice";
|
||||
pub const BOB: &str = "Bob";
|
||||
pub const DUMMY_EMPTY: &str = "JohnDoe";
|
||||
|
||||
pub fn init_balances() -> Vec<AccountId> {
|
||||
Sr25519Keyring::well_known().map(|k| k.to_account_id()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod collators {
|
||||
use super::*;
|
||||
|
||||
pub fn invulnerables() -> Vec<(AccountId, AuraId)> {
|
||||
vec![
|
||||
(Sr25519Keyring::Dave.to_account_id(), Sr25519Keyring::Dave.public().into()),
|
||||
(Sr25519Keyring::Eve.to_account_id(), Sr25519Keyring::Eve.public().into()),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub mod validators {
|
||||
use pezsp_consensus_beefy::test_utils::Keyring;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub fn initial_authorities() -> Vec<(
|
||||
AccountId,
|
||||
AccountId,
|
||||
BabeId,
|
||||
GrandpaId,
|
||||
ValidatorId,
|
||||
AssignmentId,
|
||||
AuthorityDiscoveryId,
|
||||
BeefyId,
|
||||
)> {
|
||||
vec![(
|
||||
Sr25519Keyring::AliceStash.to_account_id(),
|
||||
Sr25519Keyring::Alice.to_account_id(),
|
||||
BabeId::from(Sr25519Keyring::Alice.public()),
|
||||
GrandpaId::from(Ed25519Keyring::Alice.public()),
|
||||
ValidatorId::from(Sr25519Keyring::Alice.public()),
|
||||
AssignmentId::from(Sr25519Keyring::Alice.public()),
|
||||
AuthorityDiscoveryId::from(Sr25519Keyring::Alice.public()),
|
||||
BeefyId::from(Keyring::<BeefyId>::Alice.public()),
|
||||
)]
|
||||
}
|
||||
}
|
||||
|
||||
pub mod snowbridge {
|
||||
use hex_literal::hex;
|
||||
// Address of WETH ERC20 token contract on remote Ethereum network
|
||||
pub const WETH: [u8; 20] = hex!("fff9976782d46cc05630d1f6ebab18b2324d6b14");
|
||||
// The Ethereum network chain ID. In this case, Sepolia testnet's chain ID.
|
||||
pub const SEPOLIA_ID: u64 = 11155111;
|
||||
// The minimum balance for ether assets pre-registered in emulated tests.
|
||||
pub const ETHER_MIN_BALANCE: u128 = 1000;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,201 @@
|
||||
// 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.
|
||||
|
||||
// Pezcumulus
|
||||
use teyrchains_common::AccountId;
|
||||
|
||||
// Pezkuwi
|
||||
use pezsp_core::H256;
|
||||
use xcm::{prelude::*, DoubleEncoded};
|
||||
use xcm_emulator::Chain;
|
||||
|
||||
use crate::impls::{bx, Encode};
|
||||
use pezframe_support::dispatch::{DispatchResultWithPostInfo, PostDispatchInfo};
|
||||
use pezsp_runtime::traits::{Dispatchable, Hash};
|
||||
use xcm::{VersionedLocation, VersionedXcm};
|
||||
|
||||
/// Helper method to build a XCM with a `Transact` instruction and paying for its execution
|
||||
pub fn xcm_transact_paid_execution(
|
||||
call: DoubleEncoded<()>,
|
||||
origin_kind: OriginKind,
|
||||
fees: Asset,
|
||||
beneficiary: AccountId,
|
||||
) -> VersionedXcm<()> {
|
||||
let weight_limit = WeightLimit::Unlimited;
|
||||
|
||||
VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(fees.clone().into()),
|
||||
BuyExecution { fees, weight_limit },
|
||||
Transact { origin_kind, call, fallback_max_weight: None },
|
||||
ExpectTransactStatus(MaybeErrorCode::Success),
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: All.into(),
|
||||
beneficiary: Location {
|
||||
parents: 0,
|
||||
interior: [AccountId32 { network: None, id: beneficiary.into() }].into(),
|
||||
},
|
||||
},
|
||||
]))
|
||||
}
|
||||
|
||||
/// Helper method to build a XCM with a `Transact` instruction without paying for its execution
|
||||
pub fn xcm_transact_unpaid_execution(
|
||||
call: DoubleEncoded<()>,
|
||||
origin_kind: OriginKind,
|
||||
) -> VersionedXcm<()> {
|
||||
let weight_limit = WeightLimit::Unlimited;
|
||||
let check_origin = None;
|
||||
|
||||
VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit, check_origin },
|
||||
Transact { origin_kind, call, fallback_max_weight: None },
|
||||
ExpectTransactStatus(MaybeErrorCode::Success),
|
||||
]))
|
||||
}
|
||||
|
||||
/// Helper method to get the non-fee asset used in multiple assets transfer
|
||||
pub fn non_fee_asset(assets: &Assets, fee_asset_id: &AssetId) -> Option<(Location, u128)> {
|
||||
let asset = assets.inner().into_iter().find(|a| a.id != *fee_asset_id)?;
|
||||
let asset_amount = match asset.fun {
|
||||
Fungible(amount) => amount,
|
||||
_ => return None,
|
||||
};
|
||||
Some((asset.id.0.clone(), asset_amount))
|
||||
}
|
||||
|
||||
/// Helper method to get the fee asset used in multiple assets transfer
|
||||
pub fn fee_asset(assets: &Assets, fee_asset_id: &AssetId) -> Option<(Location, u128)> {
|
||||
let asset = assets.inner().iter().find(|a| a.id == *fee_asset_id)?;
|
||||
let asset_amount = match asset.fun {
|
||||
Fungible(amount) => amount,
|
||||
_ => return None,
|
||||
};
|
||||
Some((asset.id.0.clone(), asset_amount))
|
||||
}
|
||||
|
||||
pub fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 {
|
||||
let latest_assets: Assets = assets.try_into().unwrap();
|
||||
let Fungible(amount) = latest_assets.inner()[0].fun else {
|
||||
unreachable!("asset is non-fungible");
|
||||
};
|
||||
amount
|
||||
}
|
||||
|
||||
fn to_mq_processed_id<C: Chain>(event: C::RuntimeEvent) -> Option<H256>
|
||||
where
|
||||
<C as Chain>::Runtime: pezpallet_message_queue::Config,
|
||||
C::RuntimeEvent: TryInto<pezpallet_message_queue::Event<<C as Chain>::Runtime>>,
|
||||
{
|
||||
if let Ok(pezpallet_message_queue::Event::Processed { id, .. }) = event.try_into() {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper method to find all `Event::Processed` IDs from the chain's events.
|
||||
pub fn find_all_mq_processed_ids<C: Chain>() -> Vec<H256>
|
||||
where
|
||||
<C as Chain>::Runtime: pezpallet_message_queue::Config,
|
||||
C::RuntimeEvent: TryInto<pezpallet_message_queue::Event<<C as Chain>::Runtime>>,
|
||||
{
|
||||
C::events().into_iter().filter_map(to_mq_processed_id::<C>).collect()
|
||||
}
|
||||
|
||||
/// Helper method to find the ID of the first `Event::Processed` event in the chain's events.
|
||||
pub fn find_mq_processed_id<C: Chain>() -> Option<H256>
|
||||
where
|
||||
<C as Chain>::Runtime: pezpallet_message_queue::Config,
|
||||
C::RuntimeEvent: TryInto<pezpallet_message_queue::Event<<C as Chain>::Runtime>>,
|
||||
{
|
||||
C::events().into_iter().find_map(to_mq_processed_id::<C>)
|
||||
}
|
||||
|
||||
/// Helper method to find the message ID of the first `Event::Sent` event in the chain's events.
|
||||
pub fn find_xcm_sent_message_id<
|
||||
C: Chain<RuntimeEvent = <<C as Chain>::Runtime as pezpallet_xcm::Config>::RuntimeEvent>,
|
||||
>() -> Option<XcmHash>
|
||||
where
|
||||
C::Runtime: pezpallet_xcm::Config,
|
||||
C::RuntimeEvent: TryInto<pezpallet_xcm::Event<C::Runtime>>,
|
||||
{
|
||||
pezpallet_xcm::xcm_helpers::find_xcm_sent_message_id::<<C as Chain>::Runtime>(C::events())
|
||||
}
|
||||
|
||||
/// Wraps a runtime call in a whitelist preimage call and dispatches it
|
||||
pub fn dispatch_whitelisted_call_with_preimage<T>(
|
||||
call: T::RuntimeCall,
|
||||
origin: T::RuntimeOrigin,
|
||||
) -> DispatchResultWithPostInfo
|
||||
where
|
||||
T: Chain,
|
||||
T::Runtime: pezpallet_whitelist::Config,
|
||||
T::RuntimeCall: From<pezpallet_whitelist::Call<T::Runtime>>
|
||||
+ Into<<T::Runtime as pezpallet_whitelist::Config>::RuntimeCall>
|
||||
+ Dispatchable<RuntimeOrigin = T::RuntimeOrigin, PostInfo = PostDispatchInfo>,
|
||||
{
|
||||
T::execute_with(|| {
|
||||
let whitelist_call: T::RuntimeCall =
|
||||
pezpallet_whitelist::Call::<T::Runtime>::dispatch_whitelisted_call_with_preimage {
|
||||
call: Box::new(call.into()),
|
||||
}
|
||||
.into();
|
||||
whitelist_call.dispatch(origin)
|
||||
})
|
||||
}
|
||||
|
||||
/// Builds a `pezpallet_xcm::send` call to authorize an upgrade at the provided location,
|
||||
/// wrapped in an unpaid XCM `Transact` with `OriginKind::Superuser`.
|
||||
pub fn build_xcm_send_authorize_upgrade_call<T, D>(
|
||||
location: Location,
|
||||
code_hash: &H256,
|
||||
fallback_max_weight: Option<Weight>,
|
||||
) -> T::RuntimeCall
|
||||
where
|
||||
T: Chain,
|
||||
T::Runtime: pezpallet_xcm::Config,
|
||||
T::RuntimeCall: Encode + From<pezpallet_xcm::Call<T::Runtime>>,
|
||||
D: Chain,
|
||||
D::Runtime: pezframe_system::Config<Hash = H256>,
|
||||
D::RuntimeCall: Encode + From<pezframe_system::Call<D::Runtime>>,
|
||||
{
|
||||
let transact_call: D::RuntimeCall =
|
||||
pezframe_system::Call::authorize_upgrade { code_hash: *code_hash }.into();
|
||||
|
||||
let call: T::RuntimeCall = pezpallet_xcm::Call::send {
|
||||
dest: bx!(VersionedLocation::from(location)),
|
||||
message: bx!(VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
Transact {
|
||||
origin_kind: OriginKind::Superuser,
|
||||
fallback_max_weight,
|
||||
call: transact_call.encode().into(),
|
||||
}
|
||||
]))),
|
||||
}
|
||||
.into();
|
||||
call
|
||||
}
|
||||
|
||||
/// Encodes a runtime call and returns its H256 hash
|
||||
pub fn call_hash_of<T>(call: &T::RuntimeCall) -> H256
|
||||
where
|
||||
T: Chain,
|
||||
T::Runtime: pezframe_system::Config<Hash = H256>,
|
||||
T::RuntimeCall: Encode,
|
||||
{
|
||||
<T::Runtime as pezframe_system::Config>::Hashing::hash_of(&call)
|
||||
}
|
||||
Reference in New Issue
Block a user