Skip to content

Commit

Permalink
First commiint
Browse files Browse the repository at this point in the history
  • Loading branch information
darkforest0202 committed Oct 19, 2023
1 parent 411a4e3 commit aba06f6
Show file tree
Hide file tree
Showing 6 changed files with 289 additions and 4 deletions.
99 changes: 99 additions & 0 deletions substrate/frame/assets/src/impl_fungibles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,102 @@ 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 = HoldReason;

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);
}

Holds::<T, I>::insert(who, asset, holds);
Ok(())
}
}
33 changes: 32 additions & 1 deletion substrate/frame/assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ pub mod pallet {
Success = Self::AccountId,
>;

/// Overarching hold reason.
type RuntimeHoldReason: From<HoldReason>;

/// The origin which may forcibly create or destroy an asset or otherwise alter privileged
/// attributes.
type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
Expand Down Expand Up @@ -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,26 @@ pub mod pallet {
ValueQuery,
>;

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

/// A reason for the pallet placing a hold on funds.
#[pallet::composite_enum]
pub enum HoldReason {
/// Transfer hold reason
#[codec(index = 0)]
Transfer,
}

#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
Expand Down Expand Up @@ -571,6 +598,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 Expand Up @@ -1069,7 +1100,7 @@ pub mod pallet {
ensure!(details.status == AssetStatus::Live, Error::<T, I>::LiveAsset);
ensure!(origin == details.owner, Error::<T, I>::NoPermission);
if details.owner == owner {
return Ok(())
return Ok(());
}

let metadata_deposit = Metadata::<T, I>::get(&id).deposit;
Expand Down
2 changes: 2 additions & 0 deletions substrate/frame/assets/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ impl Config for Test {
type CallbackHandle = AssetsCallbackHandle;
type Extra = ();
type RemoveItemsLimit = ConstU32<5>;
type MaxHolds = ConstU32<10>;
type RuntimeHoldReason = RuntimeHoldReason;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
Expand Down
144 changes: 143 additions & 1 deletion substrate/frame/assets/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ use crate::{mock::*, Error};
use frame_support::{
assert_noop, assert_ok,
dispatch::GetDispatchInfo,
traits::{fungibles::InspectEnumerable, tokens::Preservation::Protect, Currency},
traits::{
fungibles::InspectEnumerable,
tokens::{Fortitude::Polite, Precision::Exact, Preservation::Protect},
Currency,
},
};
use pallet_balances::Error as BalancesError;
use sp_io::storage;
Expand Down Expand Up @@ -1775,3 +1779,141 @@ fn asset_destroy_refund_existence_deposit() {
assert_eq!(Balances::reserved_balance(&admin), 0);
});
}

#[test]
fn unbalanced_trait_set_balance_works() {
new_test_ext().execute_with(|| {
let asset = 0;
assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset, 1, false, 1));
let admin = 1;
let dest = 2; // account with own deposit
Balances::make_free_balance_be(&admin, 100);
Balances::make_free_balance_be(&dest, 100);

assert_eq!(<Assets as fungibles::Inspect<_>>::balance(asset, &dest), 0);
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), asset, dest, 100));
assert_eq!(<Assets as fungibles::Inspect<_>>::balance(asset, &dest), 100);

assert_ok!(<Assets as fungibles::MutateHold<_>>::hold(
asset,
&HoldReason::Transfer.into(),
&dest,
60
));
assert_eq!(<Assets as fungibles::Inspect<_>>::balance(asset, &dest), 40);
assert_eq!(<Assets as fungibles::InspectHold<_>>::total_balance_on_hold(asset, &dest), 60);
assert_eq!(
<Assets as fungibles::InspectHold<_>>::balance_on_hold(
asset,
&HoldReason::Transfer.into(),
&dest
),
60
);

assert_eq!(
<Assets as fungibles::InspectHold<_>>::balance_on_hold(
asset,
&HoldReason::Transfer.into(),
&dest
),
60
);

assert_ok!(<Assets as fungibles::MutateHold<_>>::release(
asset,
&HoldReason::Transfer.into(),
&dest,
30,
Exact
));

assert_eq!(
<Assets as fungibles::InspectHold<_>>::balance_on_hold(
asset,
&HoldReason::Transfer.into(),
&dest
),
30
);
assert_eq!(<Assets as fungibles::InspectHold<_>>::total_balance_on_hold(asset, &dest), 30);

assert_ok!(<Assets as fungibles::MutateHold<_>>::release(
asset,
&HoldReason::Transfer.into(),
&dest,
30,
Exact
));

assert_eq!(
<Assets as fungibles::InspectHold<_>>::balance_on_hold(
asset,
&HoldReason::Transfer.into(),
&dest
),
0
);
assert_eq!(<Assets as fungibles::InspectHold<_>>::total_balance_on_hold(asset, &dest), 0);
let holds = Holds::<Test>::get(&dest, asset);
assert_eq!(holds.len(), 0);
});
}

#[test]
fn transfer_and_hold_works() {
new_test_ext().execute_with(|| {
let asset = 0;
let admin = 1;
let source = 2; // account with own deposit
let dest = 3; // account with own deposit
assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset, admin, true, 1));

Balances::make_free_balance_be(&admin, 100);
Balances::make_free_balance_be(&source, 100);

assert_eq!(<Assets as fungibles::Inspect<_>>::balance(asset, &source), 0);
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), asset, source, 100));

assert_eq!(<Assets as fungibles::Inspect<_>>::balance(asset, &source), 100);

assert_ok!(<Assets as fungibles::MutateHold<_>>::transfer_and_hold(
asset,
&HoldReason::Transfer.into(),
&source,
&dest,
60,
Exact,
Protect,
Polite
));

assert_eq!(<Assets as fungibles::Inspect<_>>::balance(asset, &source), 40);
assert_eq!(
<Assets as fungibles::InspectHold<_>>::balance_on_hold(
asset,
&HoldReason::Transfer.into(),
&dest
),
60
);
assert_eq!(<Assets as fungibles::InspectHold<_>>::total_balance_on_hold(asset, &dest), 60);

assert_ok!(<Assets as fungibles::MutateHold<_>>::release(
asset,
&HoldReason::Transfer.into(),
&dest,
20,
Exact
));
assert_eq!(
<Assets as fungibles::InspectHold<_>>::balance_on_hold(
asset,
&HoldReason::Transfer.into(),
&dest
),
40
);
assert_eq!(<Assets as fungibles::Inspect<_>>::balance(asset, &dest), 20);
});
}
13 changes: 11 additions & 2 deletions substrate/frame/assets/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ pub struct Approval<Balance, DepositBalance> {
pub(super) deposit: DepositBalance,
}

/// An identifier and balance.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct IdAmount<Id, Balance> {
/// An identifier for this item.
pub id: Id,
/// Some amount for this item.
pub amount: Balance,
}

#[test]
fn ensure_bool_decodes_to_consumer_or_sufficient() {
assert_eq!(false.encode(), ExistenceReason::<(), ()>::Consumer.encode());
Expand Down Expand Up @@ -120,7 +129,7 @@ where
{
pub(crate) fn take_deposit(&mut self) -> Option<Balance> {
if !matches!(self, ExistenceReason::DepositHeld(_)) {
return None
return None;
}
if let ExistenceReason::DepositHeld(deposit) =
sp_std::mem::replace(self, ExistenceReason::DepositRefunded)
Expand All @@ -133,7 +142,7 @@ where

pub(crate) fn take_deposit_from(&mut self) -> Option<(AccountId, Balance)> {
if !matches!(self, ExistenceReason::DepositFrom(..)) {
return None
return None;
}
if let ExistenceReason::DepositFrom(depositor, deposit) =
sp_std::mem::replace(self, ExistenceReason::DepositRefunded)
Expand Down
2 changes: 2 additions & 0 deletions substrate/frame/nft-fractionalization/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ impl pallet_assets::Config for Test {
pallet_assets::runtime_benchmarks_enabled! {
type BenchmarkHelper = ();
}
type RuntimeHoldReason = RuntimeHoldReason;
type MaxHolds = ConstU32<1>;
}

parameter_types! {
Expand Down

0 comments on commit aba06f6

Please sign in to comment.