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,636 @@
// 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 pallet benchmarking.
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use alloc::vec;
use pezframe_benchmarking::{
v1::{
account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, BenchmarkError,
},
BenchmarkResult,
};
use pezframe_support::traits::{EnsureOrigin, Get, UnfilteredDispatchable};
use pezframe_system::RawOrigin as SystemOrigin;
use pezsp_runtime::{traits::Bounded, Weight};
use crate::Pallet as Assets;
const SEED: u32 = 0;
const MIN_BALANCE: u32 = 1;
fn default_asset_id<T: Config<I>, I: 'static>() -> T::AssetIdParameter {
T::BenchmarkHelper::create_asset_id_parameter(0)
}
fn create_default_asset<T: Config<I>, I: 'static>(
is_sufficient: bool,
) -> (T::AssetIdParameter, T::AccountId, AccountIdLookupOf<T>) {
let asset_id = default_asset_id::<T, I>();
let caller: T::AccountId = whitelisted_caller();
let caller_lookup = T::Lookup::unlookup(caller.clone());
let root = SystemOrigin::Root.into();
assert!(Assets::<T, I>::force_create(
root,
asset_id.clone(),
caller_lookup.clone(),
is_sufficient,
MIN_BALANCE.into(),
)
.is_ok());
(asset_id, caller, caller_lookup)
}
fn create_default_reserves<T: Config<I>, I: 'static>(
) -> (T::AssetIdParameter, T::AccountId, Vec<T::ReserveData>) {
// create asset
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
// build max number of reserves
let mut reserves = Vec::<T::ReserveData>::new();
for i in 0..MAX_RESERVES {
reserves.push(T::BenchmarkHelper::create_reserve_id_parameter(i));
}
(asset_id, caller, reserves)
}
pub fn create_default_minted_asset<T: Config<I>, I: 'static>(
is_sufficient: bool,
amount: T::Balance,
) -> (T::AssetIdParameter, T::AccountId, AccountIdLookupOf<T>) {
let (asset_id, caller, caller_lookup) = create_default_asset::<T, I>(is_sufficient);
if !is_sufficient {
T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance());
}
assert!(Assets::<T, I>::mint(
SystemOrigin::Signed(caller.clone()).into(),
asset_id.clone(),
caller_lookup.clone(),
amount,
)
.is_ok());
(asset_id, caller, caller_lookup)
}
fn swap_is_sufficient<T: Config<I>, I: 'static>(s: &mut bool) {
let asset_id = default_asset_id::<T, I>();
Asset::<T, I>::mutate(&asset_id.into(), |maybe_a| {
if let Some(ref mut a) = maybe_a {
core::mem::swap(s, &mut a.is_sufficient)
}
});
}
fn add_sufficients<T: Config<I>, I: 'static>(minter: T::AccountId, n: u32) {
let asset_id = default_asset_id::<T, I>();
let origin = SystemOrigin::Signed(minter);
let mut s = true;
swap_is_sufficient::<T, I>(&mut s);
for i in 0..n {
let target = account("sufficient", i, SEED);
let target_lookup = T::Lookup::unlookup(target);
assert!(Assets::<T, I>::mint(
origin.clone().into(),
asset_id.clone(),
target_lookup,
100u32.into()
)
.is_ok());
}
swap_is_sufficient::<T, I>(&mut s);
}
fn add_approvals<T: Config<I>, I: 'static>(minter: T::AccountId, n: u32) {
let asset_id = default_asset_id::<T, I>();
let _ = T::Currency::deposit_creating(
&minter,
T::ApprovalDeposit::get() * n.into() + T::Currency::minimum_balance(),
);
let minter_lookup = T::Lookup::unlookup(minter.clone());
let origin = SystemOrigin::Signed(minter);
Assets::<T, I>::mint(
origin.clone().into(),
asset_id.clone(),
minter_lookup,
(100 * (n + 1)).into(),
)
.unwrap();
let enough = T::Currency::minimum_balance();
for i in 0..n {
let target = account("approval", i, SEED);
T::Currency::make_free_balance_be(&target, enough);
let target_lookup = T::Lookup::unlookup(target);
Assets::<T, I>::approve_transfer(
origin.clone().into(),
asset_id.clone(),
target_lookup,
100u32.into(),
)
.unwrap();
}
}
fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
pezframe_system::Pallet::<T>::assert_last_event(generic_event.into());
}
fn assert_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
pezframe_system::Pallet::<T>::assert_has_event(generic_event.into());
}
benchmarks_instance_pallet! {
create {
let asset_id = default_asset_id::<T, I>();
let origin = T::CreateOrigin::try_successful_origin(&asset_id.clone().into())
.map_err(|_| BenchmarkError::Weightless)?;
let caller = T::CreateOrigin::ensure_origin(origin.clone(), &asset_id.clone().into()).unwrap();
let caller_lookup = T::Lookup::unlookup(caller.clone());
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
}: _<T::RuntimeOrigin>(origin, asset_id.clone(), caller_lookup, 1u32.into())
verify {
assert_last_event::<T, I>(Event::Created { asset_id: asset_id.into(), creator: caller.clone(), owner: caller }.into());
}
force_create {
let asset_id = default_asset_id::<T, I>();
let caller: T::AccountId = whitelisted_caller();
let caller_lookup = T::Lookup::unlookup(caller.clone());
}: _(SystemOrigin::Root, asset_id.clone(), caller_lookup, true, 1u32.into())
verify {
assert_last_event::<T, I>(Event::ForceCreated { asset_id: asset_id.into(), owner: caller }.into());
}
start_destroy {
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, 100u32.into());
Assets::<T, I>::freeze_asset(
SystemOrigin::Signed(caller.clone()).into(),
asset_id.clone(),
)?;
}:_(SystemOrigin::Signed(caller), asset_id.clone())
verify {
assert_last_event::<T, I>(Event::DestructionStarted { asset_id: asset_id.into() }.into());
}
destroy_accounts {
let c in 0 .. T::RemoveItemsLimit::get();
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
add_sufficients::<T, I>(caller.clone(), c);
Assets::<T, I>::freeze_asset(
SystemOrigin::Signed(caller.clone()).into(),
asset_id.clone(),
)?;
Assets::<T,I>::start_destroy(SystemOrigin::Signed(caller.clone()).into(), asset_id.clone())?;
}:_(SystemOrigin::Signed(caller), asset_id.clone())
verify {
assert_last_event::<T, I>(Event::AccountsDestroyed {
asset_id: asset_id.into(),
accounts_destroyed: c,
accounts_remaining: 0,
}.into());
}
destroy_approvals {
let a in 0 .. T::RemoveItemsLimit::get();
let (asset_id, caller, _) = create_default_minted_asset::<T, I>(true, 100u32.into());
add_approvals::<T, I>(caller.clone(), a);
Assets::<T, I>::freeze_asset(
SystemOrigin::Signed(caller.clone()).into(),
asset_id.clone(),
)?;
Assets::<T,I>::start_destroy(SystemOrigin::Signed(caller.clone()).into(), asset_id.clone())?;
}:_(SystemOrigin::Signed(caller), asset_id.clone())
verify {
assert_last_event::<T, I>(Event::ApprovalsDestroyed {
asset_id: asset_id.into(),
approvals_destroyed: a,
approvals_remaining: 0,
}.into());
}
finish_destroy {
let (asset_id, caller, caller_lookup) = create_default_asset::<T, I>(true);
Assets::<T, I>::freeze_asset(
SystemOrigin::Signed(caller.clone()).into(),
asset_id.clone(),
)?;
Assets::<T,I>::start_destroy(SystemOrigin::Signed(caller.clone()).into(), asset_id.clone())?;
}:_(SystemOrigin::Signed(caller), asset_id.clone())
verify {
assert_last_event::<T, I>(Event::Destroyed {
asset_id: asset_id.into(),
}.into()
);
}
mint {
let (asset_id, caller, caller_lookup) = create_default_asset::<T, I>(true);
let amount = T::Balance::from(100u32);
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup, amount)
verify {
assert_last_event::<T, I>(Event::Issued { asset_id: asset_id.into(), owner: caller, amount }.into());
}
burn {
let amount = T::Balance::from(100u32);
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, amount);
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup, amount)
verify {
assert_last_event::<T, I>(Event::Burned { asset_id: asset_id.into(), owner: caller, balance: amount }.into());
}
transfer {
let amount = T::Balance::from(100u32);
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, amount);
let target: T::AccountId = account("target", 0, SEED);
let target_lookup = T::Lookup::unlookup(target.clone());
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), target_lookup, amount)
verify {
assert_last_event::<T, I>(Event::Transferred { asset_id: asset_id.into(), from: caller, to: target, amount }.into());
}
transfer_keep_alive {
let mint_amount = T::Balance::from(200u32);
let amount = T::Balance::from(100u32);
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, mint_amount);
let target: T::AccountId = account("target", 0, SEED);
let target_lookup = T::Lookup::unlookup(target.clone());
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), target_lookup, amount)
verify {
assert!(pezframe_system::Pallet::<T>::account_exists(&caller));
assert_last_event::<T, I>(Event::Transferred { asset_id: asset_id.into(), from: caller, to: target, amount }.into());
}
force_transfer {
let amount = T::Balance::from(100u32);
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, amount);
let target: T::AccountId = account("target", 0, SEED);
let target_lookup = T::Lookup::unlookup(target.clone());
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup, target_lookup, amount)
verify {
assert_last_event::<T, I>(
Event::Transferred { asset_id: asset_id.into(), from: caller, to: target, amount }.into()
);
}
freeze {
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, 100u32.into());
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup)
verify {
assert_last_event::<T, I>(Event::Frozen { asset_id: asset_id.into(), who: caller }.into());
}
thaw {
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, 100u32.into());
Assets::<T, I>::freeze(
SystemOrigin::Signed(caller.clone()).into(),
asset_id.clone(),
caller_lookup.clone(),
)?;
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup)
verify {
assert_last_event::<T, I>(Event::Thawed { asset_id: asset_id.into(), who: caller }.into());
}
freeze_asset {
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, 100u32.into());
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone())
verify {
assert_last_event::<T, I>(Event::AssetFrozen { asset_id: asset_id.into() }.into());
}
thaw_asset {
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, 100u32.into());
Assets::<T, I>::freeze_asset(
SystemOrigin::Signed(caller.clone()).into(),
asset_id.clone(),
)?;
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone())
verify {
assert_last_event::<T, I>(Event::AssetThawed { asset_id: asset_id.into() }.into());
}
transfer_ownership {
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
let target: T::AccountId = account("target", 0, SEED);
let target_lookup = T::Lookup::unlookup(target.clone());
}: _(SystemOrigin::Signed(caller), asset_id.clone(), target_lookup)
verify {
assert_last_event::<T, I>(Event::OwnerChanged { asset_id: asset_id.into(), owner: target }.into());
}
set_team {
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
let target0 = T::Lookup::unlookup(account("target", 0, SEED));
let target1 = T::Lookup::unlookup(account("target", 1, SEED));
let target2 = T::Lookup::unlookup(account("target", 2, SEED));
}: _(SystemOrigin::Signed(caller), asset_id.clone(), target0, target1, target2)
verify {
assert_last_event::<T, I>(Event::TeamChanged {
asset_id: asset_id.into(),
issuer: account("target", 0, SEED),
admin: account("target", 1, SEED),
freezer: account("target", 2, SEED),
}.into());
}
set_reserves {
let (asset_id, caller, reserves) = create_default_reserves::<T, I>();
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
}: _(SystemOrigin::Signed(caller), asset_id.clone(), reserves.clone())
verify {
assert_last_event::<T, I>(Event::ReservesUpdated { asset_id: asset_id.into(), reserves: reserves }.into());
}
set_metadata {
let n in 0 .. T::StringLimit::get();
let s in 0 .. T::StringLimit::get();
let name = vec![0u8; n as usize];
let symbol = vec![0u8; s as usize];
let decimals = 12;
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
}: _(SystemOrigin::Signed(caller), asset_id.clone(), name.clone(), symbol.clone(), decimals)
verify {
assert_last_event::<T, I>(Event::MetadataSet { asset_id: asset_id.into(), name, symbol, decimals, is_frozen: false }.into());
}
clear_metadata {
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
let dummy = vec![0u8; T::StringLimit::get() as usize];
let origin = SystemOrigin::Signed(caller.clone()).into();
Assets::<T, I>::set_metadata(origin, asset_id.clone(), dummy.clone(), dummy, 12)?;
}: _(SystemOrigin::Signed(caller), asset_id.clone())
verify {
assert_last_event::<T, I>(Event::MetadataCleared { asset_id: asset_id.into() }.into());
}
force_set_metadata {
let n in 0 .. T::StringLimit::get();
let s in 0 .. T::StringLimit::get();
let name = vec![0u8; n as usize];
let symbol = vec![0u8; s as usize];
let decimals = 12;
let (asset_id, _, _) = create_default_asset::<T, I>(true);
let origin =
T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let call = Call::<T, I>::force_set_metadata {
id: asset_id.clone(),
name: name.clone(),
symbol: symbol.clone(),
decimals,
is_frozen: false,
};
}: { call.dispatch_bypass_filter(origin)? }
verify {
assert_last_event::<T, I>(Event::MetadataSet { asset_id: asset_id.into(), name, symbol, decimals, is_frozen: false }.into());
}
force_clear_metadata {
let (asset_id, caller, _) = create_default_asset::<T, I>(true);
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
let dummy = vec![0u8; T::StringLimit::get() as usize];
let origin = SystemOrigin::Signed(caller).into();
Assets::<T, I>::set_metadata(origin, asset_id.clone(), dummy.clone(), dummy, 12)?;
let origin =
T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let call = Call::<T, I>::force_clear_metadata { id: asset_id.clone() };
}: { call.dispatch_bypass_filter(origin)? }
verify {
assert_last_event::<T, I>(Event::MetadataCleared { asset_id: asset_id.into() }.into());
}
force_asset_status {
let (asset_id, caller, caller_lookup) = create_default_asset::<T, I>(true);
let origin =
T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let call = Call::<T, I>::force_asset_status {
id: asset_id.clone(),
owner: caller_lookup.clone(),
issuer: caller_lookup.clone(),
admin: caller_lookup.clone(),
freezer: caller_lookup,
min_balance: 100u32.into(),
is_sufficient: true,
is_frozen: false,
};
}: { call.dispatch_bypass_filter(origin)? }
verify {
assert_last_event::<T, I>(Event::AssetStatusChanged { asset_id: asset_id.into() }.into());
}
approve_transfer {
let (asset_id, caller, _) = create_default_minted_asset::<T, I>(true, 100u32.into());
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
let delegate: T::AccountId = account("delegate", 0, SEED);
let delegate_lookup = T::Lookup::unlookup(delegate.clone());
let amount = 100u32.into();
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), delegate_lookup, amount)
verify {
assert_last_event::<T, I>(Event::ApprovedTransfer { asset_id: asset_id.into(), source: caller, delegate, amount }.into());
}
transfer_approved {
let (asset_id, owner, owner_lookup) = create_default_minted_asset::<T, I>(true, 100u32.into());
T::Currency::make_free_balance_be(&owner, DepositBalanceOf::<T, I>::max_value());
let delegate: T::AccountId = account("delegate", 0, SEED);
whitelist_account!(delegate);
let delegate_lookup = T::Lookup::unlookup(delegate.clone());
let amount = 100u32.into();
let origin = SystemOrigin::Signed(owner.clone()).into();
Assets::<T, I>::approve_transfer(origin, asset_id.clone(), delegate_lookup, amount)?;
let dest: T::AccountId = account("dest", 0, SEED);
let dest_lookup = T::Lookup::unlookup(dest.clone());
}: _(SystemOrigin::Signed(delegate.clone()), asset_id.clone(), owner_lookup, dest_lookup, amount)
verify {
assert!(T::Currency::reserved_balance(&owner).is_zero());
assert_event::<T, I>(Event::Transferred { asset_id: asset_id.into(), from: owner, to: dest, amount }.into());
}
cancel_approval {
let (asset_id, caller, _) = create_default_minted_asset::<T, I>(true, 100u32.into());
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
let delegate: T::AccountId = account("delegate", 0, SEED);
let delegate_lookup = T::Lookup::unlookup(delegate.clone());
let amount = 100u32.into();
let origin = SystemOrigin::Signed(caller.clone()).into();
Assets::<T, I>::approve_transfer(origin, asset_id.clone(), delegate_lookup.clone(), amount)?;
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), delegate_lookup)
verify {
assert_last_event::<T, I>(Event::ApprovalCancelled { asset_id: asset_id.into(), owner: caller, delegate }.into());
}
force_cancel_approval {
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, 100u32.into());
T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
let delegate: T::AccountId = account("delegate", 0, SEED);
let delegate_lookup = T::Lookup::unlookup(delegate.clone());
let amount = 100u32.into();
let origin = SystemOrigin::Signed(caller.clone()).into();
Assets::<T, I>::approve_transfer(origin, asset_id.clone(), delegate_lookup.clone(), amount)?;
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup, delegate_lookup)
verify {
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>(false);
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), 50u32.into())
verify {
assert_last_event::<T, I>(Event::AssetMinBalanceChanged { asset_id: asset_id.into(), new_min_balance: 50u32.into() }.into());
}
touch {
let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::<T, I>(false);
let new_account: T::AccountId = account("newaccount", 1, SEED);
T::Currency::make_free_balance_be(&new_account, DepositBalanceOf::<T, I>::max_value());
assert_ne!(asset_owner, new_account);
assert!(!Account::<T, I>::contains_key(asset_id.clone().into(), &new_account));
}: _(SystemOrigin::Signed(new_account.clone()), asset_id.clone())
verify {
assert!(Account::<T, I>::contains_key(asset_id.into(), &new_account));
}
touch_other {
let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::<T, I>(false);
let new_account: T::AccountId = account("newaccount", 1, SEED);
let new_account_lookup = T::Lookup::unlookup(new_account.clone());
T::Currency::make_free_balance_be(&asset_owner, DepositBalanceOf::<T, I>::max_value());
assert_ne!(asset_owner, new_account);
assert!(!Account::<T, I>::contains_key(asset_id.clone().into(), &new_account));
}: _(SystemOrigin::Signed(asset_owner.clone()), asset_id.clone(), new_account_lookup)
verify {
assert!(Account::<T, I>::contains_key(asset_id.into(), &new_account));
}
refund {
let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::<T, I>(false);
let new_account: T::AccountId = account("newaccount", 1, SEED);
T::Currency::make_free_balance_be(&new_account, DepositBalanceOf::<T, I>::max_value());
assert_ne!(asset_owner, new_account);
assert!(Assets::<T, I>::touch(
SystemOrigin::Signed(new_account.clone()).into(),
asset_id.clone()
).is_ok());
// `touch` should reserve balance of the caller according to the `AssetAccountDeposit` amount...
assert_eq!(T::Currency::reserved_balance(&new_account), T::AssetAccountDeposit::get());
// ...and also create an `Account` entry.
assert!(Account::<T, I>::contains_key(asset_id.clone().into(), &new_account));
}: _(SystemOrigin::Signed(new_account.clone()), asset_id, true)
verify {
// `refund`ing should of course repatriate the reserve
assert!(T::Currency::reserved_balance(&new_account).is_zero());
}
refund_other {
let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::<T, I>(false);
let new_account: T::AccountId = account("newaccount", 1, SEED);
let new_account_lookup = T::Lookup::unlookup(new_account.clone());
T::Currency::make_free_balance_be(&asset_owner, DepositBalanceOf::<T, I>::max_value());
assert_ne!(asset_owner, new_account);
assert!(Assets::<T, I>::touch_other(
SystemOrigin::Signed(asset_owner.clone()).into(),
asset_id.clone(),
new_account_lookup.clone()
).is_ok());
// `touch` should reserve balance of the caller according to the `AssetAccountDeposit` amount...
assert_eq!(T::Currency::reserved_balance(&asset_owner), T::AssetAccountDeposit::get());
assert!(Account::<T, I>::contains_key(asset_id.clone().into(), &new_account));
}: _(SystemOrigin::Signed(asset_owner.clone()), asset_id, new_account_lookup.clone())
verify {
// this should repatriate the reserved balance of the freezer
assert!(T::Currency::reserved_balance(&asset_owner).is_zero());
}
block {
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, 100u32.into());
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup)
verify {
assert_last_event::<T, I>(Event::Blocked { asset_id: asset_id.into(), who: caller }.into());
}
transfer_all {
let amount = T::Balance::from(2 * MIN_BALANCE);
let (asset_id, caller, caller_lookup) = create_default_minted_asset::<T, I>(true, amount);
let target: T::AccountId = account("target", 0, SEED);
let target_lookup = T::Lookup::unlookup(target.clone());
}: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), target_lookup, false)
verify {
assert_last_event::<T, I>(Event::Transferred { asset_id: asset_id.into(), from: caller, to: target, amount }.into());
}
total_issuance {
use pezframe_support::traits::fungibles::Inspect;
let (asset_id, _, _) = create_default_minted_asset::<T, I>(true, 100u32.into());
let amount;
}: {
amount = Pallet::<T, I>::total_issuance(asset_id.into());
} verify {
assert_eq!(amount, 100u32.into());
}
balance {
let (asset_id, caller, _) = create_default_minted_asset::<T, I>(true, 100u32.into());
let amount;
}: {
amount = Pallet::<T, I>::balance(asset_id.into(), caller);
} verify {
assert_eq!(amount, 100u32.into());
}
allowance {
use pezframe_support::traits::fungibles::approvals::Inspect;
let (asset_id, caller, _) = create_default_minted_asset::<T, I>(true, 100u32.into());
add_approvals::<T, I>(caller.clone(), 1);
let delegate: T::AccountId = account("approval", 0, SEED);
let amount;
}: {
amount = Pallet::<T, I>::allowance(asset_id.into(), &caller, &delegate);
} verify {
assert_eq!(amount, 100u32.into());
}
migration_v2_foreign_asset_set_reserve_weight {
let (id, _, _) = create_default_asset::<T, I>(true);
let id: <T as pallet::Config<I>>::AssetId = id.into();
let reserve = T::BenchmarkHelper::create_reserve_id_parameter(42);
}: {
let asset_id = Asset::<T, I>::iter_keys().next()
.ok_or_else(|| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
assert_eq!(id, asset_id);
Pallet::<T, I>::unchecked_update_reserves(asset_id, vec![reserve.clone()]).unwrap();
}
verify {
assert_eq!(Reserves::<T, I>::get(id)[0], reserve);
}
impl_benchmark_test_suite!(Assets, crate::mock::new_test_ext(), crate::mock::Test)
}
@@ -0,0 +1,98 @@
// 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.
//! Datatype for easy mutation of the extra "sidecar" data.
use super::*;
/// A mutator type allowing inspection and possible modification of the extra "sidecar" data.
///
/// This may be used as a `Deref` for the pallet's extra data. If mutated (using `DerefMut`), then
/// any uncommitted changes (see `commit` function) will be automatically committed to storage when
/// dropped. Changes, even after committed, may be reverted to their original values with the
/// `revert` function.
pub struct ExtraMutator<T: Config<I>, I: 'static = ()> {
id: T::AssetId,
who: T::AccountId,
original: T::Extra,
pending: Option<T::Extra>,
}
impl<T: Config<I>, I: 'static> Drop for ExtraMutator<T, I> {
fn drop(&mut self) {
debug_assert!(self.commit().is_ok(), "attempt to write to non-existent asset account");
}
}
impl<T: Config<I>, I: 'static> core::ops::Deref for ExtraMutator<T, I> {
type Target = T::Extra;
fn deref(&self) -> &T::Extra {
match self.pending {
Some(ref value) => value,
None => &self.original,
}
}
}
impl<T: Config<I>, I: 'static> core::ops::DerefMut for ExtraMutator<T, I> {
fn deref_mut(&mut self) -> &mut T::Extra {
if self.pending.is_none() {
self.pending = Some(self.original.clone());
}
self.pending.as_mut().unwrap()
}
}
impl<T: Config<I>, I: 'static> ExtraMutator<T, I> {
pub(super) fn maybe_new(
id: T::AssetId,
who: impl core::borrow::Borrow<T::AccountId>,
) -> Option<ExtraMutator<T, I>> {
if let Some(a) = Account::<T, I>::get(&id, who.borrow()) {
Some(ExtraMutator::<T, I> {
id,
who: who.borrow().clone(),
original: a.extra,
pending: None,
})
} else {
None
}
}
/// Commit any changes to storage.
pub fn commit(&mut self) -> Result<(), ()> {
if let Some(extra) = self.pending.take() {
Account::<T, I>::try_mutate(&self.id, &self.who, |maybe_account| {
maybe_account.as_mut().ok_or(()).map(|account| account.extra = extra)
})
} else {
Ok(())
}
}
/// Revert any changes, even those already committed by `self` and drop self.
pub fn revert(mut self) -> Result<(), ()> {
self.pending = None;
Account::<T, I>::try_mutate(&self.id, &self.who, |maybe_account| {
maybe_account
.as_mut()
.ok_or(())
.map(|account| account.extra = self.original.clone())
})
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,360 @@
// 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.
//! Implementations for fungibles trait.
use alloc::vec::Vec;
use pezframe_support::{
defensive,
traits::tokens::{
Fortitude,
Precision::{self, BestEffort},
Preservation::{self, Expendable},
Provenance::{self, Minted},
},
};
use super::*;
impl<T: Config<I>, I: 'static> fungibles::Inspect<<T as SystemConfig>::AccountId> for Pallet<T, I> {
type AssetId = T::AssetId;
type Balance = T::Balance;
fn total_issuance(asset: Self::AssetId) -> Self::Balance {
Asset::<T, I>::get(asset).map(|x| x.supply).unwrap_or_else(Zero::zero)
}
fn minimum_balance(asset: Self::AssetId) -> Self::Balance {
Asset::<T, I>::get(asset).map(|x| x.min_balance).unwrap_or_else(Zero::zero)
}
fn balance(asset: Self::AssetId, who: &<T as SystemConfig>::AccountId) -> Self::Balance {
Pallet::<T, I>::balance(asset, who)
}
fn total_balance(asset: Self::AssetId, who: &<T as SystemConfig>::AccountId) -> Self::Balance {
Pallet::<T, I>::balance(asset.clone(), who)
.saturating_add(T::Holder::balance_on_hold(asset, who).unwrap_or_default())
}
fn reducible_balance(
asset: Self::AssetId,
who: &<T as SystemConfig>::AccountId,
preservation: Preservation,
_: Fortitude,
) -> Self::Balance {
Pallet::<T, I>::reducible_balance(asset, who, !matches!(preservation, Expendable))
.unwrap_or(Zero::zero())
}
fn can_deposit(
asset: Self::AssetId,
who: &<T as SystemConfig>::AccountId,
amount: Self::Balance,
provenance: Provenance,
) -> DepositConsequence {
Pallet::<T, I>::can_increase(asset, who, amount, provenance == Minted)
}
fn can_withdraw(
asset: Self::AssetId,
who: &<T as SystemConfig>::AccountId,
amount: Self::Balance,
) -> WithdrawConsequence<Self::Balance> {
Pallet::<T, I>::can_decrease(asset, who, amount, false)
}
fn asset_exists(asset: Self::AssetId) -> bool {
Asset::<T, I>::contains_key(asset)
}
}
impl<T: Config<I>, I: 'static> fungibles::Mutate<<T as SystemConfig>::AccountId> for Pallet<T, I> {
fn done_mint_into(
asset_id: Self::AssetId,
beneficiary: &<T as SystemConfig>::AccountId,
amount: Self::Balance,
) {
Self::deposit_event(Event::Issued { asset_id, owner: beneficiary.clone(), amount })
}
fn done_burn_from(
asset_id: Self::AssetId,
target: &<T as SystemConfig>::AccountId,
balance: Self::Balance,
) {
Self::deposit_event(Event::Burned { asset_id, owner: target.clone(), balance });
}
fn done_transfer(
asset_id: Self::AssetId,
source: &<T as SystemConfig>::AccountId,
dest: &<T as SystemConfig>::AccountId,
amount: Self::Balance,
) {
Self::deposit_event(Event::Transferred {
asset_id,
from: source.clone(),
to: dest.clone(),
amount,
});
}
}
impl<T: Config<I>, I: 'static> fungibles::Balanced<<T as SystemConfig>::AccountId>
for Pallet<T, I>
{
type OnDropCredit = fungibles::DecreaseIssuance<T::AccountId, Self>;
type OnDropDebt = fungibles::IncreaseIssuance<T::AccountId, Self>;
fn done_deposit(
asset_id: Self::AssetId,
who: &<T as SystemConfig>::AccountId,
amount: Self::Balance,
) {
Self::deposit_event(Event::Deposited { asset_id, who: who.clone(), amount })
}
fn done_withdraw(
asset_id: Self::AssetId,
who: &<T as SystemConfig>::AccountId,
amount: Self::Balance,
) {
Self::deposit_event(Event::Withdrawn { asset_id, who: who.clone(), amount })
}
}
impl<T: Config<I>, I: 'static> fungibles::Unbalanced<T::AccountId> for Pallet<T, I> {
fn handle_raw_dust(_: Self::AssetId, _: Self::Balance) {}
fn handle_dust(_: fungibles::Dust<T::AccountId, Self>) {
defensive!("`decrease_balance` and `increase_balance` have non-default impls; nothing else calls this; qed");
}
fn write_balance(
_: Self::AssetId,
_: &T::AccountId,
_: Self::Balance,
) -> Result<Option<Self::Balance>, DispatchError> {
defensive!("write_balance is not used if other functions are impl'd");
Err(DispatchError::Unavailable)
}
fn set_total_issuance(id: T::AssetId, amount: Self::Balance) {
Asset::<T, I>::mutate_exists(id, |maybe_asset| {
if let Some(ref mut asset) = maybe_asset {
asset.supply = amount
}
});
}
fn decrease_balance(
asset: T::AssetId,
who: &T::AccountId,
amount: Self::Balance,
precision: Precision,
preservation: Preservation,
_: Fortitude,
) -> Result<Self::Balance, DispatchError> {
let f = DebitFlags {
keep_alive: preservation != Expendable,
best_effort: precision == BestEffort,
};
Self::decrease_balance(asset, who, amount, f, |_, _| Ok(()))
}
fn increase_balance(
asset: T::AssetId,
who: &T::AccountId,
amount: Self::Balance,
_: Precision,
) -> Result<Self::Balance, DispatchError> {
Self::increase_balance(asset, who, amount, |_| Ok(()))?;
Ok(amount)
}
// TODO: #13196 implement deactivate/reactivate once we have inactive balance tracking.
}
impl<T: Config<I>, I: 'static> fungibles::Create<T::AccountId> for Pallet<T, I> {
fn create(
id: T::AssetId,
admin: T::AccountId,
is_sufficient: bool,
min_balance: Self::Balance,
) -> DispatchResult {
Self::do_force_create(id, admin, is_sufficient, min_balance)
}
}
impl<T: Config<I>, I: 'static> fungibles::Destroy<T::AccountId> for Pallet<T, I> {
fn start_destroy(id: T::AssetId, maybe_check_owner: Option<T::AccountId>) -> DispatchResult {
Self::do_start_destroy(id, maybe_check_owner)
}
fn destroy_accounts(id: T::AssetId, max_items: u32) -> Result<u32, DispatchError> {
Self::do_destroy_accounts(id, max_items)
}
fn destroy_approvals(id: T::AssetId, max_items: u32) -> Result<u32, DispatchError> {
Self::do_destroy_approvals(id, max_items)
}
fn finish_destroy(id: T::AssetId) -> DispatchResult {
Self::do_finish_destroy(id)
}
}
impl<T: Config<I>, I: 'static> fungibles::metadata::Inspect<<T as SystemConfig>::AccountId>
for Pallet<T, I>
{
fn name(asset: T::AssetId) -> Vec<u8> {
Metadata::<T, I>::get(asset).name.to_vec()
}
fn symbol(asset: T::AssetId) -> Vec<u8> {
Metadata::<T, I>::get(asset).symbol.to_vec()
}
fn decimals(asset: T::AssetId) -> u8 {
Metadata::<T, I>::get(asset).decimals
}
}
impl<T: Config<I>, I: 'static> fungibles::metadata::Mutate<<T as SystemConfig>::AccountId>
for Pallet<T, I>
{
fn set(
asset: T::AssetId,
from: &<T as SystemConfig>::AccountId,
name: Vec<u8>,
symbol: Vec<u8>,
decimals: u8,
) -> DispatchResult {
Self::do_set_metadata(asset, from, name, symbol, decimals)
}
}
impl<T: Config<I>, I: 'static>
fungibles::metadata::MetadataDeposit<
<T::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance,
> for Pallet<T, I>
{
fn calc_metadata_deposit(
name: &[u8],
symbol: &[u8],
) -> <T::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance {
Self::calc_metadata_deposit(&name, &symbol)
}
}
impl<T: Config<I>, I: 'static> fungibles::approvals::Inspect<<T as SystemConfig>::AccountId>
for Pallet<T, I>
{
// Check the amount approved to be spent by an owner to a delegate
fn allowance(
asset: T::AssetId,
owner: &<T as SystemConfig>::AccountId,
delegate: &<T as SystemConfig>::AccountId,
) -> T::Balance {
Approvals::<T, I>::get((asset, &owner, &delegate))
.map(|x| x.amount)
.unwrap_or_else(Zero::zero)
}
}
impl<T: Config<I>, I: 'static> fungibles::approvals::Mutate<<T as SystemConfig>::AccountId>
for Pallet<T, I>
{
// Approve spending tokens from a given account
fn approve(
asset: T::AssetId,
owner: &<T as SystemConfig>::AccountId,
delegate: &<T as SystemConfig>::AccountId,
amount: T::Balance,
) -> DispatchResult {
Self::do_approve_transfer(asset, owner, delegate, amount)
}
fn transfer_from(
asset: T::AssetId,
owner: &<T as SystemConfig>::AccountId,
delegate: &<T as SystemConfig>::AccountId,
dest: &<T as SystemConfig>::AccountId,
amount: T::Balance,
) -> DispatchResult {
Self::do_transfer_approved(asset, owner, delegate, dest, amount)
}
}
impl<T: Config<I>, I: 'static> fungibles::roles::Inspect<<T as SystemConfig>::AccountId>
for Pallet<T, I>
{
fn owner(asset: T::AssetId) -> Option<<T as SystemConfig>::AccountId> {
Asset::<T, I>::get(asset).map(|x| x.owner)
}
fn issuer(asset: T::AssetId) -> Option<<T as SystemConfig>::AccountId> {
Asset::<T, I>::get(asset).map(|x| x.issuer)
}
fn admin(asset: T::AssetId) -> Option<<T as SystemConfig>::AccountId> {
Asset::<T, I>::get(asset).map(|x| x.admin)
}
fn freezer(asset: T::AssetId) -> Option<<T as SystemConfig>::AccountId> {
Asset::<T, I>::get(asset).map(|x| x.freezer)
}
}
impl<T: Config<I>, I: 'static> fungibles::InspectEnumerable<T::AccountId> for Pallet<T, I> {
type AssetsIterator = KeyPrefixIterator<<T as Config<I>>::AssetId>;
/// Returns an iterator of the assets in existence.
///
/// NOTE: iterating this list invokes a storage read per item.
fn asset_ids() -> Self::AssetsIterator {
Asset::<T, I>::iter_keys()
}
}
impl<T: Config<I>, I: 'static> fungibles::roles::ResetTeam<T::AccountId> for Pallet<T, I> {
fn reset_team(
id: T::AssetId,
owner: T::AccountId,
admin: T::AccountId,
issuer: T::AccountId,
freezer: T::AccountId,
) -> DispatchResult {
Self::do_reset_team(id, owner, admin, issuer, freezer)
}
}
impl<T: Config<I>, I: 'static> fungibles::Refund<T::AccountId> for Pallet<T, I> {
type AssetId = T::AssetId;
type Balance = DepositBalanceOf<T, I>;
fn deposit_held(id: Self::AssetId, who: T::AccountId) -> Option<(T::AccountId, Self::Balance)> {
use ExistenceReason::*;
match Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit).ok()?.reason {
DepositHeld(b) => Some((who, b)),
DepositFrom(d, b) => Some((d, b)),
_ => None,
}
}
fn refund(id: Self::AssetId, who: T::AccountId) -> DispatchResult {
match Self::deposit_held(id.clone(), who.clone()) {
Some((d, _)) if d == who => Self::do_refund(id, who, false),
Some(..) => Self::do_refund_other(id, &who, None),
None => Err(Error::<T, I>::NoDeposit.into()),
}
}
}
@@ -0,0 +1,54 @@
// 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 pallet's `StoredMap` implementation.
use super::*;
impl<T: Config<I>, I: 'static> StoredMap<(T::AssetId, T::AccountId), T::Extra> for Pallet<T, I> {
fn get(id_who: &(T::AssetId, T::AccountId)) -> T::Extra {
let (id, who) = id_who;
Account::<T, I>::get(id, who).map(|a| a.extra).unwrap_or_default()
}
fn try_mutate_exists<R, E: From<DispatchError>>(
id_who: &(T::AssetId, T::AccountId),
f: impl FnOnce(&mut Option<T::Extra>) -> Result<R, E>,
) -> Result<R, E> {
let (id, who) = id_who;
let mut maybe_extra = Account::<T, I>::get(id, who).map(|a| a.extra);
let r = f(&mut maybe_extra)?;
// They want to write some value or delete it.
// If the account existed and they want to write a value, then we write.
// If the account didn't exist and they want to delete it, then we let it pass.
// Otherwise, we fail.
Account::<T, I>::try_mutate(id, who, |maybe_account| {
if let Some(extra) = maybe_extra {
// They want to write a value. Let this happen only if the account actually exists.
if let Some(ref mut account) = maybe_account {
account.extra = extra;
} else {
return Err(DispatchError::NoProviders.into());
}
} else {
// They want to delete it. Let this pass if the item never existed anyway.
ensure!(maybe_account.is_none(), DispatchError::ConsumerRemaining);
}
Ok(r)
})
}
}
File diff suppressed because it is too large Load Diff
+162
View File
@@ -0,0 +1,162 @@
// 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::OnRuntimeUpgrade;
use log;
#[cfg(feature = "try-runtime")]
use pezsp_runtime::TryRuntimeError;
pub mod next_asset_id {
use super::*;
use pezsp_core::Get;
/// Set [`NextAssetId`] to the value of `ID` if [`NextAssetId`] does not exist yet.
pub struct SetNextAssetId<ID, T: Config<I>, I: 'static = ()>(
core::marker::PhantomData<(ID, T, I)>,
);
impl<ID, T: Config<I>, I: 'static> OnRuntimeUpgrade for SetNextAssetId<ID, T, I>
where
T::AssetId: Incrementable,
ID: Get<T::AssetId>,
{
fn on_runtime_upgrade() -> pezframe_support::weights::Weight {
if !NextAssetId::<T, I>::exists() {
NextAssetId::<T, I>::put(ID::get());
T::DbWeight::get().reads_writes(1, 1)
} else {
T::DbWeight::get().reads(1)
}
}
}
}
pub mod v1 {
use pezframe_support::{pezpallet_prelude::*, weights::Weight};
use super::*;
#[derive(Decode)]
pub struct OldAssetDetails<Balance, AccountId, DepositBalance> {
pub owner: AccountId,
pub issuer: AccountId,
pub admin: AccountId,
pub freezer: AccountId,
pub supply: Balance,
pub deposit: DepositBalance,
pub min_balance: Balance,
pub is_sufficient: bool,
pub accounts: u32,
pub sufficients: u32,
pub approvals: u32,
pub is_frozen: bool,
}
impl<Balance, AccountId, DepositBalance> OldAssetDetails<Balance, AccountId, DepositBalance> {
fn migrate_to_v1(self) -> AssetDetails<Balance, AccountId, DepositBalance> {
let status = if self.is_frozen { AssetStatus::Frozen } else { AssetStatus::Live };
AssetDetails {
owner: self.owner,
issuer: self.issuer,
admin: self.admin,
freezer: self.freezer,
supply: self.supply,
deposit: self.deposit,
min_balance: self.min_balance,
is_sufficient: self.is_sufficient,
accounts: self.accounts,
sufficients: self.sufficients,
approvals: self.approvals,
status,
}
}
}
pub struct MigrateToV1<T>(core::marker::PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
fn on_runtime_upgrade() -> Weight {
let in_code_version = Pallet::<T>::in_code_storage_version();
let on_chain_version = Pallet::<T>::on_chain_storage_version();
if on_chain_version == 0 && in_code_version == 1 {
let mut translated = 0u64;
Asset::<T>::translate::<
OldAssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T>>,
_,
>(|_key, old_value| {
translated.saturating_inc();
Some(old_value.migrate_to_v1())
});
in_code_version.put::<Pallet<T>>();
log::info!(
target: LOG_TARGET,
"Upgraded {} pools, storage to version {:?}",
translated,
in_code_version
);
T::DbWeight::get().reads_writes(translated + 1, translated + 1)
} else {
log::info!(
target: LOG_TARGET,
"Migration did not execute. This probably should be removed"
);
T::DbWeight::get().reads(1)
}
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
pezframe_support::ensure!(
Pallet::<T>::on_chain_storage_version() == 0,
"must upgrade linearly"
);
let prev_count = Asset::<T>::iter().count();
Ok((prev_count as u32).encode())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(prev_count: Vec<u8>) -> Result<(), TryRuntimeError> {
let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect(
"the state parameter should be something that was generated by pre_upgrade",
);
let post_count = Asset::<T>::iter().count() as u32;
ensure!(
prev_count == post_count,
"the asset count before and after the migration should be the same"
);
let in_code_version = Pallet::<T>::in_code_storage_version();
let on_chain_version = Pallet::<T>::on_chain_storage_version();
pezframe_support::ensure!(in_code_version == 1, "must_upgrade");
ensure!(
in_code_version == on_chain_version,
"after migration, the in_code_version and on_chain_version should be the same"
);
Asset::<T>::iter().try_for_each(|(_id, asset)| -> Result<(), TryRuntimeError> {
ensure!(
asset.status == AssetStatus::Live || asset.status == AssetStatus::Frozen,
"assets should only be live or frozen. None should be in destroying status, or undefined state"
);
Ok(())
})?;
Ok(())
}
}
}
+245
View File
@@ -0,0 +1,245 @@
// 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.
//! Test environment for Assets pallet.
use super::*;
use crate as pezpallet_assets;
use codec::Encode;
use pezframe_support::{
assert_ok, construct_runtime, derive_impl, parameter_types,
traits::{AsEnsureOriginWithArg, ConstU32},
};
use pezsp_io::storage;
use pezsp_runtime::BuildStorage;
type Block = pezframe_system::mocking::MockBlock<Test>;
construct_runtime!(
pub enum Test
{
System: pezframe_system,
Balances: pezpallet_balances,
Assets: pezpallet_assets,
}
);
type AccountId = u64;
type AssetId = u32;
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Test {
type Block = Block;
type AccountData = pezpallet_balances::AccountData<u64>;
type MaxConsumers = ConstU32<3>;
}
#[derive_impl(pezpallet_balances::config_preludes::TestDefaultConfig)]
impl pezpallet_balances::Config for Test {
type AccountStore = System;
}
pub struct AssetsCallbackHandle;
impl AssetsCallback<AssetId, AccountId> for AssetsCallbackHandle {
fn created(_id: &AssetId, _owner: &AccountId) -> Result<(), ()> {
if Self::should_err() {
Err(())
} else {
storage::set(Self::CREATED.as_bytes(), &().encode());
Ok(())
}
}
fn destroyed(_id: &AssetId) -> Result<(), ()> {
if Self::should_err() {
Err(())
} else {
storage::set(Self::DESTROYED.as_bytes(), &().encode());
Ok(())
}
}
}
impl AssetsCallbackHandle {
pub const CREATED: &'static str = "asset_created";
pub const DESTROYED: &'static str = "asset_destroyed";
const RETURN_ERROR: &'static str = "return_error";
// Configures `Self` to return `Ok` when callbacks are invoked
pub fn set_return_ok() {
storage::clear(Self::RETURN_ERROR.as_bytes());
}
// Configures `Self` to return `Err` when callbacks are invoked
pub fn set_return_error() {
storage::set(Self::RETURN_ERROR.as_bytes(), &().encode());
}
// If `true`, callback should return `Err`, `Ok` otherwise.
fn should_err() -> bool {
storage::exists(Self::RETURN_ERROR.as_bytes())
}
}
#[derive_impl(crate::config_preludes::TestDefaultConfig)]
impl Config for Test {
type Currency = Balances;
type CreateOrigin = AsEnsureOriginWithArg<pezframe_system::EnsureSigned<u64>>;
type ForceOrigin = pezframe_system::EnsureRoot<u64>;
type Freezer = TestFreezer;
type Holder = TestHolder;
type CallbackHandle = (AssetsCallbackHandle, AutoIncAssetId<Test>);
type ReserveData = u128;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = AssetsBenchmarkHelper;
}
#[cfg(feature = "runtime-benchmarks")]
pub struct AssetsBenchmarkHelper;
#[cfg(feature = "runtime-benchmarks")]
impl<AssetIdParameter: From<u32>, ReserveIdParameter: From<u32>>
BenchmarkHelper<AssetIdParameter, ReserveIdParameter> for AssetsBenchmarkHelper
{
fn create_asset_id_parameter(id: u32) -> AssetIdParameter {
id.into()
}
fn create_reserve_id_parameter(id: u32) -> ReserveIdParameter {
id.into()
}
}
use std::collections::HashMap;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Hook {
Died(u32, u64),
}
parameter_types! {
static Frozen: HashMap<(u32, u64), u64> = Default::default();
static OnHold: HashMap<(u32, u64), u64> = Default::default();
static Hooks: Vec<Hook> = Default::default();
}
pub struct TestHolder;
impl BalanceOnHold<u32, u64, u64> for TestHolder {
fn balance_on_hold(asset: u32, who: &u64) -> Option<u64> {
OnHold::get().get(&(asset, *who)).cloned()
}
fn died(asset: u32, who: &u64) {
Hooks::mutate(|v| v.push(Hook::Died(asset, *who)))
}
fn contains_holds(asset: AssetId) -> bool {
OnHold::get().iter().any(|((k, _), _)| &asset == k)
}
}
pub(crate) fn set_balance_on_hold(asset: u32, who: u64, amount: u64) {
OnHold::mutate(|v| {
let amount_on_hold = v.get(&(asset, who)).unwrap_or(&0);
if &amount > amount_on_hold {
// Hold more funds
let amount = amount - amount_on_hold;
let f = DebitFlags { keep_alive: true, best_effort: false };
assert_ok!(Assets::decrease_balance(asset, &who, amount, f, |_, _| Ok(())));
} else {
// Release funds on hold
let amount = amount_on_hold - amount;
assert_ok!(Assets::increase_balance(asset, &who, amount, |_| Ok(())));
}
// Asset amount still "exists", we just store it here
v.insert((asset, who), amount);
});
}
pub(crate) fn clear_balance_on_hold(asset: u32, who: u64) {
OnHold::mutate(|v| {
v.remove(&(asset, who));
});
}
pub struct TestFreezer;
impl FrozenBalance<u32, u64, u64> for TestFreezer {
fn frozen_balance(asset: u32, who: &u64) -> Option<u64> {
Frozen::get().get(&(asset, *who)).cloned()
}
fn died(asset: u32, who: &u64) {
Hooks::mutate(|v| v.push(Hook::Died(asset, *who)));
// Sanity check: dead accounts have no balance.
assert!(Assets::balance(asset, *who).is_zero());
}
/// Return a value that indicates if there are registered freezes for a given asset.
fn contains_freezes(asset: AssetId) -> bool {
Frozen::get().iter().any(|((k, _), _)| &asset == k)
}
}
pub(crate) fn set_frozen_balance(asset: u32, who: u64, amount: u64) {
Frozen::mutate(|v| {
v.insert((asset, who), amount);
});
}
pub(crate) fn clear_frozen_balance(asset: u32, who: u64) {
Frozen::mutate(|v| {
v.remove(&(asset, who));
});
}
pub(crate) fn hooks() -> Vec<Hook> {
Hooks::get().clone()
}
pub(crate) fn take_hooks() -> Vec<Hook> {
Hooks::take()
}
pub(crate) fn new_test_ext() -> pezsp_io::TestExternalities {
let mut storage = pezframe_system::GenesisConfig::<Test>::default().build_storage().unwrap();
let config: pezpallet_assets::GenesisConfig<Test> = pezpallet_assets::GenesisConfig {
assets: vec![
// id, owner, is_sufficient, min_balance
(999, 0, true, 1),
],
metadata: vec![
// id, name, symbol, decimals
(999, "Token Name".into(), "TOKEN".into(), 10),
],
accounts: vec![
// id, account_id, balance
(999, 1, 100),
],
next_asset_id: None,
reserves: vec![],
};
config.assimilate_storage(&mut storage).unwrap();
let mut ext: pezsp_io::TestExternalities = storage.into();
// Clear thread local vars for https://github.com/pezkuwichain/kurdistan-sdk/issues/2.
ext.execute_with(|| take_hooks());
ext.execute_with(|| System::set_block_number(1));
ext
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,358 @@
// 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 [`ItemOf`], [`fungible::UnionOf`] and [`fungibles::UnionOf`] set types.
use super::*;
use pezframe_support::{
parameter_types,
traits::{
fungible,
fungible::ItemOf,
fungibles,
tokens::{
fungibles::{
Balanced as FungiblesBalanced, Create as FungiblesCreate,
Inspect as FungiblesInspect, Mutate as FungiblesMutate,
},
Fortitude, Precision, Preservation,
},
},
};
use pezsp_runtime::{traits::ConvertToValue, Either};
const FIRST_ASSET: u32 = 0;
const UNKNOWN_ASSET: u32 = 10;
parameter_types! {
pub const LeftAsset: Either<(), u32> = Either::Left(());
pub const RightAsset: Either<u32, ()> = Either::Right(());
pub const RightUnitAsset: Either<(), ()> = Either::Right(());
}
/// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a
/// single asset class from [`T`] identified by [`FIRST_ASSET`].
type FirstFungible<T> = ItemOf<T, pezframe_support::traits::ConstU32<{ FIRST_ASSET }>, u64>;
/// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a
/// single asset class from [`T`] identified by [`UNKNOWN_ASSET`].
type UnknownFungible<T> = ItemOf<T, pezframe_support::traits::ConstU32<{ UNKNOWN_ASSET }>, u64>;
/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
/// the [`FirstFungible`] from the left.
type LeftFungible<T> = fungible::UnionOf<FirstFungible<T>, T, ConvertToValue<LeftAsset>, (), u64>;
/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
/// the [`LeftFungible`] from the right.
type RightFungible<T> =
fungible::UnionOf<UnknownFungible<T>, LeftFungible<T>, ConvertToValue<RightUnitAsset>, (), u64>;
/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
/// the [`RightFungible`] from the left.
type LeftFungibles<T> = fungibles::UnionOf<RightFungible<T>, T, ConvertToValue<LeftAsset>, (), u64>;
/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
/// the [`LeftFungibles`] from the right.
///
/// By using this type, we can navigate through each branch of [`fungible::UnionOf`],
/// [`fungibles::UnionOf`], and [`ItemOf`] to access the underlying `fungibles::*`
/// implementation provided by the pallet.
type First<T> = fungibles::UnionOf<T, LeftFungibles<T>, ConvertToValue<RightAsset>, (), u64>;
#[test]
fn deposit_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1 = 0;
let account1 = 1;
let account2 = 2;
assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
assert_ok!(Assets::mint_into(asset1, &account1, 100));
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
let imb = First::<Assets>::deposit((), &account2, 50, Precision::Exact).unwrap();
assert_eq!(First::<Assets>::balance((), &account2), 50);
assert_eq!(First::<Assets>::total_issuance(()), 100);
System::assert_has_event(RuntimeEvent::Assets(crate::Event::Deposited {
asset_id: asset1,
who: account2,
amount: 50,
}));
assert_eq!(imb.peek(), 50);
let (imb1, imb2) = imb.split(30);
assert_eq!(imb1.peek(), 30);
assert_eq!(imb2.peek(), 20);
drop(imb2);
assert_eq!(First::<Assets>::total_issuance(()), 120);
assert!(First::<Assets>::settle(&account1, imb1, Preservation::Preserve).is_ok());
assert_eq!(First::<Assets>::balance((), &account1), 70);
assert_eq!(First::<Assets>::balance((), &account2), 50);
assert_eq!(First::<Assets>::total_issuance(()), 120);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}
#[test]
fn issue_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1: u32 = 0;
let account1: u64 = 1;
assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
assert_ok!(Assets::mint_into(asset1, &account1, 100));
assert_eq!(First::<Assets>::balance((), &account1), 100);
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
let imb = First::<Assets>::issue((), 100);
assert_eq!(First::<Assets>::total_issuance(()), 200);
assert_eq!(imb.peek(), 100);
let (imb1, imb2) = imb.split(30);
assert_eq!(imb1.peek(), 30);
assert_eq!(imb2.peek(), 70);
drop(imb2);
assert_eq!(First::<Assets>::total_issuance(()), 130);
assert!(First::<Assets>::resolve(&account1, imb1).is_ok());
assert_eq!(First::<Assets>::balance((), &account1), 130);
assert_eq!(First::<Assets>::total_issuance(()), 130);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}
#[test]
fn pair_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1: u32 = 0;
let account1: u64 = 1;
assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
assert_ok!(Assets::mint_into(asset1, &account1, 100));
assert_eq!(First::<Assets>::balance((), &account1), 100);
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
let (debt, credit) = First::<Assets>::pair((), 100).unwrap();
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(debt.peek(), 100);
assert_eq!(credit.peek(), 100);
let (debt1, debt2) = debt.split(30);
assert_eq!(debt1.peek(), 30);
assert_eq!(debt2.peek(), 70);
drop(debt2);
assert_eq!(First::<Assets>::total_issuance(()), 170);
assert!(First::<Assets>::settle(&account1, debt1, Preservation::Preserve).is_ok());
assert_eq!(First::<Assets>::balance((), &account1), 70);
assert_eq!(First::<Assets>::total_issuance(()), 170);
let (credit1, credit2) = credit.split(40);
assert_eq!(credit1.peek(), 40);
assert_eq!(credit2.peek(), 60);
drop(credit2);
assert_eq!(First::<Assets>::total_issuance(()), 110);
assert!(First::<Assets>::resolve(&account1, credit1).is_ok());
assert_eq!(First::<Assets>::balance((), &account1), 110);
assert_eq!(First::<Assets>::total_issuance(()), 110);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}
#[test]
fn rescind_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1: u32 = 0;
let account1: u64 = 1;
assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
assert_ok!(Assets::mint_into(asset1, &account1, 100));
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
let imb = First::<Assets>::rescind((), 20);
assert_eq!(First::<Assets>::total_issuance(()), 80);
assert_eq!(imb.peek(), 20);
let (imb1, imb2) = imb.split(15);
assert_eq!(imb1.peek(), 15);
assert_eq!(imb2.peek(), 5);
drop(imb2);
assert_eq!(First::<Assets>::total_issuance(()), 85);
assert!(First::<Assets>::settle(&account1, imb1, Preservation::Preserve).is_ok());
assert_eq!(First::<Assets>::balance((), &account1), 85);
assert_eq!(First::<Assets>::total_issuance(()), 85);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}
#[test]
fn resolve_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1: u32 = 0;
let account1: u64 = 1;
let account2: u64 = 2;
let ed = 11;
assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, ed));
assert_ok!(Assets::mint_into(asset1, &account1, 100));
assert_eq!(First::<Assets>::balance((), &account1), 100);
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
let imb = First::<Assets>::issue((), 100);
assert_eq!(First::<Assets>::total_issuance(()), 200);
assert_eq!(imb.peek(), 100);
let (imb1, imb2) = imb.split(10);
assert_eq!(imb1.peek(), 10);
assert_eq!(imb2.peek(), 90);
assert_eq!(First::<Assets>::total_issuance(()), 200);
// ed requirements not met.
let imb1 = First::<Assets>::resolve(&account2, imb1).unwrap_err();
assert_eq!(imb1.peek(), 10);
drop(imb1);
assert_eq!(First::<Assets>::total_issuance(()), 190);
assert_eq!(First::<Assets>::balance((), &account2), 0);
// resolve to new account `2`.
assert_ok!(First::<Assets>::resolve(&account2, imb2));
assert_eq!(First::<Assets>::total_issuance(()), 190);
assert_eq!(First::<Assets>::balance((), &account2), 90);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}
#[test]
fn settle_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1: u32 = 0;
let account1: u64 = 1;
let account2: u64 = 2;
let ed = 11;
assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, ed));
assert_ok!(Assets::mint_into(asset1, &account1, 100));
assert_ok!(Assets::mint_into(asset1, &account2, 100));
assert_eq!(First::<Assets>::balance((), &account2), 100);
assert_eq!(First::<Assets>::total_issuance(()), 200);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
let imb = First::<Assets>::rescind((), 100);
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(imb.peek(), 100);
let (imb1, imb2) = imb.split(10);
assert_eq!(imb1.peek(), 10);
assert_eq!(imb2.peek(), 90);
assert_eq!(First::<Assets>::total_issuance(()), 100);
// ed requirements not met.
let imb2 = First::<Assets>::settle(&account2, imb2, Preservation::Preserve).unwrap_err();
assert_eq!(imb2.peek(), 90);
drop(imb2);
assert_eq!(First::<Assets>::total_issuance(()), 190);
assert_eq!(First::<Assets>::balance((), &account2), 100);
// settle to account `1`.
assert_ok!(First::<Assets>::settle(&account2, imb1, Preservation::Preserve));
assert_eq!(First::<Assets>::total_issuance(()), 190);
assert_eq!(First::<Assets>::balance((), &account2), 90);
let imb = First::<Assets>::rescind((), 85);
assert_eq!(First::<Assets>::total_issuance(()), 105);
assert_eq!(imb.peek(), 85);
// settle to account `1` and expect some dust.
let imb = First::<Assets>::settle(&account2, imb, Preservation::Expendable).unwrap();
assert_eq!(imb.peek(), 5);
assert_eq!(First::<Assets>::total_issuance(()), 105);
assert_eq!(First::<Assets>::balance((), &account2), 0);
drop(imb);
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}
#[test]
fn withdraw_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1 = 0;
let account1 = 1;
let account2 = 2;
assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
assert_ok!(Assets::mint_into(asset1, &account1, 100));
assert_ok!(Assets::mint_into(asset1, &account2, 100));
assert_eq!(First::<Assets>::total_issuance(()), 200);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
let imb = First::<Assets>::withdraw(
(),
&account2,
50,
Precision::Exact,
Preservation::Preserve,
Fortitude::Polite,
)
.unwrap();
assert_eq!(First::<Assets>::balance((), &account2), 50);
assert_eq!(First::<Assets>::total_issuance(()), 200);
System::assert_has_event(RuntimeEvent::Assets(crate::Event::Withdrawn {
asset_id: asset1,
who: account2,
amount: 50,
}));
assert_eq!(imb.peek(), 50);
drop(imb);
assert_eq!(First::<Assets>::total_issuance(()), 150);
assert_eq!(First::<Assets>::balance((), &account2), 50);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}
+361
View File
@@ -0,0 +1,361 @@
// 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.
//! Various basic types for use in the assets pallet.
use super::*;
use pezframe_support::{
pezpallet_prelude::*,
traits::{fungible, tokens::ConversionToAssetBalance},
};
use pezsp_runtime::{traits::Convert, FixedPointNumber, FixedU128};
pub type DepositBalanceOf<T, I = ()> =
<<T as Config<I>>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;
pub type AssetAccountOf<T, I> = AssetAccount<
<T as Config<I>>::Balance,
DepositBalanceOf<T, I>,
<T as Config<I>>::Extra,
<T as SystemConfig>::AccountId,
>;
pub type ExistenceReasonOf<T, I> =
ExistenceReason<DepositBalanceOf<T, I>, <T as SystemConfig>::AccountId>;
/// AssetStatus holds the current state of the asset. It could either be Live and available for use,
/// or in a Destroying state.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub enum AssetStatus {
/// The asset is active and able to be used.
Live,
/// Whether the asset is frozen for non-admin transfers.
Frozen,
/// The asset is currently being destroyed, and all actions are no longer permitted on the
/// asset. Once set to `Destroying`, the asset can never transition back to a `Live` state.
Destroying,
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct AssetDetails<Balance, AccountId, DepositBalance> {
/// Can change `owner`, `issuer`, `freezer` and `admin` accounts.
pub owner: AccountId,
/// Can mint tokens.
pub issuer: AccountId,
/// Can thaw tokens, force transfers and burn tokens from any account.
pub admin: AccountId,
/// Can freeze tokens.
pub freezer: AccountId,
/// The total supply across all accounts.
pub supply: Balance,
/// The balance deposited for this asset. This pays for the data stored here.
pub deposit: DepositBalance,
/// The ED for virtual accounts.
pub min_balance: Balance,
/// If `true`, then any account with this asset is given a provider reference. Otherwise, it
/// requires a consumer reference.
pub is_sufficient: bool,
/// The total number of accounts.
pub accounts: u32,
/// The total number of accounts for which we have placed a self-sufficient reference.
pub sufficients: u32,
/// The total number of approvals.
pub approvals: u32,
/// The status of the asset
pub status: AssetStatus,
}
/// Data concerning an approval.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, MaxEncodedLen, TypeInfo)]
pub struct Approval<Balance, DepositBalance> {
/// The amount of funds approved for the balance transfer from the owner to some delegated
/// target.
pub amount: Balance,
/// The amount reserved on the owner's account to hold this item in storage.
pub deposit: DepositBalance,
}
#[test]
fn ensure_bool_decodes_to_consumer_or_sufficient() {
assert_eq!(false.encode(), ExistenceReason::<(), ()>::Consumer.encode());
assert_eq!(true.encode(), ExistenceReason::<(), ()>::Sufficient.encode());
}
/// The reason for an account's existence within an asset class.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub enum ExistenceReason<Balance, AccountId> {
/// A consumer reference was used to create this account.
#[codec(index = 0)]
Consumer,
/// The asset class is `sufficient` for account existence.
#[codec(index = 1)]
Sufficient,
/// The account holder has placed a deposit to exist within an asset class.
#[codec(index = 2)]
DepositHeld(Balance),
/// A deposit was placed for this account to exist, but it has been refunded.
#[codec(index = 3)]
DepositRefunded,
/// Some other `AccountId` has placed a deposit to make this account exist.
/// An account with such a reason might not be referenced in `system`.
#[codec(index = 4)]
DepositFrom(AccountId, Balance),
}
impl<Balance, AccountId> ExistenceReason<Balance, AccountId>
where
AccountId: Clone,
{
pub fn take_deposit(&mut self) -> Option<Balance> {
if !matches!(self, ExistenceReason::DepositHeld(_)) {
return None;
}
if let ExistenceReason::DepositHeld(deposit) =
core::mem::replace(self, ExistenceReason::DepositRefunded)
{
Some(deposit)
} else {
None
}
}
pub fn take_deposit_from(&mut self) -> Option<(AccountId, Balance)> {
if !matches!(self, ExistenceReason::DepositFrom(..)) {
return None;
}
if let ExistenceReason::DepositFrom(depositor, deposit) =
core::mem::replace(self, ExistenceReason::DepositRefunded)
{
Some((depositor, deposit))
} else {
None
}
}
}
#[test]
fn ensure_bool_decodes_to_liquid_or_frozen() {
assert_eq!(false.encode(), AccountStatus::Liquid.encode());
assert_eq!(true.encode(), AccountStatus::Frozen.encode());
}
/// The status of an asset account.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub enum AccountStatus {
/// Asset account can receive and transfer the assets.
Liquid,
/// Asset account cannot transfer the assets.
Frozen,
/// Asset account cannot receive and transfer the assets.
Blocked,
}
impl AccountStatus {
/// Returns `true` if frozen or blocked.
pub fn is_frozen(&self) -> bool {
matches!(self, AccountStatus::Frozen | AccountStatus::Blocked)
}
/// Returns `true` if blocked.
pub fn is_blocked(&self) -> bool {
matches!(self, AccountStatus::Blocked)
}
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct AssetAccount<Balance, DepositBalance, Extra, AccountId> {
/// The account's balance.
///
/// The part of the `balance` may be frozen by the [`Config::Freezer`]. The on-hold portion is
/// not included here and is tracked by the [`Config::Holder`].
pub balance: Balance,
/// The status of the account.
pub status: AccountStatus,
/// The reason for the existence of the account.
pub reason: ExistenceReason<DepositBalance, AccountId>,
/// Additional "sidecar" data, in case some other pallet wants to use this storage item.
pub extra: Extra,
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, Default, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct AssetMetadata<DepositBalance, BoundedString> {
/// The balance deposited for this metadata.
///
/// This pays for the data stored in this struct.
pub deposit: DepositBalance,
/// The user friendly name of this asset. Limited in length by `StringLimit`.
pub name: BoundedString,
/// The ticker symbol for this asset. Limited in length by `StringLimit`.
pub symbol: BoundedString,
/// The number of decimals this asset uses to represent one unit.
pub decimals: u8,
/// Whether the asset metadata may be changed by a non Force origin.
pub is_frozen: bool,
}
/// Trait for allowing a minimum balance on the account to be specified, beyond the
/// `minimum_balance` of the asset. This is additive - the `minimum_balance` of the asset must be
/// met *and then* anything here in addition.
pub trait FrozenBalance<AssetId, AccountId, Balance> {
/// Return the frozen balance.
///
/// Generally, the balance of every account must be at least the sum of this (if `Some`) and
/// the asset's `minimum_balance` (the latter since there may be complications to destroying an
/// asset's account completely).
///
/// Under normal behaviour, the account balance should not go below the sum of this (if `Some`)
/// and the asset's minimum balance. However, the account balance may reasonably begin below
/// this sum (e.g. if less than the sum had ever been transferred into the account).
///
/// In special cases (privileged intervention) the account balance may also go below the sum.
///
/// If `None` is returned, then nothing special is enforced.
fn frozen_balance(asset: AssetId, who: &AccountId) -> Option<Balance>;
/// Called after an account has been removed.
fn died(asset: AssetId, who: &AccountId);
/// Return a value that indicates if there are registered freezes for a given asset.
fn contains_freezes(asset: AssetId) -> bool;
}
impl<AssetId, AccountId, Balance> FrozenBalance<AssetId, AccountId, Balance> for () {
fn frozen_balance(_: AssetId, _: &AccountId) -> Option<Balance> {
None
}
fn died(_: AssetId, _: &AccountId) {}
fn contains_freezes(_: AssetId) -> bool {
false
}
}
/// This trait indicates a balance that is _on hold_ for an asset account.
///
/// A balance _on hold_ is a balance that, while is assigned to an account,
/// is outside the direct control of it. Instead, is being _held_ by the
/// system logic (i.e. Pallets) and can be eventually burned or released.
pub trait BalanceOnHold<AssetId, AccountId, Balance> {
/// Return the held balance.
///
/// If `Some`, it means some balance is _on hold_, and it can be
/// infallibly burned.
///
/// If `None` is returned, then no balance is _on hold_ for `who`'s asset
/// account.
fn balance_on_hold(asset: AssetId, who: &AccountId) -> Option<Balance>;
/// Called after an account has been removed.
///
/// It is expected that this method is called only when there is no balance
/// on hold. Otherwise, an account should not be removed.
fn died(asset: AssetId, who: &AccountId);
/// Return a value that indicates if there are registered holds for a given asset.
fn contains_holds(asset: AssetId) -> bool;
}
impl<AssetId, AccountId, Balance> BalanceOnHold<AssetId, AccountId, Balance> for () {
fn balance_on_hold(_: AssetId, _: &AccountId) -> Option<Balance> {
None
}
fn died(_: AssetId, _: &AccountId) {}
fn contains_holds(_: AssetId) -> bool {
false
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct TransferFlags {
/// The debited account must stay alive at the end of the operation; an error is returned if
/// this cannot be achieved legally.
pub keep_alive: bool,
/// Less than the amount specified needs be debited by the operation for it to be considered
/// successful. If `false`, then the amount debited will always be at least the amount
/// specified.
pub best_effort: bool,
/// Any additional funds debited (due to minimum balance requirements) should be burned rather
/// than credited to the destination account.
pub burn_dust: bool,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct DebitFlags {
/// The debited account must stay alive at the end of the operation; an error is returned if
/// this cannot be achieved legally.
pub keep_alive: bool,
/// Less than the amount specified needs be debited by the operation for it to be considered
/// successful. If `false`, then the amount debited will always be at least the amount
/// specified.
pub best_effort: bool,
}
impl From<TransferFlags> for DebitFlags {
fn from(f: TransferFlags) -> Self {
Self { keep_alive: f.keep_alive, best_effort: f.best_effort }
}
}
/// Possible errors when converting between external and asset balances.
#[derive(Eq, PartialEq, Copy, Clone, RuntimeDebug, Encode, Decode)]
pub enum ConversionError {
/// The external minimum balance must not be zero.
MinBalanceZero,
/// The asset is not present in storage.
AssetMissing,
/// The asset is not sufficient and thus does not have a reliable `min_balance` so it cannot be
/// converted.
AssetNotSufficient,
}
// Type alias for `pezframe_system`'s account id.
type AccountIdOf<T> = <T as pezframe_system::Config>::AccountId;
// This pallet's asset id and balance type.
type AssetIdOf<T, I> = <T as Config<I>>::AssetId;
type AssetBalanceOf<T, I> = <T as Config<I>>::Balance;
// Generic fungible balance type.
type BalanceOf<F, T> = <F as fungible::Inspect<AccountIdOf<T>>>::Balance;
/// Converts a balance value into an asset balance based on the ratio between the fungible's
/// minimum balance and the minimum asset balance.
pub struct BalanceToAssetBalance<F, T, CON, I = ()>(PhantomData<(F, T, CON, I)>);
impl<F, T, CON, I> ConversionToAssetBalance<BalanceOf<F, T>, AssetIdOf<T, I>, AssetBalanceOf<T, I>>
for BalanceToAssetBalance<F, T, CON, I>
where
F: fungible::Inspect<AccountIdOf<T>>,
T: Config<I>,
I: 'static,
CON: Convert<BalanceOf<F, T>, AssetBalanceOf<T, I>>,
{
type Error = ConversionError;
/// Convert the given balance value into an asset balance based on the ratio between the
/// fungible's minimum balance and the minimum asset balance.
///
/// Will return `Err` if the asset is not found, not sufficient or the fungible's minimum
/// balance is zero.
fn to_asset_balance(
balance: BalanceOf<F, T>,
asset_id: AssetIdOf<T, I>,
) -> Result<AssetBalanceOf<T, I>, ConversionError> {
let asset = Asset::<T, I>::get(asset_id).ok_or(ConversionError::AssetMissing)?;
// only sufficient assets have a min balance with reliable value
ensure!(asset.is_sufficient, ConversionError::AssetNotSufficient);
let min_balance = CON::convert(F::minimum_balance());
// make sure we don't divide by zero
ensure!(!min_balance.is_zero(), ConversionError::MinBalanceZero);
let balance = CON::convert(balance);
// balance * asset.min_balance / min_balance
Ok(FixedU128::saturating_from_rational(asset.min_balance, min_balance)
.saturating_mul_int(balance))
}
}
File diff suppressed because it is too large Load Diff