// This file is part of Bizinikiwi. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: MIT-0 // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //! # Assets Freezer Pezpallet //! //! A pezpallet capable of freezing fungibles from `pezpallet-assets`. This is an extension of //! `pezpallet-assets`, wrapping [`fungibles::Inspect`](`Inspect`). //! It implements both //! [`fungibles::freeze::Inspect`](InspectFreeze) and //! [`fungibles::freeze::Mutate`](MutateFreeze). The complexity //! of the operations is `O(n)`. where `n` is the variant count of `RuntimeFreezeReason`. //! //! ## Pezpallet API //! //! See the [`pezpallet`] module for more information about the interfaces this pezpallet exposes, //! including its configuration trait, dispatchables, storage items, events and errors. //! //! ## Overview //! //! This pezpallet provides the following functionality: //! //! - Pezpallet hooks allowing [`pezpallet-assets`] to know the frozen balance for an account on a //! given asset (see [`pezpallet_assets::FrozenBalance`]). //! - An implementation of [`fungibles::freeze::Inspect`](InspectFreeze) and //! [`fungibles::freeze::Mutate`](MutateFreeze), allowing other pallets to manage freezes for the //! `pezpallet-assets` assets. #![cfg_attr(not(feature = "std"), no_std)] use frame::{ prelude::*, traits::{ fungibles::{Inspect, InspectFreeze, MutateFreeze}, tokens::{ DepositConsequence, Fortitude, IdAmount, Preservation, Provenance, WithdrawConsequence, }, }, }; pub use pezpallet::*; #[cfg(feature = "try-runtime")] use frame::try_runtime::TryRuntimeError; #[cfg(test)] mod mock; #[cfg(test)] mod tests; mod impls; #[frame::pezpallet] pub mod pezpallet { use super::*; #[pezpallet::config(with_default)] pub trait Config: pezframe_system::Config + pezpallet_assets::Config { /// The overarching freeze reason. #[pezpallet::no_default_bounds] type RuntimeFreezeReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount; /// The overarching event type. #[pezpallet::no_default_bounds] #[allow(deprecated)] type RuntimeEvent: From> + IsType<::RuntimeEvent>; } #[pezpallet::error] pub enum Error { /// Number of freezes on an account would exceed `MaxFreezes`. TooManyFreezes, } #[pezpallet::pezpallet] pub struct Pezpallet(_); #[pezpallet::event] #[pezpallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event, I: 'static = ()> { // `who`s frozen balance was increased by `amount`. Frozen { who: T::AccountId, asset_id: T::AssetId, amount: T::Balance }, // `who`s frozen balance was decreased by `amount`. Thawed { who: T::AccountId, asset_id: T::AssetId, amount: T::Balance }, } /// A map that stores freezes applied on an account for a given AssetId. #[pezpallet::storage] pub type Freezes, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::AssetId, Blake2_128Concat, T::AccountId, BoundedVec< IdAmount, VariantCountOf, >, ValueQuery, >; /// A map that stores the current total frozen balance for every account on a given AssetId. #[pezpallet::storage] pub type FrozenBalances, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::AssetId, Blake2_128Concat, T::AccountId, T::Balance, >; #[pezpallet::hooks] impl, I: 'static> Hooks> for Pezpallet { #[cfg(feature = "try-runtime")] fn try_state(_: BlockNumberFor) -> Result<(), TryRuntimeError> { Self::do_try_state() } } } impl, I: 'static> Pezpallet { fn update_freezes( asset: T::AssetId, who: &T::AccountId, freezes: BoundedSlice< IdAmount, VariantCountOf, >, ) -> DispatchResult { let prev_frozen = FrozenBalances::::get(asset.clone(), who).unwrap_or_default(); let after_frozen = freezes.into_iter().map(|f| f.amount).max().unwrap_or_else(Zero::zero); FrozenBalances::::set(asset.clone(), who, Some(after_frozen)); if freezes.is_empty() { Freezes::::remove(asset.clone(), who); FrozenBalances::::remove(asset.clone(), who); } else { Freezes::::insert(asset.clone(), who, freezes); } if prev_frozen > after_frozen { let amount = prev_frozen.saturating_sub(after_frozen); Self::deposit_event(Event::Thawed { asset_id: asset, who: who.clone(), amount }); } else if after_frozen > prev_frozen { let amount = after_frozen.saturating_sub(prev_frozen); Self::deposit_event(Event::Frozen { asset_id: asset, who: who.clone(), amount }); } Ok(()) } #[cfg(feature = "try-runtime")] fn do_try_state() -> Result<(), TryRuntimeError> { for (asset, who, _) in FrozenBalances::::iter() { let max_frozen_amount = Freezes::::get(asset.clone(), who.clone()).iter().map(|l| l.amount).max(); ensure!( FrozenBalances::::get(asset, who) == max_frozen_amount, "The `FrozenAmount` is not equal to the maximum amount in `Freezes` for (`asset`, `who`)" ); } Ok(()) } }