mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 19:51:02 +00:00
Snowbridge Ethereum Deneb fork preparation (#3029)
- Prepares for the Deneb hardfork on Sepolia testnet on 31 January (needs to be deployed to Rococo before then) - Removes `beacon-minimal-spec` flag for simpler config - Adds test comments --------- Co-authored-by: Ron <yrong1997@gmail.com> Co-authored-by: claravanstaden <Cats 4 life!> Co-authored-by: Alistair Singh <alistair.singh7@gmail.com>
This commit is contained in:
@@ -100,7 +100,7 @@ pub mod accounts {
|
||||
pub const CHARLIE: &str = "Charlie";
|
||||
pub const DAVE: &str = "Dave";
|
||||
pub const EVE: &str = "Eve";
|
||||
pub const FERDIE: &str = "Ferdei";
|
||||
pub const FERDIE: &str = "Ferdie";
|
||||
pub const ALICE_STASH: &str = "Alice//stash";
|
||||
pub const BOB_STASH: &str = "Bob//stash";
|
||||
pub const CHARLIE_STASH: &str = "Charlie//stash";
|
||||
|
||||
+2
@@ -47,3 +47,5 @@ snowbridge-core = { path = "../../../../../../../bridges/snowbridge/parachain/pr
|
||||
snowbridge-router-primitives = { path = "../../../../../../../bridges/snowbridge/parachain/primitives/router", default-features = false }
|
||||
snowbridge-pallet-system = { path = "../../../../../../../bridges/snowbridge/parachain/pallets/system", default-features = false }
|
||||
snowbridge-pallet-outbound-queue = { path = "../../../../../../../bridges/snowbridge/parachain/pallets/outbound-queue", default-features = false }
|
||||
snowbridge-pallet-inbound-queue = { path = "../../../../../../../bridges/snowbridge/parachain/pallets/inbound-queue", default-features = false }
|
||||
snowbridge-pallet-inbound-queue-fixtures = { path = "../../../../../../../bridges/snowbridge/parachain/pallets/inbound-queue/fixtures" }
|
||||
|
||||
+5
-7
@@ -43,11 +43,6 @@ pub use emulated_integration_tests_common::{
|
||||
PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3,
|
||||
};
|
||||
pub use parachains_common::{AccountId, Balance};
|
||||
pub use rococo_system_emulated_network::{
|
||||
penpal_emulated_chain::PenpalAParaPallet as PenpalAPallet,
|
||||
BridgeHubRococoParaReceiver as BridgeHubRococoReceiver, PenpalAPara as PenpalA,
|
||||
PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender,
|
||||
};
|
||||
pub use rococo_westend_system_emulated_network::{
|
||||
asset_hub_rococo_emulated_chain::{
|
||||
genesis::ED as ASSET_HUB_ROCOCO_ED, AssetHubRococoParaPallet as AssetHubRococoPallet,
|
||||
@@ -58,14 +53,17 @@ pub use rococo_westend_system_emulated_network::{
|
||||
bridge_hub_rococo_emulated_chain::{
|
||||
genesis::ED as BRIDGE_HUB_ROCOCO_ED, BridgeHubRococoParaPallet as BridgeHubRococoPallet,
|
||||
},
|
||||
penpal_emulated_chain::PenpalAParaPallet as PenpalAPallet,
|
||||
rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet},
|
||||
AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver,
|
||||
AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend,
|
||||
AssetHubWestendParaReceiver as AssetHubWestendReceiver,
|
||||
AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo,
|
||||
BridgeHubRococoParaReceiver as BridgeHubRococoReceiver,
|
||||
BridgeHubRococoParaSender as BridgeHubRococoSender, BridgeHubWestendPara as BridgeHubWestend,
|
||||
RococoRelay as Rococo, RococoRelayReceiver as RococoReceiver,
|
||||
RococoRelaySender as RococoSender,
|
||||
PenpalAPara as PenpalA, PenpalAParaReceiver as PenpalAReceiver,
|
||||
PenpalAParaSender as PenpalASender, RococoRelay as Rococo,
|
||||
RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender,
|
||||
};
|
||||
|
||||
pub const ASSET_ID: u32 = 1;
|
||||
|
||||
+138
-106
@@ -13,17 +13,24 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use crate::*;
|
||||
use bridge_hub_rococo_runtime::{EthereumBeaconClient, EthereumInboundQueue, RuntimeOrigin};
|
||||
use codec::{Decode, Encode};
|
||||
use emulated_integration_tests_common::xcm_emulator::ConvertLocation;
|
||||
use frame_support::pallet_prelude::TypeInfo;
|
||||
use hex_literal::hex;
|
||||
use parachains_common::rococo::snowbridge::EthereumNetwork;
|
||||
use rococo_westend_system_emulated_network::BridgeHubRococoParaSender as BridgeHubRococoSender;
|
||||
use snowbridge_core::outbound::OperatingMode;
|
||||
use snowbridge_pallet_system;
|
||||
use snowbridge_router_primitives::inbound::{
|
||||
Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage,
|
||||
use snowbridge_pallet_inbound_queue_fixtures::{
|
||||
register_token::make_register_token_message,
|
||||
register_token_with_insufficient_fee::make_register_token_with_infufficient_fee_message,
|
||||
send_token::make_send_token_message, send_token_to_penpal::make_send_token_to_penpal_message,
|
||||
InboundQueueFixture,
|
||||
};
|
||||
use snowbridge_pallet_system;
|
||||
use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{ArithmeticError::Underflow, DispatchError::Arithmetic};
|
||||
|
||||
const INITIAL_FUND: u128 = 5_000_000_000 * ROCOCO_ED;
|
||||
const CHAIN_ID: u64 = 11155111;
|
||||
@@ -31,7 +38,6 @@ const TREASURY_ACCOUNT: [u8; 32] =
|
||||
hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000");
|
||||
const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d");
|
||||
const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e");
|
||||
const XCM_FEE: u128 = 4_000_000_000;
|
||||
|
||||
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
|
||||
pub enum ControlCall {
|
||||
@@ -48,17 +54,34 @@ pub enum SnowbridgeControl {
|
||||
Control(ControlCall),
|
||||
}
|
||||
|
||||
pub fn send_inbound_message(fixture: InboundQueueFixture) -> DispatchResult {
|
||||
EthereumBeaconClient::store_execution_header(
|
||||
fixture.message.proof.block_hash,
|
||||
fixture.execution_header,
|
||||
0,
|
||||
H256::default(),
|
||||
);
|
||||
|
||||
EthereumInboundQueue::submit(
|
||||
RuntimeOrigin::signed(BridgeHubRococoSender::get()),
|
||||
fixture.message,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create an agent on Ethereum. An agent is a representation of an entity in the Polkadot
|
||||
/// ecosystem (like a parachain) on Ethereum.
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn create_agent() {
|
||||
let origin_para: u32 = 1001;
|
||||
|
||||
// Fund the origin parachain sovereign account so that it can pay execution fees.
|
||||
BridgeHubRococo::fund_para_sovereign(origin_para.into(), INITIAL_FUND);
|
||||
|
||||
let sudo_origin = <Rococo as Chain>::RuntimeOrigin::root();
|
||||
let destination = Rococo::child_location_of(BridgeHubRococo::para_id()).into();
|
||||
|
||||
let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {});
|
||||
|
||||
// Construct XCM to create an agent for para 1001
|
||||
let remote_xcm = VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
DescendOrigin(Parachain(origin_para).into()),
|
||||
@@ -69,7 +92,7 @@ fn create_agent() {
|
||||
},
|
||||
]));
|
||||
|
||||
//Rococo Global Consensus
|
||||
// Rococo Global Consensus
|
||||
// Send XCM message from Relay Chain to Bridge Hub source Parachain
|
||||
Rococo::execute_with(|| {
|
||||
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
|
||||
@@ -79,7 +102,7 @@ fn create_agent() {
|
||||
));
|
||||
|
||||
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
|
||||
|
||||
// Check that the Transact message was sent
|
||||
assert_expected_events!(
|
||||
Rococo,
|
||||
vec![
|
||||
@@ -90,7 +113,7 @@ fn create_agent() {
|
||||
|
||||
BridgeHubRococo::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
|
||||
|
||||
// Check that a message was sent to Ethereum to create the agent
|
||||
assert_expected_events!(
|
||||
BridgeHubRococo,
|
||||
vec![
|
||||
@@ -102,10 +125,13 @@ fn create_agent() {
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a channel for a consensus system. A channel is a bidirectional messaging channel
|
||||
/// between BridgeHub and Ethereum.
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn create_channel() {
|
||||
let origin_para: u32 = 1001;
|
||||
|
||||
// Fund AssetHub sovereign account so that it can pay execution fees.
|
||||
BridgeHubRococo::fund_para_sovereign(origin_para.into(), INITIAL_FUND);
|
||||
|
||||
let sudo_origin = <Rococo as Chain>::RuntimeOrigin::root();
|
||||
@@ -113,7 +139,7 @@ fn create_channel() {
|
||||
Rococo::child_location_of(BridgeHubRococo::para_id()).into();
|
||||
|
||||
let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {});
|
||||
|
||||
// Construct XCM to create an agent for para 1001
|
||||
let create_agent_xcm = VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
DescendOrigin(Parachain(origin_para).into()),
|
||||
@@ -126,7 +152,7 @@ fn create_channel() {
|
||||
|
||||
let create_channel_call =
|
||||
SnowbridgeControl::Control(ControlCall::CreateChannel { mode: OperatingMode::Normal });
|
||||
|
||||
// Construct XCM to create a channel for para 1001
|
||||
let create_channel_xcm = VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
DescendOrigin(Parachain(origin_para).into()),
|
||||
@@ -137,7 +163,7 @@ fn create_channel() {
|
||||
},
|
||||
]));
|
||||
|
||||
//Rococo Global Consensus
|
||||
// Rococo Global Consensus
|
||||
// Send XCM message from Relay Chain to Bridge Hub source Parachain
|
||||
Rococo::execute_with(|| {
|
||||
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
|
||||
@@ -165,6 +191,7 @@ fn create_channel() {
|
||||
BridgeHubRococo::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
|
||||
|
||||
// Check that the Channel was created
|
||||
assert_expected_events!(
|
||||
BridgeHubRococo,
|
||||
vec![
|
||||
@@ -176,25 +203,18 @@ fn create_channel() {
|
||||
});
|
||||
}
|
||||
|
||||
/// Tests the registering of a token as an asset on AssetHub.
|
||||
#[test]
|
||||
fn register_weth_token_from_ethereum_to_asset_hub() {
|
||||
// Fund AssetHub sovereign account so that it can pay execution fees.
|
||||
BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND);
|
||||
|
||||
let message_id_: H256 = [1; 32].into();
|
||||
|
||||
BridgeHubRococo::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
|
||||
type EthereumInboundQueue =
|
||||
<BridgeHubRococo as BridgeHubRococoPallet>::EthereumInboundQueue;
|
||||
let message = VersionedMessage::V1(MessageV1 {
|
||||
chain_id: CHAIN_ID,
|
||||
command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE },
|
||||
});
|
||||
let (xcm, fee) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
|
||||
|
||||
assert_ok!(EthereumInboundQueue::burn_fees(AssetHubRococo::para_id().into(), fee));
|
||||
|
||||
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
|
||||
// Construct RegisterToken message and sent to inbound queue
|
||||
let register_token_message = make_register_token_message();
|
||||
send_inbound_message(register_token_message.clone()).unwrap();
|
||||
|
||||
assert_expected_events!(
|
||||
BridgeHubRococo,
|
||||
@@ -216,46 +236,38 @@ fn register_weth_token_from_ethereum_to_asset_hub() {
|
||||
});
|
||||
}
|
||||
|
||||
/// Tests sending a token to a 3rd party parachain, called PenPal. The token reserve is
|
||||
/// still located on AssetHub.
|
||||
#[test]
|
||||
fn send_token_from_ethereum_to_penpal() {
|
||||
let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new(
|
||||
1,
|
||||
[Parachain(AssetHubRococo::para_id().into())],
|
||||
));
|
||||
// Fund AssetHub sovereign account so it can pay execution fees for the asset transfer
|
||||
BridgeHubRococo::fund_accounts(vec![(asset_hub_sovereign.clone(), INITIAL_FUND)]);
|
||||
|
||||
// Fund PenPal sender and receiver
|
||||
PenpalA::fund_accounts(vec![
|
||||
(PenpalAReceiver::get(), INITIAL_FUND),
|
||||
(PenpalASender::get(), INITIAL_FUND),
|
||||
]);
|
||||
|
||||
// The Weth asset location, identified by the contract address on Ethereum
|
||||
let weth_asset_location: Location =
|
||||
(Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into();
|
||||
// Converts the Weth asset location into an asset ID
|
||||
let weth_asset_id: v3::Location = weth_asset_location.try_into().unwrap();
|
||||
|
||||
let origin_location = (Parent, Parent, EthereumNetwork::get()).into();
|
||||
|
||||
// Fund ethereum sovereign in asset hub
|
||||
// Fund ethereum sovereign on AssetHub
|
||||
let ethereum_sovereign: AccountId =
|
||||
GlobalConsensusEthereumConvertsFor::<AccountId>::convert_location(&origin_location)
|
||||
.unwrap();
|
||||
AssetHubRococo::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
|
||||
// Create asset on assethub.
|
||||
AssetHubRococo::execute_with(|| {
|
||||
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::ForeignAssets::create(
|
||||
pallet_xcm::Origin::Xcm(origin_location).into(),
|
||||
weth_asset_id,
|
||||
asset_hub_sovereign.clone().into(),
|
||||
1000,
|
||||
));
|
||||
|
||||
assert!(<AssetHubRococo as AssetHubRococoPallet>::ForeignAssets::asset_exists(
|
||||
weth_asset_id
|
||||
));
|
||||
});
|
||||
|
||||
// Create asset on penpal.
|
||||
// Create asset on the Penpal parachain.
|
||||
PenpalA::execute_with(|| {
|
||||
assert_ok!(<PenpalA as PenpalAPallet>::ForeignAssets::create(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalASender::get()),
|
||||
@@ -267,27 +279,14 @@ fn send_token_from_ethereum_to_penpal() {
|
||||
assert!(<PenpalA as PenpalAPallet>::ForeignAssets::asset_exists(weth_asset_id));
|
||||
});
|
||||
|
||||
let message_id_: H256 = [1; 32].into();
|
||||
|
||||
BridgeHubRococo::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
|
||||
type EthereumInboundQueue =
|
||||
<BridgeHubRococo as BridgeHubRococoPallet>::EthereumInboundQueue;
|
||||
let message = VersionedMessage::V1(MessageV1 {
|
||||
chain_id: CHAIN_ID,
|
||||
command: Command::SendToken {
|
||||
token: WETH.into(),
|
||||
destination: Destination::ForeignAccountId32 {
|
||||
para_id: 2000,
|
||||
id: PenpalAReceiver::get().into(),
|
||||
fee: XCM_FEE,
|
||||
},
|
||||
amount: 1_000_000_000,
|
||||
fee: XCM_FEE,
|
||||
},
|
||||
});
|
||||
let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
|
||||
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
|
||||
|
||||
// Construct RegisterToken message and sent to inbound queue
|
||||
send_inbound_message(make_register_token_message()).unwrap();
|
||||
|
||||
// Construct SendToken message and sent to inbound queue
|
||||
send_inbound_message(make_send_token_to_penpal_message()).unwrap();
|
||||
|
||||
assert_expected_events!(
|
||||
BridgeHubRococo,
|
||||
@@ -299,7 +298,7 @@ fn send_token_from_ethereum_to_penpal() {
|
||||
|
||||
AssetHubRococo::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
|
||||
|
||||
// Check that the assets were issued on AssetHub
|
||||
assert_expected_events!(
|
||||
AssetHubRococo,
|
||||
vec![
|
||||
@@ -311,7 +310,7 @@ fn send_token_from_ethereum_to_penpal() {
|
||||
|
||||
PenpalA::execute_with(|| {
|
||||
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
|
||||
|
||||
// Check that the assets were issued on PenPal
|
||||
assert_expected_events!(
|
||||
PenpalA,
|
||||
vec![
|
||||
@@ -321,37 +320,25 @@ fn send_token_from_ethereum_to_penpal() {
|
||||
});
|
||||
}
|
||||
|
||||
/// Tests the registering of a token as an asset on AssetHub, and then subsequently sending
|
||||
/// a token from Ethereum to AssetHub.
|
||||
#[test]
|
||||
fn send_token_from_ethereum_to_asset_hub() {
|
||||
BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND);
|
||||
|
||||
// Fund ethereum sovereign in asset hub
|
||||
// Fund ethereum sovereign on AssetHub
|
||||
AssetHubRococo::fund_accounts(vec![(AssetHubRococoReceiver::get(), INITIAL_FUND)]);
|
||||
|
||||
let message_id_: H256 = [1; 32].into();
|
||||
|
||||
BridgeHubRococo::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
|
||||
type EthereumInboundQueue =
|
||||
<BridgeHubRococo as BridgeHubRococoPallet>::EthereumInboundQueue;
|
||||
let message = VersionedMessage::V1(MessageV1 {
|
||||
chain_id: CHAIN_ID,
|
||||
command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE },
|
||||
});
|
||||
let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
|
||||
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
|
||||
let message = VersionedMessage::V1(MessageV1 {
|
||||
chain_id: CHAIN_ID,
|
||||
command: Command::SendToken {
|
||||
token: WETH.into(),
|
||||
destination: Destination::AccountId32 { id: AssetHubRococoReceiver::get().into() },
|
||||
amount: 1_000_000_000,
|
||||
fee: XCM_FEE,
|
||||
},
|
||||
});
|
||||
let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
|
||||
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
|
||||
|
||||
// Construct RegisterToken message and sent to inbound queue
|
||||
send_inbound_message(make_register_token_message()).unwrap();
|
||||
|
||||
// Construct SendToken message and sent to inbound queue
|
||||
send_inbound_message(make_send_token_message()).unwrap();
|
||||
|
||||
// Check that the message was sent
|
||||
assert_expected_events!(
|
||||
BridgeHubRococo,
|
||||
vec![
|
||||
@@ -363,6 +350,7 @@ fn send_token_from_ethereum_to_asset_hub() {
|
||||
AssetHubRococo::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
|
||||
|
||||
// Check that the token was received and issued as a foreign asset on AssetHub
|
||||
assert_expected_events!(
|
||||
AssetHubRococo,
|
||||
vec![
|
||||
@@ -372,6 +360,10 @@ fn send_token_from_ethereum_to_asset_hub() {
|
||||
});
|
||||
}
|
||||
|
||||
/// Tests the full cycle of token transfers:
|
||||
/// - registering a token on AssetHub
|
||||
/// - sending a token to AssetHub
|
||||
/// - returning the token to Ethereum
|
||||
#[test]
|
||||
fn send_weth_asset_from_asset_hub_to_ethereum() {
|
||||
use asset_hub_rococo_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee;
|
||||
@@ -391,31 +383,25 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
|
||||
AssetHubRococo::fund_accounts(vec![(AssetHubRococoReceiver::get(), INITIAL_FUND)]);
|
||||
|
||||
const WETH_AMOUNT: u128 = 1_000_000_000;
|
||||
let message_id_: H256 = [1; 32].into();
|
||||
|
||||
BridgeHubRococo::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
|
||||
type EthereumInboundQueue =
|
||||
<BridgeHubRococo as BridgeHubRococoPallet>::EthereumInboundQueue;
|
||||
|
||||
let message = VersionedMessage::V1(MessageV1 {
|
||||
chain_id: CHAIN_ID,
|
||||
command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE },
|
||||
});
|
||||
let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
|
||||
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
|
||||
let message = VersionedMessage::V1(MessageV1 {
|
||||
chain_id: CHAIN_ID,
|
||||
command: Command::SendToken {
|
||||
token: WETH.into(),
|
||||
destination: Destination::AccountId32 { id: AssetHubRococoReceiver::get().into() },
|
||||
amount: WETH_AMOUNT,
|
||||
fee: XCM_FEE,
|
||||
},
|
||||
});
|
||||
let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
|
||||
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
|
||||
// Construct RegisterToken message and sent to inbound queue
|
||||
send_inbound_message(make_register_token_message()).unwrap();
|
||||
|
||||
// Check that the register token message was sent using xcm
|
||||
assert_expected_events!(
|
||||
BridgeHubRococo,
|
||||
vec![
|
||||
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
]
|
||||
);
|
||||
|
||||
// Construct SendToken message and sent to inbound queue
|
||||
send_inbound_message(make_send_token_message()).unwrap();
|
||||
|
||||
// Check that the send token message was sent using xcm
|
||||
assert_expected_events!(
|
||||
BridgeHubRococo,
|
||||
vec![
|
||||
@@ -428,6 +414,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
|
||||
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <AssetHubRococo as Chain>::RuntimeOrigin;
|
||||
|
||||
// Check that AssetHub has issued the foreign asset
|
||||
assert_expected_events!(
|
||||
AssetHubRococo,
|
||||
vec![
|
||||
@@ -459,6 +446,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
|
||||
let free_balance_before = <AssetHubRococo as AssetHubRococoPallet>::Balances::free_balance(
|
||||
AssetHubRococoReceiver::get(),
|
||||
);
|
||||
// Send the Weth back to Ethereum
|
||||
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::reserve_transfer_assets(
|
||||
RuntimeOrigin::signed(AssetHubRococoReceiver::get()),
|
||||
Box::new(destination),
|
||||
@@ -470,14 +458,15 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
|
||||
let free_balance_after = <AssetHubRococo as AssetHubRococoPallet>::Balances::free_balance(
|
||||
AssetHubRococoReceiver::get(),
|
||||
);
|
||||
// assert at least DefaultBridgeHubEthereumBaseFee charged from the sender
|
||||
// Assert at least DefaultBridgeHubEthereumBaseFee charged from the sender
|
||||
let free_balance_diff = free_balance_before - free_balance_after;
|
||||
assert!(free_balance_diff > DefaultBridgeHubEthereumBaseFee::get());
|
||||
});
|
||||
|
||||
BridgeHubRococo::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
|
||||
|
||||
// Check that the transfer token back to Ethereum message was queue in the Ethereum
|
||||
// Outbound Queue
|
||||
assert_expected_events!(
|
||||
BridgeHubRococo,
|
||||
vec![
|
||||
@@ -485,6 +474,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
|
||||
]
|
||||
);
|
||||
let events = BridgeHubRococo::events();
|
||||
// Check that the local fee was credited to the Snowbridge sovereign account
|
||||
assert!(
|
||||
events.iter().any(|event| matches!(
|
||||
event,
|
||||
@@ -493,6 +483,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
|
||||
)),
|
||||
"Snowbridge sovereign takes local fee."
|
||||
);
|
||||
// Check that the remote fee was credited to the AssetHub sovereign account
|
||||
assert!(
|
||||
events.iter().any(|event| matches!(
|
||||
event,
|
||||
@@ -503,3 +494,44 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() {
|
||||
BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND);
|
||||
|
||||
BridgeHubRococo::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
|
||||
|
||||
// Construct RegisterToken message and sent to inbound queue
|
||||
let message = make_register_token_with_infufficient_fee_message();
|
||||
send_inbound_message(message).unwrap();
|
||||
|
||||
assert_expected_events!(
|
||||
BridgeHubRococo,
|
||||
vec![
|
||||
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubRococo::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubRococo,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_token_from_ethereum_to_asset_hub_fail_for_insufficient_fund() {
|
||||
// Insufficient fund
|
||||
BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), 1_000);
|
||||
|
||||
BridgeHubRococo::execute_with(|| {
|
||||
assert_err!(send_inbound_message(make_register_token_message()), Arithmetic(Underflow));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -294,9 +294,7 @@ try-runtime = [
|
||||
]
|
||||
|
||||
experimental = ["pallet-aura/experimental"]
|
||||
fast-runtime = [
|
||||
"snowbridge-pallet-ethereum-client/beacon-spec-minimal",
|
||||
]
|
||||
fast-runtime = []
|
||||
|
||||
# A feature that should be enabled when the runtime should be built for on-chain
|
||||
# deployment. This will disable stuff that shouldn't be part of the on-chain wasm
|
||||
|
||||
@@ -498,13 +498,6 @@ impl pallet_utility::Config for Runtime {
|
||||
}
|
||||
|
||||
// Ethereum Bridge
|
||||
|
||||
#[cfg(not(feature = "runtime-benchmarks"))]
|
||||
parameter_types! {
|
||||
pub storage EthereumGatewayAddress: H160 = H160::zero();
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
parameter_types! {
|
||||
pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39"));
|
||||
}
|
||||
@@ -600,29 +593,33 @@ impl snowbridge_pallet_outbound_queue::Config for Runtime {
|
||||
type Channels = EthereumSystem;
|
||||
}
|
||||
|
||||
#[cfg(feature = "fast-runtime")]
|
||||
#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))]
|
||||
parameter_types! {
|
||||
pub const ChainForkVersions: ForkVersions = ForkVersions {
|
||||
genesis: Fork {
|
||||
version: [0, 0, 0, 1], // 0x00000001
|
||||
version: [0, 0, 0, 0], // 0x00000000
|
||||
epoch: 0,
|
||||
},
|
||||
altair: Fork {
|
||||
version: [1, 0, 0, 1], // 0x01000001
|
||||
version: [1, 0, 0, 0], // 0x01000000
|
||||
epoch: 0,
|
||||
},
|
||||
bellatrix: Fork {
|
||||
version: [2, 0, 0, 1], // 0x02000001
|
||||
version: [2, 0, 0, 0], // 0x02000000
|
||||
epoch: 0,
|
||||
},
|
||||
capella: Fork {
|
||||
version: [3, 0, 0, 1], // 0x03000001
|
||||
version: [3, 0, 0, 0], // 0x03000000
|
||||
epoch: 0,
|
||||
},
|
||||
deneb: Fork {
|
||||
version: [4, 0, 0, 0], // 0x04000000
|
||||
epoch: 0,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fast-runtime"))]
|
||||
#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))]
|
||||
parameter_types! {
|
||||
pub const ChainForkVersions: ForkVersions = ForkVersions {
|
||||
genesis: Fork {
|
||||
@@ -641,6 +638,10 @@ parameter_types! {
|
||||
version: [144, 0, 0, 114], // 0x90000072
|
||||
epoch: 56832,
|
||||
},
|
||||
deneb: Fork {
|
||||
version: [144, 0, 0, 115], // 0x90000073
|
||||
epoch: 132608,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ use bp_runtime::ChainId;
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
traits::{ConstU32, Contains, Equals, Everything, Nothing},
|
||||
StoragePrefixedMap,
|
||||
};
|
||||
use frame_system::EnsureRoot;
|
||||
use pallet_xcm::XcmPassthrough;
|
||||
@@ -161,9 +162,12 @@ impl Contains<RuntimeCall> for SafeCallFilter {
|
||||
match call {
|
||||
RuntimeCall::System(frame_system::Call::set_storage { items })
|
||||
if items.iter().all(|(k, _)| {
|
||||
k.eq(&DeliveryRewardInBalance::key()) |
|
||||
k.eq(&RequiredStakeForStakeAndSlash::key()) |
|
||||
k.eq(&EthereumGatewayAddress::key())
|
||||
k.eq(&DeliveryRewardInBalance::key()) ||
|
||||
k.eq(&RequiredStakeForStakeAndSlash::key()) ||
|
||||
k.eq(&EthereumGatewayAddress::key()) ||
|
||||
// Allow resetting of Ethereum nonces in Rococo only.
|
||||
k.starts_with(&snowbridge_pallet_inbound_queue::Nonce::<Runtime>::final_prefix()) ||
|
||||
k.starts_with(&snowbridge_pallet_outbound_queue::Nonce::<Runtime>::final_prefix())
|
||||
}) =>
|
||||
return true,
|
||||
_ => (),
|
||||
@@ -223,7 +227,14 @@ impl Contains<RuntimeCall> for SafeCallFilter {
|
||||
snowbridge_pallet_inbound_queue::Call::set_operating_mode { .. },
|
||||
) | RuntimeCall::EthereumOutboundQueue(
|
||||
snowbridge_pallet_outbound_queue::Call::set_operating_mode { .. },
|
||||
) | RuntimeCall::EthereumSystem(..)
|
||||
) | RuntimeCall::EthereumSystem(
|
||||
snowbridge_pallet_system::Call::upgrade { .. } |
|
||||
snowbridge_pallet_system::Call::set_operating_mode { .. } |
|
||||
snowbridge_pallet_system::Call::set_pricing_parameters { .. } |
|
||||
snowbridge_pallet_system::Call::force_update_channel { .. } |
|
||||
snowbridge_pallet_system::Call::force_transfer_native_from_agent { .. } |
|
||||
snowbridge_pallet_system::Call::set_token_transfer_fees { .. },
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,25 @@
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use bp_polkadot_core::Signature;
|
||||
use bridge_hub_rococo_runtime::{
|
||||
xcm_config::XcmConfig, MessageQueueServiceWeight, Runtime, RuntimeEvent, SessionKeys,
|
||||
bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages,
|
||||
bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages,
|
||||
xcm_config::XcmConfig, BridgeRejectObsoleteHeadersAndMessages, Executive,
|
||||
MessageQueueServiceWeight, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, SignedExtra,
|
||||
UncheckedExtrinsic,
|
||||
};
|
||||
use codec::Decode;
|
||||
use codec::{Decode, Encode};
|
||||
use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees};
|
||||
use frame_support::parameter_types;
|
||||
use parachains_common::{AccountId, AuraId, Balance};
|
||||
use snowbridge_pallet_ethereum_client::WeightInfo;
|
||||
use sp_core::H160;
|
||||
use sp_keyring::AccountKeyring::Alice;
|
||||
use sp_runtime::{
|
||||
generic::{Era, SignedPayload},
|
||||
AccountId32,
|
||||
};
|
||||
|
||||
parameter_types! {
|
||||
pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000;
|
||||
@@ -107,3 +116,62 @@ fn max_message_queue_service_weight_is_more_than_beacon_extrinsic_weights() {
|
||||
max_message_queue_weight.all_gt(force_checkpoint);
|
||||
max_message_queue_weight.all_gt(submit_checkpoint);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ethereum_client_consensus_extrinsics_work() {
|
||||
snowbridge_runtime_test_common::ethereum_extrinsic(
|
||||
collator_session_keys(),
|
||||
1013,
|
||||
construct_and_apply_extrinsic,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ethereum_to_polkadot_message_extrinsics_work() {
|
||||
snowbridge_runtime_test_common::ethereum_to_polkadot_message_extrinsics_work(
|
||||
collator_session_keys(),
|
||||
1013,
|
||||
construct_and_apply_extrinsic,
|
||||
);
|
||||
}
|
||||
|
||||
fn construct_extrinsic(
|
||||
sender: sp_keyring::AccountKeyring,
|
||||
call: RuntimeCall,
|
||||
) -> UncheckedExtrinsic {
|
||||
let account_id = AccountId32::from(sender.public());
|
||||
let extra: SignedExtra = (
|
||||
frame_system::CheckNonZeroSender::<Runtime>::new(),
|
||||
frame_system::CheckSpecVersion::<Runtime>::new(),
|
||||
frame_system::CheckTxVersion::<Runtime>::new(),
|
||||
frame_system::CheckGenesis::<Runtime>::new(),
|
||||
frame_system::CheckEra::<Runtime>::from(Era::immortal()),
|
||||
frame_system::CheckNonce::<Runtime>::from(
|
||||
frame_system::Pallet::<Runtime>::account(&account_id).nonce,
|
||||
),
|
||||
frame_system::CheckWeight::<Runtime>::new(),
|
||||
pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(0),
|
||||
BridgeRejectObsoleteHeadersAndMessages::default(),
|
||||
(
|
||||
OnBridgeHubRococoRefundBridgeHubWestendMessages::default(),
|
||||
OnBridgeHubRococoRefundRococoBulletinMessages::default(),
|
||||
),
|
||||
);
|
||||
let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap();
|
||||
let signature = payload.using_encoded(|e| sender.sign(e));
|
||||
UncheckedExtrinsic::new_signed(
|
||||
call,
|
||||
account_id.into(),
|
||||
Signature::Sr25519(signature.clone()),
|
||||
extra,
|
||||
)
|
||||
}
|
||||
|
||||
fn construct_and_apply_extrinsic(
|
||||
origin: sp_keyring::AccountKeyring,
|
||||
call: RuntimeCall,
|
||||
) -> sp_runtime::DispatchOutcome {
|
||||
let xt = construct_extrinsic(origin, call);
|
||||
let r = Executive::apply_extrinsic(xt);
|
||||
r.unwrap()
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ use parachains_common::{
|
||||
rococo::{consensus::RELAY_CHAIN_SLOT_DURATION_MILLIS, fee::WeightToFee},
|
||||
AccountId, AuraId, Balance, SLOT_DURATION,
|
||||
};
|
||||
use snowbridge_core::ChannelId;
|
||||
use sp_consensus_aura::SlotDuration;
|
||||
use sp_core::H160;
|
||||
use sp_keyring::AccountKeyring::Alice;
|
||||
@@ -222,6 +223,72 @@ mod bridge_hub_westend_tests {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_ethereum_nonces_by_governance_works() {
|
||||
let channel_id_one: ChannelId = [1; 32].into();
|
||||
let channel_id_two: ChannelId = [2; 32].into();
|
||||
let nonce = 42;
|
||||
|
||||
// Reset a single inbound channel
|
||||
bridge_hub_test_utils::test_cases::set_storage_keys_by_governance_works::<Runtime>(
|
||||
collator_session_keys(),
|
||||
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
|
||||
Box::new(|call| RuntimeCall::System(call).encode()),
|
||||
vec![
|
||||
(snowbridge_pallet_outbound_queue::Nonce::<Runtime>::hashed_key_for::<ChannelId>(
|
||||
channel_id_one,
|
||||
)
|
||||
.to_vec(), 0u64.encode()),
|
||||
(snowbridge_pallet_inbound_queue::Nonce::<Runtime>::hashed_key_for::<ChannelId>(
|
||||
channel_id_one,
|
||||
)
|
||||
.to_vec(), 0u64.encode()),
|
||||
],
|
||||
|| {
|
||||
// Outbound
|
||||
snowbridge_pallet_outbound_queue::Nonce::<Runtime>::insert::<ChannelId, u64>(
|
||||
channel_id_one,
|
||||
nonce,
|
||||
);
|
||||
snowbridge_pallet_outbound_queue::Nonce::<Runtime>::insert::<ChannelId, u64>(
|
||||
channel_id_two,
|
||||
nonce,
|
||||
);
|
||||
|
||||
// Inbound
|
||||
snowbridge_pallet_inbound_queue::Nonce::<Runtime>::insert::<ChannelId, u64>(
|
||||
channel_id_one,
|
||||
nonce,
|
||||
);
|
||||
snowbridge_pallet_inbound_queue::Nonce::<Runtime>::insert::<ChannelId, u64>(
|
||||
channel_id_two,
|
||||
nonce,
|
||||
);
|
||||
},
|
||||
|| {
|
||||
// Outbound
|
||||
assert_eq!(
|
||||
snowbridge_pallet_outbound_queue::Nonce::<Runtime>::get(channel_id_one),
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
snowbridge_pallet_outbound_queue::Nonce::<Runtime>::get(channel_id_two),
|
||||
nonce
|
||||
);
|
||||
|
||||
// Inbound
|
||||
assert_eq!(
|
||||
snowbridge_pallet_inbound_queue::Nonce::<Runtime>::get(channel_id_one),
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
snowbridge_pallet_inbound_queue::Nonce::<Runtime>::get(channel_id_two),
|
||||
nonce
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_delivery_reward_by_governance_works() {
|
||||
bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::<
|
||||
|
||||
@@ -72,7 +72,9 @@ pub type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
|
||||
parachains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
|
||||
|
||||
// Re-export test_case from `parachains-runtimes-test-utils`
|
||||
pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
|
||||
pub use parachains_runtimes_test_utils::test_cases::{
|
||||
change_storage_constant_by_governance_works, set_storage_keys_by_governance_works,
|
||||
};
|
||||
|
||||
/// Prepare default runtime storage and run test within this context.
|
||||
pub fn run_test<Runtime, T>(
|
||||
|
||||
@@ -33,7 +33,7 @@ use polkadot_parachain_primitives::primitives::{
|
||||
HeadData, HrmpChannelId, RelayChainBlockNumber, XcmpMessageFormat,
|
||||
};
|
||||
use sp_consensus_aura::{SlotDuration, AURA_ENGINE_ID};
|
||||
use sp_core::Encode;
|
||||
use sp_core::{Encode, U256};
|
||||
use sp_runtime::{traits::Header, BuildStorage, Digest, DigestItem};
|
||||
use xcm::{
|
||||
latest::{Asset, Location, XcmContext, XcmHash},
|
||||
@@ -300,6 +300,10 @@ where
|
||||
<Runtime as frame_system::Config>::RuntimeOrigin::root()
|
||||
}
|
||||
|
||||
pub fn block_number() -> U256 {
|
||||
frame_system::Pallet::<Runtime>::block_number().into()
|
||||
}
|
||||
|
||||
pub fn origin_of(
|
||||
account_id: AccountIdOf<Runtime>,
|
||||
) -> <Runtime as frame_system::Config>::RuntimeOrigin {
|
||||
|
||||
@@ -91,3 +91,55 @@ pub fn change_storage_constant_by_governance_works<Runtime, StorageConstant, Sto
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
/// Test-case makes sure that `Runtime` can change storage constant via governance-like call
|
||||
pub fn set_storage_keys_by_governance_works<Runtime>(
|
||||
collator_session_key: CollatorSessionKeys<Runtime>,
|
||||
runtime_para_id: u32,
|
||||
runtime_call_encode: Box<dyn Fn(frame_system::Call<Runtime>) -> Vec<u8>>,
|
||||
storage_items: Vec<(Vec<u8>, Vec<u8>)>,
|
||||
initialize_storage: impl FnOnce() -> (),
|
||||
assert_storage: impl FnOnce() -> (),
|
||||
) where
|
||||
Runtime: frame_system::Config
|
||||
+ pallet_balances::Config
|
||||
+ pallet_session::Config
|
||||
+ pallet_xcm::Config
|
||||
+ parachain_info::Config
|
||||
+ pallet_collator_selection::Config
|
||||
+ cumulus_pallet_parachain_system::Config,
|
||||
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
|
||||
{
|
||||
let mut runtime = ExtBuilder::<Runtime>::default()
|
||||
.with_collators(collator_session_key.collators())
|
||||
.with_session_keys(collator_session_key.session_keys())
|
||||
.with_para_id(runtime_para_id.into())
|
||||
.with_tracing()
|
||||
.build();
|
||||
runtime.execute_with(|| {
|
||||
initialize_storage();
|
||||
});
|
||||
runtime.execute_with(|| {
|
||||
// encode `kill_storage` call
|
||||
let kill_storage_call = runtime_call_encode(frame_system::Call::<Runtime>::set_storage {
|
||||
items: storage_items.clone(),
|
||||
});
|
||||
|
||||
// estimate - storing just 1 value
|
||||
use frame_system::WeightInfo;
|
||||
let require_weight_at_most =
|
||||
<Runtime as frame_system::Config>::SystemWeightInfo::set_storage(
|
||||
storage_items.len().try_into().unwrap(),
|
||||
);
|
||||
|
||||
// execute XCM with Transact to `set_storage` as governance does
|
||||
assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance(
|
||||
kill_storage_call,
|
||||
require_weight_at_most
|
||||
)
|
||||
.ensure_complete());
|
||||
});
|
||||
runtime.execute_with(|| {
|
||||
assert_storage();
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user