diff --git a/cumulus/parachains/common/src/impls.rs b/cumulus/parachains/common/src/impls.rs index 42ea50c75a8d..c1797c0a7513 100644 --- a/cumulus/parachains/common/src/impls.rs +++ b/cumulus/parachains/common/src/impls.rs @@ -67,7 +67,7 @@ where AccountIdOf: From + Into, ::RuntimeEvent: From>, { - fn on_unbalanceds( + fn on_unbalanceds( mut fees_then_tips: impl Iterator< Item = fungible::Credit>, >, diff --git a/polkadot/runtime/common/src/impls.rs b/polkadot/runtime/common/src/impls.rs index 7fdff814a2d5..b6a93cf53685 100644 --- a/polkadot/runtime/common/src/impls.rs +++ b/polkadot/runtime/common/src/impls.rs @@ -51,7 +51,7 @@ where ::AccountId: From, ::AccountId: Into, { - fn on_unbalanceds( + fn on_unbalanceds( mut fees_then_tips: impl Iterator>>, ) { if let Some(fees) = fees_then_tips.next() { diff --git a/prdoc/pr_4564.prdoc b/prdoc/pr_4564.prdoc new file mode 100644 index 000000000000..896e49ee6b9f --- /dev/null +++ b/prdoc/pr_4564.prdoc @@ -0,0 +1,32 @@ +title: "Make `OnUnbalanced::on_unbalanceds` work with `fungibles` `imbalances`" + +doc: + - audience: Runtime Dev + description: | + The `on_unbalanceds` function of `OnUnbalanced` trait accepts the `fungibles` `imbalances` + imbalances. This is done by replacing the `Imbalance` trait bound on imbalance type with + the `TryMerge` trait bound. The `TryMerge` trait is implemented for all imbalance types. + + ### Migration for `OnUnbalanced` trait implementations: + In case if you have a custom implementation of `on_unbalanceds` trait function, remove + it's `` type argument. + + ### Migration for custom imbalance types: + If you have your own imbalance types implementations, implement the `TryMerge` trait for it + introduced with this update. + +crates: + - name: frame-support + bump: major + - name: pallet-balances + bump: minor + - name: pallet-asset-conversion-tx-payment + bump: patch + - name: pallet-transaction-payment + bump: patch + - name: kitchensink-runtime + bump: patch + - name: polkadot-runtime-common + bump: patch + - name: parachains-common + bump: minor diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 78c7bba64579..997213447262 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -189,7 +189,7 @@ type NegativeImbalance = >::NegativeImbalance; pub struct DealWithFees; impl OnUnbalanced for DealWithFees { - fn on_unbalanceds(mut fees_then_tips: impl Iterator) { + fn on_unbalanceds(mut fees_then_tips: impl Iterator) { if let Some(fees) = fees_then_tips.next() { // for fees, 80% to treasury, 20% to author let mut split = fees.ration(80, 20); diff --git a/substrate/frame/balances/src/impl_currency.rs b/substrate/frame/balances/src/impl_currency.rs index 049f0cc626c2..23feb46b72ca 100644 --- a/substrate/frame/balances/src/impl_currency.rs +++ b/substrate/frame/balances/src/impl_currency.rs @@ -41,7 +41,7 @@ use sp_runtime::traits::Bounded; mod imbalances { use super::*; use core::mem; - use frame_support::traits::SameOrOther; + use frame_support::traits::{tokens::imbalance::TryMerge, SameOrOther}; /// Opaque, move-only struct with private fields that serves as a token denoting that /// funds have been created without any equal and opposite accounting. @@ -133,6 +133,12 @@ mod imbalances { } } + impl, I: 'static> TryMerge for PositiveImbalance { + fn try_merge(self, other: Self) -> Result { + Ok(self.merge(other)) + } + } + impl, I: 'static> TryDrop for NegativeImbalance { fn try_drop(self) -> result::Result<(), Self> { self.drop_zero() @@ -197,6 +203,12 @@ mod imbalances { } } + impl, I: 'static> TryMerge for NegativeImbalance { + fn try_merge(self, other: Self) -> Result { + Ok(self.merge(other)) + } + } + impl, I: 'static> Drop for PositiveImbalance { /// Basic drop handler will just square up the total issuance. fn drop(&mut self) { diff --git a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs index 41907b2aa009..a9367d0f164f 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs @@ -24,7 +24,7 @@ use super::{super::Imbalance as ImbalanceT, Balanced, *}; use crate::traits::{ fungibles, misc::{SameOrOther, TryDrop}, - tokens::{AssetId, Balance}, + tokens::{imbalance::TryMerge, AssetId, Balance}, }; use core::marker::PhantomData; use frame_support_procedural::{EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; @@ -157,6 +157,14 @@ impl, OppositeOnDrop: HandleImbalance } } +impl, OppositeOnDrop: HandleImbalanceDrop> TryMerge + for Imbalance +{ + fn try_merge(self, other: Self) -> Result { + Ok(self.merge(other)) + } +} + /// Converts a `fungibles` `imbalance` instance to an instance of a `fungible` imbalance type. /// /// This function facilitates imbalance conversions within the implementations of diff --git a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs index c3b213cc8fc8..349d9d7c65e8 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -24,7 +24,10 @@ use super::*; use crate::traits::{ fungible, misc::{SameOrOther, TryDrop}, - tokens::{imbalance::Imbalance as ImbalanceT, AssetId, Balance}, + tokens::{ + imbalance::{Imbalance as ImbalanceT, TryMerge}, + AssetId, Balance, + }, }; use core::marker::PhantomData; use frame_support_procedural::{EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; @@ -176,6 +179,18 @@ impl< } } +impl< + A: AssetId, + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, + > TryMerge for Imbalance +{ + fn try_merge(self, other: Self) -> Result { + self.merge(other) + } +} + /// Converts a `fungible` `imbalance` instance to an instance of a `fungibles` imbalance type using /// a specified `asset`. /// diff --git a/substrate/frame/support/src/traits/tokens/imbalance.rs b/substrate/frame/support/src/traits/tokens/imbalance.rs index 8eb9b355a4cf..ee0d7a81c36e 100644 --- a/substrate/frame/support/src/traits/tokens/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/imbalance.rs @@ -58,7 +58,7 @@ pub use split_two_ways::SplitTwoWays; /// /// You can always retrieve the raw balance value using `peek`. #[must_use] -pub trait Imbalance: Sized + TryDrop + Default { +pub trait Imbalance: Sized + TryDrop + Default + TryMerge { /// The oppositely imbalanced type. They come in pairs. type Opposite: Imbalance; @@ -182,6 +182,13 @@ pub trait Imbalance: Sized + TryDrop + Default { fn peek(&self) -> Balance; } +/// Try to merge two imbalances. +pub trait TryMerge: Sized { + /// Consume `self` and an `other` to return a new instance that combines both. Errors with + /// Err(self, other) if the imbalances cannot be merged (e.g. imbalances of different assets). + fn try_merge(self, other: Self) -> Result; +} + #[cfg(feature = "std")] impl Imbalance for () { type Opposite = (); @@ -236,3 +243,10 @@ impl Imbalance for () { Default::default() } } + +#[cfg(feature = "std")] +impl TryMerge for () { + fn try_merge(self, _: Self) -> Result { + Ok(()) + } +} diff --git a/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs b/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs index 4bf9af3fbb18..461fd203668d 100644 --- a/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs +++ b/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs @@ -35,11 +35,26 @@ pub trait OnUnbalanced { /// Handler for some imbalances. The different imbalances might have different origins or /// meanings, dependent on the context. Will default to simply calling on_unbalanced for all /// of them. Infallible. - fn on_unbalanceds(amounts: impl Iterator) + fn on_unbalanceds(mut amounts: impl Iterator) where - Imbalance: crate::traits::Imbalance, + Imbalance: crate::traits::tokens::imbalance::TryMerge, { - Self::on_unbalanced(amounts.fold(Imbalance::zero(), |i, x| x.merge(i))) + let mut sum: Option = None; + while let Some(next) = amounts.next() { + sum = match sum { + Some(sum) => match sum.try_merge(next) { + Ok(sum) => Some(sum), + Err((sum, next)) => { + Self::on_unbalanced(next); + Some(sum) + }, + }, + None => Some(next), + } + } + if let Some(sum) = sum { + Self::on_unbalanced(sum) + } } /// Handler for some imbalance. Infallible. diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs index 3f8c7bc0ea34..245900760de9 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs @@ -131,7 +131,7 @@ pub struct DealWithFees; impl OnUnbalanced::AccountId, Balances>> for DealWithFees { - fn on_unbalanceds( + fn on_unbalanceds( mut fees_then_tips: impl Iterator< Item = fungible::Credit<::AccountId, Balances>, >, diff --git a/substrate/frame/transaction-payment/src/mock.rs b/substrate/frame/transaction-payment/src/mock.rs index fa61572e9831..8767024ee235 100644 --- a/substrate/frame/transaction-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/src/mock.rs @@ -105,7 +105,7 @@ pub struct DealWithFees; impl OnUnbalanced::AccountId, Balances>> for DealWithFees { - fn on_unbalanceds( + fn on_unbalanceds( mut fees_then_tips: impl Iterator< Item = fungible::Credit<::AccountId, Balances>, >,