From 0577e53c8a55f59c5b661f958c4f03878486b74a Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 16 Apr 2025 14:07:26 +0200 Subject: [PATCH 1/3] BACKPORT-CONFLICT --- .../pallets/outbound-queue-v2/src/test.rs | 2 +- bridges/snowbridge/pallets/system/src/lib.rs | 1 + .../pallets/system/src/migration.rs | 159 +++- .../outbound-queue/src/v1/message.rs | 4 +- .../outbound-queue/src/v2/message.rs | 2 +- .../src/tests/snowbridge.rs | 738 ++++++++++++++++++ .../assets/asset-hub-rococo/src/xcm_config.rs | 2 +- .../asset-hub-westend/src/xcm_config.rs | 2 +- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 1 + .../bridge-hub-rococo/tests/snowbridge.rs | 2 +- .../bridge-hubs/bridge-hub-westend/src/lib.rs | 1 + .../bridge-hub-westend/tests/snowbridge.rs | 2 +- prdoc/pr_7947.prdoc | 24 + 13 files changed, 926 insertions(+), 14 deletions(-) create mode 100644 prdoc/pr_7947.prdoc diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/test.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/test.rs index 129bdb66c583f..388fd880b37e8 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/test.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/test.rs @@ -298,7 +298,7 @@ fn encode_unlock_message() { let message: Message = mock_message(1000); let message_abi_encoded = encode_mock_message(message); println!("{}", HexDisplay::from(&message_abi_encoded)); - assert_eq!(hex!("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000b1185ede04202fe62d38f5db72f71e38ff3e830500000000000000000000000000000000000000000000000000000000000f4240").to_vec(), message_abi_encoded) + assert_eq!(hex!("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000b1185ede04202fe62d38f5db72f71e38ff3e830500000000000000000000000000000000000000000000000000000000000f4240").to_vec(), message_abi_encoded) } #[test] diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index b1956156fd3d4..c092177abdc3d 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -103,6 +103,7 @@ pub mod pallet { use super::*; #[pallet::pallet] + #[pallet::storage_version(migration::STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] diff --git a/bridges/snowbridge/pallets/system/src/migration.rs b/bridges/snowbridge/pallets/system/src/migration.rs index ee94fc091bd1e..4bac87145fce4 100644 --- a/bridges/snowbridge/pallets/system/src/migration.rs +++ b/bridges/snowbridge/pallets/system/src/migration.rs @@ -2,22 +2,30 @@ // SPDX-FileCopyrightText: 2023 Snowfork //! Governance API for controlling the Ethereum side of the bridge use super::*; -use frame_support::traits::OnRuntimeUpgrade; +use frame_support::{ + migrations::VersionedMigration, + pallet_prelude::*, + traits::{OnRuntimeUpgrade, UncheckedOnRuntimeUpgrade}, + weights::Weight, +}; use log; +use sp_std::marker::PhantomData; #[cfg(feature = "try-runtime")] use sp_runtime::TryRuntimeError; -pub mod v0 { - use frame_support::{pallet_prelude::*, weights::Weight}; +const LOG_TARGET: &str = "ethereum_system::migration"; - use super::*; +/// The in-code storage version. +pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); - const LOG_TARGET: &str = "ethereum_system::migration"; +pub mod v0 { + use super::*; pub struct InitializeOnUpgrade( - sp_std::marker::PhantomData<(T, BridgeHubParaId, AssetHubParaId)>, + PhantomData<(T, BridgeHubParaId, AssetHubParaId)>, ); + impl OnRuntimeUpgrade for InitializeOnUpgrade where @@ -72,3 +80,142 @@ pub mod v0 { } } } + +pub mod v1 { + use super::*; + + #[cfg(feature = "try-runtime")] + use sp_core::U256; + + /// Descreases the fee per gas. + pub struct FeePerGasMigration(PhantomData); + + #[cfg(feature = "try-runtime")] + impl FeePerGasMigration + where + T: Config, + { + /// Calculate the fee required to pay for gas on Ethereum. + fn calculate_remote_fee_v1(params: &PricingParametersOf) -> U256 { + use snowbridge_outbound_queue_primitives::v1::{ + AgentExecuteCommand, Command, ConstantGasMeter, GasMeter, + }; + let command = Command::AgentExecute { + agent_id: H256::zero(), + command: AgentExecuteCommand::TransferToken { + token: H160::zero(), + recipient: H160::zero(), + amount: 0, + }, + }; + let gas_used_at_most = ConstantGasMeter::maximum_gas_used_at_most(&command); + params + .fee_per_gas + .saturating_mul(gas_used_at_most.into()) + .saturating_add(params.rewards.remote) + } + + /// Calculate the fee required to pay for gas on Ethereum. + fn calculate_remote_fee_v2(params: &PricingParametersOf) -> U256 { + use snowbridge_outbound_queue_primitives::v2::{Command, ConstantGasMeter, GasMeter}; + let command = Command::UnlockNativeToken { + token: H160::zero(), + recipient: H160::zero(), + amount: 0, + }; + let gas_used_at_most = ConstantGasMeter::maximum_dispatch_gas_used_at_most(&command); + params + .fee_per_gas + .saturating_mul(gas_used_at_most.into()) + .saturating_add(params.rewards.remote) + } + } + + /// The percentage gas increase. We must adjust the fee per gas by this percentage. + const GAS_INCREASE_PERCENTAGE: u64 = 70; + + impl UncheckedOnRuntimeUpgrade for FeePerGasMigration + where + T: Config, + { + fn on_runtime_upgrade() -> Weight { + let mut params = Pallet::::parameters(); + + let old_fee_per_gas = params.fee_per_gas; + + // Fee per gas can be set based on a percentage in order to keep the remote fee the + // same. + params.fee_per_gas = params.fee_per_gas * GAS_INCREASE_PERCENTAGE / 100; + + log::info!( + target: LOG_TARGET, + "Fee per gas migrated from {old_fee_per_gas:?} to {0:?}.", + params.fee_per_gas, + ); + + PricingParameters::::put(params); + T::DbWeight::get().reads_writes(1, 1) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, TryRuntimeError> { + use codec::Encode; + + let params = Pallet::::parameters(); + let remote_fee_v1 = Self::calculate_remote_fee_v1(¶ms); + let remote_fee_v2 = Self::calculate_remote_fee_v2(¶ms); + + log::info!( + target: LOG_TARGET, + "Pre fee per gas migration: pricing parameters = {params:?}, remote_fee_v1 = {remote_fee_v1:?}, remote_fee_v2 = {remote_fee_v2:?}" + ); + Ok((params, remote_fee_v1, remote_fee_v2).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), TryRuntimeError> { + use codec::Decode; + + let (old_params, old_remote_fee_v1, old_remote_fee_v2): ( + PricingParametersOf, + U256, + U256, + ) = Decode::decode(&mut &state[..]).unwrap(); + + let params = Pallet::::parameters(); + ensure!(old_params.exchange_rate == params.exchange_rate, "Exchange rate unchanged."); + ensure!(old_params.rewards == params.rewards, "Rewards unchanged."); + ensure!( + (old_params.fee_per_gas * GAS_INCREASE_PERCENTAGE / 100) == params.fee_per_gas, + "Fee per gas decreased." + ); + ensure!(old_params.multiplier == params.multiplier, "Multiplier unchanged."); + + let remote_fee_v1 = Self::calculate_remote_fee_v1(¶ms); + let remote_fee_v2 = Self::calculate_remote_fee_v2(¶ms); + ensure!( + remote_fee_v1 <= old_remote_fee_v1, + "The v1 remote fee can cover the cost of the previous fee." + ); + ensure!( + remote_fee_v2 <= old_remote_fee_v2, + "The v2 remote fee can cover the cost of the previous fee." + ); + + log::info!( + target: LOG_TARGET, + "Post fee per gas migration: pricing parameters = {params:?} remote_fee_v1 = {remote_fee_v1:?} remote_fee_v2 = {remote_fee_v2:?}" + ); + Ok(()) + } + } +} + +/// Run the migration of the gas price and increment the pallet version so it cannot be re-run. +pub type FeePerGasMigrationV0ToV1 = VersionedMigration< + 0, + 1, + v1::FeePerGasMigration, + Pallet, + ::DbWeight, +>; diff --git a/bridges/snowbridge/primitives/outbound-queue/src/v1/message.rs b/bridges/snowbridge/primitives/outbound-queue/src/v1/message.rs index 9f96f05e0bf0e..c5a1ebb391072 100644 --- a/bridges/snowbridge/primitives/outbound-queue/src/v1/message.rs +++ b/bridges/snowbridge/primitives/outbound-queue/src/v1/message.rs @@ -349,7 +349,7 @@ impl GasMeter for ConstantGasMeter { // * No gas refund for clearing storage slot of source account in ERC20 contract // * Assume dest account in ERC20 contract does not yet have a storage slot // * ERC20.transferFrom possibly does other business logic besides updating balances - AgentExecuteCommand::TransferToken { .. } => 100_000, + AgentExecuteCommand::TransferToken { .. } => 200_000, }, Command::Upgrade { initializer, .. } => { let initializer_max_gas = match *initializer { @@ -362,7 +362,7 @@ impl GasMeter for ConstantGasMeter { }, Command::SetTokenTransferFees { .. } => 60_000, Command::SetPricingParameters { .. } => 60_000, - Command::UnlockNativeToken { .. } => 100_000, + Command::UnlockNativeToken { .. } => 200_000, Command::RegisterForeignToken { .. } => 1_200_000, Command::MintForeignToken { .. } => 100_000, } diff --git a/bridges/snowbridge/primitives/outbound-queue/src/v2/message.rs b/bridges/snowbridge/primitives/outbound-queue/src/v2/message.rs index aea756b183e5d..31abd5a5cb565 100644 --- a/bridges/snowbridge/primitives/outbound-queue/src/v2/message.rs +++ b/bridges/snowbridge/primitives/outbound-queue/src/v2/message.rs @@ -294,7 +294,7 @@ impl GasMeter for ConstantGasMeter { // the the initializer is called. 50_000 + initializer.maximum_required_gas }, - Command::UnlockNativeToken { .. } => 100_000, + Command::UnlockNativeToken { .. } => 200_000, Command::RegisterForeignToken { .. } => 1_200_000, Command::MintForeignToken { .. } => 100_000, Command::CallContract { gas: gas_limit, .. } => *gas_limit, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index eef3081f5a8c2..1b040c1140871 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -47,6 +47,7 @@ const INSUFFICIENT_XCM_FEE: u128 = 1000; const TOKEN_AMOUNT: u128 = 100_000_000_000; const TREASURY_ACCOUNT: [u8; 32] = hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000"); +const BRIDGE_FEE: u128 = 4_000_000_000_000; #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] pub enum ControlCall { @@ -1269,3 +1270,740 @@ fn transfer_ah_token() { ); }); } +<<<<<<< HEAD +======= + +#[test] +fn transfer_penpal_native_asset() { + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of( + BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()), + ); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let pal_at_asset_hub = Location::new(1, [Parachain(PenpalB::para_id().into())]); + + let pal_after_reanchored = Location::new( + 1, + [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(PenpalB::para_id().into())], + ); + + let token_id = TokenIdOf::convert_location(&pal_after_reanchored).unwrap(); + + let asset_owner = PenpalAssetOwner::get(); + + AssetHubWestend::force_create_foreign_asset( + pal_at_asset_hub.clone(), + asset_owner.into(), + true, + 1, + vec![], + ); + + let penpal_sovereign = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalB::para_id()), + ); + AssetHubWestend::fund_accounts(vec![(penpal_sovereign.clone(), INITIAL_FUND)]); + + // Register token + BridgeHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::root(), + Box::new(VersionedLocation::from(pal_at_asset_hub.clone())), + AssetMetadata { + name: "pal".as_bytes().to_vec().try_into().unwrap(), + symbol: "pal".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); + }); + + PenpalB::execute_with(|| { + assert_ok!(::ForeignAssets::mint_into( + Location::parent(), + &PenpalBSender::get(), + INITIAL_FUND, + )); + }); + + // Send PAL to Ethereum + PenpalB::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + // DOT as fee + let assets = vec![ + // Should cover the bridge fee + Asset { id: AssetId(Location::parent()), fun: Fungible(BRIDGE_FEE) }, + Asset { id: AssetId(Location::here()), fun: Fungible(TOKEN_AMOUNT) }, + ]; + + let beneficiary = Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + ); + + let destination = Location::new(1, [Parachain(AssetHubWestend::para_id().into())]); + + let custom_xcm_on_dest = Xcm::<()>(vec![DepositReserveAsset { + assets: Wild(AllOf { + id: AssetId(pal_at_asset_hub.clone()), + fun: WildFungibility::Fungible, + }), + dest: ethereum(), + xcm: vec![ + BuyExecution { + fees: Asset { + id: AssetId(pal_after_reanchored.clone()), + fun: Fungible(TOKEN_AMOUNT), + }, + weight_limit: Unlimited, + }, + DepositAsset { assets: Wild(AllCounted(1)), beneficiary }, + ] + .into(), + }]); + + assert_ok!(::PolkadotXcm::transfer_assets_using_type_and_then( + RuntimeOrigin::signed(PenpalBSender::get()), + Box::new(VersionedLocation::from(destination)), + Box::new(VersionedAssets::from(assets)), + Box::new(TransferType::Teleport), + Box::new(VersionedAssetId::from(AssetId(Location::parent()))), + Box::new(TransferType::DestinationReserve), + Box::new(VersionedXcm::from(custom_xcm_on_dest)), + Unlimited, + )); + + assert_expected_events!( + PenpalB, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned{ .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},] + ); + }); + + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] + ); + }); + + // Send PAL back from Ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::AccountId32 { id: AssetHubWestendSender::get().into() }, + amount: TOKEN_AMOUNT, + fee: XCM_FEE, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned{..}) => {},] + ); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued{..}) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + type RuntimeOrigin = ::RuntimeOrigin; + + let destination = AssetHubWestend::sibling_location_of(PenpalB::para_id()); + + let beneficiary = + Location::new(0, [AccountId32 { network: None, id: PenpalBReceiver::get().into() }]); + + // DOT as fee + let assets = + vec![Asset { id: AssetId(pal_at_asset_hub.clone()), fun: Fungible(TOKEN_AMOUNT) }]; + + assert_ok!( + ::PolkadotXcm::limited_teleport_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(VersionedLocation::from(destination)), + Box::new(VersionedLocation::from(beneficiary)), + Box::new(VersionedAssets::from(assets)), + 0, + Unlimited, + ) + ); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned{..}) => {},] + ); + }); + + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + PenpalB, + vec![RuntimeEvent::Balances(pallet_balances::Event::Minted{..}) => {},] + ); + }) +} + +#[test] +fn transfer_penpal_teleport_enabled_asset() { + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of( + BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()), + ); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let asset_location_on_penpal = PenpalLocalTeleportableToAssetHub::get(); + + let pal_at_asset_hub = Location::new(1, [Junction::Parachain(PenpalB::para_id().into())]) + .appended_with(asset_location_on_penpal.clone()) + .unwrap(); + + let pal_after_reanchored = Location::new( + 1, + [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(PenpalB::para_id().into())], + ) + .appended_with(asset_location_on_penpal.clone()) + .unwrap(); + + let token_id = TokenIdOf::convert_location(&pal_after_reanchored).unwrap(); + + let penpal_sovereign = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalB::para_id()), + ); + AssetHubWestend::fund_accounts(vec![(penpal_sovereign.clone(), INITIAL_FUND)]); + AssetHubWestend::fund_accounts(vec![(snowbridge_sovereign(), INITIAL_FUND)]); + + // Register token + BridgeHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::root(), + Box::new(VersionedLocation::from(pal_at_asset_hub.clone())), + AssetMetadata { + name: "pal".as_bytes().to_vec().try_into().unwrap(), + symbol: "pal".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); + }); + + // Fund on Penpal + PenpalB::fund_accounts(vec![(CheckingAccount::get(), INITIAL_FUND)]); + PenpalB::execute_with(|| { + assert_ok!(::Assets::mint_into( + TELEPORTABLE_ASSET_ID, + &PenpalBSender::get(), + INITIAL_FUND, + )); + assert_ok!(::ForeignAssets::mint_into( + Location::parent(), + &PenpalBSender::get(), + INITIAL_FUND, + )); + }); + + // Send PAL to Ethereum + PenpalB::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + // DOT as fee + let assets = vec![ + // Should cover the bridge fee + Asset { id: AssetId(Location::parent()), fun: Fungible(BRIDGE_FEE) }, + Asset { id: AssetId(asset_location_on_penpal.clone()), fun: Fungible(TOKEN_AMOUNT) }, + ]; + + let beneficiary = Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + ); + + let destination = Location::new(1, [Parachain(AssetHubWestend::para_id().into())]); + + let custom_xcm_on_dest = Xcm::<()>(vec![DepositReserveAsset { + assets: Wild(AllOf { + id: AssetId(pal_at_asset_hub.clone()), + fun: WildFungibility::Fungible, + }), + dest: ethereum(), + xcm: vec![ + BuyExecution { + fees: Asset { + id: AssetId(pal_after_reanchored.clone()), + fun: Fungible(TOKEN_AMOUNT), + }, + weight_limit: Unlimited, + }, + DepositAsset { assets: Wild(AllCounted(1)), beneficiary }, + ] + .into(), + }]); + + assert_ok!(::PolkadotXcm::transfer_assets_using_type_and_then( + RuntimeOrigin::signed(PenpalBSender::get()), + Box::new(VersionedLocation::from(destination)), + Box::new(VersionedAssets::from(assets)), + Box::new(TransferType::Teleport), + Box::new(VersionedAssetId::from(AssetId(Location::parent()))), + Box::new(TransferType::DestinationReserve), + Box::new(VersionedXcm::from(custom_xcm_on_dest)), + Unlimited, + )); + + assert_expected_events!( + PenpalB, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned{ .. }) => {},] + ); + + assert_expected_events!( + PenpalB, + vec![RuntimeEvent::Assets(pallet_assets::Event::Burned{ .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},] + ); + }); + + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] + ); + }); + + // Send PAL back from Ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::AccountId32 { id: AssetHubWestendSender::get().into() }, + amount: TOKEN_AMOUNT, + fee: XCM_FEE, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => + {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned{..}) => {},] + ); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued{..}) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + type RuntimeOrigin = ::RuntimeOrigin; + + let destination = AssetHubWestend::sibling_location_of(PenpalB::para_id()); + + let beneficiary = + Location::new(0, [AccountId32 { network: None, id: PenpalBReceiver::get().into() }]); + + // DOT as fee + let assets = vec![ + Asset { id: AssetId(Location::parent()), fun: Fungible(XCM_FEE) }, + Asset { id: AssetId(pal_at_asset_hub.clone()), fun: Fungible(TOKEN_AMOUNT) }, + ]; + + let custom_xcm_on_dest = Xcm::<()>(vec![ + BuyExecution { + fees: Asset { id: AssetId(Location::parent()), fun: Fungible(XCM_FEE) }, + weight_limit: Unlimited, + }, + DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, + ]); + + assert_ok!( + ::PolkadotXcm::transfer_assets_using_type_and_then( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(VersionedLocation::from(destination)), + Box::new(VersionedAssets::from(assets)), + Box::new(TransferType::Teleport), + Box::new(VersionedAssetId::from(AssetId(Location::parent()))), + Box::new(TransferType::LocalReserve), + Box::new(VersionedXcm::from(custom_xcm_on_dest)), + Unlimited, + ) + ); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned{..}) => {},] + ); + }); + + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + PenpalB, + vec![RuntimeEvent::Assets(pallet_assets::Event::Issued{..}) => {},] + ); + }) +} + +#[test] +fn mint_native_asset_on_penpal_from_relay_chain() { + // Send XCM message from Relay Chain to Penpal + Westend::execute_with(|| { + Dmp::make_parachain_reachable(PenpalB::para_id()); + // Set balance call + let mint_token_call = hex!("0a0800d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0f00406352bfc601"); + let remote_xcm = VersionedXcm::from(Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::Superuser, + fallback_max_weight: None, + call: mint_token_call.to_vec().into(), + }, + ])); + assert_ok!(::XcmPallet::send( + ::RuntimeOrigin::root(), + bx!(VersionedLocation::from(Location::new(0, [Parachain(PenpalB::para_id().into())]))), + bx!(remote_xcm), + )); + + type RuntimeEvent = ::RuntimeEvent; + // Check that the Transact message was sent + assert_expected_events!( + Westend, + vec![ + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that a message was sent to Ethereum to create the agent + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::Balances(pallet_balances::Event::BalanceSet { + .. + }) => {}, + ] + ); + }); +} + +pub(crate) fn set_up_pool_with_wnd_on_ah_westend( + asset: Location, + is_foreign: bool, + initial_fund: u128, + initial_liquidity: u128, +) { + let wnd: Location = Parent.into(); + AssetHubWestend::fund_accounts(vec![(AssetHubWestendSender::get(), initial_fund)]); + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + let owner = AssetHubWestendSender::get(); + let signed_owner = ::RuntimeOrigin::signed(owner.clone()); + + if is_foreign { + assert_ok!(::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!(::Assets::mint( + signed_owner.clone(), + asset_id.into(), + owner.clone().into(), + initial_fund, + )); + } + assert_ok!(::AssetConversion::create_pool( + signed_owner.clone(), + Box::new(wnd.clone()), + Box::new(asset.clone()), + )); + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + assert_ok!(::AssetConversion::add_liquidity( + signed_owner.clone(), + Box::new(wnd), + Box::new(asset), + initial_liquidity, + initial_liquidity, + 1, + 1, + owner.into() + )); + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded {..}) => {}, + ] + ); + }); +} + +#[test] +fn transfer_roc_from_ah_with_legacy_api_will_fail() { + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of( + BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()), + ); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); + + let ethereum_sovereign: AccountId = snowbridge_sovereign(); + AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); + + let bridged_roc_at_asset_hub_westend = bridged_roc_at_ah_westend(); + + create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), true); + + let asset_id: Location = bridged_roc_at_asset_hub_westend.clone(); + + let initial_fund: u128 = 200_000_000_000_000; + let initial_liquidity: u128 = initial_fund / 2; + // Setup pool and add liquidity + set_up_pool_with_wnd_on_ah_westend( + bridged_roc_at_asset_hub_westend.clone(), + true, + initial_fund, + initial_liquidity, + ); + + register_roc_on_bh(); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + // Send partial of the token, will fail if send all + let assets = + vec![Asset { id: AssetId(asset_id.clone()), fun: Fungible(initial_fund / 10) }]; + let versioned_assets = VersionedAssets::from(Assets::from(assets)); + + let beneficiary = VersionedLocation::from(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + let result = ::PolkadotXcm::transfer_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(VersionedLocation::from(ethereum_destination)), + Box::new(beneficiary), + Box::new(versioned_assets), + 0, + Unlimited, + ); + + assert_err!( + result, + DispatchError::Module(sp_runtime::ModuleError { + index: 31, + error: [21, 0, 0, 0], + message: Some("InvalidAssetUnknownReserve") + }) + ); + }); +} + +#[test] +fn transfer_roc_from_ah_with_transfer_and_then() { + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of( + BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()), + ); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); + + let ethereum_sovereign: AccountId = snowbridge_sovereign(); + AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); + + let bridged_roc_at_asset_hub_westend = bridged_roc_at_ah_westend(); + + create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), true); + + let asset_id: Location = bridged_roc_at_asset_hub_westend.clone(); + + let initial_fund: u128 = 200_000_000_000_000; + let initial_liquidity: u128 = initial_fund / 2; + // Setup pool and add liquidity + set_up_pool_with_wnd_on_ah_westend( + bridged_roc_at_asset_hub_westend.clone(), + true, + initial_fund, + initial_liquidity, + ); + + register_roc_on_bh(); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + // Send partial of the token, will fail if send all + let asset = Asset { id: AssetId(asset_id.clone()), fun: Fungible(initial_fund / 10) }; + let assets = vec![asset.clone()]; + let versioned_assets = VersionedAssets::from(Assets::from(assets.clone())); + + let beneficiary = Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + ); + + let custom_xcm = Xcm::<()>(vec![DepositAsset { + assets: Wild(AllCounted(assets.len() as u32)), + beneficiary, + }]); + + assert_ok!(::PolkadotXcm::transfer_assets_using_type_and_then( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(VersionedLocation::from(ethereum_destination)), + Box::new(versioned_assets), + Box::new(TransferType::LocalReserve), + Box::new(VersionedAssetId::from(asset_id.clone())), + Box::new(TransferType::LocalReserve), + Box::new(VersionedXcm::from(custom_xcm)), + Unlimited, + )); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred{ .. }) => {},] + ); + }); + + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] + ); + }); + + // Send token back from Ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + let asset_id_after_reanchor: Location = + Location::new(1, [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH))]); + let token_id = TokenIdOf::convert_location(&asset_id_after_reanchor).unwrap(); + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, + amount: initial_fund / 10, + fee: XCM_FEE, + }, + }); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued{..}) => {},] + ); + + let events = AssetHubWestend::events(); + + // Check that the native token burnt from reserved account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { owner, .. }) + if *owner == ethereum_sovereign.clone(), + )), + "token burnt from Ethereum sovereign account." + ); + + // Check that the token was minted to beneficiary + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { owner, .. }) + if *owner == AssetHubWestendReceiver::get() + )), + "Token minted to beneficiary." + ); + }); +} +>>>>>>> 13cb4a31 (Update transfer token gas and fee (#7947)) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 9d2444f76df58..de87fd2b19a30 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -661,7 +661,7 @@ pub mod bridging { /// (initially was calculated by test `OutboundQueue::calculate_fees` - ETH/ROC 1/400 and fee_per_gas 20 GWEI = 2200698000000 + *25%) /// Needs to be more than fee calculated from DefaultFeeConfig FeeConfigRecord in snowbridge:parachain/pallets/outbound-queue/src/lib.rs /// Polkadot uses 10 decimals, Kusama and Rococo 12 decimals. - pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; + pub const DefaultBridgeHubEthereumBaseFee: Balance = 3_833_568_200_000; pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); pub SiblingBridgeHubWithEthereumInboundQueueInstance: Location = Location::new( 1, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 388fecde57d1b..a619e986c5852 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -679,7 +679,7 @@ pub mod bridging { /// (initially was calculated by test `OutboundQueue::calculate_fees` - ETH/WND 1/400 and fee_per_gas 20 GWEI = 2200698000000 + *25%) /// Needs to be more than fee calculated from DefaultFeeConfig FeeConfigRecord in snowbridge:parachain/pallets/outbound-queue/src/lib.rs /// Polkadot uses 10 decimals, Kusama,Rococo,Westend 12 decimals. - pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; + pub const DefaultBridgeHubEthereumBaseFee: Balance = 3_833_568_200_000; pub const DefaultBridgeHubEthereumBaseFeeV2: Balance = 100_000_000_000; pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); pub storage BridgeHubEthereumBaseFeeV2: Balance = DefaultBridgeHubEthereumBaseFeeV2::get(); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 31dd66706aede..5aca100e1646f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -156,6 +156,7 @@ pub type Migrations = ( ConstU32, ConstU32, >, + snowbridge_pallet_system::migration::FeePerGasMigrationV0ToV1, pallet_bridge_messages::migration::v1::MigrationToV1< Runtime, bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs index e49f949fdf35a..d00cce94245eb 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs @@ -36,7 +36,7 @@ use sp_runtime::{ }; parameter_types! { - pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; + pub const DefaultBridgeHubEthereumBaseFee: Balance = 3_833_568_200_000; } fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index e9b3a16d7be5c..4b65b3789a338 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -174,6 +174,7 @@ pub type Migrations = ( ConstU32, ConstU32, >, + snowbridge_pallet_system::migration::FeePerGasMigrationV0ToV1, bridge_to_ethereum_config::migrations::MigrationForXcmV5, pallet_session::migrations::v1::MigrateV0ToV1< Runtime, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs index a51ac33ce5a8d..6e0e59924cb08 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs @@ -36,7 +36,7 @@ use sp_runtime::{ }; parameter_types! { - pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; + pub const DefaultBridgeHubEthereumBaseFee: Balance = 3_833_568_200_000; } fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys { diff --git a/prdoc/pr_7947.prdoc b/prdoc/pr_7947.prdoc new file mode 100644 index 0000000000000..e50fb89db3cd0 --- /dev/null +++ b/prdoc/pr_7947.prdoc @@ -0,0 +1,24 @@ +title: Snowbridge - Update TransferToken command gas limit. + +doc: + - audience: Runtime Dev + description: | + Transfering certain ERC20 tokens require more gas than 100_000 gas. An example is LDO token which requires 140_000 gas. + This change updates the gas limit to 200_000 and also updates the default fees for testnet runtimes. + NOTE: make sure to update the relevant runtime fees to account for this change. + +crates: + - name: asset-hub-westend-runtime + bump: patch + - name: asset-hub-rococo-runtime + bump: patch + - name: bridge-hub-westend-runtime + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: snowbridge-outbound-queue-primitives + bump: patch + - name: snowbridge-pallet-system + bump: minor + - name: snowbridge-pallet-outbound-queue-v2 + bump: minor From 75bd46cf207fbb359a70960a3d62d53a66c44f1e Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Fri, 18 Apr 2025 12:18:29 +0200 Subject: [PATCH 2/3] Merge fixes for backport 7947 to stable2503 (#8269) Merge fixes for backport branch https://github.com/paritytech/polkadot-sdk/pull/8259 Had to cherry-pick the following commits: https://github.com/paritytech/polkadot-sdk/pull/8020 https://github.com/paritytech/polkadot-sdk/pull/8038 --------- Co-authored-by: Clara van Staden Co-authored-by: Ron Co-authored-by: Adrian Catangiu --- .../pallets/ethereum-client/Cargo.toml | 28 +++++++------ .../ethereum-client/fixtures/Cargo.toml | 10 ++--- .../pallets/outbound-queue-v2/Cargo.toml | 42 ++++++++++--------- .../src/tests/snowbridge.rs | 28 +++++++++---- .../runtimes/testing/penpal/src/lib.rs | 3 +- .../runtimes/testing/penpal/src/xcm_config.rs | 37 ++++++++++------ .../src/chain_spec/penpal.rs | 42 ++++++++++++++++++- prdoc/pr_8259.prdoc | 28 +++++++++++++ 8 files changed, 159 insertions(+), 59 deletions(-) create mode 100644 prdoc/pr_8259.prdoc diff --git a/bridges/snowbridge/pallets/ethereum-client/Cargo.toml b/bridges/snowbridge/pallets/ethereum-client/Cargo.toml index e1a46a06d8f65..f4c1fc655d4a5 100644 --- a/bridges/snowbridge/pallets/ethereum-client/Cargo.toml +++ b/bridges/snowbridge/pallets/ethereum-client/Cargo.toml @@ -19,29 +19,31 @@ exclude-from-umbrella = true [dependencies] codec = { features = ["derive"], workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support.workspace = true -frame-system.workspace = true hex-literal = { optional = true, workspace = true, default-features = true } log = { workspace = true } -pallet-timestamp = { optional = true, workspace = true } scale-info = { features = ["derive"], workspace = true } serde = { optional = true, workspace = true, default-features = true } serde_json = { optional = true, workspace = true, default-features = true } -snowbridge-beacon-primitives.workspace = true -snowbridge-core.workspace = true -snowbridge-ethereum.workspace = true -snowbridge-pallet-ethereum-client-fixtures = { optional = true, workspace = true } -snowbridge-verification-primitives.workspace = true -sp-core.workspace = true + +frame-benchmarking = { optional = true, workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-core = { workspace = true } sp-io = { optional = true, workspace = true } -sp-runtime.workspace = true -sp-std.workspace = true +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +pallet-timestamp = { optional = true, workspace = true } +snowbridge-beacon-primitives = { workspace = true } +snowbridge-core = { workspace = true } +snowbridge-ethereum = { workspace = true } +snowbridge-pallet-ethereum-client-fixtures = { optional = true, workspace = true } +snowbridge-verification-primitives = { workspace = true } static_assertions = { workspace = true } [dev-dependencies] hex-literal = { workspace = true, default-features = true } -pallet-timestamp = { default-features = true, workspace = true } +pallet-timestamp = { workspace = true, default-features = true } serde = { workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } snowbridge-pallet-ethereum-client-fixtures = { default-features = true, workspace = true } diff --git a/bridges/snowbridge/pallets/ethereum-client/fixtures/Cargo.toml b/bridges/snowbridge/pallets/ethereum-client/fixtures/Cargo.toml index ca28b24d687f6..bc0b429e1b521 100644 --- a/bridges/snowbridge/pallets/ethereum-client/fixtures/Cargo.toml +++ b/bridges/snowbridge/pallets/ethereum-client/fixtures/Cargo.toml @@ -19,11 +19,11 @@ exclude-from-umbrella = true [dependencies] hex-literal = { workspace = true, default-features = true } -snowbridge-beacon-primitives.workspace = true -snowbridge-core.workspace = true -snowbridge-verification-primitives.workspace = true -sp-core.workspace = true -sp-std.workspace = true +snowbridge-beacon-primitives = { workspace = true } +snowbridge-core = { workspace = true } +snowbridge-verification-primitives = { workspace = true } +sp-core = { workspace = true } +sp-std = { workspace = true } [features] default = ["std"] diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml b/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml index f6b5ab86d3555..b39dd07b8d376 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml +++ b/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml @@ -19,32 +19,36 @@ exclude-from-umbrella = true [dependencies] alloy-core = { workspace = true, features = ["sol-types"] } -bp-relayers.workspace = true -bridge-hub-common.workspace = true codec = { features = ["derive"], workspace = true } ethabi = { workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support.workspace = true -frame-system.workspace = true hex-literal = { workspace = true, default-features = true } scale-info = { features = ["derive"], workspace = true } serde = { features = ["alloc", "derive"], workspace = true } -snowbridge-beacon-primitives.workspace = true -snowbridge-core.workspace = true -snowbridge-merkle-tree.workspace = true -snowbridge-outbound-queue-primitives.workspace = true -snowbridge-verification-primitives.workspace = true -sp-arithmetic.workspace = true -sp-core.workspace = true -sp-io.workspace = true -sp-runtime.workspace = true -sp-std.workspace = true -xcm-builder.workspace = true -xcm-executor.workspace = true -xcm.workspace = true + +frame-benchmarking = { optional = true, workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-arithmetic = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +bp-relayers = { workspace = true } +bridge-hub-common = { workspace = true } + +snowbridge-beacon-primitives = { workspace = true } +snowbridge-core = { workspace = true } +snowbridge-merkle-tree = { workspace = true } +snowbridge-outbound-queue-primitives = { workspace = true } +snowbridge-verification-primitives = { workspace = true } + +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } [dev-dependencies] -pallet-message-queue = { workspace = true, default-features = false } +pallet-message-queue = { workspace = true } [features] default = ["std"] diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index 1b040c1140871..d2064b4f3c16b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -13,8 +13,19 @@ // See the License for the specific language governing permissions and // limitations under the License. use crate::{ - imports::*, - tests::{penpal_emulated_chain::penpal_runtime, snowbridge_common::snowbridge_sovereign}, + imports::{ + penpal_emulated_chain::penpal_runtime::xcm_config::{ + CheckingAccount, TELEPORTABLE_ASSET_ID, + }, + *, + }, + tests::{ + penpal_emulated_chain::penpal_runtime, + snowbridge_common::{ + bridged_roc_at_ah_westend, ethereum, register_roc_on_bh, snowbridge_sovereign, + }, + snowbridge_v2_outbound_from_rococo::create_foreign_on_ah_westend, + }, }; use asset_hub_westend_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; use bridge_hub_westend_runtime::{ @@ -22,11 +33,11 @@ use bridge_hub_westend_runtime::{ }; use codec::{Decode, Encode}; use emulated_integration_tests_common::{PENPAL_B_ID, RESERVABLE_ASSET_ID}; -use frame_support::pallet_prelude::TypeInfo; +use frame_support::{pallet_prelude::TypeInfo, traits::fungibles::Mutate}; use hex_literal::hex; use rococo_westend_system_emulated_network::{ asset_hub_westend_emulated_chain::genesis::AssetHubWestendAssetOwner, - penpal_emulated_chain::PARA_ID_B, + penpal_emulated_chain::PARA_ID_B, westend_emulated_chain::westend_runtime::Dmp, }; use snowbridge_core::{AssetMetadata, TokenIdOf}; use snowbridge_inbound_queue_primitives::{ @@ -212,6 +223,10 @@ fn send_weth_from_ethereum_to_penpal() { ); PenpalB::execute_with(|| { + let key = PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(); + let value = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]).encode(); + assert_eq!(key, hex!("770800eb78be69c327d8334d09276072")); + assert_eq!(value, hex!("020109079edaa802")); assert_ok!(::System::set_storage( ::RuntimeOrigin::root(), vec![( @@ -1182,7 +1197,7 @@ fn transfer_ah_token() { [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], )); - assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + assert_ok!(::PolkadotXcm::transfer_assets( RuntimeOrigin::signed(AssetHubWestendSender::get()), Box::new(VersionedLocation::from(ethereum_destination)), Box::new(beneficiary), @@ -1270,8 +1285,6 @@ fn transfer_ah_token() { ); }); } -<<<<<<< HEAD -======= #[test] fn transfer_penpal_native_asset() { @@ -2006,4 +2019,3 @@ fn transfer_roc_from_ah_with_transfer_and_then() { ); }); } ->>>>>>> 13cb4a31 (Update transfer token gas and fee (#7947)) diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 78b59562772ee..50ee9cee032dd 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -1088,7 +1088,8 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - match asset.try_as::() { + let latest_asset_id: Result = asset.clone().try_into(); + match latest_asset_id { Ok(asset_id) if asset_id.0 == xcm_config::RelayLocation::get() => { // for native token Ok(WeightToFee::weight_to_fee(&weight)) diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index 2f41c118d7a89..02e33a8894dd8 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -65,16 +65,16 @@ use testnet_parachains_constants::westend::currency::deposit; use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AliasChildLocation, AliasOriginRootUsingFilter, - AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, - DescribeAllTerminal, DescribeFamily, DescribeTerminus, EnsureXcmOrigin, - ExternalConsensusLocationsConverterFor, FixedWeightBounds, FrameTransactionalProcessor, - FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NativeAsset, - NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SingleAssetExchangeAdapter, SovereignSignedViaLocation, StartsWith, - TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, + AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, + AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, + AsPrefixedGeneralIndex, ConvertedConcreteId, DescribeAllTerminal, DescribeFamily, + DescribeTerminus, EnsureXcmOrigin, ExternalConsensusLocationsConverterFor, FixedWeightBounds, + FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete, + LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, + SovereignSignedViaLocation, StartsWith, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::{traits::JustTry, XcmExecutor}; @@ -248,6 +248,8 @@ pub type Barrier = TrailingSetTopicAsId<( // If the message is one that immediately attempts to pay for execution, then // allow it. AllowTopLevelPaidExecutionFrom, + // Parent and its pluralities (i.e. governance bodies) get free execution. + AllowExplicitUnpaidExecutionFrom<(ParentOrParentsExecutivePlurality,)>, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, // HRMP notifications from the relay chain are OK. @@ -316,6 +318,10 @@ parameter_types! { /// /// By default, it is configured as a `SystemAssetHubLocation` and can be modified using `System::set_storage`. pub storage CustomizableAssetFromSystemAssetHub: Location = SystemAssetHubLocation::get(); + + pub const NativeAssetId: AssetId = AssetId(Location::here()); + pub const NativeAssetFilter: AssetFilter = Wild(AllOf { fun: WildFungible, id: NativeAssetId::get() }); + pub AssetHubTrustedTeleporter: (AssetFilter, Location) = (NativeAssetFilter::get(), SystemAssetHubLocation::get()); } /// Accepts asset with ID `AssetLocation` and is coming from `Origin` chain. @@ -336,8 +342,13 @@ pub type TrustedReserves = ( AssetsFrom, AssetPrefixFrom, ); -pub type TrustedTeleporters = - (AssetFromChain,); + +pub type TrustedTeleporters = ( + AssetFromChain, + // This is used in the `IsTeleporter` configuration, meaning it accepts + // native tokens teleported from Asset Hub. + xcm_builder::Case, +); /// Defines origin aliasing rules for this chain. /// @@ -389,6 +400,8 @@ impl xcm_executor::Config for XcmConfig { type Weigher = FixedWeightBounds; type Trader = ( UsingComponents>, + // Allow native asset to pay the execution fee + UsingComponents>, cumulus_primitives_utility::SwapFirstAssetTrader< RelayLocation, crate::AssetConversion, diff --git a/cumulus/polkadot-parachain/src/chain_spec/penpal.rs b/cumulus/polkadot-parachain/src/chain_spec/penpal.rs index 94e90efb877cd..401a872067eae 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/penpal.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/penpal.rs @@ -46,6 +46,7 @@ pub fn get_penpal_chain_spec(id: ParaId, relay_chain: &str) -> GenericChainSpec ], Sr25519Keyring::well_known().map(|k| k.to_account_id()).collect(), id, + Sr25519Keyring::Alice.to_account_id(), )) .build() } @@ -54,6 +55,7 @@ fn penpal_testnet_genesis( invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, id: ParaId, + sudo_account: AccountId, ) -> serde_json::Value { serde_json::json!({ "balances": { @@ -86,7 +88,45 @@ fn penpal_testnet_genesis( "safeXcmVersion": Some(SAFE_XCM_VERSION), }, "sudo": { - "key": Some(Sr25519Keyring::Alice.to_account_id()), + "key": Some(sudo_account.clone()), + }, + "assets": { + "assets": vec![( + penpal_runtime::xcm_config::TELEPORTABLE_ASSET_ID, + sudo_account.clone(), // owner + false, // is_sufficient + penpal_runtime::EXISTENTIAL_DEPOSIT, + )], + "metadata": vec![( + penpal_runtime::xcm_config::TELEPORTABLE_ASSET_ID, + "pal-2".as_bytes().to_vec(), + "pal-2".as_bytes().to_vec(), + 12, + )], + "accounts": vec![( + penpal_runtime::xcm_config::TELEPORTABLE_ASSET_ID, + sudo_account.clone(), + penpal_runtime::EXISTENTIAL_DEPOSIT * 4096, + )] + }, + "foreignAssets": { + "assets": vec![( + penpal_runtime::xcm_config::RelayLocation::get(), + sudo_account.clone(), + true, + penpal_runtime::EXISTENTIAL_DEPOSIT + )], + "metadata": vec![( + penpal_runtime::xcm_config::RelayLocation::get(), + "relay".as_bytes().to_vec(), + "relay".as_bytes().to_vec(), + 12 + )], + "accounts": vec![( + penpal_runtime::xcm_config::RelayLocation::get(), + sudo_account.clone(), + penpal_runtime::EXISTENTIAL_DEPOSIT * 4096, + )] }, }) } diff --git a/prdoc/pr_8259.prdoc b/prdoc/pr_8259.prdoc new file mode 100644 index 0000000000000..faa6575e94c48 --- /dev/null +++ b/prdoc/pr_8259.prdoc @@ -0,0 +1,28 @@ +title: '[stable2503] Backport - Snowbridge - Update TransferToken command gas limit.' + +doc: + - audience: Runtime Dev + description: | + Transfering certain ERC20 tokens require more gas than 100_000 gas. An example is LDO token which requires 140_000 gas. + This change updates the gas limit to 200_000 and also updates the default fees for testnet runtimes. + NOTE: make sure to update the relevant runtime fees to account for this change. + +crates: + - name: asset-hub-westend-runtime + bump: patch + - name: asset-hub-rococo-runtime + bump: patch + - name: bridge-hub-westend-runtime + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: snowbridge-outbound-queue-primitives + bump: patch + - name: snowbridge-pallet-system + bump: patch + - name: snowbridge-pallet-outbound-queue-v2 + bump: patch + - name: snowbridge-pallet-ethereum-client-fixtures + bump: patch + - name: penpal-runtime + bump: patch From a8435cea9205b5421c2317c661941d8de53c14fc Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 18 Apr 2025 13:30:13 +0300 Subject: [PATCH 3/3] remove original prdoc --- prdoc/pr_7947.prdoc | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 prdoc/pr_7947.prdoc diff --git a/prdoc/pr_7947.prdoc b/prdoc/pr_7947.prdoc deleted file mode 100644 index e50fb89db3cd0..0000000000000 --- a/prdoc/pr_7947.prdoc +++ /dev/null @@ -1,24 +0,0 @@ -title: Snowbridge - Update TransferToken command gas limit. - -doc: - - audience: Runtime Dev - description: | - Transfering certain ERC20 tokens require more gas than 100_000 gas. An example is LDO token which requires 140_000 gas. - This change updates the gas limit to 200_000 and also updates the default fees for testnet runtimes. - NOTE: make sure to update the relevant runtime fees to account for this change. - -crates: - - name: asset-hub-westend-runtime - bump: patch - - name: asset-hub-rococo-runtime - bump: patch - - name: bridge-hub-westend-runtime - bump: patch - - name: bridge-hub-rococo-runtime - bump: patch - - name: snowbridge-outbound-queue-primitives - bump: patch - - name: snowbridge-pallet-system - bump: minor - - name: snowbridge-pallet-outbound-queue-v2 - bump: minor