feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,290 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::*;
use pezframe_support::traits::{
fungibles::{Dust, Inspect, InspectHold, MutateHold, Unbalanced, UnbalancedHold},
tokens::{
DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence,
},
};
use pezpallet_assets::BalanceOnHold;
use pezsp_runtime::{
traits::{CheckedAdd, CheckedSub, Zero},
ArithmeticError,
};
use storage::StorageDoubleMap;
// Implements [`BalanceOnHold`] from [`pezpallet-assets`], so it can understand whether there's some
// balance on hold for an asset account, and is able to signal to this pallet when to clear the
// state of an account.
impl<T: Config<I>, I: 'static> BalanceOnHold<T::AssetId, T::AccountId, T::Balance>
for Pallet<T, I>
{
fn balance_on_hold(asset: T::AssetId, who: &T::AccountId) -> Option<T::Balance> {
BalancesOnHold::<T, I>::get(asset, who)
}
fn died(asset: T::AssetId, who: &T::AccountId) {
defensive_assert!(
Holds::<T, I>::get(asset.clone(), who).is_empty(),
"The list of Holds should be empty before allowing an account to die"
);
defensive_assert!(
BalancesOnHold::<T, I>::get(asset.clone(), who).is_none(),
"The should not be a balance on hold before allowing to die"
);
Holds::<T, I>::remove(asset.clone(), who);
BalancesOnHold::<T, I>::remove(asset, who);
}
fn contains_holds(asset: T::AssetId) -> bool {
Holds::<T, I>::contains_prefix(asset)
}
}
// Implement [`fungibles::Inspect`](pezframe_support::traits::fungibles::Inspect) as it is bound by
// [`fungibles::InspectHold`](pezframe_support::traits::fungibles::InspectHold) and
// [`fungibles::MutateHold`](pezframe_support::traits::fungibles::MutateHold). To do so, we'll
// re-export all of `pezpallet-assets` implementation of the same trait.
impl<T: Config<I>, I: 'static> Inspect<T::AccountId> for Pallet<T, I> {
type AssetId = T::AssetId;
type Balance = T::Balance;
fn total_issuance(asset: Self::AssetId) -> Self::Balance {
pezpallet_assets::Pallet::<T, I>::total_issuance(asset)
}
fn minimum_balance(asset: Self::AssetId) -> Self::Balance {
pezpallet_assets::Pallet::<T, I>::minimum_balance(asset)
}
fn total_balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance {
pezpallet_assets::Pallet::<T, I>::total_balance(asset, who)
}
fn balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance {
pezpallet_assets::Pallet::<T, I>::balance(asset, who)
}
fn reducible_balance(
asset: Self::AssetId,
who: &T::AccountId,
preservation: Preservation,
force: Fortitude,
) -> Self::Balance {
pezpallet_assets::Pallet::<T, I>::reducible_balance(asset, who, preservation, force)
}
fn can_deposit(
asset: Self::AssetId,
who: &T::AccountId,
amount: Self::Balance,
provenance: Provenance,
) -> DepositConsequence {
pezpallet_assets::Pallet::<T, I>::can_deposit(asset, who, amount, provenance)
}
fn can_withdraw(
asset: Self::AssetId,
who: &T::AccountId,
amount: Self::Balance,
) -> WithdrawConsequence<Self::Balance> {
pezpallet_assets::Pallet::<T, I>::can_withdraw(asset, who, amount)
}
fn asset_exists(asset: Self::AssetId) -> bool {
pezpallet_assets::Pallet::<T, I>::asset_exists(asset)
}
}
impl<T: Config<I>, I: 'static> InspectHold<T::AccountId> for Pallet<T, I> {
type Reason = T::RuntimeHoldReason;
fn total_balance_on_hold(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance {
BalancesOnHold::<T, I>::get(asset, who).unwrap_or_else(Zero::zero)
}
fn balance_on_hold(
asset: Self::AssetId,
reason: &Self::Reason,
who: &T::AccountId,
) -> Self::Balance {
Holds::<T, I>::get(asset, who)
.iter()
.find(|x| &x.id == reason)
.map(|x| x.amount)
.unwrap_or_else(Zero::zero)
}
}
impl<T: Config<I>, I: 'static> Unbalanced<T::AccountId> for Pallet<T, I> {
fn handle_dust(dust: Dust<T::AccountId, Self>) {
let Dust(id, balance) = dust;
pezpallet_assets::Pallet::<T, I>::handle_dust(Dust(id, balance));
}
fn write_balance(
asset: Self::AssetId,
who: &T::AccountId,
amount: Self::Balance,
) -> Result<Option<Self::Balance>, DispatchError> {
pezpallet_assets::Pallet::<T, I>::write_balance(asset, who, amount)
}
fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) {
pezpallet_assets::Pallet::<T, I>::set_total_issuance(asset, amount)
}
fn decrease_balance(
asset: Self::AssetId,
who: &T::AccountId,
amount: Self::Balance,
precision: Precision,
preservation: Preservation,
force: Fortitude,
) -> Result<Self::Balance, DispatchError> {
pezpallet_assets::Pallet::<T, I>::decrease_balance(
asset,
who,
amount,
precision,
preservation,
force,
)
}
fn increase_balance(
asset: Self::AssetId,
who: &T::AccountId,
amount: Self::Balance,
precision: Precision,
) -> Result<Self::Balance, DispatchError> {
pezpallet_assets::Pallet::<T, I>::increase_balance(asset, who, amount, precision)
}
}
impl<T: Config<I>, I: 'static> UnbalancedHold<T::AccountId> for Pallet<T, I> {
fn set_balance_on_hold(
asset: Self::AssetId,
reason: &Self::Reason,
who: &T::AccountId,
amount: Self::Balance,
) -> DispatchResult {
let mut holds = Holds::<T, I>::get(asset.clone(), who);
let amount_on_hold =
BalancesOnHold::<T, I>::get(asset.clone(), who).unwrap_or_else(Zero::zero);
let amount_on_hold = if amount.is_zero() {
if let Some(pos) = holds.iter().position(|x| &x.id == reason) {
let item = &mut holds[pos];
let amount = item.amount;
holds.swap_remove(pos);
amount_on_hold.checked_sub(&amount).ok_or(ArithmeticError::Underflow)?
} else {
amount_on_hold
}
} else {
let (increase, delta) = if let Some(pos) = holds.iter().position(|x| &x.id == reason) {
let item = &mut holds[pos];
let (increase, delta) =
(amount > item.amount, item.amount.max(amount) - item.amount.min(amount));
item.amount = amount;
if item.amount.is_zero() {
holds.swap_remove(pos);
}
(increase, delta)
} else {
holds
.try_push(IdAmount { id: *reason, amount })
.map_err(|_| Error::<T, I>::TooManyHolds)?;
(true, amount)
};
let amount_on_hold = if increase {
amount_on_hold.checked_add(&delta).ok_or(ArithmeticError::Overflow)?
} else {
amount_on_hold.checked_sub(&delta).ok_or(ArithmeticError::Underflow)?
};
amount_on_hold
};
if !holds.is_empty() {
Holds::<T, I>::insert(asset.clone(), who, holds);
} else {
Holds::<T, I>::remove(asset.clone(), who);
}
if amount_on_hold.is_zero() {
BalancesOnHold::<T, I>::remove(asset.clone(), who);
} else {
BalancesOnHold::<T, I>::insert(asset.clone(), who, amount_on_hold);
}
Ok(())
}
}
impl<T: Config<I>, I: 'static> MutateHold<T::AccountId> for Pallet<T, I> {
fn done_hold(
asset_id: Self::AssetId,
reason: &Self::Reason,
who: &T::AccountId,
amount: Self::Balance,
) {
Self::deposit_event(Event::<T, I>::Held {
asset_id,
who: who.clone(),
reason: *reason,
amount,
});
}
fn done_release(
asset_id: Self::AssetId,
reason: &Self::Reason,
who: &T::AccountId,
amount: Self::Balance,
) {
Self::deposit_event(Event::<T, I>::Released {
asset_id,
who: who.clone(),
reason: *reason,
amount,
});
}
fn done_burn_held(
asset_id: Self::AssetId,
reason: &Self::Reason,
who: &T::AccountId,
amount: Self::Balance,
) {
Self::deposit_event(Event::<T, I>::Burned {
asset_id,
who: who.clone(),
reason: *reason,
amount,
});
}
}
@@ -0,0 +1,178 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # Assets Holder Pallet
//!
//! A pallet capable of holding fungibles from `pezpallet-assets`. This is an extension of
//! `pezpallet-assets`, wrapping [`fungibles::Inspect`](`pezframe_support::traits::fungibles::Inspect`).
//! It implements both
//! [`fungibles::hold::Inspect`](pezframe_support::traits::fungibles::hold::Inspect),
//! [`fungibles::hold::Mutate`](pezframe_support::traits::fungibles::hold::Mutate), and especially
//! [`fungibles::hold::Unbalanced`](pezframe_support::traits::fungibles::hold::Unbalanced). The
//! complexity of the operations is `O(1)`.
//!
//! ## Pallet API
//!
//! See the [`pallet`] module for more information about the interfaces this pallet exposes,
//! including its configuration trait, dispatchables, storage items, events and errors.
//!
//! ## Overview
//!
//! This pallet provides the following functionality:
//!
//! - Pallet hooks allowing [`pezpallet-assets`] to know the balance on hold for an account on a given
//! asset (see [`pezpallet_assets::BalanceOnHold`]).
//! - An implementation of
//! [`fungibles::hold::Inspect`](pezframe_support::traits::fungibles::hold::Inspect),
//! [`fungibles::hold::Mutate`](pezframe_support::traits::fungibles::hold::Mutate) and
//! [`fungibles::hold::Unbalanced`](pezframe_support::traits::fungibles::hold::Unbalanced), allowing
//! other pallets to manage holds for the `pezpallet-assets` assets.
#![cfg_attr(not(feature = "std"), no_std)]
use pezframe_support::{
pezpallet_prelude::*,
traits::{tokens::IdAmount, VariantCount, VariantCountOf},
BoundedVec,
};
use pezframe_system::pezpallet_prelude::BlockNumberFor;
pub use pallet::*;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
mod impl_fungibles;
#[pezframe_support::pallet]
pub mod pallet {
use super::*;
#[pallet::config(with_default)]
pub trait Config<I: 'static = ()>:
pezframe_system::Config + pezpallet_assets::Config<I, Holder = Pallet<Self, I>>
{
/// The overarching freeze reason.
#[pallet::no_default_bounds]
type RuntimeHoldReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount;
/// The overarching event type.
#[pallet::no_default_bounds]
#[allow(deprecated)]
type RuntimeEvent: From<Event<Self, I>>
+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
}
#[pallet::error]
pub enum Error<T, I = ()> {
/// Number of holds on an account would exceed the count of `RuntimeHoldReason`.
TooManyHolds,
}
#[pallet::pallet]
pub struct Pallet<T, I = ()>(_);
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// `who`s balance on hold was increased by `amount`.
Held {
who: T::AccountId,
asset_id: T::AssetId,
reason: T::RuntimeHoldReason,
amount: T::Balance,
},
/// `who`s balance on hold was decreased by `amount`.
Released {
who: T::AccountId,
asset_id: T::AssetId,
reason: T::RuntimeHoldReason,
amount: T::Balance,
},
/// `who`s balance on hold was burned by `amount`.
Burned {
who: T::AccountId,
asset_id: T::AssetId,
reason: T::RuntimeHoldReason,
amount: T::Balance,
},
}
/// A map that stores holds applied on an account for a given AssetId.
#[pallet::storage]
pub(super) type Holds<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
_,
Blake2_128Concat,
T::AssetId,
Blake2_128Concat,
T::AccountId,
BoundedVec<
IdAmount<T::RuntimeHoldReason, T::Balance>,
VariantCountOf<T::RuntimeHoldReason>,
>,
ValueQuery,
>;
/// A map that stores the current total balance on hold for every account on a given AssetId.
#[pallet::storage]
pub(super) type BalancesOnHold<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
_,
Blake2_128Concat,
T::AssetId,
Blake2_128Concat,
T::AccountId,
T::Balance,
>;
#[pallet::hooks]
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
#[cfg(feature = "try-runtime")]
fn try_state(_: BlockNumberFor<T>) -> Result<(), pezsp_runtime::TryRuntimeError> {
Self::do_try_state()
}
}
}
impl<T: Config<I>, I: 'static> Pallet<T, I> {
#[cfg(any(test, feature = "try-runtime"))]
fn do_try_state() -> Result<(), pezsp_runtime::TryRuntimeError> {
use pezsp_runtime::{
traits::{CheckedAdd, Zero},
ArithmeticError,
};
for (asset, who, balance_on_hold) in BalancesOnHold::<T, I>::iter() {
ensure!(balance_on_hold != Zero::zero(), "zero on hold must not be in state");
let mut amount_from_holds: T::Balance = Zero::zero();
for l in Holds::<T, I>::get(asset.clone(), who.clone()).iter() {
ensure!(l.amount != Zero::zero(), "zero amount is invalid");
amount_from_holds =
amount_from_holds.checked_add(&l.amount).ok_or(ArithmeticError::Overflow)?;
}
pezframe_support::ensure!(
balance_on_hold == amount_from_holds,
"The `BalancesOnHold` amount is not equal to the sum of `Holds` for (`asset`, `who`)"
);
}
Ok(())
}
}
@@ -0,0 +1,128 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Tests mock for `pezpallet-assets-freezer`.
use crate as pezpallet_assets_holder;
pub use crate::*;
use codec::{Decode, Encode, MaxEncodedLen};
use pezframe_support::{derive_impl, traits::AsEnsureOriginWithArg};
use scale_info::TypeInfo;
use pezsp_runtime::BuildStorage;
pub type AccountId = <Test as pezframe_system::Config>::AccountId;
pub type Balance = <Test as pezpallet_balances::Config>::Balance;
pub type AssetId = <Test as pezpallet_assets::Config>::AssetId;
type Block = pezframe_system::mocking::MockBlock<Test>;
#[pezframe_support::runtime]
mod runtime {
#[runtime::runtime]
#[runtime::derive(
RuntimeCall,
RuntimeEvent,
RuntimeError,
RuntimeOrigin,
RuntimeTask,
RuntimeHoldReason,
RuntimeFreezeReason
)]
pub struct Test;
#[runtime::pezpallet_index(0)]
pub type System = pezframe_system;
#[runtime::pezpallet_index(10)]
pub type Balances = pezpallet_balances;
#[runtime::pezpallet_index(20)]
pub type Assets = pezpallet_assets;
#[runtime::pezpallet_index(21)]
pub type AssetsHolder = pezpallet_assets_holder;
}
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Test {
type Block = Block;
type AccountData = pezpallet_balances::AccountData<u64>;
}
#[derive_impl(pezpallet_balances::config_preludes::TestDefaultConfig as pezpallet_balances::DefaultConfig)]
impl pezpallet_balances::Config for Test {
type AccountStore = System;
}
#[derive_impl(pezpallet_assets::config_preludes::TestDefaultConfig as pezpallet_assets::DefaultConfig)]
impl pezpallet_assets::Config for Test {
// type AssetAccountDeposit = ConstU64<1>;
type CreateOrigin = AsEnsureOriginWithArg<pezframe_system::EnsureSigned<u64>>;
type ForceOrigin = pezframe_system::EnsureRoot<u64>;
type Currency = Balances;
type Holder = AssetsHolder;
}
#[derive(
Decode,
DecodeWithMemTracking,
Encode,
MaxEncodedLen,
PartialEq,
Eq,
Ord,
PartialOrd,
TypeInfo,
Debug,
Clone,
Copy,
)]
pub enum DummyHoldReason {
Governance,
Staking,
Other,
}
impl VariantCount for DummyHoldReason {
// Intentionally set below the actual count of variants, to allow testing for `can_freeze`
const VARIANT_COUNT: u32 = 3;
}
impl Config for Test {
type RuntimeHoldReason = DummyHoldReason;
type RuntimeEvent = RuntimeEvent;
}
pub fn new_test_ext(execute: impl FnOnce()) -> pezsp_io::TestExternalities {
let t = RuntimeGenesisConfig {
assets: pezpallet_assets::GenesisConfig {
assets: vec![(1, 0, true, 1)],
metadata: vec![],
accounts: vec![(1, 1, 100)],
next_asset_id: None,
reserves: vec![],
},
system: Default::default(),
balances: Default::default(),
}
.build_storage()
.unwrap();
let mut ext: pezsp_io::TestExternalities = t.into();
ext.execute_with(|| {
System::set_block_number(1);
execute();
pezframe_support::assert_ok!(AssetsHolder::do_try_state());
});
ext
}
@@ -0,0 +1,558 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Tests for pezpallet-assets-holder.
use crate::mock::*;
use pezframe_support::{
assert_noop, assert_ok,
traits::tokens::fungibles::{Inspect, InspectHold, MutateHold, UnbalancedHold},
};
use pezpallet_assets::BalanceOnHold;
const WHO: AccountId = 1;
const ASSET_ID: AssetId = 1;
fn test_hold(id: DummyHoldReason, amount: Balance) {
assert_ok!(AssetsHolder::set_balance_on_hold(ASSET_ID, &id, &WHO, amount));
}
fn test_release(id: DummyHoldReason) {
assert_ok!(AssetsHolder::set_balance_on_hold(ASSET_ID, &id, &WHO, 0));
}
mod impl_balance_on_hold {
use super::*;
#[test]
fn balance_on_hold_works() {
new_test_ext(|| {
assert_eq!(
<AssetsHolder as BalanceOnHold<_, _, _>>::balance_on_hold(ASSET_ID, &WHO),
None
);
test_hold(DummyHoldReason::Governance, 1);
assert_eq!(
<AssetsHolder as BalanceOnHold<_, _, _>>::balance_on_hold(ASSET_ID, &WHO),
Some(1u64)
);
test_hold(DummyHoldReason::Staking, 3);
assert_eq!(
<AssetsHolder as BalanceOnHold<_, _, _>>::balance_on_hold(ASSET_ID, &WHO),
Some(4u64)
);
test_hold(DummyHoldReason::Governance, 2);
assert_eq!(
<AssetsHolder as BalanceOnHold<_, _, _>>::balance_on_hold(ASSET_ID, &WHO),
Some(5u64)
);
// also test releasing works to reduce a balance, and finally releasing everything
// resets to None
test_release(DummyHoldReason::Governance);
assert_eq!(
<AssetsHolder as BalanceOnHold<_, _, _>>::balance_on_hold(ASSET_ID, &WHO),
Some(3u64)
);
test_release(DummyHoldReason::Staking);
assert_eq!(
<AssetsHolder as BalanceOnHold<_, _, _>>::balance_on_hold(ASSET_ID, &WHO),
None
);
});
}
#[test]
#[should_panic = "The list of Holds should be empty before allowing an account to die"]
fn died_fails_if_holds_exist() {
new_test_ext(|| {
test_hold(DummyHoldReason::Governance, 1);
AssetsHolder::died(ASSET_ID, &WHO);
});
}
#[test]
fn died_works() {
new_test_ext(|| {
test_hold(DummyHoldReason::Governance, 1);
test_release(DummyHoldReason::Governance);
AssetsHolder::died(ASSET_ID, &WHO);
assert!(BalancesOnHold::<Test>::get(ASSET_ID, WHO).is_none());
assert!(Holds::<Test>::get(ASSET_ID, WHO).is_empty());
});
}
}
mod impl_hold_inspect {
use super::*;
#[test]
fn total_balance_on_hold_works() {
new_test_ext(|| {
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 0u64);
test_hold(DummyHoldReason::Governance, 1);
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 1u64);
test_hold(DummyHoldReason::Staking, 3);
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 4u64);
test_hold(DummyHoldReason::Governance, 2);
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 5u64);
// also test release to reduce a balance, and finally releasing everything resets to
// 0
test_release(DummyHoldReason::Governance);
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 3u64);
test_release(DummyHoldReason::Staking);
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 0u64);
});
}
#[test]
fn balance_on_hold_works() {
new_test_ext(|| {
assert_eq!(
<AssetsHolder as InspectHold<_>>::balance_on_hold(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO
),
0u64
);
test_hold(DummyHoldReason::Governance, 1);
assert_eq!(
<AssetsHolder as InspectHold<_>>::balance_on_hold(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO
),
1u64
);
test_hold(DummyHoldReason::Staking, 3);
assert_eq!(
<AssetsHolder as InspectHold<_>>::balance_on_hold(
ASSET_ID,
&DummyHoldReason::Staking,
&WHO
),
3u64
);
test_hold(DummyHoldReason::Staking, 2);
assert_eq!(
<AssetsHolder as InspectHold<_>>::balance_on_hold(
ASSET_ID,
&DummyHoldReason::Staking,
&WHO
),
2u64
);
// also test release to reduce a balance, and finally releasing everything resets to
// 0
test_release(DummyHoldReason::Governance);
assert_eq!(
<AssetsHolder as InspectHold<_>>::balance_on_hold(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO
),
0u64
);
test_release(DummyHoldReason::Staking);
assert_eq!(
<AssetsHolder as InspectHold<_>>::balance_on_hold(
ASSET_ID,
&DummyHoldReason::Staking,
&WHO
),
0u64
);
});
}
}
mod impl_hold_unbalanced {
use super::*;
// Note: Tests for `handle_dust`, `write_balance`, `set_total_issuance`, `decrease_balance`
// and `increase_balance` are intentionally left out without testing, since:
// 1. It is expected these methods are tested within `pezpallet-assets`, and
// 2. There are no valid cases that can be directly asserted using those methods in
// the scope of this pallet.
#[test]
fn set_balance_on_hold_works() {
new_test_ext(|| {
assert_eq!(Holds::<Test>::get(ASSET_ID, WHO).to_vec(), vec![]);
assert_eq!(BalancesOnHold::<Test>::get(ASSET_ID, WHO), None);
// Adding balance on hold works
assert_ok!(AssetsHolder::set_balance_on_hold(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
1
));
assert_eq!(
Holds::<Test>::get(ASSET_ID, WHO).to_vec(),
vec![IdAmount { id: DummyHoldReason::Governance, amount: 1 }]
);
assert_eq!(BalancesOnHold::<Test>::get(ASSET_ID, WHO), Some(1));
// Increasing hold works
assert_ok!(AssetsHolder::set_balance_on_hold(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
3
));
assert_eq!(
Holds::<Test>::get(ASSET_ID, WHO).to_vec(),
vec![IdAmount { id: DummyHoldReason::Governance, amount: 3 }]
);
assert_eq!(BalancesOnHold::<Test>::get(ASSET_ID, WHO), Some(3));
// Adding new balance on hold works
assert_ok!(AssetsHolder::set_balance_on_hold(
ASSET_ID,
&DummyHoldReason::Staking,
&WHO,
2
));
assert_eq!(
Holds::<Test>::get(ASSET_ID, WHO).to_vec(),
vec![
IdAmount { id: DummyHoldReason::Governance, amount: 3 },
IdAmount { id: DummyHoldReason::Staking, amount: 2 }
]
);
assert_eq!(BalancesOnHold::<Test>::get(ASSET_ID, WHO), Some(5));
// Note: Assertion skipped to meet @gavofyork's suggestion of matching the number of
// variant count with the number of enum's variants.
// // Adding more than max holds fails
// assert_noop!(
// AssetsHolder::set_balance_on_hold(ASSET_ID, &DummyHoldReason::Other, &WHO, 1),
// Error::<Test>::TooManyHolds
// );
// Decreasing balance on hold works
assert_ok!(AssetsHolder::set_balance_on_hold(
ASSET_ID,
&DummyHoldReason::Staking,
&WHO,
1
));
assert_eq!(
Holds::<Test>::get(ASSET_ID, WHO).to_vec(),
vec![
IdAmount { id: DummyHoldReason::Governance, amount: 3 },
IdAmount { id: DummyHoldReason::Staking, amount: 1 }
]
);
assert_eq!(BalancesOnHold::<Test>::get(ASSET_ID, WHO), Some(4));
// Decreasing until removal of balance on hold works
assert_ok!(AssetsHolder::set_balance_on_hold(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
0
));
assert_eq!(
Holds::<Test>::get(ASSET_ID, WHO).to_vec(),
vec![IdAmount { id: DummyHoldReason::Staking, amount: 1 }]
);
assert_eq!(BalancesOnHold::<Test>::get(ASSET_ID, WHO), Some(1));
// Clearing ol all holds works
assert_ok!(AssetsHolder::set_balance_on_hold(
ASSET_ID,
&DummyHoldReason::Staking,
&WHO,
0
));
assert_eq!(Holds::<Test>::get(ASSET_ID, WHO).to_vec(), vec![]);
assert_eq!(BalancesOnHold::<Test>::get(ASSET_ID, WHO), None);
});
}
}
mod impl_hold_mutate {
use super::*;
use pezframe_support::traits::tokens::{Fortitude, Precision, Preservation};
use pezsp_runtime::TokenError;
#[test]
fn hold_works() {
super::new_test_ext(|| {
// Holding some `amount` would decrease the asset account balance and change the
// reducible balance, while total issuance is preserved.
assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Governance, &WHO, 10));
assert_eq!(Assets::balance(ASSET_ID, &WHO), 90);
// Reducible balance is tested once to ensure token balance model is compliant.
assert_eq!(
Assets::reducible_balance(
ASSET_ID,
&WHO,
Preservation::Expendable,
Fortitude::Force
),
89
);
assert_eq!(
<AssetsHolder as InspectHold<_>>::balance_on_hold(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO
),
10
);
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 10);
// Holding preserves `total_balance`
assert_eq!(Assets::total_balance(ASSET_ID, &WHO), 100);
// Holding preserves `total_issuance`
assert_eq!(Assets::total_issuance(ASSET_ID), 100);
// Increasing the amount on hold for the same reason has the same effect as described
// above in `set_balance_on_hold_works`, while total issuance is preserved.
// Consideration: holding for an amount `x` will increase the already amount on hold by
// `x`.
assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Governance, &WHO, 20));
assert_eq!(Assets::balance(ASSET_ID, &WHO), 70);
assert_eq!(
<AssetsHolder as InspectHold<_>>::balance_on_hold(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO
),
30
);
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 30);
assert_eq!(Assets::total_issuance(ASSET_ID), 100);
// Holding some amount for a different reason has the same effect as described above in
// `set_balance_on_hold_works`, while total issuance is preserved.
assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Staking, &WHO, 20));
assert_eq!(Assets::balance(ASSET_ID, &WHO), 50);
assert_eq!(
<AssetsHolder as InspectHold<_>>::balance_on_hold(
ASSET_ID,
&DummyHoldReason::Staking,
&WHO
),
20
);
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 50);
assert_eq!(Assets::total_issuance(ASSET_ID), 100);
});
}
fn new_test_ext() -> pezsp_io::TestExternalities {
super::new_test_ext(|| {
assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Governance, &WHO, 30));
assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Staking, &WHO, 20));
})
}
#[test]
fn release_works() {
// Releasing up to some amount will increase the balance by the released
// amount, while preserving total issuance.
new_test_ext().execute_with(|| {
assert_ok!(AssetsHolder::release(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
20,
Precision::Exact,
));
assert_eq!(
<AssetsHolder as InspectHold<_>>::balance_on_hold(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO
),
10
);
assert_eq!(Assets::balance(ASSET_ID, WHO), 70);
});
// Releasing over the max amount on hold with `BestEffort` will increase the
// balance by the previously amount on hold, while preserving total issuance.
new_test_ext().execute_with(|| {
assert_ok!(AssetsHolder::release(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
31,
Precision::BestEffort,
));
assert_eq!(
<AssetsHolder as InspectHold<_>>::balance_on_hold(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO
),
0
);
assert_eq!(Assets::balance(ASSET_ID, WHO), 80);
});
// Releasing over the max amount on hold with `Exact` will fail.
new_test_ext().execute_with(|| {
assert_noop!(
AssetsHolder::release(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
31,
Precision::Exact,
),
TokenError::FundsUnavailable
);
});
}
#[test]
fn burn_held_works() {
// Burning works, reducing total issuance and `total_balance`.
new_test_ext().execute_with(|| {
assert_ok!(AssetsHolder::burn_held(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
1,
Precision::BestEffort,
Fortitude::Polite
));
assert_eq!(Assets::total_balance(ASSET_ID, &WHO), 99);
assert_eq!(Assets::total_issuance(ASSET_ID), 99);
});
// Burning by an amount up to the balance on hold with `Exact` works, reducing balance on
// hold up to the given amount.
new_test_ext().execute_with(|| {
assert_ok!(AssetsHolder::burn_held(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
10,
Precision::Exact,
Fortitude::Polite
));
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 40);
assert_eq!(Assets::balance(ASSET_ID, WHO), 50);
});
// Burning by an amount over the balance on hold with `BestEffort` works, reducing balance
// on hold up to the given amount.
new_test_ext().execute_with(|| {
assert_ok!(AssetsHolder::burn_held(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
31,
Precision::BestEffort,
Fortitude::Polite
));
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 20);
assert_eq!(Assets::balance(ASSET_ID, WHO), 50);
});
// Burning by an amount over the balance on hold with `Exact` fails.
new_test_ext().execute_with(|| {
assert_noop!(
AssetsHolder::burn_held(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
31,
Precision::Exact,
Fortitude::Polite
),
TokenError::FundsUnavailable
);
});
}
#[test]
fn burn_all_held_works() {
new_test_ext().execute_with(|| {
// Burning all balance on hold works as burning passing it as amount with `BestEffort`
assert_ok!(AssetsHolder::burn_all_held(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
Precision::BestEffort,
Fortitude::Polite,
));
assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 20);
assert_eq!(Assets::balance(ASSET_ID, WHO), 50);
});
}
#[test]
fn done_held_works() {
new_test_ext().execute_with(|| {
System::assert_has_event(
Event::<Test>::Held {
who: WHO,
asset_id: ASSET_ID,
reason: DummyHoldReason::Governance,
amount: 30,
}
.into(),
);
});
}
#[test]
fn done_release_works() {
new_test_ext().execute_with(|| {
assert_ok!(AssetsHolder::release(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
31,
Precision::BestEffort
));
System::assert_has_event(
Event::<Test>::Released {
who: WHO,
asset_id: ASSET_ID,
reason: DummyHoldReason::Governance,
amount: 30,
}
.into(),
);
});
}
#[test]
fn done_burn_held_works() {
new_test_ext().execute_with(|| {
assert_ok!(AssetsHolder::burn_all_held(
ASSET_ID,
&DummyHoldReason::Governance,
&WHO,
Precision::BestEffort,
Fortitude::Polite,
));
System::assert_has_event(
Event::<Test>::Burned {
who: WHO,
asset_id: ASSET_ID,
reason: DummyHoldReason::Governance,
amount: 30,
}
.into(),
);
});
}
}