Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Assets pallet: Giving the asset owner the ability to set minimum balance #13486

Merged
merged 10 commits into from
Mar 2, 2023
7 changes: 7 additions & 0 deletions frame/assets/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,5 +471,12 @@ benchmarks_instance_pallet! {
assert_last_event::<T, I>(Event::ApprovalCancelled { asset_id: asset_id.into(), owner: caller, delegate }.into());
}

set_min_balance {
let (asset_id, caller, caller_lookup) = create_default_asset::<T, I>(true);
}: _(SystemOrigin::Signed(caller.clone()), asset_id, 50u32.into())
verify {
assert_last_event::<T, I>(Event::AssetStatusChanged { asset_id: asset_id.into() }.into());
}

impl_benchmark_test_suite!(Assets, crate::mock::new_test_ext(), crate::mock::Test)
}
39 changes: 39 additions & 0 deletions frame/assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1511,6 +1511,45 @@ pub mod pallet {
let id: T::AssetId = id.into();
Self::do_refund(id, ensure_signed(origin)?, allow_burn)
}

/// Sets the minimum balance of an asset. Only works if there aren't any
/// accounts that are holding the asset or if the new vlaue of new_balance
/// is less than the old one.
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
///
/// Origin must be Signed and the sender has to be the Owner of the
/// asset `id`.
///
/// - `id`: The identifier of the asset.
/// - `min_balance`: The new value of `min_balance`.
///
/// Emits `AssetStatusChanged` event when successful.
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
#[pallet::call_index(28)]
#[pallet::weight(10_000)]
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
pub fn set_min_balance(
origin: OriginFor<T>,
id: T::AssetIdParameter,
min_balance: T::Balance,
) -> DispatchResult {
let origin = ensure_signed(origin)?;
let id: T::AssetId = id.into();

let mut details = Asset::<T, I>::get(id).ok_or(Error::<T, I>::Unknown)?;
ensure!(origin == details.owner, Error::<T, I>::NoPermission);

let old_min_balance = details.min_balance;
// Ensure that either the new min_balance is less than old min_balance or there aren't
// any accounts holding the asset.
ensure!(
min_balance < old_min_balance || details.accounts == 0,
Error::<T, I>::NoPermission
);

details.min_balance = min_balance;
Asset::<T, I>::insert(&id, details);

Self::deposit_event(Event::AssetStatusChanged { asset_id: id });
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
Ok(())
}
}
}

Expand Down
29 changes: 29 additions & 0 deletions frame/assets/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,35 @@ fn force_asset_status_should_work() {
});
}

#[test]
fn set_min_balance_should_work() {
new_test_ext().execute_with(|| {
let id = 42;
Balances::make_free_balance_be(&1, 10);
assert_ok!(Assets::create(RuntimeOrigin::signed(1), id, 1, 30));

assert_ok!(Assets::mint(RuntimeOrigin::signed(1), id, 1, 50));
// won't execute because there is an asset holder.
assert_noop!(
Assets::set_min_balance(RuntimeOrigin::signed(1), id, 50),
Error::<Test>::NoPermission
);

// will execute because the new value of min_balance is less than the
// old value. 10 < 30
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), id, 1, 10));

assert_ok!(Assets::burn(RuntimeOrigin::signed(1), id, 1, 50));
assert_noop!(
Assets::set_min_balance(RuntimeOrigin::signed(2), id, 50),
Error::<Test>::NoPermission
);

assert_ok!(Assets::set_min_balance(RuntimeOrigin::signed(1), id, 50));
assert_eq!(Asset::<Test>::get(id).unwrap().min_balance, 50);
});
}

#[test]
fn balance_conversion_should_work() {
new_test_ext().execute_with(|| {
Expand Down