From 1c95310a66a7ffc832062a5cae4fa4cafc94d1d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 5 Jan 2024 19:55:32 +0100 Subject: [PATCH] `fungible::Unbalanced::decrease_balance`: Handle `precision` properly (#2823) --- prdoc/pr_2823.prdoc | 11 ++++++++ .../balances/src/tests/fungible_tests.rs | 27 ++++++++++++++++++- .../src/traits/tokens/fungible/regular.rs | 11 ++++++-- 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 prdoc/pr_2823.prdoc diff --git a/prdoc/pr_2823.prdoc b/prdoc/pr_2823.prdoc new file mode 100644 index 000000000000..64a309969efb --- /dev/null +++ b/prdoc/pr_2823.prdoc @@ -0,0 +1,11 @@ +title: "`fungible::Unbalanced::decrease_balance`: Handle `precision` properly" + +doc: + - audience: Runtime Dev + description: | + `fungible::Unbalanced::decrease_balance` will now handle `precision` properly. This means when + passing `Exact`, it will ensure that the available balance is bigger or equal to the `amount` + that should be deducted. + +crates: + - name: "frame-support" diff --git a/substrate/frame/balances/src/tests/fungible_tests.rs b/substrate/frame/balances/src/tests/fungible_tests.rs index 8bf0509d8f77..52fbe10bedec 100644 --- a/substrate/frame/balances/src/tests/fungible_tests.rs +++ b/substrate/frame/balances/src/tests/fungible_tests.rs @@ -146,7 +146,7 @@ fn unbalanced_trait_decrease_balance_works_2() { assert_eq!(Balances::total_balance_on_hold(&1337), 60); assert_noop!( Balances::decrease_balance(&1337, 40, Exact, Expendable, Polite), - Error::::InsufficientBalance + TokenError::FundsUnavailable ); assert_eq!(Balances::decrease_balance(&1337, 39, Exact, Expendable, Polite), Ok(39)); assert_eq!(>::balance(&1337), 1); @@ -468,3 +468,28 @@ fn emit_events_with_changing_freezes() { assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Thawed { who: 1, amount: 15 })]); }); } + +#[test] +fn withdraw_precision_exact_works() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 10)); + assert_eq!(Balances::account(&1).free, 10); + assert_eq!(Balances::account(&1).frozen, 10); + + // `BestEffort` will not reduce anything + assert_ok!(>::withdraw( + &1, 5, BestEffort, Preserve, Polite + )); + + assert_eq!(Balances::account(&1).free, 10); + assert_eq!(Balances::account(&1).frozen, 10); + + assert_noop!( + >::withdraw(&1, 5, Exact, Preserve, Polite), + TokenError::FundsUnavailable + ); + }); +} diff --git a/substrate/frame/support/src/traits/tokens/fungible/regular.rs b/substrate/frame/support/src/traits/tokens/fungible/regular.rs index aece73777d28..87c6ef68d77d 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/regular.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/regular.rs @@ -181,9 +181,16 @@ pub trait Unbalanced: Inspect { ) -> Result { let old_balance = Self::balance(who); let free = Self::reducible_balance(who, preservation, force); - if let BestEffort = precision { - amount = amount.min(free); + match precision { + BestEffort => { + amount = amount.min(free); + }, + Exact => + if free < amount { + return Err(TokenError::FundsUnavailable.into()) + }, } + let new_balance = old_balance.checked_sub(&amount).ok_or(TokenError::FundsUnavailable)?; if let Some(dust) = Self::write_balance(who, new_balance)? { Self::handle_dust(Dust(dust));