fix: Complete snowbridge pezpallet rebrand and critical bug fixes
- snowbridge-pezpallet-* → pezsnowbridge-pezpallet-* (201 refs) - pallet/ directories → pezpallet/ (4 locations) - Fixed pezpallet.rs self-include recursion bug - Fixed sc-chain-spec hardcoded crate name in derive macro - Reverted .pezpallet_by_name() to .pallet_by_name() (subxt API) - Added BizinikiwiConfig type alias for zombienet tests - Deleted obsolete session state files Verified: pezsnowbridge-pezpallet-*, pezpallet-staking, pezpallet-staking-async, pezframe-benchmarking-cli all pass cargo check
This commit is contained in:
+85
@@ -0,0 +1,85 @@
|
||||
[package]
|
||||
name = "pezbridge-hub-pezkuwichain-integration-tests"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "Bridge Hub Pezkuwichain runtime integration tests with xcm-pez-emulator"
|
||||
publish = false
|
||||
documentation = "https://docs.rs/pezbridge-hub-pezkuwichain-integration-tests"
|
||||
repository = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true }
|
||||
hex-literal = { workspace = true, default-features = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
|
||||
# Bizinikiwi
|
||||
pezframe-support = { workspace = true }
|
||||
pezpallet-asset-conversion = { workspace = true }
|
||||
pezpallet-assets = { workspace = true }
|
||||
pezpallet-balances = { workspace = true }
|
||||
pezpallet-message-queue = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
# Pezkuwi
|
||||
pezpallet-xcm = { workspace = true }
|
||||
xcm = { workspace = true }
|
||||
xcm-builder = { workspace = true }
|
||||
xcm-executor = { workspace = true }
|
||||
|
||||
# Bridges
|
||||
bp-asset-hub-pezkuwichain = { workspace = true }
|
||||
pezpallet-bridge-messages = { workspace = true }
|
||||
|
||||
# Pezcumulus
|
||||
asset-hub-pezkuwichain-runtime = { workspace = true }
|
||||
pezcumulus-pezpallet-xcmp-queue = { workspace = true }
|
||||
emulated-integration-tests-common = { workspace = true }
|
||||
pezkuwichain-system-emulated-network = { workspace = true }
|
||||
pezkuwichain-zagros-system-emulated-network = { workspace = true }
|
||||
testnet-teyrchains-constants = { features = [
|
||||
"pezkuwichain",
|
||||
"zagros",
|
||||
], workspace = true, default-features = true }
|
||||
teyrchains-common = { workspace = true, default-features = true }
|
||||
|
||||
# Snowbridge
|
||||
pezsnowbridge-inbound-queue-primitives = { workspace = true }
|
||||
pezsnowbridge-outbound-queue-primitives = { workspace = true }
|
||||
pezsnowbridge-pezpallet-inbound-queue-fixtures = { workspace = true, default-features = true }
|
||||
pezsnowbridge-pezpallet-outbound-queue = { workspace = true }
|
||||
pezsnowbridge-pezpallet-system = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"asset-hub-pezkuwichain-runtime/runtime-benchmarks",
|
||||
"bp-asset-hub-pezkuwichain/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-xcmp-queue/runtime-benchmarks",
|
||||
"emulated-integration-tests-common/runtime-benchmarks",
|
||||
"pezframe-support/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-xcm/runtime-benchmarks",
|
||||
"pezkuwichain-system-emulated-network/runtime-benchmarks",
|
||||
"pezkuwichain-zagros-system-emulated-network/runtime-benchmarks",
|
||||
"pezsnowbridge-inbound-queue-primitives/runtime-benchmarks",
|
||||
"pezsnowbridge-outbound-queue-primitives/runtime-benchmarks",
|
||||
"pezsnowbridge-pezpallet-inbound-queue-fixtures/runtime-benchmarks",
|
||||
"pezsnowbridge-pezpallet-outbound-queue/runtime-benchmarks",
|
||||
"pezsnowbridge-pezpallet-system/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"testnet-teyrchains-constants/runtime-benchmarks",
|
||||
"teyrchains-common/runtime-benchmarks",
|
||||
"xcm-builder/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
]
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
// 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.
|
||||
|
||||
#[cfg(test)]
|
||||
mod imports {
|
||||
// Bizinikiwi
|
||||
pub(crate) use codec::Encode;
|
||||
pub(crate) use pezframe_support::{assert_err, assert_ok, pezpallet_prelude::DispatchResult};
|
||||
pub(crate) use pezsp_runtime::DispatchError;
|
||||
|
||||
// Pezkuwi
|
||||
pub(crate) use xcm::{
|
||||
latest::{ParentThen, PEZKUWICHAIN_GENESIS_HASH, ZAGROS_GENESIS_HASH},
|
||||
prelude::{AccountId32 as AccountId32Junction, *},
|
||||
};
|
||||
pub(crate) use xcm_builder::ExternalConsensusLocationsConverterFor;
|
||||
pub(crate) use xcm_executor::traits::TransferType;
|
||||
|
||||
// Pezcumulus
|
||||
pub(crate) use emulated_integration_tests_common::{
|
||||
accounts::ALICE,
|
||||
impls::Inspect,
|
||||
test_dry_run_transfer_across_pk_bridge, test_relay_is_trusted_teleporter,
|
||||
test_teyrchain_is_trusted_teleporter, test_teyrchain_is_trusted_teleporter_for_relay,
|
||||
xcm_pez_emulator::{
|
||||
assert_expected_events, bx, Chain, RelayChain as Relay, TestExt, Teyrchain as Para,
|
||||
},
|
||||
xcm_helpers::xcm_transact_paid_execution,
|
||||
ASSETS_PALLET_ID, USDT_ID,
|
||||
};
|
||||
pub(crate) use pezkuwichain_zagros_system_emulated_network::{
|
||||
asset_hub_pezkuwichain_emulated_chain::{
|
||||
asset_hub_pezkuwichain_runtime::{
|
||||
xcm_config::TreasuryAccount, ForeignAssetReserveData,
|
||||
},
|
||||
genesis::ED as ASSET_HUB_PEZKUWICHAIN_ED,
|
||||
AssetHubPezkuwichainParaPallet as AssetHubPezkuwichainPallet,
|
||||
},
|
||||
asset_hub_zagros_emulated_chain::{
|
||||
genesis::{AssetHubZagrosAssetOwner, ED as ASSET_HUB_ZAGROS_ED},
|
||||
AssetHubZagrosParaPallet as AssetHubZagrosPallet,
|
||||
},
|
||||
pezbridge_hub_pezkuwichain_emulated_chain::{
|
||||
genesis::ED as BRIDGE_HUB_PEZKUWICHAIN_ED, BridgeHubPezkuwichainExistentialDeposit,
|
||||
BridgeHubPezkuwichainParaPallet as BridgeHubPezkuwichainPallet,
|
||||
},
|
||||
pez_penpal_emulated_chain::{
|
||||
pez_penpal_runtime::xcm_config::{
|
||||
CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub,
|
||||
UniversalLocation as PenpalUniversalLocation,
|
||||
},
|
||||
PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner,
|
||||
},
|
||||
pezkuwichain_emulated_chain::{
|
||||
genesis::ED as PEZKUWICHAIN_ED, PezkuwichainRelayPallet as PezkuwichainPallet,
|
||||
},
|
||||
AssetHubPezkuwichainPara as AssetHubPezkuwichain,
|
||||
AssetHubPezkuwichainParaReceiver as AssetHubPezkuwichainReceiver,
|
||||
AssetHubPezkuwichainParaSender as AssetHubPezkuwichainSender,
|
||||
AssetHubZagrosPara as AssetHubZagros, AssetHubZagrosParaReceiver as AssetHubZagrosReceiver,
|
||||
AssetHubZagrosParaSender as AssetHubZagrosSender,
|
||||
BridgeHubPezkuwichainPara as BridgeHubPezkuwichain,
|
||||
BridgeHubPezkuwichainParaReceiver as BridgeHubPezkuwichainReceiver,
|
||||
BridgeHubPezkuwichainParaSender as BridgeHubPezkuwichainSender,
|
||||
BridgeHubZagrosPara as BridgeHubZagros, PenpalAPara as PenpalA,
|
||||
PenpalAParaSender as PenpalASender, PezkuwichainRelay as Pezkuwichain,
|
||||
PezkuwichainRelayReceiver as PezkuwichainReceiver,
|
||||
PezkuwichainRelaySender as PezkuwichainSender,
|
||||
};
|
||||
pub(crate) use teyrchains_common::AccountId;
|
||||
|
||||
pub(crate) const ASSET_ID: u32 = 1;
|
||||
pub(crate) const ASSET_MIN_BALANCE: u128 = 1000;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
+617
@@ -0,0 +1,617 @@
|
||||
// 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.
|
||||
|
||||
use crate::tests::*;
|
||||
|
||||
fn send_assets_over_bridge<F: FnOnce()>(send_fn: F) {
|
||||
// fund the AHR's SA on BHR for paying bridge delivery fees
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
10_000_000_000_000u128,
|
||||
);
|
||||
|
||||
// set XCM versions
|
||||
let local_asset_hub = PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id());
|
||||
PenpalA::force_xcm_version(local_asset_hub.clone(), XCM_VERSION);
|
||||
AssetHubPezkuwichain::force_xcm_version(asset_hub_zagros_location(), XCM_VERSION);
|
||||
BridgeHubPezkuwichain::force_xcm_version(bridge_hub_zagros_location(), XCM_VERSION);
|
||||
|
||||
// send message over bridge
|
||||
send_fn();
|
||||
|
||||
// process and verify intermediary hops
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
}
|
||||
|
||||
fn set_up_rocs_for_penpal_pezkuwichain_through_ahr_to_ahw(
|
||||
sender: &AccountId,
|
||||
amount: u128,
|
||||
) -> (Location, v5::Location) {
|
||||
let roc_at_pezkuwichain_teyrchains = roc_at_ah_pezkuwichain();
|
||||
let roc_at_asset_hub_zagros = bridged_roc_at_ah_zagros();
|
||||
let reserves = vec![(asset_hub_pezkuwichain_global_location(), false).into()];
|
||||
create_foreign_on_ah_zagros(roc_at_asset_hub_zagros.clone(), true, reserves);
|
||||
|
||||
let penpal_location = AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
|
||||
let sov_penpal_on_ahr = AssetHubPezkuwichain::sovereign_account_id_of(penpal_location);
|
||||
// fund Penpal's sovereign account on AssetHub
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(sov_penpal_on_ahr.into(), amount * 2)]);
|
||||
// fund Penpal's sender account
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
roc_at_pezkuwichain_teyrchains.clone(),
|
||||
sender.clone(),
|
||||
amount * 2,
|
||||
);
|
||||
(roc_at_pezkuwichain_teyrchains, roc_at_asset_hub_zagros)
|
||||
}
|
||||
|
||||
fn send_assets_from_penpal_pezkuwichain_through_pezkuwichain_ah_to_zagros_ah(
|
||||
destination: Location,
|
||||
assets: (Assets, TransferType),
|
||||
fees: (AssetId, TransferType),
|
||||
custom_xcm_on_dest: Xcm<()>,
|
||||
) {
|
||||
send_assets_over_bridge(|| {
|
||||
let sov_penpal_on_ahr = AssetHubPezkuwichain::sovereign_account_id_of(
|
||||
AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
// send message over bridge
|
||||
assert_ok!(PenpalA::execute_with(|| {
|
||||
let signed_origin = <PenpalA as Chain>::RuntimeOrigin::signed(PenpalASender::get());
|
||||
<PenpalA as PenpalAPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
signed_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(assets.0.into()),
|
||||
bx!(assets.1),
|
||||
bx!(fees.0.into()),
|
||||
bx!(fees.1),
|
||||
bx!(VersionedXcm::from(custom_xcm_on_dest)),
|
||||
WeightLimit::Unlimited,
|
||||
)
|
||||
}));
|
||||
// verify intermediary AH Pezkuwichain hop
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubPezkuwichain,
|
||||
vec![
|
||||
// Amount to reserve transfer is withdrawn from Penpal's sovereign account
|
||||
RuntimeEvent::Balances(
|
||||
pezpallet_balances::Event::Burned { who, .. }
|
||||
) => {
|
||||
who: *who == sov_penpal_on_ahr.clone().into(),
|
||||
},
|
||||
// Amount deposited in AHW's sovereign account
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: *who == TreasuryAccount::get(),
|
||||
},
|
||||
RuntimeEvent::XcmpQueue(
|
||||
pezcumulus_pezpallet_xcmp_queue::Event::XcmpMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test transfer of TYR from AssetHub Pezkuwichain to AssetHub Zagros.
|
||||
fn send_roc_from_asset_hub_pezkuwichain_to_asset_hub_zagros() {
|
||||
let amount = ASSET_HUB_PEZKUWICHAIN_ED * 1_000_000;
|
||||
let sender = AssetHubPezkuwichainSender::get();
|
||||
let receiver = AssetHubZagrosReceiver::get();
|
||||
let roc_at_asset_hub_pezkuwichain = roc_at_ah_pezkuwichain();
|
||||
let bridged_roc_at_asset_hub_zagros = bridged_roc_at_ah_zagros();
|
||||
let reserves = vec![(asset_hub_pezkuwichain_global_location(), false).into()];
|
||||
create_foreign_on_ah_zagros(bridged_roc_at_asset_hub_zagros.clone(), true, reserves);
|
||||
set_up_pool_with_wnd_on_ah_zagros(bridged_roc_at_asset_hub_zagros.clone(), true);
|
||||
|
||||
let sov_ahw_on_ahr =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
let rocs_in_reserve_on_ahr_before =
|
||||
<AssetHubPezkuwichain as Chain>::account_data_of(sov_ahw_on_ahr.clone()).free;
|
||||
let sender_rocs_before = <AssetHubPezkuwichain as Chain>::account_data_of(sender.clone()).free;
|
||||
let receiver_rocs_before =
|
||||
foreign_balance_on_ah_zagros(bridged_roc_at_asset_hub_zagros.clone(), &receiver);
|
||||
|
||||
// send TYRs, use them for fees
|
||||
send_assets_over_bridge(|| {
|
||||
let destination = asset_hub_zagros_location();
|
||||
let assets: Assets = (roc_at_asset_hub_pezkuwichain.clone(), amount).into();
|
||||
let fee_idx = 0;
|
||||
let transfer_type = TransferType::LocalReserve;
|
||||
assert_ok!(send_assets_from_asset_hub_pezkuwichain(
|
||||
destination,
|
||||
assets,
|
||||
fee_idx,
|
||||
transfer_type
|
||||
));
|
||||
});
|
||||
|
||||
// verify expected events on final destination
|
||||
let roc = Location::new(2, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))]);
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// issue TYRs on AHW
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == roc,
|
||||
owner: owner == &receiver,
|
||||
},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
let sender_rocs_after = <AssetHubPezkuwichain as Chain>::account_data_of(sender.clone()).free;
|
||||
let receiver_rocs_after =
|
||||
foreign_balance_on_ah_zagros(bridged_roc_at_asset_hub_zagros, &receiver);
|
||||
let rocs_in_reserve_on_ahr_after =
|
||||
<AssetHubPezkuwichain as Chain>::account_data_of(sov_ahw_on_ahr.clone()).free;
|
||||
|
||||
// Sender's TYR balance is reduced
|
||||
assert!(sender_rocs_before > sender_rocs_after);
|
||||
// Receiver's TYR balance is increased
|
||||
assert!(receiver_rocs_after > receiver_rocs_before);
|
||||
// Reserve TYR balance is increased by sent amount
|
||||
assert_eq!(rocs_in_reserve_on_ahr_after, rocs_in_reserve_on_ahr_before + amount);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Send bridged assets "back" from AssetHub Pezkuwichain to AssetHub Zagros.
|
||||
///
|
||||
/// This mix of assets should cover the whole range:
|
||||
/// - bridged native assets: TYR,
|
||||
/// - bridged trust-based assets: USDT (exists only on Zagros, Pezkuwichain gets it from Zagros over
|
||||
/// bridge),
|
||||
/// - bridged foreign asset / double-bridged asset (other bridge / Snowfork): wETH (bridged from
|
||||
/// Ethereum to Zagros over Snowbridge, then bridged over to Pezkuwichain through this bridge).
|
||||
fn send_back_wnds_usdt_and_weth_from_asset_hub_pezkuwichain_to_asset_hub_zagros() {
|
||||
let prefund_amount = 10_000_000_000_000u128;
|
||||
let amount_to_send = ASSET_HUB_ZAGROS_ED * 1_000;
|
||||
let sender = AssetHubPezkuwichainSender::get();
|
||||
let receiver = AssetHubZagrosReceiver::get();
|
||||
let wnd_at_asset_hub_pezkuwichain = bridged_wnd_at_ah_pezkuwichain();
|
||||
let prefund_accounts = vec![(sender.clone(), prefund_amount)];
|
||||
let reserves = vec![(asset_hub_zagros_location(), false).into()];
|
||||
create_foreign_on_ah_pezkuwichain(
|
||||
wnd_at_asset_hub_pezkuwichain.clone(),
|
||||
true,
|
||||
reserves,
|
||||
prefund_accounts,
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Let's first send back just some ZGRs as a simple example
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
// fund the AHR's SA on AHW with the ZGR tokens held in reserve
|
||||
let sov_ahr_on_ahw = AssetHubZagros::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
);
|
||||
AssetHubZagros::fund_accounts(vec![(sov_ahr_on_ahw.clone(), prefund_amount)]);
|
||||
|
||||
let wnds_in_reserve_on_ahw_before =
|
||||
<AssetHubZagros as Chain>::account_data_of(sov_ahr_on_ahw.clone()).free;
|
||||
assert_eq!(wnds_in_reserve_on_ahw_before, prefund_amount);
|
||||
|
||||
let sender_wnds_before =
|
||||
foreign_balance_on_ah_pezkuwichain(wnd_at_asset_hub_pezkuwichain.clone(), &sender);
|
||||
assert_eq!(sender_wnds_before, prefund_amount);
|
||||
let receiver_wnds_before = <AssetHubZagros as Chain>::account_data_of(receiver.clone()).free;
|
||||
|
||||
// send back WNDs, use them for fees
|
||||
send_assets_over_bridge(|| {
|
||||
let destination = asset_hub_zagros_location();
|
||||
let assets: Assets = (wnd_at_asset_hub_pezkuwichain.clone(), amount_to_send).into();
|
||||
let fee_idx = 0;
|
||||
let transfer_type = TransferType::DestinationReserve;
|
||||
assert_ok!(send_assets_from_asset_hub_pezkuwichain(
|
||||
destination,
|
||||
assets,
|
||||
fee_idx,
|
||||
transfer_type
|
||||
));
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// ZGR is withdrawn from AHR's SA on AHW
|
||||
RuntimeEvent::Balances(
|
||||
pezpallet_balances::Event::Burned { who, amount }
|
||||
) => {
|
||||
who: *who == sov_ahr_on_ahw,
|
||||
amount: *amount == amount_to_send,
|
||||
},
|
||||
// ZGRs deposited to beneficiary
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: who == &receiver,
|
||||
},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
let sender_wnds_after =
|
||||
foreign_balance_on_ah_pezkuwichain(wnd_at_asset_hub_pezkuwichain, &sender);
|
||||
let receiver_wnds_after = <AssetHubZagros as Chain>::account_data_of(receiver.clone()).free;
|
||||
let wnds_in_reserve_on_ahw_after =
|
||||
<AssetHubZagros as Chain>::account_data_of(sov_ahr_on_ahw).free;
|
||||
|
||||
// Sender's balance is reduced
|
||||
assert!(sender_wnds_before > sender_wnds_after);
|
||||
// Receiver's balance is increased
|
||||
assert!(receiver_wnds_after > receiver_wnds_before);
|
||||
// Reserve balance is reduced by sent amount
|
||||
assert_eq!(wnds_in_reserve_on_ahw_after, wnds_in_reserve_on_ahw_before - amount_to_send);
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Now let's send back over USDTs + wETH (and pay fees with USDT)
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
// wETH has same relative location on both Zagros and Pezkuwichain AssetHubs
|
||||
let bridged_weth_at_ah = weth_at_asset_hubs();
|
||||
let bridged_usdt_at_asset_hub_pezkuwichain = bridged_usdt_at_ah_pezkuwichain();
|
||||
|
||||
// set up destination chain AH Zagros:
|
||||
// create a ZGR/USDT pool to be able to pay fees with USDT (USDT created in genesis)
|
||||
set_up_pool_with_wnd_on_ah_zagros(usdt_at_ah_zagros(), false);
|
||||
// prefund AHR's sovereign account on AHW to be able to withdraw USDT and wETH from reserves
|
||||
let sov_ahr_on_ahw = AssetHubZagros::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
);
|
||||
|
||||
let sov_ahw_on_ahr =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
|
||||
AssetHubZagros::mint_asset(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosAssetOwner::get()),
|
||||
USDT_ID,
|
||||
sov_ahr_on_ahw.clone(),
|
||||
amount_to_send * 2,
|
||||
);
|
||||
AssetHubZagros::mint_foreign_asset(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(snowbridge_sovereign()),
|
||||
bridged_weth_at_ah.clone(),
|
||||
sov_ahr_on_ahw.clone(),
|
||||
amount_to_send * 2,
|
||||
);
|
||||
AssetHubPezkuwichain::mint_foreign_asset(
|
||||
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(sov_ahw_on_ahr.clone()),
|
||||
bridged_weth_at_ah.clone(),
|
||||
sov_ahr_on_ahw,
|
||||
prefund_amount,
|
||||
);
|
||||
AssetHubPezkuwichain::mint_foreign_asset(
|
||||
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(sov_ahw_on_ahr),
|
||||
bridged_weth_at_ah.clone(),
|
||||
sender.clone(),
|
||||
prefund_amount,
|
||||
);
|
||||
|
||||
// set up source chain AH Pezkuwichain:
|
||||
// create wETH and USDT foreign assets on Pezkuwichain and prefund sender's account
|
||||
let prefund_accounts = vec![(sender.clone(), amount_to_send * 2)];
|
||||
let reserves = vec![(asset_hub_zagros_location(), false).into()];
|
||||
create_foreign_on_ah_pezkuwichain(
|
||||
bridged_usdt_at_asset_hub_pezkuwichain.clone(),
|
||||
true,
|
||||
reserves,
|
||||
prefund_accounts,
|
||||
);
|
||||
|
||||
// check balances before
|
||||
let receiver_usdts_before = AssetHubZagros::execute_with(|| {
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::Assets;
|
||||
<Assets as Inspect<_>>::balance(USDT_ID, &receiver)
|
||||
});
|
||||
let receiver_weth_before = foreign_balance_on_ah_zagros(bridged_weth_at_ah.clone(), &receiver);
|
||||
|
||||
let usdt_id: AssetId =
|
||||
Location::try_from(bridged_usdt_at_asset_hub_pezkuwichain).unwrap().into();
|
||||
// send USDTs and wETHs
|
||||
let assets: Assets = vec![
|
||||
(usdt_id.clone(), amount_to_send).into(),
|
||||
(Location::try_from(bridged_weth_at_ah.clone()).unwrap(), amount_to_send).into(),
|
||||
]
|
||||
.into();
|
||||
// use USDT for fees
|
||||
let fee = usdt_id;
|
||||
|
||||
// use the more involved transfer extrinsic
|
||||
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
|
||||
assets: Wild(AllCounted(assets.len() as u32)),
|
||||
beneficiary: AccountId32Junction { network: None, id: receiver.clone().into() }.into(),
|
||||
}]);
|
||||
assert_ok!(AssetHubPezkuwichain::execute_with(|| {
|
||||
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(sender.into()),
|
||||
bx!(asset_hub_zagros_location().into()),
|
||||
bx!(assets.into()),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
bx!(fee.into()),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
bx!(VersionedXcm::from(custom_xcm_on_dest)),
|
||||
WeightLimit::Unlimited,
|
||||
)
|
||||
}));
|
||||
// verify hops (also advances the message through the hops)
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
});
|
||||
|
||||
let receiver_usdts_after = AssetHubZagros::execute_with(|| {
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::Assets;
|
||||
<Assets as Inspect<_>>::balance(USDT_ID, &receiver)
|
||||
});
|
||||
let receiver_weth_after = foreign_balance_on_ah_zagros(bridged_weth_at_ah, &receiver);
|
||||
|
||||
// Receiver's USDT balance is increased by almost `amount_to_send` (minus fees)
|
||||
assert!(receiver_usdts_after > receiver_usdts_before);
|
||||
assert!(receiver_usdts_after < receiver_usdts_before + amount_to_send);
|
||||
// Receiver's wETH balance is increased by `amount_to_send`
|
||||
assert_eq!(receiver_weth_after, receiver_weth_before + amount_to_send);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_rocs_from_penpal_pezkuwichain_through_asset_hub_pezkuwichain_to_asset_hub_zagros() {
|
||||
let amount = ASSET_HUB_PEZKUWICHAIN_ED * 10_000_000;
|
||||
let sender = PenpalASender::get();
|
||||
let receiver = AssetHubZagrosReceiver::get();
|
||||
let local_asset_hub = PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id());
|
||||
let (roc_at_pezkuwichain_teyrchains, roc_at_asset_hub_zagros) =
|
||||
set_up_rocs_for_penpal_pezkuwichain_through_ahr_to_ahw(&sender, amount);
|
||||
set_up_pool_with_wnd_on_ah_zagros(roc_at_asset_hub_zagros.clone(), true);
|
||||
|
||||
let sov_ahw_on_ahr =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
let rocs_in_reserve_on_ahr_before =
|
||||
<AssetHubPezkuwichain as Chain>::account_data_of(sov_ahw_on_ahr.clone()).free;
|
||||
let sender_rocs_before = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(roc_at_pezkuwichain_teyrchains.clone(), &sender)
|
||||
});
|
||||
let receiver_rocs_before =
|
||||
foreign_balance_on_ah_zagros(roc_at_asset_hub_zagros.clone(), &receiver);
|
||||
|
||||
// Send TYRs over bridge
|
||||
{
|
||||
let destination = asset_hub_zagros_location();
|
||||
let assets: Assets = (roc_at_pezkuwichain_teyrchains.clone(), amount).into();
|
||||
let asset_transfer_type = TransferType::RemoteReserve(local_asset_hub.clone().into());
|
||||
let fees_id: AssetId = roc_at_pezkuwichain_teyrchains.clone().into();
|
||||
let fees_transfer_type = TransferType::RemoteReserve(local_asset_hub.into());
|
||||
let beneficiary: Location =
|
||||
AccountId32Junction { network: None, id: receiver.clone().into() }.into();
|
||||
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
|
||||
assets: Wild(AllCounted(assets.len() as u32)),
|
||||
beneficiary,
|
||||
}]);
|
||||
send_assets_from_penpal_pezkuwichain_through_pezkuwichain_ah_to_zagros_ah(
|
||||
destination,
|
||||
(assets, asset_transfer_type),
|
||||
(fees_id, fees_transfer_type),
|
||||
custom_xcm_on_dest,
|
||||
);
|
||||
}
|
||||
|
||||
// process AHW incoming message and check events
|
||||
let roc = Location::new(2, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))]);
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// issue TYRs on AHW
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == roc,
|
||||
owner: owner == &receiver,
|
||||
},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
let sender_rocs_after = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(roc_at_pezkuwichain_teyrchains, &sender)
|
||||
});
|
||||
let receiver_rocs_after = foreign_balance_on_ah_zagros(roc_at_asset_hub_zagros, &receiver);
|
||||
let rocs_in_reserve_on_ahr_after =
|
||||
<AssetHubPezkuwichain as Chain>::account_data_of(sov_ahw_on_ahr.clone()).free;
|
||||
|
||||
// Sender's balance is reduced
|
||||
assert!(sender_rocs_after < sender_rocs_before);
|
||||
// Receiver's balance is increased
|
||||
assert!(receiver_rocs_after > receiver_rocs_before);
|
||||
// Reserve balance is increased by sent amount (less fess)
|
||||
assert!(rocs_in_reserve_on_ahr_after > rocs_in_reserve_on_ahr_before);
|
||||
assert!(rocs_in_reserve_on_ahr_after <= rocs_in_reserve_on_ahr_before + amount);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_back_wnds_from_penpal_pezkuwichain_through_asset_hub_pezkuwichain_to_asset_hub_zagros() {
|
||||
let wnd_at_pezkuwichain_teyrchains = bridged_wnd_at_ah_pezkuwichain();
|
||||
let amount = ASSET_HUB_PEZKUWICHAIN_ED * 10_000_000;
|
||||
let sender = PenpalASender::get();
|
||||
let receiver = AssetHubZagrosReceiver::get();
|
||||
|
||||
// set up TYRs for transfer
|
||||
let (roc_at_pezkuwichain_teyrchains, _) =
|
||||
set_up_rocs_for_penpal_pezkuwichain_through_ahr_to_ahw(&sender, amount);
|
||||
|
||||
// set up ZGRs for transfer
|
||||
let penpal_location = AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
|
||||
let sov_penpal_on_ahr = AssetHubPezkuwichain::sovereign_account_id_of(penpal_location);
|
||||
let prefund_accounts = vec![(sov_penpal_on_ahr, amount * 2)];
|
||||
let reserves = vec![(asset_hub_zagros_location(), false).into()];
|
||||
create_foreign_on_ah_pezkuwichain(
|
||||
wnd_at_pezkuwichain_teyrchains.clone(),
|
||||
true,
|
||||
reserves,
|
||||
prefund_accounts,
|
||||
);
|
||||
let asset_owner: AccountId = AssetHubPezkuwichain::account_id_of(ALICE);
|
||||
PenpalA::force_create_foreign_asset(
|
||||
wnd_at_pezkuwichain_teyrchains.clone(),
|
||||
asset_owner.clone(),
|
||||
true,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![(sender.clone(), amount * 2)],
|
||||
);
|
||||
// Configure source Penpal chain to trust local AH as reserve of bridged ZGR
|
||||
PenpalA::execute_with(|| {
|
||||
assert_ok!(<PenpalA as Chain>::System::set_storage(
|
||||
<PenpalA as Chain>::RuntimeOrigin::root(),
|
||||
vec![(
|
||||
PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(),
|
||||
wnd_at_pezkuwichain_teyrchains.encode(),
|
||||
)],
|
||||
));
|
||||
});
|
||||
|
||||
// fund the AHR's SA on AHW with the ZGR tokens held in reserve
|
||||
let sov_ahr_on_ahw = AssetHubZagros::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
NetworkId::ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
);
|
||||
AssetHubZagros::fund_accounts(vec![(sov_ahr_on_ahw.clone(), amount * 2)]);
|
||||
|
||||
// balances before
|
||||
let sender_wnds_before = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(
|
||||
wnd_at_pezkuwichain_teyrchains.clone().into(),
|
||||
&sender,
|
||||
)
|
||||
});
|
||||
let receiver_wnds_before = <AssetHubZagros as Chain>::account_data_of(receiver.clone()).free;
|
||||
|
||||
// send ZGRs over the bridge, TYRs only used to pay fees on local AH, pay with ZGR on remote AH
|
||||
{
|
||||
let final_destination = asset_hub_zagros_location();
|
||||
let intermediary_hop = PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id());
|
||||
let context = PenpalA::execute_with(|| PenpalUniversalLocation::get());
|
||||
|
||||
// what happens at final destination
|
||||
let beneficiary = AccountId32Junction { network: None, id: receiver.clone().into() }.into();
|
||||
// use ZGR as fees on the final destination (AHW)
|
||||
let remote_fees: Asset = (wnd_at_pezkuwichain_teyrchains.clone(), amount).into();
|
||||
let remote_fees = remote_fees.reanchored(&final_destination, &context).unwrap();
|
||||
// buy execution using WNDs, then deposit all remaining WNDs
|
||||
let xcm_on_final_dest = Xcm::<()>(vec![
|
||||
BuyExecution { fees: remote_fees, weight_limit: WeightLimit::Unlimited },
|
||||
DepositAsset { assets: Wild(AllCounted(1)), beneficiary },
|
||||
]);
|
||||
|
||||
// what happens at intermediary hop
|
||||
// reanchor final dest (Asset Hub Zagros) to the view of hop (Asset Hub Pezkuwichain)
|
||||
let mut final_destination = final_destination.clone();
|
||||
final_destination.reanchor(&intermediary_hop, &context).unwrap();
|
||||
// reanchor ZGRs to the view of hop (Asset Hub Pezkuwichain)
|
||||
let asset: Asset = (wnd_at_pezkuwichain_teyrchains.clone(), amount).into();
|
||||
let asset = asset.reanchored(&intermediary_hop, &context).unwrap();
|
||||
// on Asset Hub Pezkuwichain, forward a request to withdraw ZGRs from reserve on Asset Hub
|
||||
// Zagros
|
||||
let xcm_on_hop = Xcm::<()>(vec![InitiateReserveWithdraw {
|
||||
assets: Definite(asset.into()), // WNDs
|
||||
reserve: final_destination, // AHW
|
||||
xcm: xcm_on_final_dest, // XCM to execute on AHW
|
||||
}]);
|
||||
// assets to send from Penpal and how they reach the intermediary hop
|
||||
let assets: Assets = vec![
|
||||
(wnd_at_pezkuwichain_teyrchains.clone(), amount).into(),
|
||||
(roc_at_pezkuwichain_teyrchains.clone(), amount).into(),
|
||||
]
|
||||
.into();
|
||||
let asset_transfer_type = TransferType::DestinationReserve;
|
||||
let fees_id: AssetId = roc_at_pezkuwichain_teyrchains.into();
|
||||
let fees_transfer_type = TransferType::DestinationReserve;
|
||||
|
||||
// initiate the transfer
|
||||
send_assets_from_penpal_pezkuwichain_through_pezkuwichain_ah_to_zagros_ah(
|
||||
intermediary_hop,
|
||||
(assets, asset_transfer_type),
|
||||
(fees_id, fees_transfer_type),
|
||||
xcm_on_hop,
|
||||
);
|
||||
}
|
||||
|
||||
// process AHW incoming message and check events
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// issue TYRs on AHW
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Issued { .. }) => {},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
let sender_wnds_after = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_pezkuwichain_teyrchains.into(), &sender)
|
||||
});
|
||||
let receiver_wnds_after = <AssetHubZagros as Chain>::account_data_of(receiver).free;
|
||||
|
||||
// Sender's balance is reduced by sent "amount"
|
||||
assert_eq!(sender_wnds_after, sender_wnds_before - amount);
|
||||
// Receiver's balance is increased by no more than "amount"
|
||||
assert!(receiver_wnds_after > receiver_wnds_before);
|
||||
assert!(receiver_wnds_after <= receiver_wnds_before + amount);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dry_run_transfer_to_zagros_sends_xcm_to_bridge_hub() {
|
||||
test_dry_run_transfer_across_pk_bridge!(
|
||||
AssetHubPezkuwichain,
|
||||
BridgeHubPezkuwichain,
|
||||
asset_hub_zagros_location()
|
||||
);
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// 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.
|
||||
|
||||
//! Tests related to claiming assets trapped during XCM execution.
|
||||
|
||||
use crate::imports::*;
|
||||
|
||||
use emulated_integration_tests_common::test_chain_can_claim_assets;
|
||||
|
||||
#[test]
|
||||
fn assets_can_be_claimed() {
|
||||
let amount = BridgeHubPezkuwichainExistentialDeposit::get();
|
||||
let assets: Assets = (Parent, amount).into();
|
||||
|
||||
test_chain_can_claim_assets!(
|
||||
AssetHubPezkuwichain,
|
||||
RuntimeCall,
|
||||
NetworkId::ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
assets,
|
||||
amount
|
||||
);
|
||||
}
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
// 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.
|
||||
|
||||
use crate::imports::*;
|
||||
use emulated_integration_tests_common::{snowbridge, snowbridge::WETH};
|
||||
use testnet_teyrchains_constants::pezkuwichain::snowbridge::EthereumNetwork;
|
||||
use xcm::opaque::v5;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
mod asset_transfers;
|
||||
mod claim_assets;
|
||||
mod register_bridged_assets;
|
||||
mod send_xcm;
|
||||
mod teleport;
|
||||
|
||||
pub(crate) fn asset_hub_zagros_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
pub(crate) fn asset_hub_pezkuwichain_global_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubPezkuwichain::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
pub(crate) fn bridge_hub_zagros_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(BridgeHubZagros::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
// TYR and wTYR
|
||||
pub(crate) fn roc_at_ah_pezkuwichain() -> Location {
|
||||
Parent.into()
|
||||
}
|
||||
pub(crate) fn bridged_roc_at_ah_zagros() -> Location {
|
||||
Location::new(2, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))])
|
||||
}
|
||||
|
||||
// ZGR and wWND
|
||||
pub(crate) fn bridged_wnd_at_ah_pezkuwichain() -> Location {
|
||||
Location::new(2, [GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH))])
|
||||
}
|
||||
|
||||
// USDT and wUSDT
|
||||
pub(crate) fn usdt_at_ah_zagros() -> Location {
|
||||
Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())])
|
||||
}
|
||||
pub(crate) fn bridged_usdt_at_ah_pezkuwichain() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(USDT_ID.into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
// wETH has same relative location on both Pezkuwichain and Zagros AssetHubs
|
||||
pub(crate) fn weth_at_asset_hubs() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(Ethereum { chain_id: snowbridge::SEPOLIA_ID }),
|
||||
AccountKey20 { network: None, key: WETH },
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn create_foreign_on_ah_pezkuwichain(
|
||||
id: v5::Location,
|
||||
sufficient: bool,
|
||||
reserves: Vec<ForeignAssetReserveData>,
|
||||
prefund_accounts: Vec<(AccountId, u128)>,
|
||||
) {
|
||||
let owner = AssetHubPezkuwichain::account_id_of(ALICE);
|
||||
let min = ASSET_MIN_BALANCE;
|
||||
AssetHubPezkuwichain::force_create_foreign_asset(
|
||||
id.clone(),
|
||||
owner.clone(),
|
||||
sufficient,
|
||||
min,
|
||||
prefund_accounts,
|
||||
);
|
||||
AssetHubPezkuwichain::set_foreign_asset_reserves(id, owner, reserves);
|
||||
}
|
||||
|
||||
pub(crate) fn create_foreign_on_ah_zagros(
|
||||
id: v5::Location,
|
||||
sufficient: bool,
|
||||
reserves: Vec<ForeignAssetReserveData>,
|
||||
) {
|
||||
let owner = AssetHubZagros::account_id_of(ALICE);
|
||||
AssetHubZagros::force_create_foreign_asset(
|
||||
id.clone(),
|
||||
owner.clone(),
|
||||
sufficient,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
AssetHubZagros::set_foreign_asset_reserves(id, owner, reserves);
|
||||
}
|
||||
|
||||
pub(crate) fn foreign_balance_on_ah_pezkuwichain(id: v5::Location, who: &AccountId) -> u128 {
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(id, who)
|
||||
})
|
||||
}
|
||||
pub(crate) fn foreign_balance_on_ah_zagros(id: v5::Location, who: &AccountId) -> u128 {
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(id, who)
|
||||
})
|
||||
}
|
||||
|
||||
// set up pool
|
||||
pub(crate) fn set_up_pool_with_wnd_on_ah_zagros(asset: v5::Location, is_foreign: bool) {
|
||||
let wnd: v5::Location = v5::Parent.into();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
let owner = AssetHubZagrosSender::get();
|
||||
let signed_owner = <AssetHubZagros as Chain>::RuntimeOrigin::signed(owner.clone());
|
||||
|
||||
if is_foreign {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint(
|
||||
signed_owner.clone(),
|
||||
asset.clone().into(),
|
||||
owner.clone().into(),
|
||||
3_000_000_000_000,
|
||||
));
|
||||
} else {
|
||||
let asset_id = match asset.interior.last() {
|
||||
Some(GeneralIndex(id)) => *id as u32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::mint(
|
||||
signed_owner.clone(),
|
||||
asset_id.into(),
|
||||
owner.clone().into(),
|
||||
3_000_000_000_000,
|
||||
));
|
||||
}
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::create_pool(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd.clone()),
|
||||
Box::new(asset.clone()),
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
|
||||
]
|
||||
);
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::add_liquidity(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd),
|
||||
Box::new(asset),
|
||||
1_000_000_000_000,
|
||||
2_000_000_000_000,
|
||||
1,
|
||||
1,
|
||||
owner.into()
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded {..}) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn send_assets_from_asset_hub_pezkuwichain(
|
||||
destination: Location,
|
||||
assets: Assets,
|
||||
fee_idx: u32,
|
||||
// For knowing what reserve to pick.
|
||||
// We only allow using the same transfer type for assets and fees right now.
|
||||
// And only `LocalReserve` or `DestinationReserve`.
|
||||
transfer_type: TransferType,
|
||||
) -> DispatchResult {
|
||||
let signed_origin =
|
||||
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(AssetHubPezkuwichainSender::get());
|
||||
let beneficiary: Location =
|
||||
AccountId32Junction { network: None, id: AssetHubZagrosReceiver::get().into() }.into();
|
||||
|
||||
type Runtime = <AssetHubPezkuwichain as Chain>::Runtime;
|
||||
let remote_fee_id: AssetId = assets
|
||||
.clone()
|
||||
.into_inner()
|
||||
.get(fee_idx as usize)
|
||||
.ok_or(pezpallet_xcm::Error::<Runtime>::Empty)?
|
||||
.clone()
|
||||
.id;
|
||||
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
signed_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(assets.into()),
|
||||
bx!(transfer_type.clone()),
|
||||
bx!(remote_fee_id.into()),
|
||||
bx!(transfer_type),
|
||||
bx!(VersionedXcm::from(
|
||||
Xcm::<()>::builder_unsafe().deposit_asset(AllCounted(1), beneficiary).build()
|
||||
)),
|
||||
WeightLimit::Unlimited,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_pezkuwichain_message_accepted(expected_processed: bool) {
|
||||
BridgeHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
if expected_processed {
|
||||
assert_expected_events!(
|
||||
BridgeHubPezkuwichain,
|
||||
vec![
|
||||
// pay for bridge fees
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { .. }) => {},
|
||||
// message exported
|
||||
RuntimeEvent::BridgeZagrosMessages(
|
||||
pezpallet_bridge_messages::Event::MessageAccepted { .. }
|
||||
) => {},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
} else {
|
||||
assert_expected_events!(
|
||||
BridgeHubPezkuwichain,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed {
|
||||
success: false,
|
||||
..
|
||||
}) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_zagros_message_received() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
// message sent to destination
|
||||
RuntimeEvent::XcmpQueue(
|
||||
pezcumulus_pezpallet_xcmp_queue::Event::XcmpMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn snowbridge_sovereign() -> pezsp_runtime::AccountId32 {
|
||||
use asset_hub_pezkuwichain_runtime::xcm_config::UniversalLocation as AssetHubPezkuwichainUniversalLocation;
|
||||
let ethereum_sovereign: AccountId = AssetHubPezkuwichain::execute_with(|| {
|
||||
ExternalConsensusLocationsConverterFor::<
|
||||
AssetHubPezkuwichainUniversalLocation,
|
||||
[u8; 32],
|
||||
>::convert_location(&Location::new(
|
||||
2,
|
||||
[xcm::v5::Junction::GlobalConsensus(EthereumNetwork::get())],
|
||||
))
|
||||
.unwrap()
|
||||
.into()
|
||||
});
|
||||
|
||||
ethereum_sovereign
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// 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.
|
||||
|
||||
use crate::{imports::*, tests::*};
|
||||
|
||||
const XCM_FEE: u128 = 4_000_000_000_000;
|
||||
|
||||
/// Tests the registering of a Pezkuwichain Asset as a bridged asset on Zagros Asset Hub.
|
||||
#[test]
|
||||
fn register_pezkuwichain_asset_on_wah_from_rah() {
|
||||
let sa_of_rah_on_wah = AssetHubZagros::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
);
|
||||
|
||||
// Pezkuwichain Asset Hub asset when bridged to Zagros Asset Hub.
|
||||
let bridged_asset_at_wah = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubPezkuwichain::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(ASSET_ID.into()),
|
||||
],
|
||||
);
|
||||
|
||||
// Encoded `create_asset` call to be executed in Zagros Asset Hub ForeignAssets pezpallet.
|
||||
let call = AssetHubZagros::create_foreign_asset_call(
|
||||
bridged_asset_at_wah.clone(),
|
||||
ASSET_MIN_BALANCE,
|
||||
sa_of_rah_on_wah.clone(),
|
||||
);
|
||||
|
||||
let origin_kind = OriginKind::Xcm;
|
||||
let fee_amount = XCM_FEE;
|
||||
let fees = (Parent, fee_amount).into();
|
||||
|
||||
let xcm = xcm_transact_paid_execution(call, origin_kind, fees, sa_of_rah_on_wah.clone());
|
||||
|
||||
// SA-of-RAH-on-WAH needs to have balance to pay for fees and asset creation deposit
|
||||
AssetHubZagros::fund_accounts(vec![(
|
||||
sa_of_rah_on_wah.clone(),
|
||||
ASSET_HUB_ZAGROS_ED * 10000000000,
|
||||
)]);
|
||||
|
||||
let destination = asset_hub_zagros_location();
|
||||
|
||||
// fund the RAH's SA on RBH for paying bridge delivery fees
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
10_000_000_000_000u128,
|
||||
);
|
||||
|
||||
// set XCM versions
|
||||
AssetHubPezkuwichain::force_xcm_version(destination.clone(), XCM_VERSION);
|
||||
BridgeHubPezkuwichain::force_xcm_version(bridge_hub_zagros_location(), XCM_VERSION);
|
||||
|
||||
let root_origin = <AssetHubPezkuwichain as Chain>::RuntimeOrigin::root();
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::send(
|
||||
root_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
AssetHubPezkuwichain::assert_xcm_pallet_sent();
|
||||
});
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Burned the fee
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { who, amount }) => {
|
||||
who: *who == sa_of_rah_on_wah.clone(),
|
||||
amount: *amount == fee_amount,
|
||||
},
|
||||
// Foreign Asset created
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Created { asset_id, creator, owner }) => {
|
||||
asset_id: asset_id == &bridged_asset_at_wah,
|
||||
creator: *creator == sa_of_rah_on_wah.clone(),
|
||||
owner: *owner == sa_of_rah_on_wah,
|
||||
},
|
||||
// Unspent fee minted to origin
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: *who == sa_of_rah_on_wah.clone(),
|
||||
},
|
||||
]
|
||||
);
|
||||
type ForeignAssets = <AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets;
|
||||
assert!(ForeignAssets::asset_exists(bridged_asset_at_wah));
|
||||
});
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
// 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.
|
||||
|
||||
use pezkuwichain_system_emulated_network::pezkuwichain_emulated_chain::pezkuwichain_runtime::Dmp;
|
||||
|
||||
use crate::tests::*;
|
||||
|
||||
#[test]
|
||||
fn send_xcm_from_pezkuwichain_relay_to_zagros_asset_hub_should_fail_on_not_applicable() {
|
||||
// Init tests variables
|
||||
// XcmPallet send arguments
|
||||
let sudo_origin = <Pezkuwichain as Chain>::RuntimeOrigin::root();
|
||||
let destination = Pezkuwichain::child_location_of(BridgeHubPezkuwichain::para_id()).into();
|
||||
let weight_limit = WeightLimit::Unlimited;
|
||||
let check_origin = None;
|
||||
|
||||
let remote_xcm = Xcm(vec![ClearOrigin]);
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit, check_origin },
|
||||
ExportMessage {
|
||||
network: ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
destination: [Teyrchain(AssetHubZagros::para_id().into())].into(),
|
||||
xcm: remote_xcm,
|
||||
},
|
||||
]));
|
||||
|
||||
// Pezkuwichain Global Consensus
|
||||
// Send XCM message from Relay Chain to Bridge Hub source Teyrchain
|
||||
Pezkuwichain::execute_with(|| {
|
||||
Dmp::make_teyrchain_reachable(BridgeHubPezkuwichain::para_id());
|
||||
|
||||
assert_ok!(<Pezkuwichain as PezkuwichainPallet>::XcmPallet::send(
|
||||
sudo_origin,
|
||||
bx!(destination),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
type RuntimeEvent = <Pezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
Pezkuwichain,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
// Receive XCM message in Bridge Hub source Teyrchain, it should fail, because we don't have
|
||||
// opened bridge/lane.
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
|
||||
// prepare data
|
||||
let destination = asset_hub_zagros_location();
|
||||
let native_token = Location::parent();
|
||||
let amount = ASSET_HUB_PEZKUWICHAIN_ED * 1_000;
|
||||
|
||||
// fund the AHR's SA on BHR for paying bridge delivery fees
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
10_000_000_000_000u128,
|
||||
);
|
||||
// fund sender
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(
|
||||
AssetHubPezkuwichainSender::get().into(),
|
||||
amount * 10,
|
||||
)]);
|
||||
|
||||
// Initially set only default version on all runtimes
|
||||
let newer_xcm_version = xcm::prelude::XCM_VERSION;
|
||||
let older_xcm_version = newer_xcm_version - 1;
|
||||
AssetHubPezkuwichain::force_default_xcm_version(Some(older_xcm_version));
|
||||
BridgeHubPezkuwichain::force_default_xcm_version(Some(older_xcm_version));
|
||||
BridgeHubZagros::force_default_xcm_version(Some(older_xcm_version));
|
||||
AssetHubZagros::force_default_xcm_version(Some(older_xcm_version));
|
||||
|
||||
// send XCM from AssetHubPezkuwichain - fails - destination version not known
|
||||
assert_err!(
|
||||
send_assets_from_asset_hub_pezkuwichain(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
),
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 31,
|
||||
error: [1, 0, 0, 0],
|
||||
message: Some("SendFailure")
|
||||
})
|
||||
);
|
||||
|
||||
// set destination version
|
||||
AssetHubPezkuwichain::force_xcm_version(destination.clone(), newer_xcm_version);
|
||||
|
||||
// set version with `ExportMessage` for BridgeHubPezkuwichain
|
||||
AssetHubPezkuwichain::force_xcm_version(
|
||||
ParentThen(Teyrchain(BridgeHubPezkuwichain::para_id().into()).into()).into(),
|
||||
newer_xcm_version,
|
||||
);
|
||||
// send XCM from AssetHubPezkuwichain - ok
|
||||
assert_ok!(send_assets_from_asset_hub_pezkuwichain(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
));
|
||||
|
||||
// `ExportMessage` on local BridgeHub - fails - remote BridgeHub version not known
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(false);
|
||||
|
||||
// set version for remote BridgeHub on BridgeHubPezkuwichain
|
||||
BridgeHubPezkuwichain::force_xcm_version(bridge_hub_zagros_location(), newer_xcm_version);
|
||||
// set version for AssetHubZagros on BridgeHubZagros
|
||||
BridgeHubZagros::force_xcm_version(
|
||||
ParentThen(Teyrchain(AssetHubZagros::para_id().into()).into()).into(),
|
||||
newer_xcm_version,
|
||||
);
|
||||
|
||||
// send XCM from AssetHubPezkuwichain - ok
|
||||
assert_ok!(send_assets_from_asset_hub_pezkuwichain(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
));
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
// message delivered and processed at destination
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// message processed with failure, but for this scenario it is ok, important is that was delivered
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: false, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
// 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.
|
||||
|
||||
use crate::imports::*;
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_to_other_system_teyrchains_works() {
|
||||
let amount = BRIDGE_HUB_PEZKUWICHAIN_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
BridgeHubPezkuwichain, // Origin
|
||||
vec![AssetHubPezkuwichain], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_to_other_system_teyrchains_works() {
|
||||
let amount = BRIDGE_HUB_PEZKUWICHAIN_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
BridgeHubPezkuwichain, // Origin
|
||||
vec![AssetHubPezkuwichain], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_and_to_relay() {
|
||||
let amount = PEZKUWICHAIN_ED * 100;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Pezkuwichain,
|
||||
vec![BridgeHubPezkuwichain],
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
BridgeHubPezkuwichain,
|
||||
Pezkuwichain,
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_and_to_relay() {
|
||||
let amount = PEZKUWICHAIN_ED * 100;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Pezkuwichain,
|
||||
vec![BridgeHubPezkuwichain],
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
BridgeHubPezkuwichain,
|
||||
Pezkuwichain,
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
[package]
|
||||
name = "pezbridge-hub-zagros-integration-tests"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "Bridge Hub Zagros runtime integration tests with xcm-pez-emulator"
|
||||
publish = false
|
||||
documentation = "https://docs.rs/pezbridge-hub-zagros-integration-tests"
|
||||
repository = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true }
|
||||
hex-literal = { workspace = true, default-features = true }
|
||||
scale-info = { workspace = true }
|
||||
|
||||
# Bizinikiwi
|
||||
pezframe-support = { workspace = true }
|
||||
pezpallet-asset-conversion = { workspace = true }
|
||||
pezpallet-assets = { workspace = true }
|
||||
pezpallet-balances = { workspace = true }
|
||||
pezpallet-message-queue = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true }
|
||||
pezsp-io = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
# Pezkuwi
|
||||
pezpallet-xcm = { workspace = true }
|
||||
xcm = { workspace = true }
|
||||
xcm-builder = { workspace = true }
|
||||
xcm-executor = { workspace = true }
|
||||
|
||||
# Bridges
|
||||
pezpallet-bridge-messages = { workspace = true }
|
||||
pezpallet-bridge-relayers = { workspace = true }
|
||||
|
||||
# Pezcumulus
|
||||
asset-hub-zagros-runtime = { workspace = true }
|
||||
bp-asset-hub-zagros = { workspace = true }
|
||||
bridge-hub-common = { workspace = true }
|
||||
pezbridge-hub-zagros-runtime = { workspace = true }
|
||||
pezcumulus-pezpallet-teyrchain-system = { workspace = true }
|
||||
pezcumulus-pezpallet-xcmp-queue = { workspace = true }
|
||||
emulated-integration-tests-common = { workspace = true }
|
||||
pezkuwichain-zagros-system-emulated-network = { workspace = true }
|
||||
testnet-teyrchains-constants = { features = [
|
||||
"pezkuwichain",
|
||||
"zagros",
|
||||
], workspace = true, default-features = true }
|
||||
teyrchains-common = { workspace = true, default-features = true }
|
||||
|
||||
# Snowbridge
|
||||
pezsnowbridge-core = { workspace = true }
|
||||
pezsnowbridge-inbound-queue-primitives = { workspace = true }
|
||||
pezsnowbridge-outbound-queue-primitives = { workspace = true }
|
||||
pezsnowbridge-pezpallet-inbound-queue = { workspace = true }
|
||||
pezsnowbridge-pezpallet-inbound-queue-fixtures = { workspace = true }
|
||||
pezsnowbridge-pezpallet-inbound-queue-v2 = { workspace = true }
|
||||
pezsnowbridge-pezpallet-outbound-queue = { workspace = true }
|
||||
pezsnowbridge-pezpallet-outbound-queue-v2 = { workspace = true }
|
||||
pezsnowbridge-pezpallet-system = { workspace = true }
|
||||
pezsnowbridge-pezpallet-system-v2 = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"asset-hub-zagros-runtime/runtime-benchmarks",
|
||||
"bp-asset-hub-zagros/runtime-benchmarks",
|
||||
"bridge-hub-common/runtime-benchmarks",
|
||||
"pezbridge-hub-zagros-runtime/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-xcmp-queue/runtime-benchmarks",
|
||||
"emulated-integration-tests-common/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezpallet-asset-conversion/runtime-benchmarks",
|
||||
"pezpallet-assets/runtime-benchmarks",
|
||||
"pezpallet-balances/runtime-benchmarks",
|
||||
"pezpallet-bridge-messages/runtime-benchmarks",
|
||||
"pezpallet-bridge-relayers/runtime-benchmarks",
|
||||
"pezpallet-message-queue/runtime-benchmarks",
|
||||
"pezpallet-xcm/runtime-benchmarks",
|
||||
"pezkuwichain-zagros-system-emulated-network/runtime-benchmarks",
|
||||
"pezsnowbridge-core/runtime-benchmarks",
|
||||
"pezsnowbridge-inbound-queue-primitives/runtime-benchmarks",
|
||||
"pezsnowbridge-outbound-queue-primitives/runtime-benchmarks",
|
||||
"pezsnowbridge-pezpallet-inbound-queue-fixtures/runtime-benchmarks",
|
||||
"pezsnowbridge-pezpallet-inbound-queue-v2/runtime-benchmarks",
|
||||
"pezsnowbridge-pezpallet-inbound-queue/runtime-benchmarks",
|
||||
"pezsnowbridge-pezpallet-outbound-queue-v2/runtime-benchmarks",
|
||||
"pezsnowbridge-pezpallet-outbound-queue/runtime-benchmarks",
|
||||
"pezsnowbridge-pezpallet-system-v2/runtime-benchmarks",
|
||||
"pezsnowbridge-pezpallet-system/runtime-benchmarks",
|
||||
"pezsp-io/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"testnet-teyrchains-constants/runtime-benchmarks",
|
||||
"teyrchains-common/runtime-benchmarks",
|
||||
"xcm-builder/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
]
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// 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.
|
||||
|
||||
#[cfg(test)]
|
||||
mod imports {
|
||||
// Bizinikiwi
|
||||
pub(crate) use codec::Encode;
|
||||
pub(crate) use pezframe_support::{
|
||||
assert_err, assert_ok, pezpallet_prelude::DispatchResult, BoundedVec,
|
||||
};
|
||||
pub(crate) use pezsp_core::H160;
|
||||
pub(crate) use pezsp_runtime::DispatchError;
|
||||
|
||||
// Pezkuwi
|
||||
pub(crate) use xcm::{
|
||||
latest::{ParentThen, PEZKUWICHAIN_GENESIS_HASH, ZAGROS_GENESIS_HASH},
|
||||
prelude::{AccountId32 as AccountId32Junction, *},
|
||||
v5,
|
||||
};
|
||||
pub(crate) use xcm_executor::traits::TransferType;
|
||||
// Pezcumulus
|
||||
pub(crate) use emulated_integration_tests_common::{
|
||||
accounts::ALICE,
|
||||
create_pool_with_native_on,
|
||||
impls::Inspect,
|
||||
test_dry_run_transfer_across_pk_bridge, test_relay_is_trusted_teleporter,
|
||||
test_teyrchain_is_trusted_teleporter, test_teyrchain_is_trusted_teleporter_for_relay,
|
||||
xcm_pez_emulator::{
|
||||
assert_expected_events, bx, Chain, RelayChain as Relay, TestExt, Teyrchain as Para,
|
||||
},
|
||||
xcm_helpers::xcm_transact_paid_execution,
|
||||
ASSETS_PALLET_ID, USDT_ID,
|
||||
};
|
||||
pub(crate) use pezkuwichain_zagros_system_emulated_network::{
|
||||
asset_hub_pezkuwichain_emulated_chain::{
|
||||
asset_hub_pezkuwichain_runtime::{
|
||||
xcm_config::TreasuryAccount, ForeignAssetReserveData,
|
||||
},
|
||||
genesis::ED as ASSET_HUB_PEZKUWICHAIN_ED,
|
||||
AssetHubPezkuwichainParaPallet as AssetHubPezkuwichainPallet,
|
||||
},
|
||||
asset_hub_zagros_emulated_chain::{
|
||||
genesis::{AssetHubZagrosAssetOwner, ED as ASSET_HUB_ZAGROS_ED},
|
||||
AssetHubZagrosParaPallet as AssetHubZagrosPallet,
|
||||
},
|
||||
pezbridge_hub_zagros_emulated_chain::{
|
||||
pezbridge_hub_zagros_runtime, genesis::ED as BRIDGE_HUB_ZAGROS_ED,
|
||||
BridgeHubZagrosExistentialDeposit, BridgeHubZagrosParaPallet as BridgeHubZagrosPallet,
|
||||
BridgeHubZagrosRuntimeOrigin,
|
||||
},
|
||||
pez_penpal_emulated_chain::{
|
||||
self,
|
||||
pez_penpal_runtime::xcm_config::{
|
||||
CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub,
|
||||
LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub,
|
||||
UniversalLocation as PenpalUniversalLocation,
|
||||
},
|
||||
PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner,
|
||||
PenpalBParaPallet as PenpalBPallet,
|
||||
},
|
||||
pezkuwichain_emulated_chain::PezkuwichainRelayPallet as PezkuwichainPallet,
|
||||
zagros_emulated_chain::{genesis::ED as ZAGROS_ED, ZagrosRelayPallet as ZagrosPallet},
|
||||
AssetHubPezkuwichainPara as AssetHubPezkuwichain,
|
||||
AssetHubPezkuwichainParaReceiver as AssetHubPezkuwichainReceiver,
|
||||
AssetHubPezkuwichainParaSender as AssetHubPezkuwichainSender,
|
||||
AssetHubZagrosPara as AssetHubZagros, AssetHubZagrosParaReceiver as AssetHubZagrosReceiver,
|
||||
AssetHubZagrosParaSender as AssetHubZagrosSender,
|
||||
BridgeHubPezkuwichainPara as BridgeHubPezkuwichain, BridgeHubZagrosPara as BridgeHubZagros,
|
||||
BridgeHubZagrosParaReceiver as BridgeHubZagrosReceiver,
|
||||
BridgeHubZagrosParaSender as BridgeHubZagrosSender, PenpalAPara as PenpalA,
|
||||
PenpalAParaReceiver as PenpalAReceiver, PenpalBPara as PenpalB,
|
||||
PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender,
|
||||
PezkuwichainRelay as Pezkuwichain, PezkuwichainRelayReceiver as PezkuwichainReceiver,
|
||||
ZagrosRelay as Zagros, ZagrosRelayReceiver as ZagrosReceiver,
|
||||
ZagrosRelaySender as ZagrosSender,
|
||||
};
|
||||
pub(crate) use teyrchains_common::AccountId;
|
||||
|
||||
pub(crate) const ASSET_ID: u32 = 1;
|
||||
pub(crate) const ASSET_MIN_BALANCE: u128 = 1000;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
// 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.
|
||||
|
||||
//! Tests related to XCM aliasing.
|
||||
|
||||
use crate::imports::*;
|
||||
use pezbridge_hub_zagros_runtime::xcm_config::XcmConfig;
|
||||
use emulated_integration_tests_common::{macros::AccountId, test_cross_chain_alias};
|
||||
use pezframe_support::traits::ContainsPair;
|
||||
use xcm::latest::Junctions::*;
|
||||
|
||||
const ALLOWED: bool = true;
|
||||
const DENIED: bool = false;
|
||||
|
||||
const TELEPORT_FEES: bool = true;
|
||||
const RESERVE_TRANSFER_FEES: bool = false;
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_aliases_into_same_local_account() {
|
||||
// origin and target are the same account on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target = origin.clone();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// On Bridge Hub we don't want to support aliasing from other chains: there is no real world
|
||||
// demand for it, only low-level power users (like relayers) directly interact with Bridge
|
||||
// Hub. They don't need aliasing to operate cross-chain they can operate locally.
|
||||
// Aliasing same account doesn't work on BH.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and BH: denied
|
||||
(AssetHubZagros, BridgeHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and BH: denied
|
||||
(PenpalB, BridgeHubZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_cannot_alias_into_different_local_account() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target: AccountId = [2; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// Aliasing different account on different chains
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and BH: denied
|
||||
(AssetHubZagros, BridgeHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and BH: denied
|
||||
(PenpalB, BridgeHubZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliasing_child_locations() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
// Bridge Hub allows aliasing descendant of origin.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(1, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(8), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
// Does not allow if not descendant.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
0,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_hub_root_aliases_anything() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
// Does not allow AH root to alias other locations.
|
||||
let origin = Location::new(1, X1([Teyrchain(1000)].into()));
|
||||
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(42), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other AH locations cannot alias anything.
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), PalletInstance(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(1000), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other root locations cannot alias anything.
|
||||
let origin = Location::new(1, Here);
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
let origin = Location::new(0, Here);
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1001)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1002)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorized_cross_chain_aliases() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [100; 32].into();
|
||||
let bad_origin: AccountId = [150; 32].into();
|
||||
let target: AccountId = [200; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
let pal_admin = <PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get());
|
||||
PenpalB::mint_foreign_asset(pal_admin.clone(), Location::parent(), origin.clone(), fees * 10);
|
||||
PenpalB::mint_foreign_asset(pal_admin, Location::parent(), bad_origin.clone(), fees * 10);
|
||||
BridgeHubZagros::fund_accounts(vec![(target.clone(), fees * 10)]);
|
||||
|
||||
// let's authorize `origin` on Penpal to alias `target` on BridgeHub
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
let penpal_origin = Location::new(
|
||||
1,
|
||||
X2([
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: origin.clone().into(),
|
||||
},
|
||||
]
|
||||
.into()),
|
||||
);
|
||||
// `target` adds `penpal_origin` as authorized alias
|
||||
assert_ok!(<BridgeHubZagros as BridgeHubZagrosPallet>::PezkuwiXcm::add_authorized_alias(
|
||||
<BridgeHubZagros as Chain>::RuntimeOrigin::signed(target.clone()),
|
||||
Box::new(penpal_origin.into()),
|
||||
None
|
||||
));
|
||||
});
|
||||
// Verify that unauthorized `bad_origin` cannot alias into `target`, from any chain.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and BridgeHub: denied
|
||||
(AssetHubZagros, BridgeHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and BridgeHub: denied
|
||||
(PenpalB, BridgeHubZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
bad_origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// Verify that only authorized `penpal::origin` can alias into `target`, while `origin` on other
|
||||
// chains cannot.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and BridgeHub: denied
|
||||
(AssetHubZagros, BridgeHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and BridgeHub: allowed
|
||||
(PenpalB, BridgeHubZagros, RESERVE_TRANSFER_FEES, ALLOWED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// remove authorization for `origin` on Penpal to alias `target` on BridgeHub
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
// `target` removes all authorized aliases
|
||||
assert_ok!(
|
||||
<BridgeHubZagros as BridgeHubZagrosPallet>::PezkuwiXcm::remove_all_authorized_aliases(
|
||||
<BridgeHubZagros as Chain>::RuntimeOrigin::signed(target.clone())
|
||||
)
|
||||
);
|
||||
});
|
||||
// Verify `penpal::origin` can no longer alias into `target` on BridgeHub.
|
||||
test_cross_chain_alias!(
|
||||
vec![(PenpalB, BridgeHubZagros, RESERVE_TRANSFER_FEES, DENIED)],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
+1599
File diff suppressed because it is too large
Load Diff
+34
@@ -0,0 +1,34 @@
|
||||
// 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.
|
||||
|
||||
//! Tests related to claiming assets trapped during XCM execution.
|
||||
|
||||
use crate::imports::*;
|
||||
|
||||
use emulated_integration_tests_common::test_chain_can_claim_assets;
|
||||
|
||||
#[test]
|
||||
fn assets_can_be_claimed() {
|
||||
let amount = BridgeHubZagrosExistentialDeposit::get();
|
||||
let assets: Assets = (Parent, amount).into();
|
||||
|
||||
test_chain_can_claim_assets!(
|
||||
AssetHubZagros,
|
||||
RuntimeCall,
|
||||
NetworkId::ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
assets,
|
||||
amount
|
||||
);
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
// 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.
|
||||
|
||||
use crate::imports::*;
|
||||
use emulated_integration_tests_common::snowbridge::{SEPOLIA_ID, WETH};
|
||||
|
||||
mod aliases;
|
||||
mod asset_transfers;
|
||||
mod claim_assets;
|
||||
mod register_bridged_assets;
|
||||
mod send_xcm;
|
||||
mod snowbridge;
|
||||
mod snowbridge_common;
|
||||
// mod snowbridge_v2_inbound;
|
||||
mod snowbridge_edge_case;
|
||||
mod snowbridge_v2_inbound;
|
||||
mod snowbridge_v2_inbound_to_pezkuwichain;
|
||||
mod snowbridge_v2_outbound;
|
||||
mod snowbridge_v2_outbound_edge_case;
|
||||
mod snowbridge_v2_outbound_from_pezkuwichain;
|
||||
mod snowbridge_v2_rewards;
|
||||
mod teleport;
|
||||
mod transact;
|
||||
|
||||
pub(crate) fn asset_hub_pezkuwichain_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubPezkuwichain::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn asset_hub_zagros_global_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn bridge_hub_pezkuwichain_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(BridgeHubPezkuwichain::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
// ZGR and wWND
|
||||
pub(crate) fn wnd_at_ah_zagros() -> Location {
|
||||
Parent.into()
|
||||
}
|
||||
pub(crate) fn bridged_wnd_at_ah_pezkuwichain() -> Location {
|
||||
Location::new(2, [GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH))])
|
||||
}
|
||||
|
||||
// TYR and wTYR
|
||||
pub(crate) fn bridged_roc_at_ah_zagros() -> Location {
|
||||
Location::new(2, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))])
|
||||
}
|
||||
|
||||
// USDT and wUSDT
|
||||
pub(crate) fn usdt_at_ah_zagros() -> Location {
|
||||
Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())])
|
||||
}
|
||||
pub(crate) fn bridged_usdt_at_ah_pezkuwichain() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(USDT_ID.into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
// wETH has same relative location on both Pezkuwichain and Zagros AssetHubs
|
||||
pub(crate) fn weth_at_asset_hubs() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID }),
|
||||
AccountKey20 { network: None, key: WETH },
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn create_foreign_on_ah_pezkuwichain(
|
||||
id: v5::Location,
|
||||
sufficient: bool,
|
||||
reserves: Vec<ForeignAssetReserveData>,
|
||||
) {
|
||||
let owner = AssetHubPezkuwichain::account_id_of(ALICE);
|
||||
AssetHubPezkuwichain::force_create_foreign_asset(
|
||||
id.clone(),
|
||||
owner.clone(),
|
||||
sufficient,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
AssetHubPezkuwichain::set_foreign_asset_reserves(id, owner, reserves);
|
||||
}
|
||||
|
||||
pub(crate) fn create_foreign_on_ah_zagros(
|
||||
id: v5::Location,
|
||||
sufficient: bool,
|
||||
reserves: Vec<ForeignAssetReserveData>,
|
||||
prefund_accounts: Vec<(AccountId, u128)>,
|
||||
) {
|
||||
let owner = AssetHubZagros::account_id_of(ALICE);
|
||||
let min = ASSET_MIN_BALANCE;
|
||||
AssetHubZagros::force_create_foreign_asset(
|
||||
id.clone(),
|
||||
owner.clone(),
|
||||
sufficient,
|
||||
min,
|
||||
prefund_accounts,
|
||||
);
|
||||
AssetHubZagros::set_foreign_asset_reserves(id, owner, reserves);
|
||||
}
|
||||
|
||||
pub(crate) fn foreign_balance_on_ah_pezkuwichain(id: v5::Location, who: &AccountId) -> u128 {
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(id, who)
|
||||
})
|
||||
}
|
||||
pub(crate) fn foreign_balance_on_ah_zagros(id: v5::Location, who: &AccountId) -> u128 {
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(id, who)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn send_assets_from_asset_hub_zagros(
|
||||
destination: Location,
|
||||
assets: Assets,
|
||||
fee_idx: u32,
|
||||
// For knowing what reserve to pick.
|
||||
// We only allow using the same transfer type for assets and fees right now.
|
||||
// And only `LocalReserve` or `DestinationReserve`.
|
||||
transfer_type: TransferType,
|
||||
) -> DispatchResult {
|
||||
let signed_origin =
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get().into());
|
||||
let beneficiary: Location =
|
||||
AccountId32Junction { network: None, id: AssetHubPezkuwichainReceiver::get().into() }
|
||||
.into();
|
||||
|
||||
type Runtime = <AssetHubZagros as Chain>::Runtime;
|
||||
let remote_fee_id: AssetId = assets
|
||||
.clone()
|
||||
.into_inner()
|
||||
.get(fee_idx as usize)
|
||||
.ok_or(pezpallet_xcm::Error::<Runtime>::Empty)?
|
||||
.clone()
|
||||
.id;
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
signed_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(assets.into()),
|
||||
bx!(transfer_type.clone()),
|
||||
bx!(remote_fee_id.into()),
|
||||
bx!(transfer_type),
|
||||
bx!(VersionedXcm::from(
|
||||
Xcm::<()>::builder_unsafe().deposit_asset(AllCounted(1), beneficiary).build()
|
||||
)),
|
||||
WeightLimit::Unlimited,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_zagros_message_accepted(expected_processed: bool) {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
if expected_processed {
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
// pay for bridge fees
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { .. }) => {},
|
||||
// message exported
|
||||
RuntimeEvent::BridgePezkuwichainMessages(
|
||||
pezpallet_bridge_messages::Event::MessageAccepted { .. }
|
||||
) => {},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
} else {
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed {
|
||||
success: false,
|
||||
..
|
||||
}) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_pezkuwichain_message_received() {
|
||||
BridgeHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubPezkuwichain,
|
||||
vec![
|
||||
// message sent to destination
|
||||
RuntimeEvent::XcmpQueue(
|
||||
pezcumulus_pezpallet_xcmp_queue::Event::XcmpMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
})
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
// 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.
|
||||
|
||||
use crate::{imports::*, tests::*};
|
||||
|
||||
const XCM_FEE: u128 = 40_000_000_000;
|
||||
|
||||
/// Tests the registering of a Zagros Asset as a bridged asset on Pezkuwichain Asset Hub.
|
||||
#[test]
|
||||
fn register_zagros_asset_on_rah_from_wah() {
|
||||
// Zagros Asset Hub asset when bridged to Pezkuwichain Asset Hub.
|
||||
let bridged_asset_at_rah = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(ASSET_ID.into()),
|
||||
],
|
||||
);
|
||||
// Register above asset on Pezkuwichain AH from Zagros AH.
|
||||
register_asset_on_rah_from_wah(bridged_asset_at_rah);
|
||||
}
|
||||
|
||||
/// Tests the registering of an Ethereum Asset as a bridged asset on Pezkuwichain Asset Hub.
|
||||
#[test]
|
||||
fn register_ethereum_asset_on_rah_from_wah() {
|
||||
// Ethereum asset when bridged to Pezkuwichain Asset Hub.
|
||||
let token_id = H160::random();
|
||||
let bridged_asset_at_rah = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID }),
|
||||
AccountKey20 { network: None, key: token_id.into() },
|
||||
],
|
||||
);
|
||||
// Register above asset on Pezkuwichain AH from Zagros AH.
|
||||
register_asset_on_rah_from_wah(bridged_asset_at_rah);
|
||||
}
|
||||
|
||||
fn register_asset_on_rah_from_wah(bridged_asset_at_rah: Location) {
|
||||
let sa_of_wah_on_rah =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
|
||||
// Encoded `create_asset` call to be executed in Pezkuwichain Asset Hub ForeignAssets pezpallet.
|
||||
let call = AssetHubPezkuwichain::create_foreign_asset_call(
|
||||
bridged_asset_at_rah.clone(),
|
||||
ASSET_MIN_BALANCE,
|
||||
sa_of_wah_on_rah.clone(),
|
||||
);
|
||||
|
||||
let origin_kind = OriginKind::Xcm;
|
||||
let fee_amount = XCM_FEE;
|
||||
let fees = (Parent, fee_amount).into();
|
||||
|
||||
let xcm = xcm_transact_paid_execution(call, origin_kind, fees, sa_of_wah_on_rah.clone());
|
||||
|
||||
// SA-of-WAH-on-RAH needs to have balance to pay for fees and asset creation deposit
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(
|
||||
sa_of_wah_on_rah.clone(),
|
||||
ASSET_HUB_PEZKUWICHAIN_ED * 10000000000,
|
||||
)]);
|
||||
|
||||
let destination = asset_hub_pezkuwichain_location();
|
||||
|
||||
// fund the WAH's SA on WBH for paying bridge delivery fees
|
||||
BridgeHubZagros::fund_para_sovereign(AssetHubZagros::para_id(), 10_000_000_000_000u128);
|
||||
|
||||
// set XCM versions
|
||||
AssetHubZagros::force_xcm_version(destination.clone(), XCM_VERSION);
|
||||
BridgeHubZagros::force_xcm_version(bridge_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
|
||||
let root_origin = <AssetHubZagros as Chain>::RuntimeOrigin::root();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::send(
|
||||
root_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
AssetHubZagros::assert_xcm_pallet_sent();
|
||||
});
|
||||
|
||||
assert_bridge_hub_zagros_message_accepted(true);
|
||||
assert_bridge_hub_pezkuwichain_message_received();
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
AssetHubPezkuwichain::assert_xcmp_queue_success(None);
|
||||
assert_expected_events!(
|
||||
AssetHubPezkuwichain,
|
||||
vec![
|
||||
// Burned the fee
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { who, amount }) => {
|
||||
who: *who == sa_of_wah_on_rah.clone(),
|
||||
amount: *amount == fee_amount,
|
||||
},
|
||||
// Foreign Asset created
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Created { asset_id, creator, owner }) => {
|
||||
asset_id: asset_id == &bridged_asset_at_rah,
|
||||
creator: *creator == sa_of_wah_on_rah.clone(),
|
||||
owner: *owner == sa_of_wah_on_rah,
|
||||
},
|
||||
// Unspent fee minted to origin
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: *who == sa_of_wah_on_rah.clone(),
|
||||
},
|
||||
]
|
||||
);
|
||||
type ForeignAssets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
|
||||
assert!(ForeignAssets::asset_exists(bridged_asset_at_rah));
|
||||
});
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
// 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.
|
||||
|
||||
use emulated_integration_tests_common::xcm_helpers::{
|
||||
find_mq_processed_id, find_xcm_sent_message_id,
|
||||
};
|
||||
use pezkuwichain_zagros_system_emulated_network::zagros_emulated_chain::zagros_runtime::Dmp;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::tests::*;
|
||||
|
||||
#[test]
|
||||
fn send_xcm_from_zagros_relay_to_pezkuwichain_asset_hub_should_fail_on_not_applicable() {
|
||||
// Init tests variables
|
||||
// XcmPallet send arguments
|
||||
let sudo_origin = <Zagros as Chain>::RuntimeOrigin::root();
|
||||
let destination = Zagros::child_location_of(BridgeHubZagros::para_id()).into();
|
||||
let weight_limit = WeightLimit::Unlimited;
|
||||
let check_origin = None;
|
||||
|
||||
let remote_xcm = Xcm(vec![ClearOrigin]);
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit, check_origin },
|
||||
ExportMessage {
|
||||
network: ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
destination: [Teyrchain(AssetHubPezkuwichain::para_id().into())].into(),
|
||||
xcm: remote_xcm,
|
||||
},
|
||||
]));
|
||||
|
||||
// Zagros Global Consensus
|
||||
// Send XCM message from Relay Chain to Bridge Hub source Teyrchain
|
||||
Zagros::execute_with(|| {
|
||||
Dmp::make_teyrchain_reachable(BridgeHubZagros::para_id());
|
||||
|
||||
assert_ok!(<Zagros as ZagrosPallet>::XcmPallet::send(
|
||||
sudo_origin,
|
||||
bx!(destination),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
// Receive XCM message in Bridge Hub source Teyrchain, it should fail, because we don't have
|
||||
// opened bridge/lane.
|
||||
assert_bridge_hub_zagros_message_accepted(false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
|
||||
// prepare data
|
||||
let destination = asset_hub_pezkuwichain_location();
|
||||
let native_token = Location::parent();
|
||||
let amount = ASSET_HUB_ZAGROS_ED * 1_000;
|
||||
|
||||
// fund the AHR's SA on BHR for paying bridge delivery fees
|
||||
BridgeHubZagros::fund_para_sovereign(AssetHubZagros::para_id(), 10_000_000_000_000u128);
|
||||
// fund sender
|
||||
AssetHubZagros::fund_accounts(vec![(AssetHubZagrosSender::get().into(), amount * 10)]);
|
||||
|
||||
// Initially set only default version on all runtimes
|
||||
let newer_xcm_version = xcm::prelude::XCM_VERSION;
|
||||
let older_xcm_version = newer_xcm_version - 1;
|
||||
AssetHubPezkuwichain::force_default_xcm_version(Some(older_xcm_version));
|
||||
BridgeHubPezkuwichain::force_default_xcm_version(Some(older_xcm_version));
|
||||
BridgeHubZagros::force_default_xcm_version(Some(older_xcm_version));
|
||||
AssetHubZagros::force_default_xcm_version(Some(older_xcm_version));
|
||||
|
||||
// send XCM from AssetHubZagros - fails - destination version not known
|
||||
assert_err!(
|
||||
send_assets_from_asset_hub_zagros(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
),
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 31,
|
||||
error: [1, 0, 0, 0],
|
||||
message: Some("SendFailure")
|
||||
})
|
||||
);
|
||||
|
||||
// set destination version
|
||||
AssetHubZagros::force_xcm_version(destination.clone(), newer_xcm_version);
|
||||
|
||||
// set version with `ExportMessage` for BridgeHubZagros
|
||||
AssetHubZagros::force_xcm_version(
|
||||
ParentThen(Teyrchain(BridgeHubZagros::para_id().into()).into()).into(),
|
||||
newer_xcm_version,
|
||||
);
|
||||
// send XCM from AssetHubZagros - ok
|
||||
assert_ok!(send_assets_from_asset_hub_zagros(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
));
|
||||
|
||||
// `ExportMessage` on local BridgeHub - fails - remote BridgeHub version not known
|
||||
assert_bridge_hub_zagros_message_accepted(false);
|
||||
|
||||
// set version for remote BridgeHub on BridgeHubZagros
|
||||
BridgeHubZagros::force_xcm_version(bridge_hub_pezkuwichain_location(), newer_xcm_version);
|
||||
// set version for AssetHubPezkuwichain on BridgeHubPezkuwichain
|
||||
BridgeHubPezkuwichain::force_xcm_version(
|
||||
ParentThen(Teyrchain(AssetHubPezkuwichain::para_id().into()).into()).into(),
|
||||
newer_xcm_version,
|
||||
);
|
||||
|
||||
// send XCM from AssetHubZagros - ok
|
||||
assert_ok!(send_assets_from_asset_hub_zagros(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
));
|
||||
assert_bridge_hub_zagros_message_accepted(true);
|
||||
assert_bridge_hub_pezkuwichain_message_received();
|
||||
// message delivered and processed at destination
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubPezkuwichain,
|
||||
vec![
|
||||
// message processed with failure, but for this scenario it is ok, important is that was delivered
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: false, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xcm_persists_set_topic_across_hops() {
|
||||
for test_topic_id in [Some([42; 32]), None] {
|
||||
// Reset tracked topic state before each run
|
||||
let mut tracked_topic_ids = HashMap::new();
|
||||
|
||||
// Prepare test input
|
||||
let sudo_origin = <Zagros as Chain>::RuntimeOrigin::root();
|
||||
let destination = Zagros::child_location_of(BridgeHubZagros::para_id()).into();
|
||||
let weight_limit = Unlimited;
|
||||
let check_origin = None;
|
||||
|
||||
// Construct XCM with optional SetTopic
|
||||
let mut message = vec![UnpaidExecution { weight_limit, check_origin }, ClearOrigin];
|
||||
if let Some(topic_id) = test_topic_id {
|
||||
message.push(SetTopic(topic_id));
|
||||
}
|
||||
let xcm = VersionedXcm::from(Xcm(message));
|
||||
|
||||
// Send XCM from Zagros to BridgeHubZagros
|
||||
Zagros::execute_with(|| {
|
||||
Dmp::make_teyrchain_reachable(BridgeHubZagros::para_id());
|
||||
assert_ok!(<Zagros as ZagrosPallet>::XcmPallet::send(
|
||||
sudo_origin.clone(),
|
||||
bx!(destination),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
let msg_sent_id = find_xcm_sent_message_id::<Zagros>().expect("Missing Sent Event");
|
||||
tracked_topic_ids.insert("Zagros", msg_sent_id.into());
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
let mq_prc_id =
|
||||
find_mq_processed_id::<BridgeHubZagros>().expect("Missing Processed Event");
|
||||
tracked_topic_ids.insert("BridgeHubZagros", mq_prc_id);
|
||||
});
|
||||
|
||||
// Assert exactly one consistent topic ID across all hops
|
||||
let topic_id = tracked_topic_ids.get("Zagros");
|
||||
assert_eq!(tracked_topic_ids.get("BridgeHubZagros"), topic_id);
|
||||
if let Some(expected) = test_topic_id {
|
||||
assert_eq!(topic_id, Some(&expected.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+2294
File diff suppressed because it is too large
Load Diff
+544
@@ -0,0 +1,544 @@
|
||||
// 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.
|
||||
|
||||
use crate::{imports::*, tests::bridged_roc_at_ah_zagros};
|
||||
use asset_hub_zagros_runtime::xcm_config::LocationToAccountId;
|
||||
use emulated_integration_tests_common::{
|
||||
snowbridge::{SEPOLIA_ID, WETH},
|
||||
PenpalBTeleportableAssetLocation,
|
||||
};
|
||||
use pezframe_support::traits::fungibles::Mutate;
|
||||
use hex_literal::hex;
|
||||
use pezkuwichain_zagros_system_emulated_network::pez_penpal_emulated_chain::{
|
||||
pez_penpal_runtime::xcm_config::{CheckingAccount, TELEPORTABLE_ASSET_ID},
|
||||
PenpalAssetOwner,
|
||||
};
|
||||
use pezsnowbridge_core::AssetMetadata;
|
||||
use pezsp_core::H160;
|
||||
use testnet_teyrchains_constants::zagros::snowbridge::EthereumNetwork;
|
||||
use xcm_builder::ExternalConsensusLocationsConverterFor;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
pub const INITIAL_FUND: u128 = 50_000_000_000_000;
|
||||
pub const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e");
|
||||
pub const AGENT_ADDRESS: [u8; 20] = hex!("90A987B944Cb1dCcE5564e5FDeCD7a54D3de27Fe");
|
||||
pub const TOKEN_AMOUNT: u128 = 10_000_000_000_000;
|
||||
pub const REMOTE_FEE_AMOUNT_IN_ETHER: u128 = 600_000_000_000;
|
||||
pub const LOCAL_FEE_AMOUNT_IN_DOT: u128 = 800_000_000_000;
|
||||
|
||||
pub const EXECUTION_WEIGHT: u64 = 8_000_000_000;
|
||||
|
||||
pub fn beneficiary() -> Location {
|
||||
Location::new(0, [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }])
|
||||
}
|
||||
|
||||
pub fn asset_hub() -> Location {
|
||||
Location::new(1, Teyrchain(AssetHubZagros::para_id().into()))
|
||||
}
|
||||
|
||||
pub fn bridge_hub() -> Location {
|
||||
Location::new(1, Teyrchain(BridgeHubZagros::para_id().into()))
|
||||
}
|
||||
|
||||
pub fn fund_on_bh() {
|
||||
let assethub_sovereign = BridgeHubZagros::sovereign_account_id_of(asset_hub());
|
||||
BridgeHubZagros::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]);
|
||||
}
|
||||
|
||||
pub fn register_assets_on_ah() {}
|
||||
pub fn register_relay_token_on_bh() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
// Register ZGR on BH
|
||||
assert_ok!(<BridgeHubZagros as BridgeHubZagrosPallet>::EthereumSystem::register_token(
|
||||
RuntimeOrigin::root(),
|
||||
Box::new(VersionedLocation::from(Location::parent())),
|
||||
AssetMetadata {
|
||||
name: "wnd".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "wnd".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 12,
|
||||
},
|
||||
));
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::EthereumSystem(pezsnowbridge_pezpallet_system::Event::RegisterToken { .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn register_assets_on_penpal() {
|
||||
let ethereum_sovereign: AccountId = snowbridge_sovereign();
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::force_create(
|
||||
<PenpalB as Chain>::RuntimeOrigin::root(),
|
||||
weth_location().try_into().unwrap(),
|
||||
ethereum_sovereign.clone().into(),
|
||||
true,
|
||||
1,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::force_create(
|
||||
<PenpalB as Chain>::RuntimeOrigin::root(),
|
||||
ethereum().try_into().unwrap(),
|
||||
ethereum_sovereign.into(),
|
||||
true,
|
||||
1,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn register_foreign_asset(token_location: Location) {
|
||||
let bridge_owner = snowbridge_sovereign();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::force_create(
|
||||
RuntimeOrigin::root(),
|
||||
token_location.clone().try_into().unwrap(),
|
||||
bridge_owner.clone().into(),
|
||||
true,
|
||||
1000,
|
||||
));
|
||||
assert!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::asset_exists(
|
||||
token_location.clone().try_into().unwrap(),
|
||||
));
|
||||
});
|
||||
AssetHubZagros::set_foreign_asset_reserves(
|
||||
token_location,
|
||||
bridge_owner,
|
||||
vec![(ethereum(), false).into()],
|
||||
);
|
||||
}
|
||||
|
||||
pub fn register_pal_on_ah() {
|
||||
// Create PAL(i.e. native asset for penpal) on AH.
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
let penpal_asset_id = Location::new(1, Teyrchain(PenpalB::para_id().into()));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::force_create(
|
||||
RuntimeOrigin::root(),
|
||||
penpal_asset_id.clone(),
|
||||
PenpalAssetOwner::get().into(),
|
||||
false,
|
||||
1_000_000,
|
||||
));
|
||||
|
||||
assert!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::asset_exists(
|
||||
penpal_asset_id.clone(),
|
||||
));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
penpal_asset_id.clone(),
|
||||
&AssetHubZagrosReceiver::get(),
|
||||
TOKEN_AMOUNT,
|
||||
));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
penpal_asset_id.clone(),
|
||||
&AssetHubZagrosSender::get(),
|
||||
TOKEN_AMOUNT,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn penpal_root_sovereign() -> pezsp_runtime::AccountId32 {
|
||||
let penpal_root_sovereign: AccountId = PenpalB::execute_with(|| {
|
||||
use pezkuwichain_zagros_system_emulated_network::pez_penpal_emulated_chain::pez_penpal_runtime::xcm_config;
|
||||
xcm_config::LocationToAccountId::convert_location(&xcm_config::RootLocation::get())
|
||||
.unwrap()
|
||||
.into()
|
||||
});
|
||||
penpal_root_sovereign
|
||||
}
|
||||
|
||||
pub fn fund_on_penpal() {
|
||||
let sudo_account = penpal_root_sovereign();
|
||||
PenpalB::fund_accounts(vec![
|
||||
(PenpalBReceiver::get(), INITIAL_FUND),
|
||||
(PenpalBSender::get(), INITIAL_FUND),
|
||||
(CheckingAccount::get(), INITIAL_FUND),
|
||||
(sudo_account.clone(), INITIAL_FUND),
|
||||
]);
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
Location::parent(),
|
||||
&PenpalBReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
Location::parent(),
|
||||
&PenpalBSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
Location::parent(),
|
||||
&sudo_account,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
});
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::Assets::mint_into(
|
||||
TELEPORTABLE_ASSET_ID,
|
||||
&PenpalBReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::Assets::mint_into(
|
||||
TELEPORTABLE_ASSET_ID,
|
||||
&PenpalBSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::Assets::mint_into(
|
||||
TELEPORTABLE_ASSET_ID,
|
||||
&sudo_account,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
});
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&PenpalBReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&PenpalBSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&sudo_account,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&PenpalBReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&PenpalBSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&sudo_account,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_trust_reserve_on_penpal() {
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as Chain>::System::set_storage(
|
||||
<PenpalB as Chain>::RuntimeOrigin::root(),
|
||||
vec![(
|
||||
PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(),
|
||||
Location::new(2, [GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID })]).encode(),
|
||||
)],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn fund_on_ah() {
|
||||
AssetHubZagros::fund_accounts(vec![(AssetHubZagrosSender::get(), INITIAL_FUND)]);
|
||||
AssetHubZagros::fund_accounts(vec![(AssetHubZagrosReceiver::get(), INITIAL_FUND)]);
|
||||
|
||||
let penpal_sovereign = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::sibling_location_of(PenpalB::para_id()),
|
||||
);
|
||||
let penpal_user_sovereign = LocationToAccountId::convert_location(&Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: PenpalBSender::get().into(),
|
||||
},
|
||||
],
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&penpal_sovereign,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&penpal_user_sovereign,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&AssetHubZagrosReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&AssetHubZagrosSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&penpal_sovereign,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&penpal_user_sovereign,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&AssetHubZagrosReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&AssetHubZagrosSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
});
|
||||
|
||||
AssetHubZagros::fund_accounts(vec![(snowbridge_sovereign(), INITIAL_FUND)]);
|
||||
AssetHubZagros::fund_accounts(vec![(penpal_sovereign.clone(), INITIAL_FUND)]);
|
||||
AssetHubZagros::fund_accounts(vec![(penpal_user_sovereign.clone(), INITIAL_FUND)]);
|
||||
}
|
||||
|
||||
pub fn create_pools_on_ah() {
|
||||
// We create a pool between ZGR and WETH in AssetHub to support paying for fees with WETH.
|
||||
let ethereum_sovereign = snowbridge_sovereign();
|
||||
AssetHubZagros::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
PenpalB::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
create_pool_with_native_on!(
|
||||
AssetHubZagros,
|
||||
weth_location(),
|
||||
true,
|
||||
ethereum_sovereign.clone(),
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
create_pool_with_native_on!(
|
||||
AssetHubZagros,
|
||||
ethereum(),
|
||||
true,
|
||||
ethereum_sovereign.clone(),
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn set_up_eth_and_hez_pool() {
|
||||
// We create a pool between ZGR and WETH in AssetHub to support paying for fees with WETH.
|
||||
let ethereum_sovereign = snowbridge_sovereign();
|
||||
AssetHubZagros::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
PenpalB::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
create_pool_with_native_on!(AssetHubZagros, eth_location(), true, ethereum_sovereign.clone());
|
||||
}
|
||||
|
||||
pub(crate) fn set_up_eth_and_hez_pool_on_penpal() {
|
||||
let ethereum_sovereign = snowbridge_sovereign();
|
||||
AssetHubZagros::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
PenpalB::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
create_pool_with_native_on!(PenpalB, eth_location(), true, ethereum_sovereign.clone());
|
||||
}
|
||||
|
||||
pub(crate) fn set_up_eth_and_hez_pool_on_pezkuwichain() {
|
||||
let sa_of_wah_on_rah =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(sa_of_wah_on_rah.clone(), INITIAL_FUND)]);
|
||||
create_pool_with_native_on!(
|
||||
AssetHubPezkuwichain,
|
||||
eth_location(),
|
||||
true,
|
||||
sa_of_wah_on_rah.clone()
|
||||
);
|
||||
}
|
||||
|
||||
pub fn register_pal_on_bh() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
assert_ok!(<BridgeHubZagros as BridgeHubZagrosPallet>::EthereumSystem::register_token(
|
||||
RuntimeOrigin::root(),
|
||||
Box::new(VersionedLocation::from(PenpalBTeleportableAssetLocation::get())),
|
||||
AssetMetadata {
|
||||
name: "pal".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "pal".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 12,
|
||||
},
|
||||
));
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::EthereumSystem(pezsnowbridge_pezpallet_system::Event::RegisterToken { .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn snowbridge_sovereign() -> pezsp_runtime::AccountId32 {
|
||||
use asset_hub_zagros_runtime::xcm_config::UniversalLocation as AssetHubZagrosUniversalLocation;
|
||||
let ethereum_sovereign: AccountId = AssetHubZagros::execute_with(|| {
|
||||
ExternalConsensusLocationsConverterFor::<
|
||||
AssetHubZagrosUniversalLocation,
|
||||
[u8; 32],
|
||||
>::convert_location(&Location::new(
|
||||
2,
|
||||
[xcm::v5::Junction::GlobalConsensus(EthereumNetwork::get())],
|
||||
))
|
||||
.unwrap()
|
||||
.into()
|
||||
});
|
||||
|
||||
ethereum_sovereign
|
||||
}
|
||||
|
||||
pub fn weth_location() -> Location {
|
||||
erc20_token_location(WETH.into())
|
||||
}
|
||||
|
||||
pub fn eth_location() -> Location {
|
||||
Location::new(2, [GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID })])
|
||||
}
|
||||
|
||||
pub fn ethereum() -> Location {
|
||||
eth_location()
|
||||
}
|
||||
|
||||
pub fn erc20_token_location(token_id: H160) -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(EthereumNetwork::get().into()),
|
||||
AccountKey20 { network: None, key: token_id.into() },
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
// TYR and wTYR
|
||||
pub(crate) fn roc_at_ah_pezkuwichain() -> Location {
|
||||
Parent.into()
|
||||
}
|
||||
|
||||
// set up pool
|
||||
pub(crate) fn set_up_pool_with_wnd_on_ah_zagros(
|
||||
asset: Location,
|
||||
is_foreign: bool,
|
||||
initial_fund: u128,
|
||||
initial_liquidity: u128,
|
||||
) {
|
||||
let wnd: Location = Parent.into();
|
||||
AssetHubZagros::fund_accounts(vec![(AssetHubZagrosSender::get(), initial_fund)]);
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
let owner = AssetHubZagrosSender::get();
|
||||
let signed_owner = <AssetHubZagros as Chain>::RuntimeOrigin::signed(owner.clone());
|
||||
|
||||
if is_foreign {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint(
|
||||
signed_owner.clone(),
|
||||
asset.clone().into(),
|
||||
owner.clone().into(),
|
||||
initial_fund,
|
||||
));
|
||||
} else {
|
||||
let asset_id = match asset.interior.last() {
|
||||
Some(GeneralIndex(id)) => *id as u32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::mint(
|
||||
signed_owner.clone(),
|
||||
asset_id.into(),
|
||||
owner.clone().into(),
|
||||
initial_fund,
|
||||
));
|
||||
}
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::create_pool(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd.clone()),
|
||||
Box::new(asset.clone()),
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
|
||||
]
|
||||
);
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::add_liquidity(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd),
|
||||
Box::new(asset),
|
||||
initial_liquidity,
|
||||
initial_liquidity,
|
||||
1,
|
||||
1,
|
||||
owner.into()
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded {..}) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn register_roc_on_bh() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
// Register TYR on BH
|
||||
assert_ok!(<BridgeHubZagros as BridgeHubZagrosPallet>::EthereumSystem::register_token(
|
||||
RuntimeOrigin::root(),
|
||||
Box::new(VersionedLocation::from(bridged_roc_at_ah_zagros())),
|
||||
AssetMetadata {
|
||||
name: "roc".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "roc".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 12,
|
||||
},
|
||||
));
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::EthereumSystem(pezsnowbridge_pezpallet_system::Event::RegisterToken { .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn asset_hub_zagros_global_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
pub(crate) fn bridge_hub_zagros_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(BridgeHubZagros::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
+296
@@ -0,0 +1,296 @@
|
||||
// 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.
|
||||
|
||||
use crate::{imports::*, tests::snowbridge_common::*};
|
||||
use pezbridge_hub_zagros_runtime::xcm_config::LocationToAccountId;
|
||||
use emulated_integration_tests_common::snowbridge::{SEPOLIA_ID, WETH};
|
||||
use pezsnowbridge_core::AssetMetadata;
|
||||
use pezsnowbridge_pezpallet_system::Error;
|
||||
use testnet_teyrchains_constants::zagros::snowbridge::EthereumNetwork;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
// The user origin should be banned in ethereum_blob_exporter with error logs
|
||||
// xcm::ethereum_blob_exporter: could not get teyrchain id from universal source
|
||||
// 'X2([Teyrchain(1000), AccountId32 {...}])'
|
||||
#[test]
|
||||
fn user_send_message_directly_bypass_exporter_from_ah_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
create_pools_on_ah();
|
||||
|
||||
let sov_account_for_sender = LocationToAccountId::convert_location(&Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: AssetHubZagrosSender::get().into(),
|
||||
},
|
||||
],
|
||||
))
|
||||
.unwrap();
|
||||
BridgeHubZagros::fund_accounts(vec![(sov_account_for_sender, INITIAL_FUND)]);
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(1_000_000_000_000) };
|
||||
|
||||
let weth_location_reanchored =
|
||||
Location::new(0, [AccountKey20 { network: None, key: WETH.into() }]);
|
||||
|
||||
let weth_asset = Asset {
|
||||
id: AssetId(weth_location_reanchored.clone()),
|
||||
fun: Fungible(TOKEN_AMOUNT * 1_000_000_000),
|
||||
};
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::send(
|
||||
RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
bx!(VersionedLocation::from(bridge_hub())),
|
||||
bx!(VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(local_fee_asset.clone().into()),
|
||||
BuyExecution { fees: local_fee_asset.clone(), weight_limit: Unlimited },
|
||||
ExportMessage {
|
||||
network: Ethereum { chain_id: SEPOLIA_ID },
|
||||
destination: Here,
|
||||
xcm: Xcm(vec![
|
||||
WithdrawAsset(weth_asset.clone().into()),
|
||||
DepositAsset { assets: Wild(All), beneficiary: beneficiary() },
|
||||
SetTopic([0; 32]),
|
||||
]),
|
||||
},
|
||||
]))),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success:false, .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ENA is not allowed to be registered as PNA
|
||||
#[test]
|
||||
fn test_register_ena_on_bh_will_fail() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
type Runtime = <BridgeHubZagros as Chain>::Runtime;
|
||||
|
||||
assert_ok!(<BridgeHubZagros as BridgeHubZagrosPallet>::Balances::force_set_balance(
|
||||
RuntimeOrigin::root(),
|
||||
pezsp_runtime::MultiAddress::Id(BridgeHubZagrosSender::get()),
|
||||
INITIAL_FUND * 10,
|
||||
));
|
||||
|
||||
assert_err!(
|
||||
<BridgeHubZagros as BridgeHubZagrosPallet>::EthereumSystem::register_token(
|
||||
RuntimeOrigin::root(),
|
||||
Box::new(VersionedLocation::from(weth_location())),
|
||||
AssetMetadata {
|
||||
name: "weth".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "weth".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 18,
|
||||
},
|
||||
),
|
||||
Error::<Runtime>::LocationConversionFailed
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_exploit_with_arbitrary_message_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let remote_fee_asset_location: Location =
|
||||
Location::new(2, [EthereumNetwork::get().into()]).into();
|
||||
|
||||
let remote_fee_asset: Asset = (remote_fee_asset_location.clone(), 1).into();
|
||||
|
||||
let assets = VersionedAssets::from(vec![remote_fee_asset]);
|
||||
|
||||
let exploited_weth = Asset {
|
||||
id: AssetId(Location::new(0, [AccountKey20 { network: None, key: WETH.into() }])),
|
||||
// A big amount without burning
|
||||
fun: Fungible(TOKEN_AMOUNT * 1_000_000_000),
|
||||
};
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
bx!(VersionedLocation::from(ethereum())),
|
||||
bx!(assets),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
bx!(VersionedAssetId::from(remote_fee_asset_location.clone())),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
// exploited_weth here is far more than the burnt, which means instructions inner
|
||||
// are user provided and untrustworthy/dangerous!
|
||||
// Currently it depends on EthereumBlobExporter on BH to check the message is legal
|
||||
// and convert to Ethereum command.
|
||||
bx!(VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(exploited_weth.clone().into()),
|
||||
DepositAsset { assets: Wild(All), beneficiary: beneficiary() },
|
||||
SetTopic([0; 32]),
|
||||
]))),
|
||||
Unlimited
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success:false, .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_from_system_teyrchain_but_not_root_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
create_pools_on_ah();
|
||||
|
||||
let sub_location = PalletInstance(100);
|
||||
let assethub_pallet_sovereign = BridgeHubZagros::sovereign_account_id_of(Location::new(
|
||||
1,
|
||||
[Teyrchain(AssetHubZagros::para_id().into()), sub_location],
|
||||
));
|
||||
BridgeHubZagros::fund_accounts(vec![(assethub_pallet_sovereign.clone(), INITIAL_FUND)]);
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(1_000_000_000_000) };
|
||||
|
||||
let weth_location_reanchored =
|
||||
Location::new(0, [AccountKey20 { network: None, key: WETH.into() }]);
|
||||
|
||||
let weth_asset = Asset {
|
||||
id: AssetId(weth_location_reanchored.clone()),
|
||||
fun: Fungible(TOKEN_AMOUNT * 1_000_000_000),
|
||||
};
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::send(
|
||||
RuntimeOrigin::root(),
|
||||
bx!(VersionedLocation::from(bridge_hub())),
|
||||
bx!(VersionedXcm::from(Xcm(vec![
|
||||
DescendOrigin(sub_location.into()),
|
||||
WithdrawAsset(local_fee_asset.clone().into()),
|
||||
BuyExecution { fees: local_fee_asset.clone(), weight_limit: Unlimited },
|
||||
ExportMessage {
|
||||
network: Ethereum { chain_id: SEPOLIA_ID },
|
||||
destination: Here,
|
||||
xcm: Xcm(vec![
|
||||
WithdrawAsset(weth_asset.clone().into()),
|
||||
DepositAsset { assets: Wild(All), beneficiary: beneficiary() },
|
||||
SetTopic([0; 32]),
|
||||
]),
|
||||
},
|
||||
]))),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success:false, .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_from_non_system_teyrchain_will_fail() {
|
||||
let penpal_sovereign = BridgeHubZagros::sovereign_account_id_of(Location::new(
|
||||
1,
|
||||
[Teyrchain(PenpalB::para_id().into())],
|
||||
));
|
||||
BridgeHubZagros::fund_accounts(vec![(penpal_sovereign.clone(), INITIAL_FUND)]);
|
||||
|
||||
PenpalB::execute_with(|| {
|
||||
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <PenpalB as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset =
|
||||
Asset { id: AssetId(Location::here()), fun: Fungible(1_000_000_000_000) };
|
||||
|
||||
let weth_location_reanchored =
|
||||
Location::new(0, [AccountKey20 { network: None, key: WETH.into() }]);
|
||||
|
||||
let weth_asset =
|
||||
Asset { id: AssetId(weth_location_reanchored.clone()), fun: Fungible(TOKEN_AMOUNT) };
|
||||
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::PezkuwiXcm::send(
|
||||
RuntimeOrigin::root(),
|
||||
bx!(VersionedLocation::from(bridge_hub())),
|
||||
bx!(VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(local_fee_asset.clone().into()),
|
||||
BuyExecution { fees: local_fee_asset.clone(), weight_limit: Unlimited },
|
||||
ExportMessage {
|
||||
network: Ethereum { chain_id: SEPOLIA_ID },
|
||||
destination: Here,
|
||||
xcm: Xcm(vec![
|
||||
WithdrawAsset(weth_asset.clone().into()),
|
||||
DepositAsset { assets: Wild(All), beneficiary: beneficiary() },
|
||||
SetTopic([0; 32]),
|
||||
]),
|
||||
},
|
||||
]))),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
PenpalB,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success:false, origin, .. }) => {
|
||||
origin: *origin == bridge_hub_common::AggregateMessageOrigin::Sibling(PenpalB::para_id()),
|
||||
},]
|
||||
);
|
||||
});
|
||||
}
|
||||
+1068
File diff suppressed because it is too large
Load Diff
+612
@@ -0,0 +1,612 @@
|
||||
// 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.
|
||||
use crate::{
|
||||
imports::*,
|
||||
tests::{
|
||||
assert_bridge_hub_pezkuwichain_message_received, assert_bridge_hub_zagros_message_accepted,
|
||||
asset_hub_pezkuwichain_location, bridged_roc_at_ah_zagros, create_foreign_on_ah_zagros,
|
||||
snowbridge_common::{
|
||||
asset_hub_zagros_global_location, erc20_token_location, eth_location,
|
||||
register_foreign_asset, register_roc_on_bh, set_up_eth_and_hez_pool,
|
||||
set_up_eth_and_hez_pool_on_pezkuwichain, set_up_pool_with_wnd_on_ah_zagros,
|
||||
snowbridge_sovereign, TOKEN_AMOUNT,
|
||||
},
|
||||
},
|
||||
};
|
||||
use asset_hub_zagros_runtime::ForeignAssets;
|
||||
use pezbridge_hub_zagros_runtime::{
|
||||
bridge_common_config::BridgeReward, bridge_to_ethereum_config::EthereumGatewayAddress,
|
||||
EthereumInboundQueueV2,
|
||||
};
|
||||
use codec::Encode;
|
||||
use hex_literal::hex;
|
||||
use pezsnowbridge_core::TokenIdOf;
|
||||
use pezsnowbridge_inbound_queue_primitives::v2::{
|
||||
EthereumAsset::{ForeignTokenERC20, NativeTokenERC20},
|
||||
Message, XcmPayload,
|
||||
};
|
||||
use pezsp_core::{H160, H256};
|
||||
use xcm::opaque::latest::AssetTransferFilter::{ReserveDeposit, ReserveWithdraw};
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
/// Calculates the XCM prologue fee for sending an XCM to AH.
|
||||
const INITIAL_FUND: u128 = 500_000_000_000_000;
|
||||
|
||||
/// An ERC-20 token to be registered and sent.
|
||||
const TOKEN_ID: [u8; 20] = hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2");
|
||||
|
||||
#[test]
|
||||
fn send_token_to_pezkuwichain_v2() {
|
||||
let relayer_account = BridgeHubZagrosSender::get();
|
||||
let relayer_reward = 1_500_000_000_000u128;
|
||||
|
||||
let token: H160 = TOKEN_ID.into();
|
||||
let token_location = erc20_token_location(token);
|
||||
|
||||
let beneficiary_acc_id: H256 = H256::random();
|
||||
let beneficiary_acc_bytes: [u8; 32] = beneficiary_acc_id.into();
|
||||
let beneficiary =
|
||||
Location::new(0, AccountId32 { network: None, id: beneficiary_acc_id.into() });
|
||||
|
||||
let claimer_acc_id = H256::random();
|
||||
let claimer = AccountId32 { network: None, id: claimer_acc_id.into() };
|
||||
let claimer_bytes = claimer.encode();
|
||||
|
||||
// set XCM versions
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_zagros_global_location(), XCM_VERSION);
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
AssetHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
|
||||
// To pay fees on Pezkuwichain.
|
||||
let eth_fee_pezkuwichain_ah: xcm::prelude::Asset =
|
||||
(eth_location(), 3_000_000_000_000u128).into();
|
||||
|
||||
// To satisfy ED
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(
|
||||
pezsp_runtime::AccountId32::from(beneficiary_acc_bytes),
|
||||
3_000_000_000_000,
|
||||
)]);
|
||||
BridgeHubZagros::fund_para_sovereign(AssetHubZagros::para_id(), INITIAL_FUND);
|
||||
|
||||
// Register the token on AH Zagros and Pezkuwichain
|
||||
let snowbridge_sovereign = snowbridge_sovereign();
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeOrigin = <AssetHubPezkuwichain as Chain>::RuntimeOrigin;
|
||||
|
||||
assert_ok!(
|
||||
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets::force_create(
|
||||
RuntimeOrigin::root(),
|
||||
token_location.clone().try_into().unwrap(),
|
||||
snowbridge_sovereign.clone().into(),
|
||||
true,
|
||||
1000,
|
||||
)
|
||||
);
|
||||
|
||||
assert!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets::asset_exists(
|
||||
token_location.clone().try_into().unwrap(),
|
||||
));
|
||||
});
|
||||
register_foreign_asset(token_location.clone());
|
||||
|
||||
set_up_eth_and_hez_pool();
|
||||
set_up_eth_and_hez_pool_on_pezkuwichain();
|
||||
|
||||
let token_transfer_value = 2_000_000_000_000u128;
|
||||
|
||||
let assets = vec![
|
||||
// the token being transferred
|
||||
NativeTokenERC20 { token_id: token.into(), value: token_transfer_value },
|
||||
];
|
||||
|
||||
let token_asset_ah: xcm::prelude::Asset = (token_location.clone(), token_transfer_value).into();
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
let instructions = vec![
|
||||
// Send message to Pezkuwichain AH
|
||||
InitiateTransfer {
|
||||
// Pezkuwichain
|
||||
destination: Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(xcm::latest::PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(1000u32),
|
||||
],
|
||||
),
|
||||
remote_fees: Some(ReserveDeposit(Definite(
|
||||
vec![eth_fee_pezkuwichain_ah.clone()].into(),
|
||||
))),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![ReserveDeposit(Definite(
|
||||
vec![token_asset_ah.clone()].into(),
|
||||
))]),
|
||||
remote_xcm: vec![
|
||||
// Refund unspent fees
|
||||
RefundSurplus,
|
||||
// Deposit assets to beneficiary.
|
||||
DepositAsset { assets: Wild(AllCounted(3)), beneficiary: beneficiary.clone() },
|
||||
SetTopic(H256::random().into()),
|
||||
]
|
||||
.into(),
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: Wild(AllOf { id: AssetId(eth_location()), fun: WildFungibility::Fungible }),
|
||||
beneficiary,
|
||||
},
|
||||
];
|
||||
let xcm: Xcm<()> = instructions.into();
|
||||
let versioned_message_xcm = VersionedXcm::V5(xcm);
|
||||
let origin = EthereumGatewayAddress::get();
|
||||
|
||||
let message = Message {
|
||||
gateway: origin,
|
||||
nonce: 1,
|
||||
origin,
|
||||
assets,
|
||||
xcm: XcmPayload::Raw(versioned_message_xcm.encode()),
|
||||
claimer: Some(claimer_bytes),
|
||||
value: 3_500_000_000_000u128,
|
||||
execution_fee: 1_500_000_000_000u128,
|
||||
relayer_fee: relayer_reward,
|
||||
};
|
||||
|
||||
EthereumInboundQueueV2::process_message(relayer_account.clone(), message).unwrap();
|
||||
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmpQueue(pezcumulus_pezpallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
// Check that the relayer reward was registered.
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardRegistered { relayer, reward_kind, reward_balance }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == relayer_reward,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
// Check that the assets were issued on AssetHub
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
RuntimeEvent::XcmpQueue(pezcumulus_pezpallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
]
|
||||
);
|
||||
|
||||
let events = AssetHubZagros::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped, should not happen."
|
||||
);
|
||||
});
|
||||
|
||||
assert_bridge_hub_zagros_message_accepted(true);
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_received();
|
||||
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubPezkuwichain,
|
||||
vec![
|
||||
// Message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
// Token was issued to beneficiary
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == token_location,
|
||||
owner: *owner == beneficiary_acc_bytes.into(),
|
||||
},
|
||||
// Leftover fees was deposited to beneficiary
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == eth_location(),
|
||||
owner: *owner == beneficiary_acc_bytes.into(),
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
// Beneficiary received the token transfer value
|
||||
assert_eq!(
|
||||
ForeignAssets::balance(token_location, AccountId::from(beneficiary_acc_bytes)),
|
||||
token_transfer_value
|
||||
);
|
||||
|
||||
let events = AssetHubPezkuwichain::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped on Pezkuwichain AssetHub, should not happen."
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_ether_to_pezkuwichain_v2() {
|
||||
let relayer_account = BridgeHubZagrosSender::get();
|
||||
let relayer_reward = 1_500_000_000_000u128;
|
||||
|
||||
let beneficiary_acc_id: H256 = H256::random();
|
||||
let beneficiary_acc_bytes: [u8; 32] = beneficiary_acc_id.into();
|
||||
let beneficiary =
|
||||
Location::new(0, AccountId32 { network: None, id: beneficiary_acc_id.into() });
|
||||
|
||||
let claimer_acc_id = H256::random();
|
||||
let claimer = AccountId32 { network: None, id: claimer_acc_id.into() };
|
||||
let claimer_bytes = claimer.encode();
|
||||
|
||||
// set XCM versions
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_zagros_global_location(), XCM_VERSION);
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
AssetHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
|
||||
// To pay fees on Pezkuwichain.
|
||||
let eth_fee_pezkuwichain_ah: xcm::prelude::Asset =
|
||||
(eth_location(), 2_000_000_000_000u128).into();
|
||||
let ether_asset_ah: xcm::prelude::Asset = (eth_location(), 4_000_000_000_000u128).into();
|
||||
|
||||
BridgeHubZagros::fund_para_sovereign(AssetHubZagros::para_id(), INITIAL_FUND);
|
||||
|
||||
set_up_eth_and_hez_pool();
|
||||
set_up_eth_and_hez_pool_on_pezkuwichain();
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
let instructions = vec![
|
||||
// Send message to Pezkuwichain AH
|
||||
InitiateTransfer {
|
||||
// Pezkuwichain
|
||||
destination: Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(xcm::latest::PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(1000u32),
|
||||
],
|
||||
),
|
||||
remote_fees: Some(ReserveDeposit(Definite(
|
||||
vec![eth_fee_pezkuwichain_ah.clone()].into(),
|
||||
))),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![ReserveDeposit(Definite(
|
||||
vec![ether_asset_ah.clone()].into(),
|
||||
))]),
|
||||
remote_xcm: vec![
|
||||
// Refund unspent fees
|
||||
RefundSurplus,
|
||||
// Deposit assets to beneficiary.
|
||||
DepositAsset { assets: Wild(AllCounted(3)), beneficiary: beneficiary.clone() },
|
||||
SetTopic(H256::random().into()),
|
||||
]
|
||||
.into(),
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: Wild(AllOf { id: AssetId(eth_location()), fun: WildFungibility::Fungible }),
|
||||
beneficiary,
|
||||
},
|
||||
];
|
||||
let xcm: Xcm<()> = instructions.into();
|
||||
let versioned_message_xcm = VersionedXcm::V5(xcm);
|
||||
let origin = EthereumGatewayAddress::get();
|
||||
|
||||
let message = Message {
|
||||
gateway: origin,
|
||||
nonce: 1,
|
||||
origin,
|
||||
assets: vec![],
|
||||
xcm: XcmPayload::Raw(versioned_message_xcm.encode()),
|
||||
claimer: Some(claimer_bytes),
|
||||
value: 6_500_000_000_000u128,
|
||||
execution_fee: 1_500_000_000_000u128,
|
||||
relayer_fee: relayer_reward,
|
||||
};
|
||||
|
||||
EthereumInboundQueueV2::process_message(relayer_account.clone(), message).unwrap();
|
||||
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmpQueue(pezcumulus_pezpallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
// Check that the relayer reward was registered.
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardRegistered { relayer, reward_kind, reward_balance }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == relayer_reward,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
// Check that the assets were issued on AssetHub
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
RuntimeEvent::XcmpQueue(pezcumulus_pezpallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
]
|
||||
);
|
||||
|
||||
let events = AssetHubZagros::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped, should not happen."
|
||||
);
|
||||
});
|
||||
|
||||
assert_bridge_hub_zagros_message_accepted(true);
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_received();
|
||||
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubPezkuwichain,
|
||||
vec![
|
||||
// Message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
// Ether was deposited to beneficiary
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == eth_location(),
|
||||
owner: *owner == beneficiary_acc_bytes.into(),
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
let events = AssetHubPezkuwichain::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped on Pezkuwichain AssetHub, should not happen."
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_roc_from_ethereum_to_pezkuwichain() {
|
||||
let initial_fund: u128 = 200_000_000_000_000;
|
||||
let initial_liquidity: u128 = initial_fund / 2;
|
||||
|
||||
let relayer_account = BridgeHubZagrosSender::get();
|
||||
let relayer_reward = 1_500_000_000_000u128;
|
||||
|
||||
let claimer = AccountId32 { network: None, id: H256::random().into() };
|
||||
let claimer_bytes = claimer.encode();
|
||||
let beneficiary = Location::new(
|
||||
0,
|
||||
AccountId32 { network: None, id: AssetHubPezkuwichainReceiver::get().into() },
|
||||
);
|
||||
|
||||
BridgeHubZagros::fund_para_sovereign(AssetHubZagros::para_id(), INITIAL_FUND);
|
||||
|
||||
let ethereum_sovereign: AccountId = snowbridge_sovereign();
|
||||
let bridged_roc_at_asset_hub_zagros = bridged_roc_at_ah_zagros();
|
||||
create_foreign_on_ah_zagros(
|
||||
bridged_roc_at_asset_hub_zagros.clone(),
|
||||
true,
|
||||
vec![(asset_hub_pezkuwichain_location(), false).into()],
|
||||
vec![],
|
||||
);
|
||||
set_up_pool_with_wnd_on_ah_zagros(
|
||||
bridged_roc_at_asset_hub_zagros.clone(),
|
||||
true,
|
||||
initial_fund,
|
||||
initial_liquidity,
|
||||
);
|
||||
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(AssetHubPezkuwichain::para_id(), initial_fund);
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(AssetHubPezkuwichainSender::get(), initial_fund)]);
|
||||
register_roc_on_bh();
|
||||
|
||||
set_up_eth_and_hez_pool();
|
||||
set_up_eth_and_hez_pool_on_pezkuwichain();
|
||||
|
||||
// set XCM versions
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_zagros_global_location(), XCM_VERSION);
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
AssetHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
|
||||
let eth_fee_pezkuwichain_ah: xcm::prelude::Asset =
|
||||
(eth_location(), 2_000_000_000_000u128).into();
|
||||
|
||||
let roc = Location::new(1, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))]);
|
||||
let token_id = TokenIdOf::convert_location(&roc).unwrap();
|
||||
|
||||
let roc_reachored: xcm::prelude::Asset =
|
||||
(Location::new(2, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))]), TOKEN_AMOUNT)
|
||||
.into();
|
||||
|
||||
let assets = vec![
|
||||
// the token being transferred
|
||||
ForeignTokenERC20 { token_id: token_id.into(), value: TOKEN_AMOUNT },
|
||||
];
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
// Mint the asset into the bridge sovereign account, to mimic locked funds
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosAssetOwner::get()),
|
||||
bridged_roc_at_asset_hub_zagros.clone().into(),
|
||||
ethereum_sovereign.clone().into(),
|
||||
TOKEN_AMOUNT,
|
||||
));
|
||||
});
|
||||
|
||||
// fund the AHW's SA on AHR with the TYR tokens held in reserve
|
||||
let sov_ahw_on_ahr =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(sov_ahw_on_ahr.clone(), INITIAL_FUND)]);
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
let instructions = vec![
|
||||
// Send message to Pezkuwichain AH
|
||||
InitiateTransfer {
|
||||
// Pezkuwichain
|
||||
destination: Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(xcm::latest::PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(1000u32),
|
||||
],
|
||||
),
|
||||
remote_fees: Some(ReserveDeposit(Definite(
|
||||
vec![eth_fee_pezkuwichain_ah.clone()].into(),
|
||||
))),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![ReserveWithdraw(Definite(
|
||||
vec![roc_reachored.clone()].into(),
|
||||
))]),
|
||||
remote_xcm: vec![
|
||||
// Refund unspent fees
|
||||
RefundSurplus,
|
||||
// Deposit assets and leftover fees to beneficiary.
|
||||
DepositAsset { assets: Wild(AllCounted(2)), beneficiary: beneficiary.clone() },
|
||||
SetTopic(H256::random().into()),
|
||||
]
|
||||
.into(),
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: Wild(AllOf { id: AssetId(eth_location()), fun: WildFungibility::Fungible }),
|
||||
beneficiary,
|
||||
},
|
||||
];
|
||||
let xcm: Xcm<()> = instructions.into();
|
||||
let versioned_message_xcm = VersionedXcm::V5(xcm);
|
||||
let origin = EthereumGatewayAddress::get();
|
||||
|
||||
let message = Message {
|
||||
gateway: origin,
|
||||
nonce: 1,
|
||||
origin,
|
||||
assets,
|
||||
xcm: XcmPayload::Raw(versioned_message_xcm.encode()),
|
||||
claimer: Some(claimer_bytes),
|
||||
value: 9_500_000_000_000u128,
|
||||
execution_fee: 3_500_000_000_000u128,
|
||||
relayer_fee: relayer_reward,
|
||||
};
|
||||
|
||||
EthereumInboundQueueV2::process_message(relayer_account.clone(), message).unwrap();
|
||||
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmpQueue(pezcumulus_pezpallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
// Check that the relayer reward was registered.
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardRegistered { relayer, reward_kind, reward_balance }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == relayer_reward,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
|
||||
let events = AssetHubZagros::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped, should not happen."
|
||||
);
|
||||
});
|
||||
|
||||
assert_bridge_hub_zagros_message_accepted(true);
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_received();
|
||||
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubPezkuwichain,
|
||||
vec![
|
||||
// TYR is withdrawn from AHW's SA on AHR
|
||||
RuntimeEvent::Balances(
|
||||
pezpallet_balances::Event::Burned { who, amount }
|
||||
) => {
|
||||
who: *who == sov_ahw_on_ahr,
|
||||
amount: *amount == TOKEN_AMOUNT,
|
||||
},
|
||||
// TYRs deposited to beneficiary
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: *who == AssetHubPezkuwichainReceiver::get(),
|
||||
},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
|
||||
let events = AssetHubPezkuwichain::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped on Pezkuwichain AssetHub, should not happen."
|
||||
);
|
||||
});
|
||||
}
|
||||
+1042
File diff suppressed because it is too large
Load Diff
+432
@@ -0,0 +1,432 @@
|
||||
// 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.
|
||||
|
||||
use crate::{
|
||||
imports::*,
|
||||
tests::{
|
||||
snowbridge_common::*,
|
||||
snowbridge_v2_outbound::{EthereumSystemFrontend, EthereumSystemFrontendCall},
|
||||
usdt_at_ah_zagros,
|
||||
},
|
||||
};
|
||||
use emulated_integration_tests_common::snowbridge::{SEPOLIA_ID, WETH};
|
||||
use pezframe_support::assert_noop;
|
||||
use pezsnowbridge_core::AssetMetadata;
|
||||
use pezsp_runtime::DispatchError::BadOrigin;
|
||||
use xcm::v5::AssetTransferFilter;
|
||||
|
||||
#[test]
|
||||
fn register_penpal_a_asset_from_penpal_b_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
create_pools_on_ah();
|
||||
set_trust_reserve_on_penpal();
|
||||
register_assets_on_penpal();
|
||||
fund_on_penpal();
|
||||
let penpal_user_location = Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: PenpalBSender::get().into(),
|
||||
},
|
||||
],
|
||||
);
|
||||
let asset_location_on_penpal =
|
||||
PenpalB::execute_with(|| PenpalLocalTeleportableToAssetHub::get());
|
||||
let penpal_a_asset_at_asset_hub =
|
||||
Location::new(1, [Junction::Teyrchain(PenpalA::para_id().into())])
|
||||
.appended_with(asset_location_on_penpal)
|
||||
.unwrap();
|
||||
PenpalB::execute_with(|| {
|
||||
type RuntimeOrigin = <PenpalB as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset_on_penpal =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(LOCAL_FEE_AMOUNT_IN_DOT) };
|
||||
|
||||
let remote_fee_asset_on_ah =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let remote_fee_asset_on_ethereum =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let call = EthereumSystemFrontend::EthereumSystemFrontend(
|
||||
EthereumSystemFrontendCall::RegisterToken {
|
||||
asset_id: Box::new(VersionedLocation::from(penpal_a_asset_at_asset_hub)),
|
||||
metadata: Default::default(),
|
||||
fee_asset: remote_fee_asset_on_ethereum.clone(),
|
||||
},
|
||||
);
|
||||
|
||||
let assets = vec![
|
||||
local_fee_asset_on_penpal.clone(),
|
||||
remote_fee_asset_on_ah.clone(),
|
||||
remote_fee_asset_on_ethereum.clone(),
|
||||
];
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
PayFees { asset: local_fee_asset_on_penpal.clone() },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
remote_fee_asset_on_ah.clone().into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveWithdraw(
|
||||
Definite(remote_fee_asset_on_ethereum.clone().into()),
|
||||
)]),
|
||||
remote_xcm: Xcm(vec![
|
||||
DepositAsset { assets: Wild(All), beneficiary: penpal_user_location },
|
||||
Transact {
|
||||
origin_kind: OriginKind::Xcm,
|
||||
call: call.encode().into(),
|
||||
fallback_max_weight: None,
|
||||
},
|
||||
]),
|
||||
},
|
||||
]));
|
||||
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::PezkuwiXcm::execute(
|
||||
RuntimeOrigin::root(),
|
||||
bx!(xcm.clone()),
|
||||
Weight::from(EXECUTION_WEIGHT),
|
||||
));
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Burned { .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
// No events should be emitted on the bridge hub
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
assert_expected_events!(BridgeHubZagros, vec![]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_from_non_system_teyrchain_will_fail() {
|
||||
let penpal_location = Location::new(1, [Teyrchain(PenpalB::para_id().into())]);
|
||||
let penpal_sovereign = BridgeHubZagros::sovereign_account_id_of(penpal_location.clone());
|
||||
BridgeHubZagros::fund_accounts(vec![(penpal_sovereign.clone(), INITIAL_FUND)]);
|
||||
|
||||
PenpalB::execute_with(|| {
|
||||
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <PenpalB as Chain>::RuntimeOrigin;
|
||||
|
||||
let relay_fee_asset =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(1_000_000_000_000) };
|
||||
|
||||
let weth_location_reanchored =
|
||||
Location::new(0, [AccountKey20 { network: None, key: WETH.into() }]);
|
||||
|
||||
let weth_asset =
|
||||
Asset { id: AssetId(weth_location_reanchored.clone()), fun: Fungible(TOKEN_AMOUNT) };
|
||||
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::PezkuwiXcm::send(
|
||||
RuntimeOrigin::root(),
|
||||
bx!(VersionedLocation::from(bridge_hub())),
|
||||
bx!(VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(relay_fee_asset.clone().into()),
|
||||
BuyExecution { fees: relay_fee_asset.clone(), weight_limit: Unlimited },
|
||||
ExportMessage {
|
||||
network: Ethereum { chain_id: SEPOLIA_ID },
|
||||
destination: Here,
|
||||
xcm: Xcm(vec![
|
||||
AliasOrigin(penpal_location),
|
||||
WithdrawAsset(weth_asset.clone().into()),
|
||||
DepositAsset { assets: Wild(All), beneficiary: beneficiary() },
|
||||
SetTopic([0; 32]),
|
||||
]),
|
||||
},
|
||||
]))),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
PenpalB,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success: false, .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn register_usdt_not_from_owner_on_asset_hub_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let fees_asset =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
assert_noop!(
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::SnowbridgeSystemFrontend::register_token(
|
||||
// The owner is Alice, while AssetHubZagrosReceiver is Bob, so it should fail
|
||||
RuntimeOrigin::signed(AssetHubZagrosReceiver::get()),
|
||||
bx!(VersionedLocation::from(usdt_at_ah_zagros())),
|
||||
AssetMetadata {
|
||||
name: "usdt".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "usdt".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 6,
|
||||
},
|
||||
fees_asset
|
||||
),
|
||||
BadOrigin
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn register_relay_token_from_asset_hub_user_origin_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let fees_asset =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
assert_noop!(
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::SnowbridgeSystemFrontend::register_token(
|
||||
RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
bx!(VersionedLocation::from(Location { parents: 1, interior: [].into() })),
|
||||
AssetMetadata {
|
||||
name: "wnd".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "wnd".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 12,
|
||||
},
|
||||
fees_asset
|
||||
),
|
||||
BadOrigin
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub const INSUFFICIENT_REMOTE_FEE_AMOUNT: u128 = 1_000_000_000;
|
||||
|
||||
// Test that the asset trapping and claim flow work correctly.
|
||||
#[test]
|
||||
fn transfer_from_penpal_to_ethereum_trapped_on_ah_and_then_claim_can_work() {
|
||||
create_pools_on_ah();
|
||||
register_pal_on_ah();
|
||||
register_pal_on_bh();
|
||||
fund_on_ah();
|
||||
// penpal
|
||||
set_trust_reserve_on_penpal();
|
||||
register_assets_on_penpal();
|
||||
fund_on_penpal();
|
||||
|
||||
let penpal_user_location = Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: PenpalBSender::get().into(),
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
// Since fee is insufficient, asset got trapped on AH
|
||||
PenpalB::execute_with(|| {
|
||||
type RuntimeOrigin = <PenpalB as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset_on_penpal =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(LOCAL_FEE_AMOUNT_IN_DOT) };
|
||||
|
||||
let remote_fee_asset_on_ah =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(INSUFFICIENT_REMOTE_FEE_AMOUNT) };
|
||||
|
||||
let remote_fee_asset_on_ethereum =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let ena = Asset { id: AssetId(weth_location()), fun: Fungible(TOKEN_AMOUNT) };
|
||||
|
||||
let assets = vec![
|
||||
local_fee_asset_on_penpal.clone(),
|
||||
remote_fee_asset_on_ah.clone(),
|
||||
remote_fee_asset_on_ethereum.clone(),
|
||||
ena.clone(),
|
||||
];
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
PayFees { asset: local_fee_asset_on_penpal.clone() },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
remote_fee_asset_on_ah.clone().into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![
|
||||
AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
remote_fee_asset_on_ethereum.clone().into(),
|
||||
)),
|
||||
AssetTransferFilter::ReserveWithdraw(Definite(ena.clone().into())),
|
||||
]),
|
||||
remote_xcm: Xcm(vec![
|
||||
SetAppendix(Xcm(vec![SetHints {
|
||||
hints: vec![AssetClaimer { location: penpal_user_location }]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
}])),
|
||||
InitiateTransfer {
|
||||
destination: ethereum(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
remote_fee_asset_on_ethereum.clone().into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![
|
||||
AssetTransferFilter::ReserveWithdraw(Definite(ena.clone().into())),
|
||||
]),
|
||||
remote_xcm: Xcm(vec![DepositAsset {
|
||||
assets: Wild(All),
|
||||
beneficiary: beneficiary(),
|
||||
}]),
|
||||
},
|
||||
]),
|
||||
},
|
||||
]));
|
||||
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::PezkuwiXcm::execute(
|
||||
RuntimeOrigin::signed(PenpalBSender::get()),
|
||||
bx!(xcm.clone()),
|
||||
Weight::from(EXECUTION_WEIGHT),
|
||||
));
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::ProcessXcmError { .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
// Claim the trapped asset and deposit on AH.
|
||||
PenpalB::execute_with(|| {
|
||||
type RuntimeOrigin = <PenpalB as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset_on_penpal =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(LOCAL_FEE_AMOUNT_IN_DOT) };
|
||||
|
||||
let remote_fee_asset_on_ah =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let assets = vec![local_fee_asset_on_penpal.clone(), remote_fee_asset_on_ah.clone()];
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
PayFees { asset: local_fee_asset_on_penpal.clone() },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
remote_fee_asset_on_ah.clone().into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![]),
|
||||
remote_xcm: Xcm(vec![
|
||||
ClaimAsset {
|
||||
assets: vec![
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(600914043236) },
|
||||
Asset { id: AssetId(weth_location()), fun: Fungible(TOKEN_AMOUNT) },
|
||||
]
|
||||
.into(),
|
||||
ticket: GeneralIndex(5).into(),
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: Wild(All),
|
||||
beneficiary: AssetHubZagrosReceiver::get().into(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
]));
|
||||
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::PezkuwiXcm::execute(
|
||||
RuntimeOrigin::signed(PenpalBSender::get()),
|
||||
bx!(xcm.clone()),
|
||||
Weight::from(EXECUTION_WEIGHT),
|
||||
));
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsClaimed { .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// A malicious user attempted to exploit the bridge by manually adding an AliasOrigin in the
|
||||
// remoteXcm, successfully routing to the V2 path, but ultimately failing at the BH Exporter.
|
||||
#[test]
|
||||
pub fn exploit_v2_route_with_legacy_v1_transfer_will_fail() {
|
||||
create_pools_on_ah();
|
||||
fund_on_ah();
|
||||
|
||||
let remote_fee_asset =
|
||||
Asset { id: AssetId(eth_location()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let reserve_asset = Asset { id: AssetId(eth_location()), fun: Fungible(TOKEN_AMOUNT) };
|
||||
|
||||
let assets = vec![reserve_asset.clone(), remote_fee_asset.clone()];
|
||||
|
||||
let custom_xcm_on_dest = Xcm::<()>(vec![
|
||||
AliasOrigin(Location::parent()),
|
||||
DepositAsset { assets: Wild(AllCounted(2)), beneficiary: beneficiary() },
|
||||
]);
|
||||
|
||||
assert_ok!(AssetHubZagros::execute_with(|| {
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
bx!(eth_location().into()),
|
||||
bx!(assets.into()),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
bx!(AssetId(eth_location()).into()),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
bx!(VersionedXcm::from(custom_xcm_on_dest)),
|
||||
Unlimited,
|
||||
)
|
||||
}));
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
// Check that the process failed in MessageQueue
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success: false, .. }) => {},
|
||||
]
|
||||
);
|
||||
})
|
||||
}
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
// 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.
|
||||
|
||||
use crate::{
|
||||
imports::*,
|
||||
tests::{
|
||||
asset_hub_pezkuwichain_location, bridged_roc_at_ah_zagros, create_foreign_on_ah_zagros,
|
||||
snowbridge_common::*,
|
||||
snowbridge_v2_outbound::{EthereumSystemFrontend, EthereumSystemFrontendCall},
|
||||
},
|
||||
};
|
||||
use pezframe_support::traits::fungibles::Mutate;
|
||||
use xcm::latest::AssetTransferFilter;
|
||||
|
||||
// set up pool
|
||||
pub(crate) fn set_up_pool_with_wnd_on_ah_zagros(
|
||||
asset: Location,
|
||||
is_foreign: bool,
|
||||
initial_fund: u128,
|
||||
initial_liquidity: u128,
|
||||
) {
|
||||
let wnd: Location = Parent.into();
|
||||
AssetHubZagros::fund_accounts(vec![(AssetHubZagrosSender::get(), initial_fund)]);
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
let owner = AssetHubZagrosSender::get();
|
||||
let signed_owner = <AssetHubZagros as Chain>::RuntimeOrigin::signed(owner.clone());
|
||||
|
||||
if is_foreign {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint(
|
||||
signed_owner.clone(),
|
||||
asset.clone().into(),
|
||||
owner.clone().into(),
|
||||
initial_fund,
|
||||
));
|
||||
} else {
|
||||
let asset_id = match asset.interior.last() {
|
||||
Some(GeneralIndex(id)) => *id as u32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::mint(
|
||||
signed_owner.clone(),
|
||||
asset_id.into(),
|
||||
owner.clone().into(),
|
||||
initial_fund,
|
||||
));
|
||||
}
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::create_pool(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd.clone()),
|
||||
Box::new(asset.clone()),
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
|
||||
]
|
||||
);
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::add_liquidity(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd),
|
||||
Box::new(asset),
|
||||
initial_liquidity,
|
||||
initial_liquidity,
|
||||
1,
|
||||
1,
|
||||
owner.into()
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded {..}) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_pezkuwichain_message_accepted(expected_processed: bool) {
|
||||
BridgeHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
if expected_processed {
|
||||
assert_expected_events!(
|
||||
BridgeHubPezkuwichain,
|
||||
vec![
|
||||
// pay for bridge fees
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { .. }) => {},
|
||||
// message exported
|
||||
RuntimeEvent::BridgeZagrosMessages(
|
||||
pezpallet_bridge_messages::Event::MessageAccepted { .. }
|
||||
) => {},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
} else {
|
||||
assert_expected_events!(
|
||||
BridgeHubPezkuwichain,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed {
|
||||
success: false,
|
||||
..
|
||||
}) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_zagros_message_received() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
// message sent to destination
|
||||
RuntimeEvent::XcmpQueue(
|
||||
pezcumulus_pezpallet_xcmp_queue::Event::XcmpMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_roc_from_asset_hub_pezkuwichain_to_ethereum() {
|
||||
let initial_fund: u128 = 200_000_000_000_000;
|
||||
let initial_liquidity: u128 = initial_fund / 2;
|
||||
let amount: u128 = initial_fund;
|
||||
let roc_fee_amount: u128 = initial_liquidity / 2;
|
||||
let wnd_amount_to_swap: u128 = initial_liquidity / 10;
|
||||
let wnd_fee_amount: u128 = wnd_amount_to_swap / 10;
|
||||
|
||||
let ether_fee_amount: u128 = 4_000_000;
|
||||
|
||||
let sender = AssetHubPezkuwichainSender::get();
|
||||
let roc_at_asset_hub_pezkuwichain = roc_at_ah_pezkuwichain();
|
||||
let bridged_roc_at_asset_hub_zagros = bridged_roc_at_ah_zagros();
|
||||
|
||||
create_foreign_on_ah_zagros(
|
||||
bridged_roc_at_asset_hub_zagros.clone(),
|
||||
true,
|
||||
vec![(asset_hub_pezkuwichain_location(), false).into()],
|
||||
vec![],
|
||||
);
|
||||
set_up_pool_with_wnd_on_ah_zagros(
|
||||
bridged_roc_at_asset_hub_zagros.clone(),
|
||||
true,
|
||||
initial_fund,
|
||||
initial_liquidity,
|
||||
);
|
||||
let previous_owner = snowbridge_sovereign();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::start_destroy(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(previous_owner),
|
||||
ethereum()
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::finish_destroy(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagros::account_id_of(ALICE)),
|
||||
ethereum()
|
||||
));
|
||||
});
|
||||
create_foreign_on_ah_zagros(ethereum(), true, vec![(ethereum(), false).into()], vec![]);
|
||||
set_up_pool_with_wnd_on_ah_zagros(ethereum(), true, initial_fund, initial_liquidity);
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(AssetHubPezkuwichain::para_id(), initial_fund);
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(AssetHubPezkuwichainSender::get(), initial_fund)]);
|
||||
fund_on_bh();
|
||||
register_roc_on_bh();
|
||||
|
||||
// set XCM versions
|
||||
AssetHubPezkuwichain::force_xcm_version(asset_hub_zagros_global_location(), XCM_VERSION);
|
||||
BridgeHubPezkuwichain::force_xcm_version(bridge_hub_zagros_location(), XCM_VERSION);
|
||||
|
||||
// send TYRs, use them for fees
|
||||
let local_fee_asset: Asset = (roc_at_asset_hub_pezkuwichain.clone(), roc_fee_amount).into();
|
||||
let remote_fee_on_zagros: Asset =
|
||||
(roc_at_asset_hub_pezkuwichain.clone(), roc_fee_amount).into();
|
||||
let assets: Assets = (roc_at_asset_hub_pezkuwichain.clone(), amount).into();
|
||||
let reserved_asset_on_zagros: Asset =
|
||||
(roc_at_asset_hub_pezkuwichain.clone(), amount - roc_fee_amount * 2).into();
|
||||
let reserved_asset_on_zagros_reanchored: Asset =
|
||||
(bridged_roc_at_asset_hub_zagros.clone(), (amount - roc_fee_amount * 2) / 2).into();
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
PayFees { asset: local_fee_asset.clone() },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub_zagros_global_location(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveDeposit(Definite(
|
||||
remote_fee_on_zagros.clone().into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveDeposit(Definite(
|
||||
reserved_asset_on_zagros.clone().into(),
|
||||
))]),
|
||||
remote_xcm: Xcm(vec![
|
||||
// swap from roc to wnd
|
||||
ExchangeAsset {
|
||||
give: Definite(reserved_asset_on_zagros_reanchored.clone().into()),
|
||||
want: (Parent, wnd_amount_to_swap).into(),
|
||||
maximal: true,
|
||||
},
|
||||
// swap some wnd to ether
|
||||
ExchangeAsset {
|
||||
give: Definite((Parent, ether_fee_amount * 2).into()),
|
||||
want: (ethereum(), ether_fee_amount).into(),
|
||||
maximal: true,
|
||||
},
|
||||
PayFees { asset: (Parent, wnd_fee_amount).into() },
|
||||
InitiateTransfer {
|
||||
destination: ethereum(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(ether_fee_amount) }.into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveDeposit(
|
||||
Definite(reserved_asset_on_zagros_reanchored.clone().into()),
|
||||
)]),
|
||||
remote_xcm: Xcm(vec![DepositAsset {
|
||||
assets: Wild(All),
|
||||
beneficiary: beneficiary(),
|
||||
}]),
|
||||
},
|
||||
]),
|
||||
},
|
||||
]));
|
||||
|
||||
let _ = AssetHubPezkuwichain::execute_with(|| {
|
||||
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::execute(
|
||||
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(sender),
|
||||
bx!(xcm),
|
||||
Weight::from(EXECUTION_WEIGHT),
|
||||
)
|
||||
});
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
|
||||
// verify expected events on final destination
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
// Check that the Ethereum message was queue in the Outbound Queue
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::EthereumOutboundQueueV2(pezsnowbridge_pezpallet_outbound_queue_v2::Event::MessageQueued{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register_pezkuwichain_asset_on_ethereum_from_rah() {
|
||||
const XCM_FEE: u128 = 4_000_000_000_000;
|
||||
let sa_of_rah_on_wah = AssetHubZagros::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
);
|
||||
|
||||
// Pezkuwichain Asset Hub asset when bridged to Zagros Asset Hub.
|
||||
let bridged_asset_at_wah = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubPezkuwichain::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(ASSET_ID.into()),
|
||||
],
|
||||
);
|
||||
|
||||
AssetHubZagros::force_create_foreign_asset(
|
||||
bridged_asset_at_wah.clone(),
|
||||
sa_of_rah_on_wah.clone(),
|
||||
true,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
|
||||
let fee_asset = Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let call =
|
||||
EthereumSystemFrontend::EthereumSystemFrontend(EthereumSystemFrontendCall::RegisterToken {
|
||||
asset_id: Box::new(VersionedLocation::from(bridged_asset_at_wah.clone())),
|
||||
metadata: Default::default(),
|
||||
fee_asset,
|
||||
})
|
||||
.encode();
|
||||
|
||||
let origin_kind = OriginKind::Xcm;
|
||||
let fee_amount = XCM_FEE;
|
||||
let fees = (Parent, fee_amount).into();
|
||||
|
||||
let xcm = xcm_transact_paid_execution(call.into(), origin_kind, fees, sa_of_rah_on_wah.clone());
|
||||
|
||||
// SA-of-RAH-on-WAH needs to have balance to pay for fees and asset creation deposit
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&sa_of_rah_on_wah,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Balances::force_set_balance(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::root(),
|
||||
sa_of_rah_on_wah.into(),
|
||||
INITIAL_FUND
|
||||
));
|
||||
});
|
||||
|
||||
let destination = asset_hub_zagros_global_location();
|
||||
|
||||
// fund the RAH's SA on RBH for paying bridge delivery fees
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
10_000_000_000_000u128,
|
||||
);
|
||||
|
||||
// set XCM versions
|
||||
AssetHubPezkuwichain::force_xcm_version(destination.clone(), XCM_VERSION);
|
||||
BridgeHubPezkuwichain::force_xcm_version(bridge_hub_zagros_location(), XCM_VERSION);
|
||||
|
||||
let root_origin = <AssetHubPezkuwichain as Chain>::RuntimeOrigin::root();
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::send(
|
||||
root_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
AssetHubPezkuwichain::assert_xcm_pallet_sent();
|
||||
});
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
});
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
// Check that the Ethereum message was queue in the Outbound Queue
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::EthereumOutboundQueueV2(pezsnowbridge_pezpallet_outbound_queue_v2::Event::MessageQueued{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
// 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.
|
||||
|
||||
use crate::tests::snowbridge_common::{eth_location, set_up_eth_and_hez_pool};
|
||||
use pezbridge_hub_zagros_runtime::bridge_common_config::{BridgeReward, BridgeRewardBeneficiaries};
|
||||
use emulated_integration_tests_common::snowbridge::ETHER_MIN_BALANCE;
|
||||
use pezpallet_bridge_relayers::{Error::FailedToPayReward, RewardLedger};
|
||||
|
||||
use crate::imports::*;
|
||||
|
||||
const INITIAL_FUND: u128 = 5_000_000_000_000;
|
||||
//1_000_000_000u128
|
||||
#[test]
|
||||
fn claim_rewards_works() {
|
||||
let assethub_location = BridgeHubZagros::sibling_location_of(AssetHubZagros::para_id());
|
||||
let assethub_sovereign = BridgeHubZagros::sovereign_account_id_of(assethub_location);
|
||||
|
||||
let relayer_account = BridgeHubZagrosSender::get();
|
||||
let reward_address = AssetHubZagrosReceiver::get();
|
||||
|
||||
BridgeHubZagros::fund_accounts(vec![
|
||||
(assethub_sovereign.clone(), INITIAL_FUND),
|
||||
(relayer_account.clone(), INITIAL_FUND),
|
||||
]);
|
||||
set_up_eth_and_hez_pool();
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
let reward_amount = ETHER_MIN_BALANCE * 2; // Reward should be more than Ether min balance
|
||||
|
||||
type BridgeRelayers = <BridgeHubZagros as BridgeHubZagrosPallet>::BridgeRelayers;
|
||||
BridgeRelayers::register_reward(
|
||||
(&relayer_account.clone()).into(),
|
||||
BridgeReward::Snowbridge,
|
||||
reward_amount,
|
||||
);
|
||||
|
||||
// Check that the reward was registered.
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardRegistered { relayer, reward_kind, reward_balance }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == reward_amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
let relayer_location = Location::new(
|
||||
0,
|
||||
[Junction::AccountId32 { id: reward_address.clone().into(), network: None }],
|
||||
);
|
||||
let reward_beneficiary =
|
||||
BridgeRewardBeneficiaries::AssetHubLocation(VersionedLocation::V5(relayer_location));
|
||||
let result = BridgeRelayers::claim_rewards_to(
|
||||
RuntimeOrigin::signed(relayer_account.clone()),
|
||||
BridgeReward::Snowbridge,
|
||||
reward_beneficiary.clone(),
|
||||
);
|
||||
assert_ok!(result);
|
||||
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
// Check that the pay reward event was emitted on BH
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardPaid { relayer, reward_kind, reward_balance, beneficiary }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == reward_amount,
|
||||
beneficiary: *beneficiary == reward_beneficiary,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Check that the reward was paid on AH
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == eth_location(),
|
||||
owner: *owner == reward_address.clone().into(),
|
||||
},
|
||||
]
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn claim_snowbridge_rewards_to_local_account_fails() {
|
||||
let assethub_location = BridgeHubZagros::sibling_location_of(AssetHubZagros::para_id());
|
||||
let assethub_sovereign = BridgeHubZagros::sovereign_account_id_of(assethub_location);
|
||||
|
||||
let relayer_account = BridgeHubZagrosSender::get();
|
||||
let reward_address = AssetHubZagrosReceiver::get();
|
||||
|
||||
BridgeHubZagros::fund_accounts(vec![
|
||||
(assethub_sovereign.clone(), INITIAL_FUND),
|
||||
(relayer_account.clone(), INITIAL_FUND),
|
||||
]);
|
||||
set_up_eth_and_hez_pool();
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type Runtime = <BridgeHubZagros as Chain>::Runtime;
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
let reward_amount = ETHER_MIN_BALANCE * 2; // Reward should be more than Ether min balance
|
||||
|
||||
type BridgeRelayers = <BridgeHubZagros as BridgeHubZagrosPallet>::BridgeRelayers;
|
||||
BridgeRelayers::register_reward(
|
||||
&relayer_account.clone(),
|
||||
BridgeReward::Snowbridge,
|
||||
reward_amount,
|
||||
);
|
||||
|
||||
// Check that the reward was registered.
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardRegistered { relayer, reward_kind, reward_balance }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == reward_amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
let reward_beneficiary = BridgeRewardBeneficiaries::LocalAccount(reward_address);
|
||||
let result = BridgeRelayers::claim_rewards_to(
|
||||
RuntimeOrigin::signed(relayer_account.clone()),
|
||||
BridgeReward::Snowbridge,
|
||||
reward_beneficiary.clone(),
|
||||
);
|
||||
assert_err!(result, FailedToPayReward::<Runtime, ()>);
|
||||
})
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
// 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.
|
||||
|
||||
use crate::imports::*;
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_to_other_system_teyrchains_works() {
|
||||
let amount = BRIDGE_HUB_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
BridgeHubZagros, // Origin
|
||||
vec![AssetHubZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_to_other_system_teyrchains_works() {
|
||||
let amount = BRIDGE_HUB_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
BridgeHubZagros, // Origin
|
||||
vec![AssetHubZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 100;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Zagros,
|
||||
vec![BridgeHubZagros],
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
BridgeHubZagros,
|
||||
Zagros,
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 100;
|
||||
|
||||
test_relay_is_trusted_teleporter!(Zagros, vec![BridgeHubZagros], amount, transfer_assets);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
BridgeHubZagros,
|
||||
Zagros,
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
// 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.
|
||||
|
||||
use crate::tests::{snowbridge_common::snowbridge_sovereign, *};
|
||||
use pezsp_core::Get;
|
||||
use xcm::latest::AssetTransferFilter;
|
||||
|
||||
const ETHEREUM_BOB: [u8; 20] = hex_literal::hex!("11b0b11000011b0b11000011b0b11000011b0b11");
|
||||
|
||||
/// Bob on Ethereum transacts on PenpalB, paying fees using WETH. XCM has to go through Asset Hub
|
||||
/// as the reserve location of WETH. The original origin `Ethereum/Bob` is proxied by Asset Hub.
|
||||
///
|
||||
/// This particular test is not testing snowbridge, but only Bridge Hub, so the tested XCM flow from
|
||||
/// Ethereum starts from Bridge Hub.
|
||||
// TODO(https://github.com/pezkuwichain/pezkuwi-sdk/issues/149): Once Snowbridge supports Transact, start the flow from Ethereum and test completely e2e.
|
||||
fn transfer_and_transact_in_same_xcm(
|
||||
sender: Location,
|
||||
weth: Asset,
|
||||
destination: Location,
|
||||
beneficiary: Location,
|
||||
call: xcm::DoubleEncoded<()>,
|
||||
) {
|
||||
let signed_origin = <BridgeHubZagros as Chain>::RuntimeOrigin::root();
|
||||
let context: InteriorLocation = [
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(<BridgeHubZagros as Para>::TeyrchainInfo::get().into()),
|
||||
]
|
||||
.into();
|
||||
let asset_hub_location = BridgeHubZagros::sibling_location_of(AssetHubZagros::para_id());
|
||||
|
||||
// TODO(https://github.com/pezkuwichain/pezkuwi-sdk/issues/147): dry-run to get local fees, for now use hardcoded value.
|
||||
let ah_fees_amount = 90_000_000_000u128; // current exact value 79_948_099_299
|
||||
let fees_for_ah: Asset = (weth.id.clone(), ah_fees_amount).into();
|
||||
|
||||
// xcm to be executed at dest
|
||||
let xcm_on_dest = Xcm(vec![
|
||||
Transact { origin_kind: OriginKind::Xcm, call, fallback_max_weight: None },
|
||||
ExpectTransactStatus(MaybeErrorCode::Success),
|
||||
// since this is the last hop, we don't need to further use any assets previously
|
||||
// reserved for fees (there are no further hops to cover delivery fees for); we
|
||||
// RefundSurplus to get back any unspent fees
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary },
|
||||
]);
|
||||
let destination = destination.reanchored(&asset_hub_location, &context).unwrap();
|
||||
let xcm_to_ah = Xcm::<()>(vec![
|
||||
UnpaidExecution { check_origin: None, weight_limit: Unlimited },
|
||||
DescendOrigin([PalletInstance(80)].into()), // snowbridge pezpallet
|
||||
UniversalOrigin(GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID })),
|
||||
ReserveAssetDeposited(weth.clone().into()),
|
||||
AliasOrigin(sender),
|
||||
PayFees { asset: fees_for_ah },
|
||||
InitiateTransfer {
|
||||
destination,
|
||||
// on the last hop we can just put everything in fees and `RefundSurplus` to get any
|
||||
// unused back
|
||||
remote_fees: Some(AssetTransferFilter::ReserveDeposit(Wild(All))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: xcm_on_dest,
|
||||
},
|
||||
]);
|
||||
<BridgeHubZagros as BridgeHubZagrosPallet>::PezkuwiXcm::send(
|
||||
signed_origin,
|
||||
bx!(asset_hub_location.into()),
|
||||
bx!(xcm::VersionedXcm::from(xcm_to_ah.into())),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Bob on Ethereum transacts on PenpalB, paying fees using WETH. XCM has to go through Asset Hub
|
||||
/// as the reserve location of WETH. The original origin `Ethereum/Bob` is proxied by Asset Hub.
|
||||
///
|
||||
/// This particular test is not testing snowbridge, but only Bridge Hub, so the tested XCM flow from
|
||||
/// Ethereum starts from Bridge Hub.
|
||||
// TODO(https://github.com/pezkuwichain/pezkuwi-sdk/issues/149): Once Snowbridge supports Transact, start the flow from Ethereum and test completely e2e.
|
||||
#[test]
|
||||
fn transact_from_ethereum_to_penpalb_through_asset_hub() {
|
||||
// Snowbridge doesn't support transact yet, we are emulating it by sending one from Bridge Hub
|
||||
// as if it comes from Snowbridge.
|
||||
let destination = BridgeHubZagros::sibling_location_of(PenpalB::para_id());
|
||||
let sender = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID }),
|
||||
AccountKey20 { network: None, key: ETHEREUM_BOB },
|
||||
],
|
||||
);
|
||||
|
||||
let bridged_weth = weth_at_asset_hubs();
|
||||
PenpalB::force_create_foreign_asset(
|
||||
bridged_weth.clone(),
|
||||
PenpalAssetOwner::get(),
|
||||
true,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
// Configure source Penpal chain to trust local AH as reserve of bridged WETH
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as Chain>::System::set_storage(
|
||||
<PenpalB as Chain>::RuntimeOrigin::root(),
|
||||
vec![(
|
||||
PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(),
|
||||
bridged_weth.encode(),
|
||||
)],
|
||||
));
|
||||
});
|
||||
|
||||
let fee_amount_to_send: teyrchains_common::Balance = ASSET_HUB_ZAGROS_ED * 10000;
|
||||
let sender_chain_as_seen_by_asset_hub =
|
||||
Location::new(2, [GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID })]);
|
||||
|
||||
let sov_of_sender_on_asset_hub = AssetHubZagros::execute_with(|| {
|
||||
AssetHubZagros::sovereign_account_id_of(sender_chain_as_seen_by_asset_hub)
|
||||
});
|
||||
let receiver_as_seen_by_asset_hub = AssetHubZagros::sibling_location_of(PenpalB::para_id());
|
||||
let sov_of_receiver_on_asset_hub = AssetHubZagros::execute_with(|| {
|
||||
AssetHubZagros::sovereign_account_id_of(receiver_as_seen_by_asset_hub)
|
||||
});
|
||||
// Create SAs of sender and receiver on AHW with ED.
|
||||
AssetHubZagros::fund_accounts(vec![
|
||||
(sov_of_sender_on_asset_hub.clone().into(), ASSET_HUB_ZAGROS_ED),
|
||||
(sov_of_receiver_on_asset_hub.clone().into(), ASSET_HUB_ZAGROS_ED),
|
||||
(snowbridge_sovereign().into(), 10_000_000_000_00),
|
||||
]);
|
||||
|
||||
// We create a pool between ZGR and WETH in AssetHub to support paying for fees with WETH.
|
||||
let snowbridge_sovereign = snowbridge_sovereign();
|
||||
create_pool_with_native_on!(
|
||||
AssetHubZagros,
|
||||
bridged_weth.clone(),
|
||||
true,
|
||||
snowbridge_sovereign,
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
// We also need a pool between ZGR and WETH on PenpalB to support paying for fees with WETH.
|
||||
create_pool_with_native_on!(
|
||||
PenpalB,
|
||||
bridged_weth.clone(),
|
||||
true,
|
||||
PenpalAssetOwner::get(),
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
|
||||
// Init values for Teyrchain Destination
|
||||
let receiver = PenpalBReceiver::get();
|
||||
|
||||
// Query initial balances
|
||||
let receiver_assets_before = PenpalB::execute_with(|| {
|
||||
type Assets = <PenpalB as PenpalBPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(bridged_weth.clone(), &receiver)
|
||||
});
|
||||
|
||||
// Now register a new asset on PenpalB from Ethereum/Bob account while paying fees using WETH
|
||||
// (going through Asset Hub)
|
||||
let weth_to_send: Asset = (bridged_weth.clone(), fee_amount_to_send).into();
|
||||
// Silly example of a Transact: Bob creates his own foreign assset on PenpalB based on his
|
||||
// Ethereum address
|
||||
let foreign_asset_at_penpal_b = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID }),
|
||||
AccountKey20 { network: None, key: ETHEREUM_BOB },
|
||||
],
|
||||
);
|
||||
// Encoded `create_asset` call to be executed in PenpalB
|
||||
let call = PenpalB::create_foreign_asset_call(
|
||||
foreign_asset_at_penpal_b.clone(),
|
||||
ASSET_MIN_BALANCE,
|
||||
receiver.clone(),
|
||||
);
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
// initiate transaction
|
||||
transfer_and_transact_in_same_xcm(
|
||||
sender.clone(),
|
||||
weth_to_send,
|
||||
destination,
|
||||
receiver.clone().into(),
|
||||
call,
|
||||
);
|
||||
});
|
||||
AssetHubZagros::execute_with(|| {
|
||||
asset_hub_hop_assertions();
|
||||
});
|
||||
PenpalB::execute_with(|| {
|
||||
let expected_creator = PenpalB::sovereign_account_id_of(sender);
|
||||
penpal_b_assertions(foreign_asset_at_penpal_b, expected_creator, receiver.clone());
|
||||
});
|
||||
|
||||
// Query final balances
|
||||
let receiver_assets_after = PenpalB::execute_with(|| {
|
||||
type Assets = <PenpalB as PenpalBPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(bridged_weth, &receiver)
|
||||
});
|
||||
// Receiver's balance is increased
|
||||
assert!(receiver_assets_after > receiver_assets_before);
|
||||
}
|
||||
|
||||
fn asset_hub_hop_assertions() {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Deposited to receiver teyrchain SA
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pezpallet_assets::Event::Deposited { .. }
|
||||
) => {},
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn penpal_b_assertions(
|
||||
expected_asset: Location,
|
||||
expected_creator: AccountId,
|
||||
expected_owner: AccountId,
|
||||
) {
|
||||
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
|
||||
PenpalB::assert_xcmp_queue_success(None);
|
||||
assert_expected_events!(
|
||||
PenpalB,
|
||||
vec![
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pezpallet_assets::Event::Created { asset_id, creator, owner }
|
||||
) => {
|
||||
asset_id: *asset_id == expected_asset,
|
||||
creator: *creator == expected_creator,
|
||||
owner: *owner == expected_owner,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user