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 222e96d59d17..83fa31abd7f5 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 @@ -53,7 +53,7 @@ use xcm_builder::{ SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeesToAccount, + XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -75,7 +75,7 @@ parameter_types! { PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); pub const GovernanceLocation: MultiLocation = MultiLocation::parent(); - pub TreasuryAccount: Option = Some(TREASURY_PALLET_ID.into_account_truncating()); + pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); pub RelayTreasuryLocation: MultiLocation = (Parent, PalletInstance(rococo_runtime_constants::TREASURY_PALLET_ID)).into(); } @@ -619,7 +619,10 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); - type FeeManager = XcmFeesToAccount; + type FeeManager = XcmFeeManagerFromComponents< + WaivedLocations, + XcmFeeToAccount, + >; type MessageExporter = (); type UniversalAliases = (bridging::to_wococo::UniversalAliases, bridging::to_rococo::UniversalAliases); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs index b93315cc39d8..18f5f4fc41ec 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -683,7 +683,7 @@ mod asset_hub_rococo_tests { bridging_to_asset_hub_wococo, WeightLimit::Unlimited, Some(xcm_config::bridging::XcmBridgeHubRouterFeeAssetId::get()), - Some(xcm_config::TreasuryAccount::get().unwrap()), + Some(xcm_config::TreasuryAccount::get()), ) } @@ -871,7 +871,7 @@ mod asset_hub_wococo_tests { with_wococo_flavor_bridging_to_asset_hub_rococo, WeightLimit::Unlimited, Some(xcm_config::bridging::XcmBridgeHubRouterFeeAssetId::get()), - Some(xcm_config::TreasuryAccount::get().unwrap()), + Some(xcm_config::TreasuryAccount::get()), ) } 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 fe0fd613d220..6b5ce904da9a 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 @@ -52,7 +52,7 @@ use xcm_builder::{ SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeesToAccount, + XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -73,7 +73,7 @@ parameter_types! { pub PoolAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - pub TreasuryAccount: Option = Some(TREASURY_PALLET_ID.into_account_truncating()); + pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); pub RelayTreasuryLocation: MultiLocation = (Parent, PalletInstance(westend_runtime_constants::TREASURY_PALLET_ID)).into(); } @@ -562,7 +562,10 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); - type FeeManager = XcmFeesToAccount; + type FeeManager = XcmFeeManagerFromComponents< + WaivedLocations, + XcmFeeToAccount, + >; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = WithOriginFilter; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 2456a7ee63a8..dc6f61d01465 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -24,9 +24,18 @@ use crate::{ BridgeGrandpaRococoInstance, BridgeGrandpaWococoInstance, DeliveryRewardInBalance, RequiredStakeForStakeAndSlash, }, - bridge_hub_rococo_config::ToBridgeHubWococoHaulBlobExporter, - bridge_hub_wococo_config::ToBridgeHubRococoHaulBlobExporter, + bridge_hub_rococo_config::{ + AssetHubRococoParaId, BridgeHubWococoChainId, BridgeHubWococoMessagesLane, + ToBridgeHubWococoHaulBlobExporter, WococoGlobalConsensusNetwork, + }, + bridge_hub_wococo_config::{ + AssetHubWococoParaId, BridgeHubRococoChainId, BridgeHubRococoMessagesLane, + RococoGlobalConsensusNetwork, ToBridgeHubRococoHaulBlobExporter, + }, }; +use bp_messages::LaneId; +use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams}; +use bp_runtime::ChainId; use frame_support::{ match_types, parameter_types, traits::{ConstU32, Contains, Equals, Everything, Nothing}, @@ -43,18 +52,20 @@ use polkadot_runtime_common::xcm_sender::ExponentialPrice; use rococo_runtime_constants::system_parachain; use sp_core::Get; use sp_runtime::traits::AccountIdConversion; +use sp_std::marker::PhantomData; use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, - AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, - DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, IsConcrete, ParentAsSuperuser, - ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeesToAccount, + deposit_or_burn_fee, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, + AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, + CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, HandleFee, + IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + XcmFeeToAccount, }; use xcm_executor::{ - traits::{ExportXcm, WithOriginFilter}, + traits::{ExportXcm, FeeReason, TransactAsset, WithOriginFilter}, XcmExecutor, }; @@ -66,7 +77,7 @@ parameter_types! { X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; - pub TreasuryAccount: Option = Some(TREASURY_PALLET_ID.into_account_truncating()); + pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); pub RelayTreasuryLocation: MultiLocation = (Parent, PalletInstance(rococo_runtime_constants::TREASURY_PALLET_ID)).into(); } @@ -290,7 +301,26 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = PolkadotXcm; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = XcmFeesToAccount; + type FeeManager = XcmFeeManagerFromComponents< + WaivedLocations, + ( + XcmExportFeeToRelayerRewardAccounts< + Self::AssetTransactor, + WococoGlobalConsensusNetwork, + AssetHubWococoParaId, + BridgeHubWococoChainId, + BridgeHubWococoMessagesLane, + >, + XcmExportFeeToRelayerRewardAccounts< + Self::AssetTransactor, + RococoGlobalConsensusNetwork, + AssetHubRococoParaId, + BridgeHubRococoChainId, + BridgeHubRococoMessagesLane, + >, + XcmFeeToAccount, + ), + >; type MessageExporter = BridgeHubRococoOrBridgeHubWococoSwitchExporter; type UniversalAliases = Nothing; type CallDispatcher = WithOriginFilter; @@ -401,3 +431,96 @@ impl ExportXcm for BridgeHubRococoOrBridgeHubWococoSwitchExporter { } } } + +/// A `HandleFee` implementation that simply deposits the fees for `ExportMessage` XCM instructions +/// into the accounts that are used for paying the relayer rewards. +/// Burns the fees in case of a failure. +pub struct XcmExportFeeToRelayerRewardAccounts< + AssetTransactor, + DestNetwork, + DestParaId, + DestBridgeHubId, + BridgeLaneId, +>(PhantomData<(AssetTransactor, DestNetwork, DestParaId, DestBridgeHubId, BridgeLaneId)>); + +impl< + AssetTransactor: TransactAsset, + DestNetwork: Get, + DestParaId: Get, + DestBridgeHubId: Get, + BridgeLaneId: Get, + > HandleFee + for XcmExportFeeToRelayerRewardAccounts< + AssetTransactor, + DestNetwork, + DestParaId, + DestBridgeHubId, + BridgeLaneId, + > +{ + fn handle_fee( + fee: MultiAssets, + maybe_context: Option<&XcmContext>, + reason: FeeReason, + ) -> MultiAssets { + if matches!(reason, FeeReason::Export { network: bridged_network, destination } + if bridged_network == DestNetwork::get() && + destination == X1(Parachain(DestParaId::get().into()))) + { + // We have 2 relayer rewards accounts: + // - the SA of the source parachain on this BH: this pays the relayers for delivering + // Source para -> Target Para message delivery confirmations + // - the SA of the destination parachain on this BH: this pays the relayers for + // delivering Target para -> Source Para messages + // We split the `ExportMessage` fee between these 2 accounts. + let source_para_account = PayRewardFromAccount::< + pallet_balances::Pallet, + AccountId, + >::rewards_account(RewardsAccountParams::new( + BridgeLaneId::get(), + DestBridgeHubId::get(), + RewardsAccountOwner::ThisChain, + )); + + let dest_para_account = PayRewardFromAccount::< + pallet_balances::Pallet, + AccountId, + >::rewards_account(RewardsAccountParams::new( + BridgeLaneId::get(), + DestBridgeHubId::get(), + RewardsAccountOwner::BridgedChain, + )); + + for asset in fee.into_inner() { + match asset.fun { + Fungible(total_fee) => { + let source_fee = total_fee / 2; + deposit_or_burn_fee::( + MultiAsset { id: asset.id, fun: Fungible(source_fee) }.into(), + maybe_context, + source_para_account.clone(), + ); + + let dest_fee = total_fee - source_fee; + deposit_or_burn_fee::( + MultiAsset { id: asset.id, fun: Fungible(dest_fee) }.into(), + maybe_context, + dest_para_account.clone(), + ); + }, + NonFungible(_) => { + deposit_or_burn_fee::( + asset.into(), + maybe_context, + source_para_account.clone(), + ); + }, + } + } + + return MultiAssets::new() + } + + fee + } +} diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index 6ff60b958fed..ebb3de740b9e 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -41,7 +41,7 @@ use xcm_builder::{ NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WithComputedOrigin, WithUniqueTopic, XcmFeesToAccount, + WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -51,7 +51,7 @@ parameter_types! { pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); pub const ExecutiveBody: BodyId = BodyId::Executive; - pub TreasuryAccount: Option = Some(TREASURY_PALLET_ID.into_account_truncating()); + pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); pub RelayTreasuryLocation: MultiLocation = (Parent, PalletInstance(rococo_runtime_constants::TREASURY_PALLET_ID)).into(); } @@ -199,7 +199,10 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = ConstU32<8>; type AssetLocker = (); type AssetExchanger = (); - type FeeManager = XcmFeesToAccount; + type FeeManager = XcmFeeManagerFromComponents< + WaivedLocations, + XcmFeeToAccount, + >; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs index fb1653c549e1..0814b77414f2 100644 --- a/polkadot/runtime/rococo/src/xcm_config.rs +++ b/polkadot/runtime/rococo/src/xcm_config.rs @@ -43,7 +43,8 @@ use xcm_builder::{ DescribeFamily, FixedWeightBounds, HashedDescription, IsChildSystemParachain, IsConcrete, MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeesToAccount, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -53,7 +54,7 @@ parameter_types! { pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into(); pub CheckAccount: AccountId = XcmPallet::check_account(); pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local); - pub TreasuryAccount: Option = Some(Treasury::account_id()); + pub TreasuryAccount: AccountId = Treasury::account_id(); } pub type LocationConverter = ( @@ -191,7 +192,10 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = XcmFeesToAccount; + type FeeManager = XcmFeeManagerFromComponents< + SystemParachains, + XcmFeeToAccount, + >; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs index dd6a29885ad6..64e07317fc74 100644 --- a/polkadot/runtime/westend/src/xcm_config.rs +++ b/polkadot/runtime/westend/src/xcm_config.rs @@ -44,7 +44,7 @@ use xcm_builder::{ DescribeFamily, HashedDescription, IsConcrete, MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeesToAccount, + XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -54,7 +54,7 @@ parameter_types! { pub const UniversalLocation: InteriorMultiLocation = X1(GlobalConsensus(ThisNetwork::get())); pub CheckAccount: AccountId = XcmPallet::check_account(); pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local); - pub TreasuryAccount: Option = Some(Treasury::account_id()); + pub TreasuryAccount: AccountId = Treasury::account_id(); /// The asset ID for the asset that we use to pay for message delivery fees. pub FeeAssetId: AssetId = Concrete(TokenLocation::get()); /// The base fee for the message delivery fees. @@ -185,7 +185,10 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = XcmFeesToAccount; + type FeeManager = XcmFeeManagerFromComponents< + SystemParachains, + XcmFeeToAccount, + >; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs index c6b76e0ffade..4a997666027f 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs @@ -561,7 +561,7 @@ benchmarks! { let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery( &origin, &destination.into(), - FeeReason::Export(network), + FeeReason::Export { network, destination }, ); let sender_account = T::AccountIdConverter::convert_location(&origin).unwrap(); let sender_account_balance_before = T::TransactAsset::balance(&sender_account); diff --git a/polkadot/xcm/pallet-xcm/src/mock.rs b/polkadot/xcm/pallet-xcm/src/mock.rs index afa956c3cdae..3b41ad90ec99 100644 --- a/polkadot/xcm/pallet-xcm/src/mock.rs +++ b/polkadot/xcm/pallet-xcm/src/mock.rs @@ -34,7 +34,7 @@ use xcm_builder::{ AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, XcmFeesToAccount, + SovereignSignedViaLocation, TakeWeightCredit, XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -343,11 +343,9 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = XcmFeesToAccount< - Self, + type FeeManager = XcmFeeManagerFromComponents< EverythingBut, - AccountId, - XcmFeesTargetAccount, + XcmFeeToAccount, >; type MessageExporter = (); type UniversalAliases = Nothing; diff --git a/polkadot/xcm/xcm-builder/src/fee_handling.rs b/polkadot/xcm/xcm-builder/src/fee_handling.rs index 1386747c9778..c158d5d862d7 100644 --- a/polkadot/xcm/xcm-builder/src/fee_handling.rs +++ b/polkadot/xcm/xcm-builder/src/fee_handling.rs @@ -19,40 +19,103 @@ use frame_support::traits::{Contains, Get}; use xcm::prelude::*; use xcm_executor::traits::{FeeManager, FeeReason, TransactAsset}; -/// A `FeeManager` implementation that simply deposits the fees handled into a specific on-chain -/// `ReceiverAccount`. -/// -/// It reuses the `AssetTransactor` configured on the XCM executor to deposit fee assets, and also -/// permits specifying `WaivedLocations` for locations that are privileged to not pay for fees. If -/// the `AssetTransactor` returns an error while calling `deposit_asset`, then a warning will be -/// logged. -pub struct XcmFeesToAccount( - PhantomData<(XcmConfig, WaivedLocations, AccountId, ReceiverAccount)>, +/// Handles the fees that are taken by certain XCM instructions. +pub trait HandleFee { + /// Do something with the fee which has been paid. Doing nothing here silently burns the + /// fees. + /// + /// Returns any part of the fee that wasn't consumed. + fn handle_fee(fee: MultiAssets, context: Option<&XcmContext>, reason: FeeReason) + -> MultiAssets; +} + +// Default `HandleFee` implementation that just burns the fee. +impl HandleFee for () { + fn handle_fee(_: MultiAssets, _: Option<&XcmContext>, _: FeeReason) -> MultiAssets { + MultiAssets::new() + } +} + +#[impl_trait_for_tuples::impl_for_tuples(1, 30)] +impl HandleFee for Tuple { + fn handle_fee( + fee: MultiAssets, + context: Option<&XcmContext>, + reason: FeeReason, + ) -> MultiAssets { + let mut unconsumed_fee = fee; + for_tuples!( #( + unconsumed_fee = Tuple::handle_fee(unconsumed_fee, context, reason); + if unconsumed_fee.is_none() { + return unconsumed_fee; + } + )* ); + + unconsumed_fee + } +} + +/// A `FeeManager` implementation that permits the specified `WaivedLocations` to not pay for fees +/// and that uses the provided `HandleFee` implementation otherwise. +pub struct XcmFeeManagerFromComponents( + PhantomData<(WaivedLocations, HandleFee)>, ); -impl< - XcmConfig: xcm_executor::Config, - WaivedLocations: Contains, - AccountId: Clone + Into<[u8; 32]>, - ReceiverAccount: Get>, - > FeeManager for XcmFeesToAccount +impl, FeeHandler: HandleFee> FeeManager + for XcmFeeManagerFromComponents { fn is_waived(origin: Option<&MultiLocation>, _: FeeReason) -> bool { let Some(loc) = origin else { return false }; WaivedLocations::contains(loc) } - fn handle_fee(fees: MultiAssets, context: Option<&XcmContext>) { - if let Some(receiver) = ReceiverAccount::get() { - let dest = AccountId32 { network: None, id: receiver.into() }.into(); - for asset in fees.into_inner() { - if let Err(e) = XcmConfig::AssetTransactor::deposit_asset(&asset, &dest, context) { - log::trace!( - target: "xcm::fees", - "`AssetTransactor::deposit_asset` returned error: {:?}, burning fees: {:?}", - e, asset, - ); - } - } + fn handle_fee(fee: MultiAssets, context: Option<&XcmContext>, reason: FeeReason) { + FeeHandler::handle_fee(fee, context, reason); + } +} + +/// Try to deposit the given fee in the specified account. +/// Burns the fee in case of a failure. +pub fn deposit_or_burn_fee>( + fee: MultiAssets, + context: Option<&XcmContext>, + receiver: AccountId, +) { + let dest = AccountId32 { network: None, id: receiver.into() }.into(); + for asset in fee.into_inner() { + if let Err(e) = AssetTransactor::deposit_asset(&asset, &dest, context) { + log::trace!( + target: "xcm::fees", + "`AssetTransactor::deposit_asset` returned error: {:?}. Burning fee: {:?}. \ + They might be burned.", + e, asset, + ); } } } + +/// A `HandleFee` implementation that simply deposits the fees into a specific on-chain +/// `ReceiverAccount`. +/// +/// It reuses the `AssetTransactor` configured on the XCM executor to deposit fee assets. If +/// the `AssetTransactor` returns an error while calling `deposit_asset`, then a warning will be +/// logged and the fee burned. +pub struct XcmFeeToAccount( + PhantomData<(AssetTransactor, AccountId, ReceiverAccount)>, +); + +impl< + AssetTransactor: TransactAsset, + AccountId: Clone + Into<[u8; 32]>, + ReceiverAccount: Get, + > HandleFee for XcmFeeToAccount +{ + fn handle_fee( + fee: MultiAssets, + context: Option<&XcmContext>, + _reason: FeeReason, + ) -> MultiAssets { + deposit_or_burn_fee::(fee, context, ReceiverAccount::get()); + + MultiAssets::new() + } +} diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index 34371398cdc3..0a74b3f579ae 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -68,7 +68,9 @@ mod currency_adapter; pub use currency_adapter::CurrencyAdapter; mod fee_handling; -pub use fee_handling::XcmFeesToAccount; +pub use fee_handling::{ + deposit_or_burn_fee, HandleFee, XcmFeeManagerFromComponents, XcmFeeToAccount, +}; mod fungibles_adapter; pub use fungibles_adapter::{ diff --git a/polkadot/xcm/xcm-builder/src/tests/mock.rs b/polkadot/xcm/xcm-builder/src/tests/mock.rs index 7a7c8837fc1c..543b00e0118c 100644 --- a/polkadot/xcm/xcm-builder/src/tests/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/mock.rs @@ -526,7 +526,8 @@ impl FeeManager for TestFeeManager { fn is_waived(_: Option<&MultiLocation>, r: FeeReason) -> bool { IS_WAIVED.with(|l| l.borrow().contains(&r)) } - fn handle_fee(_: MultiAssets, _: Option<&XcmContext>) {} + + fn handle_fee(_: MultiAssets, _: Option<&XcmContext>, _: FeeReason) {} } #[derive(Clone, Eq, PartialEq, Debug)] diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index e11ec2630e43..e43d7a048992 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -248,7 +248,7 @@ impl ExecuteXcm for XcmExecutor XcmExecutor { destination, xcm, )?; - self.take_fee(fee, FeeReason::Export(network))?; + self.take_fee(fee, FeeReason::Export { network, destination })?; Config::MessageExporter::deliver(ticket)?; Ok(()) }, @@ -962,7 +962,7 @@ impl XcmExecutor { } else { self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?.into() }; - Config::FeeManager::handle_fee(paid, Some(&self.context)); + Config::FeeManager::handle_fee(paid, Some(&self.context), reason); Ok(()) } diff --git a/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs b/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs index 2b2f21927f2e..d7146457f3b9 100644 --- a/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs +++ b/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs @@ -18,12 +18,12 @@ use xcm::prelude::*; /// Handle stuff to do with taking fees in certain XCM instructions. pub trait FeeManager { - /// Determine if a fee which would normally payable should be waived. + /// Determine if a fee should be waived. fn is_waived(origin: Option<&MultiLocation>, r: FeeReason) -> bool; /// Do something with the fee which has been paid. Doing nothing here silently burns the /// fees. - fn handle_fee(fee: MultiAssets, context: Option<&XcmContext>); + fn handle_fee(fee: MultiAssets, context: Option<&XcmContext>, r: FeeReason); } /// Context under which a fee is paid. @@ -42,7 +42,7 @@ pub enum FeeReason { /// When the `QueryPallet` instruction is called. QueryPallet, /// When the `ExportMessage` instruction is called (and includes the network ID). - Export(NetworkId), + Export { network: NetworkId, destination: InteriorMultiLocation }, /// The `charge_fees` API. ChargeFees, /// When the `LockAsset` instruction is called. @@ -55,5 +55,6 @@ impl FeeManager for () { fn is_waived(_: Option<&MultiLocation>, _: FeeReason) -> bool { false } - fn handle_fee(_: MultiAssets, _: Option<&XcmContext>) {} + + fn handle_fee(_: MultiAssets, _: Option<&XcmContext>, _: FeeReason) {} }