Skip to content
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

Adding hold traits to asset pallet - Initial work. #1

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,23 @@ impl pallet_lottery::Config for Runtime {
type WeightInfo = pallet_lottery::weights::SubstrateWeight<Runtime>;
}

#[derive(
Encode,
Decode,
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
MaxEncodedLen,
scale_info::TypeInfo,
RuntimeDebug,
)]
pub enum AssetRuntimeHoldReasons {
TransferHold,
}

parameter_types! {
pub const AssetDeposit: Balance = 100 * DOLLARS;
pub const ApprovalDeposit: Balance = 1 * DOLLARS;
Expand Down Expand Up @@ -1513,6 +1530,8 @@ impl pallet_assets::Config<Instance1> for Runtime {
type CallbackHandle = ();
type WeightInfo = pallet_assets::weights::SubstrateWeight<Runtime>;
type RemoveItemsLimit = ConstU32<1000>;
type MaxHolds = ConstU32<500>;
type RuntimeHoldReason = AssetRuntimeHoldReasons;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
Expand Down Expand Up @@ -1540,6 +1559,8 @@ impl pallet_assets::Config<Instance2> for Runtime {
type WeightInfo = pallet_assets::weights::SubstrateWeight<Runtime>;
type RemoveItemsLimit = ConstU32<1000>;
type CallbackHandle = ();
type MaxHolds = ConstU32<500>;
type RuntimeHoldReason = AssetRuntimeHoldReasons;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
Expand Down
4 changes: 3 additions & 1 deletion frame/asset-conversion/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1194,7 +1194,9 @@ pub mod pallet {
()
);
} else {
let MultiAssetIdConversionResult::Converted(asset_id) = T::MultiAssetIdConverter::try_convert(asset) else {
let MultiAssetIdConversionResult::Converted(asset_id) =
T::MultiAssetIdConverter::try_convert(asset)
else {
return Err(())
};
let minimal = T::Assets::minimum_balance(asset_id);
Expand Down
27 changes: 25 additions & 2 deletions frame/asset-conversion/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

use super::*;
use crate as pallet_asset_conversion;

use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
construct_runtime,
instances::{Instance1, Instance2},
Expand All @@ -32,7 +32,7 @@ use sp_arithmetic::Permill;
use sp_core::H256;
use sp_runtime::{
traits::{AccountIdConversion, BlakeTwo256, IdentityLookup},
BuildStorage,
BuildStorage, RuntimeDebug,
};

type Block = frame_system::mocking::MockBlock<Test>;
Expand Down Expand Up @@ -90,6 +90,25 @@ impl pallet_balances::Config for Test {
type MaxHolds = ();
}

#[derive(
Encode,
Decode,
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
MaxEncodedLen,
scale_info::TypeInfo,
RuntimeDebug,
)]
pub enum TestId {
Foo,
Bar,
Baz,
}

impl pallet_assets::Config<Instance1> for Test {
type RuntimeEvent = RuntimeEvent;
type Balance = u128;
Expand All @@ -109,6 +128,8 @@ impl pallet_assets::Config<Instance1> for Test {
type Extra = ();
type WeightInfo = ();
type CallbackHandle = ();
type MaxHolds = ConstU32<500>;
type RuntimeHoldReason = TestId;
pallet_assets::runtime_benchmarks_enabled! {
type BenchmarkHelper = ();
}
Expand All @@ -134,6 +155,8 @@ impl pallet_assets::Config<Instance2> for Test {
type Extra = ();
type WeightInfo = ();
type CallbackHandle = ();
type MaxHolds = ConstU32<500>;
type RuntimeHoldReason = TestId;
pallet_assets::runtime_benchmarks_enabled! {
type BenchmarkHelper = ();
}
Expand Down
6 changes: 5 additions & 1 deletion frame/asset-conversion/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ fn pool_assets() -> Vec<u32> {

fn create_tokens(owner: u128, tokens: Vec<NativeOrAssetId<u32>>) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i should remove this change ...

for token_id in tokens {
let MultiAssetIdConversionResult::Converted(asset_id) = NativeOrAssetIdConverter::try_convert(&token_id) else { unreachable!("invalid token") };
let MultiAssetIdConversionResult::Converted(asset_id) =
NativeOrAssetIdConverter::try_convert(&token_id)
else {
unreachable!("invalid token")
};
assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, owner, false, 1));
}
}
Expand Down
2 changes: 2 additions & 0 deletions frame/assets/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,11 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
Some(details) => details,
None => return DepositConsequence::UnknownAsset,
};

if increase_supply && details.supply.checked_add(&amount).is_none() {
return DepositConsequence::Overflow
}

if let Some(account) = Account::<T, I>::get(id, who) {
if account.status.is_blocked() {
return DepositConsequence::Blocked
Expand Down
101 changes: 101 additions & 0 deletions frame/assets/src/impl_fungibles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,104 @@ impl<T: Config<I>, I: 'static> fungibles::InspectEnumerable<T::AccountId> for Pa
Asset::<T, I>::iter_keys()
}
}

impl<T: Config<I>, I: 'static> fungibles::MutateHold<T::AccountId> for Pallet<T, I> {}

impl<T: Config<I>, I: 'static> fungibles::InspectHold<T::AccountId> for Pallet<T, I> {
type Reason = T::RuntimeHoldReason;

fn total_balance_on_hold(asset: T::AssetId, who: &T::AccountId) -> T::Balance {
Holds::<T, I>::get(who, asset)
.iter()
.map(|x| x.amount)
.fold(T::Balance::zero(), |acc, x| acc.saturating_add(x))
}

fn reducible_total_balance_on_hold(
asset: T::AssetId,
who: &T::AccountId,
_force: Fortitude,
) -> Self::Balance {
let total_hold = Self::total_balance_on_hold(asset.clone(), who);
let free = Account::<T, I>::get(asset.clone(), who)
.map(|account| account.balance)
.unwrap_or(Self::Balance::zero());
// take alternative of unwrap
let ed = Asset::<T, I>::get(asset).map(|x| x.min_balance).unwrap();

if free.saturating_sub(total_hold) < ed {
return total_hold.saturating_sub(ed)
}
total_hold
}
fn balance_on_hold(asset: T::AssetId, reason: &Self::Reason, who: &T::AccountId) -> T::Balance {
Holds::<T, I>::get(who, asset)
.iter()
.find(|x| &x.id == reason)
.map_or_else(Zero::zero, |x| x.amount)
}
fn hold_available(asset: T::AssetId, reason: &Self::Reason, who: &T::AccountId) -> bool {
let asset_details = Asset::<T, I>::get(asset.clone()).unwrap();
let holds = Holds::<T, I>::get(who, asset);
if !holds.is_full() && asset_details.is_sufficient == true {
return true
}

if frame_system::Pallet::<T>::providers(who) == 0 {
return false
}

if holds.is_full() && !holds.iter().any(|x| &x.id == reason) {
return false
}
true
}
}

impl<T: Config<I>, I: 'static> fungibles::UnbalancedHold<T::AccountId> for Pallet<T, I> {
fn set_balance_on_hold(
asset: T::AssetId,
reason: &Self::Reason,
who: &T::AccountId,
amount: Self::Balance,
) -> DispatchResult {
let mut holds = Holds::<T, I>::get(who, asset.clone());

if let Some(item) = holds.iter_mut().find(|x| &x.id == reason) {
let delta = item.amount.max(amount) - item.amount.min(amount);
let increase = amount > item.amount;

if increase {
item.amount = item.amount.checked_add(&delta).ok_or(ArithmeticError::Overflow)?
} else {
item.amount = item.amount.checked_sub(&delta).ok_or(ArithmeticError::Underflow)?
};

holds.retain(|x| !x.amount.is_zero());
} else {
if !amount.is_zero() {
holds
.try_push(IdAmount { id: *reason, amount })
.map_err(|_| Error::<T, I>::TooManyHolds)?;
}
}

let account: Option<AssetAccountOf<T, I>> = Account::<T, I>::get(&asset, &who);

if let None = account {
let mut details = Asset::<T, I>::get(&asset).ok_or(Error::<T, I>::Unknown)?;
let new_account = AssetAccountOf::<T, I> {
balance: Zero::zero(),
status: AccountStatus::Liquid,
reason: Self::new_account(who, &mut details, None)?,
extra: T::Extra::default(),
};
Account::<T, I>::insert(&asset, &who, new_account);
}

// Here the balance pallet calls try_mutate_account that calculates then dust. We should do
// something similar.
Holds::<T, I>::insert(who, asset, holds);
Ok(())
}
}
23 changes: 23 additions & 0 deletions frame/assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ pub mod pallet {
/// attributes.
type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;

/// The overarching hold reason.
type RuntimeHoldReason: Parameter + Member + MaxEncodedLen + Ord + Copy;

/// The basic amount of funds that must be reserved for an asset.
#[pallet::constant]
type AssetDeposit: Get<DepositBalanceOf<Self, I>>;
Expand All @@ -306,6 +309,10 @@ pub mod pallet {
#[pallet::constant]
type StringLimit: Get<u32>;

/// The maximum number of holds that can exist on an account at any time.
#[pallet::constant]
type MaxHolds: Get<u32>;

/// A hook to allow a per-asset, per-account minimum balance to be enforced. This must be
/// respected in all permissionless operations.
type Freezer: FrozenBalance<Self::AssetId, Self::AccountId, Self::Balance>;
Expand Down Expand Up @@ -368,6 +375,18 @@ pub mod pallet {
ValueQuery,
>;

/// Holds on account balances.
#[pallet::storage]
pub type Holds<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
_,
Blake2_128Concat,
T::AccountId,
Blake2_128Concat,
T::AssetId,
BoundedVec<IdAmount<T::RuntimeHoldReason, T::Balance>, T::MaxHolds>,
ValueQuery,
>;

#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
Expand Down Expand Up @@ -571,6 +590,10 @@ pub mod pallet {
NotFrozen,
/// Callback action resulted in error
CallbackFailed,
/// Number of holds exceed `MaxHolds`
TooManyHolds,
/// Error to update holds
HoldsNotUpdated,
}

#[pallet::call(weight(<T as Config<I>>::WeightInfo))]
Expand Down
24 changes: 23 additions & 1 deletion frame/assets/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@

use super::*;
use crate as pallet_assets;
use codec::{Decode, Encode, MaxEncodedLen};

use codec::Encode;
use frame_support::{
construct_runtime, parameter_types,
traits::{AsEnsureOriginWithArg, ConstU32, ConstU64},
RuntimeDebug,
};
use sp_core::H256;
use sp_io::storage;
Expand Down Expand Up @@ -131,6 +132,25 @@ impl AssetsCallbackHandle {
}
}

#[derive(
Encode,
Decode,
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
MaxEncodedLen,
scale_info::TypeInfo,
RuntimeDebug,
)]
pub enum TestId {
Foo,
Bar,
Baz,
}

impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type Balance = u64;
Expand All @@ -150,6 +170,8 @@ impl Config for Test {
type CallbackHandle = AssetsCallbackHandle;
type Extra = ();
type RemoveItemsLimit = ConstU32<5>;
type MaxHolds = ConstU32<500>;
type RuntimeHoldReason = TestId;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
Expand Down
Loading