-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: only destroy funds on refund if collateral is BelowMinimum #866
Changes from all commits
f02e12d
a8e1a83
4e31596
87cb2e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,9 +17,9 @@ | |
// If you feel like getting in touch with us, you can do so at info@botlabs.org | ||
use frame_support::{ | ||
assert_err, assert_ok, | ||
traits::fungibles::{Create, Inspect, Mutate}, | ||
traits::fungibles::{roles::Inspect as InspectRole, Create, Inspect, Mutate}, | ||
}; | ||
use frame_system::{pallet_prelude::OriginFor, RawOrigin}; | ||
use frame_system::{pallet_prelude::OriginFor, ConsumerLimits, RawOrigin}; | ||
use sp_runtime::TokenError; | ||
|
||
use crate::{ | ||
|
@@ -266,6 +266,112 @@ fn refund_below_min_balance() { | |
}); | ||
} | ||
|
||
#[test] | ||
fn refund_account_fails_when_account_blocked() { | ||
Comment on lines
+269
to
+270
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @abdulmth We have a test that checks the minimum balance case above this one, and I added a test here that ensures no balance is burnt when the beneficiary account cannot receive the funds due to any other reason (here: being blocked). This test fails if you revert the changes I made to the refund_account extrinsic. |
||
let pool_details = generate_pool_details( | ||
vec![DEFAULT_BONDED_CURRENCY_ID], | ||
get_linear_bonding_curve(), | ||
false, | ||
Some(PoolStatus::Refunding), | ||
Some(ACCOUNT_00), | ||
None, | ||
None, | ||
None, | ||
); | ||
let pool_id: AccountIdOf<Test> = calculate_pool_id(&[DEFAULT_BONDED_CURRENCY_ID]); | ||
|
||
ExtBuilder::default() | ||
.with_native_balances(vec![(ACCOUNT_01, ONE_HUNDRED_KILT)]) | ||
.with_pools(vec![(pool_id.clone(), pool_details)]) | ||
.with_collaterals(vec![DEFAULT_COLLATERAL_CURRENCY_ID]) | ||
.with_bonded_balance(vec![ | ||
(DEFAULT_COLLATERAL_CURRENCY_ID, pool_id.clone(), ONE_HUNDRED_KILT), | ||
(DEFAULT_COLLATERAL_CURRENCY_ID, ACCOUNT_01, ONE_HUNDRED_KILT), | ||
(DEFAULT_BONDED_CURRENCY_ID, ACCOUNT_01, ONE_HUNDRED_KILT), | ||
]) | ||
.build_and_execute_with_sanity_tests(|| { | ||
let origin: OriginFor<Test> = RawOrigin::Signed(ACCOUNT_01).into(); | ||
|
||
Assets::block( | ||
RawOrigin::Signed(Assets::owner(DEFAULT_COLLATERAL_CURRENCY_ID).unwrap()).into(), | ||
DEFAULT_COLLATERAL_CURRENCY_ID, | ||
ACCOUNT_01, | ||
) | ||
.expect("Failed to block account for test"); | ||
|
||
// Ensure the refund_account call fails due to failing can_deposit check | ||
assert_err!( | ||
BondingPallet::refund_account(origin, pool_id.clone(), ACCOUNT_01, 0, 1), | ||
TokenError::Blocked | ||
); | ||
}); | ||
} | ||
|
||
#[test] | ||
fn refund_account_fails_if_account_cannot_be_created() { | ||
let pool_details = generate_pool_details( | ||
vec![DEFAULT_BONDED_CURRENCY_ID], | ||
get_linear_bonding_curve(), | ||
false, | ||
Some(PoolStatus::Refunding), | ||
Some(ACCOUNT_00), | ||
None, | ||
None, | ||
None, | ||
); | ||
let pool_id: AccountIdOf<Test> = calculate_pool_id(&[DEFAULT_BONDED_CURRENCY_ID]); | ||
|
||
// get MaxConsumers value | ||
let max_consumers: usize = <Test as frame_system::Config>::MaxConsumers::max_consumers() | ||
.try_into() | ||
.expect(""); | ||
// Collateral must be non-sufficient for these tests to work, so we'll create a | ||
// new collateral asset in the test | ||
let collateral_id = DEFAULT_BONDED_CURRENCY_ID + 1; | ||
|
||
ExtBuilder::default() | ||
.with_native_balances(vec![ | ||
(ACCOUNT_01, ONE_HUNDRED_KILT), | ||
(pool_id.clone(), ONE_HUNDRED_KILT), | ||
]) | ||
.with_pools(vec![(pool_id.clone(), pool_details)]) | ||
.with_collaterals(vec![DEFAULT_COLLATERAL_CURRENCY_ID]) | ||
.with_bonded_balance(vec![ | ||
(DEFAULT_COLLATERAL_CURRENCY_ID, pool_id.clone(), ONE_HUNDRED_KILT), | ||
(DEFAULT_BONDED_CURRENCY_ID, ACCOUNT_01, ONE_HUNDRED_KILT), | ||
]) | ||
// Freeze some funds for ACCOUNT_01 so refund can only be partial | ||
.with_freezes(vec![(DEFAULT_BONDED_CURRENCY_ID, ACCOUNT_01, 100_000)]) | ||
.build_and_execute_with_sanity_tests(|| { | ||
// switch pool's collateral to a non-sufficient one | ||
assert_ok!(<Assets as Create<_>>::create(collateral_id, ACCOUNT_00, false, 1)); | ||
Pools::<Test>::mutate(&pool_id, |details| details.as_mut().unwrap().collateral = collateral_id); | ||
// make sure pool account holds collateral - this should work because it also | ||
// holds the (sufficient) original collateral | ||
assert_ok!(Assets::mint_into(collateral_id, &pool_id, ONE_HUNDRED_KILT)); | ||
// create non-sufficient assets to increase ACCOUNT_01 consumers count | ||
// the assets pallet requires at least two references to be available for | ||
// creating an account, but creates only one, meaning we create max_consumers - | ||
// 2 currencies (resulting in max_consumers - 1 consumers because we created one | ||
// previously) | ||
for i in 1..max_consumers - 1 { | ||
let i_u32: u32 = i.try_into().expect("Failed to convert to u32"); | ||
let asset_id = collateral_id + i_u32; | ||
assert_ok!(<Assets as Create<_>>::create(asset_id, ACCOUNT_00, false, 1)); | ||
assert_ok!(Assets::mint_into(asset_id, &ACCOUNT_01, ONE_HUNDRED_KILT)); | ||
} | ||
|
||
let origin: OriginFor<Test> = RawOrigin::Signed(ACCOUNT_01).into(); | ||
|
||
// Collateral is non-sufficient and thus would create additional consumer | ||
// references on ACCOUNT_01, which is too many | ||
assert_err!( | ||
BondingPallet::refund_account(origin, pool_id.clone(), ACCOUNT_01, 0, 1), | ||
TokenError::CannotCreate | ||
); | ||
}); | ||
} | ||
|
||
#[test] | ||
fn refund_account_fails_when_pool_not_refunding() { | ||
let pool_details = generate_pool_details( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in what case
into_result().is_err()
would betrue
? 🤔There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The DepositConsequence has multiple error cases and one success case. In all cases but the success case this would have been false, only the success case yields true. In other words, the transfer below can now still fail (because there's error cases we are not covering in this branch), leading to the extrinsic failing and being rolled back. As I explained in the PR description though, that would be intentional.