Adds Snowbridge to Rococo runtime (#2522)

# Description

Adds Snowbridge to the Rococo bridge hub runtime. Includes config
changes required in Rococo asset hub.

---------

Co-authored-by: Alistair Singh <alistair.singh7@gmail.com>
Co-authored-by: ron <yrong1997@gmail.com>
Co-authored-by: Vincent Geddes <vincent.geddes@hey.com>
Co-authored-by: claravanstaden <Cats 4 life!>
This commit is contained in:
Clara van Staden
2023-12-21 18:06:36 +02:00
committed by GitHub
parent 9f5221cc2f
commit 18d53dbf91
151 changed files with 19379 additions and 149 deletions
@@ -0,0 +1,26 @@
[package]
name = "snowbridge-rococo-common"
description = "Snowbridge Rococo Common"
version = "0.0.1"
authors = ["Snowfork <contact@snowfork.com>"]
edition = "2021"
license = "Apache-2.0"
[dependencies]
log = { version = "0.4.20", default-features = false }
frame-support = { path = "../../../../../substrate/frame/support", default-features = false }
xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false }
[dev-dependencies]
[features]
default = ["std"]
std = [
"frame-support/std",
"log/std",
"xcm/std",
]
runtime-benchmarks = [
"frame-support/runtime-benchmarks",
]
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
//! # Rococo Common
//!
//! Config used for the Rococo asset hub and bridge hub runtimes.
#![cfg_attr(not(feature = "std"), no_std)]
use frame_support::parameter_types;
use xcm::opaque::lts::NetworkId;
pub const INBOUND_QUEUE_MESSAGES_PALLET_INDEX: u8 = 80;
parameter_types! {
// Network and location for the Ethereum chain.
pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 };
}
@@ -0,0 +1,41 @@
[package]
name = "snowbridge-runtime-common"
description = "Snowbridge Runtime Common"
version = "0.1.1"
authors = ["Snowfork <contact@snowfork.com>"]
edition = "2021"
license = "Apache-2.0"
[dependencies]
log = { version = "0.4.20", default-features = false }
frame-support = { path = "../../../../../substrate/frame/support", default-features = false }
frame-system = { path = "../../../../../substrate/frame/system", default-features = false }
sp-arithmetic = { path = "../../../../../substrate/primitives/arithmetic", default-features = false }
xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false }
xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false }
xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false }
snowbridge-core = { path = "../../primitives/core", default-features = false }
[dev-dependencies]
[features]
default = ["std"]
std = [
"frame-support/std",
"frame-system/std",
"log/std",
"snowbridge-core/std",
"sp-arithmetic/std",
"xcm-builder/std",
"xcm-executor/std",
"xcm/std",
]
runtime-benchmarks = [
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"snowbridge-core/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
]
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
//! # Runtime Common
//!
//! Common traits and types shared by runtimes.
#![cfg_attr(not(feature = "std"), no_std)]
use core::marker::PhantomData;
use frame_support::traits::Get;
use snowbridge_core::{outbound::SendMessageFeeProvider, sibling_sovereign_account_raw};
use sp_arithmetic::traits::{BaseArithmetic, Unsigned};
use xcm::prelude::*;
use xcm_builder::{deposit_or_burn_fee, HandleFee};
use xcm_executor::traits::{FeeReason, TransactAsset};
/// A `HandleFee` implementation that takes fees from `ExportMessage` XCM instructions
/// to Snowbridge and splits off the remote fee and deposits it to the origin
/// parachain sovereign account. The local fee is then returned back to be handled by
/// the next fee handler in the chain. Most likely the treasury account.
pub struct XcmExportFeeToSibling<
Balance,
AccountId,
FeeAssetLocation,
EthereumNetwork,
AssetTransactor,
FeeProvider,
>(
PhantomData<(
Balance,
AccountId,
FeeAssetLocation,
EthereumNetwork,
AssetTransactor,
FeeProvider,
)>,
);
impl<Balance, AccountId, FeeAssetLocation, EthereumNetwork, AssetTransactor, FeeProvider> HandleFee
for XcmExportFeeToSibling<
Balance,
AccountId,
FeeAssetLocation,
EthereumNetwork,
AssetTransactor,
FeeProvider,
> where
Balance: BaseArithmetic + Unsigned + Copy + From<u128> + Into<u128>,
AccountId: Clone + Into<[u8; 32]> + From<[u8; 32]>,
FeeAssetLocation: Get<MultiLocation>,
EthereumNetwork: Get<NetworkId>,
AssetTransactor: TransactAsset,
FeeProvider: SendMessageFeeProvider<Balance = Balance>,
{
fn handle_fee(
fees: MultiAssets,
context: Option<&XcmContext>,
reason: FeeReason,
) -> MultiAssets {
let token_location = FeeAssetLocation::get();
// Check the reason to see if this export is for snowbridge.
if !matches!(
reason,
FeeReason::Export { network: bridged_network, destination }
if bridged_network == EthereumNetwork::get() && destination == Here
) {
return fees
}
// Get the parachain sovereign from the `context`.
let para_sovereign = if let Some(XcmContext {
origin: Some(MultiLocation { parents: 1, interior }),
..
}) = context
{
if let Some(Parachain(sibling_para_id)) = interior.first() {
let account: AccountId =
sibling_sovereign_account_raw((*sibling_para_id).into()).into();
account
} else {
return fees
}
} else {
return fees
};
// Get the total fee offered by export message.
let maybe_total_supplied_fee: Option<(usize, Balance)> = fees
.inner()
.iter()
.enumerate()
.filter_map(|(index, asset)| {
if let MultiAsset { id: Concrete(location), fun: Fungible(amount) } = asset {
if *location == token_location {
return Some((index, (*amount).into()))
}
}
None
})
.next();
if let Some((fee_index, total_fee)) = maybe_total_supplied_fee {
let remote_fee = total_fee.saturating_sub(FeeProvider::local_fee());
if remote_fee > (0u128).into() {
// Refund remote component of fee to physical origin
deposit_or_burn_fee::<AssetTransactor, _>(
MultiAsset { id: Concrete(token_location), fun: Fungible(remote_fee.into()) }
.into(),
context,
para_sovereign,
);
// Return remaining fee to the next fee handler in the chain.
let mut modified_fees = fees.inner().clone();
modified_fees.remove(fee_index);
modified_fees.push(MultiAsset {
id: Concrete(token_location),
fun: Fungible((total_fee - remote_fee).into()),
});
return modified_fees.into()
}
}
log::info!(
target: "xcm::fees",
"XcmExportFeeToSibling skipped: {fees:?}, context: {context:?}, reason: {reason:?}",
);
fees
}
}
@@ -0,0 +1,244 @@
[package]
name = "snowbridge-runtime-tests"
description = "Snowbridge Runtime Tests"
version = "0.1.0"
authors = ["Snowfork <contact@snowfork.com>"]
edition = "2021"
license = "Apache-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
hex-literal = { version = "0.4.1" }
log = { version = "0.4.20", default-features = false }
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.188", optional = true, features = ["derive"] }
smallvec = "1.11.0"
# Substrate
frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true }
frame-executive = { path = "../../../../../substrate/frame/executive", default-features = false }
frame-support = { path = "../../../../../substrate/frame/support", default-features = false }
frame-system = { path = "../../../../../substrate/frame/system", default-features = false }
frame-system-benchmarking = { path = "../../../../../substrate/frame/system/benchmarking", default-features = false, optional = true }
frame-system-rpc-runtime-api = { path = "../../../../../substrate/frame/system/rpc/runtime-api", default-features = false }
frame-try-runtime = { path = "../../../../../substrate/frame/try-runtime", default-features = false, optional = true }
pallet-aura = { path = "../../../../../substrate/frame/aura", default-features = false }
pallet-authorship = { path = "../../../../../substrate/frame/authorship", default-features = false }
pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false }
pallet-session = { path = "../../../../../substrate/frame/session", default-features = false }
pallet-multisig = { path = "../../../../../substrate/frame/multisig", default-features = false }
pallet-message-queue = { path = "../../../../../substrate/frame/message-queue", default-features = false }
pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false }
pallet-transaction-payment = { path = "../../../../../substrate/frame/transaction-payment", default-features = false }
pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false }
pallet-utility = { path = "../../../../../substrate/frame/utility", default-features = false }
sp-api = { path = "../../../../../substrate/primitives/api", default-features = false }
sp-block-builder = { path = "../../../../../substrate/primitives/block-builder", default-features = false }
sp-consensus-aura = { path = "../../../../../substrate/primitives/consensus/aura", default-features = false }
sp-core = { path = "../../../../../substrate/primitives/core", default-features = false }
sp-genesis-builder = { path = "../../../../../substrate/primitives/genesis-builder", default-features = false }
sp-inherents = { path = "../../../../../substrate/primitives/inherents", default-features = false }
sp-io = { path = "../../../../../substrate/primitives/io", default-features = false }
sp-offchain = { path = "../../../../../substrate/primitives/offchain", default-features = false }
sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false }
sp-session = { path = "../../../../../substrate/primitives/session", default-features = false }
sp-std = { path = "../../../../../substrate/primitives/std", default-features = false }
sp-storage = { path = "../../../../../substrate/primitives/storage", default-features = false }
sp-transaction-pool = { path = "../../../../../substrate/primitives/transaction-pool", default-features = false }
sp-version = { path = "../../../../../substrate/primitives/version", default-features = false }
# Polkadot
rococo-runtime-constants = { path = "../../../../../polkadot/runtime/rococo/constants", default-features = false }
pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false }
pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true }
polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false }
polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false }
polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false }
xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false }
xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false }
xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false }
# Cumulus
cumulus-pallet-aura-ext = { path = "../../../../../cumulus/pallets/aura-ext", default-features = false }
cumulus-pallet-dmp-queue = { path = "../../../../../cumulus/pallets/dmp-queue", default-features = false }
cumulus-pallet-parachain-system = { path = "../../../../../cumulus/pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] }
cumulus-pallet-session-benchmarking = { path = "../../../../../cumulus/pallets/session-benchmarking", default-features = false }
cumulus-pallet-xcm = { path = "../../../../../cumulus/pallets/xcm", default-features = false }
cumulus-pallet-xcmp-queue = { path = "../../../../../cumulus/pallets/xcmp-queue", default-features = false, features = ["bridging"] }
cumulus-primitives-core = { path = "../../../../../cumulus/primitives/core", default-features = false }
cumulus-primitives-utility = { path = "../../../../../cumulus/primitives/utility", default-features = false }
pallet-collator-selection = { path = "../../../../../cumulus/pallets/collator-selection", default-features = false }
parachain-info = { package = "staging-parachain-info", path = "../../../../../cumulus/parachains/pallets/parachain-info", default-features = false }
parachains-common = { path = "../../../../../cumulus/parachains/common", default-features = false }
parachains-runtimes-test-utils = { path = "../../../../../cumulus/parachains/runtimes/test-utils", default-features = false }
bridge-hub-rococo-runtime = { path = "../../../../../cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo", default-features = false }
asset-hub-rococo-runtime = { path = "../../../../../cumulus/parachains/runtimes/assets/asset-hub-rococo", default-features = false }
assets-common = { path = "../../../../../cumulus/parachains/runtimes/assets/common", default-features = false }
# Ethereum Bridge (Snowbridge)
snowbridge-core = { path = "../../primitives/core", default-features = false }
snowbridge-beacon-primitives = { path = "../../primitives/beacon", default-features = false }
snowbridge-router-primitives = { path = "../../primitives/router", default-features = false }
snowbridge-ethereum-beacon-client = { path = "../../pallets/ethereum-beacon-client", default-features = false }
snowbridge-inbound-queue = { path = "../../pallets/inbound-queue", default-features = false }
snowbridge-outbound-queue = { path = "../../pallets/outbound-queue", default-features = false }
snowbridge-outbound-queue-runtime-api = { path = "../../pallets/outbound-queue/runtime-api", default-features = false }
snowbridge-system = { path = "../../pallets/system", default-features = false }
snowbridge-system-runtime-api = { path = "../../pallets/system/runtime-api", default-features = false }
[dev-dependencies]
static_assertions = "1.1"
bridge-hub-test-utils = { path = "../../../../../cumulus/parachains/runtimes/bridge-hubs/test-utils" }
bridge-runtime-common = { path = "../../../../../bridges/bin/runtime-common", features = ["integrity-test"] }
sp-keyring = { path = "../../../../../substrate/primitives/keyring" }
[features]
default = ["std"]
std = [
"asset-hub-rococo-runtime/std",
"assets-common/std",
"bridge-hub-rococo-runtime/std",
"codec/std",
"cumulus-pallet-aura-ext/std",
"cumulus-pallet-dmp-queue/std",
"cumulus-pallet-parachain-system/std",
"cumulus-pallet-session-benchmarking/std",
"cumulus-pallet-xcm/std",
"cumulus-pallet-xcmp-queue/std",
"cumulus-primitives-core/std",
"cumulus-primitives-utility/std",
"frame-benchmarking/std",
"frame-executive/std",
"frame-support/std",
"frame-system-benchmarking?/std",
"frame-system-rpc-runtime-api/std",
"frame-system/std",
"frame-try-runtime?/std",
"log/std",
"pallet-aura/std",
"pallet-authorship/std",
"pallet-balances/std",
"pallet-collator-selection/std",
"pallet-message-queue/std",
"pallet-multisig/std",
"pallet-session/std",
"pallet-timestamp/std",
"pallet-transaction-payment-rpc-runtime-api/std",
"pallet-transaction-payment/std",
"pallet-utility/std",
"pallet-xcm-benchmarks?/std",
"pallet-xcm/std",
"parachain-info/std",
"parachains-common/std",
"parachains-runtimes-test-utils/std",
"polkadot-core-primitives/std",
"polkadot-parachain-primitives/std",
"polkadot-runtime-common/std",
"rococo-runtime-constants/std",
"scale-info/std",
"serde",
"snowbridge-beacon-primitives/std",
"snowbridge-core/std",
"snowbridge-ethereum-beacon-client/std",
"snowbridge-inbound-queue/std",
"snowbridge-outbound-queue-runtime-api/std",
"snowbridge-outbound-queue/std",
"snowbridge-router-primitives/std",
"snowbridge-system-runtime-api/std",
"snowbridge-system/std",
"sp-api/std",
"sp-block-builder/std",
"sp-consensus-aura/std",
"sp-core/std",
"sp-genesis-builder/std",
"sp-inherents/std",
"sp-io/std",
"sp-offchain/std",
"sp-runtime/std",
"sp-session/std",
"sp-std/std",
"sp-storage/std",
"sp-transaction-pool/std",
"sp-version/std",
"xcm-builder/std",
"xcm-executor/std",
"xcm/std",
]
runtime-benchmarks = [
"asset-hub-rococo-runtime/runtime-benchmarks",
"assets-common/runtime-benchmarks",
"bridge-hub-rococo-runtime/runtime-benchmarks",
"bridge-runtime-common/runtime-benchmarks",
"cumulus-pallet-dmp-queue/runtime-benchmarks",
"cumulus-pallet-parachain-system/runtime-benchmarks",
"cumulus-pallet-session-benchmarking/runtime-benchmarks",
"cumulus-pallet-xcmp-queue/runtime-benchmarks",
"cumulus-primitives-core/runtime-benchmarks",
"cumulus-primitives-utility/runtime-benchmarks",
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system-benchmarking/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-collator-selection/runtime-benchmarks",
"pallet-message-queue/runtime-benchmarks",
"pallet-multisig/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"pallet-utility/runtime-benchmarks",
"pallet-xcm-benchmarks/runtime-benchmarks",
"pallet-xcm/runtime-benchmarks",
"parachains-common/runtime-benchmarks",
"polkadot-parachain-primitives/runtime-benchmarks",
"polkadot-runtime-common/runtime-benchmarks",
"snowbridge-core/runtime-benchmarks",
"snowbridge-ethereum-beacon-client/runtime-benchmarks",
"snowbridge-inbound-queue/runtime-benchmarks",
"snowbridge-outbound-queue/runtime-benchmarks",
"snowbridge-router-primitives/runtime-benchmarks",
"snowbridge-system/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
]
try-runtime = [
"asset-hub-rococo-runtime/try-runtime",
"bridge-hub-rococo-runtime/try-runtime",
"cumulus-pallet-aura-ext/try-runtime",
"cumulus-pallet-dmp-queue/try-runtime",
"cumulus-pallet-parachain-system/try-runtime",
"cumulus-pallet-xcm/try-runtime",
"cumulus-pallet-xcmp-queue/try-runtime",
"frame-executive/try-runtime",
"frame-support/try-runtime",
"frame-system/try-runtime",
"frame-try-runtime/try-runtime",
"pallet-aura/try-runtime",
"pallet-authorship/try-runtime",
"pallet-balances/try-runtime",
"pallet-collator-selection/try-runtime",
"pallet-message-queue/try-runtime",
"pallet-multisig/try-runtime",
"pallet-session/try-runtime",
"pallet-timestamp/try-runtime",
"pallet-transaction-payment/try-runtime",
"pallet-utility/try-runtime",
"pallet-xcm/try-runtime",
"parachain-info/try-runtime",
"polkadot-runtime-common/try-runtime",
"snowbridge-ethereum-beacon-client/try-runtime",
"snowbridge-inbound-queue/try-runtime",
"snowbridge-outbound-queue/try-runtime",
"snowbridge-system/try-runtime",
"sp-runtime/try-runtime",
]
beacon-spec-mainnet = [
"snowbridge-ethereum-beacon-client/beacon-spec-mainnet",
]
experimental = ["pallet-aura/experimental"]
# 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
# to make it smaller like logging for example.
on-chain-release-build = ["sp-api/disable-logging"]
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
#![cfg(test)]
mod test_cases;
use asset_hub_rococo_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee;
use bridge_hub_rococo_runtime::{
xcm_config::XcmConfig, MessageQueueServiceWeight, Runtime, RuntimeEvent, SessionKeys,
};
use codec::Decode;
use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees};
use parachains_common::{AccountId, AuraId};
use snowbridge_ethereum_beacon_client::WeightInfo;
use sp_core::H160;
use sp_keyring::AccountKeyring::Alice;
pub fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys<Runtime> {
bridge_hub_test_utils::CollatorSessionKeys::new(
AccountId::from(Alice),
AccountId::from(Alice),
SessionKeys { aura: AuraId::from(Alice.public()) },
)
}
#[test]
pub fn transfer_token_to_ethereum_works() {
test_cases::send_transfer_token_message_success::<Runtime, XcmConfig>(
collator_session_keys(),
1013,
1000,
H160::random(),
H160::random(),
DefaultBridgeHubEthereumBaseFee::get(),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::EthereumOutboundQueue(event)) => Some(event),
_ => None,
}
}),
)
}
#[test]
pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() {
test_cases::send_unpaid_transfer_token_message::<Runtime, XcmConfig>(
collator_session_keys(),
1013,
1000,
H160::random(),
H160::random(),
)
}
#[test]
pub fn transfer_token_to_ethereum_fee_not_enough() {
test_cases::send_transfer_token_message_failure::<Runtime, XcmConfig>(
collator_session_keys(),
1013,
1000,
DefaultBridgeHubEthereumBaseFee::get() + 1_000_000_000,
H160::random(),
H160::random(),
// fee not enough
1_000_000_000,
NotHoldingFees,
)
}
#[test]
pub fn transfer_token_to_ethereum_insufficient_fund() {
test_cases::send_transfer_token_message_failure::<Runtime, XcmConfig>(
collator_session_keys(),
1013,
1000,
1_000_000_000,
H160::random(),
H160::random(),
DefaultBridgeHubEthereumBaseFee::get(),
FailedToTransactAsset("InsufficientBalance"),
)
}
#[test]
fn max_message_queue_service_weight_is_more_than_beacon_extrinsic_weights() {
let max_message_queue_weight = MessageQueueServiceWeight::get();
let force_checkpoint =
<Runtime as snowbridge_ethereum_beacon_client::Config>::WeightInfo::force_checkpoint();
let submit_checkpoint =
<Runtime as snowbridge_ethereum_beacon_client::Config>::WeightInfo::submit();
max_message_queue_weight.all_gt(force_checkpoint);
max_message_queue_weight.all_gt(submit_checkpoint);
}
@@ -0,0 +1,293 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
//! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities.
use asset_hub_rococo_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee;
use bridge_hub_rococo_runtime::EthereumSystem;
use codec::Encode;
use frame_support::{assert_err, assert_ok, traits::fungible::Mutate};
use parachains_runtimes_test_utils::{
AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, ValidatorIdOf, XcmReceivedFrom,
};
use sp_core::H160;
use sp_runtime::SaturatedConversion;
use xcm::latest::prelude::*;
use xcm_executor::XcmExecutor;
// Re-export test_case from `parachains-runtimes-test-utils`
pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
use xcm::v3::Error::{self, Barrier};
type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
parachains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
pub fn initial_fund<Runtime>(assethub_parachain_id: u32, initial_amount: u128)
where
Runtime: frame_system::Config + pallet_balances::Config,
{
// fund asset hub sovereign account enough so it can pay fees
let asset_hub_sovereign_account =
snowbridge_core::sibling_sovereign_account::<Runtime>(assethub_parachain_id.into());
<pallet_balances::Pallet<Runtime>>::mint_into(
&asset_hub_sovereign_account,
initial_amount.saturated_into::<BalanceOf<Runtime>>(),
)
.unwrap();
}
pub fn send_transfer_token_message<Runtime, XcmConfig>(
assethub_parachain_id: u32,
weth_contract_address: H160,
destination_address: H160,
fee_amount: u128,
) -> Outcome
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
+ snowbridge_outbound_queue::Config,
XcmConfig: xcm_executor::Config,
{
let assethub_parachain_location = MultiLocation::new(1, Parachain(assethub_parachain_id));
let asset = MultiAsset {
id: Concrete(MultiLocation {
parents: 0,
interior: X1(AccountKey20 { network: None, key: weth_contract_address.into() }),
}),
fun: Fungible(1000000000),
};
let assets = vec![asset.clone()];
let inner_xcm = Xcm(vec![
WithdrawAsset(MultiAssets::from(assets.clone())),
ClearOrigin,
BuyExecution { fees: asset, weight_limit: Unlimited },
DepositAsset {
assets: Wild(All),
beneficiary: MultiLocation {
parents: 0,
interior: X1(AccountKey20 { network: None, key: destination_address.into() }),
},
},
SetTopic([0; 32]),
]);
let fee = MultiAsset {
id: Concrete(MultiLocation { parents: 1, interior: Here }),
fun: Fungible(fee_amount),
};
// prepare transfer token message
let xcm = Xcm(vec![
WithdrawAsset(MultiAssets::from(vec![fee.clone()])),
BuyExecution { fees: fee, weight_limit: Unlimited },
ExportMessage {
network: Ethereum { chain_id: 11155111 },
destination: Here,
xcm: inner_xcm,
},
]);
// execute XCM
let hash = xcm.using_encoded(sp_io::hashing::blake2_256);
XcmExecutor::<XcmConfig>::execute_xcm(
assethub_parachain_location,
xcm,
hash,
RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
)
}
pub fn send_transfer_token_message_success<Runtime, XcmConfig>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
assethub_parachain_id: u32,
weth_contract_address: H160,
destination_address: H160,
fee_amount: u128,
snowbridge_outbound_queue: Box<
dyn Fn(Vec<u8>) -> Option<snowbridge_outbound_queue::Event<Runtime>>,
>,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config
+ snowbridge_outbound_queue::Config
+ snowbridge_system::Config,
XcmConfig: xcm_executor::Config,
ValidatorIdOf<Runtime>: From<AccountIdOf<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()
.execute_with(|| {
EthereumSystem::initialize(runtime_para_id.into(), assethub_parachain_id.into())
.unwrap();
// fund asset hub sovereign account enough so it can pay fees
initial_fund::<Runtime>(
assethub_parachain_id,
DefaultBridgeHubEthereumBaseFee::get() + 1_000_000_000,
);
let outcome = send_transfer_token_message::<Runtime, XcmConfig>(
assethub_parachain_id,
weth_contract_address,
destination_address,
fee_amount,
);
assert_ok!(outcome.ensure_complete());
// check events
let mut events = <frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| snowbridge_outbound_queue(e.event.encode()));
assert!(
events.any(|e| matches!(e, snowbridge_outbound_queue::Event::MessageQueued { .. }))
);
});
}
pub fn send_unpaid_transfer_token_message<Runtime, XcmConfig>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
assethub_parachain_id: u32,
weth_contract_address: H160,
destination_contract: H160,
) 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
+ snowbridge_outbound_queue::Config,
XcmConfig: xcm_executor::Config,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
{
let assethub_parachain_location = MultiLocation::new(1, Parachain(assethub_parachain_id));
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()
.execute_with(|| {
let asset_hub_sovereign_account =
snowbridge_core::sibling_sovereign_account::<Runtime>(assethub_parachain_id.into());
<pallet_balances::Pallet<Runtime>>::mint_into(
&asset_hub_sovereign_account,
4000000000u32.into(),
)
.unwrap();
let asset = MultiAsset {
id: Concrete(MultiLocation {
parents: 0,
interior: X1(AccountKey20 { network: None, key: weth_contract_address.into() }),
}),
fun: Fungible(1000000000),
};
let assets = vec![asset.clone()];
let inner_xcm = Xcm(vec![
WithdrawAsset(MultiAssets::from(assets.clone())),
ClearOrigin,
BuyExecution { fees: asset, weight_limit: Unlimited },
DepositAsset {
assets: Wild(AllCounted(1)),
beneficiary: MultiLocation {
parents: 0,
interior: X1(AccountKey20 {
network: None,
key: destination_contract.into(),
}),
},
},
SetTopic([0; 32]),
]);
// prepare transfer token message
let xcm = Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
ExportMessage {
network: Ethereum { chain_id: 11155111 },
destination: Here,
xcm: inner_xcm,
},
]);
// execute XCM
let hash = xcm.using_encoded(sp_io::hashing::blake2_256);
let outcome = XcmExecutor::<XcmConfig>::execute_xcm(
assethub_parachain_location,
xcm,
hash,
RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
);
// check error is barrier
assert_err!(outcome.ensure_complete(), Barrier);
});
}
#[allow(clippy::too_many_arguments)]
pub fn send_transfer_token_message_failure<Runtime, XcmConfig>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
assethub_parachain_id: u32,
initial_amount: u128,
weth_contract_address: H160,
destination_address: H160,
fee_amount: u128,
expected_error: Error,
) 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
+ snowbridge_outbound_queue::Config
+ snowbridge_system::Config,
XcmConfig: xcm_executor::Config,
ValidatorIdOf<Runtime>: From<AccountIdOf<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()
.execute_with(|| {
EthereumSystem::initialize(runtime_para_id.into(), assethub_parachain_id.into())
.unwrap();
// fund asset hub sovereign account enough so it can pay fees
initial_fund::<Runtime>(assethub_parachain_id, initial_amount);
let outcome = send_transfer_token_message::<Runtime, XcmConfig>(
assethub_parachain_id,
weth_contract_address,
destination_address,
fee_amount,
);
// check err is NotHoldingFees
assert_err!(outcome.ensure_complete(), expected_error);
});
}